From 5baa3aa5b6efd6273fb50869b5cf6a911d334f78 Mon Sep 17 00:00:00 2001 From: Jerome Benoit Date: Fri, 28 Jun 2024 20:51:42 +0000 Subject: [PATCH 1/1] Import igraph_0.10.13+ds.orig.tar.xz [dgit import orig igraph_0.10.13+ds.orig.tar.xz] --- ACKNOWLEDGEMENTS.md | 209 + AUTHORS | 6 + CHANGELOG.md | 1427 + CMakeLists.txt | 185 + CODE_OF_CONDUCT.md | 178 + CONTRIBUTING.md | 244 + CONTRIBUTORS.md | 101 + CONTRIBUTORS.txt | 73 + COPYING | 340 + ChangeLog | 1 + IGRAPH_VERSION | 1 + INSTALL | 7 + NEWS | 5 + ONEWS | 1433 + README.md | 30 + SUPPORT.md | 13 + doc/CMakeLists.txt | 359 + doc/adjlist.xxml | 51 + doc/arpack.xxml | 54 + doc/attributes.xxml | 126 + doc/basicigraph.xxml | 219 + doc/bibdatabase.xml | 51 + doc/bipartite.xxml | 41 + doc/bitset.xxml | 59 + doc/c-docbook.re | 735 + doc/cliques.xxml | 45 + doc/coloring.xxml | 14 + doc/community.xxml | 63 + doc/cycles.xxml | 22 + doc/docbook.outlang | 36 + doc/doxrox.py | 567 + doc/dqueue.xxml | 24 + doc/embedding.xxml | 16 + doc/error.xxml | 88 + doc/fdl.xml | 420 + doc/flows.xxml | 48 + doc/foreign.xxml | 64 + doc/generators.xxml | 92 + doc/gpl.xml | 444 + doc/graphlets.xxml | 20 + doc/gtk-doc.xsl | 342 + doc/heap.xxml | 21 + doc/hrg.xxml | 45 + doc/html/home.png | Bin 0 -> 654 bytes doc/html/left.png | Bin 0 -> 459 bytes doc/html/right.png | Bin 0 -> 472 bytes doc/html/style.css | 279 + doc/html/toggle.js | 23 + doc/html/up.png | Bin 0 -> 406 bytes doc/igraph-docs.xml | 166 + doc/igraph.3 | 47 + doc/igraphlogo/igraph-white.svg.gz | Bin 0 -> 6138 bytes doc/igraphlogo/igraph.svg.gz | Bin 0 -> 6101 bytes doc/igraphlogo/igraph2.svg.gz | Bin 0 -> 1952 bytes doc/installation.xml | 624 + doc/introduction.xml | 109 + doc/isomorphism.xxml | 72 + doc/iterators.xxml | 108 + doc/layout.xxml | 56 + doc/licenses.xml | 14 + doc/licenses/Licence_CeCILL-B_V1-en.txt | 515 + doc/licenses/Licence_CeCILL-B_V1-fr.txt | 519 + doc/licenses/gpl-2.0.txt | 280 + doc/licenses/gpl-3.0.txt | 621 + doc/licenses/lgpl-2.1.txt | 458 + doc/licenses/lgpl-3.0.txt | 165 + doc/matrix.xxml | 137 + doc/memory.xxml | 16 + doc/motifs.xxml | 32 + doc/nongraph.xxml | 44 + doc/operators.xxml | 43 + doc/pmt.xml | 145 + doc/progress.xxml | 38 + doc/psumtree.xxml | 20 + doc/random.xxml | 54 + doc/separators.xxml | 16 + doc/sparsemat.xxml | 116 + doc/spatialgames.xxml | 23 + doc/stack.xxml | 20 + doc/status.xxml | 27 + doc/structural.xxml | 274 + doc/strvector.xxml | 38 + doc/threading.xxml | 45 + doc/tutorial.xml | 321 + doc/vector.xxml | 176 + doc/vectorlist.xxml | 60 + doc/version-greater-or-equal.xsl | 54 + doc/visitors.xxml | 30 + etc/cmake/BuildType.cmake | 12 + etc/cmake/CheckTLSSupport.cmake | 36 + etc/cmake/CodeCoverage.cmake | 682 + etc/cmake/FindARPACK.cmake | 85 + etc/cmake/FindGLPK.cmake | 81 + etc/cmake/FindGMP.cmake | 36 + etc/cmake/FindPLFIT.cmake | 63 + etc/cmake/GetGitRevisionDescription.cmake | 166 + etc/cmake/GetGitRevisionDescription.cmake.in | 41 + etc/cmake/JoinPaths.cmake | 26 + etc/cmake/PadString.cmake | 39 + etc/cmake/PreventInSourceBuilds.cmake | 34 + etc/cmake/UseCCacheWhenInstalled.cmake | 7 + etc/cmake/attribute_support.cmake | 27 + etc/cmake/benchmark_helpers.cmake | 43 + etc/cmake/bit_operations_support.cmake | 92 + etc/cmake/compilers.cmake | 119 + etc/cmake/cpack_install_script.cmake | 82 + etc/cmake/create_igraph_version_file.cmake | 8 + etc/cmake/debugging.cmake | 7 + etc/cmake/dependencies.cmake | 163 + etc/cmake/features.cmake | 25 + etc/cmake/fuzz_helpers.cmake | 16 + etc/cmake/generate_tags_file.cmake | 46 + etc/cmake/helpers.cmake | 6 + etc/cmake/ieee754_endianness.cmake | 54 + etc/cmake/ieee754_endianness_check.c | 24 + etc/cmake/igraph-config.cmake.in | 40 + etc/cmake/lto.cmake | 23 + etc/cmake/packaging.cmake | 70 + etc/cmake/pkgconfig_helpers.cmake | 75 + etc/cmake/run_legacy_test.cmake | 73 + etc/cmake/safe_math_support.cmake | 18 + etc/cmake/sanitizers.cmake | 88 + etc/cmake/summary.cmake | 105 + etc/cmake/test_helpers.cmake | 120 + etc/cmake/tls.cmake | 25 + etc/cmake/uint128_support.cmake | 43 + etc/cmake/version.cmake | 90 + examples/simple/adjlist.c | 48 + examples/simple/ak-4102.max | 24623 ++++++++++++++++ examples/simple/bellman_ford.c | 92 + examples/simple/bellman_ford.out | 16 + examples/simple/blas.c | 42 + examples/simple/blas.out | 2 + examples/simple/blas_dgemm.c | 27 + examples/simple/blas_dgemm.out | 2 + examples/simple/cattributes.c | 133 + examples/simple/cattributes.out | 6 + examples/simple/cattributes2.c | 68 + examples/simple/cattributes2.out | 337 + examples/simple/cattributes3.c | 97 + examples/simple/cattributes3.out | 243 + examples/simple/cattributes4.c | 82 + examples/simple/cattributes4.out | 81 + examples/simple/celegansneural.gml | 15644 ++++++++++ examples/simple/centralization.c | 78 + examples/simple/centralization.out | 7 + examples/simple/cohesive_blocks.c | 63 + examples/simple/cohesive_blocks.out | 21 + examples/simple/creation.c | 30 + examples/simple/distances.c | 70 + examples/simple/distances.out | 51 + examples/simple/dominator_tree.c | 58 + examples/simple/dominator_tree.out | 9 + examples/simple/dot.c | 52 + examples/simple/dot.out | 196 + examples/simple/dqueue.c | 117 + examples/simple/dqueue.out | 2 + examples/simple/edgelist1.dl | 10 + examples/simple/edgelist2.dl | 9 + examples/simple/edgelist3.dl | 11 + examples/simple/edgelist4.dl | 10 + examples/simple/edgelist5.dl | 9 + examples/simple/edgelist6.dl | 11 + examples/simple/edgelist7.dl | 6 + examples/simple/eigenvector_centrality.c | 56 + examples/simple/eigenvector_centrality.out | 3 + examples/simple/even_tarjan.c | 70 + examples/simple/flow.c | 106 + examples/simple/flow2.c | 73 + examples/simple/flow2.out | 5 + examples/simple/foreign.c | 51 + examples/simple/foreign.out | 11 + examples/simple/fullmatrix1.dl | 7 + examples/simple/fullmatrix2.dl | 10 + examples/simple/fullmatrix3.dl | 12 + examples/simple/fullmatrix4.dl | 10 + examples/simple/gml.c | 52 + examples/simple/gml.out | 614 + examples/simple/graphml.c | 83 + examples/simple/igraph_adjacency.c | 29 + examples/simple/igraph_all_st_mincuts.c | 66 + examples/simple/igraph_assortativity_degree.c | 34 + .../simple/igraph_assortativity_degree.out | 17 + .../simple/igraph_assortativity_nominal.c | 43 + .../simple/igraph_assortativity_nominal.out | 17 + examples/simple/igraph_atlas.c | 46 + examples/simple/igraph_atlas.out | 31 + .../simple/igraph_attribute_combination.c | 29 + .../simple/igraph_attribute_combination.out | 17 + examples/simple/igraph_average_path_length.c | 21 + .../igraph_avg_nearest_neighbor_degree.c | 63 + .../igraph_avg_nearest_neighbor_degree.out | 4 + examples/simple/igraph_barabasi_game.c | 94 + examples/simple/igraph_barabasi_game2.c | 109 + examples/simple/igraph_bfs.c | 70 + examples/simple/igraph_bfs.out | 6 + examples/simple/igraph_bfs_callback.c | 62 + examples/simple/igraph_bfs_callback.out | 1 + examples/simple/igraph_bfs_simple.c | 49 + examples/simple/igraph_bfs_simple.out | 3 + .../simple/igraph_biconnected_components.c | 68 + .../simple/igraph_biconnected_components.out | 17 + examples/simple/igraph_bipartite_create.c | 46 + examples/simple/igraph_bipartite_create.out | 8 + examples/simple/igraph_bipartite_projection.c | 51 + examples/simple/igraph_cliques.c | 145 + examples/simple/igraph_cliques.out | 50 + examples/simple/igraph_cocitation.c | 48 + examples/simple/igraph_cocitation.out | 11 + examples/simple/igraph_coloring.c | 39 + .../igraph_community_edge_betweenness.c | 57 + .../igraph_community_edge_betweenness.out | 8 + examples/simple/igraph_community_fastgreedy.c | 39 + .../simple/igraph_community_fastgreedy.out | 5 + .../igraph_community_label_propagation.c | 58 + .../igraph_community_label_propagation.out | 2 + .../igraph_community_leading_eigenvector.c | 90 + .../igraph_community_leading_eigenvector.out | 7 + examples/simple/igraph_community_leiden.c | 82 + examples/simple/igraph_community_leiden.out | 9 + examples/simple/igraph_community_multilevel.c | 112 + .../simple/igraph_community_multilevel.out | 15 + .../igraph_community_optimal_modularity.c | 110 + examples/simple/igraph_complementer.c | 109 + examples/simple/igraph_complementer.out | 87 + examples/simple/igraph_compose.c | 117 + examples/simple/igraph_compose.out | 11 + examples/simple/igraph_contract_vertices.c | 81 + examples/simple/igraph_copy.c | 56 + examples/simple/igraph_create.c | 70 + examples/simple/igraph_decompose.c | 50 + examples/simple/igraph_decompose.out | 18 + examples/simple/igraph_degree.c | 152 + examples/simple/igraph_degree.out | 13 + examples/simple/igraph_degree_sequence_game.c | 94 + .../simple/igraph_degree_sequence_game.out | 7 + examples/simple/igraph_delete_edges.c | 43 + examples/simple/igraph_delete_vertices.c | 51 + .../igraph_deterministic_optimal_imitation.c | 172 + examples/simple/igraph_diameter.c | 45 + examples/simple/igraph_diameter.out | 3 + examples/simple/igraph_difference.c | 138 + examples/simple/igraph_difference.out | 24 + examples/simple/igraph_disjoint_union.c | 73 + examples/simple/igraph_disjoint_union.out | 30 + examples/simple/igraph_eccentricity.c | 29 + examples/simple/igraph_eccentricity.out | 3 + examples/simple/igraph_erdos_renyi_game_gnm.c | 33 + examples/simple/igraph_erdos_renyi_game_gnp.c | 33 + examples/simple/igraph_es_pairs.c | 80 + examples/simple/igraph_feedback_arc_set.c | 94 + examples/simple/igraph_feedback_arc_set.out | 3 + examples/simple/igraph_feedback_arc_set_ip.c | 125 + .../simple/igraph_feedback_arc_set_ip.out | 5 + examples/simple/igraph_fisher_yates_shuffle.c | 104 + examples/simple/igraph_full.c | 59 + examples/simple/igraph_full.out | 4 + .../igraph_get_all_shortest_paths_dijkstra.c | 84 + ...igraph_get_all_shortest_paths_dijkstra.out | 18 + examples/simple/igraph_get_eid.c | 66 + examples/simple/igraph_get_eid.out | 2 + examples/simple/igraph_get_eids.c | 106 + examples/simple/igraph_get_laplacian.c | 37 + examples/simple/igraph_get_laplacian.out | 5 + examples/simple/igraph_get_laplacian_sparse.c | 131 + .../simple/igraph_get_laplacian_sparse.out | 200 + examples/simple/igraph_get_shortest_paths.c | 114 + examples/simple/igraph_get_shortest_paths.out | 7 + .../igraph_get_shortest_paths_dijkstra.c | 83 + .../igraph_get_shortest_paths_dijkstra.out | 21 + examples/simple/igraph_girth.c | 58 + examples/simple/igraph_grg_game.c | 50 + examples/simple/igraph_grg_game.out | 1 + examples/simple/igraph_has_multiple.c | 85 + examples/simple/igraph_independent_sets.c | 69 + examples/simple/igraph_independent_sets.out | 36 + examples/simple/igraph_intersection.c | 124 + examples/simple/igraph_intersection.out | 7 + examples/simple/igraph_is_biconnected.c | 33 + examples/simple/igraph_is_biconnected.out | 1 + examples/simple/igraph_is_directed.c | 43 + examples/simple/igraph_is_loop.c | 55 + examples/simple/igraph_is_loop.out | 8 + examples/simple/igraph_is_minimal_separator.c | 74 + examples/simple/igraph_is_multiple.c | 56 + examples/simple/igraph_is_multiple.out | 2 + examples/simple/igraph_is_separator.c | 86 + examples/simple/igraph_isomorphic_vf2.c | 95 + examples/simple/igraph_join.c | 50 + examples/simple/igraph_join.out | 44 + examples/simple/igraph_kary_tree.c | 43 + examples/simple/igraph_kary_tree.out | 2 + examples/simple/igraph_lapack_dgeev.c | 63 + examples/simple/igraph_lapack_dgeev.out | 8 + examples/simple/igraph_lapack_dgeevx.c | 95 + examples/simple/igraph_lapack_dgeevx.out | 13 + examples/simple/igraph_lapack_dgesv.c | 151 + examples/simple/igraph_lapack_dgesv.out | 1 + examples/simple/igraph_lapack_dsyevr.c | 69 + examples/simple/igraph_lapack_dsyevr.out | 13 + .../simple/igraph_layout_reingold_tilford.c | 47 + .../simple/igraph_layout_reingold_tilford.in | 44 + examples/simple/igraph_lcf.c | 87 + examples/simple/igraph_list_triangles.c | 37 + examples/simple/igraph_list_triangles.out | 4 + examples/simple/igraph_maximal_cliques.c | 53 + .../igraph_maximum_bipartite_matching.c | 77 + examples/simple/igraph_mincut.c | 112 + examples/simple/igraph_mincut.out | 12 + examples/simple/igraph_minimal_separators.c | 53 + .../simple/igraph_minimum_size_separators.c | 45 + .../simple/igraph_minimum_spanning_tree.c | 68 + .../simple/igraph_minimum_spanning_tree.out | 17 + examples/simple/igraph_motifs_randesu.c | 54 + examples/simple/igraph_motifs_randesu.out | 17 + examples/simple/igraph_neighbors.c | 49 + examples/simple/igraph_neighbors.out | 3 + examples/simple/igraph_pagerank.c | 36 + examples/simple/igraph_power_law_fit.c | 64 + examples/simple/igraph_power_law_fit.out | 3 + examples/simple/igraph_radius.c | 54 + examples/simple/igraph_random_sample.c | 30 + examples/simple/igraph_read_graph_dl.c | 59 + examples/simple/igraph_read_graph_dl.out | 104 + examples/simple/igraph_read_graph_graphdb.c | 41 + examples/simple/igraph_read_graph_graphdb.out | 1500 + examples/simple/igraph_read_graph_lgl-1.lgl | 7 + examples/simple/igraph_read_graph_lgl-2.lgl | 7 + examples/simple/igraph_read_graph_lgl-3.lgl | 4 + examples/simple/igraph_read_graph_lgl.c | 83 + examples/simple/igraph_read_graph_lgl.out | 12 + .../simple/igraph_realize_degree_sequence.c | 38 + .../simple/igraph_realize_degree_sequence.out | 8 + examples/simple/igraph_reciprocity.c | 65 + examples/simple/igraph_regular_tree.c | 27 + examples/simple/igraph_regular_tree.out | 3 + examples/simple/igraph_ring.c | 46 + examples/simple/igraph_ring.out | 16 + .../simple/igraph_roulette_wheel_imitation.c | 213 + examples/simple/igraph_similarity.c | 85 + examples/simple/igraph_similarity.out | 25 + examples/simple/igraph_simplify.c | 92 + examples/simple/igraph_simplify.out | 10 + examples/simple/igraph_small.c | 35 + examples/simple/igraph_small.out | 5 + examples/simple/igraph_sparsemat.c | 173 + examples/simple/igraph_sparsemat.out | 295 + examples/simple/igraph_sparsemat3.c | 312 + examples/simple/igraph_sparsemat3.out | 0 examples/simple/igraph_sparsemat4.c | 311 + examples/simple/igraph_sparsemat4.out | 0 examples/simple/igraph_sparsemat6.c | 65 + examples/simple/igraph_sparsemat7.c | 76 + examples/simple/igraph_sparsemat8.c | 209 + examples/simple/igraph_star.c | 35 + examples/simple/igraph_star.out | 6 + examples/simple/igraph_stochastic_imitation.c | 182 + examples/simple/igraph_strvector.c | 86 + examples/simple/igraph_strvector.out | 45 + examples/simple/igraph_subisomorphic_lad.c | 125 + examples/simple/igraph_subisomorphic_lad.out | 30 + examples/simple/igraph_symmetric_tree.c | 27 + examples/simple/igraph_to_undirected.c | 52 + examples/simple/igraph_to_undirected.out | 48 + examples/simple/igraph_topological_sorting.c | 49 + .../simple/igraph_topological_sorting.out | 3 + examples/simple/igraph_transitivity.c | 94 + examples/simple/igraph_union.c | 51 + examples/simple/igraph_union.out | 9 + examples/simple/igraph_vector_int_list_sort.c | 41 + examples/simple/igraph_version.c | 41 + examples/simple/igraph_vs_nonadj.c | 65 + examples/simple/igraph_vs_nonadj.out | 2 + examples/simple/igraph_vs_range.c | 50 + examples/simple/igraph_vs_range.out | 1 + examples/simple/igraph_vs_vector.c | 85 + examples/simple/igraph_vs_vector.out | 1 + examples/simple/igraph_weighted_adjacency.c | 53 + examples/simple/igraph_weighted_adjacency.out | 6 + examples/simple/igraph_write_graph_lgl.c | 47 + examples/simple/igraph_write_graph_lgl.out | 37 + examples/simple/igraph_write_graph_pajek.c | 75 + examples/simple/igraph_write_graph_pajek.out | 56 + examples/simple/iso_b03_m1000.A00 | Bin 0 -> 5002 bytes examples/simple/karate.gml | 530 + examples/simple/links.net | 16 + examples/simple/nodelist1.dl | 9 + examples/simple/nodelist2.dl | 8 + examples/simple/random_seed.c | 36 + examples/simple/safelocale.c | 49 + examples/simple/safelocale.out | 21 + examples/simple/test.graphml | 55 + examples/simple/walktrap.c | 59 + examples/simple/weighted.gml | 17 + examples/tutorial/tutorial1.c | 29 + examples/tutorial/tutorial2.c | 37 + examples/tutorial/tutorial3.c | 48 + igraph.pc.in | 13 + include/igraph.h | 101 + include/igraph_adjlist.h | 224 + include/igraph_arpack.h | 337 + include/igraph_array.h | 63 + include/igraph_array_pmt.h | 54 + include/igraph_attributes.h | 860 + include/igraph_bipartite.h | 112 + include/igraph_bitset.h | 267 + include/igraph_bitset_list.h | 51 + include/igraph_blas.h | 72 + include/igraph_centrality.h | 204 + include/igraph_cliques.h | 116 + include/igraph_cocitation.h | 67 + include/igraph_cohesive_blocks.h | 43 + include/igraph_coloring.h | 55 + include/igraph_community.h | 259 + include/igraph_complex.h | 113 + include/igraph_components.h | 73 + include/igraph_config.h.in | 55 + include/igraph_constants.h | 220 + include/igraph_constructors.h | 107 + include/igraph_conversion.h | 80 + include/igraph_cycles.h | 30 + include/igraph_datatype.h | 127 + include/igraph_decls.h | 60 + include/igraph_dqueue.h | 71 + include/igraph_dqueue_pmt.h | 50 + include/igraph_eigen.h | 101 + include/igraph_embedding.h | 68 + include/igraph_epidemics.h | 68 + include/igraph_error.h | 985 + include/igraph_eulerian.h | 39 + include/igraph_flow.h | 158 + include/igraph_foreign.h | 113 + include/igraph_games.h | 235 + include/igraph_graph_list.h | 60 + include/igraph_graphicality.h | 55 + include/igraph_graphlets.h | 53 + include/igraph_heap.h | 85 + include/igraph_heap_pmt.h | 40 + include/igraph_hrg.h | 132 + include/igraph_interface.h | 139 + include/igraph_interrupt.h | 128 + include/igraph_iterators.h | 421 + include/igraph_lapack.h | 112 + include/igraph_layout.h | 298 + include/igraph_lsap.h | 18 + include/igraph_matching.h | 52 + include/igraph_matrix.h | 103 + include/igraph_matrix_list.h | 59 + include/igraph_matrix_pmt.h | 263 + include/igraph_memory.h | 56 + include/igraph_microscopic_update.h | 61 + include/igraph_mixing.h | 73 + include/igraph_motifs.h | 101 + include/igraph_neighborhood.h | 49 + include/igraph_nongraph.h | 115 + include/igraph_operators.h | 99 + include/igraph_paths.h | 364 + include/igraph_pmt.h | 204 + include/igraph_pmt_off.h | 186 + include/igraph_progress.h | 184 + include/igraph_psumtree.h | 52 + include/igraph_qsort.h | 40 + include/igraph_random.h | 190 + include/igraph_reachability.h | 49 + include/igraph_scan.h | 72 + include/igraph_separators.h | 53 + include/igraph_sparsemat.h | 312 + include/igraph_stack.h | 71 + include/igraph_stack_pmt.h | 48 + include/igraph_statusbar.h | 129 + include/igraph_structural.h | 180 + include/igraph_strvector.h | 115 + include/igraph_threading.h.in | 47 + include/igraph_topology.h | 315 + include/igraph_transitivity.h | 59 + include/igraph_typed_list_pmt.h | 119 + include/igraph_types.h | 154 + include/igraph_vector.h | 165 + include/igraph_vector_list.h | 73 + include/igraph_vector_pmt.h | 320 + include/igraph_vector_ptr.h | 105 + include/igraph_vector_type.h | 33 + include/igraph_version.h.in | 44 + include/igraph_visitor.h | 139 + interfaces/CMakeLists.txt | 28 + interfaces/functions.yaml | 2654 ++ interfaces/types.yaml | 638 + src/CMakeLists.txt | 494 + src/centrality/betweenness.c | 1285 + src/centrality/centrality_internal.h | 37 + src/centrality/centrality_other.c | 52 + src/centrality/centralization.c | 662 + src/centrality/closeness.c | 808 + src/centrality/coreness.c | 156 + src/centrality/eigenvector.c | 552 + src/centrality/hub_authority.c | 523 + src/centrality/pagerank.c | 715 + src/centrality/prpack.cpp | 134 + src/centrality/prpack/CMakeLists.txt | 44 + src/centrality/prpack/prpack.h | 11 + src/centrality/prpack/prpack_base_graph.cpp | 345 + src/centrality/prpack/prpack_base_graph.h | 46 + src/centrality/prpack/prpack_csc.h | 30 + src/centrality/prpack/prpack_csr.h | 16 + src/centrality/prpack/prpack_edge_list.h | 16 + src/centrality/prpack/prpack_igraph_graph.cpp | 176 + src/centrality/prpack/prpack_igraph_graph.h | 33 + .../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 + src/centrality/prpack/prpack_result.cpp | 11 + src/centrality/prpack/prpack_result.h | 30 + src/centrality/prpack/prpack_solver.cpp | 886 + src/centrality/prpack/prpack_solver.h | 180 + src/centrality/prpack/prpack_utils.cpp | 58 + src/centrality/prpack/prpack_utils.h | 33 + src/centrality/prpack_internal.h | 44 + src/centrality/truss.cpp | 286 + src/cliques/cliquer/CMakeLists.txt | 27 + src/cliques/cliquer/README | 61 + src/cliques/cliquer/cliquer.c | 1850 ++ src/cliques/cliquer/cliquer.h | 78 + src/cliques/cliquer/cliquer_graph.c | 764 + src/cliques/cliquer/cliquerconf.h | 68 + src/cliques/cliquer/graph.h | 75 + src/cliques/cliquer/misc.h | 59 + src/cliques/cliquer/reorder.c | 424 + src/cliques/cliquer/reorder.h | 26 + src/cliques/cliquer/set.h | 386 + src/cliques/cliquer_internal.h | 49 + src/cliques/cliquer_wrapper.c | 459 + src/cliques/cliques.c | 1053 + src/cliques/glet.c | 899 + src/cliques/maximal_cliques.c | 562 + src/cliques/maximal_cliques_template.h | 366 + src/community/community_misc.c | 922 + src/community/edge_betweenness.c | 767 + src/community/fast_modularity.c | 1088 + src/community/fluid.c | 262 + src/community/infomap/infomap.cc | 323 + src/community/infomap/infomap_FlowGraph.cc | 382 + src/community/infomap/infomap_FlowGraph.h | 81 + src/community/infomap/infomap_Greedy.cc | 522 + src/community/infomap/infomap_Greedy.h | 74 + src/community/infomap/infomap_Node.h | 52 + src/community/label_propagation.c | 464 + src/community/leading_eigenvector.c | 840 + src/community/leiden.c | 1042 + src/community/louvain.c | 721 + src/community/modularity.c | 395 + src/community/optimal_modularity.c | 298 + src/community/spinglass/NetDataTypes.cpp | 102 + src/community/spinglass/NetDataTypes.h | 568 + src/community/spinglass/NetRoutines.cpp | 81 + src/community/spinglass/NetRoutines.h | 55 + src/community/spinglass/clustertool.cpp | 648 + src/community/spinglass/pottsmodel_2.cpp | 1707 ++ src/community/spinglass/pottsmodel_2.h | 165 + src/community/voronoi.c | 656 + src/community/walktrap/walktrap.cpp | 233 + .../walktrap/walktrap_communities.cpp | 786 + src/community/walktrap/walktrap_communities.h | 161 + src/community/walktrap/walktrap_graph.cpp | 229 + src/community/walktrap/walktrap_graph.h | 101 + src/community/walktrap/walktrap_heap.cpp | 142 + src/community/walktrap/walktrap_heap.h | 107 + src/config.h.in | 41 + src/connectivity/cohesive_blocks.c | 567 + src/connectivity/components.c | 1637 + src/connectivity/reachability.c | 263 + src/connectivity/separators.c | 885 + src/constructors/adjacency.c | 1432 + src/constructors/atlas-edges.h | 1292 + src/constructors/atlas.c | 80 + src/constructors/basic_constructors.c | 156 + src/constructors/circulant.c | 111 + src/constructors/de_bruijn.c | 114 + src/constructors/famous.c | 497 + src/constructors/full.c | 377 + src/constructors/generalized_petersen.c | 95 + src/constructors/kautz.c | 211 + src/constructors/lattices.c | 603 + src/constructors/lcf.c | 155 + src/constructors/linegraph.c | 175 + src/constructors/prufer.c | 126 + src/constructors/regular.c | 1003 + src/constructors/trees.c | 163 + src/core/array.c | 50 + src/core/array.pmt | 110 + src/core/bitset.c | 833 + src/core/bitset_list.c | 54 + src/core/buckets.c | 193 + src/core/buckets.h | 72 + src/core/cutheap.c | 169 + src/core/cutheap.h | 53 + src/core/dqueue.c | 49 + src/core/dqueue.pmt | 443 + src/core/error.c | 599 + src/core/estack.c | 67 + src/core/estack.h | 52 + src/core/exceptions.h | 34 + src/core/fixed_vectorlist.c | 59 + src/core/fixed_vectorlist.h | 50 + src/core/genheap.c | 307 + src/core/genheap.h | 73 + src/core/grid.c | 275 + src/core/grid.h | 78 + src/core/heap.c | 64 + src/core/heap.pmt | 382 + src/core/indheap.c | 1038 + src/core/indheap.h | 157 + src/core/interruption.c | 40 + src/core/interruption.h | 81 + src/core/marked_queue.c | 115 + src/core/marked_queue.h | 75 + src/core/math.h | 81 + src/core/matrix.c | 304 + src/core/matrix.pmt | 1877 ++ src/core/matrix_list.c | 54 + src/core/memory.c | 124 + src/core/printing.c | 231 + src/core/progress.c | 155 + src/core/psumtree.c | 268 + src/core/set.c | 326 + src/core/set.h | 66 + src/core/sparsemat.c | 3561 +++ src/core/stack.c | 49 + src/core/stack.pmt | 306 + src/core/statusbar.c | 133 + src/core/strvector.c | 699 + src/core/trie.c | 435 + src/core/trie.h | 72 + src/core/typed_list.pmt | 1101 + src/core/vector.c | 708 + src/core/vector.pmt | 3344 +++ src/core/vector_list.c | 171 + src/core/vector_ptr.c | 802 + src/flow/flow.c | 2620 ++ src/flow/flow_conversion.c | 93 + src/flow/flow_internal.h | 44 + src/flow/st-cuts.c | 1575 + src/games/barabasi.c | 859 + src/games/callaway_traits.c | 203 + src/games/chung_lu.c | 323 + src/games/citations.c | 515 + src/games/correlated.c | 324 + src/games/degree_sequence.c | 813 + .../degree_sequence_vl/degree_sequence_vl.h | 34 + .../degree_sequence_vl/gengraph_definitions.h | 195 + .../gengraph_degree_sequence.cpp | 140 + .../gengraph_degree_sequence.h | 88 + .../gengraph_graph_molloy_hash.cpp | 732 + .../gengraph_graph_molloy_hash.h | 188 + .../gengraph_graph_molloy_optimized.cpp | 1212 + .../gengraph_graph_molloy_optimized.h | 231 + src/games/degree_sequence_vl/gengraph_hash.h | 315 + .../degree_sequence_vl/gengraph_header.h | 109 + .../gengraph_mr-connected.cpp | 188 + src/games/degree_sequence_vl/gengraph_qsort.h | 307 + .../degree_sequence_vl/gengraph_random.cpp | 275 + .../degree_sequence_vl/gengraph_random.h | 213 + src/games/dotproduct.c | 281 + src/games/erdos_renyi.c | 451 + src/games/establishment.c | 186 + src/games/forestfire.c | 262 + src/games/grg.c | 180 + src/games/growing_random.c | 111 + src/games/islands.c | 176 + src/games/k_regular.c | 85 + src/games/preference.c | 621 + src/games/recent_degree.c | 388 + src/games/sbm.c | 643 + src/games/static_fitness.c | 464 + src/games/tree.c | 202 + src/games/watts_strogatz.c | 119 + src/graph/adjlist.c | 1342 + src/graph/attributes.c | 557 + src/graph/attributes.h | 115 + src/graph/basic_query.c | 93 + src/graph/caching.c | 214 + src/graph/caching.h | 52 + src/graph/cattributes.c | 4524 +++ src/graph/graph_list.c | 61 + src/graph/internal.h | 42 + src/graph/iterators.c | 2085 ++ src/graph/type_common.c | 207 + src/graph/type_indexededgelist.c | 1905 ++ src/graph/visitors.c | 660 + src/hrg/dendro.h | 296 + src/hrg/graph.h | 156 + src/hrg/graph_simp.h | 142 + src/hrg/hrg.cc | 1093 + src/hrg/hrg_types.cc | 3589 +++ src/hrg/rbtree.h | 144 + src/hrg/splittree_eq.h | 168 + src/internal/glpk_support.c | 172 + src/internal/glpk_support.h | 149 + src/internal/gmp_internal.h | 34 + src/internal/hacks.c | 62 + src/internal/hacks.h | 73 + src/internal/lsap.c | 689 + src/internal/qsort.c | 267 + src/internal/qsort_r.c | 8 + src/internal/utils.c | 95 + src/internal/utils.h | 32 + src/internal/zeroin.c | 201 + src/io/dimacs.c | 389 + src/io/dl-header.h | 53 + src/io/dl.c | 203 + src/io/dot.c | 322 + src/io/edgelist.c | 161 + src/io/gml-header.h | 42 + src/io/gml-tree.c | 279 + src/io/gml-tree.h | 87 + src/io/gml.c | 1320 + src/io/graphdb.c | 130 + src/io/graphml.c | 2150 ++ src/io/leda.c | 310 + src/io/lgl-header.h | 37 + src/io/lgl.c | 457 + src/io/ncol-header.h | 36 + src/io/ncol.c | 438 + src/io/pajek-header.h | 62 + src/io/pajek.c | 790 + src/io/parse_utils.c | 381 + src/io/parse_utils.h | 41 + src/io/parsers/dl-lexer.c | 2533 ++ src/io/parsers/dl-lexer.h | 731 + src/io/parsers/dl-parser.c | 2192 ++ src/io/parsers/dl-parser.h | 109 + src/io/parsers/gml-lexer.c | 2346 ++ src/io/parsers/gml-lexer.h | 730 + src/io/parsers/gml-parser.c | 1859 ++ src/io/parsers/gml-parser.h | 94 + src/io/parsers/lgl-lexer.c | 2284 ++ src/io/parsers/lgl-lexer.h | 730 + src/io/parsers/lgl-parser.c | 1691 ++ src/io/parsers/lgl-parser.h | 89 + src/io/parsers/ncol-lexer.c | 2279 ++ src/io/parsers/ncol-lexer.h | 730 + src/io/parsers/ncol-parser.c | 1683 ++ src/io/parsers/ncol-parser.h | 87 + src/io/parsers/pajek-lexer.c | 2683 ++ src/io/parsers/pajek-lexer.h | 734 + src/io/parsers/pajek-parser.c | 2830 ++ src/io/parsers/pajek-parser.h | 180 + src/isomorphism/bliss.cc | 629 + src/isomorphism/bliss/CMakeLists.txt | 43 + src/isomorphism/bliss/bignum.hh | 103 + src/isomorphism/bliss/defs.cc | 32 + src/isomorphism/bliss/defs.hh | 90 + src/isomorphism/bliss/graph.cc | 5035 ++++ src/isomorphism/bliss/graph.hh | 876 + src/isomorphism/bliss/heap.cc | 111 + src/isomorphism/bliss/heap.hh | 89 + src/isomorphism/bliss/igraph-changes.md | 34 + src/isomorphism/bliss/kqueue.hh | 168 + src/isomorphism/bliss/kstack.hh | 145 + src/isomorphism/bliss/orbit.cc | 151 + src/isomorphism/bliss/orbit.hh | 112 + src/isomorphism/bliss/partition.cc | 1127 + src/isomorphism/bliss/partition.hh | 299 + src/isomorphism/bliss/stats.hh | 87 + src/isomorphism/bliss/uintseqhash.cc | 117 + src/isomorphism/bliss/uintseqhash.hh | 63 + src/isomorphism/bliss/utils.cc | 60 + src/isomorphism/bliss/utils.hh | 46 + src/isomorphism/isoclasses.c | 2933 ++ src/isomorphism/isoclasses.h | 46 + src/isomorphism/isomorphism_misc.c | 115 + src/isomorphism/lad.c | 1656 ++ src/isomorphism/queries.c | 243 + src/isomorphism/vf2.c | 1782 ++ src/layout/circular.c | 188 + src/layout/davidson_harel.c | 456 + src/layout/drl/DensityGrid.cpp | 258 + src/layout/drl/DensityGrid.h | 81 + src/layout/drl/DensityGrid_3d.cpp | 282 + src/layout/drl/DensityGrid_3d.h | 81 + src/layout/drl/drl_Node.h | 70 + src/layout/drl/drl_Node_3d.h | 70 + src/layout/drl/drl_graph.cpp | 1270 + src/layout/drl/drl_graph.h | 132 + src/layout/drl/drl_graph_3d.cpp | 837 + src/layout/drl/drl_graph_3d.h | 124 + src/layout/drl/drl_layout.cpp | 485 + src/layout/drl/drl_layout.h | 65 + src/layout/drl/drl_layout_3d.cpp | 136 + src/layout/drl/drl_layout_3d.h | 65 + src/layout/drl/drl_parse.cpp | 197 + src/layout/drl/drl_parse.h | 68 + src/layout/fruchterman_reingold.c | 692 + src/layout/gem.c | 254 + src/layout/graphopt.c | 434 + src/layout/kamada_kawai.c | 702 + src/layout/large_graph.c | 389 + src/layout/layout_bipartite.c | 81 + src/layout/layout_grid.c | 113 + src/layout/layout_internal.h | 76 + src/layout/layout_random.c | 288 + src/layout/mds.c | 304 + src/layout/merge_dla.c | 292 + src/layout/merge_grid.c | 218 + src/layout/merge_grid.h | 59 + src/layout/reingold_tilford.c | 1012 + src/layout/sugiyama.c | 1328 + src/layout/umap.c | 1261 + src/linalg/arpack.c | 1556 + src/linalg/arpack_internal.h | 233 + src/linalg/blas.c | 262 + src/linalg/blas_internal.h | 94 + src/linalg/eigen.c | 1468 + src/linalg/lapack.c | 1058 + src/linalg/lapack_internal.h | 186 + src/math/complex.c | 390 + src/math/safe_intop.c | 186 + src/math/safe_intop.h | 139 + src/math/utils.c | 182 + src/misc/bipartite.c | 1338 + src/misc/chordality.c | 480 + src/misc/cocitation.c | 763 + src/misc/coloring.c | 308 + src/misc/conversion.c | 1037 + src/misc/cycle_bases.c | 540 + src/misc/degree_sequence.cpp | 1161 + src/misc/embedding.c | 1201 + src/misc/feedback_arc_set.c | 664 + src/misc/feedback_arc_set.h | 46 + src/misc/graphicality.c | 910 + src/misc/matching.c | 1023 + src/misc/microscopic_update.c | 1209 + src/misc/mixing.c | 957 + src/misc/motifs.c | 1193 + src/misc/order_cycle.cpp | 100 + src/misc/order_cycle.h | 35 + src/misc/other.c | 378 + src/misc/power_law_fit.c | 328 + src/misc/scan.c | 806 + src/misc/sir.c | 269 + src/misc/spanning_trees.c | 488 + src/operators/add_edge.c | 63 + src/operators/complementer.c | 102 + src/operators/compose.c | 134 + src/operators/connect_neighborhood.c | 322 + src/operators/contract.c | 150 + src/operators/difference.c | 183 + src/operators/disjoint_union.c | 196 + src/operators/intersection.c | 287 + src/operators/join.c | 109 + src/operators/misc_internal.c | 240 + src/operators/misc_internal.h | 47 + src/operators/permute.c | 129 + src/operators/reverse.c | 102 + src/operators/rewire.c | 264 + src/operators/rewire_edges.c | 395 + src/operators/rewire_internal.h | 15 + src/operators/simplify.c | 210 + src/operators/subgraph.c | 629 + src/operators/subgraph.h | 42 + src/operators/union.c | 248 + src/paths/all_shortest_paths.c | 338 + src/paths/astar.c | 275 + src/paths/bellman_ford.c | 600 + src/paths/dijkstra.c | 1219 + src/paths/distances.c | 1051 + src/paths/eulerian.c | 683 + src/paths/floyd_warshall.c | 363 + src/paths/histogram.c | 149 + src/paths/johnson.c | 242 + src/paths/random_walk.c | 396 + src/paths/shortest_paths.c | 1680 ++ src/paths/simple_paths.c | 167 + src/paths/sparsifier.c | 462 + src/paths/unweighted.c | 619 + src/paths/voronoi.c | 425 + src/paths/widest_paths.c | 731 + src/properties/basic_properties.c | 381 + src/properties/complete.c | 279 + src/properties/constraint.c | 296 + src/properties/convergence_degree.c | 208 + src/properties/dag.c | 315 + src/properties/degrees.c | 750 + src/properties/ecc.c | 387 + src/properties/girth.c | 217 + src/properties/loops.c | 164 + src/properties/multiplicity.c | 536 + src/properties/neighborhood.c | 439 + src/properties/perfect.c | 191 + src/properties/properties_internal.h | 34 + src/properties/spectral.c | 431 + src/properties/trees.c | 746 + src/properties/triangles.c | 896 + src/properties/triangles_template.h | 141 + src/properties/triangles_template1.h | 97 + src/random/random.c | 2240 ++ src/random/random_internal.h | 38 + src/random/rng_glibc2.c | 144 + src/random/rng_mt19937.c | 179 + src/random/rng_pcg32.c | 124 + src/random/rng_pcg64.c | 138 + src/version.c | 57 + tests/CMakeLists.txt | 1049 + tests/benchmarks/bench.h | 62 + tests/benchmarks/connectivity.c | 92 + tests/benchmarks/erdos_renyi.c | 118 + tests/benchmarks/graphicality.c | 127 + .../igraph_average_path_length_unweighted.c | 78 + tests/benchmarks/igraph_betweenness.c | 66 + .../benchmarks/igraph_betweenness_weighted.c | 146 + tests/benchmarks/igraph_cliques.c | 37 + tests/benchmarks/igraph_closeness_weighted.c | 146 + tests/benchmarks/igraph_coloring.c | 39 + tests/benchmarks/igraph_decompose.c | 39 + tests/benchmarks/igraph_degree.c | 106 + .../benchmarks/igraph_degree_sequence_game.c | 103 + tests/benchmarks/igraph_distances.c | 211 + tests/benchmarks/igraph_ecc.c | 158 + .../igraph_induced_subgraph_edges.c | 77 + tests/benchmarks/igraph_layout_umap.c | 70 + tests/benchmarks/igraph_matrix_transpose.c | 40 + tests/benchmarks/igraph_maximal_cliques.c | 58 + tests/benchmarks/igraph_neighborhood.c | 474 + tests/benchmarks/igraph_pagerank.c | 101 + tests/benchmarks/igraph_pagerank_weighted.c | 120 + tests/benchmarks/igraph_power_law_fit.c | 154 + tests/benchmarks/igraph_qsort.c | 49 + tests/benchmarks/igraph_random_walk.c | 183 + tests/benchmarks/igraph_strength.c | 127 + tests/benchmarks/igraph_transitivity.c | 158 + tests/benchmarks/igraph_tree_game.c | 90 + tests/benchmarks/igraph_vertex_connectivity.c | 60 + tests/benchmarks/igraph_voronoi.c | 156 + tests/benchmarks/inc_vs_adj.c | 456 + tests/benchmarks/intersection.c | 69 + tests/benchmarks/lad.c | 50 + tests/regression/bug-1033045.c | 44 + tests/regression/bug-1033045.out | 11 + tests/regression/bug-1149658.c | 48 + tests/regression/bug_1760.c | 159 + tests/regression/bug_1760.out | 67 + tests/regression/bug_1814.c | 75 + tests/regression/bug_1814.out | 135 + tests/regression/bug_1970.c | 44 + tests/regression/bug_1970.graphml | 1 + tests/regression/bug_2150.c | 47 + tests/regression/bug_2497.c | 48 + tests/regression/bug_2497.gml | 4 + tests/regression/bug_2497.out | 13 + tests/regression/bug_2506.c | 68 + tests/regression/bug_2506_1.graphml | 4 + tests/regression/bug_2506_2.graphml | 1 + tests/regression/bug_2506_3.graphml | 1 + tests/regression/bug_2517.c | 44 + tests/regression/bug_2517.out | 4 + tests/regression/cattr_bool_bug.c | 73 + tests/regression/cattr_bool_bug.graphml | 76 + tests/regression/cattr_bool_bug2.c | 63 + tests/regression/cattr_bool_bug2.graphml | 7 + .../igraph_layout_kamada_kawai_3d_bug_1462.c | 66 + ...igraph_layout_kamada_kawai_3d_bug_1462.out | 2 + .../igraph_layout_reingold_tilford_bug_879.c | 56 + .../igraph_layout_reingold_tilford_bug_879.in | 15 + ...igraph_layout_reingold_tilford_bug_879.out | 16 + .../igraph_read_graph_gml_invalid_inputs.c | 74 + ...igraph_read_graph_graphml_invalid_inputs.c | 80 + ...raph_read_graph_graphml_invalid_inputs.out | 0 .../igraph_read_graph_pajek_invalid_inputs.c | 70 + tests/regression/invalid1.gml | 463 + tests/regression/invalid1.graphml | 18 + tests/regression/invalid2.gml | 1 + tests/regression/invalid2.graphml | 26 + tests/regression/invalid3.graphml | 0 tests/regression/invalid4.gml | 1 + tests/regression/invalid4.graphml | Bin 0 -> 92 bytes tests/regression/invalid5.gml | 4 + tests/regression/invalid5.graphml | 30 + tests/regression/invalid6.gml | 1 + tests/regression/invalid_pajek1.net | 1 + tests/regression/invalid_pajek2.net | 3 + tests/regression/invalid_pajek3.net | 2 + tests/unit/2wheap.c | 172 + tests/unit/VF2-compat.c | 258 + tests/unit/adj.c | 309 + tests/unit/adj.out | 285 + tests/unit/adjlist.c | 348 + tests/unit/adjlist.out | 692 + tests/unit/ak-4102.max | 24623 ++++++++++++++++ tests/unit/all_almost_e.c | 87 + tests/unit/all_shortest_paths.c | 162 + tests/unit/all_shortest_paths.out | 125 + tests/unit/assortativity.c | 362 + tests/unit/assortativity.out | 33 + tests/unit/bfs.c | 150 + tests/unit/bfs.out | 13 + tests/unit/bfs_simple.c | 78 + tests/unit/bfs_simple.out | 9 + tests/unit/bipartite.net | 26 + tests/unit/bitset.c | 405 + tests/unit/bitset.out | 137 + tests/unit/bliss_automorphisms.c | 57 + tests/unit/bliss_automorphisms.out | 8 + tests/unit/cattributes5.c | 98 + tests/unit/cattributes5.out | 243 + tests/unit/cattributes6.c | 323 + tests/unit/cattributes6.out | 152 + tests/unit/centralization.c | 74 + tests/unit/centralization.out | 5 + tests/unit/cmp_epsilon.c | 77 + tests/unit/community_indexing.c | 88 + tests/unit/community_label_propagation.c | 116 + tests/unit/community_label_propagation2.c | 86 + tests/unit/community_label_propagation2.out | 3 + tests/unit/community_label_propagation3.c | 75 + tests/unit/community_leiden.c | 200 + tests/unit/community_leiden.out | 45 + tests/unit/community_walktrap.c | 204 + tests/unit/community_walktrap.out | 43 + tests/unit/components.c | 97 + tests/unit/components.out | 90 + tests/unit/constructor-failure.c | 210 + tests/unit/coreness.c | 232 + tests/unit/coreness.out | 24 + tests/unit/cutheap.c | 50 + tests/unit/cutheap.out | 1 + tests/unit/cycle_bases.c | 184 + tests/unit/cycle_bases.out | 78 + tests/unit/d_indheap.c | 103 + tests/unit/d_indheap.out | 12 + tests/unit/dgemv.c | 103 + tests/unit/dgemv.out | 23 + tests/unit/edge_selectors.c | 143 + tests/unit/edge_selectors.out | 36 + tests/unit/efficiency.c | 153 + tests/unit/efficiency.out | 90 + tests/unit/empty | 0 tests/unit/erdos_renyi_game_gnm.c | 116 + tests/unit/erdos_renyi_game_gnp.c | 173 + tests/unit/error_macros.c | 71 + tests/unit/error_macros.out | 4 + tests/unit/expand_path_to_pairs.c | 61 + tests/unit/expand_path_to_pairs.out | 4 + tests/unit/fatal_handler.c | 39 + tests/unit/fatal_handler.out | 3 + tests/unit/foreign_empty.c | 91 + tests/unit/full.c | 68 + tests/unit/full.out | 326 + tests/unit/gen2wheap.c | 123 + tests/unit/gen2wheap.out | 37 + tests/unit/global_transitivity.c | 113 + tests/unit/global_transitivity.out | 17 + tests/unit/glpk_error.c | 73 + tests/unit/gml.c | 56 + tests/unit/gml.out | 79 + tests/unit/graph1.gml | 44 + tests/unit/graph2.gml | 11 + tests/unit/graph3.gml | 4 + tests/unit/graphlets.c | 150 + tests/unit/graphlets.out | 111 + tests/unit/graphml-default-attrs.xml | 25 + tests/unit/graphml-hsa05010.xml | 379 + tests/unit/graphml-lenient.xml | 10 + tests/unit/graphml-namespace.xml | 11 + tests/unit/graphml-whitespace.xml | 51 + tests/unit/graphml-yed.xml | 177 + tests/unit/harmonic_centrality.c | 138 + tests/unit/harmonic_centrality.out | 33 + tests/unit/heap.c | 145 + tests/unit/heap.out | 16 + tests/unit/hub_and_authority.c | 148 + tests/unit/hub_and_authority.out | 96 + tests/unit/igraph_add_edges.c | 74 + tests/unit/igraph_add_edges.out | 3 + tests/unit/igraph_add_vertices.c | 61 + tests/unit/igraph_adhesion.c | 49 + tests/unit/igraph_adjacency.c | 257 + tests/unit/igraph_adjacency.out | 453 + .../igraph_adjacency_spectral_embedding.c | 73 + .../igraph_adjacency_spectral_embedding.out | 29 + tests/unit/igraph_adjacent_triangles.c | 75 + tests/unit/igraph_adjacent_triangles.out | 12 + tests/unit/igraph_adjlist_init_complementer.c | 45 + .../unit/igraph_adjlist_init_complementer.out | 21 + tests/unit/igraph_adjlist_simplify.c | 47 + tests/unit/igraph_adjlist_simplify.out | 19 + tests/unit/igraph_all_st_cuts.c | 404 + tests/unit/igraph_all_st_cuts.out | 98 + tests/unit/igraph_all_st_mincuts.c | 191 + tests/unit/igraph_all_st_mincuts.out | 132 + tests/unit/igraph_almost_equals.c | 191 + tests/unit/igraph_are_connected.c | 69 + tests/unit/igraph_arpack_rnsolve.c | 233 + tests/unit/igraph_arpack_rnsolve.out | 41 + tests/unit/igraph_arpack_unpack_complex.c | 103 + tests/unit/igraph_arpack_unpack_complex.out | 125 + tests/unit/igraph_array.c | 72 + tests/unit/igraph_array.out | 37 + tests/unit/igraph_atlas.c | 30 + .../igraph_attribute_combination_remove.c | 71 + .../igraph_attribute_combination_remove.out | 18 + tests/unit/igraph_average_path_length.c | 80 + tests/unit/igraph_average_path_length.out | 35 + .../igraph_average_path_length_dijkstra.c | 99 + .../igraph_average_path_length_dijkstra.out | 34 + tests/unit/igraph_barabasi_aging_game.c | 146 + tests/unit/igraph_barabasi_aging_game.out | 72 + tests/unit/igraph_barabasi_game.c | 70 + tests/unit/igraph_betweenness.c | 318 + tests/unit/igraph_betweenness.out | 111 + tests/unit/igraph_betweenness_subset.c | 362 + tests/unit/igraph_betweenness_subset.out | 72 + tests/unit/igraph_biadjacency.c | 99 + tests/unit/igraph_biadjacency.out | 115 + tests/unit/igraph_biconnected_components.c | 63 + tests/unit/igraph_biconnected_components.out | 23 + tests/unit/igraph_bipartite_create.c | 40 + tests/unit/igraph_bipartite_game.c | 165 + tests/unit/igraph_bipartite_projection.c | 183 + tests/unit/igraph_blas_dgemm.c | 81 + tests/unit/igraph_blas_dgemm.out | 26 + tests/unit/igraph_bridges.c | 59 + tests/unit/igraph_bridges.out | 6 + tests/unit/igraph_callaway_traits_game.c | 99 + tests/unit/igraph_chung_lu_game.c | 178 + tests/unit/igraph_circulant.c | 178 + tests/unit/igraph_cited_type_game.c | 112 + tests/unit/igraph_cited_type_game.out | 40 + tests/unit/igraph_citing_cited_type_game.c | 87 + tests/unit/igraph_citing_cited_type_game.out | 14 + tests/unit/igraph_clique_size_hist.c | 57 + tests/unit/igraph_clique_size_hist.out | 10 + tests/unit/igraph_closeness.c | 345 + tests/unit/igraph_closeness.out | 152 + tests/unit/igraph_cohesion.c | 49 + tests/unit/igraph_cohesive_blocks.c | 125 + tests/unit/igraph_cohesive_blocks.out | 46 + tests/unit/igraph_coloring.c | 228 + tests/unit/igraph_coloring.out | 94 + tests/unit/igraph_community_eb_get_merges.c | 127 + tests/unit/igraph_community_eb_get_merges.out | 47 + .../unit/igraph_community_edge_betweenness.c | 196 + .../igraph_community_edge_betweenness.out | 4 + tests/unit/igraph_community_fastgreedy.c | 219 + tests/unit/igraph_community_fastgreedy.out | 35 + .../unit/igraph_community_fluid_communities.c | 92 + .../igraph_community_fluid_communities.out | 0 tests/unit/igraph_community_infomap.c | 269 + tests/unit/igraph_community_infomap.out | 18 + .../igraph_community_leading_eigenvector2.c | 99 + .../igraph_community_leading_eigenvector2.out | 7 + tests/unit/igraph_community_voronoi.c | 120 + tests/unit/igraph_community_voronoi.out | 27 + tests/unit/igraph_compare_communities.c | 101 + tests/unit/igraph_compare_communities.out | 27 + tests/unit/igraph_complex.c | 184 + tests/unit/igraph_connect_neighborhood.c | 74 + tests/unit/igraph_connect_neighborhood.out | 124 + tests/unit/igraph_constraint.c | 104 + tests/unit/igraph_constraint.out | 27 + tests/unit/igraph_contract_vertices.c | 117 + tests/unit/igraph_contract_vertices.out | 73 + tests/unit/igraph_convergence_degree.c | 57 + tests/unit/igraph_convergence_degree.out | 2 + tests/unit/igraph_convex_hull.c | 159 + tests/unit/igraph_convex_hull.out | 39 + tests/unit/igraph_correlated_game.c | 45 + tests/unit/igraph_correlated_pair_game.c | 46 + tests/unit/igraph_correlated_pair_game.out | 8 + tests/unit/igraph_count_multiple.c | 37 + tests/unit/igraph_count_multiple.out | 2 + tests/unit/igraph_create.c | 40 + tests/unit/igraph_decompose_strong.c | 63 + tests/unit/igraph_decompose_strong.out | 21 + tests/unit/igraph_degree.c | 106 + tests/unit/igraph_degree.out | 6 + tests/unit/igraph_degree_sequence_game.c | 246 + tests/unit/igraph_delete_edges.c | 56 + tests/unit/igraph_delete_vertices.c | 32 + tests/unit/igraph_density.c | 155 + tests/unit/igraph_density.out | 29 + .../igraph_deterministic_optimal_imitation.c | 57 + tests/unit/igraph_diameter.c | 94 + tests/unit/igraph_diameter.out | 17 + tests/unit/igraph_diameter_dijkstra.c | 113 + tests/unit/igraph_diameter_dijkstra.out | 79 + tests/unit/igraph_disjoint_union.c | 69 + tests/unit/igraph_disjoint_union.out | 42 + tests/unit/igraph_distances_floyd_warshall.c | 119 + .../unit/igraph_distances_floyd_warshall.out | 77 + .../igraph_distances_floyd_warshall_speedup.c | 126 + ...graph_distances_floyd_warshall_speedup.out | 77 + tests/unit/igraph_distances_johnson.c | 76 + tests/unit/igraph_distances_johnson.out | 19 + tests/unit/igraph_diversity.c | 87 + tests/unit/igraph_diversity.out | 8 + tests/unit/igraph_dominator_tree.c | 132 + tests/unit/igraph_dominator_tree.out | 40 + tests/unit/igraph_dot_product_game.c | 54 + tests/unit/igraph_dot_product_game.out | 16 + tests/unit/igraph_dyad_census.c | 63 + tests/unit/igraph_dyad_census.out | 15 + tests/unit/igraph_ecc.c | 219 + tests/unit/igraph_ecc.out | 79 + tests/unit/igraph_eccentricity.c | 86 + tests/unit/igraph_eccentricity.out | 23 + tests/unit/igraph_eccentricity_dijkstra.c | 117 + tests/unit/igraph_eccentricity_dijkstra.out | 33 + tests/unit/igraph_edge_betweenness.c | 196 + tests/unit/igraph_edge_betweenness.out | 28 + tests/unit/igraph_edge_betweenness_subset.c | 315 + tests/unit/igraph_edge_betweenness_subset.out | 27 + tests/unit/igraph_edge_disjoint_paths.c | 52 + tests/unit/igraph_edges.c | 53 + tests/unit/igraph_edges.out | 6 + tests/unit/igraph_eigen_matrix.c | 134 + tests/unit/igraph_eigen_matrix.out | 11 + tests/unit/igraph_eigen_matrix2.c | 117 + tests/unit/igraph_eigen_matrix2.out | 11 + tests/unit/igraph_eigen_matrix3.c | 98 + tests/unit/igraph_eigen_matrix3.out | 0 tests/unit/igraph_eigen_matrix4.c | 100 + tests/unit/igraph_eigen_matrix4.out | 2 + tests/unit/igraph_eigen_matrix_symmetric.c | 127 + tests/unit/igraph_eigen_matrix_symmetric.out | 4 + .../igraph_eigen_matrix_symmetric_arpack.c | 128 + .../igraph_eigen_matrix_symmetric_arpack.out | 4 + tests/unit/igraph_eigenvector_centrality.c | 119 + tests/unit/igraph_eigenvector_centrality.out | 51 + tests/unit/igraph_empty.c | 61 + tests/unit/igraph_es_all_between.c | 202 + tests/unit/igraph_es_path.c | 75 + tests/unit/igraph_establishment_game.c | 88 + tests/unit/igraph_eulerian_cycle.c | 183 + tests/unit/igraph_eulerian_cycle.out | 32 + tests/unit/igraph_eulerian_path.c | 443 + tests/unit/igraph_eulerian_path.out | 95 + tests/unit/igraph_extended_chordal_ring.c | 74 + .../unit/igraph_feedback_arc_set_undirected.c | 59 + tests/unit/igraph_forest_fire_game.c | 67 + tests/unit/igraph_forest_fire_game.out | 29 + tests/unit/igraph_from_prufer.c | 42 + tests/unit/igraph_from_prufer.out | 25 + tests/unit/igraph_full_citation.c | 57 + tests/unit/igraph_full_multipartite.c | 158 + tests/unit/igraph_full_multipartite.out | 197 + tests/unit/igraph_generalized_petersen.c | 55 + tests/unit/igraph_get_adjacency.c | 200 + tests/unit/igraph_get_adjacency.out | 168 + tests/unit/igraph_get_adjacency_sparse.c | 210 + tests/unit/igraph_get_adjacency_sparse.out | 168 + .../igraph_get_all_shortest_paths_dijkstra.c | 184 + ...igraph_get_all_shortest_paths_dijkstra.out | 37 + tests/unit/igraph_get_all_simple_paths.c | 61 + tests/unit/igraph_get_all_simple_paths.out | 12 + tests/unit/igraph_get_biadjacency.c | 89 + tests/unit/igraph_get_biadjacency.out | 37 + tests/unit/igraph_get_eid.c | 41 + tests/unit/igraph_get_isomorphisms_vf2.c | 137 + tests/unit/igraph_get_isomorphisms_vf2.out | 94 + tests/unit/igraph_get_k_shortest_paths.c | 159 + tests/unit/igraph_get_k_shortest_paths.out | 219 + tests/unit/igraph_get_laplacian.c | 116 + tests/unit/igraph_get_laplacian.out | 113 + tests/unit/igraph_get_shortest_path_astar.c | 231 + tests/unit/igraph_get_shortest_path_astar.out | 18 + .../igraph_get_shortest_path_bellman_ford.c | 61 + .../igraph_get_shortest_path_bellman_ford.out | 5 + tests/unit/igraph_get_shortest_paths2.c | 73 + tests/unit/igraph_get_shortest_paths2.out | 16 + .../igraph_get_shortest_paths_bellman_ford.c | 215 + ...igraph_get_shortest_paths_bellman_ford.out | 32 + .../unit/igraph_get_shortest_paths_dijkstra.c | 172 + .../igraph_get_shortest_paths_dijkstra.out | 12 + tests/unit/igraph_get_stochastic.c | 98 + tests/unit/igraph_get_stochastic.out | 64 + tests/unit/igraph_get_stochastic_sparse.c | 50 + tests/unit/igraph_get_stochastic_sparse.out | 19 + tests/unit/igraph_get_subisomorphisms_vf2.c | 151 + tests/unit/igraph_get_subisomorphisms_vf2.out | 96 + tests/unit/igraph_gomory_hu_tree.c | 222 + tests/unit/igraph_graph_center.c | 277 + tests/unit/igraph_graph_center.out | 99 + tests/unit/igraph_graph_power.c | 120 + tests/unit/igraph_graph_power.out | 210 + tests/unit/igraph_grg_game.c | 42 + tests/unit/igraph_growing_random_game.c | 66 + tests/unit/igraph_has_mutual.c | 82 + tests/unit/igraph_hexagonal_lattice.c | 102 + tests/unit/igraph_hexagonal_lattice.out | 374 + tests/unit/igraph_hrg.c | 100 + tests/unit/igraph_hrg2.c | 98 + tests/unit/igraph_hrg3.c | 99 + tests/unit/igraph_hrg_create.c | 79 + tests/unit/igraph_hrg_create.out | 17 + tests/unit/igraph_hsbm_game.c | 74 + tests/unit/igraph_hsbm_game.out | 79 + tests/unit/igraph_hsbm_list_game.c | 95 + tests/unit/igraph_hsbm_list_game.out | 79 + tests/unit/igraph_i_incident.c | 109 + tests/unit/igraph_i_incident.out | 33 + tests/unit/igraph_i_layout_sphere.c | 87 + tests/unit/igraph_i_neighbors.c | 122 + tests/unit/igraph_i_neighbors.out | 41 + tests/unit/igraph_i_umap_fit_ab.c | 53 + tests/unit/igraph_i_umap_fit_ab.o | 8 + tests/unit/igraph_induced_subgraph.c | 112 + tests/unit/igraph_induced_subgraph.out | 43 + tests/unit/igraph_induced_subgraph_edges.c | 54 + tests/unit/igraph_induced_subgraph_edges.out | 4 + tests/unit/igraph_induced_subgraph_map.c | 66 + tests/unit/igraph_induced_subgraph_map.out | 20 + tests/unit/igraph_intersection.c | 63 + tests/unit/igraph_intersection.out | 29 + tests/unit/igraph_is_acyclic.c | 111 + tests/unit/igraph_is_biconnected.c | 135 + tests/unit/igraph_is_bigraphical.c | 54 + tests/unit/igraph_is_bigraphical.out | 32 + tests/unit/igraph_is_bipartite.c | 79 + tests/unit/igraph_is_chordal.c | 99 + tests/unit/igraph_is_chordal.out | 129 + tests/unit/igraph_is_clique.c | 145 + tests/unit/igraph_is_complete.c | 207 + tests/unit/igraph_is_connected.c | 102 + tests/unit/igraph_is_dag.c | 126 + tests/unit/igraph_is_eulerian.c | 207 + tests/unit/igraph_is_eulerian.out | 34 + tests/unit/igraph_is_forest.c | 145 + tests/unit/igraph_is_forest.out | 36 + tests/unit/igraph_is_forest2.c | 109 + tests/unit/igraph_is_graphical.c | 284 + tests/unit/igraph_is_graphical.out | 168 + tests/unit/igraph_is_mutual.c | 69 + tests/unit/igraph_is_mutual.out | 16 + tests/unit/igraph_is_same_graph.c | 84 + tests/unit/igraph_is_separator.c | 293 + tests/unit/igraph_is_tree.c | 140 + tests/unit/igraph_isomorphic.c | 72 + tests/unit/igraph_isomorphic.out | 7 + tests/unit/igraph_isomorphic_bliss.c | 150 + tests/unit/igraph_isomorphic_bliss.out | 42 + tests/unit/igraph_isomorphic_vf2.c | 226 + tests/unit/igraph_join.c | 78 + tests/unit/igraph_join.out | 54 + tests/unit/igraph_joint_degree_distribution.c | 312 + .../unit/igraph_joint_degree_distribution.out | 26 + tests/unit/igraph_joint_type_distribution.c | 163 + tests/unit/igraph_joint_type_distribution.out | 26 + tests/unit/igraph_k_regular_game.c | 200 + tests/unit/igraph_k_regular_game.out | 16 + tests/unit/igraph_k_shortest_paths.out | 0 tests/unit/igraph_kautz.c | 61 + tests/unit/igraph_lapack_dgeev.c | 224 + tests/unit/igraph_lapack_dgeevx.c | 206 + tests/unit/igraph_lapack_dgehrd.c | 81 + tests/unit/igraph_lapack_dgehrd.out | 0 tests/unit/igraph_lapack_dgetrf.c | 74 + tests/unit/igraph_lapack_dgetrf.out | 63 + tests/unit/igraph_lapack_dgetrs.c | 122 + tests/unit/igraph_lapack_dgetrs.out | 86 + tests/unit/igraph_lapack_dsyevr.c | 216 + tests/unit/igraph_lastcit_game.c | 86 + tests/unit/igraph_lastcit_game.out | 24 + tests/unit/igraph_layout_bipartite.c | 110 + tests/unit/igraph_layout_bipartite.out | 42 + tests/unit/igraph_layout_davidson_harel.c | 184 + tests/unit/igraph_layout_davidson_harel.out | 3 + tests/unit/igraph_layout_drl.c | 99 + tests/unit/igraph_layout_drl_3d.c | 64 + tests/unit/igraph_layout_drl_3d.out | 2 + .../unit/igraph_layout_fruchterman_reingold.c | 128 + .../igraph_layout_fruchterman_reingold.out | 14 + .../igraph_layout_fruchterman_reingold_3d.c | 130 + .../igraph_layout_fruchterman_reingold_3d.out | 14 + tests/unit/igraph_layout_gem.c | 120 + tests/unit/igraph_layout_gem.out | 13 + tests/unit/igraph_layout_graphopt.c | 98 + tests/unit/igraph_layout_graphopt.out | 17 + tests/unit/igraph_layout_grid.c | 72 + tests/unit/igraph_layout_grid.out | 95 + tests/unit/igraph_layout_kamada_kawai.c | 154 + tests/unit/igraph_layout_kamada_kawai.out | 17 + tests/unit/igraph_layout_lgl.c | 109 + tests/unit/igraph_layout_lgl.out | 4 + tests/unit/igraph_layout_mds.c | 101 + tests/unit/igraph_layout_mds.out | 11 + tests/unit/igraph_layout_merge.c | 83 + tests/unit/igraph_layout_merge2.c | 87 + tests/unit/igraph_layout_merge2.out | 13 + tests/unit/igraph_layout_merge3.c | 46 + tests/unit/igraph_layout_random_3d.c | 54 + tests/unit/igraph_layout_random_3d.out | 15 + .../igraph_layout_reingold_tilford_circular.c | 116 + ...graph_layout_reingold_tilford_circular.out | 39 + .../igraph_layout_reingold_tilford_extended.c | 56 + ...igraph_layout_reingold_tilford_extended.in | 4 + tests/unit/igraph_layout_sphere.c | 47 + tests/unit/igraph_layout_sphere.out | 22 + tests/unit/igraph_layout_star.c | 70 + tests/unit/igraph_layout_star.out | 24 + tests/unit/igraph_layout_sugiyama.c | 101 + tests/unit/igraph_layout_sugiyama.out | 87 + tests/unit/igraph_layout_umap.c | 332 + tests/unit/igraph_layout_umap.out | 26 + .../unit/igraph_le_community_to_membership.c | 111 + .../igraph_le_community_to_membership.out | 17 + tests/unit/igraph_linegraph.c | 81 + tests/unit/igraph_list_triangles.c | 61 + tests/unit/igraph_list_triangles.out | 12 + tests/unit/igraph_local_scan_k_ecount.c | 107 + tests/unit/igraph_local_scan_k_ecount.out | 40 + tests/unit/igraph_local_scan_k_ecount_them.c | 135 + .../unit/igraph_local_scan_k_ecount_them.out | 58 + tests/unit/igraph_local_scan_subset_ecount.c | 101 + .../unit/igraph_local_scan_subset_ecount.out | 24 + tests/unit/igraph_local_transitivity.c | 246 + tests/unit/igraph_local_transitivity.out | 37 + tests/unit/igraph_maxflow.c | 240 + tests/unit/igraph_maxflow.out | 5 + tests/unit/igraph_maximal_cliques.c | 151 + tests/unit/igraph_maximal_cliques.out | 15 + tests/unit/igraph_maximal_cliques2.c | 81 + tests/unit/igraph_maximal_cliques2.out | 20 + tests/unit/igraph_maximal_cliques3.c | 60 + tests/unit/igraph_maximal_cliques3.out | 6 + tests/unit/igraph_maximal_cliques4.c | 90 + tests/unit/igraph_maximal_cliques4.out | 108 + tests/unit/igraph_maximal_cliques_file.c | 45 + tests/unit/igraph_maximal_cliques_file.out | 13 + .../unit/igraph_maximum_bipartite_matching.c | 180 + tests/unit/igraph_mean_degree.c | 59 + tests/unit/igraph_minimum_size_separators.c | 169 + tests/unit/igraph_minimum_size_separators.out | 30 + tests/unit/igraph_modularity.c | 117 + tests/unit/igraph_modularity.out | 9 + tests/unit/igraph_modularity_matrix.c | 129 + tests/unit/igraph_modularity_matrix.out | 48 + tests/unit/igraph_moran_process.c | 224 + tests/unit/igraph_motifs_randesu.c | 69 + tests/unit/igraph_motifs_randesu.out | 47 + tests/unit/igraph_motifs_randesu_estimate.c | 97 + tests/unit/igraph_motifs_randesu_estimate.out | 27 + tests/unit/igraph_motifs_randesu_no.c | 79 + tests/unit/igraph_motifs_randesu_no.out | 19 + tests/unit/igraph_neighborhood.c | 85 + tests/unit/igraph_neighborhood.out | 59 + tests/unit/igraph_neighborhood_graphs.c | 102 + tests/unit/igraph_neighborhood_graphs.out | 173 + tests/unit/igraph_neighborhood_size.c | 86 + tests/unit/igraph_neighborhood_size.out | 16 + tests/unit/igraph_neighbors.c | 39 + tests/unit/igraph_pagerank.c | 401 + tests/unit/igraph_pagerank.out | 52 + tests/unit/igraph_path_length_hist.c | 68 + tests/unit/igraph_path_length_hist.out | 24 + tests/unit/igraph_perfect.c | 135 + tests/unit/igraph_permute_vertices.c | 66 + tests/unit/igraph_permute_vertices.out | 23 + tests/unit/igraph_power_law_fit.c | 331 + tests/unit/igraph_power_law_fit.out | 37 + tests/unit/igraph_preference_game.c | 251 + tests/unit/igraph_progress_handler_stderr.c | 27 + tests/unit/igraph_pseudo_diameter.c | 131 + tests/unit/igraph_pseudo_diameter.out | 108 + tests/unit/igraph_pseudo_diameter_dijkstra.c | 153 + .../unit/igraph_pseudo_diameter_dijkstra.out | 128 + tests/unit/igraph_psumtree.c | 218 + tests/unit/igraph_qsort.c | 62 + tests/unit/igraph_qsort.out | 1 + tests/unit/igraph_qsort_r.c | 72 + tests/unit/igraph_qsort_r.out | 1 + tests/unit/igraph_random_sample.c | 65 + tests/unit/igraph_random_walk.c | 235 + tests/unit/igraph_random_walk.out | 42 + tests/unit/igraph_read_graph_graphdb.c | 58 + tests/unit/igraph_read_graph_graphdb.out | 8 + tests/unit/igraph_read_graph_graphml.c | 213 + tests/unit/igraph_read_graph_graphml.out | 67 + ...igraph_realize_bipartite_degree_sequence.c | 323 + ...raph_realize_bipartite_degree_sequence.out | 148 + tests/unit/igraph_realize_degree_sequence.c | 138 + tests/unit/igraph_realize_degree_sequence.out | 974 + tests/unit/igraph_recent_degree_aging_game.c | 93 + .../unit/igraph_recent_degree_aging_game.out | 43 + tests/unit/igraph_recent_degree_game.c | 73 + tests/unit/igraph_recent_degree_game.out | 33 + tests/unit/igraph_residual_graph.c | 62 + tests/unit/igraph_reverse_edges.c | 46 + tests/unit/igraph_rewire.c | 84 + tests/unit/igraph_rewire_directed_edges.c | 88 + tests/unit/igraph_rewire_directed_edges.out | 14 + tests/unit/igraph_rng_get_integer.c | 210 + tests/unit/igraph_rng_get_integer.out | 33 + tests/unit/igraph_roulette_wheel_imitation.c | 66 + tests/unit/igraph_running_mean.c | 60 + tests/unit/igraph_running_mean.out | 8 + tests/unit/igraph_sample_dirichlet.c | 83 + tests/unit/igraph_sample_dirichlet.out | 10 + tests/unit/igraph_sample_sphere.c | 66 + tests/unit/igraph_sbm_game.c | 119 + tests/unit/igraph_sbm_game.out | 69 + tests/unit/igraph_set_progress_handler.c | 46 + tests/unit/igraph_set_progress_handler.out | 7 + tests/unit/igraph_similarity.c | 205 + tests/unit/igraph_similarity.out | 38 + ...graph_simple_interconnected_islands_game.c | 93 + ...aph_simple_interconnected_islands_game.out | 63 + tests/unit/igraph_sir.c | 96 + tests/unit/igraph_sir.out | 40 + tests/unit/igraph_solve_lsap.c | 67 + tests/unit/igraph_solve_lsap.out | 9 + tests/unit/igraph_spanner.c | 203 + tests/unit/igraph_sparsemat2.c | 152 + tests/unit/igraph_sparsemat2.out | 0 tests/unit/igraph_sparsemat5.c | 401 + tests/unit/igraph_sparsemat5.out | 159 + tests/unit/igraph_sparsemat9.c | 88 + tests/unit/igraph_sparsemat_droptol.c | 63 + tests/unit/igraph_sparsemat_droptol.out | 16 + tests/unit/igraph_sparsemat_fkeep.c | 80 + tests/unit/igraph_sparsemat_fkeep.out | 15 + .../igraph_sparsemat_getelements_sorted.c | 88 + .../igraph_sparsemat_getelements_sorted.out | 19 + tests/unit/igraph_sparsemat_is_symmetric.c | 72 + tests/unit/igraph_sparsemat_iterator_idx.c | 56 + tests/unit/igraph_sparsemat_minmax.c | 248 + tests/unit/igraph_sparsemat_minmax.out | 32 + tests/unit/igraph_sparsemat_nonzero_storage.c | 69 + tests/unit/igraph_sparsemat_normalize.c | 106 + tests/unit/igraph_sparsemat_normalize.out | 41 + tests/unit/igraph_sparsemat_view.c | 47 + tests/unit/igraph_sparsemat_view.out | 6 + tests/unit/igraph_sparsemat_which_minmax.c | 283 + tests/unit/igraph_sparsemat_which_minmax.out | 23 + tests/unit/igraph_split_join_distance.c | 79 + tests/unit/igraph_split_join_distance.out | 13 + tests/unit/igraph_square_lattice.c | 232 + tests/unit/igraph_st_edge_connectivity.c | 39 + tests/unit/igraph_st_mincut.c | 70 + tests/unit/igraph_st_mincut.out | 6 + tests/unit/igraph_st_mincut_value.c | 42 + tests/unit/igraph_st_vertex_connectivity.c | 86 + tests/unit/igraph_st_vertex_connectivity.out | 12 + tests/unit/igraph_static_power_law_game.c | 113 + tests/unit/igraph_static_power_law_game.out | 15 + tests/unit/igraph_stochastic_imitation.c | 57 + tests/unit/igraph_strvector.c | 183 + tests/unit/igraph_strvector.out | 87 + tests/unit/igraph_subcomponent.c | 81 + tests/unit/igraph_subcomponent.out | 57 + tests/unit/igraph_subisomorphic.c | 64 + tests/unit/igraph_subisomorphic_lad.c | 196 + tests/unit/igraph_to_directed.c | 43 + tests/unit/igraph_to_directed.out | 61 + tests/unit/igraph_to_prufer.c | 172 + tests/unit/igraph_transitive_closure.c | 88 + tests/unit/igraph_transitive_closure.out | 92 + .../igraph_transitivity_avglocal_undirected.c | 77 + ...graph_transitivity_avglocal_undirected.out | 14 + tests/unit/igraph_transitivity_barrat.c | 132 + tests/unit/igraph_transitivity_barrat.out | 45 + tests/unit/igraph_tree_from_parent_vector.c | 77 + tests/unit/igraph_tree_from_parent_vector.out | 50 + tests/unit/igraph_triangular_lattice.c | 102 + tests/unit/igraph_triangular_lattice.out | 228 + tests/unit/igraph_trussness.c | 114 + tests/unit/igraph_trussness.out | 69 + tests/unit/igraph_turan.c | 168 + tests/unit/igraph_turan.out | 156 + tests/unit/igraph_unfold_tree.c | 82 + tests/unit/igraph_unfold_tree.out | 80 + tests/unit/igraph_union.c | 156 + tests/unit/igraph_union.out | 133 + tests/unit/igraph_vector_floor.c | 41 + tests/unit/igraph_vector_floor.out | 4 + tests/unit/igraph_vector_lex_cmp.c | 54 + tests/unit/igraph_vector_lex_cmp.out | 19 + tests/unit/igraph_vertex_disjoint_paths.c | 53 + tests/unit/igraph_voronoi.c | 134 + tests/unit/igraph_voronoi.out | 48 + tests/unit/igraph_weighted_adjacency.c | 340 + tests/unit/igraph_weighted_adjacency.out | 383 + tests/unit/igraph_weighted_cliques.c | 249 + tests/unit/igraph_weighted_cliques.out | 204 + tests/unit/igraph_wheel.c | 56 + tests/unit/igraph_wheel.out | 104 + tests/unit/igraph_widest_paths.c | 547 + tests/unit/igraph_widest_paths.out | 233 + tests/unit/igraph_write_graph_dimacs_flow.c | 69 + tests/unit/igraph_write_graph_dimacs_flow.out | 19 + tests/unit/igraph_write_graph_dot.c | 56 + tests/unit/igraph_write_graph_dot.out | 74 + tests/unit/igraph_write_graph_leda.c | 113 + tests/unit/igraph_write_graph_leda.out | 133 + tests/unit/inclist.c | 208 + tests/unit/inclist.out | 287 + tests/unit/input.dl | 104 + tests/unit/isoclasses.c | 68 + tests/unit/isoclasses.out | 4 + tests/unit/isoclasses2.c | 180 + tests/unit/isomorphism_test.c | 243 + tests/unit/isomorphism_test.out | 0 tests/unit/jdm.c | 322 + tests/unit/jdm.out | 139 + tests/unit/kary_tree.c | 57 + tests/unit/kary_tree.out | 61 + tests/unit/knn.c | 199 + tests/unit/knn.out | 61 + tests/unit/levc-stress.c | 71 + tests/unit/lineendings.c | 71 + tests/unit/lineendings.out | 44 + tests/unit/links.net | 16 + tests/unit/marked_queue.c | 66 + tests/unit/matrix.c | 178 + tests/unit/matrix.out | 44 + tests/unit/matrix2.c | 345 + tests/unit/matrix2.out | 167 + tests/unit/matrix3.c | 48 + tests/unit/matrix_complex.c | 94 + tests/unit/matrix_complex.out | 19 + tests/unit/maximal_cliques_callback.c | 106 + tests/unit/maximal_cliques_hist.c | 42 + tests/unit/maximal_cliques_hist.out | 1 + tests/unit/ncol.c | 73 + tests/unit/ncol.out | 11 + tests/unit/null_communities.c | 191 + tests/unit/overflow.c | 73 + tests/unit/pajek.c | 106 + tests/unit/pajek1.net | 21 + tests/unit/pajek2.c | 55 + tests/unit/pajek2.net | 1 + tests/unit/pajek2.out | 1 + tests/unit/pajek3.net | 21 + tests/unit/pajek4.net | 21 + tests/unit/pajek5.net | 21 + tests/unit/pajek6.net | 21 + tests/unit/pajek_arcslist.net | 40 + tests/unit/pajek_bip.net | 27 + tests/unit/pajek_bip2.net | 27 + tests/unit/pajek_bipartite.c | 47 + tests/unit/pajek_bipartite.out | 22 + tests/unit/pajek_bipartite2.c | 64 + tests/unit/pajek_bipartite2.out | 83 + tests/unit/pajek_edgeslist.net | 8 + tests/unit/pajek_signed.c | 48 + tests/unit/pajek_signed.net | 23 + tests/unit/pajek_signed.out | 56 + tests/unit/prop_caching.c | 159 + tests/unit/random_sampling.c | 292 + tests/unit/random_spanning_tree.c | 75 + tests/unit/reachability.c | 129 + tests/unit/reachability.out | 225 + tests/unit/ring.c | 52 + tests/unit/ring.out | 237 + ...g_init_destroy_max_bits_name_set_default.c | 133 + ...init_destroy_max_bits_name_set_default.out | 39 + tests/unit/rng_reproducibility.c | 38 + tests/unit/rng_reproducibility.out | 64 + tests/unit/set.c | 86 + tests/unit/set.out | 1 + tests/unit/si2_b06m_s20-bad1.A98 | Bin 0 -> 18 bytes tests/unit/si2_b06m_s20-bad2.A98 | Bin 0 -> 17 bytes tests/unit/si2_b06m_s20.A98 | Bin 0 -> 18 bytes tests/unit/simplify_and_colorize.c | 78 + tests/unit/simplify_and_colorize.out | 70 + tests/unit/single_target_shortest_path.c | 79 + tests/unit/single_target_shortest_path.out | 10 + tests/unit/spinglass.c | 347 + tests/unit/spinglass.out | 49 + tests/unit/stack.c | 94 + tests/unit/strvector_set_len_remove_print.c | 53 + tests/unit/strvector_set_len_remove_print.out | 8 + tests/unit/symmetric_tree.c | 111 + tests/unit/symmetric_tree.out | 220 + tests/unit/test.graphml | 55 + tests/unit/test_utilities.c | 610 + tests/unit/test_utilities.h | 174 + tests/unit/tls1.c | 53 + tests/unit/tls2.c | 244 + tests/unit/tls2.out | 23 + tests/unit/topological_sorting.c | 100 + tests/unit/topological_sorting.out | 2 + tests/unit/tree_game.c | 88 + tests/unit/triad_census.c | 56 + tests/unit/triad_census.out | 2 + tests/unit/trie.c | 138 + tests/unit/trie.out | 38 + tests/unit/utf8_with_bom.net | 18 + tests/unit/vector.c | 388 + tests/unit/vector.out | 67 + tests/unit/vector2.c | 136 + tests/unit/vector2.out | 19 + tests/unit/vector3.c | 53 + tests/unit/vector4.c | 112 + tests/unit/vector4.out | 28 + tests/unit/vector_list.c | 228 + tests/unit/vector_list.out | 157 + tests/unit/vector_ptr.c | 285 + tests/unit/vector_ptr_qsort_ind.out | 12 + tests/unit/vector_ptr_sort_ind.c | 81 + tests/unit/vector_qsort_ind.c | 64 + tests/unit/vector_qsort_ind.out | 4 + tests/unit/vertex_selectors.c | 116 + tests/unit/vertex_selectors.out | 39 + tests/unit/watts_strogatz_game.c | 125 + tests/unit/wikti_en_V_syn.elist | 8293 ++++++ tests/unit/zapsmall.c | 49 + tests/unit/zapsmall.out | 6 + tests/unit/zero_allocs.c | 37 + tools/removeexamples.py | 37 + tools/strip_licenses_from_examples.py | 67 + vendor/CMakeLists.txt | 7 + vendor/cs/CMakeLists.txt | 93 + vendor/cs/License.txt | 19 + vendor/cs/cs.h | 315 + vendor/cs/cs_add.c | 28 + vendor/cs/cs_amd.c | 364 + vendor/cs/cs_chol.c | 59 + vendor/cs/cs_cholsol.c | 26 + vendor/cs/cs_compress.c | 22 + vendor/cs/cs_counts.c | 61 + vendor/cs/cs_cumsum.c | 17 + vendor/cs/cs_dfs.c | 36 + vendor/cs/cs_dmperm.c | 144 + vendor/cs/cs_droptol.c | 9 + vendor/cs/cs_dropzeros.c | 9 + vendor/cs/cs_dupl.c | 34 + vendor/cs/cs_entry.c | 13 + vendor/cs/cs_ereach.c | 23 + vendor/cs/cs_etree.c | 30 + vendor/cs/cs_fkeep.c | 25 + vendor/cs/cs_gaxpy.c | 17 + vendor/cs/cs_happly.c | 19 + vendor/cs/cs_house.c | 30 + vendor/cs/cs_ipvec.c | 9 + vendor/cs/cs_leaf.c | 22 + vendor/cs/cs_load.c | 26 + vendor/cs/cs_lsolve.c | 18 + vendor/cs/cs_ltsolve.c | 18 + vendor/cs/cs_lu.c | 88 + vendor/cs/cs_lusol.c | 26 + vendor/cs/cs_malloc.c | 35 + vendor/cs/cs_maxtrans.c | 92 + vendor/cs/cs_multiply.c | 35 + vendor/cs/cs_norm.c | 16 + vendor/cs/cs_permute.c | 25 + vendor/cs/cs_pinv.c | 11 + vendor/cs/cs_post.c | 24 + vendor/cs/cs_print.c | 55 + vendor/cs/cs_pvec.c | 9 + vendor/cs/cs_qr.c | 74 + vendor/cs/cs_qrsol.c | 53 + vendor/cs/cs_randperm.c | 28 + vendor/cs/cs_reach.c | 19 + vendor/cs/cs_scatter.c | 22 + vendor/cs/cs_scc.c | 41 + vendor/cs/cs_schol.c | 26 + vendor/cs/cs_spsolve.c | 28 + vendor/cs/cs_sqr.c | 87 + vendor/cs/cs_symperm.c | 39 + vendor/cs/cs_tdfs.c | 24 + vendor/cs/cs_transpose.c | 25 + vendor/cs/cs_updown.c | 48 + vendor/cs/cs_usolve.c | 18 + vendor/cs/cs_util.c | 120 + vendor/cs/cs_utsolve.c | 18 + vendor/pcg/CMakeLists.txt | 21 + vendor/pcg/LICENSE.txt | 19 + vendor/pcg/pcg-advance-128.c | 61 + vendor/pcg/pcg-advance-64.c | 59 + vendor/pcg/pcg-output-128.c | 63 + vendor/pcg/pcg-output-32.c | 63 + vendor/pcg/pcg-output-64.c | 73 + vendor/pcg/pcg-rngs-128.c | 378 + vendor/pcg/pcg-rngs-64.c | 255 + vendor/pcg/pcg_variants.h | 2557 ++ 1780 files changed, 407072 insertions(+) create mode 100644 ACKNOWLEDGEMENTS.md create mode 100644 AUTHORS create mode 100644 CHANGELOG.md create mode 100644 CMakeLists.txt create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 CONTRIBUTORS.md create mode 100644 CONTRIBUTORS.txt create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 IGRAPH_VERSION create mode 100644 INSTALL create mode 100644 NEWS create mode 100644 ONEWS create mode 100644 README.md create mode 100644 SUPPORT.md create mode 100644 doc/CMakeLists.txt create mode 100644 doc/adjlist.xxml create mode 100644 doc/arpack.xxml create mode 100644 doc/attributes.xxml create mode 100644 doc/basicigraph.xxml create mode 100644 doc/bibdatabase.xml create mode 100644 doc/bipartite.xxml create mode 100644 doc/bitset.xxml create mode 100644 doc/c-docbook.re create mode 100644 doc/cliques.xxml create mode 100644 doc/coloring.xxml create mode 100644 doc/community.xxml create mode 100644 doc/cycles.xxml create mode 100644 doc/docbook.outlang create mode 100755 doc/doxrox.py create mode 100644 doc/dqueue.xxml create mode 100644 doc/embedding.xxml create mode 100644 doc/error.xxml create mode 100644 doc/fdl.xml create mode 100644 doc/flows.xxml create mode 100644 doc/foreign.xxml create mode 100644 doc/generators.xxml create mode 100644 doc/gpl.xml create mode 100644 doc/graphlets.xxml create mode 100644 doc/gtk-doc.xsl create mode 100644 doc/heap.xxml create mode 100644 doc/hrg.xxml create mode 100644 doc/html/home.png create mode 100644 doc/html/left.png create mode 100644 doc/html/right.png create mode 100644 doc/html/style.css create mode 100644 doc/html/toggle.js create mode 100644 doc/html/up.png create mode 100644 doc/igraph-docs.xml create mode 100644 doc/igraph.3 create mode 100644 doc/igraphlogo/igraph-white.svg.gz create mode 100644 doc/igraphlogo/igraph.svg.gz create mode 100644 doc/igraphlogo/igraph2.svg.gz create mode 100644 doc/installation.xml create mode 100644 doc/introduction.xml create mode 100644 doc/isomorphism.xxml create mode 100644 doc/iterators.xxml create mode 100644 doc/layout.xxml create mode 100644 doc/licenses.xml create mode 100644 doc/licenses/Licence_CeCILL-B_V1-en.txt create mode 100644 doc/licenses/Licence_CeCILL-B_V1-fr.txt create mode 100644 doc/licenses/gpl-2.0.txt create mode 100644 doc/licenses/gpl-3.0.txt create mode 100644 doc/licenses/lgpl-2.1.txt create mode 100644 doc/licenses/lgpl-3.0.txt create mode 100644 doc/matrix.xxml create mode 100644 doc/memory.xxml create mode 100644 doc/motifs.xxml create mode 100644 doc/nongraph.xxml create mode 100644 doc/operators.xxml create mode 100644 doc/pmt.xml create mode 100644 doc/progress.xxml create mode 100644 doc/psumtree.xxml create mode 100644 doc/random.xxml create mode 100644 doc/separators.xxml create mode 100644 doc/sparsemat.xxml create mode 100644 doc/spatialgames.xxml create mode 100644 doc/stack.xxml create mode 100644 doc/status.xxml create mode 100644 doc/structural.xxml create mode 100644 doc/strvector.xxml create mode 100644 doc/threading.xxml create mode 100644 doc/tutorial.xml create mode 100644 doc/vector.xxml create mode 100644 doc/vectorlist.xxml create mode 100644 doc/version-greater-or-equal.xsl create mode 100644 doc/visitors.xxml create mode 100644 etc/cmake/BuildType.cmake create mode 100644 etc/cmake/CheckTLSSupport.cmake create mode 100644 etc/cmake/CodeCoverage.cmake create mode 100644 etc/cmake/FindARPACK.cmake create mode 100644 etc/cmake/FindGLPK.cmake create mode 100644 etc/cmake/FindGMP.cmake create mode 100644 etc/cmake/FindPLFIT.cmake create mode 100644 etc/cmake/GetGitRevisionDescription.cmake create mode 100644 etc/cmake/GetGitRevisionDescription.cmake.in create mode 100644 etc/cmake/JoinPaths.cmake create mode 100644 etc/cmake/PadString.cmake create mode 100644 etc/cmake/PreventInSourceBuilds.cmake create mode 100644 etc/cmake/UseCCacheWhenInstalled.cmake create mode 100644 etc/cmake/attribute_support.cmake create mode 100644 etc/cmake/benchmark_helpers.cmake create mode 100644 etc/cmake/bit_operations_support.cmake create mode 100644 etc/cmake/compilers.cmake create mode 100644 etc/cmake/cpack_install_script.cmake create mode 100644 etc/cmake/create_igraph_version_file.cmake create mode 100644 etc/cmake/debugging.cmake create mode 100644 etc/cmake/dependencies.cmake create mode 100644 etc/cmake/features.cmake create mode 100644 etc/cmake/fuzz_helpers.cmake create mode 100644 etc/cmake/generate_tags_file.cmake create mode 100644 etc/cmake/helpers.cmake create mode 100644 etc/cmake/ieee754_endianness.cmake create mode 100644 etc/cmake/ieee754_endianness_check.c create mode 100644 etc/cmake/igraph-config.cmake.in create mode 100644 etc/cmake/lto.cmake create mode 100644 etc/cmake/packaging.cmake create mode 100644 etc/cmake/pkgconfig_helpers.cmake create mode 100644 etc/cmake/run_legacy_test.cmake create mode 100644 etc/cmake/safe_math_support.cmake create mode 100644 etc/cmake/sanitizers.cmake create mode 100644 etc/cmake/summary.cmake create mode 100644 etc/cmake/test_helpers.cmake create mode 100644 etc/cmake/tls.cmake create mode 100644 etc/cmake/uint128_support.cmake create mode 100644 etc/cmake/version.cmake create mode 100644 examples/simple/adjlist.c create mode 100644 examples/simple/ak-4102.max create mode 100644 examples/simple/bellman_ford.c create mode 100644 examples/simple/bellman_ford.out create mode 100644 examples/simple/blas.c create mode 100644 examples/simple/blas.out create mode 100644 examples/simple/blas_dgemm.c create mode 100644 examples/simple/blas_dgemm.out create mode 100644 examples/simple/cattributes.c create mode 100644 examples/simple/cattributes.out create mode 100644 examples/simple/cattributes2.c create mode 100644 examples/simple/cattributes2.out create mode 100644 examples/simple/cattributes3.c create mode 100644 examples/simple/cattributes3.out create mode 100644 examples/simple/cattributes4.c create mode 100644 examples/simple/cattributes4.out create mode 100644 examples/simple/celegansneural.gml create mode 100644 examples/simple/centralization.c create mode 100644 examples/simple/centralization.out create mode 100644 examples/simple/cohesive_blocks.c create mode 100644 examples/simple/cohesive_blocks.out create mode 100644 examples/simple/creation.c create mode 100644 examples/simple/distances.c create mode 100644 examples/simple/distances.out create mode 100644 examples/simple/dominator_tree.c create mode 100644 examples/simple/dominator_tree.out create mode 100644 examples/simple/dot.c create mode 100644 examples/simple/dot.out create mode 100644 examples/simple/dqueue.c create mode 100644 examples/simple/dqueue.out create mode 100644 examples/simple/edgelist1.dl create mode 100644 examples/simple/edgelist2.dl create mode 100644 examples/simple/edgelist3.dl create mode 100644 examples/simple/edgelist4.dl create mode 100644 examples/simple/edgelist5.dl create mode 100644 examples/simple/edgelist6.dl create mode 100644 examples/simple/edgelist7.dl create mode 100644 examples/simple/eigenvector_centrality.c create mode 100644 examples/simple/eigenvector_centrality.out create mode 100644 examples/simple/even_tarjan.c create mode 100644 examples/simple/flow.c create mode 100644 examples/simple/flow2.c create mode 100644 examples/simple/flow2.out create mode 100644 examples/simple/foreign.c create mode 100644 examples/simple/foreign.out create mode 100644 examples/simple/fullmatrix1.dl create mode 100644 examples/simple/fullmatrix2.dl create mode 100644 examples/simple/fullmatrix3.dl create mode 100644 examples/simple/fullmatrix4.dl create mode 100644 examples/simple/gml.c create mode 100644 examples/simple/gml.out create mode 100644 examples/simple/graphml.c create mode 100644 examples/simple/igraph_adjacency.c create mode 100644 examples/simple/igraph_all_st_mincuts.c create mode 100644 examples/simple/igraph_assortativity_degree.c create mode 100644 examples/simple/igraph_assortativity_degree.out create mode 100644 examples/simple/igraph_assortativity_nominal.c create mode 100644 examples/simple/igraph_assortativity_nominal.out create mode 100644 examples/simple/igraph_atlas.c create mode 100644 examples/simple/igraph_atlas.out create mode 100644 examples/simple/igraph_attribute_combination.c create mode 100644 examples/simple/igraph_attribute_combination.out create mode 100644 examples/simple/igraph_average_path_length.c create mode 100644 examples/simple/igraph_avg_nearest_neighbor_degree.c create mode 100644 examples/simple/igraph_avg_nearest_neighbor_degree.out create mode 100644 examples/simple/igraph_barabasi_game.c create mode 100644 examples/simple/igraph_barabasi_game2.c create mode 100644 examples/simple/igraph_bfs.c create mode 100644 examples/simple/igraph_bfs.out create mode 100644 examples/simple/igraph_bfs_callback.c create mode 100644 examples/simple/igraph_bfs_callback.out create mode 100644 examples/simple/igraph_bfs_simple.c create mode 100644 examples/simple/igraph_bfs_simple.out create mode 100644 examples/simple/igraph_biconnected_components.c create mode 100644 examples/simple/igraph_biconnected_components.out create mode 100644 examples/simple/igraph_bipartite_create.c create mode 100644 examples/simple/igraph_bipartite_create.out create mode 100644 examples/simple/igraph_bipartite_projection.c create mode 100644 examples/simple/igraph_cliques.c create mode 100644 examples/simple/igraph_cliques.out create mode 100644 examples/simple/igraph_cocitation.c create mode 100644 examples/simple/igraph_cocitation.out create mode 100644 examples/simple/igraph_coloring.c create mode 100644 examples/simple/igraph_community_edge_betweenness.c create mode 100644 examples/simple/igraph_community_edge_betweenness.out create mode 100644 examples/simple/igraph_community_fastgreedy.c create mode 100644 examples/simple/igraph_community_fastgreedy.out create mode 100644 examples/simple/igraph_community_label_propagation.c create mode 100644 examples/simple/igraph_community_label_propagation.out create mode 100644 examples/simple/igraph_community_leading_eigenvector.c create mode 100644 examples/simple/igraph_community_leading_eigenvector.out create mode 100644 examples/simple/igraph_community_leiden.c create mode 100644 examples/simple/igraph_community_leiden.out create mode 100644 examples/simple/igraph_community_multilevel.c create mode 100644 examples/simple/igraph_community_multilevel.out create mode 100644 examples/simple/igraph_community_optimal_modularity.c create mode 100644 examples/simple/igraph_complementer.c create mode 100644 examples/simple/igraph_complementer.out create mode 100644 examples/simple/igraph_compose.c create mode 100644 examples/simple/igraph_compose.out create mode 100644 examples/simple/igraph_contract_vertices.c create mode 100644 examples/simple/igraph_copy.c create mode 100644 examples/simple/igraph_create.c create mode 100644 examples/simple/igraph_decompose.c create mode 100644 examples/simple/igraph_decompose.out create mode 100644 examples/simple/igraph_degree.c create mode 100644 examples/simple/igraph_degree.out create mode 100644 examples/simple/igraph_degree_sequence_game.c create mode 100644 examples/simple/igraph_degree_sequence_game.out create mode 100644 examples/simple/igraph_delete_edges.c create mode 100644 examples/simple/igraph_delete_vertices.c create mode 100644 examples/simple/igraph_deterministic_optimal_imitation.c create mode 100644 examples/simple/igraph_diameter.c create mode 100644 examples/simple/igraph_diameter.out create mode 100644 examples/simple/igraph_difference.c create mode 100644 examples/simple/igraph_difference.out create mode 100644 examples/simple/igraph_disjoint_union.c create mode 100644 examples/simple/igraph_disjoint_union.out create mode 100644 examples/simple/igraph_eccentricity.c create mode 100644 examples/simple/igraph_eccentricity.out create mode 100644 examples/simple/igraph_erdos_renyi_game_gnm.c create mode 100644 examples/simple/igraph_erdos_renyi_game_gnp.c create mode 100644 examples/simple/igraph_es_pairs.c create mode 100644 examples/simple/igraph_feedback_arc_set.c create mode 100644 examples/simple/igraph_feedback_arc_set.out create mode 100644 examples/simple/igraph_feedback_arc_set_ip.c create mode 100644 examples/simple/igraph_feedback_arc_set_ip.out create mode 100644 examples/simple/igraph_fisher_yates_shuffle.c create mode 100644 examples/simple/igraph_full.c create mode 100644 examples/simple/igraph_full.out create mode 100644 examples/simple/igraph_get_all_shortest_paths_dijkstra.c create mode 100644 examples/simple/igraph_get_all_shortest_paths_dijkstra.out create mode 100644 examples/simple/igraph_get_eid.c create mode 100644 examples/simple/igraph_get_eid.out create mode 100644 examples/simple/igraph_get_eids.c create mode 100644 examples/simple/igraph_get_laplacian.c create mode 100644 examples/simple/igraph_get_laplacian.out create mode 100644 examples/simple/igraph_get_laplacian_sparse.c create mode 100644 examples/simple/igraph_get_laplacian_sparse.out create mode 100644 examples/simple/igraph_get_shortest_paths.c create mode 100644 examples/simple/igraph_get_shortest_paths.out create mode 100644 examples/simple/igraph_get_shortest_paths_dijkstra.c create mode 100644 examples/simple/igraph_get_shortest_paths_dijkstra.out create mode 100644 examples/simple/igraph_girth.c create mode 100644 examples/simple/igraph_grg_game.c create mode 100644 examples/simple/igraph_grg_game.out create mode 100644 examples/simple/igraph_has_multiple.c create mode 100644 examples/simple/igraph_independent_sets.c create mode 100644 examples/simple/igraph_independent_sets.out create mode 100644 examples/simple/igraph_intersection.c create mode 100644 examples/simple/igraph_intersection.out create mode 100644 examples/simple/igraph_is_biconnected.c create mode 100644 examples/simple/igraph_is_biconnected.out create mode 100644 examples/simple/igraph_is_directed.c create mode 100644 examples/simple/igraph_is_loop.c create mode 100644 examples/simple/igraph_is_loop.out create mode 100644 examples/simple/igraph_is_minimal_separator.c create mode 100644 examples/simple/igraph_is_multiple.c create mode 100644 examples/simple/igraph_is_multiple.out create mode 100644 examples/simple/igraph_is_separator.c create mode 100644 examples/simple/igraph_isomorphic_vf2.c create mode 100644 examples/simple/igraph_join.c create mode 100644 examples/simple/igraph_join.out create mode 100644 examples/simple/igraph_kary_tree.c create mode 100644 examples/simple/igraph_kary_tree.out create mode 100644 examples/simple/igraph_lapack_dgeev.c create mode 100644 examples/simple/igraph_lapack_dgeev.out create mode 100644 examples/simple/igraph_lapack_dgeevx.c create mode 100644 examples/simple/igraph_lapack_dgeevx.out create mode 100644 examples/simple/igraph_lapack_dgesv.c create mode 100644 examples/simple/igraph_lapack_dgesv.out create mode 100644 examples/simple/igraph_lapack_dsyevr.c create mode 100644 examples/simple/igraph_lapack_dsyevr.out create mode 100644 examples/simple/igraph_layout_reingold_tilford.c create mode 100644 examples/simple/igraph_layout_reingold_tilford.in create mode 100644 examples/simple/igraph_lcf.c create mode 100644 examples/simple/igraph_list_triangles.c create mode 100644 examples/simple/igraph_list_triangles.out create mode 100644 examples/simple/igraph_maximal_cliques.c create mode 100644 examples/simple/igraph_maximum_bipartite_matching.c create mode 100644 examples/simple/igraph_mincut.c create mode 100644 examples/simple/igraph_mincut.out create mode 100644 examples/simple/igraph_minimal_separators.c create mode 100644 examples/simple/igraph_minimum_size_separators.c create mode 100644 examples/simple/igraph_minimum_spanning_tree.c create mode 100644 examples/simple/igraph_minimum_spanning_tree.out create mode 100644 examples/simple/igraph_motifs_randesu.c create mode 100644 examples/simple/igraph_motifs_randesu.out create mode 100644 examples/simple/igraph_neighbors.c create mode 100644 examples/simple/igraph_neighbors.out create mode 100644 examples/simple/igraph_pagerank.c create mode 100644 examples/simple/igraph_power_law_fit.c create mode 100644 examples/simple/igraph_power_law_fit.out create mode 100644 examples/simple/igraph_radius.c create mode 100644 examples/simple/igraph_random_sample.c create mode 100644 examples/simple/igraph_read_graph_dl.c create mode 100644 examples/simple/igraph_read_graph_dl.out create mode 100644 examples/simple/igraph_read_graph_graphdb.c create mode 100644 examples/simple/igraph_read_graph_graphdb.out create mode 100644 examples/simple/igraph_read_graph_lgl-1.lgl create mode 100644 examples/simple/igraph_read_graph_lgl-2.lgl create mode 100644 examples/simple/igraph_read_graph_lgl-3.lgl create mode 100644 examples/simple/igraph_read_graph_lgl.c create mode 100644 examples/simple/igraph_read_graph_lgl.out create mode 100644 examples/simple/igraph_realize_degree_sequence.c create mode 100644 examples/simple/igraph_realize_degree_sequence.out create mode 100644 examples/simple/igraph_reciprocity.c create mode 100644 examples/simple/igraph_regular_tree.c create mode 100644 examples/simple/igraph_regular_tree.out create mode 100644 examples/simple/igraph_ring.c create mode 100644 examples/simple/igraph_ring.out create mode 100644 examples/simple/igraph_roulette_wheel_imitation.c create mode 100644 examples/simple/igraph_similarity.c create mode 100644 examples/simple/igraph_similarity.out create mode 100644 examples/simple/igraph_simplify.c create mode 100644 examples/simple/igraph_simplify.out create mode 100644 examples/simple/igraph_small.c create mode 100644 examples/simple/igraph_small.out create mode 100644 examples/simple/igraph_sparsemat.c create mode 100644 examples/simple/igraph_sparsemat.out create mode 100644 examples/simple/igraph_sparsemat3.c create mode 100644 examples/simple/igraph_sparsemat3.out create mode 100644 examples/simple/igraph_sparsemat4.c create mode 100644 examples/simple/igraph_sparsemat4.out create mode 100644 examples/simple/igraph_sparsemat6.c create mode 100644 examples/simple/igraph_sparsemat7.c create mode 100644 examples/simple/igraph_sparsemat8.c create mode 100644 examples/simple/igraph_star.c create mode 100644 examples/simple/igraph_star.out create mode 100644 examples/simple/igraph_stochastic_imitation.c create mode 100644 examples/simple/igraph_strvector.c create mode 100644 examples/simple/igraph_strvector.out create mode 100644 examples/simple/igraph_subisomorphic_lad.c create mode 100644 examples/simple/igraph_subisomorphic_lad.out create mode 100644 examples/simple/igraph_symmetric_tree.c create mode 100644 examples/simple/igraph_to_undirected.c create mode 100644 examples/simple/igraph_to_undirected.out create mode 100644 examples/simple/igraph_topological_sorting.c create mode 100644 examples/simple/igraph_topological_sorting.out create mode 100644 examples/simple/igraph_transitivity.c create mode 100644 examples/simple/igraph_union.c create mode 100644 examples/simple/igraph_union.out create mode 100644 examples/simple/igraph_vector_int_list_sort.c create mode 100644 examples/simple/igraph_version.c create mode 100644 examples/simple/igraph_vs_nonadj.c create mode 100644 examples/simple/igraph_vs_nonadj.out create mode 100644 examples/simple/igraph_vs_range.c create mode 100644 examples/simple/igraph_vs_range.out create mode 100644 examples/simple/igraph_vs_vector.c create mode 100644 examples/simple/igraph_vs_vector.out create mode 100644 examples/simple/igraph_weighted_adjacency.c create mode 100644 examples/simple/igraph_weighted_adjacency.out create mode 100644 examples/simple/igraph_write_graph_lgl.c create mode 100644 examples/simple/igraph_write_graph_lgl.out create mode 100644 examples/simple/igraph_write_graph_pajek.c create mode 100644 examples/simple/igraph_write_graph_pajek.out create mode 100644 examples/simple/iso_b03_m1000.A00 create mode 100644 examples/simple/karate.gml create mode 100644 examples/simple/links.net create mode 100644 examples/simple/nodelist1.dl create mode 100644 examples/simple/nodelist2.dl create mode 100644 examples/simple/random_seed.c create mode 100644 examples/simple/safelocale.c create mode 100644 examples/simple/safelocale.out create mode 100644 examples/simple/test.graphml create mode 100644 examples/simple/walktrap.c create mode 100644 examples/simple/weighted.gml create mode 100644 examples/tutorial/tutorial1.c create mode 100644 examples/tutorial/tutorial2.c create mode 100644 examples/tutorial/tutorial3.c create mode 100644 igraph.pc.in create mode 100644 include/igraph.h create mode 100644 include/igraph_adjlist.h create mode 100644 include/igraph_arpack.h create mode 100644 include/igraph_array.h create mode 100644 include/igraph_array_pmt.h create mode 100644 include/igraph_attributes.h create mode 100644 include/igraph_bipartite.h create mode 100644 include/igraph_bitset.h create mode 100644 include/igraph_bitset_list.h create mode 100644 include/igraph_blas.h create mode 100644 include/igraph_centrality.h create mode 100644 include/igraph_cliques.h create mode 100644 include/igraph_cocitation.h create mode 100644 include/igraph_cohesive_blocks.h create mode 100644 include/igraph_coloring.h create mode 100644 include/igraph_community.h create mode 100644 include/igraph_complex.h create mode 100644 include/igraph_components.h create mode 100644 include/igraph_config.h.in create mode 100644 include/igraph_constants.h create mode 100644 include/igraph_constructors.h create mode 100644 include/igraph_conversion.h create mode 100644 include/igraph_cycles.h create mode 100644 include/igraph_datatype.h create mode 100644 include/igraph_decls.h create mode 100644 include/igraph_dqueue.h create mode 100644 include/igraph_dqueue_pmt.h create mode 100644 include/igraph_eigen.h create mode 100644 include/igraph_embedding.h create mode 100644 include/igraph_epidemics.h create mode 100644 include/igraph_error.h create mode 100644 include/igraph_eulerian.h create mode 100644 include/igraph_flow.h create mode 100644 include/igraph_foreign.h create mode 100644 include/igraph_games.h create mode 100644 include/igraph_graph_list.h create mode 100644 include/igraph_graphicality.h create mode 100644 include/igraph_graphlets.h create mode 100644 include/igraph_heap.h create mode 100644 include/igraph_heap_pmt.h create mode 100644 include/igraph_hrg.h create mode 100644 include/igraph_interface.h create mode 100644 include/igraph_interrupt.h create mode 100644 include/igraph_iterators.h create mode 100644 include/igraph_lapack.h create mode 100644 include/igraph_layout.h create mode 100644 include/igraph_lsap.h create mode 100644 include/igraph_matching.h create mode 100644 include/igraph_matrix.h create mode 100644 include/igraph_matrix_list.h create mode 100644 include/igraph_matrix_pmt.h create mode 100644 include/igraph_memory.h create mode 100644 include/igraph_microscopic_update.h create mode 100644 include/igraph_mixing.h create mode 100644 include/igraph_motifs.h create mode 100644 include/igraph_neighborhood.h create mode 100644 include/igraph_nongraph.h create mode 100644 include/igraph_operators.h create mode 100644 include/igraph_paths.h create mode 100644 include/igraph_pmt.h create mode 100644 include/igraph_pmt_off.h create mode 100644 include/igraph_progress.h create mode 100644 include/igraph_psumtree.h create mode 100644 include/igraph_qsort.h create mode 100644 include/igraph_random.h create mode 100644 include/igraph_reachability.h create mode 100644 include/igraph_scan.h create mode 100644 include/igraph_separators.h create mode 100644 include/igraph_sparsemat.h create mode 100644 include/igraph_stack.h create mode 100644 include/igraph_stack_pmt.h create mode 100644 include/igraph_statusbar.h create mode 100644 include/igraph_structural.h create mode 100644 include/igraph_strvector.h create mode 100644 include/igraph_threading.h.in create mode 100644 include/igraph_topology.h create mode 100644 include/igraph_transitivity.h create mode 100644 include/igraph_typed_list_pmt.h create mode 100644 include/igraph_types.h create mode 100644 include/igraph_vector.h create mode 100644 include/igraph_vector_list.h create mode 100644 include/igraph_vector_pmt.h create mode 100644 include/igraph_vector_ptr.h create mode 100644 include/igraph_vector_type.h create mode 100644 include/igraph_version.h.in create mode 100644 include/igraph_visitor.h create mode 100644 interfaces/CMakeLists.txt create mode 100644 interfaces/functions.yaml create mode 100644 interfaces/types.yaml create mode 100644 src/CMakeLists.txt create mode 100644 src/centrality/betweenness.c create mode 100644 src/centrality/centrality_internal.h create mode 100644 src/centrality/centrality_other.c create mode 100644 src/centrality/centralization.c create mode 100644 src/centrality/closeness.c create mode 100644 src/centrality/coreness.c create mode 100644 src/centrality/eigenvector.c create mode 100644 src/centrality/hub_authority.c create mode 100644 src/centrality/pagerank.c create mode 100644 src/centrality/prpack.cpp create mode 100644 src/centrality/prpack/CMakeLists.txt create mode 100644 src/centrality/prpack/prpack.h create mode 100644 src/centrality/prpack/prpack_base_graph.cpp create mode 100644 src/centrality/prpack/prpack_base_graph.h create mode 100644 src/centrality/prpack/prpack_csc.h create mode 100644 src/centrality/prpack/prpack_csr.h create mode 100644 src/centrality/prpack/prpack_edge_list.h create mode 100644 src/centrality/prpack/prpack_igraph_graph.cpp create mode 100644 src/centrality/prpack/prpack_igraph_graph.h create mode 100644 src/centrality/prpack/prpack_preprocessed_ge_graph.cpp create mode 100644 src/centrality/prpack/prpack_preprocessed_ge_graph.h create mode 100644 src/centrality/prpack/prpack_preprocessed_graph.h create mode 100644 src/centrality/prpack/prpack_preprocessed_gs_graph.cpp create mode 100644 src/centrality/prpack/prpack_preprocessed_gs_graph.h create mode 100644 src/centrality/prpack/prpack_preprocessed_scc_graph.cpp create mode 100644 src/centrality/prpack/prpack_preprocessed_scc_graph.h create mode 100644 src/centrality/prpack/prpack_preprocessed_schur_graph.cpp create mode 100644 src/centrality/prpack/prpack_preprocessed_schur_graph.h create mode 100644 src/centrality/prpack/prpack_result.cpp create mode 100644 src/centrality/prpack/prpack_result.h create mode 100644 src/centrality/prpack/prpack_solver.cpp create mode 100644 src/centrality/prpack/prpack_solver.h create mode 100644 src/centrality/prpack/prpack_utils.cpp create mode 100644 src/centrality/prpack/prpack_utils.h create mode 100644 src/centrality/prpack_internal.h create mode 100644 src/centrality/truss.cpp create mode 100644 src/cliques/cliquer/CMakeLists.txt create mode 100644 src/cliques/cliquer/README create mode 100644 src/cliques/cliquer/cliquer.c create mode 100644 src/cliques/cliquer/cliquer.h create mode 100644 src/cliques/cliquer/cliquer_graph.c create mode 100644 src/cliques/cliquer/cliquerconf.h create mode 100644 src/cliques/cliquer/graph.h create mode 100644 src/cliques/cliquer/misc.h create mode 100644 src/cliques/cliquer/reorder.c create mode 100644 src/cliques/cliquer/reorder.h create mode 100644 src/cliques/cliquer/set.h create mode 100644 src/cliques/cliquer_internal.h create mode 100644 src/cliques/cliquer_wrapper.c create mode 100644 src/cliques/cliques.c create mode 100644 src/cliques/glet.c create mode 100644 src/cliques/maximal_cliques.c create mode 100644 src/cliques/maximal_cliques_template.h create mode 100644 src/community/community_misc.c create mode 100644 src/community/edge_betweenness.c create mode 100644 src/community/fast_modularity.c create mode 100644 src/community/fluid.c create mode 100644 src/community/infomap/infomap.cc create mode 100644 src/community/infomap/infomap_FlowGraph.cc create mode 100644 src/community/infomap/infomap_FlowGraph.h create mode 100644 src/community/infomap/infomap_Greedy.cc create mode 100644 src/community/infomap/infomap_Greedy.h create mode 100644 src/community/infomap/infomap_Node.h create mode 100644 src/community/label_propagation.c create mode 100644 src/community/leading_eigenvector.c create mode 100644 src/community/leiden.c create mode 100644 src/community/louvain.c create mode 100644 src/community/modularity.c create mode 100644 src/community/optimal_modularity.c create mode 100644 src/community/spinglass/NetDataTypes.cpp create mode 100644 src/community/spinglass/NetDataTypes.h create mode 100644 src/community/spinglass/NetRoutines.cpp create mode 100644 src/community/spinglass/NetRoutines.h create mode 100644 src/community/spinglass/clustertool.cpp create mode 100644 src/community/spinglass/pottsmodel_2.cpp create mode 100644 src/community/spinglass/pottsmodel_2.h create mode 100644 src/community/voronoi.c create mode 100644 src/community/walktrap/walktrap.cpp create mode 100644 src/community/walktrap/walktrap_communities.cpp create mode 100644 src/community/walktrap/walktrap_communities.h create mode 100644 src/community/walktrap/walktrap_graph.cpp create mode 100644 src/community/walktrap/walktrap_graph.h create mode 100644 src/community/walktrap/walktrap_heap.cpp create mode 100644 src/community/walktrap/walktrap_heap.h create mode 100644 src/config.h.in create mode 100644 src/connectivity/cohesive_blocks.c create mode 100644 src/connectivity/components.c create mode 100644 src/connectivity/reachability.c create mode 100644 src/connectivity/separators.c create mode 100644 src/constructors/adjacency.c create mode 100644 src/constructors/atlas-edges.h create mode 100644 src/constructors/atlas.c create mode 100644 src/constructors/basic_constructors.c create mode 100644 src/constructors/circulant.c create mode 100644 src/constructors/de_bruijn.c create mode 100644 src/constructors/famous.c create mode 100644 src/constructors/full.c create mode 100644 src/constructors/generalized_petersen.c create mode 100644 src/constructors/kautz.c create mode 100644 src/constructors/lattices.c create mode 100644 src/constructors/lcf.c create mode 100644 src/constructors/linegraph.c create mode 100644 src/constructors/prufer.c create mode 100644 src/constructors/regular.c create mode 100644 src/constructors/trees.c create mode 100644 src/core/array.c create mode 100644 src/core/array.pmt create mode 100644 src/core/bitset.c create mode 100644 src/core/bitset_list.c create mode 100644 src/core/buckets.c create mode 100644 src/core/buckets.h create mode 100644 src/core/cutheap.c create mode 100644 src/core/cutheap.h create mode 100644 src/core/dqueue.c create mode 100644 src/core/dqueue.pmt create mode 100644 src/core/error.c create mode 100644 src/core/estack.c create mode 100644 src/core/estack.h create mode 100644 src/core/exceptions.h create mode 100644 src/core/fixed_vectorlist.c create mode 100644 src/core/fixed_vectorlist.h create mode 100644 src/core/genheap.c create mode 100644 src/core/genheap.h create mode 100644 src/core/grid.c create mode 100644 src/core/grid.h create mode 100644 src/core/heap.c create mode 100644 src/core/heap.pmt create mode 100644 src/core/indheap.c create mode 100644 src/core/indheap.h create mode 100644 src/core/interruption.c create mode 100644 src/core/interruption.h create mode 100644 src/core/marked_queue.c create mode 100644 src/core/marked_queue.h create mode 100644 src/core/math.h create mode 100644 src/core/matrix.c create mode 100644 src/core/matrix.pmt create mode 100644 src/core/matrix_list.c create mode 100644 src/core/memory.c create mode 100644 src/core/printing.c create mode 100644 src/core/progress.c create mode 100644 src/core/psumtree.c create mode 100644 src/core/set.c create mode 100644 src/core/set.h create mode 100644 src/core/sparsemat.c create mode 100644 src/core/stack.c create mode 100644 src/core/stack.pmt create mode 100644 src/core/statusbar.c create mode 100644 src/core/strvector.c create mode 100644 src/core/trie.c create mode 100644 src/core/trie.h create mode 100644 src/core/typed_list.pmt create mode 100644 src/core/vector.c create mode 100644 src/core/vector.pmt create mode 100644 src/core/vector_list.c create mode 100644 src/core/vector_ptr.c create mode 100644 src/flow/flow.c create mode 100644 src/flow/flow_conversion.c create mode 100644 src/flow/flow_internal.h create mode 100644 src/flow/st-cuts.c create mode 100644 src/games/barabasi.c create mode 100644 src/games/callaway_traits.c create mode 100644 src/games/chung_lu.c create mode 100644 src/games/citations.c create mode 100644 src/games/correlated.c create mode 100644 src/games/degree_sequence.c create mode 100644 src/games/degree_sequence_vl/degree_sequence_vl.h create mode 100644 src/games/degree_sequence_vl/gengraph_definitions.h create mode 100644 src/games/degree_sequence_vl/gengraph_degree_sequence.cpp create mode 100644 src/games/degree_sequence_vl/gengraph_degree_sequence.h create mode 100644 src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp create mode 100644 src/games/degree_sequence_vl/gengraph_graph_molloy_hash.h create mode 100644 src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp create mode 100644 src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h create mode 100644 src/games/degree_sequence_vl/gengraph_hash.h create mode 100644 src/games/degree_sequence_vl/gengraph_header.h create mode 100644 src/games/degree_sequence_vl/gengraph_mr-connected.cpp create mode 100644 src/games/degree_sequence_vl/gengraph_qsort.h create mode 100644 src/games/degree_sequence_vl/gengraph_random.cpp create mode 100644 src/games/degree_sequence_vl/gengraph_random.h create mode 100644 src/games/dotproduct.c create mode 100644 src/games/erdos_renyi.c create mode 100644 src/games/establishment.c create mode 100644 src/games/forestfire.c create mode 100644 src/games/grg.c create mode 100644 src/games/growing_random.c create mode 100644 src/games/islands.c create mode 100644 src/games/k_regular.c create mode 100644 src/games/preference.c create mode 100644 src/games/recent_degree.c create mode 100644 src/games/sbm.c create mode 100644 src/games/static_fitness.c create mode 100644 src/games/tree.c create mode 100644 src/games/watts_strogatz.c create mode 100644 src/graph/adjlist.c create mode 100644 src/graph/attributes.c create mode 100644 src/graph/attributes.h create mode 100644 src/graph/basic_query.c create mode 100644 src/graph/caching.c create mode 100644 src/graph/caching.h create mode 100644 src/graph/cattributes.c create mode 100644 src/graph/graph_list.c create mode 100644 src/graph/internal.h create mode 100644 src/graph/iterators.c create mode 100644 src/graph/type_common.c create mode 100644 src/graph/type_indexededgelist.c create mode 100644 src/graph/visitors.c create mode 100644 src/hrg/dendro.h create mode 100644 src/hrg/graph.h create mode 100644 src/hrg/graph_simp.h create mode 100644 src/hrg/hrg.cc create mode 100644 src/hrg/hrg_types.cc create mode 100644 src/hrg/rbtree.h create mode 100644 src/hrg/splittree_eq.h create mode 100644 src/internal/glpk_support.c create mode 100644 src/internal/glpk_support.h create mode 100644 src/internal/gmp_internal.h create mode 100644 src/internal/hacks.c create mode 100644 src/internal/hacks.h create mode 100644 src/internal/lsap.c create mode 100644 src/internal/qsort.c create mode 100644 src/internal/qsort_r.c create mode 100644 src/internal/utils.c create mode 100644 src/internal/utils.h create mode 100644 src/internal/zeroin.c create mode 100644 src/io/dimacs.c create mode 100644 src/io/dl-header.h create mode 100644 src/io/dl.c create mode 100644 src/io/dot.c create mode 100644 src/io/edgelist.c create mode 100644 src/io/gml-header.h create mode 100644 src/io/gml-tree.c create mode 100644 src/io/gml-tree.h create mode 100644 src/io/gml.c create mode 100644 src/io/graphdb.c create mode 100644 src/io/graphml.c create mode 100644 src/io/leda.c create mode 100644 src/io/lgl-header.h create mode 100644 src/io/lgl.c create mode 100644 src/io/ncol-header.h create mode 100644 src/io/ncol.c create mode 100644 src/io/pajek-header.h create mode 100644 src/io/pajek.c create mode 100644 src/io/parse_utils.c create mode 100644 src/io/parse_utils.h create mode 100644 src/io/parsers/dl-lexer.c create mode 100644 src/io/parsers/dl-lexer.h create mode 100644 src/io/parsers/dl-parser.c create mode 100644 src/io/parsers/dl-parser.h create mode 100644 src/io/parsers/gml-lexer.c create mode 100644 src/io/parsers/gml-lexer.h create mode 100644 src/io/parsers/gml-parser.c create mode 100644 src/io/parsers/gml-parser.h create mode 100644 src/io/parsers/lgl-lexer.c create mode 100644 src/io/parsers/lgl-lexer.h create mode 100644 src/io/parsers/lgl-parser.c create mode 100644 src/io/parsers/lgl-parser.h create mode 100644 src/io/parsers/ncol-lexer.c create mode 100644 src/io/parsers/ncol-lexer.h create mode 100644 src/io/parsers/ncol-parser.c create mode 100644 src/io/parsers/ncol-parser.h create mode 100644 src/io/parsers/pajek-lexer.c create mode 100644 src/io/parsers/pajek-lexer.h create mode 100644 src/io/parsers/pajek-parser.c create mode 100644 src/io/parsers/pajek-parser.h create mode 100644 src/isomorphism/bliss.cc create mode 100644 src/isomorphism/bliss/CMakeLists.txt create mode 100644 src/isomorphism/bliss/bignum.hh create mode 100644 src/isomorphism/bliss/defs.cc create mode 100644 src/isomorphism/bliss/defs.hh create mode 100644 src/isomorphism/bliss/graph.cc create mode 100644 src/isomorphism/bliss/graph.hh create mode 100644 src/isomorphism/bliss/heap.cc create mode 100644 src/isomorphism/bliss/heap.hh create mode 100644 src/isomorphism/bliss/igraph-changes.md create mode 100644 src/isomorphism/bliss/kqueue.hh create mode 100644 src/isomorphism/bliss/kstack.hh create mode 100644 src/isomorphism/bliss/orbit.cc create mode 100644 src/isomorphism/bliss/orbit.hh create mode 100644 src/isomorphism/bliss/partition.cc create mode 100644 src/isomorphism/bliss/partition.hh create mode 100644 src/isomorphism/bliss/stats.hh create mode 100644 src/isomorphism/bliss/uintseqhash.cc create mode 100644 src/isomorphism/bliss/uintseqhash.hh create mode 100644 src/isomorphism/bliss/utils.cc create mode 100644 src/isomorphism/bliss/utils.hh create mode 100644 src/isomorphism/isoclasses.c create mode 100644 src/isomorphism/isoclasses.h create mode 100644 src/isomorphism/isomorphism_misc.c create mode 100644 src/isomorphism/lad.c create mode 100644 src/isomorphism/queries.c create mode 100644 src/isomorphism/vf2.c create mode 100644 src/layout/circular.c create mode 100644 src/layout/davidson_harel.c create mode 100644 src/layout/drl/DensityGrid.cpp create mode 100644 src/layout/drl/DensityGrid.h create mode 100644 src/layout/drl/DensityGrid_3d.cpp create mode 100644 src/layout/drl/DensityGrid_3d.h create mode 100644 src/layout/drl/drl_Node.h create mode 100644 src/layout/drl/drl_Node_3d.h create mode 100644 src/layout/drl/drl_graph.cpp create mode 100644 src/layout/drl/drl_graph.h create mode 100644 src/layout/drl/drl_graph_3d.cpp create mode 100644 src/layout/drl/drl_graph_3d.h create mode 100644 src/layout/drl/drl_layout.cpp create mode 100644 src/layout/drl/drl_layout.h create mode 100644 src/layout/drl/drl_layout_3d.cpp create mode 100644 src/layout/drl/drl_layout_3d.h create mode 100644 src/layout/drl/drl_parse.cpp create mode 100644 src/layout/drl/drl_parse.h create mode 100644 src/layout/fruchterman_reingold.c create mode 100644 src/layout/gem.c create mode 100644 src/layout/graphopt.c create mode 100644 src/layout/kamada_kawai.c create mode 100644 src/layout/large_graph.c create mode 100644 src/layout/layout_bipartite.c create mode 100644 src/layout/layout_grid.c create mode 100644 src/layout/layout_internal.h create mode 100644 src/layout/layout_random.c create mode 100644 src/layout/mds.c create mode 100644 src/layout/merge_dla.c create mode 100644 src/layout/merge_grid.c create mode 100644 src/layout/merge_grid.h create mode 100644 src/layout/reingold_tilford.c create mode 100644 src/layout/sugiyama.c create mode 100644 src/layout/umap.c create mode 100644 src/linalg/arpack.c create mode 100644 src/linalg/arpack_internal.h create mode 100644 src/linalg/blas.c create mode 100644 src/linalg/blas_internal.h create mode 100644 src/linalg/eigen.c create mode 100644 src/linalg/lapack.c create mode 100644 src/linalg/lapack_internal.h create mode 100644 src/math/complex.c create mode 100644 src/math/safe_intop.c create mode 100644 src/math/safe_intop.h create mode 100644 src/math/utils.c create mode 100644 src/misc/bipartite.c create mode 100644 src/misc/chordality.c create mode 100644 src/misc/cocitation.c create mode 100644 src/misc/coloring.c create mode 100644 src/misc/conversion.c create mode 100644 src/misc/cycle_bases.c create mode 100644 src/misc/degree_sequence.cpp create mode 100644 src/misc/embedding.c create mode 100644 src/misc/feedback_arc_set.c create mode 100644 src/misc/feedback_arc_set.h create mode 100644 src/misc/graphicality.c create mode 100644 src/misc/matching.c create mode 100644 src/misc/microscopic_update.c create mode 100644 src/misc/mixing.c create mode 100644 src/misc/motifs.c create mode 100644 src/misc/order_cycle.cpp create mode 100644 src/misc/order_cycle.h create mode 100644 src/misc/other.c create mode 100644 src/misc/power_law_fit.c create mode 100644 src/misc/scan.c create mode 100644 src/misc/sir.c create mode 100644 src/misc/spanning_trees.c create mode 100644 src/operators/add_edge.c create mode 100644 src/operators/complementer.c create mode 100644 src/operators/compose.c create mode 100644 src/operators/connect_neighborhood.c create mode 100644 src/operators/contract.c create mode 100644 src/operators/difference.c create mode 100644 src/operators/disjoint_union.c create mode 100644 src/operators/intersection.c create mode 100644 src/operators/join.c create mode 100644 src/operators/misc_internal.c create mode 100644 src/operators/misc_internal.h create mode 100644 src/operators/permute.c create mode 100644 src/operators/reverse.c create mode 100644 src/operators/rewire.c create mode 100644 src/operators/rewire_edges.c create mode 100644 src/operators/rewire_internal.h create mode 100644 src/operators/simplify.c create mode 100644 src/operators/subgraph.c create mode 100644 src/operators/subgraph.h create mode 100644 src/operators/union.c create mode 100644 src/paths/all_shortest_paths.c create mode 100644 src/paths/astar.c create mode 100644 src/paths/bellman_ford.c create mode 100644 src/paths/dijkstra.c create mode 100644 src/paths/distances.c create mode 100644 src/paths/eulerian.c create mode 100644 src/paths/floyd_warshall.c create mode 100644 src/paths/histogram.c create mode 100644 src/paths/johnson.c create mode 100644 src/paths/random_walk.c create mode 100644 src/paths/shortest_paths.c create mode 100644 src/paths/simple_paths.c create mode 100644 src/paths/sparsifier.c create mode 100644 src/paths/unweighted.c create mode 100644 src/paths/voronoi.c create mode 100644 src/paths/widest_paths.c create mode 100644 src/properties/basic_properties.c create mode 100644 src/properties/complete.c create mode 100644 src/properties/constraint.c create mode 100644 src/properties/convergence_degree.c create mode 100644 src/properties/dag.c create mode 100644 src/properties/degrees.c create mode 100644 src/properties/ecc.c create mode 100644 src/properties/girth.c create mode 100644 src/properties/loops.c create mode 100644 src/properties/multiplicity.c create mode 100644 src/properties/neighborhood.c create mode 100644 src/properties/perfect.c create mode 100644 src/properties/properties_internal.h create mode 100644 src/properties/spectral.c create mode 100644 src/properties/trees.c create mode 100644 src/properties/triangles.c create mode 100644 src/properties/triangles_template.h create mode 100644 src/properties/triangles_template1.h create mode 100644 src/random/random.c create mode 100644 src/random/random_internal.h create mode 100644 src/random/rng_glibc2.c create mode 100644 src/random/rng_mt19937.c create mode 100644 src/random/rng_pcg32.c create mode 100644 src/random/rng_pcg64.c create mode 100644 src/version.c create mode 100644 tests/CMakeLists.txt create mode 100644 tests/benchmarks/bench.h create mode 100644 tests/benchmarks/connectivity.c create mode 100644 tests/benchmarks/erdos_renyi.c create mode 100644 tests/benchmarks/graphicality.c create mode 100644 tests/benchmarks/igraph_average_path_length_unweighted.c create mode 100644 tests/benchmarks/igraph_betweenness.c create mode 100644 tests/benchmarks/igraph_betweenness_weighted.c create mode 100644 tests/benchmarks/igraph_cliques.c create mode 100644 tests/benchmarks/igraph_closeness_weighted.c create mode 100644 tests/benchmarks/igraph_coloring.c create mode 100644 tests/benchmarks/igraph_decompose.c create mode 100644 tests/benchmarks/igraph_degree.c create mode 100644 tests/benchmarks/igraph_degree_sequence_game.c create mode 100644 tests/benchmarks/igraph_distances.c create mode 100644 tests/benchmarks/igraph_ecc.c create mode 100644 tests/benchmarks/igraph_induced_subgraph_edges.c create mode 100644 tests/benchmarks/igraph_layout_umap.c create mode 100644 tests/benchmarks/igraph_matrix_transpose.c create mode 100644 tests/benchmarks/igraph_maximal_cliques.c create mode 100644 tests/benchmarks/igraph_neighborhood.c create mode 100644 tests/benchmarks/igraph_pagerank.c create mode 100644 tests/benchmarks/igraph_pagerank_weighted.c create mode 100644 tests/benchmarks/igraph_power_law_fit.c create mode 100644 tests/benchmarks/igraph_qsort.c create mode 100644 tests/benchmarks/igraph_random_walk.c create mode 100644 tests/benchmarks/igraph_strength.c create mode 100644 tests/benchmarks/igraph_transitivity.c create mode 100644 tests/benchmarks/igraph_tree_game.c create mode 100644 tests/benchmarks/igraph_vertex_connectivity.c create mode 100644 tests/benchmarks/igraph_voronoi.c create mode 100644 tests/benchmarks/inc_vs_adj.c create mode 100644 tests/benchmarks/intersection.c create mode 100644 tests/benchmarks/lad.c create mode 100644 tests/regression/bug-1033045.c create mode 100644 tests/regression/bug-1033045.out create mode 100644 tests/regression/bug-1149658.c create mode 100644 tests/regression/bug_1760.c create mode 100644 tests/regression/bug_1760.out create mode 100644 tests/regression/bug_1814.c create mode 100644 tests/regression/bug_1814.out create mode 100644 tests/regression/bug_1970.c create mode 100644 tests/regression/bug_1970.graphml create mode 100644 tests/regression/bug_2150.c create mode 100644 tests/regression/bug_2497.c create mode 100644 tests/regression/bug_2497.gml create mode 100644 tests/regression/bug_2497.out create mode 100644 tests/regression/bug_2506.c create mode 100644 tests/regression/bug_2506_1.graphml create mode 100644 tests/regression/bug_2506_2.graphml create mode 100644 tests/regression/bug_2506_3.graphml create mode 100644 tests/regression/bug_2517.c create mode 100644 tests/regression/bug_2517.out create mode 100644 tests/regression/cattr_bool_bug.c create mode 100644 tests/regression/cattr_bool_bug.graphml create mode 100644 tests/regression/cattr_bool_bug2.c create mode 100644 tests/regression/cattr_bool_bug2.graphml create mode 100644 tests/regression/igraph_layout_kamada_kawai_3d_bug_1462.c create mode 100644 tests/regression/igraph_layout_kamada_kawai_3d_bug_1462.out create mode 100644 tests/regression/igraph_layout_reingold_tilford_bug_879.c create mode 100644 tests/regression/igraph_layout_reingold_tilford_bug_879.in create mode 100644 tests/regression/igraph_layout_reingold_tilford_bug_879.out create mode 100644 tests/regression/igraph_read_graph_gml_invalid_inputs.c create mode 100644 tests/regression/igraph_read_graph_graphml_invalid_inputs.c create mode 100644 tests/regression/igraph_read_graph_graphml_invalid_inputs.out create mode 100644 tests/regression/igraph_read_graph_pajek_invalid_inputs.c create mode 100644 tests/regression/invalid1.gml create mode 100644 tests/regression/invalid1.graphml create mode 100644 tests/regression/invalid2.gml create mode 100644 tests/regression/invalid2.graphml create mode 100644 tests/regression/invalid3.graphml create mode 100644 tests/regression/invalid4.gml create mode 100644 tests/regression/invalid4.graphml create mode 100644 tests/regression/invalid5.gml create mode 100644 tests/regression/invalid5.graphml create mode 100644 tests/regression/invalid6.gml create mode 100644 tests/regression/invalid_pajek1.net create mode 100644 tests/regression/invalid_pajek2.net create mode 100644 tests/regression/invalid_pajek3.net create mode 100644 tests/unit/2wheap.c create mode 100644 tests/unit/VF2-compat.c create mode 100644 tests/unit/adj.c create mode 100644 tests/unit/adj.out create mode 100644 tests/unit/adjlist.c create mode 100644 tests/unit/adjlist.out create mode 100644 tests/unit/ak-4102.max create mode 100644 tests/unit/all_almost_e.c create mode 100644 tests/unit/all_shortest_paths.c create mode 100644 tests/unit/all_shortest_paths.out create mode 100644 tests/unit/assortativity.c create mode 100644 tests/unit/assortativity.out create mode 100644 tests/unit/bfs.c create mode 100644 tests/unit/bfs.out create mode 100644 tests/unit/bfs_simple.c create mode 100644 tests/unit/bfs_simple.out create mode 100644 tests/unit/bipartite.net create mode 100644 tests/unit/bitset.c create mode 100644 tests/unit/bitset.out create mode 100644 tests/unit/bliss_automorphisms.c create mode 100644 tests/unit/bliss_automorphisms.out create mode 100644 tests/unit/cattributes5.c create mode 100644 tests/unit/cattributes5.out create mode 100644 tests/unit/cattributes6.c create mode 100644 tests/unit/cattributes6.out create mode 100644 tests/unit/centralization.c create mode 100644 tests/unit/centralization.out create mode 100644 tests/unit/cmp_epsilon.c create mode 100644 tests/unit/community_indexing.c create mode 100644 tests/unit/community_label_propagation.c create mode 100644 tests/unit/community_label_propagation2.c create mode 100644 tests/unit/community_label_propagation2.out create mode 100644 tests/unit/community_label_propagation3.c create mode 100644 tests/unit/community_leiden.c create mode 100644 tests/unit/community_leiden.out create mode 100644 tests/unit/community_walktrap.c create mode 100644 tests/unit/community_walktrap.out create mode 100644 tests/unit/components.c create mode 100644 tests/unit/components.out create mode 100644 tests/unit/constructor-failure.c create mode 100644 tests/unit/coreness.c create mode 100644 tests/unit/coreness.out create mode 100644 tests/unit/cutheap.c create mode 100644 tests/unit/cutheap.out create mode 100644 tests/unit/cycle_bases.c create mode 100644 tests/unit/cycle_bases.out create mode 100644 tests/unit/d_indheap.c create mode 100644 tests/unit/d_indheap.out create mode 100644 tests/unit/dgemv.c create mode 100644 tests/unit/dgemv.out create mode 100644 tests/unit/edge_selectors.c create mode 100644 tests/unit/edge_selectors.out create mode 100644 tests/unit/efficiency.c create mode 100644 tests/unit/efficiency.out create mode 100644 tests/unit/empty create mode 100644 tests/unit/erdos_renyi_game_gnm.c create mode 100644 tests/unit/erdos_renyi_game_gnp.c create mode 100644 tests/unit/error_macros.c create mode 100644 tests/unit/error_macros.out create mode 100644 tests/unit/expand_path_to_pairs.c create mode 100644 tests/unit/expand_path_to_pairs.out create mode 100644 tests/unit/fatal_handler.c create mode 100644 tests/unit/fatal_handler.out create mode 100644 tests/unit/foreign_empty.c create mode 100644 tests/unit/full.c create mode 100644 tests/unit/full.out create mode 100644 tests/unit/gen2wheap.c create mode 100644 tests/unit/gen2wheap.out create mode 100644 tests/unit/global_transitivity.c create mode 100644 tests/unit/global_transitivity.out create mode 100644 tests/unit/glpk_error.c create mode 100644 tests/unit/gml.c create mode 100644 tests/unit/gml.out create mode 100644 tests/unit/graph1.gml create mode 100644 tests/unit/graph2.gml create mode 100644 tests/unit/graph3.gml create mode 100644 tests/unit/graphlets.c create mode 100644 tests/unit/graphlets.out create mode 100644 tests/unit/graphml-default-attrs.xml create mode 100644 tests/unit/graphml-hsa05010.xml create mode 100644 tests/unit/graphml-lenient.xml create mode 100644 tests/unit/graphml-namespace.xml create mode 100644 tests/unit/graphml-whitespace.xml create mode 100644 tests/unit/graphml-yed.xml create mode 100644 tests/unit/harmonic_centrality.c create mode 100644 tests/unit/harmonic_centrality.out create mode 100644 tests/unit/heap.c create mode 100644 tests/unit/heap.out create mode 100644 tests/unit/hub_and_authority.c create mode 100644 tests/unit/hub_and_authority.out create mode 100644 tests/unit/igraph_add_edges.c create mode 100644 tests/unit/igraph_add_edges.out create mode 100644 tests/unit/igraph_add_vertices.c create mode 100644 tests/unit/igraph_adhesion.c create mode 100644 tests/unit/igraph_adjacency.c create mode 100644 tests/unit/igraph_adjacency.out create mode 100644 tests/unit/igraph_adjacency_spectral_embedding.c create mode 100644 tests/unit/igraph_adjacency_spectral_embedding.out create mode 100644 tests/unit/igraph_adjacent_triangles.c create mode 100644 tests/unit/igraph_adjacent_triangles.out create mode 100644 tests/unit/igraph_adjlist_init_complementer.c create mode 100644 tests/unit/igraph_adjlist_init_complementer.out create mode 100644 tests/unit/igraph_adjlist_simplify.c create mode 100644 tests/unit/igraph_adjlist_simplify.out create mode 100644 tests/unit/igraph_all_st_cuts.c create mode 100644 tests/unit/igraph_all_st_cuts.out create mode 100644 tests/unit/igraph_all_st_mincuts.c create mode 100644 tests/unit/igraph_all_st_mincuts.out create mode 100644 tests/unit/igraph_almost_equals.c create mode 100644 tests/unit/igraph_are_connected.c create mode 100644 tests/unit/igraph_arpack_rnsolve.c create mode 100644 tests/unit/igraph_arpack_rnsolve.out create mode 100644 tests/unit/igraph_arpack_unpack_complex.c create mode 100644 tests/unit/igraph_arpack_unpack_complex.out create mode 100644 tests/unit/igraph_array.c create mode 100644 tests/unit/igraph_array.out create mode 100644 tests/unit/igraph_atlas.c create mode 100644 tests/unit/igraph_attribute_combination_remove.c create mode 100644 tests/unit/igraph_attribute_combination_remove.out create mode 100644 tests/unit/igraph_average_path_length.c create mode 100644 tests/unit/igraph_average_path_length.out create mode 100644 tests/unit/igraph_average_path_length_dijkstra.c create mode 100644 tests/unit/igraph_average_path_length_dijkstra.out create mode 100644 tests/unit/igraph_barabasi_aging_game.c create mode 100644 tests/unit/igraph_barabasi_aging_game.out create mode 100644 tests/unit/igraph_barabasi_game.c create mode 100644 tests/unit/igraph_betweenness.c create mode 100644 tests/unit/igraph_betweenness.out create mode 100644 tests/unit/igraph_betweenness_subset.c create mode 100644 tests/unit/igraph_betweenness_subset.out create mode 100644 tests/unit/igraph_biadjacency.c create mode 100644 tests/unit/igraph_biadjacency.out create mode 100644 tests/unit/igraph_biconnected_components.c create mode 100644 tests/unit/igraph_biconnected_components.out create mode 100644 tests/unit/igraph_bipartite_create.c create mode 100644 tests/unit/igraph_bipartite_game.c create mode 100644 tests/unit/igraph_bipartite_projection.c create mode 100644 tests/unit/igraph_blas_dgemm.c create mode 100644 tests/unit/igraph_blas_dgemm.out create mode 100644 tests/unit/igraph_bridges.c create mode 100644 tests/unit/igraph_bridges.out create mode 100644 tests/unit/igraph_callaway_traits_game.c create mode 100644 tests/unit/igraph_chung_lu_game.c create mode 100644 tests/unit/igraph_circulant.c create mode 100644 tests/unit/igraph_cited_type_game.c create mode 100644 tests/unit/igraph_cited_type_game.out create mode 100644 tests/unit/igraph_citing_cited_type_game.c create mode 100644 tests/unit/igraph_citing_cited_type_game.out create mode 100644 tests/unit/igraph_clique_size_hist.c create mode 100644 tests/unit/igraph_clique_size_hist.out create mode 100644 tests/unit/igraph_closeness.c create mode 100644 tests/unit/igraph_closeness.out create mode 100644 tests/unit/igraph_cohesion.c create mode 100644 tests/unit/igraph_cohesive_blocks.c create mode 100644 tests/unit/igraph_cohesive_blocks.out create mode 100644 tests/unit/igraph_coloring.c create mode 100644 tests/unit/igraph_coloring.out create mode 100644 tests/unit/igraph_community_eb_get_merges.c create mode 100644 tests/unit/igraph_community_eb_get_merges.out create mode 100644 tests/unit/igraph_community_edge_betweenness.c create mode 100644 tests/unit/igraph_community_edge_betweenness.out create mode 100644 tests/unit/igraph_community_fastgreedy.c create mode 100644 tests/unit/igraph_community_fastgreedy.out create mode 100644 tests/unit/igraph_community_fluid_communities.c create mode 100644 tests/unit/igraph_community_fluid_communities.out create mode 100644 tests/unit/igraph_community_infomap.c create mode 100644 tests/unit/igraph_community_infomap.out create mode 100644 tests/unit/igraph_community_leading_eigenvector2.c create mode 100644 tests/unit/igraph_community_leading_eigenvector2.out create mode 100644 tests/unit/igraph_community_voronoi.c create mode 100644 tests/unit/igraph_community_voronoi.out create mode 100644 tests/unit/igraph_compare_communities.c create mode 100644 tests/unit/igraph_compare_communities.out create mode 100644 tests/unit/igraph_complex.c create mode 100644 tests/unit/igraph_connect_neighborhood.c create mode 100644 tests/unit/igraph_connect_neighborhood.out create mode 100644 tests/unit/igraph_constraint.c create mode 100644 tests/unit/igraph_constraint.out create mode 100644 tests/unit/igraph_contract_vertices.c create mode 100644 tests/unit/igraph_contract_vertices.out create mode 100644 tests/unit/igraph_convergence_degree.c create mode 100644 tests/unit/igraph_convergence_degree.out create mode 100644 tests/unit/igraph_convex_hull.c create mode 100644 tests/unit/igraph_convex_hull.out create mode 100644 tests/unit/igraph_correlated_game.c create mode 100644 tests/unit/igraph_correlated_pair_game.c create mode 100644 tests/unit/igraph_correlated_pair_game.out create mode 100644 tests/unit/igraph_count_multiple.c create mode 100644 tests/unit/igraph_count_multiple.out create mode 100644 tests/unit/igraph_create.c create mode 100644 tests/unit/igraph_decompose_strong.c create mode 100644 tests/unit/igraph_decompose_strong.out create mode 100644 tests/unit/igraph_degree.c create mode 100644 tests/unit/igraph_degree.out create mode 100644 tests/unit/igraph_degree_sequence_game.c create mode 100644 tests/unit/igraph_delete_edges.c create mode 100644 tests/unit/igraph_delete_vertices.c create mode 100644 tests/unit/igraph_density.c create mode 100644 tests/unit/igraph_density.out create mode 100644 tests/unit/igraph_deterministic_optimal_imitation.c create mode 100644 tests/unit/igraph_diameter.c create mode 100644 tests/unit/igraph_diameter.out create mode 100644 tests/unit/igraph_diameter_dijkstra.c create mode 100644 tests/unit/igraph_diameter_dijkstra.out create mode 100644 tests/unit/igraph_disjoint_union.c create mode 100644 tests/unit/igraph_disjoint_union.out create mode 100644 tests/unit/igraph_distances_floyd_warshall.c create mode 100644 tests/unit/igraph_distances_floyd_warshall.out create mode 100644 tests/unit/igraph_distances_floyd_warshall_speedup.c create mode 100644 tests/unit/igraph_distances_floyd_warshall_speedup.out create mode 100644 tests/unit/igraph_distances_johnson.c create mode 100644 tests/unit/igraph_distances_johnson.out create mode 100644 tests/unit/igraph_diversity.c create mode 100644 tests/unit/igraph_diversity.out create mode 100644 tests/unit/igraph_dominator_tree.c create mode 100644 tests/unit/igraph_dominator_tree.out create mode 100644 tests/unit/igraph_dot_product_game.c create mode 100644 tests/unit/igraph_dot_product_game.out create mode 100644 tests/unit/igraph_dyad_census.c create mode 100644 tests/unit/igraph_dyad_census.out create mode 100644 tests/unit/igraph_ecc.c create mode 100644 tests/unit/igraph_ecc.out create mode 100644 tests/unit/igraph_eccentricity.c create mode 100644 tests/unit/igraph_eccentricity.out create mode 100644 tests/unit/igraph_eccentricity_dijkstra.c create mode 100644 tests/unit/igraph_eccentricity_dijkstra.out create mode 100644 tests/unit/igraph_edge_betweenness.c create mode 100644 tests/unit/igraph_edge_betweenness.out create mode 100644 tests/unit/igraph_edge_betweenness_subset.c create mode 100644 tests/unit/igraph_edge_betweenness_subset.out create mode 100644 tests/unit/igraph_edge_disjoint_paths.c create mode 100644 tests/unit/igraph_edges.c create mode 100644 tests/unit/igraph_edges.out create mode 100644 tests/unit/igraph_eigen_matrix.c create mode 100644 tests/unit/igraph_eigen_matrix.out create mode 100644 tests/unit/igraph_eigen_matrix2.c create mode 100644 tests/unit/igraph_eigen_matrix2.out create mode 100644 tests/unit/igraph_eigen_matrix3.c create mode 100644 tests/unit/igraph_eigen_matrix3.out create mode 100644 tests/unit/igraph_eigen_matrix4.c create mode 100644 tests/unit/igraph_eigen_matrix4.out create mode 100644 tests/unit/igraph_eigen_matrix_symmetric.c create mode 100644 tests/unit/igraph_eigen_matrix_symmetric.out create mode 100644 tests/unit/igraph_eigen_matrix_symmetric_arpack.c create mode 100644 tests/unit/igraph_eigen_matrix_symmetric_arpack.out create mode 100644 tests/unit/igraph_eigenvector_centrality.c create mode 100644 tests/unit/igraph_eigenvector_centrality.out create mode 100644 tests/unit/igraph_empty.c create mode 100644 tests/unit/igraph_es_all_between.c create mode 100644 tests/unit/igraph_es_path.c create mode 100644 tests/unit/igraph_establishment_game.c create mode 100644 tests/unit/igraph_eulerian_cycle.c create mode 100644 tests/unit/igraph_eulerian_cycle.out create mode 100644 tests/unit/igraph_eulerian_path.c create mode 100644 tests/unit/igraph_eulerian_path.out create mode 100644 tests/unit/igraph_extended_chordal_ring.c create mode 100644 tests/unit/igraph_feedback_arc_set_undirected.c create mode 100644 tests/unit/igraph_forest_fire_game.c create mode 100644 tests/unit/igraph_forest_fire_game.out create mode 100644 tests/unit/igraph_from_prufer.c create mode 100644 tests/unit/igraph_from_prufer.out create mode 100644 tests/unit/igraph_full_citation.c create mode 100644 tests/unit/igraph_full_multipartite.c create mode 100644 tests/unit/igraph_full_multipartite.out create mode 100644 tests/unit/igraph_generalized_petersen.c create mode 100644 tests/unit/igraph_get_adjacency.c create mode 100644 tests/unit/igraph_get_adjacency.out create mode 100644 tests/unit/igraph_get_adjacency_sparse.c create mode 100644 tests/unit/igraph_get_adjacency_sparse.out create mode 100644 tests/unit/igraph_get_all_shortest_paths_dijkstra.c create mode 100644 tests/unit/igraph_get_all_shortest_paths_dijkstra.out create mode 100644 tests/unit/igraph_get_all_simple_paths.c create mode 100644 tests/unit/igraph_get_all_simple_paths.out create mode 100644 tests/unit/igraph_get_biadjacency.c create mode 100644 tests/unit/igraph_get_biadjacency.out create mode 100644 tests/unit/igraph_get_eid.c create mode 100644 tests/unit/igraph_get_isomorphisms_vf2.c create mode 100644 tests/unit/igraph_get_isomorphisms_vf2.out create mode 100644 tests/unit/igraph_get_k_shortest_paths.c create mode 100644 tests/unit/igraph_get_k_shortest_paths.out create mode 100644 tests/unit/igraph_get_laplacian.c create mode 100644 tests/unit/igraph_get_laplacian.out create mode 100644 tests/unit/igraph_get_shortest_path_astar.c create mode 100644 tests/unit/igraph_get_shortest_path_astar.out create mode 100644 tests/unit/igraph_get_shortest_path_bellman_ford.c create mode 100644 tests/unit/igraph_get_shortest_path_bellman_ford.out create mode 100644 tests/unit/igraph_get_shortest_paths2.c create mode 100644 tests/unit/igraph_get_shortest_paths2.out create mode 100644 tests/unit/igraph_get_shortest_paths_bellman_ford.c create mode 100644 tests/unit/igraph_get_shortest_paths_bellman_ford.out create mode 100644 tests/unit/igraph_get_shortest_paths_dijkstra.c create mode 100644 tests/unit/igraph_get_shortest_paths_dijkstra.out create mode 100644 tests/unit/igraph_get_stochastic.c create mode 100644 tests/unit/igraph_get_stochastic.out create mode 100644 tests/unit/igraph_get_stochastic_sparse.c create mode 100644 tests/unit/igraph_get_stochastic_sparse.out create mode 100644 tests/unit/igraph_get_subisomorphisms_vf2.c create mode 100644 tests/unit/igraph_get_subisomorphisms_vf2.out create mode 100644 tests/unit/igraph_gomory_hu_tree.c create mode 100644 tests/unit/igraph_graph_center.c create mode 100644 tests/unit/igraph_graph_center.out create mode 100644 tests/unit/igraph_graph_power.c create mode 100644 tests/unit/igraph_graph_power.out create mode 100644 tests/unit/igraph_grg_game.c create mode 100644 tests/unit/igraph_growing_random_game.c create mode 100644 tests/unit/igraph_has_mutual.c create mode 100644 tests/unit/igraph_hexagonal_lattice.c create mode 100644 tests/unit/igraph_hexagonal_lattice.out create mode 100644 tests/unit/igraph_hrg.c create mode 100644 tests/unit/igraph_hrg2.c create mode 100644 tests/unit/igraph_hrg3.c create mode 100644 tests/unit/igraph_hrg_create.c create mode 100644 tests/unit/igraph_hrg_create.out create mode 100644 tests/unit/igraph_hsbm_game.c create mode 100644 tests/unit/igraph_hsbm_game.out create mode 100644 tests/unit/igraph_hsbm_list_game.c create mode 100644 tests/unit/igraph_hsbm_list_game.out create mode 100644 tests/unit/igraph_i_incident.c create mode 100644 tests/unit/igraph_i_incident.out create mode 100644 tests/unit/igraph_i_layout_sphere.c create mode 100644 tests/unit/igraph_i_neighbors.c create mode 100644 tests/unit/igraph_i_neighbors.out create mode 100644 tests/unit/igraph_i_umap_fit_ab.c create mode 100644 tests/unit/igraph_i_umap_fit_ab.o create mode 100644 tests/unit/igraph_induced_subgraph.c create mode 100644 tests/unit/igraph_induced_subgraph.out create mode 100644 tests/unit/igraph_induced_subgraph_edges.c create mode 100644 tests/unit/igraph_induced_subgraph_edges.out create mode 100644 tests/unit/igraph_induced_subgraph_map.c create mode 100644 tests/unit/igraph_induced_subgraph_map.out create mode 100644 tests/unit/igraph_intersection.c create mode 100644 tests/unit/igraph_intersection.out create mode 100644 tests/unit/igraph_is_acyclic.c create mode 100644 tests/unit/igraph_is_biconnected.c create mode 100644 tests/unit/igraph_is_bigraphical.c create mode 100644 tests/unit/igraph_is_bigraphical.out create mode 100644 tests/unit/igraph_is_bipartite.c create mode 100644 tests/unit/igraph_is_chordal.c create mode 100644 tests/unit/igraph_is_chordal.out create mode 100644 tests/unit/igraph_is_clique.c create mode 100644 tests/unit/igraph_is_complete.c create mode 100644 tests/unit/igraph_is_connected.c create mode 100644 tests/unit/igraph_is_dag.c create mode 100644 tests/unit/igraph_is_eulerian.c create mode 100644 tests/unit/igraph_is_eulerian.out create mode 100644 tests/unit/igraph_is_forest.c create mode 100644 tests/unit/igraph_is_forest.out create mode 100644 tests/unit/igraph_is_forest2.c create mode 100644 tests/unit/igraph_is_graphical.c create mode 100644 tests/unit/igraph_is_graphical.out create mode 100644 tests/unit/igraph_is_mutual.c create mode 100644 tests/unit/igraph_is_mutual.out create mode 100644 tests/unit/igraph_is_same_graph.c create mode 100644 tests/unit/igraph_is_separator.c create mode 100644 tests/unit/igraph_is_tree.c create mode 100644 tests/unit/igraph_isomorphic.c create mode 100644 tests/unit/igraph_isomorphic.out create mode 100644 tests/unit/igraph_isomorphic_bliss.c create mode 100644 tests/unit/igraph_isomorphic_bliss.out create mode 100644 tests/unit/igraph_isomorphic_vf2.c create mode 100644 tests/unit/igraph_join.c create mode 100644 tests/unit/igraph_join.out create mode 100644 tests/unit/igraph_joint_degree_distribution.c create mode 100644 tests/unit/igraph_joint_degree_distribution.out create mode 100644 tests/unit/igraph_joint_type_distribution.c create mode 100644 tests/unit/igraph_joint_type_distribution.out create mode 100644 tests/unit/igraph_k_regular_game.c create mode 100644 tests/unit/igraph_k_regular_game.out create mode 100644 tests/unit/igraph_k_shortest_paths.out create mode 100644 tests/unit/igraph_kautz.c create mode 100644 tests/unit/igraph_lapack_dgeev.c create mode 100644 tests/unit/igraph_lapack_dgeevx.c create mode 100644 tests/unit/igraph_lapack_dgehrd.c create mode 100644 tests/unit/igraph_lapack_dgehrd.out create mode 100644 tests/unit/igraph_lapack_dgetrf.c create mode 100644 tests/unit/igraph_lapack_dgetrf.out create mode 100644 tests/unit/igraph_lapack_dgetrs.c create mode 100644 tests/unit/igraph_lapack_dgetrs.out create mode 100644 tests/unit/igraph_lapack_dsyevr.c create mode 100644 tests/unit/igraph_lastcit_game.c create mode 100644 tests/unit/igraph_lastcit_game.out create mode 100644 tests/unit/igraph_layout_bipartite.c create mode 100644 tests/unit/igraph_layout_bipartite.out create mode 100644 tests/unit/igraph_layout_davidson_harel.c create mode 100644 tests/unit/igraph_layout_davidson_harel.out create mode 100644 tests/unit/igraph_layout_drl.c create mode 100644 tests/unit/igraph_layout_drl_3d.c create mode 100644 tests/unit/igraph_layout_drl_3d.out create mode 100644 tests/unit/igraph_layout_fruchterman_reingold.c create mode 100644 tests/unit/igraph_layout_fruchterman_reingold.out create mode 100644 tests/unit/igraph_layout_fruchterman_reingold_3d.c create mode 100644 tests/unit/igraph_layout_fruchterman_reingold_3d.out create mode 100644 tests/unit/igraph_layout_gem.c create mode 100644 tests/unit/igraph_layout_gem.out create mode 100644 tests/unit/igraph_layout_graphopt.c create mode 100644 tests/unit/igraph_layout_graphopt.out create mode 100644 tests/unit/igraph_layout_grid.c create mode 100644 tests/unit/igraph_layout_grid.out create mode 100644 tests/unit/igraph_layout_kamada_kawai.c create mode 100644 tests/unit/igraph_layout_kamada_kawai.out create mode 100644 tests/unit/igraph_layout_lgl.c create mode 100644 tests/unit/igraph_layout_lgl.out create mode 100644 tests/unit/igraph_layout_mds.c create mode 100644 tests/unit/igraph_layout_mds.out create mode 100644 tests/unit/igraph_layout_merge.c create mode 100644 tests/unit/igraph_layout_merge2.c create mode 100644 tests/unit/igraph_layout_merge2.out create mode 100644 tests/unit/igraph_layout_merge3.c create mode 100644 tests/unit/igraph_layout_random_3d.c create mode 100644 tests/unit/igraph_layout_random_3d.out create mode 100644 tests/unit/igraph_layout_reingold_tilford_circular.c create mode 100644 tests/unit/igraph_layout_reingold_tilford_circular.out create mode 100644 tests/unit/igraph_layout_reingold_tilford_extended.c create mode 100644 tests/unit/igraph_layout_reingold_tilford_extended.in create mode 100644 tests/unit/igraph_layout_sphere.c create mode 100644 tests/unit/igraph_layout_sphere.out create mode 100644 tests/unit/igraph_layout_star.c create mode 100644 tests/unit/igraph_layout_star.out create mode 100644 tests/unit/igraph_layout_sugiyama.c create mode 100644 tests/unit/igraph_layout_sugiyama.out create mode 100644 tests/unit/igraph_layout_umap.c create mode 100644 tests/unit/igraph_layout_umap.out create mode 100644 tests/unit/igraph_le_community_to_membership.c create mode 100644 tests/unit/igraph_le_community_to_membership.out create mode 100644 tests/unit/igraph_linegraph.c create mode 100644 tests/unit/igraph_list_triangles.c create mode 100644 tests/unit/igraph_list_triangles.out create mode 100644 tests/unit/igraph_local_scan_k_ecount.c create mode 100644 tests/unit/igraph_local_scan_k_ecount.out create mode 100644 tests/unit/igraph_local_scan_k_ecount_them.c create mode 100644 tests/unit/igraph_local_scan_k_ecount_them.out create mode 100644 tests/unit/igraph_local_scan_subset_ecount.c create mode 100644 tests/unit/igraph_local_scan_subset_ecount.out create mode 100644 tests/unit/igraph_local_transitivity.c create mode 100644 tests/unit/igraph_local_transitivity.out create mode 100644 tests/unit/igraph_maxflow.c create mode 100644 tests/unit/igraph_maxflow.out create mode 100644 tests/unit/igraph_maximal_cliques.c create mode 100644 tests/unit/igraph_maximal_cliques.out create mode 100644 tests/unit/igraph_maximal_cliques2.c create mode 100644 tests/unit/igraph_maximal_cliques2.out create mode 100644 tests/unit/igraph_maximal_cliques3.c create mode 100644 tests/unit/igraph_maximal_cliques3.out create mode 100644 tests/unit/igraph_maximal_cliques4.c create mode 100644 tests/unit/igraph_maximal_cliques4.out create mode 100644 tests/unit/igraph_maximal_cliques_file.c create mode 100644 tests/unit/igraph_maximal_cliques_file.out create mode 100644 tests/unit/igraph_maximum_bipartite_matching.c create mode 100644 tests/unit/igraph_mean_degree.c create mode 100644 tests/unit/igraph_minimum_size_separators.c create mode 100644 tests/unit/igraph_minimum_size_separators.out create mode 100644 tests/unit/igraph_modularity.c create mode 100644 tests/unit/igraph_modularity.out create mode 100644 tests/unit/igraph_modularity_matrix.c create mode 100644 tests/unit/igraph_modularity_matrix.out create mode 100644 tests/unit/igraph_moran_process.c create mode 100644 tests/unit/igraph_motifs_randesu.c create mode 100644 tests/unit/igraph_motifs_randesu.out create mode 100644 tests/unit/igraph_motifs_randesu_estimate.c create mode 100644 tests/unit/igraph_motifs_randesu_estimate.out create mode 100644 tests/unit/igraph_motifs_randesu_no.c create mode 100644 tests/unit/igraph_motifs_randesu_no.out create mode 100644 tests/unit/igraph_neighborhood.c create mode 100644 tests/unit/igraph_neighborhood.out create mode 100644 tests/unit/igraph_neighborhood_graphs.c create mode 100644 tests/unit/igraph_neighborhood_graphs.out create mode 100644 tests/unit/igraph_neighborhood_size.c create mode 100644 tests/unit/igraph_neighborhood_size.out create mode 100644 tests/unit/igraph_neighbors.c create mode 100644 tests/unit/igraph_pagerank.c create mode 100644 tests/unit/igraph_pagerank.out create mode 100644 tests/unit/igraph_path_length_hist.c create mode 100644 tests/unit/igraph_path_length_hist.out create mode 100644 tests/unit/igraph_perfect.c create mode 100644 tests/unit/igraph_permute_vertices.c create mode 100644 tests/unit/igraph_permute_vertices.out create mode 100644 tests/unit/igraph_power_law_fit.c create mode 100644 tests/unit/igraph_power_law_fit.out create mode 100644 tests/unit/igraph_preference_game.c create mode 100644 tests/unit/igraph_progress_handler_stderr.c create mode 100644 tests/unit/igraph_pseudo_diameter.c create mode 100644 tests/unit/igraph_pseudo_diameter.out create mode 100644 tests/unit/igraph_pseudo_diameter_dijkstra.c create mode 100644 tests/unit/igraph_pseudo_diameter_dijkstra.out create mode 100644 tests/unit/igraph_psumtree.c create mode 100644 tests/unit/igraph_qsort.c create mode 100644 tests/unit/igraph_qsort.out create mode 100644 tests/unit/igraph_qsort_r.c create mode 100644 tests/unit/igraph_qsort_r.out create mode 100644 tests/unit/igraph_random_sample.c create mode 100644 tests/unit/igraph_random_walk.c create mode 100644 tests/unit/igraph_random_walk.out create mode 100644 tests/unit/igraph_read_graph_graphdb.c create mode 100644 tests/unit/igraph_read_graph_graphdb.out create mode 100644 tests/unit/igraph_read_graph_graphml.c create mode 100644 tests/unit/igraph_read_graph_graphml.out create mode 100644 tests/unit/igraph_realize_bipartite_degree_sequence.c create mode 100644 tests/unit/igraph_realize_bipartite_degree_sequence.out create mode 100644 tests/unit/igraph_realize_degree_sequence.c create mode 100644 tests/unit/igraph_realize_degree_sequence.out create mode 100644 tests/unit/igraph_recent_degree_aging_game.c create mode 100644 tests/unit/igraph_recent_degree_aging_game.out create mode 100644 tests/unit/igraph_recent_degree_game.c create mode 100644 tests/unit/igraph_recent_degree_game.out create mode 100644 tests/unit/igraph_residual_graph.c create mode 100644 tests/unit/igraph_reverse_edges.c create mode 100644 tests/unit/igraph_rewire.c create mode 100644 tests/unit/igraph_rewire_directed_edges.c create mode 100644 tests/unit/igraph_rewire_directed_edges.out create mode 100644 tests/unit/igraph_rng_get_integer.c create mode 100644 tests/unit/igraph_rng_get_integer.out create mode 100644 tests/unit/igraph_roulette_wheel_imitation.c create mode 100644 tests/unit/igraph_running_mean.c create mode 100644 tests/unit/igraph_running_mean.out create mode 100644 tests/unit/igraph_sample_dirichlet.c create mode 100644 tests/unit/igraph_sample_dirichlet.out create mode 100644 tests/unit/igraph_sample_sphere.c create mode 100644 tests/unit/igraph_sbm_game.c create mode 100644 tests/unit/igraph_sbm_game.out create mode 100644 tests/unit/igraph_set_progress_handler.c create mode 100644 tests/unit/igraph_set_progress_handler.out create mode 100644 tests/unit/igraph_similarity.c create mode 100644 tests/unit/igraph_similarity.out create mode 100644 tests/unit/igraph_simple_interconnected_islands_game.c create mode 100644 tests/unit/igraph_simple_interconnected_islands_game.out create mode 100644 tests/unit/igraph_sir.c create mode 100644 tests/unit/igraph_sir.out create mode 100644 tests/unit/igraph_solve_lsap.c create mode 100644 tests/unit/igraph_solve_lsap.out create mode 100644 tests/unit/igraph_spanner.c create mode 100644 tests/unit/igraph_sparsemat2.c create mode 100644 tests/unit/igraph_sparsemat2.out create mode 100644 tests/unit/igraph_sparsemat5.c create mode 100644 tests/unit/igraph_sparsemat5.out create mode 100644 tests/unit/igraph_sparsemat9.c create mode 100644 tests/unit/igraph_sparsemat_droptol.c create mode 100644 tests/unit/igraph_sparsemat_droptol.out create mode 100644 tests/unit/igraph_sparsemat_fkeep.c create mode 100644 tests/unit/igraph_sparsemat_fkeep.out create mode 100644 tests/unit/igraph_sparsemat_getelements_sorted.c create mode 100644 tests/unit/igraph_sparsemat_getelements_sorted.out create mode 100644 tests/unit/igraph_sparsemat_is_symmetric.c create mode 100644 tests/unit/igraph_sparsemat_iterator_idx.c create mode 100644 tests/unit/igraph_sparsemat_minmax.c create mode 100644 tests/unit/igraph_sparsemat_minmax.out create mode 100644 tests/unit/igraph_sparsemat_nonzero_storage.c create mode 100644 tests/unit/igraph_sparsemat_normalize.c create mode 100644 tests/unit/igraph_sparsemat_normalize.out create mode 100644 tests/unit/igraph_sparsemat_view.c create mode 100644 tests/unit/igraph_sparsemat_view.out create mode 100644 tests/unit/igraph_sparsemat_which_minmax.c create mode 100644 tests/unit/igraph_sparsemat_which_minmax.out create mode 100644 tests/unit/igraph_split_join_distance.c create mode 100644 tests/unit/igraph_split_join_distance.out create mode 100644 tests/unit/igraph_square_lattice.c create mode 100644 tests/unit/igraph_st_edge_connectivity.c create mode 100644 tests/unit/igraph_st_mincut.c create mode 100644 tests/unit/igraph_st_mincut.out create mode 100644 tests/unit/igraph_st_mincut_value.c create mode 100644 tests/unit/igraph_st_vertex_connectivity.c create mode 100644 tests/unit/igraph_st_vertex_connectivity.out create mode 100644 tests/unit/igraph_static_power_law_game.c create mode 100644 tests/unit/igraph_static_power_law_game.out create mode 100644 tests/unit/igraph_stochastic_imitation.c create mode 100644 tests/unit/igraph_strvector.c create mode 100644 tests/unit/igraph_strvector.out create mode 100644 tests/unit/igraph_subcomponent.c create mode 100644 tests/unit/igraph_subcomponent.out create mode 100644 tests/unit/igraph_subisomorphic.c create mode 100644 tests/unit/igraph_subisomorphic_lad.c create mode 100644 tests/unit/igraph_to_directed.c create mode 100644 tests/unit/igraph_to_directed.out create mode 100644 tests/unit/igraph_to_prufer.c create mode 100644 tests/unit/igraph_transitive_closure.c create mode 100644 tests/unit/igraph_transitive_closure.out create mode 100644 tests/unit/igraph_transitivity_avglocal_undirected.c create mode 100644 tests/unit/igraph_transitivity_avglocal_undirected.out create mode 100644 tests/unit/igraph_transitivity_barrat.c create mode 100644 tests/unit/igraph_transitivity_barrat.out create mode 100644 tests/unit/igraph_tree_from_parent_vector.c create mode 100644 tests/unit/igraph_tree_from_parent_vector.out create mode 100644 tests/unit/igraph_triangular_lattice.c create mode 100644 tests/unit/igraph_triangular_lattice.out create mode 100644 tests/unit/igraph_trussness.c create mode 100644 tests/unit/igraph_trussness.out create mode 100644 tests/unit/igraph_turan.c create mode 100644 tests/unit/igraph_turan.out create mode 100644 tests/unit/igraph_unfold_tree.c create mode 100644 tests/unit/igraph_unfold_tree.out create mode 100644 tests/unit/igraph_union.c create mode 100644 tests/unit/igraph_union.out create mode 100644 tests/unit/igraph_vector_floor.c create mode 100644 tests/unit/igraph_vector_floor.out create mode 100644 tests/unit/igraph_vector_lex_cmp.c create mode 100644 tests/unit/igraph_vector_lex_cmp.out create mode 100644 tests/unit/igraph_vertex_disjoint_paths.c create mode 100644 tests/unit/igraph_voronoi.c create mode 100644 tests/unit/igraph_voronoi.out create mode 100644 tests/unit/igraph_weighted_adjacency.c create mode 100644 tests/unit/igraph_weighted_adjacency.out create mode 100644 tests/unit/igraph_weighted_cliques.c create mode 100644 tests/unit/igraph_weighted_cliques.out create mode 100644 tests/unit/igraph_wheel.c create mode 100644 tests/unit/igraph_wheel.out create mode 100644 tests/unit/igraph_widest_paths.c create mode 100644 tests/unit/igraph_widest_paths.out create mode 100644 tests/unit/igraph_write_graph_dimacs_flow.c create mode 100644 tests/unit/igraph_write_graph_dimacs_flow.out create mode 100644 tests/unit/igraph_write_graph_dot.c create mode 100644 tests/unit/igraph_write_graph_dot.out create mode 100644 tests/unit/igraph_write_graph_leda.c create mode 100644 tests/unit/igraph_write_graph_leda.out create mode 100644 tests/unit/inclist.c create mode 100644 tests/unit/inclist.out create mode 100644 tests/unit/input.dl create mode 100644 tests/unit/isoclasses.c create mode 100644 tests/unit/isoclasses.out create mode 100644 tests/unit/isoclasses2.c create mode 100644 tests/unit/isomorphism_test.c create mode 100644 tests/unit/isomorphism_test.out create mode 100644 tests/unit/jdm.c create mode 100644 tests/unit/jdm.out create mode 100644 tests/unit/kary_tree.c create mode 100644 tests/unit/kary_tree.out create mode 100644 tests/unit/knn.c create mode 100644 tests/unit/knn.out create mode 100644 tests/unit/levc-stress.c create mode 100644 tests/unit/lineendings.c create mode 100644 tests/unit/lineendings.out create mode 100644 tests/unit/links.net create mode 100644 tests/unit/marked_queue.c create mode 100644 tests/unit/matrix.c create mode 100644 tests/unit/matrix.out create mode 100644 tests/unit/matrix2.c create mode 100644 tests/unit/matrix2.out create mode 100644 tests/unit/matrix3.c create mode 100644 tests/unit/matrix_complex.c create mode 100644 tests/unit/matrix_complex.out create mode 100644 tests/unit/maximal_cliques_callback.c create mode 100644 tests/unit/maximal_cliques_hist.c create mode 100644 tests/unit/maximal_cliques_hist.out create mode 100644 tests/unit/ncol.c create mode 100644 tests/unit/ncol.out create mode 100644 tests/unit/null_communities.c create mode 100644 tests/unit/overflow.c create mode 100644 tests/unit/pajek.c create mode 100644 tests/unit/pajek1.net create mode 100644 tests/unit/pajek2.c create mode 100644 tests/unit/pajek2.net create mode 100644 tests/unit/pajek2.out create mode 100644 tests/unit/pajek3.net create mode 100644 tests/unit/pajek4.net create mode 100644 tests/unit/pajek5.net create mode 100644 tests/unit/pajek6.net create mode 100644 tests/unit/pajek_arcslist.net create mode 100644 tests/unit/pajek_bip.net create mode 100644 tests/unit/pajek_bip2.net create mode 100644 tests/unit/pajek_bipartite.c create mode 100644 tests/unit/pajek_bipartite.out create mode 100644 tests/unit/pajek_bipartite2.c create mode 100644 tests/unit/pajek_bipartite2.out create mode 100644 tests/unit/pajek_edgeslist.net create mode 100644 tests/unit/pajek_signed.c create mode 100644 tests/unit/pajek_signed.net create mode 100644 tests/unit/pajek_signed.out create mode 100644 tests/unit/prop_caching.c create mode 100644 tests/unit/random_sampling.c create mode 100644 tests/unit/random_spanning_tree.c create mode 100644 tests/unit/reachability.c create mode 100644 tests/unit/reachability.out create mode 100644 tests/unit/ring.c create mode 100644 tests/unit/ring.out create mode 100644 tests/unit/rng_init_destroy_max_bits_name_set_default.c create mode 100644 tests/unit/rng_init_destroy_max_bits_name_set_default.out create mode 100644 tests/unit/rng_reproducibility.c create mode 100644 tests/unit/rng_reproducibility.out create mode 100644 tests/unit/set.c create mode 100644 tests/unit/set.out create mode 100644 tests/unit/si2_b06m_s20-bad1.A98 create mode 100644 tests/unit/si2_b06m_s20-bad2.A98 create mode 100644 tests/unit/si2_b06m_s20.A98 create mode 100644 tests/unit/simplify_and_colorize.c create mode 100644 tests/unit/simplify_and_colorize.out create mode 100644 tests/unit/single_target_shortest_path.c create mode 100644 tests/unit/single_target_shortest_path.out create mode 100644 tests/unit/spinglass.c create mode 100644 tests/unit/spinglass.out create mode 100644 tests/unit/stack.c create mode 100644 tests/unit/strvector_set_len_remove_print.c create mode 100644 tests/unit/strvector_set_len_remove_print.out create mode 100644 tests/unit/symmetric_tree.c create mode 100644 tests/unit/symmetric_tree.out create mode 100644 tests/unit/test.graphml create mode 100644 tests/unit/test_utilities.c create mode 100644 tests/unit/test_utilities.h create mode 100644 tests/unit/tls1.c create mode 100644 tests/unit/tls2.c create mode 100644 tests/unit/tls2.out create mode 100644 tests/unit/topological_sorting.c create mode 100644 tests/unit/topological_sorting.out create mode 100644 tests/unit/tree_game.c create mode 100644 tests/unit/triad_census.c create mode 100644 tests/unit/triad_census.out create mode 100644 tests/unit/trie.c create mode 100644 tests/unit/trie.out create mode 100644 tests/unit/utf8_with_bom.net create mode 100644 tests/unit/vector.c create mode 100644 tests/unit/vector.out create mode 100644 tests/unit/vector2.c create mode 100644 tests/unit/vector2.out create mode 100644 tests/unit/vector3.c create mode 100644 tests/unit/vector4.c create mode 100644 tests/unit/vector4.out create mode 100644 tests/unit/vector_list.c create mode 100644 tests/unit/vector_list.out create mode 100644 tests/unit/vector_ptr.c create mode 100644 tests/unit/vector_ptr_qsort_ind.out create mode 100644 tests/unit/vector_ptr_sort_ind.c create mode 100644 tests/unit/vector_qsort_ind.c create mode 100644 tests/unit/vector_qsort_ind.out create mode 100644 tests/unit/vertex_selectors.c create mode 100644 tests/unit/vertex_selectors.out create mode 100644 tests/unit/watts_strogatz_game.c create mode 100644 tests/unit/wikti_en_V_syn.elist create mode 100644 tests/unit/zapsmall.c create mode 100644 tests/unit/zapsmall.out create mode 100644 tests/unit/zero_allocs.c create mode 100644 tools/removeexamples.py create mode 100644 tools/strip_licenses_from_examples.py create mode 100644 vendor/CMakeLists.txt create mode 100644 vendor/cs/CMakeLists.txt create mode 100644 vendor/cs/License.txt create mode 100644 vendor/cs/cs.h create mode 100644 vendor/cs/cs_add.c create mode 100644 vendor/cs/cs_amd.c create mode 100644 vendor/cs/cs_chol.c create mode 100644 vendor/cs/cs_cholsol.c create mode 100644 vendor/cs/cs_compress.c create mode 100644 vendor/cs/cs_counts.c create mode 100644 vendor/cs/cs_cumsum.c create mode 100644 vendor/cs/cs_dfs.c create mode 100644 vendor/cs/cs_dmperm.c create mode 100644 vendor/cs/cs_droptol.c create mode 100644 vendor/cs/cs_dropzeros.c create mode 100644 vendor/cs/cs_dupl.c create mode 100644 vendor/cs/cs_entry.c create mode 100644 vendor/cs/cs_ereach.c create mode 100644 vendor/cs/cs_etree.c create mode 100644 vendor/cs/cs_fkeep.c create mode 100644 vendor/cs/cs_gaxpy.c create mode 100644 vendor/cs/cs_happly.c create mode 100644 vendor/cs/cs_house.c create mode 100644 vendor/cs/cs_ipvec.c create mode 100644 vendor/cs/cs_leaf.c create mode 100644 vendor/cs/cs_load.c create mode 100644 vendor/cs/cs_lsolve.c create mode 100644 vendor/cs/cs_ltsolve.c create mode 100644 vendor/cs/cs_lu.c create mode 100644 vendor/cs/cs_lusol.c create mode 100644 vendor/cs/cs_malloc.c create mode 100644 vendor/cs/cs_maxtrans.c create mode 100644 vendor/cs/cs_multiply.c create mode 100644 vendor/cs/cs_norm.c create mode 100644 vendor/cs/cs_permute.c create mode 100644 vendor/cs/cs_pinv.c create mode 100644 vendor/cs/cs_post.c create mode 100644 vendor/cs/cs_print.c create mode 100644 vendor/cs/cs_pvec.c create mode 100644 vendor/cs/cs_qr.c create mode 100644 vendor/cs/cs_qrsol.c create mode 100644 vendor/cs/cs_randperm.c create mode 100644 vendor/cs/cs_reach.c create mode 100644 vendor/cs/cs_scatter.c create mode 100644 vendor/cs/cs_scc.c create mode 100644 vendor/cs/cs_schol.c create mode 100644 vendor/cs/cs_spsolve.c create mode 100644 vendor/cs/cs_sqr.c create mode 100644 vendor/cs/cs_symperm.c create mode 100644 vendor/cs/cs_tdfs.c create mode 100644 vendor/cs/cs_transpose.c create mode 100644 vendor/cs/cs_updown.c create mode 100644 vendor/cs/cs_usolve.c create mode 100644 vendor/cs/cs_util.c create mode 100644 vendor/cs/cs_utsolve.c create mode 100644 vendor/pcg/CMakeLists.txt create mode 100644 vendor/pcg/LICENSE.txt create mode 100644 vendor/pcg/pcg-advance-128.c create mode 100644 vendor/pcg/pcg-advance-64.c create mode 100644 vendor/pcg/pcg-output-128.c create mode 100644 vendor/pcg/pcg-output-32.c create mode 100644 vendor/pcg/pcg-output-64.c create mode 100644 vendor/pcg/pcg-rngs-128.c create mode 100644 vendor/pcg/pcg-rngs-64.c create mode 100644 vendor/pcg/pcg_variants.h diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md new file mode 100644 index 0000000..9c52d1b --- /dev/null +++ b/ACKNOWLEDGEMENTS.md @@ -0,0 +1,209 @@ +# Acknowledgements + +[igraph](https://igraph.org) includes or links to code from the following sources. + + +#### [bliss 0.75](https://users.aalto.fi/~tjunttil/bliss/) + +Copyright (c) 2003-2021 Tommi Junttila. + +License: [GNU LGPLv3][lgpl3] + + +#### [Cliquer 1.21](https://users.aalto.fi/~pat/cliquer.html) + +Copyright (C) 2002 Sampo Niskanen, Patric ÖstergÃ¥rd. + +License: [GNU GPLv2][gpl2] or later + + +#### [PRPACK](https://github.com/dgleich/prpack) + +Copyright (C) David Kurokawa, David Gleich, Chen Greif. + + +#### [gengraph](https://www-complexnetworks.lip6.fr/~latapy/FV/generation.html) + +Algorithm by Fabien Viger and Matthieu Latapy. + +Implementation Copyright (C) Fabien Viger. + +License: [GNU GPLv2][gpl2] or later + + +#### [Walktrap 0.2](https://www-complexnetworks.lip6.fr/~latapy/PP/walktrap.html) + +Algorithm by Pascal Pons and Matthieu Latapy. + +Implementation Copyright (C) 2004-2005 Pascal Pons. + +License: [GNU GPLv2][gpl2] or later + + +#### [plfit](https://github.com/ntamas/plfit) + +Copyright (C) 2010-2011 Tamás Nepusz. + +License: [GNU GPLv2][gpl2] or later + +#### DrL + +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. + + +#### [Hierarchical Random Graphs](http://tuvalu.santafe.edu/~aaronc/hierarchy/) + +Copyright (C) 2006-2008 Aaron Clauset. + +License: [GNU GPLv2][gpl2] or later + + +#### Spinglass community detection + +Copyright (C) 2004 by Joerg Reichardt. + +License: [GNU GPLv2][gpl2] or later + + +#### [LAD version 1](http://liris.cnrs.fr/csolnon/LAD.html) + +Copyright (C) Christine Solnon. + +License: [CeCILL-B license](https://cecill.info/licences.en.html) + + +#### [LAPACK 3.5.0](http://www.netlib.org/lapack/) + +Copyright (c) 1992-2011 The University of Tennessee and The University of Tennessee Research Foundation. All rights reserved. + +Copyright (c) 2000-2011 The University of California Berkeley. All rights reserved. + +Copyright (c) 2006-2012 The University of Colorado Denver. All rights reserved. + +License: [New BSD license](http://www.netlib.org/lapack/LICENSE.txt) + +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 listed + in this license in the documentation and/or other materials + provided with the distribution. + +- Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +The copyright holders provide no reassurances that the source code +provided does not infringe any patent, copyright, or any other +intellectual property rights of third parties. The copyright holders +disclaim any liability to any recipient for claims brought against +recipient by any third party for infringement of that parties +intellectual property rights. + +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. + + +#### [f2c](http://www.netlib.org/f2c/) + +Copyright 1990 - 1997 by AT&T, Lucent Technologies and Bellcore. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T, Bell Laboratories, +Lucent or Bellcore or any of their entities not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +AT&T, Lucent and Bellcore disclaim all warranties with regard to +this software, including all implied warranties of +merchantability and fitness. In no event shall AT&T, Lucent or +Bellcore be liable for any special, indirect or consequential +damages or any damages whatsoever resulting from loss of use, +data or profits, whether in an action of contract, negligence or +other tortious action, arising out of or in connection with the +use or performance of this software. + + +#### [SuiteSparse](http://www.suitesparse.com) + + * CXSPARSE: a Concise Sparse Matrix package - Extended. Copyright (c) 2006-2017, Timothy A. Davis. + + License: [GNU LGPLv2.1][lgpl2] or later + + +#### [GLPK (GNU Linear Programming Kit) Version 5.0](https://www.gnu.org/software/glpk/) + +Copyright (C) 2000-2020 Free Software Foundation, Inc. + +Written by Andrew Makhorin, Department for Applied Informatics, +Moscow Aviation Institute, Moscow, Russia. E-mail: . + +License: [GNU GPLv3][gpl3] or later + + +#### [GMP (GNU Multiple Precision Arithmetic Library) and mini-gmp](https://gmplib.org/) + +Copyright (C) Free Software Foundation, Inc. + +License: [GNU LGPLv3][lgpl3] or later; or [GNU GPLv2][gpl2] or later + + +#### [libxml2](http://xmlsoft.org/) + +Copyright (C) 1998-2012 Daniel Veillard. + +License: [MIT license][mit] + + + [mit]: https://opensource.org/licenses/mit-license.html + [gpl2]: https://www.gnu.org/licenses/gpl-2.0.html + [lgpl2]: https://www.gnu.org/licenses/lgpl-2.1.html + [gpl3]: https://www.gnu.org/licenses/gpl-3.0.html + [lgpl3]: https://www.gnu.org/licenses/lgpl-3.0.html diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..446955d --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Gabor Csardi +Tamas Nepusz +Szabolcs Horvat +Vincent Traag +Fabio Zanini +Daniel Noom diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d617129 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1427 @@ +# igraph C library changelog + +## [0.10.13] + +### Added + + - `igraph_bitset_fill()` sets all elements of a bitset to the same value (experimental function). + - `igraph_bitset_null()` clears all elements of a bitset (experimental function). + - `igraph_bitset_is_all_zero()`, `igraph_bitset_is_all_one()`, `igraph_bitset_is_any_zero()`, `igraph_bitset_is_any_one()` check if any/all elements of a bitset are zeros/ones (experimental functions). + - `igraph_chung_lu_game()` implements the classic Chung-Lu model, as well as a number of its variants (experimental function). + - `igraph_mean_degree()` computes the average of vertex degrees (experimental function). + - `igraph_count_loops()` counts self-loops in the graph (experimental function). + - `igraph_is_clique()` checks if all pairs within a set of vertices are connected (experimental function). + - `igraph_is_independent_vertex_set()` checks if no pairs within a set of vertices are connected (experimental function). + - `igraph_hypercube()` creates a hypercube graph (experimental function). + - `igraph_vector_intersection_size_sorted()` counts elements common to two sorted vectors (experimental function). + - `igraph_stack_capacity()` returns the allocated capacity of a stack. + - `igraph_vector_is_all_finite()` checks if all elements in a vector are finite (i.e. neither NaN nor Inf). + +### Fixed + + - Fixed a bug that incorrectly cached that a graph has no multiple edges when `igraph_init_adjlist()` was called with `IGRAPH_NO_LOOPS` and `IGRAPH_NO_MULTIPLE` and all the multi-edges were loop edges. + - `igraph_is_forest()` would fail to set the result variable when testing for a directed forest, and it was already cached that the graph was not an undirected forest. + - `igraph_hub_and_authority_scores()` no longer clips negative results to zeros when negative weights are present. + - Fixed an assertion failure in `igraph_realize_bipartite_degree_sequence()` with some non-graphical degree sequences when requesting simple bipartite graphs. + - `igraph_static_fitness_game()` checks the input more carefully, and avoids an infinite loop in rare edge cases, such as when (almost) all fitness scores are zero. + - `igraph_arpack_rnsolve()` used the incorrect error message text for some errors. This is now corrected. + - Corrected the detection of some MSVC-specific bitset intrinsics during configuration. + - Corrected a bug in the fallback implementation of `igraph_bitset_countl_zero()` when `IGRAPH_INTEGER_SIZE` was set to 32. This fallback implementation was _not_ used with GCC, Clang, or MSVC. + +### Changed + + - `igraph_is_graphical()` and `igraph_is_bigraphical()` are now linear-time in all cases, and generally several times faster than before (thanks to @gendelpiekel, contributed in #2605). + - `igraph_erdos_renyi_game_gnp()` can now generate graphs with more than a hundred million vertices. + - `igraph_hub_and_authority_scores()` now warns when negative edge weights are present. + - `igraph_layout_lgl()` now uses a BFS tree rooted in the vertex specified as `proot` to guide the layout. Previously it used an unspecified (arbitrary) spanning tree. + - Updated the internal heuristics used by igraph's ARPACK interface, `igraph_arpack_rssolve()` and `igraph_arpack_rnsolve()`, to improve the robustness of calculations. + - Updated the initial vector construction in `igraph_hub_and_authority_scores()`, `igraph_eigenvector_centrality()` and `igraph_(personalized_)pagerank()` with `IGRAPH_PAGERANK_ALGO_ARPACK`. This improves the robustness and convergence of calculations. + +### Other + + - Documentation improvements. + - Reduced the memory usage of several functions by using bitsets instead of boolean vectors. + - `igraph_vector_intersect_sorted()` has better performance when the input vector sizes are similar. + +## [0.10.12] - 2024-05-06 + +### Added + + - `igraph_transitive_closure()` computes the transitive closure of a graph (experimental function). + - `igraph_reachability()` determines which vertices are reachable from each other in a graph (experimental function). + - `igraph_count_reachable()` counts how many vertices are reachable from each vertex (experimental function). + - Added a bitset data structure, `igraph_bitset_t`, and a set of corresponding functions (experimental functionality). + +### Fixed + + - `igraph_community_label_propagation()` is now interruptible. + - `igraph_is_bipartite()` would on rare occasions return invalid results when the cache was employed. + - `igraph_weighted_adjacency()` correctly passes through NaN values with `IGRAPH_ADJ_MAX`, and correctly recognizes symmetric adjacency matrices containing NaN values with `IGRAPH_ADJ_UNDIRECTED`. + - `igraph_read_graph_gml()` can now read GML files that use ids larger than what is representable on 32 bits, provided that igraph was configured with a 64-bit `igraph_integer_t` size. + - Fixed a performance issue in `igraph_read_graph_graphml()` with files containing a very large number of entities, such as `>`. + - `igraph_read_graph_pajek()` has improved vertex ID validation that better matches that of Pajek's own behavior. + +### Changed + + - `igraph_eigenvector_centrality()` no longer issues a warning when the input is directed and weighted. When using this function, keep in mind that eigenvector centrality is well-defined only for (strongly) connected graphs, and edges with a zero weights are effectively treated as absent. + +### Deprecated + + - `igraph_transitive_closure_dag()` is deprecated in favour of `igraph_transitive_closure()` + +### Other + + - Documentation improvements. + - `igraph_strength()` and `igraph_degree(loops=false)` are now faster when calculating values for all vertices (contributed by @gendelpiekel in #2602) + +## [0.10.11] - 2024-04-02 + +### Added + + - `igraph_is_complete()` checks whether there is a connection between all pairs of vertices (experimental function, contributed by Aymeric Agon-Rambosson @aagon in #2510). + - `igraph_join()` creates the _join_ of two graphs (experimental function, contributed by Quinn Buratynski @GanzuraTheConsumer in #2508). + +### Fixed + + - Fixed a corruption of the "finally" stack in `igraph_write_graph_gml()` for certain invalid GML files. + - Fixed a memory leak in `igraph_write_graph_lgl()` when vertex names were present but edge weights were not. + - Fixed the handling of duplicate edge IDs in `igraph_subgraph_from_edges()`. + - Fixed conversion of sparse matrices to dense with `igraph_sparsemat_as_matrix()` when sparse matrix object did not make use of its full allocated capacity. + - `igraph_write_graph_ncol()` and `igraph_write_graph_lgl()` now refuse to write vertex names which would result in an invalid file that cannot be read back in. + - `igraph_write_graph_gml()` now ignores graph attributes called `edge` or `node` with a warning. Writing these would create an invalid GML file that igraph couldn't read back. + - `igraph_disjoint_union()` and `igraph_disjoint_union_many()` now check for overflow. + - `igraph_read_graph_graphml()` now correctly compares attribute values with certain expected values, meaning that prefixes of valid values of `attr.type` are not accepted anymore. + - Empty IDs are not allowed any more in `` tags of GraphML files as this is a violation of the GraphML specification. + - `igraph_is_separator()` and `igraph_is_minimal_separator()` now work correctly with disconnected graphs. + - `igraph_linegraph()` now considers self-loops to be self-adjacent in undirected graphs, bringing consistency with how directed graphs were already handled in previous versions. + - `igraph_all_st_mincuts()` now correctly returns all minimum cuts. This also fixes a problem with `igraph_minimum_size_separators()`. + - Corrected minor error in `igraph_community_label_propagation()` when adding labels to isolated nodes with some fixed labels present. + - `igraph_community_spinglass()` no longer crashes when passing an edgeless graph and an empty weight vector. + - `igraph_rewire()` no longer crashes on graphs with more than three vertices but fewer than two edges. + +### Changed + + - `igraph_rewire()` on longer throws an error on graphs with fewer than four vertices. These graphs are now returned unchanged, just like other graphs which are the unique realization of their degree sequence. + +### Other + + - Performance: `igraph_is_simple()` now makes more granular use of the cache. + - Performance: `igraph_degree()` now makes use of the cache when checking for self-loops. + - The performance of `igraph_is_minimal_separator()` was improved. + - `igraph_is_graphical()` now performs graphicality checks for degree sequences of simple directed graphs in linear time, an improvement from the previously used quadratic algorithm (contributed by Arnar Bjarni Arnarson @Tagl in #2537). + - Documentation improvements. + +## [0.10.10] - 2024-02-13 + +### Fixed + + - When `igraph_is_forest()` determined that a graph is not a directed forest, and the `roots` output parameter was set to `NULL`, it would incorrectly cache that the graph is also not an undirected forest. + - `igraph_spanner()` now correctly ignores edge directions, and no longer crashes on directed graphs. + +### Deprecated + + - `igraph_are_connected()` is renamed to `igraph_are_adjacent()`; the old name is kept available until at least igraph 1.0. + +### Other + + - Documentation improvements. + +## [0.10.9] - 2024-02-02 + +### Added + + - `igraph_is_biconnected()` checks if a graph is biconnected. + - `igraph_realize_bipartite_degree_sequence()` constructs a bipartite graph that has the given bidegree sequence, optionally ensuring that it is connected (PR #2425 by Lára Margrét Hólmfríðardóttir @larah19). + +### Fixed + + - More robust error handling in HRG code. + - Fixed infinite loop in `igraph_hrg_sample_many()`. + - `igraph_community_fastgreedy()` no longer crashes when providing a modularity vector only, but not a merges matrix of membership vector. + - The graph property cache was not initialized correctly on systems where the size of `bool` was not 1 byte (#2477). + - Compatibility with libxml2 version 2.12 (#2442). + +### Deprecated + + - The macro `STR()` is deprecated; use the function `igraph_strvector_get()` instead. + +### Other + + - Performance: Reduced memory usage and improved initialization performance for `igraph_strvector_t`. + - Performance: Improved cache use by `igraph_is_bipartite()`. + - The documentation is now also generated in Texinfo format. + - Documentation improvements. + +## [0.10.8] - 2023-11-17 + +### Added + + - `igraph_joint_degree_matrix()` computes the joint degree matrix, i.e. counts connections between vertices of different degrees (PR #2407 by Lára Margrét Hólmfríðardóttir @larah19). + - `igraph_joint_degree_distribution()` computes the joint distribution of degrees at either end of edges. + - `igraph_joint_type_distribution()` computes the joint distribution of vertex categories at either end of edges, i.e. the mixing matrix. + - `igraph_degree_correlation_vector()` computes the degree correlation function and its various directed generalizations. + +### Changed + + - The behaviour of the Pajek format reader and writer is now more closely aligned with the Pajek software and the reader is more tolerant of input it cannot interpret. Only those vertex and edge parameters are treated as valid which Pajek itself understands, therefore support for `size` is now dropped, and support for the `font` edge parameter is added. See http://mrvar.fdv.uni-lj.si/pajek/DrawEPS.htm for more information. Invalid/unrecognized parameters are now converted to igraph attributes by the reader, but just as before, they are not output by the writer. + - The Pajek format writer now encodes newline and quotation mark characters in a Pajek-compatible manner (`\n` and `"`, respectively). + - `igraph_avg_nearest_neighbor_degree()` now supports non-simple graphs. + +### Fixed + + - Resolved "ignoring duplicate libraries" warning when building tests with Xcode 15 on macOS. + - Fixed the handling of duplicate vertex IDs in `igraph_induced_subgraph()`. + - `igraph_vector_which_min()` and `igraph_vector_which_max()` no longer allow zero-length input, which makes them consistent with other similar functions, and was the originally intended behaviour. Passing zero-length input is invalid use and currently triggers an assertion failure. + - `igraph_erdos_renyi_game_gnm()` and `igraph_erdos_renyi_game_gnp()` are now interruptible. + - `igraph_de_bruijn()` and `igraph_kautz()` are now interruptible. + - `igraph_full()`, `igraph_full_citation()`, `igraph_full_multipartite()` and `igraph_turan()` are now interruptible. + - `igraph_avg_nearest_neighbor_degree()` did not compute `knnk` correctly in the weighted case. + - Fixed variadic arguments of invalid types, which could cause incorrect behaviour with `igraph_matrix_print()`, as well as test suite failures, on some platforms. 32-bit x86 was affected when setting `IGRAPH_INTEGER_SIZE` to 64. + - `igraph_subisomorphic_lad()` now returns a single null map when the pattern is the null graph. + - `igraph_community_spinglass()` now checks its parameters more carefully. + - `igraph_similarity_dice_pairs()` and `igraph_similarity_jaccard_pairs()` now validate vertex IDs. + - `igraph_maxflow()` now returns an error code if the source and target vertices are the same. It used to get stuck in an infinite loop in earlier versions when the `flow` argument was non-NULL. + +### Other + + - Updated vendored mini-gmp to 6.3.0. + - `igraph_connected_components()` makes better use of the cache, improving overall performance. + - Documentation improvements. + +## [0.10.7] - 2023-09-04 + +### Added + + - `igraph_radius_dijkstra()` computes the graph radius with weighted edges (experimental function). + - `igraph_graph_center_dijkstra()` computes the graph center, i.e. the set of minimum eccentricity vertices, with weighted edges (experimental function). + +### Fixed + + - `igraph_full_bipartite()` now checks for overflow. + - `igraph_bipartite_game_gnm()` and `igraph_bipartite_game_gnp()` are now more robust to overflow. + - Bipartite graph creation functions now check input arguments. + - `igraph_write_graph_dot()` now quotes real numbers written in exponential notation as necessary. + - Independent vertex set finding functions could trigger the fatal error "Finally stack too large" when called on large graphs. + +### Deprecated + + - `igraph_bipartite_game()` is now deprecated; use `igraph_bipartite_game_gnm()` and `igraph_bipartite_game_gnp()` instead. + +### Other + + - Documentation improvements. + +## [0.10.6] - 2023-07-13 + +### Fixed + + - Compatibility with libxml2 2.11. + - Fixed some converge failures in `igraph_community_voronoi()`. + - `IGRAPH_CALLOC()` and `IGRAPH_REALLOC()` now check for overflow. + - CMake packages created with the `install` target of the CMake build system are now relocatable, i.e. the generated `igraph-targets.cmake` file does not contain absolute paths any more. + +## [0.10.5] - 2023-06-29 + +### Added + + - `igraph_graph_power()` computes the kth power of a graph (experimental function). + - `igraph_community_voronoi()` for detecting communities using Voronoi partitioning (experimental function). + +### Changed + + - `igraph_community_walktrap()` no longer requires `modularity` and `merges` to be non-NULL when `membership` is non-NULL. + - `igraph_isomorphic()` now supports multigraphs. + - Shortest path related functions now consistently ignore edges with positive infinite weights. + +### Fixed + + - `igraph_hub_and_authority_scores()`, `igraph_hub_score()` and `igraph_authority_score()` considered self-loops only once on the diagonal of the adjacency matrix of undirected graphs, thus the result was not identical to that obtained by `igraph_eigenvector_centrality()` on loopy undirected graphs. This is now corrected. + - `igraph_community_infomap()` now checks edge and vertex weights for validity. + - `igraph_minimum_spanning_tree()` and `igraph_minimum_spanning_tree_prim()` now check that edge weights are not NaN. + - Fixed an initialization error in the string attribute combiner of the C attribute handler. + - Fixed an issue with the weighted clique number calculation when all the weights were the same. + - HRG functions now require a graph with at least 3 vertices; previous versions crashed with smaller graphs. + - `igraph_arpack_rssolve()` and `igraph_arpack_rnsolve()`, i.e. the ARPACK interface in igraph, are now interruptible. As a result, several other functions that rely on ARPACK (eigenvector centrality, hub and authority scores, etc.) also became interruptible. + - `igraph_get_shortest_paths_dijkstra()`, `igraph_get_all_shortest_paths_dijkstra()` and `igraph_get_shortest_paths_bellman_ford()` now validate the `from` vertex. + - Fixed bugs in `igraph_local_scan_1_ecount()` for weighted undirected graphs which would miscount loops and multi-edges. + +### Deprecated + +- `igraph_automorphisms()` is now deprecated; its new name is `igraph_count_automorphisms()`. The old name is kept available until at least igraph 0.11. +- `igraph_hub_score()` and `igraph_authority_score()` are now deprecated. Use `igraph_hub_and_authority_scores()` instead. +- `igraph_get_incidence()` is now deprecated; its new name is `igraph_get_biadjacency()` to reflect that the returned matrix is an _adjacency_ matrix between pairs of vertices and not an _incidence_ matrix between vertices and edges. The new name is kept available until at least igraph 0.11. We plan to re-use the name in later versions to provide a proper incidence matrix where the rows are vertices and the columns are edges. +- `igraph_hrg_dendrogram()` is deprecated because it requires an attribute handler and it goes against the convention of returning attributes in vectors where possible. Use `igraph_from_hrg_dendrogram()` instead, which constructs the dendrogram as an igraph graph _and_ returns the associated probabilities in a vector. + +### Other + + - Improved performance for `igraph_vertex_connectivity()`. + - `igraph_simplify()` makes use of the cache, and avoids simplification when the graph is already known to be simple. + - Documentation improvements. + +## [0.10.4] - 2023-01-26 + +### Added + + - `igraph_get_shortest_path_astar()` finds a shortest path with the A* algorithm. + - `igraph_vertex_coloring_greedy()` now supports the DSatur heuristics through `IGRAPH_COLORING_GREEDY_DSATUR` (#2284, thanks to @professorcode1). + +### Changed + + - The `test` build target now only _runs_ the unit tests, but it does not _build_ them. In order to both build and run tests, use the `check` target, which continues to behave as before (PR #2291). + - The experimental function `igraph_distances_floyd_warshall()` now has `from` and `to` parameters for choosing source and target vertices. + - The experimental function `igraph_distances_floyd_warshall()` now has an additional `method` parameter to select a specific algorithm. A faster "Tree" variant of the Floyd-Warshall algorithm is now available (#2267, thanks to @rfulekjames). + +### Fixed + + - The Bellman-Ford shortest path finder is now interruptible. + - The Floyd-Warshall shortest path finder is now interruptible. + - Running CTest no longer builds the tests automatically, as this interfered with VSCode, which would invoke the `ctest` executable after configuring a project in order to determine test executables. Use the `build_tests` target to build the tests first, or use the `check` target to both _build_ and _run_ all unit tests (PR #2291). + +### Other + + - Improved the performance and memory usage of `igraph_widest_path_widths_floyd_warshall()`. + - Documentation improvements. + +## [0.10.3] - 2022-12-30 + +### Added + + - `igraph_matrix_init_array()` to initialize an igraph matrix by copying an existing C array in column-major or row-major order. + - `igraph_layout_umap_compute_weights()` computes weights for the UMAP layout algorithm from distances. This used to be part of `igraph_layout_umap()`, but it is now in a separate function to allow the user to experiment with different weighting schemes. + - `igraph_triangular_lattice()` to generate triangular lattices of various kinds (#2235, thanks to @rfulekjames). + - `igraph_hexagonal_lattice()` to generate hexagonal lattices of various kinds (#2262, thanks to @rfulekjames). + - `igraph_tree_from_parent_vector()` to create a tree or a forest from a parent vector (i.e. a vector that encodes the parent vertex of each vertex). + - `igraph_induced_subgraph_edges()` produces the IDs of edges contained within a subgraph induced by the given vertices. + +### Changed + + - The signature of the experimental `igraph_layout_umap()` function changed; the last argument is now a Boolean that specifies whether distances should already be treated as weights, and the sampling probability argument was removed. + +### Fixed + + - `igraph_transitivity_barrat()`, `igraph_community_fluid_communities()`, `igraph_sir()`, `igraph_trussness()` and graphlet functions did not correctly detect when a directed input graph had effective multi-edges due to ignoring edge directions. Such graphs are now rejected by these functions. + - Fixed a bug in `igraph_2dgrid_move()` that sometimes crashed the Large Graph Layout function when a grid cell became empty. + - `igraph_pagerank()` and `igraph_personalized_pagerank()` would fail to converge when the ARPACK implementation was used and a vertex had more than one outgoing edge but all these edges had zero weights. + - `igraph_pagerank()` and `igraph_personalized_pagerank()` no longer allow negative weights. Previously, edges with negative weights were silently ignored when using the PRPACK implementation. The ARPACK implementation would issue a warning saying that they are ignored, but in fact it computed an incorrect result. + - `igraph_all_st_cuts()` and `igraph_all_st_mincuts()` no longer trigger the "Finally stack too large" fatal error when called on certain large graphs. This was a regression in igraph 0.10. + - `igraph_community_label_propagation()` no longer rounds weights to integers. This was a regression in igraph 0.10. + - `igraph_read_graph_graphdb()` does more thorough checks on the input file. + - `igraph_calloc()` did not zero-initialize the allocated memory. This is now corrected. Note that the macro `IGRAPH_CALLOC()` was _not_ affected. + - Fixed new warnings issued by the Xcode 14.1 toolchain. + +### Deprecated + +- `igraph_subgraph_edges()` is now deprecated to avoid confusion with `igraph_induced_subgraph_edges()`; its new name is `igraph_subgraph_from_edges()`. The old name is kept available until at least igraph 0.11. + +### Other + + - Significantly improved performance for `igraph_matrix_transpose()`. + - Documentation improvements. + +## [0.10.2] - 2022-10-14 + +### Added + + - `igraph_distances_cutoff()` and `igraph_distances_dijkstra_cutoff()` calculate shortest paths with an upper limit on the path length (experimental functions). + - `igraph_distances_floyd_warshall()` for computing all-pairs shortest path lengths in dense graphs (experimental function). + - `igraph_ecc()` computes the edge clustering coefficient of some edges (experimental function). + - `igraph_voronoi()` computes a Voronoi partitioning of vertices (experimental function). + - `igraph_count_multiple_1()` determines the multiplicity of a single edge in the graph. + - `igraph_dqueue_get()` accesses an element in a queue by index. + - `igraph_degree_1()` efficiently retrieves the degee of a single vertex. + - `igraph_lazy_adjlist_has()` and `igraph_lazy_inclist_has()` to check if adjacent vertices / incident edges have already been computed and stored for a given vertex in a lazy adjlist / inclist. + +### Changed + + - `igraph_edge()` now verifies that the input edge ID is valid. + - `igraph_community_leading_eigenvector()`, `igraph_adjacency_spectral_embedding()`, `igraph_laplacian_spectral_embedding()`, `igraph_arpack_rssolve()` and `igraph_arpack_rnsolve()` now generate a random starting vector using igraph's own RNG if needed instead of relying on LAPACK or ARPACK to do so. This makes sure that the results obtained from these functions remain the same if igraph's RNG is seeded with the same value. + - `igraph_community_leading_eigenvector()` does not stop the splitting process any more when there are multiple equally likely splits (indicated by the multiplicity of the leading eigenvector being larger than 1). The algorithm picks an arbitrary split instead and proceeds normally. + +### Fixed + + - Fixed a bug in `igraph_get_k_shortest_paths()` that sometimes yielded incorrect results on undirected graphs when the `mode` argument was set to `IGRAPH_OUT` or `IGRAPH_IN`. + - `igraph_trussness()` is now interruptible. + - `igraph_spanner()` is now interruptible. + - `igraph_layout_umap()` and `igraph_layout_umap3d()` are now interruptible. + - In some rare cases, roundoff errors would cause `igraph_distance_johnson()` to fail on graphs with negative weights. + - `igraph_eulerian_cycle()` and `igraph_eulerian_path()` now returns a more specific error code (`IGRAPH_ENOSOL`) when the graph contains no Eulerian cycle or path. + - `igraph_heap_init_array()` did not copy the array data correctly for non-real specializations. + - `igraph_layout_umap_3d()` now actually uses three dimensions. + - `igraph_layout_umap()` and `igraph_layout_umap_3d()` are now interruptible. + - `igraph_vit_create()` and `igraph_eit_create()` no longer fails when trying to create an iterator for the null graph or edgeless graph from an empty range-based vertex or edge selector. + - `igraph_write_graph_leda()` did not correctly print attribute names in some warning messages. + - Addressed new warnings introduced by Clang 15. + - In the generated pkg-config file, libxml2 is now placed in the `Requires.private` section instead of the `Libs.private` one. + +### Removed + + - Removed unused and undocumented `igraph_bfgs()` function. + - Removed the undocumented function `igraph_complex_mod()`. Use `igraph_complex_abs()` instead, as it has identical functionality. + +### Deprecated + + - The `IGRAPH_EDRL` error code was deprecated; the DrL algorithm now returns `IGRAPH_FAILURE` when it used to return `IGRAPH_EDRL` (not likely to happen in practice). + - The undocumented function `igraph_dqueue_e()` is now deprecated and replaced by `igraph_dqueue_get()`. + - `igraph_finite()`, `igraph_is_nan()`, `igraph_is_inf()`, `igraph_is_posinf()` and `igraph_is_neginf()` are now deprecated. They were relics from a time when no standard alternatives existed. Use the C99 standard `isfinite()`, `isnan()` and `isinf()` instead. + +### Other + + - Documentation improvements. + +## [0.10.1] - 2022-09-08 + +### Fixed + + - Corrected a regression (compared to igraph 0.9) in weighted clique search functions. + - `igraph_girth()` no longer fails when the graph has no cycles and the `girth` parameter is set to `NULL`. + - `igraph_write_graph_gml()` did not respect entity encoding options when writing the `Creator` line. + - Fixed potential memory leak on out-of-memory condition in `igraph_asymmetric_preference_game()`, `igraph_vs_copy()` and `igraph_es_copy()`. + - Fixed an assertion failure in `igraph_barabasi_game()` and `igraph_barabasi_aging_game()` when passing in negative degree exponents. + - Fixed a compilation failure with some old Clang versions. + +### Changed + + - `igraph_write_graph_leda()` can now write boolean attributes. + +### Other + + - Support for ARM64 on Windows. + - Documentation improvements. + +## [0.10.0] - 2022-09-05 + +### Release notes + +This release focuses on infrastructural improvements, stability, and making the igraph interface more consistent, more predictable and easier to use. It contains many API-breaking changes and function renamings, in preparation for a future 1.0 release, at which point the API will become stable. Changes in this direction are likely to continue through a 0.11 release. It is recommended that you migrate your code from 0.9 to 0.10 soon, to make the eventual transition to 1.0 easier. + +Some of the highlights are: + + - A consistent use of `igraph_integer_t` for all indices and most integer quantities, both in the API and internally. This type is 64-bit by default on all 64-bit systems, bringing support for very large graphs with more than 2 billion vertices. Previously, vertex and edge indices were often represented as `igraph_real_t`. The move to an `igraph_integer_t` also implies a change from `igraph_vector_t` to `igraph_vector_int_t` in many functions. + - The random number generation framework has been overhauled. Sampling from the full range of `igraph_integer_t` is now possible. Similarly, the sampling of random reals has been improved to utilize almost the full range of the mantissa of an `igraph_real_t`. + - There is a new fully memory-managed container type for lists of vectors (`igraph_vector_list_t`), replacing most previous uses of the non-managed `igraph_vector_ptr_t`. Functions that previously used `igraph_vector_ptr_t` to return results and relied on the user to manage memory appropriately are now using `igraph_vector_list_t`, `igraph_graph_list_t` or similar and manage memory on their own. + - Some simple graph properties, such as whether a graph contains self-loops or multi-edges, or whether it is connected, are now cached in the graph data structure. Querying these properties for a second time will take constant computational time. The `igraph_invalidate_cache()` function is provided for debugging purposes. It will invaidate all cache entries. + - File format readers are much more robust and more tolerant of invalid input. + - igraph is much more resilient to overflow errors. + - Many improvements to robustness and reliability, made possible by internal refactorings. + +### Breaking changes + + - igraph now requires CMake 3.18 or later. + - In order to facilitate the usage of graphs with more than 2 billion vertices and edges, we have made the size of the `igraph_integer_t` data type to be 32 bits on 32-bit platforms and 64 bits on 64-bit platforms by default. You also have the option to compile a 32-bit igraph variant on a 64-bit platform by changing the `IGRAPH_INTEGER_SIZE` build variable in CMake to 32. + - `igraph_bool_t` is now a C99 `bool` and not an `int`. Similarly, `igraph_vector_bool_t` now consumes `sizeof(bool)` bytes per entry only, not `sizeof(int)`. The standard constants `true` and `false` may be used for Boolean values for readability. + - The random number generator interface, `igraph_rng_type_t`, has been overhauled. Check the declaration of the type for details. + - The default random number generator has been changed from Mersenne Twister to PCG32. + - Functions related to spectral coarse graining (i.e. all functions starting with `igraph_scg_...`) were separated into a project of its own. If you wish to keep on using these functions, please refer to the repository hosting the spectral coarse graining code at https://github.com/igraph/igraph-scg . The spectral coarse graining code was updated to support igraph 0.10. + - Since `igraph_integer_t` aims to be the largest integer size that is feasible on a particular platform, there is no need for generic data types based on `long int` any more. The `long` variants of generic data types (e.g., `igraph_vector_long_t`) are therefore removed; you should use the corresponding `int` variant instead, whose elements are of type `igraph_integer_t`. + - Generic data types based on `float` were removed as they were not used anywhere in the library. + - Several igraph functions that used to take a `long int` or return a `long int` now takes or returns an `igraph_integer_t` instead to make the APIs more consistent. Similarly, igraph functions that used `igraph_vector_t` for arguments that take or return _integral_ vectors (e.g., vertex or edge indices) now take `igraph_vector_int_t` instead. Graph-related functions where the API was changed due to this reason are listed below, one by one. + - Similarly, igraph functions that used to accept the `long` variant of a generic igraph data type (e.g., `igraph_vector_long_t`) now take the `int` variant of the same data type. + - The type `igraph_stack_ptr_t` and its associated functions were removed. Use `igraph_vector_ptr_t` and associated functions instead. + - Error handlers should no longer perform a `longjmp()`. Doing so will introduce memory leaks, as resource cleanup is now done in multiple stages, through multiple calls to the error handler. Thus, the error handler should either abort execution immediately (as the default handler does), or report the error, call `IGRAPH_FINALLY_FREE()`, and return normally. + - Most callback functions now return an error code. In previous versions they returned a boolean value indicating whether to terminate the search. A request to stop the search is now indicated with the special return code `IGRAPH_STOP`. + - `igraph_add_edges()` now uses an `igraph_vector_int_t` for its `edges` parameter. + - `igraph_adjacency()` no longer accepts a negative number of edges in its adjacency matrix. When negative entries are found, an error is generated. + - `igraph_adjacency()` gained an additional `loops` argument that lets you specify whether the diagonal entries should be ignored or should be interpreted as raw edge counts or _twice_ the number of edges (which is common in linear algebra contexts). + - `igraph_all_minimal_st_separators()` now returns the separators in an `igraph_vector_int_list_t` containing `igraph_vector_int_t` vectors. + - `igraph_all_st_cuts()` and `igraph_all_st_mincuts()` now return the cuts in an `igraph_vector_int_list_t` containing `igraph_vector_int_t` vectors. + - `igraph_arpack_unpack_complex()` now uses `igraph_integer_t` for its `nev` argument instead of `long int`. + - `igraph_articulation_points()` now uses an `igraph_vector_int_t` to return the list of articulation points, not an `igraph_vector_t`. + - `igraph_assortativity_nominal()` now accepts vertex types in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_asymmetric_preferennce_game()` now uses an `igraph_vector_int_t` to return the types of the nodes in the generated graph. + - `igraph_atlas()` now uses `igraph_integer_t` for its `number` argument. + - `igraph_automorphism_group()` now returns the generators in an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. + - `igraph_barabasi_game()`, `igraph_barabasi_aging_game()`, `igraph_recent_degree_game()` and `igraph_recent_degree_aging_game()` now use an `igraph_vector_int_t` for the out-degree sequence of the nodes being generated instead of an `igraph_vector_t`. + - `igraph_bfs()` now takes an `igraph_vector_int_t` for its `roots`, `restricted`, `order`, `father`, `pred`, `succ` and `dist` arguments instead of an `igraph_vector_t`. + - `igraph_bfs_simple()` now takes `igraph_vector_int_t` for its `vids`, `layers` and `parents` arguments instead of an `igraph_vector_t`. + - `igraph_bfs_simple()` now returns -1 in `parents` for the root node of the traversal, and -2 for unreachable vertices. This is now consistent with other functions that return a parent vector. + - `igraph_biconnected_components()` now uses an `igraph_vector_int_t` to return the list of articulation points, not an `igraph_vector_t`. Also, the container used for the edges and vertices of the components is now an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. + - `igraph_bipartite_projection()` now uses `igraph_vector_int_t` to return `multiplicity1` and `multiplicity2`, not `igraph_vector_t`. + - `igraph_bridges()` now uses an `igraph_vector_int_t` to return the list of bridges, not an `igraph_vector_t`. + - `igraph_callaway_traits_game()` returns the node types in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_canonical_permutation()` now uses an `igraph_vector_int_t` for its labeling parameter. + - `igraph_cattribute_list()` now uses `igraph_vector_int_t` to return `gtypes`, `vtypes` and `etypes`. + - `igraph_cited_type_game()` now uses an `igraph_vector_int_t` for its types parameter. + - `igraph_citing_cited_type_game()` now uses an `igraph_vector_int_t` for its + types parameter. + - `igraph_clique_handler_t` now uses an `igraph_vector_int_t` for its `clique` parameter, and must return an `igraph_error_t`. Use `IGRAPH_STOP` as the return code to terminate the search prematurely. The vector that the handler receives is owned by the clique search routine. If you want to hold on to the vector for a longer period of time, you need to make a copy of it in the handler. Cliques passed to the callback are marked as `const` as a reminder to this change. + - The `res` parameter of `igraph_cliques()` is now an `igraph_vector_int_list_t`. + - Callbacks used by `igraph_cliques_callback()` need to be updated to account for the fact that the callback does not own the clique passed to it any more; the callback needs to make a copy if it wants to hold on to the clique for a longer period of time. If the callback does not need to store the clique, it does not need to do anything any more, and it must not destroy or free the clique. + - `igraph_closeness()` and `igraph_closeness_cutoff()` now use an `igraph_vector_int_t` to return `reachable_count`, not an `igraph_vector_t`. + - `igraph_cohesive_blocks()` now uses an `igraph_vector_int_t` to return the mapping from block indices to parent block indices, and the `cohesion`; also, it uses an `igraph_vector_int_list_t` to return the blocks themselves instead of a pointer vector of `igraph_vector_t`. + - The `igraph_community_eb_get_merges()` bridges parameter now starts the indices into the edge removal vector at 0, not 1. + - The `igraph_community_eb_get_merges()` now reports an error when not all edges in the graph are removed, instead of a nonsensical result. + - `igraph_community_edge_betweenness()` now uses an `igraph_vector_int_t` to return the edge IDs in the order of their removal as well as the list of edge IDs whose removal broke a single component into two. + - `igraph_community_fluid_communities()` does not provide the modularity in a separate output argument any more; use `igraph_modularity()` to retrieve the modularity if you need it. + - `igraph_community_infomap()` now uses `igraph_integer_t` for its `nb_trials` argument. + - `igraph_community_label_propagation()` now uses an `igraph_vector_int_t` for its `initial` parameter. It also takes a `mode` argument that specifies how labels should be propagated along edges (forward, backward or ignoring edge directions). + - `igraph_community_label_propagation()` does not provide the modularity in a separate output argument any more; use `igraph_modularity()` to retrieve the modularity if you need it. + - `igraph_community_leiden()` has an additional parameter to indicate the number of iterations to perform (PR #2177). + - `igraph_community_walktrap()`, `igraph_community_edge_betweenness()`, `igraph_community_eb_get_merges()`, `igraph_community_fastgreedy()`, `igraph_community_to_membership()`, `igraph_le_community_to_membership()`, `igraph_community_leading_eigenvector()` now use an `igraph_vector_int_t` for their `merges` parameter. + - `igraph_community_walktrap()` now uses `igraph_integer_t` for its `steps` argument. + - `igraph_coreness()` now uses an `igraph_vector_int_t` to return the coreness + values. + - `igraph_convex_hull()` now uses an `igraph_vector_int_t` to return the indices of the input vertices that were chosen to be in the convex hull. + - `igraph_correlated_game()` and `igraph_correlated_pair_game()` now take an `igraph_vector_int_t` as the permutation vector, not an `igraph_vector_t`. + - `igraph_create()` now uses an `igraph_vector_int_t` for its `edges` parameter. + - `igraph_create_bipartite()` now uses an `igraph_vector_int_t` for its `edges` parameter. + - `igraph_compose()` now returns the edge maps in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_count_multiple()` now returns the multiplicities in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_decompose()` now uses an `igraph_integer_t` for its `maxcompno` and `minelements` arguments instead of a `long int`. + - `igraph_degree()` now uses an `igraph_vector_int_t` to return the degrees. If you need the degrees in a vector containing floating-point numbers instead (e.g., because you want to pass them on to some other function that takes an `igraph_vector_t`), use `igraph_strength()` instead with a null weight vector. + - `igraph_degree_sequence_game()` now takes degree sequences represented as `igraph_vector_int_t` instead of `igraph_vector_t`. + - `igraph_degseq_t`, used by `igraph_degree_sequence_game()`, uses new names for its constants. The old names are deprecated, but retained for compatibility. See `igraph_constants.h` to see which new name corresponds to which old one. + - `igraph_delete_vertices_idx()` now uses `igraph_vector_int_t` vectors to return the mapping and the inverse mapping of old vertex IDs to new ones. + - `igraph_deterministic_optimal_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. + - `igraph_dfs()` now takes an `igraph_vector_int_t` for its `order`, `order_out`, `father` and `dist` arguments instead of an `igraph_vector_t`. Furthermore, these vectors will contain -2 for vertices that have not been visited; in earlier versions, they used to contain NaN instead. Note that -1 is still used in the `father` vector to indicate the root of a DFS tree. + - `igraph_diameter()` and `igraph_diameter_dijkstra()` now use `igraph_vector_int_t` vectors to return the list of vertex and edge IDs in the diameter. + - `igraph_dominator_tree()` now takes an `igraph_vector_int_t` for its `dom` and `leftout` arguments instead of an `igraph_vector_t`. + - `igraph_dyad_census()` now uses `igraph_real_t` instead of `igraph_integer_t` for its output arguments, and it no longer returns -1 when overflow occurs. + - `igraph_edges()` now takes an `igraph_vector_int_t` for its `edges` argument instead of an `igraph_vector_t`. + - `igraph_es_multipairs()` was removed; you can use the newly added `igraph_es_all_between()` instead. + - `igraph_establishment_game()` now takes an `igraph_vector_int_t` for its `node_type_vec` argument instead of an `igraph_vector_t`. + - `igraph_eulerian_path()` and `igraph_eulerian_cycle()` now use `igraph_vector_int_t` to return the list of edge and vertex IDs participating in an Eulerian path or cycle instead of an `igraph_vector_t`. + - `igraph_feedback_arc_set()` now uses an `igraph_vector_int_t` to return the IDs of the edges in the feedback arc set instead of an `igraph_vector_t`. + - `igraph_get_adjacency()` no longer has the `eids` argument, which would produce an adjacency matrix where non-zero values were 1-based (not 0-based) edge IDs. If you need a matrix with edge IDs, create it manually. + - `igraph_get_adjacency_sparse()` now returns the sparse adjacency matrix in an `igraph_sparsemat_t` structure, and it assumes that the input matrix is _initialized_ for sake of consistency with other igraph functions. + - `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()` now has a `loops` argument that lets the user specify how loop edges should be handled. + - `igraph_get_edgelist()` now uses an `igraph_vector_int_t` for its `res` parameter. + - `igraph_get_eids()` now uses `igraph_vector_int_t` to return lists of edge IDs and to receive lists of vertex IDs. + - The `path` argument of `igraph_get_eids()` was removed. You can replicate the old behaviour by constructing the list of vertex IDs explicitly from the path by duplicating each vertex in the path except the first and last ones. A helper function called `igraph_expand_path_to_pairs()` is provided to ease the transition. + - `igraph_get_eids_multi()` was removed as its design was fundamentally broken; there was no way to retrieve the IDs of all edges between a specific pair of vertices without knowing in advance how many such edges there are in the graph. Use `igraph_get_all_eids_between()` instead. + - `igraph_get_incidence()` now returns the vertex IDs corresponding to the rows and columns of the incidence matrix as `igraph_vector_int_t`. + - `igraph_get_shortest_path()`, `igraph_get_shortest_path_bellman_ford()` and `igraph_get_shortest_path_dijkstra()` now use `igraph_vector_int_t` vectors to return the list of vertex and edge IDs in the shortest path. + - `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_dijkstra()` and `igraph_get_shortest_paths_bellman_ford()` now use an `igraph_vector_int_t` to return the predecessors and inbound edges instead of an `igraph_vector_long_t`. + - The functions `igraph_get_all_shortest_paths()`, `igraph_get_all_shortest_paths_dijkstra()`, `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_bellman_ford()` and `igraph_get_shortest_paths_dijkstra()` now return paths in an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. + - The vector of parents in `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_bellman_ford()` and `igraph_get_shortest_paths_dijkstra()` now use -1 to represent the starting vertex, and -2 for unreachable vertices. + - The `maps` parameters in `igraph_get_isomorphisms_vf2()` and `igraph_get_subisomorphisms_vf2()` are now of type `igraph_vector_int_list_t`. + - `igraph_get_stochastic()` now has an additional `weights` argument for edge weights. + - `igraph_get_stochastic_sparse()` now returns the sparse adjacency matrix in an `igraph_sparsemat_t` structure, and it assumes that the input matrix is _initialized_ for sake of consistency with other igraph functions. It also received an additional `weights` argument for edge weights. + - `igraph_girth()` now uses an `igraph_vector_int_t` for its `circle` parameter. + - `igraph_girth()` now uses `igraph_real_t` as the return value so we can return infinity for graphs with no cycles (instead of zero). + - The `cliques` parameters of type `igraph_vector_ptr_t` in `igraph_graphlets()`, `igraph_graphlets_candidate_basis()` and `igraph_graphlets_project()` were changed to an `igraph_vector_int_list_t`. + - `igraph_hrg_init()` and `igraph_hrg_resize()` now takes an `igraph_integer_t` as their size arguments instead of an `int`. + - `igraph_hrg_consensus()` now returns the parent vector in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_hrg_create()` now takes a vector of probabilities corresponding to the internal nodes of the dendogram. It used to also take probabilities for the leaf nodes and then ignore them. + - `igraph_hrg_predict()` now uses an `igraph_vector_int_t` for its `edges` parameter. + - `igraph_hrg_sample()` now always samples a single graph only. Use `igraph_hrg_sample_many()` if you need more than one sample, and call `igraph_hrg_fit()` beforehand if you do not have a HRG model but only a single input graph. + - `igraph_hrg_size()` now returns an `igraph_integer_t` instead of an `int`. + - `igraph_incidence()` does not accept negative incidence counts any more. + - `igraph_incident()` now uses an `igraph_vector_int_t` for its `eids` parameter. + - The `res` parameter in `igraph_independent_vertex_sets()` is now an `igraph_vector_int_list_t`. + - `igraph_induced_subgraph_map()` now uses `igraph_vector_int_t` vectors to return the mapping and the inverse mapping of old vertex IDs to new ones. + - `igraph_intersection()` now uses an `igraph_vector_int_t` for its `edge_map1` and `edge_map2` parameters. + - The `edgemaps` parameter of `igraph_intersection_many()` is now an `igraph_vector_int_list_t` instead of a pointer vector. + - `igraph_is_chordal()` now uses an `igraph_vector_int_t` for its `alpha`, `alpham1` and `fill_in` parameters. + - `igraph_is_graphical()` and `igraph_is_bigraphical()` now take degree sequences represented as `igraph_vector_int_t` instead of `igraph_vector_t`. + - `igraph_is_matching()`, `igraph_is_maximal_matching()` and `igraph_maximum_bipartite_matching` now use an `igraph_vector_int_t` to return the matching instead of an `igraph_vector_long_t`. + - `igraph_is_mutual()` has an additional parameter which controls whether directed self-loops are considered mutual. + - The `vids` parameter for `igraph_isoclass_subgraph()` is now an `igraph_vector_int_t` instead of `igraph_vector_t`. + - `igraph_isomorphic_vf2()`, `igraph_get_isomorphisms_vf2_callback()` (which used to be called `igraph_isomorphic_function_vf2()`) and `igraph_isohandler_t` now all use `igraph_vector_int_t` for their `map12` and `map21` parameters. + - The `cliques` parameter of type `igraph_vector_ptr_t` in `igraph_largest_cliques()` was changed to an `igraph_vector_int_list_t`. + - The `res` parameters of type `igraph_vector_ptr_t` in `igraph_largest_independent_vertex_sets()` and `igraph_largest_weighted_cliques()` were changed to an `igraph_vector_int_list_t`. + - The dimension vector parameter for `igraph_square_lattice()` (used to be `igraph_lattice()`) is now an `igraph_vector_int_t` instead of `igraph_vector_t`. + - The maxiter parameter of `igraph_layout_bipartite()` is now an `igraph_integer_t` instead of `long int`. + - The fixed parameter of `igraph_layout_drl()` and `igraph_layout_drl_3d()` was removed as it has never been implemented properly. + - The width parameter of `igraph_layout_grid()` is now an `igraph_integer_t` instead of `long int`. + - The width and height parameters of `igraph_layout_grid_3d()` are now `igraph_integer_t` instead of `long int`. + - The dimension parameter of `igraph_layout_mds()` is now an `igraph_integer_t` instead of `long int`. + - The `roots` and `rootlevel` parameters of `igraph_layout_reingold_tilford()` are now `igraph_vector_int_t` instead of `igraph_vector_t`. + - The `roots` and `rootlevel` parameters of `igraph_layout_reingold_tilford_circular()` are now `igraph_vector_int_t` instead of `igraph_vector_t`. + - The order parameter of `igraph_layout_star()` is now an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - The maxiter parameter of `igraph_layout_sugiyama()` is now an `igraph_integer_t` instead of `long int`. Also, the function now uses an `igraph_vector_int_t` for its `extd_to_orig_eids` parameter. + - The shifts parameter of `igraph_lcf_vector()` is now an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_matrix_minmax()`, `igraph_matrix_which_minmax()`, `igraph_matrix_which_min()` and `igraph_matrix_which_max()` no longer return an error code. The return type is now `void`. These functions never fail. + - `igraph_maxflow()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. + - The `igraph_maxflow_stats_t` struct now contains `igraph_integer_t` values instead of `int` ones. + - The `res` parameters in `igraph_maximal_cliques()` and `igraph_maximal_cliques_subset()` are now of type `igraph_vector_int_list_t`. + - Callbacks used by `igraph_maximal_cliques_callback()` need to be updated to account for the fact that the callback does not own the clique passed to it any more; the callback needs to make a copy if it wants to hold on to the clique for a longer period of time. If the callback does not need to store the clique, it does not need to do anything any more, and it must not destroy or free the clique. + - The `res` parameter in `igraph_maximal_independent_vertex_sets()` is now an `igraph_vector_int_list_t`. + - `igraph_maximum_cardinality_search()` now uses an `igraph_vector_int_t` for its `alpha` and `alpham1` arguments. + - `igraph_mincut()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. + - `igraph_moran_process()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. + - Motif callbacks of type `igraph_motifs_handler_t` now take an `igraph_vector_int_t` with the vertex IDs instead of an `igraph_vector_t`, and use `igraph_integer_t` for the isoclass parameter. + - Motif functions now use `igraph_integer_t` instead of `int` for their `size` parameter. + - `igraph_neighborhood_size()` now uses an `igraph_vector_int_t` for its `res` parameter. + - The `res` parameter of `igraph_neighborhood()` is now an `igraph_vector_int_list_t`. + - `igraph_neighbors()` now uses an `igraph_vector_int_t` for its `neis` parameter. + - `igraph_permute_vertices()` now takes an `igraph_vector_int_t` as the permutation vector. + - `igraph_power_law_fit()` does not calculate the p-value automatically any more because the previous estimation method did not match the results from the original paper of Clauset, Shalizi and Newman (2009) and the implementation of the method outlined in the paper runs slower than the previous naive estimate. A separate function named `igraph_plfit_result_calculate_p_value()` is now provided for calculating the p-value. The automatic selection of the `x_min` cutoff also uses a different method than earlier versions. As a consequence, results might be slightly different if you used tests where the `x_min` cutoff was selected automatically. The new behaviour is now consistent with the defaults of the underlying `plfit` library. + - `igraph_preference_game()` now uses an `igraph_vector_int_t` to return the types of the nodes in the generated graph. + - `igraph_random_walk()` now uses an `igraph_vector_int_t` for its results. Also, the function now takes both vertices and edges as parameters. It can return IDs of vertices and/or edges on the walk. The function now takes weights as a parameter to support weighted graphs. + - `igraph_random_edge_walk()` now uses an `igraph_vector_int_t` for its `edgewalk` parameter. + - `igraph_read_graph_dimacs_flow()` now uses an `igraph_vector_int_t` for its label parameter. + - `igraph_read_graph_graphml()` now uses `igraph_integer_t` for its `index` argument. + - `igraph_read_graph_pajek()` now creates a Boolean `type` attribute for bipartite graphs. Previously it created a numeric attribute. + - `igraph_realize_degree_sequence()` now uses an `igraph_vector_int_t` for its `outdeg` and `indeg` parameters. + - `igraph_reindex_membership()` now uses an `igraph_vector_int_t` for its `new_to_old` parameter. + - `igraph_rng_seed()` now requires an `igraph_uint_t` as its seed arguments. RNG implementations are free to use only the lower bits of the seed if they do not support 64-bit seeds. + - `igraph_rngtype_rand` (i.e. the RNG that is based on BSD `rand()`) was removed due to poor statistical properties that sometimes resulted in weird artifacts like all-even "random" numbers when igraph's usage patterns happened to line up with the shortcomings of the `rand()` generator in a certain way. + - `igraph_roulette_wheel_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. + - `igraph_similarity_dice_pairs()` now uses an `igraph_vector_int_t` for its `pairs` parameter. + - `igraph_similarity_jaccard_pairs()` now uses an `igraph_vector_int_t` for its `pairs` parameter. + - `igraph_simple_interconnected_islands_game()` does not generate multi-edges between islands any more. + - `igraph_sort_vertex_ids_by_degree()` and `igraph_topological_sorting()` now use an `igraph_vector_int_t` to return the vertex IDs instead of an `igraph_vector_t`. + - `igraph_spanning_tree()`, `igraph_minimum_spanning_tree()` and `igraph_random_spanning_tree()` now all use an `igraph_vector_int_t` to return the vector of edge IDs in the spanning tree instead of an `igraph_vector_t`. + - `igraph_sparsemat_cholsol()`, `igraph_sparsemat_lusol()`, `igraph_sparsemat_symbqr()` and `igraph_sparsemat_symblu()` now take an `igraph_integer_t` as their `order` parameter. + - `igraph_sparsemat_count_nonzero()` and `igraph_sparsemat_count_nonzerotol()` now return an `igraph_integer_t`. + - `igraph_sparsemat_is_symmetric()` now returns an error code and the result itself is provided in an output argument. + - The `values` argument of `igraph_sparsemat_transpose()` was removed; now the function always copies the values over to the transposed matrix. + - `igraph_spmatrix_t` and related functions were removed as they mostly duplicated functionality that was already present in `igraph_sparsemat_t`. Functions that used `igraph_spmatrix_t` in the library now use `igraph_sparsemat_t`. + - `igraph_stochastic_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. + - `igraph_st_mincut()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. + - `igraph_st_vertex_connectivity()` now ignores edges between source and target for `IGRAPH_VCONN_NEI_IGNORE` + - `igraph_strvector_get()` now returns strings in the return value, not in an output argument. + - `igraph_subcomponent()` now uses an `igraph_integer_t` for the seed vertex instead of an `igraph_real_t`. It also uses an `igraph_vector_int_t` to return the list of vertices in the same component as the seed vertex instead of an `igraph_vector_t`. + - `igraph_subisomorphic_vf2()`, `igraph_get_subisomorphisms_vf2_callback()` (which used to be called `igraph_subisomorphic_function_vf2()`) and `igraph_isomorphic_bliss()` now all use `igraph_vector_int_t` for their `map12` and `map21` parameters. + - The `maps` parameters in `igraph_subisomorphic_lad()`, `igraph_get_isomorphisms_vf2()` and `igraph_get_subisomorphisms_vf2()` are now of type `igraph_vector_int_list_t`. + - `igraph_subisomorphic_lad()` now uses an `igraph_vector_int_t` for its `map` parameter. Also, its `domains` parameter is now an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. + - `igraph_unfold_tree()` now uses an `igraph_vector_int_t` for its `vertex_index` and `roots` parameters. + - `igraph_union()` now uses an `igraph_vector_int_t` for its `edge_map1` and `edge_map2` parameters. + - The `edgemaps` parameter of `igraph_union_many()` is now an `igraph_vector_int_list_t` instead of a pointer vector. + - `igraph_vector_init_copy()` was refactored to take _another_ vector that the newly initialized vector should copy. The old array-based initialization function is now called `igraph_vector_init_array()`. + - `igraph_vector_ptr_init_copy()` was renamed to `igraph_vector_ptr_init_array()` for sake of consistency. + - `igraph_vs_vector()`, `igraph_vss_vector()` and `igraph_vs_vector_copy()` now all take an `igraph_vector_int_t` as the vector of vertex IDs, not an `igraph_vector_t`. Similarly, `igraph_vs_as_vector()` now returns the vector of matched vertex IDs in an `igraph_vector_int_t`, not an `igraph_vector_t`. + - The `res` parameter of `igraph_weighted_cliques()` is now an `igraph_vector_int_list_t`. + - `igraph_write_graph_dimacs_flow()` now uses `igraph_integer_t` for the source and target vertex index instead of a `long int`. + - `igraph_vector_*()`, `igraph_matrix_*()`, `igraph_stack_*()`, `igraph_array_*()` and several other generic igraph data types now use `igraph_integer_t` for indexing, _not_ `long int`. Please refer to the headers for the exact details; the list of affected functions is too large to include here. + - `igraph_vector_minmax()` and `igraph_vector_which_minmax()` no longer return an error code. The return type is now `void`. These functions never fail. + - `igraph_vector_order()` was removed; use `igraph_vector_int_pair_order()` instead. (The original function worked for vectors containing integers only). + - `igraph_vector_resize_min()` and `igraph_matrix_resize_min()` no longer return an error code (return type is now `void`). The vector or matrix is always left in a consistent state by these functions, with all data intact, even if releasing unused storage is not successful. + - `igraph_vector_qsort_ind()` and its variants now take an `igraph_order_t` enum instead of a boolean to denote whether the order should be ascending or descending. + - `igraph_weighted_adjacency()` now returns the weights in a separate vector instead of storing it in a vertex attribute. The reason is twofold: first, the previous solution worked only with the C attribute handler (not the ones from the higher-level interfaces), and second, it wasn't consistent with other igraph functions that use weights provided as separate arguments. + - The `loops` argument of `igraph_weighted_adjacency()` was converted to an `igraph_loops_t` for sake of consistency with `igraph_adjacency()` and `igraph_get_adjacency()`. + - `igraph_write_graph_gml()` takes an additional bitfield parameter controlling some aspects of writing the GML file. + - The `add_edges()` function in the attribute handler now takes an `igraph_vector_int_t` for its `edges` parameter instead of an `igraph_vector_t`. The `add_vertices()` function now takes an `igraph_integer_t` for the vertex count instead of a `long int`. The `combine_vertices()` and `combine_edges()` functions now take an `igraph_vector_ptr_t` containing vectors of type `igraph_vector_int_t` in their `merges` parameters. The `get_info()` function now uses `igraph_vector_int_t` to return the types of the graph, vertex and edge attribute types. The `permute_vertices()` and `permute_edges()` functions in the attribute handler tables now take an `igraph_vector_int_t` instead of an `igraph_vector_t` for the index vectors. These are relevant only to maintainers of higher level interfaces to igraph; they should update their attribute handlers accordingly. + - igraph functions that interface with external libraries such as BLAS or LAPACK may now fail if the underlying BLAS or LAPACK implementation cannot handle the size of input vectors or matrices (BLAS and LAPACK are usually limited to vectors whose size fits in an `int`). `igraph_blas_dgemv()` and `igraph_blas_dgemv_array()` thus now return an `igraph_error_t`, which may be set to `IGRAPH_EOVERFLOW` if the input vectors or matrices are too large. + - Functions that used an `igraph_vector_t` to represent cluster size and cluster membership now use an `igraph_vector_int_t` instead. These are: + - `igraph_connected_components()` (used to be `igraph_clusters()` in 0.9 and before) + - `igraph_community_eb_get_merges()` + - `igraph_community_edge_betweenness()` + - `igraph_community_fastgreedy()` + - `igraph_community_fluid_communities()` + - `igraph_community_infomap()` + - `igraph_community_label_propagation()` + - `igraph_community_leading_eigenvector()` + - `igraph_community_leiden()` + - `igraph_community_multilevel()` + - `igraph_community_optimal_modularity()` + - `igraph_community_spinglass()` + - `igraph_community_spinglass_single()` + - `igraph_community_to_membership()` + - `igraph_community_walktrap()` + - `igraph_compare_communities()` + - `igraph_le_community_to_membership()` + - `igraph_modularity()` + - `igraph_reindex_membership()` + - `igraph_split_join_distance()` + - `igraph_community_multilevel()` additionally uses a `igraph_matrix_int_t` instead of `igraph_matrix_t()` for its memberships parameter. + - `IGRAPH_TOTAL` was removed from the `igraph_neimode_t` enum; use the equivalent `IGRAPH_ALL` instead. + +### Added + + - A new integer type, `igraph_uint_t` has been added. This is the unsigned pair of `igraph_integer_t` and they are always consistent in size. + - A new container type, `igraph_vector_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of vectors. The type contains `igraph_vector_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). There is also a variant named `igraph_vector_int_list_t` for vectors of `igraph_vector_int_t` objects. + - A new container type, `igraph_matrix_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of matrices. The type contains `igraph_matrix_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). + - A new container type, `igraph_graph_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of graphs. The type contains `igraph_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). + - The vector container type, `igraph_vector_t`, has been extended with a new variant whose functions all start with `igraph_vector_fortran_int_...`. This vector container can be used for interfacing with Fortran code as it guarantees that the integers in the vector are compatible with Fortran integers. Note that `igraph_vector_int_t` is not suitable any more, as the elements of `igraph_vector_int_t` are of type `igraph_integer_t`, whose size may differ on 32-bit and 64-bit platforms, depending on how igraph was compiled. + - `igraph_adjlist_init_from_inclist()` to create an adjacency list from an already existing incidence list by resolving edge IDs to their corresponding endpoints. This function is useful for algorithms when both an adjacency and an incidence list is needed and they should be in the same order. + - `igraph_almost_equals()` and `igraph_cmp_epsilon()` to compare floating point numbers with a relative tolerance. + - `igraph_betweenness_subset()` and `igraph_edge_betweenness_subset()` calculates betweenness and edge betweenness scores using shortest paths between a subset of vertices only (#1711, thanks to @guyroznb) + - `igraph_blas_dgemm()` to multiply two matrices. + - `igraph_calloc()` and `igraph_realloc()` are now publicly exposed; these functions provide variants of `calloc()` and `realloc()` that can safely be deallocated within igraph functions. + - `igraph_circulant()` to create circulant graphs (#1856, thanks to @Gomango999). + - `igraph_complex_almost_equals()` to compare complex numbers with a relative tolerance. + - `igraph_eccentricity_dijkstra()` finds the longest weighted path length among all shortest paths between a set of vertices. + - `igraph_enter_safelocale()` and `igraph_exit_safelocale()` for temporarily setting the locale to C. Foreign format readers and writers require a locale which uses a decimal point instead of decimal comma. + - `igraph_es_all_between()` to create an edge selector that selects all edges between a pair of vertices. + - `igraph_full_multipartite()` generates full multipartite graphs (a generalization of bipartite graphs to multiple groups). + - `igraph_fundamental_cycles()` computes a fundamental cycle basis (experimental). + - `igraph_generalized_petersen()` to create generalized Petersen graphs (#1844, thanks to @alexsyou). + - `igraph_get_all_eids_between()` returns the IDs of all edges between a pair of vertices. + - `igraph_get_k_shortest_paths()` finds the k shortest paths between a source and a target vertex. + - `igraph_get_laplacian()` and `igraph_get_laplacian_sparse()` return the Laplacian matrix of the graph as a dense or sparse matrix, with various kinds of normalizations. They replace the now-deprecated `igraph_laplacian()` function. This makes the API consistent with `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()`. + - `igraph_get_widest_path()`, `igraph_get_widest_paths()`, `igraph_widest_path_widths_dijkstra()` and `igraph_widest_path_widths_floyd_warshall()` to find widest paths (#1893, thanks to @Gomango999). + - `igraph_graph_center()` finds the central vertices of the graph. The central vertices are the ones having a minimum eccentricity (PR #2084, thanks to @pradkrish). + - `igraph_graph_count()` returns the number of unlabelled graphs on a given number of vertices. It is meant to find the maximum isoclass value. + - `igraph_has_mutual()` checks if a directed graph has any mutual edges. + - `igraph_heap_clear()` and `igraph_heap_min_clear()` remove all elements from an `igraph_heap_t` or an `igraph_heap_min_t`, respectively. + - `igraph_invalidate_cache()` invalidates all cached graph properties, forcing their recomputation next time they are requested. This function should not be needed in everyday usage, but may be useful in debugging and benchmarking. + - `igraph_is_forest()` to check whether a graph is a forest (#1888, thanks to @rohitt28). + - `igraph_is_acyclic()` to check whether a graph is acyclic (#1945, thanks to @borsgeorgica). + - `igraph_is_perfect()` to check whether a graph is a perfect graph (#1730, thanks to @guyroznb). + - `igraph_hub_and_authority_scores()` calculates the hub and authority scores of a graph as a matching pair. + - `igraph_layout_umap()` and `igraph_layout_umap_3d()` to lay out a graph in 2D or 3D space using the UMAP dimensionality reduction algorithm. + - `igraph_local_scan_subset_ecount()` counts the number of edges in induced sugraphs from a subset of vertices. + - `igraph_matrix_view_from_vector()` allows interpreting the data stored in a vector as a matrix of the specified size. + - `igraph_minimum_cycle_basis()` computes an unweighted minimum cycle basis (experimental). + - `igraph_pseudo_diameter()` and `igraph_pseudo_diameter_dijkstra()` to determine a lower bound for the diameter of a graph (unweighted or weighted). + - `igraph_regular_tree()` creates a regular tree where all internal vertices have the same total degree. + - `igraph_rngtype_pcg32` and `igraph_rngtype_pcg64` implement 32-bit and 64-bit variants of the PCG random number generator. + - `igraph_rng_get_pois()` generates random variates from the Poisson distribution. + - `igraph_roots_for_tree_layout()` computes a set of roots suitable for a nice tree layout. + - `igraph_spanner()` calculates a spanner of a graph with a given stretch factor (#1752, thanks to @guyroznb) + - `igraph_sparse_adjacency()` and `igraph_sparse_weighted_adjacency()` constructs graphs from (weighted) sparse matrices. + - `igraph_sparsemat_get()` to retrieve a single element of a sparse matrix. + - `igraph_sparsemat_normalize_rows()` and `igraph_sparsemat_normalize_cols()` to normalize sparse matrices row-wise or column-wise. + - `igraph_stack_capacity()` to query the capacity of a stack. + - `igraph_strvector_capacity()` returns the maximum number of strings that can be stored in a string vector without reallocating the memory block holding the pointers to the individual strings. + - `igraph_strvector_merge()` moves all strings from one string vectors to the end of another without re-allocating them. + - `igraph_strvector_push_back_len()` adds a new string to the end of a string vector and allows the user to specify the length of the string being added. + - `igraph_strvector_reserve()` reserves space for a given number of string pointers in a string vector. + - `igraph_symmetric_tree()` to create a tree with the specified number of branches at each level (#1859, thanks to @YuliYudith and @DoruntinaM). + - `igraph_trussness()` calculates the trussness of each edge in the graph (#1034, thanks to @alexperrone) + - `igraph_turan()` generates Turán graphs (#2088, thanks to @pradkrish) + - `igraph_vector_all_almost_e()`, `igraph_vector_complex_all_almost_e()`, `igraph_matrix_all_almost_e()`, `igraph_matrix_complex_all_almost_e()` for elementwise comparisons of floating point vector and matrices with a relative tolerance. + - `igraph_vector_complex_zapsmall()` and `igraph_matrix_complex_zapsmall()` for replacing small components of complex vector or matrix elements with exact zeros. + - `igraph_vector_lex_cmp_untyped()` and `igraph_vector_colex_cmp_untyped()` for lexicographic and colexicographic comparison of vectors, similarly to `igraph_vector_lex_cmp()` and `igraph_vector_colex_cmp()`. The difference between the two variants is that the untyped versions declare the vectors as `const void*`, making the functions suitable as comparators for `qsort()`. + - `igraph_vector_permute()` functions to permute a vector based on an index vector. + - `igraph_vector_ptr_sort_ind()` to obtain an index vector that would sort a vector of pointers based on some comparison function. + - `igraph_vector_range()` to fill an existing vector with a range of increasing numbers. + - `igraph_vector_remove_fast()` functions to remove an item from a vector by swapping it with the last element and then popping it off. It allows one to remove an item from a vector in constant time if the order of items does not matter. + - `igraph_vertex_path_from_edge_path()` converts a sequence of edge IDs representing a path to an equivalent sequence of vertex IDs that represent the vertices the path travelled through. + - `igraph_vs_range()`, `igraph_vss_range()`, `igraph_es_range()` and `igraph_ess_range()` creates vertex and edge sequences from C-style intervals (closed from the left, open from the right). + - `igraph_wheel()` to create a wheel graph (#1938, thanks to @kwofach). + +### Removed + + - `igraph_adjlist_remove_duplicate()`, `igraph_betweenness_estimate()`, `igraph_closeness_estimate()`, `igraph_edge_betweenness_estimate()`, `igraph_inclist_remove_duplicate()`, `igraph_is_degree_sequence()` and `igraph_is_graphical_degree_sequence()` were deprecated earlier in 0.9.0 and are now removed in this release. + - `igraph_dnorm()`, `igraph_strvector_move_interval()`, `igraph_strvector_permdelete()` and `igraph_strvector_remove_negidx()` were removed. These are not breaking changes as the functions were never documented, they were only exposed from one of the headers. + - `igraph_eigen_laplacian()`, `igraph_es_fromto()` and `igraph_maximum_matching()` were removed. These are not breaking changes either as the functions were never implemented, they returned an error code unconditionally. + +### Changed + + - `igraph_degree_sequence_game()` now supports an additional method, `IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE`, an edge-switching MCMC sampler. + - `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()` now count loop edges _twice_ in undirected graphs when using `IGRAPH_GET_ADJACENCY_BOTH`. This is to ensure consistency with `IGRAPH_GET_ADJACENCY_UPPER` and `IGRAPH_GET_ADJACENCY_LOWER` such that the sum of the upper and the lower triangle matrix is equal to the full adjacency matrix even in the presence of loop edges. + - `igraph_matrix_print()` and `igraph_matrix_fprint()` functions now align columns when priting. + - `igraph_read_graph_gml()` now supports graph attributes (in addition to vertex and edge attributes). + - `igraph_read_graph_gml()` now uses NaN as the default numerical attribute values instead of 0. + - The Pajek parser in `igraph_read_graph_pajek()` is now less strict and accepts more files. + - `igraph_ring()` no longer simplifies its result when generating a one- or two-vertex graph. The one-cycle has a self-loop and the undirected two-cycle has parallel edges. + - `igraph_vector_view()` now allows `data` to be `NULL` in the special case when `length == 0`. + - `igraph_version()` no longer returns an error code. + - `igraph_write_graph_gml()` uses the `creator` parameter in a different way: the supplied string is now written into the Creator line as-is instead of being appended to a default value. + - `igraph_write_graph_gml()` skips writing NaN values. These two changes ensure consistent round-tripping. + - `igraph_write_graph_gml()` and `igraph_read_graph_gml()` now have limited support for entity encoding. + - `igraph_write_graph_ncol()` now preserves the edge ordering of the graph when writing an NCOL file. + - igraph functions that take an ARPACK options object now also accept `NULL` in place of an options object, and they will fall back to using a default object provided by `igraph_arpack_options_get_default()`. + - Foreign format readers now present more informative error messages. + - The default tolerance of the zapsmall functions is now `eps^(2/3)` instead of `eps^(1/2)` where eps is the machine epsilon of `igraph_real_t`. + - It is now possible to override the uniform integer and the Poisson samplers in the random number generator interface. + +### Fixed + + - When an error occurs during parsing DL, GML, NCOL, LGL or Pajek files, line numbers are now reported correctly. + - The GraphML parser does not print to stderr any more in case of encoding errors and other error conditions originating from the underlying `libxml2` library. + - The GraphML parser would omit some edges and vertices when reading files with custom attribute types, such as those produced by yEd. This is now corrected. + - The GML parser no longer mixes up Inf and NaN and -Inf now works. + - The GML parser now supports nodes with no id field. + - The GML parser now performs more stringent checks on the input file, such as verifying that `id`, `source`, `target` and `directed` fields are not duplicated. + - The core data structures (vector, etc.) have overflow checks now. + - Deterministic graph generators, as well as most random ones, have overflow checks now. + - Graphs no longer lose all their attributes after calling `igraph_contract_vertices()`. + - `igraph_hrg_init()` does not throw an assertion error anymore for zero vertices. + - `igraph_matrix_complex_create()` and `igraph_matrix_complex_create_polar()` now set their sizes correctly. + - `igraph_random_walk()` took one fewer steps than specified. + - `igraph_sparsemat_getelements_sorted()` did not sort the elements for triplet matrices correctly; this is fixed now. + - `igraph_write_graph_gml()` no longer produces corrupt output when some string attribute values contain `"` characters. + +### Deprecated + + - `igraph_clusters()` has been renamed to `igraph_connected_components()`; the old name is deprecated and will be removed in 0.11. + - `igraph_complex_eq_tol()` is now deprecated in favour of `igraph_complex_almost_equals()`. + - `igraph_get_sparsemat()` is deprecated in favour of `igraph_get_adjacency_sparse()`, and will be removed in 0.11. Note that `igraph_get_adjacency_sparse()` takes an _initialized_ sparse matrix as input, unlike `igraph_get_sparsemat()` which takes an uninitialized one. + - `igraph_get_stochastic_sparsemat()` is deprecated in favour of `igraph_get_stochastic_sparse()`, and will be removed in 0.11. Note that `igraph_get_stochastic_sparse()` takes an _initialized_ sparse matrix as input, unlike `igraph_get_stochastic_sparsemat()`, which takes an uninitialized one. + - `igraph_isomorphic_34()` has been deprecated in favour of `igraph_isomorphic()`. Note that `igraph_isomorphic()` calls an optimized version for directed graphs of size 3 and 4, and undirected graphs with 3-6 vertices, so there is no need for a separate function. + - `igraph_laplacian()` is now deprecated; use `igraph_get_laplacian()` or `igraph_get_laplacian_sparse()` depending on whether you need a dense or a sparse matrix. + - `igraph_lattice()` has been renamed to `igraph_square_lattice()` to indicate that this function generates square lattices only. The old name is deprecated and will either be removed in 0.11 or will be changed to become a generic lattice generator that also supports other types of lattices. + - `igraph_local_scan_neighborhood_ecount()` is now deprecated in favour of `igraph_local_scan_subset_ecount()`. + - `igraph_matrix_all_e_tol()` is now deprecated in favour of `igraph_matrix_all_almost_e()`. + - `igraph_matrix_copy()` is now deprecated; use `igraph_matrix_init_copy()` instead. The new name emphasizes that the function _initializes_ the first argument instead of expecting an already-initialized target matrix. The old name will be removed in 0.11. + - `igraph_matrix_e()` and `igraph_matrix_e_ptr()` have been renamed to `igraph_matrix_get()` and `igraph_matrix_get_ptr()`. The old names are deprecated and will be removed in 0.11. +- `igraph_random_edge_walk()` has been deprecated by `igraph_random_walk()` to support edges and/or vertices for the random walk in a single function. It will be removed in 0.11. + - `igraph_read_graph_dimacs()` has been renamed to `igraph_read_graph_dimacs_flow()`; the old name is deprecated and might be re-used as a generic DIMACS reader in the future. Also, the function now uses `igraph_integer_t` as the source and target vertex IDs instead of a `long int`. + - `igraph_shortest_paths()` and related functions were renamed to `igraph_distances()`; the old name was unfortunate because these functions calculated _path lengths_ only and not the paths themselves. The old names are deprecated and will be removed in 0.11. + - `igraph_sparsemat_copy()`, `igraph_sparsemat_diag()` and `igraph_sparsemat_eye()` have been renamed to `igraph_sparsemat_init_copy()`, `igraph_sparsemat_init_diag()` and `igraph_sparsemat_init_eye()` to indicate that they _initialize_ a new sparse matrix. The old names are deprecated and will be removed in 0.11. + - `igraph_strvector_add()` has been renamed to `igraph_strvector_push_back()` for sake of consistency with other vector-like data structures; the old name is deprecated and will be removed in 0.11. + - `igraph_strvector_copy()` has been renamed to `igraph_strvector_init_copy()` for sake of consistency with other vector-like data structures; the old name is deprecated and will be removed in 0.11. + - `igraph_strvector_get()` now returns a `const char*` and not a `char*` to indicate that you are not supposed to modify the string in the vector directly. If you do want to modify it and you are aware of the implications (i.e. the new string must not be longer than the original one), you can cast away the constness of the return value before modifying it. + - `igraph_strvector_set2()` has been renamed to `igraph_strvector_set_len()`; the old name is deprecated and will be removed in 0.11. + - `igraph_tree()` has been renamed to `igraph_kary_tree()`; the old name is deprecated and will be removed in 0.11. + - `igraph_vector_e()` and `igraph_vector_e_ptr()` have been renamed to `igraph_vector_get()` and `igraph_vector_get_ptr()`. The old names are deprecated and will be removed in 0.11. + - `igraph_vector_e_tol()` is now deprecated in favour of `igraph_vector_all_almost_e()`. + - `igraph_vector_copy()` is now deprecated; use `igraph_vector_init_copy()` instead. The new name emphasizes that the function _initializes_ the first argument instead of expecting an already-initialized target vector. The old name will be removed in 0.11. + - `igraph_vector_init_seq()` is now deprecated in favour of `igraph_vector_init_range()`, which uses C-style intervals (closed from the left and open from the right). + - `igraph_vs_seq()`, `igraph_vss_seq()`, `igraph_es_seq()` and `igraph_ess_seq()` are now deprecated in favour of `igraph_vs_range()`, `igraph_vss_range()`, `igraph_es_range()` and `igraph_ess_range()` because these use C-style intervals (closed from the left, open from the right). + - `igraph_write_graph_dimacs()` has been renamed to `igraph_write_graph_dimacs_flow()`; the old name is deprecated and might be re-used as a generic DIMACS writer in the future. Also, the function now uses `igraph_integer_t` as the source and target vertex IDs instead of a `long int`. + - `igraph_zeroin()` is deprecated and will be removed in 0.11, with no replacement. The function is not graph-related and was never part of the public API. + - The macros `igraph_Calloc`, `igraph_Realloc` and `igraph_Free` have been deprecated in favour of `IGRAPH_CALLOC`, `IGRAPH_REALLOC` and `IGRAPH_FREE` to simplify the API. The deprecated variants will be removed in 0.11. + +### Other + + - Documentation improvements. + - Support for Intel's LLVM-based compiler. + +## [0.9.10] - 2022-09-02 + +### Added + + - `igraph_reverse_edges()` reverses the specified edges in the graph while preserving all attributes. + +### Changed + + - The `IGRAPH_ARPACK_PROD` error code is no longer used. Instead, the specific error encountered while doing matrix multiplication is reported. + - XML external entities are not resolved any more when parsing GraphML files to prevent XML external entity injection (XXE) attacks. Standard XML entities like `<` or `"` still work. + +### Fixed + + - Fixed incorrect results from `igraph_local_scan_1_ecount()` when the graph was directed but the mode was `IGRAPH_ALL` and some nodes had loop edges. See issue #2092. + - Fixed incorrect counting of self-loops in `igraph_local_scan_neighborhood_ecount()` when the graph was undirected. + - In some rare edge cases, `igraph_pagerank()` with the ARPACK method and `igraph_hub_score()` / `igraph_authority_score()` could return incorrect results. The problem could be detected by checking that the returned eigenvalue is not negative. See issue #2090. + - `igraph_permute_vertices()` now checks for out-of-range indices and duplicates in the permutation vector. + - `igraph_create()` now checks for non-finite vertex indices in the edges vector. + - `igraph_eigenvector_centrality()` would return incorrect scores when some weights were negative. + - `igraph_es_seq()` and `igraph_ess_seq()` did not include the `to` vertex in the sequence. + - `igraph_eit_create()` and `igraph_vit_create()` now check that all edge/vertex indices are in range when creating iterators from sequence-type selectors. + - `igraph_grg_game()` now validates its arguments. + - `igraph_layout_drl()` and its 3D version now validate their inputs. + - `igraph_layout_kamada_kawai()`, `igraph_layout_fruchterman_reingold()`, `igraph_layout_drl()`, as well as their 3D versions now check for non-positive weights. + - `igraph_asymmetric_preference_game()` interpreted its `type_dist_matrix` argument incorrectly. + - Fixed incorrect result of `igraph_community_spinglass()` for null and singleton graphs. + - `igraph_layout_gem()` does not crash any more for graphs with only a single vertex. + - `igraph_bridges()` no longer uses recursion and thus is no longer prone to stack overflow. + - Include paths of dependent packages would be specified incorrectly in some environments. + +### Other + + - Documentation improvements. + +## [0.9.9] - 2022-06-04 + +### Changed + + - `igraph_community_walktrap()` now uses double precision floating point operations internally instead of single precision. + - In `igraph_community_leiden()`, the `nb_clusters` output parameter is now optional (i.e. it can be `NULL`). + - `igraph_read_graph_graphml()` no longer attempts to temporarily set the C locale, and will therefore not work correctly if the current locale uses a decimal comma. + +### Fixed + + - `igraph_community_walktrap()` would return an invalid `modularity` vector when the `merges` matrix was not requested. + - `igraph_community_walktrap()` would return a `modularity` vector that was too long for disconnected graphs. This would cause a failure in some weighted graphs when the `membership` vector was requested. + - `igraph_community_walktrap()` now checks the weight vector: only non-negative weights are accepted, and all vertices must have non-zero strength. + - `igraph_community_walktrap()` now returns a modularity score of NaN for graphs with no edges. + - `igraph_community_fast_greedy()` now returns a modularity score of NaN for graphs with no edges. + - `igraph_community_edge_betweenness()` now returns a modularity vector with a single NaN entry for graph with no edges. Previously it returned a zero-length vector. + - `igraph_community_leading_eigenvector()` does not ignore non-ARPACK-related errors from `igraph_arpack_rssolve()` any more. + - `igraph_preference_game()` now works correctly when `fixed_size` is true and + `type_dist` is not given; earlier versions had a bug where more than half of + the vertices mistakenly ended up in group 0. + - Fixed a memory leak in `igraph_hrg_fit()` when using `start=1`. + - `igraph_write_graph_dot()` now outputs NaN values unchanged. + - `igraph_write_graph_dot()` no longer produces invalid DOT files when empty string attributes are present. + - `igraph_layout_fruchterman_reingold()` and `igraph_layout_kamada_kawai()`, as well as their 3D versions, did not respect vertex coordinate bounds (`xmin`, `xmax`, etc.) when minimum values were large or maximum values were small. This is now fixed. + - The initial coordinates of the Kamada-Kawai layout (`igraph_layout_kamada_kawai()` and `igraph_layout_kamada_kawai_3d()`) are chosen to be more in line with the original publication, improving the stability of the result. See isse #963. This changes the output of the function for the same graph, compared with previous versions. To obtain the same layout, initialize coordinates with `igraph_layout_circle()` (in 2D) or `igraph_layout_sphere()` (in 3D). + - Improved numerical stability in Kamada-Kawai layout. + - Corrected a problem in the calculation of displacements in `igraph_layout_fruchterman_reingold()` and its 3D version. This fixes using the "grid" variant of the algorithm on disconnected graphs. + - `igraph_sumtree_search()` would consider search intervals open on the left and closed on the right, contrary to the documentation. This is now corrected to closed on the left and open on the right. In some cases this lead to a zero-weight element being returned for a zero search value. See issue #2080. + +### Other + + - Greatly improved error reporting from foregin format parsers. + - Documentation improvements. + +## [0.9.8] - 2022-04-08 + +### Fixed + + - Assertion failure in `igraph_bfs()` when an empty `roots` or `restricted` vector was provided. + - `igraph_diversity()` now returns 0 for degree-1 vertices. Previously it incorrectly returned NaN or +-Inf depending on roundoff errors. + - `igraph_community_walktrap()` does not crash any more when provided with + `modularity=NULL` and `membership=NULL`. + +### Other + + - Documentation improvements. + +## [0.9.7] - 2022-03-16 + +### Changed + + - `igraph_get_all_shortest_paths_dijsktra()` now uses tolerances when comparing path + lengths, and is thus robust to numerical roundoff errors. + - `igraph_vector_*_swap` and `igraph_matrix_swap` now take O(1) instead of O(n) and accept all sizes. + +### Fixed + + - NCOL and LGL format writers no longer accept "name" and "weight" attributes + of invalid types. + - The LGL writer could not access numerical weight attributes, potentially leading + to crashes. + - External PLFIT libraries and their headers are now detected at their standard + installation location. + - `igraph_vector_init()` no longer accepts negative vector sizes. + - `igraph_assortativity_nominal()` crashed on the null graph. + - Label propagation now ensures that all labels are dominant. + - Fixed incorrect partition results for walktrap algorithm (issue #1927) + - Negative values returned by `igraph_rng_get_integer()` and `RNG_INTEGER()` were incorrect, + one larger than they should have been. + - `igraph_community_walktrap()` now checks its `steps` input argument. + - The first modularity value reported by `igraph_community_walktrap()` was + incorrect (it was always zero). This is now fixed. + - `igraph_correlated_game()` would return incorrect results, or exhaust the memory, + for most input graphs that were not generated with `igraph_erdos_renyi_game_gnp()`. + - `igraph_community_label_propagation` incorrectly did not result in all labels being dominant (issue #1963, fixed in PR #1966). + +### Other + + - The C attribute handler now verifies attribute types when retrieving attributes. + - Documentation improvements. + +## [0.9.6] - 2022-01-05 + + - Isomorphism class functions (`igraph_isoclass()`, `igraph_isoclass_subgraph()`, + `igraph_isoclass_create`) and motif finder functions (`igraph_motifs_randesu()`, + `igraph_motifs_randesu_estimate()`, `igraph_motifs_randesu_callback()`) now + support undirected (sub)graphs of sizes 5 and 6. Previsouly only sizes 3 and 4 + were supported. + +### Fixed + + - igraph would not build with MinGW when using the vendored GLPK and enabling TLS. + - Removed some uses of `abort()` from vendored libraries, which could unexpectedly + shut down the host language of igraph's high-level interfaces. + - `igraph_community_label_propagation()` no longer leaves any vertices unlabeled + when they were not reachable from any labeled ones, i.e. the returned membership + vector is guaranteed not to contain negative values (#1853). + - The Kamada-Kawai layout is now interruptible. + - The Fruchterman-Reingold layout is now interruptible. + - Fixed a bug in `igraph_cmp_epsilon()` that resulted in incorrect results for + edge betweenness calculations in certain rare cases with x87 floating point + math when LTO was also enabled (#1894). + - Weighted clique related functions now fall back to the unweighted variants + when a null vertex weight vector is given to them. + - `igraph_erdos_renyi_game_(gnm|gnp)` would not produce self-loops for the singleton + graph. + - Fixed a bug in `igraph_local_efficiency()` that sometimes erroneously + reported zero as the local efficiency of a vertex in directed graphs. + - `igraph_vector_update()` (and its type-specific variants) did not check for + memory allocation failure. + - Fixed a potential crash in the GraphML reader that would be triggered by some + invalid GraphML files. + +### Other + + - `igraph_is_tree()` has improved performance and memory usage. + - `igraph_is_connected()` has improved performance when checking weak connectedness. + - Improved error handling in `igraph_maximal_cliques()` and related functions. + - The build system now checks that GLPK is of a compatible version (4.57 or later). + - The vendored `plfit` package was updated to 0.9.3. + - You can now build igraph with an external `plfit` instead of the vendored one. + - Documentation improvements. + +## [0.9.5] - 2021-11-11 + +### Fixed + + - `igraph_reindex_membership()` does not allow negative membership indices any more. + + - `igraph_rewire_directed_edges()` now generates multigraphs when edge directions + are ignored, to make it consistent with the directed case. + + - Fixed a bug in `igraph_gomory_hu_tree()` that returned only the equivalent flow + tree instead of the cut tree (#1810). + + - Fixed a bug in the `IGRAPH_TO_UNDIRECTED_COLLAPSE` mode of + `igraph_to_undirected()` that provided an incorrect merge vector to the + attribute handler, leading to problems when edge attributes were merged + using an attribute combination (#1814). + + - Fixed the behaviour of the `IGRAPH_ENABLE_LTO` option when it was set to + `AUTO`; earlier versions had a bug where `AUTO` simply checked whether LTO + is supported but then did not use LTO even if it was supported. + + - When using igraph from a CMake project, it is now checked that the project has + the C++ language enabled. This is necessary for linking to igraph with CMake. + +### Other + + - Improved the root selection method for disconnected graphs in the + Reingold-Tilford layout (#1836). The new root selection method provides + niceer results if the graph is not a tree, although it is still recommended + to use the Sugiyama layout instead, unless the input graph is _almost_ a + tree, in which case Reingold-Tilfold may still be preferred. + + - `igraph_decompose()` is now much faster for large graphs containing many + isolates or small components (#960). + + - `igraph_largest_cliques()` and `igraph_clique_number()` were re-written to + use `igraph_maximal_cliques_callback()` so they are much faster now (#804). + + - The vendored GLPK has been upgraded to GLPK 5.0. + + - Documentation improvements. + +## [0.9.4] - 2021-05-31 + +### Changed + + - Unweighted transitivity (i.e. clustering coefficient) calculations now ignore multi-edges and edge directions instead of rejecting multigraphs and directed graphs. + - `igraph_transitivity_barrat()` now returns an error code if the input graph has multiple edges (which is not handled correctly by the implementation yet). + +### Fixed + + - `igraph_local_scan_k_ecount()` now handles loops correctly. + - `igraph_transitivity_avglocal_undirected()` is no longer slower than `igraph_transitivity_local_undirected()`. + - Worked around an invalid warning issued by Clang 9.0 when compiling with OpenMP. + +### Other + + - Documentation improvements. + +## [0.9.3] - 2021-05-05 + +### Added + + - OpenMP is now enabled and used by certain functions (notably PageRank calculation) when the compiler supports it. Set `IGRAPH_OPENMP_SUPPORT=OFF` at configuration time to disable this. + +### Fixed + + - `igraph_get_incidence()` no longer reads and writes out of bounds when given a non-bipartite graph, but gives a warning and ignores edges within a part. + - `igraph_dyad_census()` no longer reports an overflow on singleton graphs, and handles loops and multigraphs correctly. Undirected graphs are handled consistently and will no longer give a warning. + - `igraph_vector_lex_cmp()` and `igraph_vector_colex_cmp()` dereferenced their arguments only once instead of twice, and therefore did not work with `igraph_vector_ptr_sort()`. + - `igraph_maximal_cliques_subset()` and `igraph_transitivity_barrat()` corrupted the error handling stack ("finally stack") under some circumstances. + - CMake package files did not respect `CMAKE_INSTALL_LIBDIR`. This only affected Linux distributions which install into `lib64` or other locations instead of `lib`. + - The parser sources could not be generated when igraph was in a location that contained spaces in its path. + - igraph no longer links to the math library (`libm`) when this is not necessary. + - `_CRT_SECURE_NO_WARNINGS` is now defined during compilation to enable compatibility with UWP. + - Fixed a compilation issue on MSYS / MinGW when link-time optimization was enabled and the `MSYS Makefiles` CMake generator was used. Some source files in igraph were renamed as a consequence, but these should not affect users of the library. + +### Deprecated + + - `igraph_rng_min()` is now deprecated; assume a constant zero as its return value if you used this function in your own code. + +### Other + + - Updated the vendored CXSparse library to version 3.2.0 + +## [0.9.2] - 2021-04-14 + +### Added + + - CMake package files are now installed with igraph. This allows `find_package(igraph)` to find igraph and detect the appropriate compilation options for projects that link to it. + +### Fixed + + - igraph can now be used as a CMake subproject in other CMake-based projects. + - The documentaton can now be built from the release tarball. + - Configuration will no longer fail when the release tarball is extracted into a subdirectory of an unrelated git repository. + - The generated pkg-config file was incorrect when `CMAKE_INSTALL_` variables were absolute paths. + - On Unix-like systems, the library name is now `libigraph.so.0.0.0`, as it used to be for igraph 0.8 and earlier. + - Fixed a return type mismatch in parser sources, and fixed some warnings with recent versions of gcc. + - Fixed a bug in `igraph_get_shortest_paths_dijkstra()` and `igraph_get_shortest_paths_bellman_ford()` that returned incorrect results for unreachable vertices. + +### Other + + - Improved installation instructions and tutorial. + +## [0.9.1] - 2021-03-23 + +### Added + + - `igraph_vector_lex_cmp()` and `igraph_vector_colex_cmp()` for lexicographic + and colexicographic comparison of vectors. These functions may also be used + for sorting. + +### Changed + + - `igraph_community_multilevel()` is now randomized (PR #1696, thanks to Daniel Noom). + +### Fixed + + - CMake settings that controlled the library installation directory name, such as `CMAKE_INSTALL_LIBDIR`, were not respected. + - Under some conditions, the generated pkg-config file contained an incorrect include directory path. + - The following functions were not exported from the shared library: `igraph_subcomponent()`, `igraph_stack_ptr_free_all()`, `igraph_stack_ptr_destroy_all()`, `igraph_status_handler_stderr()`, `igraph_progress_handler_stderr()`. + - Built-in random number generators (`igraph_rngtype_mt19937`, `igraph_rngtype_rand`, `igraph_rngtype_glibc2`) were not exported from the shared library. + - `igraph_layout_graphopt()` no longer rounds the `spring_length` parameter to an integer. + - `igraph_get_all_shortest_paths_dijkstra()` no longer modifies the `res` vector's item destructor. + - `igraph_get_shortest_path_bellman_ford()` did not work correctly when calculating paths to all vertices. + - `igraph_arpack_rnsolve()` checks its parameters more carefully. + - `igraph_community_to_membership()` does not crash anymore when `csize` is requested but `membership` is not. + - `igraph_citing_cited_type_game()`: fixed memory leaks (PR #1700, thanks to Daniel Noom). + - `igraph_transitivity_undirected()`, `igraph_transitivity_avglocal_undirected()` and `igraph_transitivity_barrat()` no longer trigger an assertion failure when used with the null graph (PRs #1709, #1710). + - `igraph_(personalized_)pagerank()` would return incorrect results for weighted multigraphs with fewer than 128 vertices when using `IGRAPH_PAGERANK_ALGO_PRPACK`. + - `igraph_diversity()` now checks its input more carefully, and throws an error when the input graph has multi-edges or is directed. + - `igraph_shortest_paths_johnson()` would return incorrect results when the `to` argument differed from `from` (thanks to Daniel Noom). + - `igraph_is_graphical()` would fail to set the result variable for certain special degree sequences in the undirected simple graph case. + - Non-maximal clique finding functions would sometimes return incomplete results when finding more than 2147483647 (i.e. 2^31 - 1) cliques. + - GLPK internal errors no longer crash igraph. + - Fixed some potential memory leaks that could happen on error conditions or when certain functions were interrupted. + - When testing a DLL build on Windows, the `PATH` was sometimes not set correctly, causing the tests to fail (PR #1692). + - When compiling from the git repository (as opposed to the release tarball), the build would fail with recent versions of `bison` and `flex`. + +### Other + + - Documentation improvements. + - Much faster documentation builds. + - Allow using a pre-generated `arith.h` header for f2c when cross-compiling; see the Installation section of the documentation. + - The `IGRAPH_ENABLE_LTO` build option now supports the `AUTO` value, which uses LTO only if the compiler supports it. Warning: CMake may not always be able to detect that LTO is not fully supported. Therefore, the default setting is `OFF`. + - The following functions are now interruptible: `igraph_grg_game()`, `igraph_sbm_game()`, `igraph_barabasi_game()`, `igraph_barabasi_aging_game()`. + - Functions that use GLPK, such as `igraph_feedback_arc_set()` and `igraph_community_optimal_modularity()` are now interruptible. + - Add support for older versions of Clang that do not recognize the `-Wno-varargs` flag. + +### Acknowledgments + + - Big thanks to Daniel Noom for continuing to expand the test suite and discovering and fixing several bugs in the process! + +## [0.9.0] - 2021-02-16 + +### Added + + - Eulerian paths/cycles (PR #1346): + * `igraph_is_eulerian()` finds out whether an Eulerian path/cycle exists. + * `igraph_eulerian_path()` returns an Eulerian path. + * `igraph_eulerian_cycle()` returns an Eulerian cycle. + - Efficiency (PR #1344): + * `igraph_global_efficiency()` computes the global efficiency of a network. + * `igraph_local_efficiency()` computes the local efficiency around each vertex. + * `igraph_average_local_efficiency()` computes the mean local efficiency. + - Degree sequences (PR #1445): + * `igraph_is_graphical()` checks if a degree sequence has a realization as a simple or multigraph, with or without self-loops. + * `igraph_is_bigraphical()` checks if two degree sequences have a realization as a bipartite graph. + * `igraph_realize_degree_sequence()` now supports constructing non-simple graphs as well. + - There is a new fatal error handling mechanism (PR #1548): + * `igraph_set_fatal_handler()` sets the fatal error handler. It is the only function in this functionality group that is relevant to end users. + * The macro `IGRAPH_FATAL()` and the functions `igraph_fatal()` and `igraph_fatalf()` raise a fatal error. These are for internal use. + * `IGRAPH_ASSERT()` is a replacement for the `assert()` macro. It is for internal use. + * `igraph_fatal_handler_abort()` is the default fatal error handler. + - The new `IGRAPH_WARNINGF`, `IGRAPH_ERRORF` and `IGRAPH_FATALF` macros provide warning/error reporting with `printf`-like syntax. (PR #1627, thanks to Daniel Noom!) + - `igraph_average_path_length_dijkstra()` computes the mean shortest path length in weighted graphs (PR #1344). + - `igraph_get_shortest_paths_bellman_ford()` computes the shortest paths (including the vertex and edge IDs along the paths) using the Bellman-Ford algorithm (PR #1642, thanks to Guy Rozenberg). This makes it possible to calculate the shortest paths on graphs with negative edge weights, which was not possible before with Dijkstra's algorithm. + - `igraph_get_shortest_path_bellman_ford()` is a wrapper for `igraph_get_shortest_paths_bellman_ford()` for the single path case. + - `igraph_is_same_graph()` cheks that two labelled graphs are the same (PR #1604). + - Harmonic centrality (PR #1583): + * `igraph_harmonic_centrality()` computes the harmonic centrality of vertices. + * `igraph_harmonic_centrality_cutoff()` computes the range-limited harmonic centrality. + - Range-limited centralities, currently equivalent to the old functions with names ending in `_estimate` (PR #1583): + * `igraph_closeness_cutoff()`. + * `igraph_betweenness_cutoff()`. + * `igraph_edge_betweenness_cutoff()`. + - `igraph_vector_is_any_nan()` checks if any elements of an `igraph_vector_t` is NaN. + - `igraph_inclist_size()` returns the number of vertices in an incidence list. + - `igraph_lazy_adjlist_size()` returns the number of vertices in a lazy adjacency list. + - `igraph_lazy_inclist_size()` returns the number of vertices in a lazy incidence list. + - `igraph_bfs_simple()` now provides a simpler interface to the breadth-first search functionality. + +### Changed + + - igraph now uses a CMake-based build sysyem. + - GMP support can no longer be disabled. When GMP is not present on the system, igraph will use an embedded copy of Mini-GMP (PR #1549). + - Bliss has been updated to version 0.75. Bliss functions are now interruptible. Thanks to Tommi Junttila for making this possible! + - Adjacency and incidence lists: + * `igraph_adjlist_init()` and `igraph_lazy_adjlist_init()` now require the caller to specify what to do with loop and multiple edges. + * `igraph_inclist_init()` and `igraph_lazy_inclist_init()` now require the caller to specify what to do with loop edges. + * Adjacency and incidence lists now use `igraph_vector_int_t` consistently. + - Community detection: + * `igraph_community_multilevel()`: added resolution parameter. + * `igraph_community_fluid_communities()`: graphs with no vertices or with one vertex only are now supported; they return a trivial partition. + - Modularity: + * `igraph_modularity()` and `igraph_modularity_matrix()`: added resolution parameter. + * `igraph_modularity()` and `igraph_modularity_matrix()` now support the directed version of modularity. + * `igraph_modularity()` returns NaN for graphs with no edges to indicate that the modularity is not well-defined for such graphs. + - Centralities: + * `cutoff=0` is no longer interpreted as infinity (i.e. no cutoff) in `betweenness`, `edge_betweenness` and `closeness`. If no cutoff is desired, use a negative value such as `cutoff=-1`. + * The `nobigint` argument has been removed from `igraph_betweenness()`, `igraph_betweenness_estimate()` and `igraph_centralization_betweenness()`, as it is not longer needed. The current implementation is more accurate than the old one using big integers. + * `igraph_closeness()` now considers only reachable vertices during the calculation (i.e. the closeness is calculated per-component in the undirected case) (PR #1630). + * `igraph_closeness()` gained two additional output parameters, `reachable_count` and `all_reachable`, returning the number of reached vertices from each vertex, as well as whether all vertices were reachable. This allows for computing various generalizations of closeness for disconnected graphs (PR #1630). + * `igraph_pagerank()`, `igraph_personalized_pagerank()` and `igraph_personalized_pagerank_vs()` no longer support the `IGRAPH_PAGERANK_ALGO_POWER` method. Their `options` argument now has type `igraph_arpack_options_t *` instead of `void *`. + - Shortest paths (PR #1344): + * `igraph_average_path_length()` now returns the number of disconnected vertex pairs in the new `unconn_pairs` output argument. + * `igraph_diameter()` now return the result as an `igraph_real_t` instead of an `igraph_integer_t`. + * `igraph_average_path_length()` and `igraph_diameter()` now return `IGRAPH_INFINITY` when `unconn=FALSE` and the graph is not connected. Previously they returned the number of vertices. + - Trait-based random graph generators: + * `igraph_callaway_traits_game()` and `igraph_establishment_game()` now have an optional output argument to retrieve the generated vertex types. + * `igraph_callaway_traits_game()` and `igraph_establishment_game()` now allow omitting the type distribution vector, in which case they assume a uniform distribution. + * `igraph_asymmetric_preference_game()` now accept a different number of in-types and out-types. + - `igraph_subisomorphic_lad()` now supports graphs with self-loops. + - `igraph_is_chordal()` and `igraph_maximum_cardinality_search()` now support non-simple graphs and directed graphs. + - `igraph_realize_degree_sequence()` has an additional argument controlling whether multi-edges or self-loops are allowed. + - `igraph_is_connected()` now returns false for the null graph; see https://github.com/igraph/igraph/issues/1538 for the reasoning behind this decision. + - `igraph_lapack_ddot()` is renamed to `igraph_blas_ddot()`. + - `igraph_to_directed()`: added RANDOM and ACYCLIC modes (PR #1511). + - `igraph_topological_sorting()` now issues an error if the input graph is not acyclic. Previously it issued a warning. + - `igraph_vector_(which_)(min|max|minmax)()` now handles NaN elements. + - `igraph_i_set_attribute_table()` is renamed to `igraph_set_attribute_table()`. + - `igraph_i_sparsemat_view()` is renamed to `igraph_sparsemat_view()`. + +### Deprecated + + - `igraph_is_degree_sequence()` and `igraph_is_graphical_degree_sequence()` are deprecated in favour of the newly added `igraph_is_graphical()`. + - `igraph_closeness_estimate()` is deprecated in favour of the newly added `igraph_closeness_cutoff()`. + - `igraph_betweenness_estimate()` and `igraph_edge_betweenness_estimate()` are deprecated in favour of the newly added `igraph_betweenness_cutoff()` and `igraph_edge_betweenness_cutoff()`. + - `igraph_adjlist_remove_duplicate()` and `igraph_inclist_remove_duplicate()` are now deprecated in favour of the new constructor arguments in `igraph_adjlist_init()` and `igraph_inclist_init()`. + +### Removed + + - The following functions, all deprecated in igraph 0.6, have been removed (PR #1562): + * `igraph_adjedgelist_init()`, `igraph_adjedgelist_destroy()`, `igraph_adjedgelist_get()`, `igraph_adjedgelist_print()`, `igraph_adjedgelist_remove_duplicate()`. + * `igraph_lazy_adjedgelist_init()`, `igraph_lazy_adjedgelist_destroy()`, `igraph_lazy_adjedgelist_get()`, `igraph_lazy_adjedgelist_get_real()`. + * `igraph_adjacent()`. + * `igraph_es_adj()`. + * `igraph_subgraph()`. + - `igraph_pagerank_old()`, deprecated in 0.7, has been removed. + - `igraph_vector_bool` and `igraph_matrix_bool` functions that relied on inequality-comparing `igraph_bool_t` values are removed. + +### Fixed + + - Betweenness calculations are no longer at risk from integer overflow. + - The actual cutoff distance used in closeness calculation was one smaller than the `cutoff` parameter. This is corrected (PR #1630). + - `igraph_layout_gem()` was not interruptible; now it is. + - `igraph_barabasi_aging_game()` now checks its parameters more carefully. + - `igraph_callaway_traits_game()` and `igraph_establishment_game()` now check their parameters. + - `igraph_lastcit_game()` checks its parameters more carefully, and no longer crashes with zero vertices (PR #1625). + - `igraph_cited_type_game()` incorrectly rounded the attractivity vector entries to integers. + - `igraph_residual_graph()` now returns the correct _residual_ capacities; previously it wrongly returned the original capacities (PR #1598). + - `igraph_psumtree_update()` now checks for negative values and NaN. + - `igraph_communities_spinglass()`: fixed several memory leaks in the `IGRAPH_SPINCOMM_IMP_NEG` implementation. + - `igraph_incident()` now returns edges in the same order as `igraph_neighbors()`. + - `igraph_modularity_matrix()` returned incorrect results for weighted graphs. This is now fixed. (PR #1649, thanks to Daniel Noom!) + - `igraph_lapack_dgetrf()` would crash when passing `NULL` for its `ipiv` argument (thanks for the fix to Daniel Noom). + - Some `igraph_matrix` functions would fail to report errors on out-of-memory conditions. + - `igraph_maxdegree()` now returns 0 for the null graph or empty vector set. Previously, it did not handle this case. + - `igraph_vector_bool_all_e()` now considers all nonzero (i.e. "true") values to be the same. + - PageRank (PR #1640): + * `igraph_(personalized_)pagerank(_vs)()` now check their parameters more carefully. + * `igraph_personalized_pagerank()` no longer modifies its `reset` parameter. + * `igraph_(personalized_)pagerank(_vs)`: the `IGRAPH_PAGERANK_ALGO_ARPACK` method now handles self-loops correctly. + * `igraph_personalized_pagerank(_vs)()`: the result retuned for edgeless or all-zero-weight graphs with the `IGRAPH_PAGERANK_ALGO_ARPACK` ignored the personalization vector. This is now corrected. + * `igraph_personalized_pagerank(_vs)()` with a non-uniform personalization vector, a disconnected graph and the `IGRAPH_PAGERANK_ALGO_PRPACK` method would return results that were inconsistent with `IGRAPH_PAGERANK_ALGO_ARPACK`. This happened because PRPACK always used a uniform reset distribution when the random walk got stuck in a sink vertex. Now it uses the user-specified reset distribution for this case as well. + - Fixed crashes in several functions when passing a weighted graph with zero edges (due to `vector_min` being called on the zero-length weight vector). + - Fixed problems in several functions when passing in a graph with zero vertices. + - Weighted betweenness, closeness, PageRank, shortest path calculations and random walk functions now check if any weights are NaN. + - Many functions now reject input arguments containing NaN values. + - Compatibility with the PGI compiler. + +### Other + + - Documentation improvements. + - Improved error and warning messages. + - More robust error handling. + - General code cleanup to reduce the number of compiler warnings. + - igraph's source files have been re-organized for better maintainability. + - Debugging aid: When igraph is build with AddressSanitizer, the default error handler prints a stack trace before exiting. + - igraph can now be built with an external CXSparse library. + - The references to igraph source files in error and warning messages are now always relative to igraph's base directory. + - When igraph is built as a shared library, only public symbols are exported even on Linux and macOS. + +### Acknowledgments + + - Thanks to Daniel Noom for significantly expanding igraph's test coverage and exposing several issues in the process! + +## [0.8.5] - 2020-12-07 + +### Changed + + - `igraph_write_graph_pajek()`: the function now always uses the platform-native line endings (CRLF on Windows, LF on Unix and macOS). Earlier versions tried to enforce Windows line endings, but this was error-prone, and since all recent versions of Pajek support both line endings, enforcing Windows line endings is not necessary any more. + +### Fixed + + - Fixed several compilation issues with MINGW32/64 (PR #1554) + - `igraph_layout_davidson_harel()` was not interruptible; now it is. + - Added a missing memory cleanup call in `igraph_i_cattribute_combine_vertices()`. + - Fixed a few memory leaks in test cases. + +## [0.8.4] - 2020-11-24 + +### Fixed + + - `igraph_i_cattribute_combine_vertices()`: fixed invalid cleanup code that eventually filled up the "finally" stack when combining vertices with attributes extensively. + - `igraph_hrg_sample()`: fixed incorrect function prototype + - `igraph_is_posinf()` and `igraph_is_neginf()`: fixed incorrect result on platforms where the sign of the result of `isinf()` is not indicative of the sign of the input. + - Fixed building with vendored LAPACK and external BLAS + - Fixed building with XCode 12.2 on macOS + +### Other + + - Documentation improvements + - General code cleanup to reduce the number of compiler warnings + +## [0.8.3] - 2020-10-02 + +### Added + + - `igraph_vector_binsearch_slice()` performs binary search on a sorted slice of a vector. + +### Changed + + - `igraph_eigenvector_centrality()` assumes the adjacency matrix of undirected graphs to have twice the number of self-loops for each vertex on the diagonal. This makes the results consistent between an undirected graph and its directed equivalent when each edge is replaced by a mutual edge pair. + +### Fixed + + - `igraph_isomorphic()` now verifies that the input graphs have no multi-edges (PR #1464). + - `igraph_difference()` was creating superfluous self loops (#597). + - `igraph_count_multiple()` was giving incorrect results for self-loops in directed graph (PR #1399). + - `igraph_betweenness_estimate()`: fixed incorrect results with finite cutoff (PR #1392). + - `igraph_count_multiple()` was giving incorrect results for self-loops in directed graph (PR #1399). + - `igraph_eigen_matrix_symmetric()`: fixed incorrect matrix multiplication (PR #1379). + - Corrected several issues that could arise during an error condition (PRs #1405, #1406, #1438). + - `igraph_realize_degree_sequence()` did not correctly detect some non-graphical inputs. + - `igraph_is_graphical_degree_sequence()`: fixed incorrect results in undirected case (PR #1441). + - `igraph_community_leiden()`: fixed incorrect result when self-loops are present (PR #1476). + - `igraph_eigenvector_centrality()`: fixed incorrect value for isolated vertices in weighted graphs. + - `igraph_eigenvector_centrality()`: corrected the handling of self-loops. + - `igraph_layout_reingold_tilford()`: fixed an issue where branches of the tree would sometimes overlap. + +### Other + + - `igraph_degree_sequence_game()`: improved performance with `IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM` method. + - Improved the robustness of the test suite. + - Documentation improvements. + - Improved error and warning messages. + - Improved compatibility with recent versions of Microsoft Visual C. + +## [0.8.2] - 2020-04-28 + +### Changed + + - Improved argument checking: `igraph_all_st_mincuts()` and `igraph_sir()` + - Improved interruptibility: `igraph_sir()` + +### Fixed + + - `igraph_community_leiden()`: fixed crash when interrupting + - The tests are now more robust. Some incorrect test failures were fixed when + running on i386 architecture, or when using different versions of external + dependencies. + +### Other + + - Improved error messages from `igraph_sir()`. + - Improved compatibility with more recent versions of Microsoft Visual C. + +## [0.8.1] - 2020-03-13 + +### Changed + + - Improved interruptability: `igraph_degree_sequence_game()` + - Improved argument checking: `igraph_forest_fire_game()` + - Updated the plfit library to version 0.8.1 + +### Fixed + + - `igraph_community_edge_betweenness()`: fix for graphs with no edges (PR #1312) + - `igraph_bridges()` now handles multigraphs correctly (PR #1335) + - `igraph_avg_nearest_neighbor_degree()`: fix for memory leak in weighted case (PR #1339) + - `igraph_community_leiden()`: fix crash bug (PR #1357) + +### Other + + - Included `ACKOWLEDGEMENTS.md` + - Documentation improvements + +## [0.8.0] - 2020-01-29 + +### Added + + * Trees + + - `igraph_to_prufer()` and `igraph_from_prufer()` convert labelled trees to/from Prüfer sequences + - `igraph_tree_game()` samples uniformly from the set of labelled trees + - `igraph_is_tree()` checks if a graph is a tree + - `igraph_random_spanning_tree()` picks a spanning tree of a graph uniformly at random + - `igraph_random_edge_walk()` returns the indices of edges traversed by a random walk; useful for multigraphs + + * Community detection + + - `igraph_community_fluid_communities()` detects communities based on interacting fluids + - `igraph_community_leiden()` detects communities with the Leiden method + + * Cliques + + - `igraph_maximal_cliques_hist()` counts maximal cliques of each size + - `igraph_maximal_cliques_callback()` calls a function for each maximal clique + - `igraph_clique_size_hist()` counts cliques of each size + - `igraph_cliques_callback()` calls a function for each clique + - `igraph_weighted_cliques()` finds weighted cliques in graphs with integer vertex weights + - `igraph_weighted_clique_number()` computes the weighted clique number + - `igraph_largest_weighted_cliques()` finds the largest weighted cliques + + * Graph generators + + - `igraph_hsbm_game()` for a hierarchical stochastic block model + - `igraph_hsbm_list_game()` for a more general hierarchical stochastic block model + - `igraph_correlated_game()` generates pairs of correlated random graphs by perturbing existing adjacency matrix + - `igraph_correlated_pair_game()` generates pairs of correlated random graphs + - `igraph_tree_game()` samples uniformly from the set of labelled trees + - `igraph_dot_product_game()` generates a random dot product graph + - `igraph_realize_degree_sequence()` creates a single graph with a given degree sequence (Havel-Hakimi algorithm) + + * Graph embeddings + + - `igraph_adjacency_spectral_embedding()` and `igraph_laplacian_spectral_embedding()` provide graph embedddings + - `igraph_dim_select()` provides dimensionality selection for singular values using profile likelihood + + * Isomorphism + + - `igraph_automorphism_group()` computes the generators of the automorphism group of a simple graph + - `igraph_simplify_and_colorize()` encodes edge and self-loop multiplicities into edge and vertex colors; use in conjunction with VF2 to test isomorphism of non-simple graphs + + * Other + + - `igraph_bridges()` finds edges whose removal would disconnect a graph + - `igraph_vertex_coloring_greedy()` computes a vertex coloring using a greedy algorithm + - `igraph_rewire_directed_edges()` randomly rewires only the starting points or only the endpoints of directed edges + - Various `igraph_local_scan_*` functions provide local counts and statistics of neighborhoods + - `igraph_sample_sphere_surface()` samples points uniformly from the surface of a sphere + - `igraph_sample_sphere_volume()` samples points uniformly from the volume of a sphere + - `igraph_sample_dirichlet()` samples points from a Dirichlet distribution + - `igraph_malloc()`, to be paired with the existing `igraph_free()` + +### Changed + + - `igraph_degree_sequence_game()`: new method added for uniform sampling: `IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM` + - `igraph_modularity_matrix()`: removed `membership` argument (PR #1194) + - `igraph_avg_nearest_neighbor_degree()`: added `mode` and `neighbor_degree_mode` arguments (PR #1214). + - `igraph_get_all_simple_paths()`: added `cutoff` argument (PR #1232). + - `igraph_unfold_tree()`: no longer preserves edge ordering of original graph + - `igraph_decompose()`: support strongly connected components + - `igraph_isomorphic_bliss()`, `igraph_canonical_permutation()`, `igraph_automorphisms()`: added additional arguments to support vertex colored graphs (PR #873) + - `igraph_extended_chordal_ring`: added argument to support direction (PR #1096), and fixed issue #1093. + +### Other + + - The [Bliss isomorphism library](http://www.tcs.hut.fi/Software/bliss/) was updated to version 0.73. This version adds support for vertex colored and directed graphs. + - igraph now uses the high-performance [Cliquer library](https://users.aalto.fi/~pat/cliquer.html) to find (non-maximal) cliques + - Provide proper support for Windows, using `__declspec(dllexport)` and `__declspec(dllimport)` for `DLL`s and static usage by using `#define IGRAPH_STATIC 1`. + - Provided integer versions of `dqueue` and `stack` data types. + +[master]: https://github.com/igraph/igraph/compare/0.10.13..master +[0.10.13]: https://github.com/igraph/igraph/compare/0.10.12..0.10.13 +[0.10.12]: https://github.com/igraph/igraph/compare/0.10.11..0.10.12 +[0.10.11]: https://github.com/igraph/igraph/compare/0.10.10..0.10.11 +[0.10.10]: https://github.com/igraph/igraph/compare/0.10.9..0.10.10 +[0.10.9]: https://github.com/igraph/igraph/compare/0.10.8..0.10.9 +[0.10.8]: https://github.com/igraph/igraph/compare/0.10.7..0.10.8 +[0.10.7]: https://github.com/igraph/igraph/compare/0.10.6..0.10.7 +[0.10.6]: https://github.com/igraph/igraph/compare/0.10.5..0.10.6 +[0.10.5]: https://github.com/igraph/igraph/compare/0.10.4..0.10.5 +[0.10.4]: https://github.com/igraph/igraph/compare/0.10.3..0.10.4 +[0.10.3]: https://github.com/igraph/igraph/compare/0.10.2..0.10.3 +[0.10.2]: https://github.com/igraph/igraph/compare/0.10.1..0.10.2 +[0.10.1]: https://github.com/igraph/igraph/compare/0.10.0..0.10.1 +[0.10.0]: https://github.com/igraph/igraph/compare/0.9.10..0.10.0 +[0.9.10]: https://github.com/igraph/igraph/compare/0.9.9...0.9.10 +[0.9.9]: https://github.com/igraph/igraph/compare/0.9.8...0.9.9 +[0.9.8]: https://github.com/igraph/igraph/compare/0.9.7...0.9.8 +[0.9.7]: https://github.com/igraph/igraph/compare/0.9.6...0.9.7 +[0.9.6]: https://github.com/igraph/igraph/compare/0.9.5...0.9.6 +[0.9.5]: https://github.com/igraph/igraph/compare/0.9.4...0.9.5 +[0.9.4]: https://github.com/igraph/igraph/compare/0.9.3...0.9.4 +[0.9.3]: https://github.com/igraph/igraph/compare/0.9.2...0.9.3 +[0.9.2]: https://github.com/igraph/igraph/compare/0.9.1...0.9.2 +[0.9.1]: https://github.com/igraph/igraph/compare/0.9.0...0.9.1 +[0.9.0]: https://github.com/igraph/igraph/compare/0.8.5...0.9.0 +[0.8.5]: https://github.com/igraph/igraph/compare/0.8.4...0.8.5 +[0.8.4]: https://github.com/igraph/igraph/compare/0.8.3...0.8.4 +[0.8.3]: https://github.com/igraph/igraph/compare/0.8.2...0.8.3 +[0.8.2]: https://github.com/igraph/igraph/compare/0.8.1...0.8.2 +[0.8.1]: https://github.com/igraph/igraph/compare/0.8.0...0.8.1 +[0.8.0]: https://github.com/igraph/igraph/releases/tag/0.8.0 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..26256d5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,185 @@ +# Minimum CMake that we require is 3.18. +# Some of the recent features we use: +# * --ignore-eol when comparing unit test results with expected outcomes (3.14) +# * CROSSCOMPILING_EMULATOR can be a semicolon-separated list to pass arguments (3.15) +# * SKIP_REGULAR_EXPRESSION to handle skipped tests properly (3.16) +# * CheckLinkerFlag for HAVE_NEW_DTAGS test (3.18) +# * cmake -E cat (3.18) +cmake_minimum_required(VERSION 3.18...3.29) + +# Add etc/cmake to CMake's search path so we can put our private stuff there +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/etc/cmake) + +# Set a default build type if none was specified +# This must precede the project() line, which would set the CMAKE_BUILD_TYPE +# to 'Debug' with single-config generators on Windows. +# Note that we must do this only if PROJECT_NAME is not set at this point. If +# it is set, it means that igraph is being used as a subproject of another +# project. +if(NOT PROJECT_NAME) + include(BuildType) +endif() + +# Prevent in-source builds +include(PreventInSourceBuilds) + +# Make use of ccache if it is present on the host system -- unless explicitly +# asked to disable it +include(UseCCacheWhenInstalled) + +# Figure out the version number from Git +include(version) + +# Declare the project, its version number and language +project( + igraph + VERSION ${PACKAGE_VERSION_BASE} + DESCRIPTION "A library for creating and manipulating graphs" + HOMEPAGE_URL https://igraph.org + LANGUAGES C CXX +) + +# Include some compiler-related helpers and set global compiler options +include(compilers) + +# Detect is certain attributes are supported by the compiler +include(attribute_support) + +# Set default symbol visibility to hidden +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) + +# Set C and C++ standard version +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED True) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Expose the BUILD_SHARED_LIBS option in the ccmake UI +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) + +# Add switches to use sanitizers and debugging helpers if needed +include(debugging) +include(sanitizers) + +# Enable fuzzer instrumentation if needed +# FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION is a conventional +# macro used to adapt code for fuzzability, for example by +# reducing largest allowed graph sizes when reading various +# file formats. +if(BUILD_FUZZING) + add_compile_options(-fsanitize=fuzzer-no-link) + add_compile_definitions(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) +endif() + +# Add version information +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/igraph_version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/igraph_version.h +) + +# Create configuration options for optional features +include(features) + +# Handle dependencies and dependency-related configuration options +include(dependencies) +find_dependencies() + +# Run compile-time checks, generate config.h and igraph_threading.h +include(CheckSymbolExists) +include(CheckIncludeFiles) +include(CMakePushCheckState) + +# First we check for some functions and symbols +cmake_push_check_state() +if(NEED_LINKING_AGAINST_LIBM) + list(APPEND CMAKE_REQUIRED_LIBRARIES m) +endif() +check_symbol_exists(strcasecmp strings.h HAVE_STRCASECMP) +check_symbol_exists(strncasecmp strings.h HAVE_STRNCASECMP) +check_symbol_exists(_stricmp string.h HAVE__STRICMP) +check_symbol_exists(_strnicmp string.h HAVE__STRNICMP) +check_symbol_exists(strdup string.h HAVE_STRDUP) +check_symbol_exists(strndup string.h HAVE_STRNDUP) +check_include_files(xlocale.h HAVE_XLOCALE) +if(HAVE_XLOCALE) + # On BSD, uselocale() is in xlocale.h instead of locale.h. + # Some systems provide xlocale.h, but uselocale() is still in locale.h, + # thus we try both. + check_symbol_exists(uselocale "xlocale.h;locale.h" HAVE_USELOCALE) +else() + check_symbol_exists(uselocale locale.h HAVE_USELOCALE) +endif() +check_symbol_exists(_configthreadlocale locale.h HAVE__CONFIGTHREADLOCALE) +cmake_pop_check_state() + +# Check for 128-bit integer multiplication support, floating-point endianness, +# support for built-in overflow detection and fast bit operation support. +include(ieee754_endianness) +include(uint128_support) +include(bit_operations_support) +include(safe_math_support) + +if(NOT HAVE_USELOCALE AND NOT HAVE__CONFIGTHREADLOCALE) + message(WARNING "igraph cannot set per-thread locale on this platform. igraph_enter_safelocale() and igraph_exit_safelocale() will not be safe to use in multithreaded programs.") +endif() + +# Check for code coverage support +option(IGRAPH_ENABLE_CODE_COVERAGE "Enable code coverage calculation" OFF) +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND IGRAPH_ENABLE_CODE_COVERAGE) + include(CodeCoverage) + append_coverage_compiler_flags() + setup_target_for_coverage_lcov( + NAME coverage + EXECUTABLE "${CMAKE_COMMAND}" "--build" "${PROJECT_BINARY_DIR}" "--target" "check" + # Generated files are excluded; apparently the CodeCoverage script has some + # problems with them. Yes, the exclusion is correct, it refers to a nonexistent + # directory that somehow gets into the coverage results. /Applications and + # /Library/Developer are for macOS -- they exclude files from the macOS SDK. + EXCLUDE "io/*.l" "src/io/parsers/*" "io/parsers/*" "/Applications/Xcode*" "/Library/Developer/*" "examples/*" "interfaces/*" "tests/*" "vendor/pcg/*" + ) +endif() + +# Generate configuration headers +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/src/config.h +) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/igraph_config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/igraph_config.h +) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/igraph_threading.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/igraph_threading.h +) + +# Enable unit tests. Behave nicely and do this only if we are not being +# included as a sub-project in another CMake project +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(CTest) +endif() + +# Traverse subdirectories. vendor/ should come first because code in +# src/CMakeLists.txt depends on targets in vendor/ +add_subdirectory(vendor) +add_subdirectory(src) +add_subdirectory(interfaces) +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + add_subdirectory(tests) +endif() +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_FUZZING) + add_subdirectory(fuzzing) +endif() +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + add_subdirectory(doc) +endif() + +# Configure packaging -- only if igraph is the top-level project and not a +# subproject +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(packaging) +endif() + +# Show result of configuration +include(summary) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3402d54 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,178 @@ +# igraph Code of Conduct + +## Introduction + +This code of conduct applies to all spaces managed by the igraph project, +including all public and private mailing lists, issue trackers, wikis, blogs, +Twitter, and any other communication channel used by our community. Any events +related to our community shall also be bound by this code of conduct or a very +similar variant thereof. + +This code of conduct should be honored by everyone who participates in the +igraph community formally or informally, or claims any affiliation with the +project, in any project-related activities, and, especially, when representing +the project, in any capacity. + +This code of conduct is neither exhaustive nor complete. It serves to distill +our common understanding of a collaborative, shared environment and goals. +Please try to follow this code in spirit as much as in letter, to create +a friendly and productive environment that enriches the surrounding community. + +## Specific guidelines + +We strive to: + +1. Be open. We invite anyone to participate in our community. We prefer to use + public methods of communication for project-related messages, unless + discussing something sensitive. This applies to messages for help or + project-related support, too; not only is a public-support request much more + likely to result in an answer to a question, it also ensures that any + inadvertent mistakes in answering are more easily detected and corrected. + +2. Be empathetic, welcoming, friendly, and patient. We work together to resolve + conflict, and assume good intentions. We may all experience some frustration + from time to time, but we do not allow frustration to turn into a personal + attack. A community where people feel uncomfortable or threatened is not a + productive one. + +3. Be collaborative. Our work will be used by other people, and in turn we will + depend on the work of others. When we make something for the benefit of the + project, we are willing to explain to others how it works, so that they can + build on the work to make it even better. Any decision we make will affect + users and colleagues, and we take those consequences seriously when making + decisions. + +4. Be inquisitive. Nobody knows everything! Asking questions early avoids many + problems later, so we encourage questions, although we may direct them to + the appropriate forum. We will try hard to be responsive and helpful. + +5. Be careful in the words that we choose. We are careful and respectful in + our communication and we take responsibility for our own words. Be kind to + others. Do not insult or put down other participants. We do not tolerate + harassment or other exclusionary behavior, such as: + + * Violent threats or language directed against another person. + + * Sexist, racist, or otherwise discriminatory jokes and language. + + * Posting sexually explicit or violent material. + + * Posting (or threatening to post) other people’s personally identifying + information (“doxing”). + + * Sharing private content, such as emails sent privately or non-publicly, + or unlogged forums, such as IRC channel history, without the sender’s consent. + + * Personal insults, especially those using racist or sexist terms. + + * Unwelcome sexual attention. + + * Excessive profanity. Please avoid swearwords; people differ greatly in + their sensitivity to swearing. + + * Repeated harassment of others. In general, if someone asks you to stop, + then stop. + + * Advocating for, or encouraging, any of the above behavior. + +## Diversity statement + +The igraph project welcomes and encourages participation by everyone. We are +committed to being a community that everyone enjoys being part of. Although +we may not always be able to accommodate each individual’s preferences, we try +our best to treat everyone kindly. + +No matter how you identify yourself or how others perceive you: we welcome you. +Though no list can hope to be comprehensive, we explicitly honor diversity in: +age, culture, ethnicity, genotype, gender identity or expression, language, +national origin, neurotype, phenotype, political beliefs, profession, race, +religion, sexual orientation, socioeconomic status, subculture and technical +ability, to the extent that these do not conflict with this code of conduct. + +Though we welcome people fluent in all languages, igraph development is +conducted in English. + +Standards for behavior in the igraph community are detailed in the Code of +Conduct above. Participants in our community should uphold these standards +in all their interactions and help others to do so as well (see next section). + +## Reporting guidelines + +We know that it is painfully common for internet communication to start at or +devolve into obvious and flagrant abuse. We also recognize that sometimes +people may have a bad day, or be unaware of some of the guidelines in this Code +of Conduct. Please keep this in mind when deciding on how to respond to a +breach of this Code. + +For clearly flagrant breaches, report those to the igraph organisation +(see below). For possibly unintentional breaches, you may reply to the person +and point out this Code of Conduct (either in public or in private, whatever is +most appropriate). If you would prefer not to do that, please feel free to +report to the igraph organisation directly, or ask the organisation for +advice, in confidence. + +You can report issues to the igraph organisation, at . +Currently, the following persons will receive your report: + +* Gábor Csárdi + +* Tamás Nepusz + +* Szabolcs Horvát + +* Vincent Traag + +If your report involves any of the above mentioned persons, or if they feel +they have a conflict of interest in handling it, they will recuse themselves +from considering your report. Alternatively, if, for any reason, you feel +uncomfortable making a report to the organisation directly, then you can also +contact any of the above mentioned persons individually. + +## Incident reporting + +We will investigate and respond to all complaints. The igraph organisation will +protect the identity of the reporter, and treat the content of complaints as +confidential (unless the reporter agrees otherwise). + +In case of flagrant breaches, e.g., personal threats or violent, sexist or +racist language, we will immediately disconnect the originator from igraph. In +particular, the organisation will + +1. Immediately disconnect the originator from all igraph communication channels. + +2. Revoke any granted permissions from the originator. + +3. Reply to the reporter that their report has been received and that the + originator has been disconnected. + +4. In every case, the moderator should make a reasonable effort to contact the + originator, and tell them specifically how their language or actions qualify + as a “flagrant breach”. The moderator should also say that, if the + originator believes this is unfair or they want to be reconnected to igraph, + they have the right to ask for a review, as below, by the igraph + organisation. + +5. The igraph organisation will formally review and sign off on all cases + where this mechanism has been applied to make sure it is not being + used to control ordinary heated disagreement. + +In cases not involving flagrant breaches of this code of conduct, the process +for acting on any received code of conduct violation report will be: + +1. acknowledgement that the report has been received + +2. reasonable discussion/feedback + +3. mediation (if feedback didn’t help, and only if both reporter and reportee + agree to this) + +The organisation will respond to any report as soon as possible, and at most +within 5 working days. + +## Endnotes + +This Code of Conduct is inspired by [the SciPy Code of Conduct](https://docs.scipy.org/doc/scipy/reference/dev/conduct/code_of_conduct.html). + +The current organisation of the igraph community is rudimentary. A more +professional organisation may develop in the future, at which point the +procedure of handling incident reports will also be further formalized. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..087dbd0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,244 @@ +# Contributing to this project + +Thank you for being interested in contributing to `igraph`! We need the help of +volunteers to keep the package going, so every little bit is welcome. You can help out +the project in several different ways. + +This repository only hosts the C code of the `igraph` project. Even if you are not so +experienced with C, you can contribute in a number of ways: + +1. Respond to user questions on our [support forum](https://igraph.discourse.group/). +2. Correct or improve our [documentation](https://igraph.org/c/html/latest/). +3. Go over [open issues](https://github.com/igraph/igraph/issues): + - Are some older issues still relevant in the most recent version? If not, write a + comment to the issue stating that you feel that the issue is not relevant any more. + - Can you reproduce some of the bugs that are reported? If so, write a comment to + the issue stating that this is still a problem in version X. + - Some [issues point out problems with the documentation](https://github.com/igraph/igraph/labels/documentation); + perhaps you could help correct these? + - Some [issues require clarifying a mathematical problem, or some literature research](https://github.com/igraph/igraph/labels/theory), + before any programming can begin. Can you contribute through your theoretical expertise? + - Looking to contribute code? Take a look at some [good first issues](https://github.com/igraph/igraph/labels/good%20first%20issue). + +## Using the issue tracker + +- The issue tracker is the preferred channel for [bug reports](#bugs), + [feature requests](#features) and [submitting pull requests](#pull-requests). + +- Do you have a question? Please use our [igraph support forum](https://igraph.discourse.group) + for support requests. + +- Please keep the discussion on topic and respect the opinions of others, and + adhere to our [Code of Conduct](https://igraph.org/code-of-conduct.html). + + +## Bug reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. +Good bug reports are extremely helpful — thank you for reporting! + +Guidelines for bug reports: + +1. **Make sure that the bug is in the C code of igraph and not in one of the + higher level interfaces** — if you are using igraph from R, Python + or Mathematica, consider submitting your issue in + [igraph/rigraph](https://github.com/igraph/rigraph/issues/new), + [igraph/python-igraph](https://github.com/igraph/python-igraph/issues/new) + or [szhorvat/IGraphM](https://github.com/szhorvat/IGraphM/issues/new) + instead. If you are unsure whether your issue is in the C layer, submit + a bug report in the repository of the higher level interface — + we will transfer the issue here if it indeed affects the C layer. + +2. **Use the GitHub issue search** — check if the issue has already been + reported. + +3. **Check if the issue has been fixed** — try to reproduce it using the + latest `master` or development branch in the repository. + +4. **Isolate the problem** — create a [short, self-contained, correct + example](http://sscce.org/). + +Please try to be as detailed as possible in your report and provide all +necessary information. What is your environment? What steps will reproduce the +issue? What would you expect to be the outcome? All these details will help us +to fix any potential bugs. + +Example: + +> Short and descriptive example bug report title +> +> A summary of the issue and the compiler/OS environment in which it occurs. If +> suitable, include the steps required to reproduce the bug. +> +> 1. This is the first step +> 2. This is the second step +> 3. Further steps, etc. +> +> `` - a link to the reduced test case +> +> Any other information you want to share that is relevant to the issue being +> reported. This might include the lines of code that you have identified as +> causing the bug, and potential solutions (and your opinions on their +> merits). + + + +## Feature requests + +Feature requests are always welcome. First, take a moment to find out whether your +idea fits with the scope and aims of the project. Please provide as much detail +and context as possible, and where possible, references to relevant literature. +Having said that, implementing new features can be quite time consuming, and as +such they might not be implemented quickly. In addition, the development team +might decide not to implement a certain feature. It is up to you to make a case +to convince the project's developers of the merits of this feature. + + +## Pull requests + +_**Note:** The wiki has a lot of useful information for newcomers, as well as a +[quick start guide](https://github.com/igraph/igraph/wiki/Quickstart-for-new-contributors)!_ + +Good pull requests - patches, improvements, new features - are a fantastic help. +They should remain focused in scope and avoid containing unrelated commits. +Please also take a look at our [tips on writing igraph code](#tips) before +getting your hands dirty. + +**Please ask first** before embarking on any significant pull request (e.g. +implementing features, refactoring code, porting to a different language), +otherwise you risk spending a lot of time working on something that the +project's developers might not want to merge into the project. + +Please adhere to the coding conventions used throughout a project (indentation, +accurate comments, etc.) and any other requirements (such as test coverage). + +Follow the following steps if you would like to make a new pull request: + +1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com// + # Navigate to the newly cloned directory + cd + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com// + ``` + +2. Please checkout the section on [branching](#branching) to see whether you + need to branch off from the `master` branch or the `develop` branch. + + If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout + git pull --rebase upstream + ``` + +3. Create a new topic branch (off the targeted branch, see + [branching](#branching) section) to contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +4. Please commit your changes in logical chunks, and try to provide clear commit + messages. It helps us during the review process if we can follow your thought + process during the implementation. If you hit a dead end, use `git revert` + to revert your commits or just go back to an earlier commit with `git checkout` + and continue your work from there. + +5. We have a [checklist for new igraph functions](https://github.com/igraph/igraph/wiki/Checklist-for-new-(and-old)-functions). + If you have added any new functions to igraph, please go through the + checklist to ensure that your functions play nicely with the rest of the + library. + +6. Make sure that your PR is based off the latest code and locally merge (or + rebase) the upstream development branch into your topic branch: + + ```bash + git pull [--rebase] upstream + ``` + + Rebasing is preferable over merging as you do not need to deal with merge + conflicts; however, if you already have many commits, merging the upstream + development branch may be faster. + +7. WHen your topic branch is up-to-date with the upstream development branch, you can + push your topic branch up to your fork: + + ```bash + git push origin + ``` + +8. [Open a pull request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description. + +**IMPORTANT**: By submitting a pull request, you agree to allow the project +owner to license your work under the same license as that used by the project, +see also [Legal Stuff](#legal). + + +### Branching + +`igraph` is committed to [semantic versioning](https://semver.org/). We are +currently still in the development release (0.x), which in principle is a mark +that the public API is not yet stable. Regardless, we try to maintain semantic +versioning also for the development releases. We do so as follows. Any released +minor version (0.x.z) will be API backwards-compatible with any previous release +of the *same* minor version (0.x.y, with y < z). This means that *if* there is +an API incompatible change, we will increase the minor version. For example, +release 0.8.1 is API backwards-compatible with release 0.8.0, while release +0.9.0 might be API incompatible with version 0.8.1. Note that this only concerns +the *public* API, internal functions may change also within a minor version. + +There will always be two versions of `igraph`: the most recent released version, +and the next upcoming minor release, which is by definition not yet released. +The most recent release version is in the `master` branch, while the next +upcoming minor release is in the `develop` branch. If you make a change that is +API incompatible with the most recent release, it **must** be merged to +the `develop` branch. If the change is API backwards-compatible, it **can** be +merged to the `master` branch. It is possible that you build on recent +improvements in the `develop` branch, in which case your change should of course +target the `develop` branch. If you only add new functionality, but do not +change anything of the existing API, this should be backwards-compatible, and +can be merged in the `master` branch. + +When you make a new pull request, please specify the correct target branch. The +maintainers of `igraph` may decide to retarget your pull request to the correct +branch. Retargeting you pull request may result in merge conflicts, so it is +always good to decide **before** starting to work on something whether you +should start from the `master` branch or from the `develop` branch. In most +cases, changes in the `master` branch will also be merged to the `develop` +branch by the maintainers. + +If you are unsure about the branch to target, open an issue about your proposed +feature and we can discuss the appropriate target branch in the issue before +you send a PR. + + +## Writing igraph Code + +[Some tips on writing igraph code](https://github.com/igraph/igraph/wiki/Tips-on-writing-igraph-code). + +## Ask Us! + +In general, if you are not sure about something, please ask! You can +open an issue on GitHub, open a thread in our +[igraph support forum](https://igraph.discourse.group), or write to +[@ntamas](https://github.com/ntamas), [@vtraag](https://github.com/vtraag), +[@szhorvat](https://github.com/szhorvat), [@iosonofabio](https://github.com/iosonofabio) or +[@gaborcsardi](https://github.com/gaborcsardi). +We prefer open communication channels, because others can then learn from it +too. + + +## Legal Stuff + +This is a pain to deal with, but we can't avoid it, unfortunately. + +`igraph` is licensed under the "General Public License (GPL) version 2, or +later". The igraph manual is licensed under the "GNU Free Documentation +License". By submitting a patch or pull request, you agree to allow the project +owner to license your work under the same license as that used by the project. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..3d2f470 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,101 @@ +# Contributors ✨ + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Gábor Csárdi

💻

Tamás Nepusz

💻

Szabolcs Horvát

💻

Vincent Traag

💻

GroteGnoom

💻

Fabio Zanini

💻

Jan Katins

💻

Sancar Adali

💻

Ferran Parés

💻

mvngu

💻

Dr. Nick

💻

jannick0

💻

Jérôme Benoit

💻

Frederik Harwath

💻

AdamKorcz

💻

Antonio Rojas

💻

Árpád Horváth

💻

Peter Scott

💻

Navid Dianati

💻

YasirKusay

💻

Andreas Beham

💻

Bart Kastermans

💻

Erik Welch

💻

Hong Xu

💻

Hosseinazari

💻

Jean Monlong

💻

Keivin98

💻

Leonardo de Araujo

💻

Min Kim

💻

Nikolay Khitrin

💻

Peter Schmiedeskamp

💻

Philipp A.

💻

Ramy Saied

💻

Robert Schütz

💻

Ryan Duffin

💻

Shlomi Fish

💻

Tomasz Kłoczko

💻

Watal M. Iwasaki

💻

Aman Verma

💻

guy rozenberg

💻

Artem V L

💻

Kateřina Č.

💻

valdaarhun

💻

YuliYudith

💻

alexsyou

💻

Rohit Tawde

💻

alexperrone

💻

Georgica Bors

💻

MEET PATEL

💻

kwofach

💻

Kevin Zhu

💻

Pradeep Krishnamurthy

💻

flange-ipb

💻

Juan Julián Merelo Guervós

💻

Radoslav Fulek

💻

professorcode1

💻

larah19

💻

Biswapriyo Nath

💻

Gwyn Ciesla

💻

aagon

💻

Quinn Buratynski

💻

Arnar Bjarni Arnarson

💻

David Seifert

💻

Kirill Müller

💻

Michael

💻
+ + + + + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 0000000..92702f3 --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,73 @@ +Thanks goes to these wonderful people: + +Gábor Csárdi (@gaborcsardi) +Tamás Nepusz (@ntamas) +Szabolcs Horvát (@szhorvat) +Vincent Traag (@vtraag) +GroteGnoom (@GroteGnoom) +Fabio Zanini (@iosonofabio) +Jan Katins (@jankatins) +Sancar Adali (@adalisan) +Ferran Parés (@FerranPares) +mvngu (@mvngu) +Dr. Nick (@das-intensity) +jannick0 (@jannick0) +Jérôme Benoit (@jgmbenoit) +Frederik Harwath (@frederik-h) +AdamKorcz (@AdamKorcz) +Antonio Rojas (@antonio-rojas) +Árpád Horváth (@horvatha) +Peter Scott (@PeterScott) +Navid Dianati (@naviddianati) +YasirKusay (@YasirKusay) +Andreas Beham (@abeham) +Bart Kastermans (@kasterma) +Erik Welch (@eriknw) +Hong Xu (@xuhdev) +Hosseinazari (@Hosseinazari) +Jean Monlong (@jmonlong) +Keivin98 (@Keivin98) +Leonardo de Araujo (@araujo88) +Min Kim (@msk) +Nikolay Khitrin (@khitrin) +Peter Schmiedeskamp (@pschmied) +Philipp A. (@flying-sheep) +Ramy Saied (@RamySaied1) +Robert Schütz (@dotlambda) +Ryan Duffin (@ryanduffin) +Shlomi Fish (@shlomif) +Tomasz Kłoczko (@kloczek) +Watal M. Iwasaki (@heavywatal) +Aman Verma (@nograpes) +guy rozenberg (@guyroznb) +Artem V L (@luav) +Kateřina Č. (@Katterrina) +valdaarhun (@valdaarhun) +YuliYudith (@YuliYudith) +alexsyou (@alexsyou) +Rohit Tawde (@rohitt28) +alexperrone (@alexperrone) +Georgica Bors (@borsgeorgica) +MEET PATEL (@meetpatel0963) +kwofach (@kwofach) +Kevin Zhu (@Gomango999) +Pradeep Krishnamurthy (@pradkrish) +flange-ipb (@flange-ipb) +Juan Julián Merelo Guervós (@JJ) +Radoslav Fulek (@rfulekjames) +professorcode1 (@professorcode1) +larah19 (@larah19) +Biswapriyo Nath (@Biswa96) +Gwyn Ciesla (@limburgher) +aagon (@aagon) +Quinn Buratynski (@GanzuraTheConsumer) +Arnar Bjarni Arnarson (@Tagl) +David Seifert (@SoapGentoo) +Kirill Müller (@krlmlr) +Michael (@gendelpiekel) + +This project follows the [all-contributors][1] specification. Contributions of any kind welcome! + +This file is an automatically generated, plain-text version of CONTRIBUTORS.md. + +[1]: https://github.com/all-contributors/all-contributors diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..6d45519 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..04bd5c9 --- /dev/null +++ b/ChangeLog @@ -0,0 +1 @@ +See CHANGELOG.md for a list of changes between versions. diff --git a/IGRAPH_VERSION b/IGRAPH_VERSION new file mode 100644 index 0000000..4dd35ad --- /dev/null +++ b/IGRAPH_VERSION @@ -0,0 +1 @@ +0.10.13 \ No newline at end of file diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..307dd76 --- /dev/null +++ b/INSTALL @@ -0,0 +1,7 @@ +Instructions for installation are provided in Chapter 2 of the manual; see +`doc/html` in the distributed tarball. + +An online version of the installation instructions for the most recent version +can be found here: + +https://igraph.org/c/doc/igraph-Installation.html diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..ec318ba --- /dev/null +++ b/NEWS @@ -0,0 +1,5 @@ +News about each release of igraph from version 0.8 onwards can be found in +CHANGELOG.md. + +Archived news items before version 0.7 are to be found in ONEWS -- these are +most likely of historical interest only. diff --git a/ONEWS b/ONEWS new file mode 100644 index 0000000..4a262ca --- /dev/null +++ b/ONEWS @@ -0,0 +1,1433 @@ + +igraph 0.6.5 +============ + +Released February 24, 2013 + +The version number is not a mistake, we jump to 0.6.5 from 0.6, +for technical reasons. + +R: new features and bug fixes +----------------------------- + +- Added a vertex shape API for defining new vertex shapes, and also + a couple of new vertex shapes. +- Added the get.data.frame() function, opposite of graph.data.frame(). +- Added bipartite support to the Pajek reader and writer, closes bug + \#1042298. +- `degree.sequence.game()` has a new method now: "simple_no_multiple". +- Added the is.degree.sequence() and is.graphical.degree.sequence() + functions. +- rewire() has a new method: "loops", that can create loop edges. +- Walktrap community detection now handles isolates. +- layout.mds() returns a layout matrix now. +- layout.mds() uses LAPACK instead of ARPACK. +- Handle the '~' character in write.graph and read.graph. Bug + \#1066986. +- Added k.regular.game(). +- Use vertex names to plot if no labels are specified in the function + call or as vetex attributes. Fixes issue \#1085431. +- power.law.fit() can now use a C implementation. + +- Fixed a bug in barabasi.game() when out.seq was an empty vector. +- Fixed a bug that made functions with a progress bar fail if called + from another package. +- Fixed a bug when creating graphs from a weighted integer adjacency + matrix via graph.adjacency(). Bug \#1019624. +- Fixed overflow issues in centralization calculations. +- Fixed a minimal.st.separators() bug, some vertex sets were incorrectly + reported as separators. Bug \#1033045. +- Fixed a bug that mishandled vertex colors in VF2 isomorphism + functions. Bug \#1032819. +- Pajek exporter now always quotes strings, thanks to Elena Tea Russo. +- Fixed a bug with handling small edge weights in shortest paths + calculation in shortest.paths() (Dijkstra's algorithm.) Thanks to + Martin J Reed. +- Weighted transitivity uses V(graph) as 'vids' if it is NULL. +- Fixed a bug when 'pie' vertices were drawn together with other + vertex shapes. +- Speed up printing graphs. +- Speed up attribute queries and other basic operations, by avoiding + copying of the graph. Bug \#1043616. +- Fixed a bug in the NCV setting for ARPACK functions. It cannot be + bigger than the matrix size. +- layout.merge()'s DLA mode has better defaults now. +- Fixed a bug in layout.mds() that resulted vertices on top of each + other. +- Fixed a bug in layout.spring(), it was not working properly. +- Fixed layout.svd(), which was completely defunct. +- Fixed a bug in layout.graphopt() that caused warnings and on + some platforms crashes. +- Fixed community.to.membership(). Bug \#1022850. +- Fixed a graph.incidence() crash if it was called with a non-matrix + argument. +- Fixed a get.shortest.paths bug, when output was set to "both". +- Motif finding functions return NA for isomorphism classes that are + not motifs (i.e. not connected). Fixes bug \#1050859. +- Fixed get.adjacency() when attr is given, and the attribute has some + complex type. Bug \#1025799. +- Fixed attribute name in graph.adjacency() for dense matrices. Bug + \#1066952. +- Fixed erratic behavior of alpha.centrality(). +- Fixed igraph indexing, when attr is given. Bug \#1073705. +- Fixed a bug when calculating the largest cliques of a directed + graph. Bug \#1073800. +- Fixed a bug in the maximal clique search, closes \#1074402. +- Warn for negative weights when calculating PageRank. +- Fixed dense, unweighted graph.adjacency when diag=FALSE. Closes + issue \#1077425. +- Fixed a bug in eccentricity() and radius(), the results were often + simply wrong. +- Fixed a bug in get.all.shortest.paths() when some edges had zero weight. +- graph.data.frame() is more careful when vertex names are numbers, to + avoid their scientific notation. Fixes issue \#1082221. +- Better check for NAs in vertex names. Fixes issue \#1087215 +- Fixed some potential crashes in the DrL layout generator. +- Fixed a bug in the Reingold-Tilford layout when the graph is + directed and mode != ALL. +- Eliminate gap between vertex and edge when plotting an edge without an arrow. + Fixes \#1118448. +- Fixed a bug in has.multiple() that resulted in false negatives for + some undirected graphs. +- Fixed a crash in weighted betweenness calculation. +- R plotting: fixed a bug that caused misplaced arrows at rectangle + vertex shapes. + +Python news and fixes +--------------------- + +- Added bipartite support to the Pajek reader and writer, closes bug + \#1042298. +- Graph.Degree_Sequence() has a new method now: "no_multiple". +- Added the is_degree_sequence() and is_graphical_degree_sequence() + functions. +- rewire() has a new mode: "loops", that can create loop edges. +- Walktrap community detection now handles isolates. +- Added Graph.K_Regular(). +- power_law_fit() now uses a C implementation. +- Added support for setting the frame (stroke) width of vertices using the + frame_width attribute or the vertex_frame_width keyword argument in plot() +- Improved Inkscape-friendly SVG output from Graph.write_svg(), thanks to drlog +- Better handling of named vertices in Graph.delete_vertices() +- Added experimental Gephi graph streaming support; see igraph.remote.gephi and + igraph.drawing.graph.GephiGraphStreamingDrawer +- Nicer __repr__ output for Flow and Cut instances +- Arrows are now placed correctly around diamond-shaped nodes on plots +- Added Graph.TupleList, a function that allows one to create graphs with + edge attributes quickly from a list of tuples. +- plot() now also supports .eps as an extension, not only .ps + +- Fixed overflow issues in centralization calculations. +- Fixed a bug that mishandled vertex colors in VF2 isomorphism + functions. Bug \#1032819. +- Pajek exporter now always quotes strings, thanks to Elena Tea Russo. +- Fixed a bug with handling small edge weights in shortest paths + calculation in Graph.shortest_paths() (Dijkstra's algorithm.) Thanks to + Martin J Reed. +- Fixed a bug in the NCV setting for ARPACK functions. It cannot be + bigger than the matrix size. +- Fixed a bug in Graph.layout_mds() that resulted vertices on top of each + other. +- Motif finding functions return nan for isomorphism classes that are + not motifs (i.e. not connected). Fixes bug \#1050859. +- Fixed a bug when calculating the largest cliques of a directed + graph. Bug \#1073800. +- Warn for negative weights when calculating PageRank. +- Fixed a bug in Graph.eccentricity() and Graph.radius(), the results were often + simply wrong. +- Fixed a bug in Graph.get.all.shortest.paths() when some edges had zero weight. +- Fixed some potential crashes in the DrL layout generator. +- Fixed a bug in the Reingold-Tilford layout when the graph is + directed and mode != ALL. +- Fixed a bug in Graph.layout_sugiyama() when the graph had no edges. +- Fixed a bug in Graph.community_label_propagation() when initial labels + contained -1 entries. Issue \#1105460. +- Repaired the DescartesCoordinateSystem class (which is not used too frequently + anyway) +- Fixed a bug that caused segfaults when an igraph Graph was used in a thread + forked from the main Python interpreter thread +- Fixed a bug that affected file handles created from Python strings in the + C layer +- Fixed a bug in has_multiple() that resulted in false negatives + for some undirected graphs. +- Fixed a crash in weighted betweenness calculation. + +C library news and changes +-------------------------- + +- Added bipartite support to the Pajek reader and writer, closes bug + \#1042298. +- igraph_layout_mds() uses LAPACK instead of ARPACK. +- igraph_degree_sequence_game has a new method: + IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE. +- Added the igraph_is_degree_sequence() and + igraph_is_graphical_degree_sequence() functions. +- igraph_rewire() has a new method: IGRAPH_REWIRING_SIMPLE_LOOPS, + that can create loops. +- Walktrap community detection now handles isolates. +- Added igraph_k_regular_game(). +- Added igraph_power_law_fit. + +- Fixed a bug in igraph_barabasi_game when outseq was an empty vector. +- Fixed overflow issues in centralization calculations. +- Fixed an invalid return value of igraph_vector_ptr_pop_back. +- Fixed a igraph_all_minimal_st_separators() bug, some vertex sets + were incorrectly reported as separators. Bug \#1033045. +- Pajek exporter now always quotes strings, thanks to Elena Tea Russo. +- Fixed a bug with handling small edge weights in + igraph_shortest_paths_dijkstra(), thanks to Martin J Reed. +- Fixed a bug in the NCV setting for ARPACK functions. It cannot be + bigger than the matrix size. +- igraph_layout_merge_dla uses better default parameter values now. +- Fixed a bug in igraph_layout_mds() that resulted vertices on top of + each other. +- Attribute handler table is not thread-local any more. +- Motif finding functions return IGRAPH_NAN for isomorphism classes + that are not motifs (i.e. not connected). Fixes bug \#1050859. +- Fixed a bug when calculating the largest cliques of a directed + graph. Bug \#1073800. +- Fix a bug in degree_sequence_game(), in_seq can be an empty vector as + well instead of NULL, for an undirected graph. +- Fixed a bug in the maximal clique search, closes \#1074402. +- Warn for negative weights when calculating PageRank. +- Fixed a bug in igraph_eccentricity() (and also igraph_radius()), + the results were often simply wrong. +- Fixed a bug in igraph_get_all_shortest_paths_dijkstra() when edges + had zero weight. +- Fixed some potential crashes in the DrL layout generator. +- Fixed a bug in the Reingold-Tilford layout when the graph is + directed and mode != ALL. +- Fixed a bug in igraph_has_multiple() that resulted in false negatives + for some undirected graphs. +- Fixed a crash in weighted betweenness calculation. + +igraph 0.6 +========== + +Released June 11, 2012 + +See also the release notes at +http://igraph.sf.net/relnotes-0.6.html + +R: Major new features +--------------------- + +- Vertices and edges are numbered from 1 instead of 0. + Note that this makes most of the old R igraph code incompatible + with igraph 0.6. If you want to use your old code, please use + the igraph0 package. See more at http://igraph.sf.net/relnotes-0.6.html. +- The '\[' and '\[\[' operators can now be used on igraph graphs, + for '\[' the graph behaves as an adjacency matrix, for '[[' is + is treated as an adjacency list. It is also much simpler to + manipulate the graph structure, i.e. add/remove edges and vertices, + with some new operators. See more at ?graph.structure. +- In all functions that take a vector or list of vertices or edges, + vertex/edge names can be given instead of the numeric ids. +- New package 'igraphdata', contains a number of data sets that can + be used directly in igraph. +- Igraph now supports loading graphs from the Nexus online data + repository, see nexus.get(), nexus.info(), nexus.list() and + nexus.search(). +- All the community structure finding algorithm return a 'communities' + object now, which has a bunch of useful operations, see + ?communities for details. +- Vertex and edge attributes are handled much better now. They + are kept whenever possible, and can be combined via a flexible API. + See ?attribute.combination. +- R now prints igraph graphs to the screen in a more structured and + informative way. The output of summary() was also updated + accordingly. + +R: Other new features +--------------------- + +- It is possible to mark vertex groups on plots, via + shading. Communities and cohesive blocks are plotted using this by + default. +- Some igraph demos are now available, see a list via + 'demo(package="igraph")'. +- igraph now tries to select the optimal layout algorithm, when + plotting a graph. +- Added a simple console, using Tcl/Tk. It contains a text area + for status messages and also a status bar. See igraph.console(). +- Reimplemented igraph options support, see igraph.options() and + getIgraphOpt(). +- Igraph functions can now print status messages. + +R: New or updated functions +--------------------------- + +Community detection +------------------- +- The multi-level modularity optimization community structure detection + algorithm by Blondel et al. was added, see multilevel.community(). +- Distance between two community structures: compare.communities(). +- Community structure via exact modularity optimization, + optimal.community(). +- Hierarchical random graphs and community finding, porting the code + from Aaron Clauset. See hrg.game(), hrg.fit(), etc. +- Added the InfoMAP community finding method, thanks to Emmanuel + Navarro for the code. See infomap.community(). + +Shortest paths +-------------- +- Eccentricity (eccentricity()), and radius (radius()) calculations. +- Shortest path calculations with get.shortest.paths() can now + return the edges along the shortest paths. +- get.all.shortest.paths() now supports edge weights. + +Centrality +---------- +- Centralization scores for degree, closeness, betweenness and + eigenvector centrality. See centralization.scores(). +- Personalized Page-Rank scores, see page.rank(). +- Subgraph centrality, subgraph.centrality(). +- Authority (authority.score()) and hub (hub.score()) scores support + edge weights now. +- Support edge weights in betweenness and closeness calculations. +- bonpow(), Bonacich's power centrality and alpha.centrality(), + Alpha centrality calculations now use sparse matrices by default. +- Eigenvector centrality calculation, evcent() now works for + directed graphs. +- Betweenness calculation can now use arbitrarily large integers, + this is required for some lattice-like graphs to avoid overflow. + +Input/output and file formats +----------------------------- +- Support the DL file format in graph.read(). See + http://www.analytictech.com/networks/dataentry.htm. +- Support writing the LEDA file format in write.graph(). + +Plotting and layouts +-------------------- +- Star layout: layout.star(). +- Layout based on multidimensional scaling, layout.mds(). +- New layouts layout.grid() and layout.grid.3d(). +- Sugiyama layout algorithm for layered directed acyclic graphs, + layout.sugiyama(). + +Graph generators +---------------- +- New graph generators: static.fitness.game(), static.power.law.game(). +- barabasi.game() was rewritten and it supports three algorithms now, + the default algorithm does not generate multiple or loop edges. + The graph generation process can now start from a supplied graph. +- The Watts-Strogatz graph generator, igraph_watts_strogatz() can + now create graphs without loop edges. + +Others +------ +- Added the Spectral Coarse Graining algorithm, see scg(). +- The cohesive.blocks() function was rewritten in C, it is much faster + now. It has a nicer API, too. See demo("cohesive"). +- Added generic breadth-first and depth-first search implementations + with many callbacks, graph.bfs() and graph_dfs(). +- Support vertex and edge coloring in the VF2 (sub)graph isomorphism + functions (graph.isomorphic.vf2(), graph.count.isomorphisms.vf2(), + graph.get.isomorphisms.vf2(), graph.subisomorphic.vf2(), + graph.count.subisomorphisms.vf2(), graph.get.subisomorphisms.vf2()). +- Assortativity coefficient, assortativity(), assortativity.nominal() + and assortativity.degree(). +- Vertex operators that work by vertex names: + graph.intersection.by.name(), graph.union.by.name(), + graph.difference.by.name(). Thanks to Magnus Torfason for + contributing his code! +- Function to calculate a non-induced subraph: subgraph.edges(). +- More comprehensive maximum flow and minimum cut calculation, + see functions graph.maxflow(), graph.mincut(), stCuts(), stMincuts(). +- Check whether a directed graph is a DAG, is.dag(). +- has.multiple() to decide whether a graph has multiple edges. +- Added a function to calculate a diversity score for the vertices, + graph.diversity(). +- Graph Laplacian calculation (graph.laplacian()) supports edge + weights now. +- Biconnected component calculation, biconnected.components() + now returns the components themselves. +- bipartite.projection() calculates multiplicity of edges. +- Maximum cardinality search: maximum.cardinality.search() and + chordality test: is.chordal() +- Convex hull computation, convex.hull(). +- Contract vertices, contract.vertices(). + +New in the Python interface +--------------------------- + +TODO + +Major changes in the Python interface +------------------------------------- + +TODO + +New in the C layer +------------------ + +- Maximum cardinality search: igraph_maximum_cardinality_search() and + chordality test: igraph_is_chordal(). +- Support the DL file format, igraph_read_graph_dl(). See + http://www.analytictech.com/networks/dataentry.htm. +- Added generic breadth-first and depth-first search implementations + with many callbacks (igraph_bfs(), igraph_dfs()). +- Centralization scores for degree, closeness, betweenness and + eigenvector centrality, see igraph_centralization(). +- Added igraph_sparsemat_t, a type that implements sparse + matrices based on the CXSparse library by Tim Davis. + See http://www.cise.ufl.edu/research/sparse/CXSparse/. +- Personalized Page-Rank scores, igraph_personalized_pagerank() and + igraph_personalized_pagerank_vs(). +- Assortativity coefficient, igraph_assortativity(), + igraph_assortativity_nominal(), and igraph_assortativity_degree(). +- The multi-level modularity optimization community structure detection + algorithm by Blondel et al. was added, see igraph_community_multilevel(). +- Added the igraph_version() function. +- Star layout: igraph_layout_star(). +- Function to calculate a non-induced subraph: igraph_subgraph_edges(). +- Distance between two community structures: igraph_compare_communities(). +- Community structure via exact modularity optimization, + igraph_community_optimal_community(). +- More comprehensive maximum flow and minimum cut calculation, + see functions igraph_maxflow(), igraph_mincut(), + igraph_all_st_cuts(), igraph_all_st_mincuts(). +- Layout based on multidimensional scaling, igraph_layout_mds(). +- It is now possible to access the random number generator(s) via an + API. Multiple RNGs can be used, from external sources as well. + The default RNG is MT19937. +- Added igraph_get_all_shortest_paths_dijkstra, for calculating all + non-negatively weighted shortest paths. +- Check whether a directed graph is a DAG, igraph_is_dag(). +- Cohesive blocking, a'la Moody & White, igraph_cohesive_blocks(). +- Igraph functions can now print status messages, see igraph_status() + and related functions. +- Support writing the LEDA file format, igraph_write_graph_leda(). +- Contract vertices, igraph_contract_vertices(). +- The C reference manual has now a lot of example programs. +- Hierarchical random graphs and community finding, porting the code + from Aaron Clauset. See igraph_hrg_game(), igraph_hrg_fit(), etc. +- igraph_has_multiple() to decide whether a graph has multiple edges. +- New layouts igraph_layout_grid() and igraph_layout_grid_3d(). +- igraph_integer_t is really an integer now, it used to be a double. +- igraph_minimum_spanning_tree(), calls either the weighted or + the unweighted implementation. +- Eccentricity (igraph_eccentricity()), and radius (igraph_radius()) + calculations. +- Several game theory update rules, written by Minh Van Nguyen. See + igraph_deterministic_optimal_imitation(), + igraph_stochastic_imitation(), igraph_roulette_wheel_imitation(), + igraph_moran_process(), +- Sugiyama layout algorithm for layered directed acyclic graphs, + igraph_layout_sugiyama(). +- New graph generators: igraph_static_fitness_game(), + igraph_static_power_law_game(). +- Added the InfoMAP community finding method, thanks to Emmanuel + Navarro for the code. See igraph_community_infomap(). +- Added the Spectral Coarse Graining algorithm, see igraph_scg(). +- Added a function to calculate a diversity score for the vertices, + igraph_diversity(). + +Major changes in the C layer +---------------------------- + +- Authority (igraph_authority_score()) and hub (igraph_hub_score()) scores + support edge weights now. +- Graph Laplacian calculation (igraph_laplacian()) supports edge + weights now. +- Support edge weights in betweenness (igraph_betweenness()) and closeness + (igraph_closeness()) calculations. +- Support vertex and edge coloring in the VF2 graph isomorphism + algorithm (igraph_isomorphic_vf2(), igraph_count_isomorphisms_vf2(), + igraph_get_isomorphisms_vf2(), igraph_subisomorphic_vf2(), + igraph_count_subisomorphisms_vf2(), igraph_get_subisomorphisms_vf2()). +- Added print operations for the igraph_vector*_t, igraph_matrix*_t and + igraph_strvector_t types. +- Biconnected component calculation (igraph_biconnected_components()) + can now return the components themselves. +- Eigenvector centrality calculation, igraph_eigenvector_centrality() + now works for directed graphs. +- Shortest path calculations with get_shortest_paths() and + get_shortest_paths_dijkstra() can now return the edges along the paths. +- Betweenness calculation can now use arbitrarily large integers, + this is required for some lattice-like graphs to avoid overflow. +- igraph_bipartite_projection() calculates multiplicity of edges. +- igraph_barabasi_game() was rewritten and it supports three + algorithms now, the default algorithm does not generate multiple or + loop edges. +- The Watts-Strogatz graph generator, igraph_watts_strogatz() can + now create graphs without loop edges. +- igraph should be now thread-safe, on architectures that support + thread-local storage (Linux and Windows: yes, Mac OSX: no). + +We also fixed numerous bugs, too many to include them here, sorry. +You may look at our bug tracker at https://bugs.launchpad.net/igraph +to check whether a bug was fixed or not. Thanks for all the people +reporting bugs. Special thanks to Minh Van Nguyen for a lot of bug +reports, documentation fixes and contributed code! + +igraph 0.5.3 +============ + +Released November 22, 2009 + +Bugs corrected in the R interface +--------------------------------- +- Some small changes to make 'R CMD check' clean +- Fixed a bug in graph.incidence, the 'directed' and 'mode' arguments + were not handled correctly +- Betweenness and edge betweenness functions work for graphs with + many shortest paths now (up to the limit of long long int) +- When compiling the package, the configure script fails if there is + no C compiler available +- igraph.from.graphNEL creates the right number of loop edges now +- Fixed a bug in bipartite.projection() that caused occasional crashes + on some systems + +New in the Python interface +--------------------------- +- Added support for weighted diameter +- get_eid() considers edge directions by default from now on +- Fixed a memory leak in the attribute handler +- 'NaN' and 'inf' are treated correctly now + +Bugs corrected in the C layer +----------------------------- +- Betweenness and edge betweenness functions work for graphs with + many shortest paths now (up to the limit of long long int) +- The configure script fails if there is no C compiler available +- Fixed a bug in igraph_community_spinglass, when csize was a NULL + pointer, but membership was not +- Fixed a bug in igraph_bipartite_projection that caused occasional + crashes on some systems + +igraph 0.5.2 +============ + +Released April 10, 2009 + +See also the release notes at +http://igraph.sf.net/relnotes-0.5.2.html + +New in the R interface +---------------------- + +- Added progress bar support to beweenness() and + betweenness.estimate(), layout.drl() +- Speeded up betweenness estimation +- Speeded up are.connected() +- Johnson's shortest paths algorithm added +- shortest.paths() has now an 'algorithm' argument to choose from the + various implementations manually +- Always quote symbolic vertex names when printing graphs or edges +- Average nearest neighbor degree calculation, graph.knn() +- Weighted degree (also called strength) calculation, graph.strength() +- Some new functions to support bipartite graphs: graph.bipartite(), + is.bipartite(), get.indicence(), graph.incidence(), + bipartite.projection(), bipartite.projection.size() +- Support for plotting curved edges with plot.igraph() and tkplot() +- Added support for weighted graphs in alpha.centrality() +- Added the label propagation community detection algorithm by + Raghavan et al., label.propagation.community() +- cohesive.blocks() now has a 'cutsetHeuristic' argument to choose + between two cutset algorithms +- Added a function to "unfold" a tree, unfold.tree() +- New tkplot() arguments to change the drawing area +- Added a minimal GUI, invoke it with tkigraph() +- The DrL layout generator, layout.drl() has a three dimensional mode + now. + +Bugs corrected in the R interface +--------------------------------- + +- Fixed a bug in VF2 graph isomorphism functions +- Fixed a bug when a sparse adjacency matrix was requested in + get.adjacency() and the graph was named +- VL graph generator in degree.sequence.game() checks now that + the sum of the degrees is even +- Many fixes for supporting various compilers, e.g. GCC 4.4 and Sun's + C compiler +- Fixed memory leaks in graph.automorphisms(), Bellman-Ford + shortest.paths(), independent.vertex.sets() +- Fix a bug when a graph was imported from LGL and exported to NCOL + format (\#289596) +- cohesive.blocks() creates its temporary file in the session + temporary directory +- write.graph() and read.graph() now give error messages when unknown + arguments are given +- The GraphML reader checks the name of the attributes to avoid adding + a duplicate 'id' attribute +- It is possible to change the 'ncv' ARPACK parameter for + leading.eigenvector.community() +- Fixed a bug in path.length.hist(), 'unconnected' was wrong + for unconnected and undirected graphs +- Better handling of attribute assingment via iterators, this is now + also clarified in the manual +- Better error messages for unknown vertex shapes +- Make R package unload cleanly if unloadNamespace() is used +- Fixed a bug in plotting square shaped vertices (\#325244) +- Fixed a bug in graph.adjacency() when the matrix is a sparse matrix + of class "dgTMatrix" + +New in the Python interface +--------------------------- + +- Speeded up betweenness estimation +- Johnson's shortest paths algorithm added (selected automatically + by Graph.shortest_paths() if needed) +- Weighted degree (also called strength) calculation, Graph.strength() +- Some new methods to support bipartite graphs: Graph.Bipartite(), + Graph.is_bipartite(), Graph.get_indicence(), Graph.Incidence(), + Graph.bipartite_projection(), Graph.bipartite_projection_size() +- Added the label propagation community detection algorithm by + Raghavan et al., Graph.community_label_propagation() +- Added a function to "unfold" a tree, Graph.unfold_tree() +- setup.py script improvements +- Graph plotting now supports edge_arrow_size and edge_arrow_width +- Added Graph.Formula to create small graphs from a simple notation +- VertexSeq and EdgeSeq objects can now be indexed by slices + +New in the C layer +------------------ + +- Added progress bar support to igraph_betweenness() and + igraph_betweenness_estimate(), igraph_layout_drl() +- Speeded up igraph_betweenness_estimate(), igraph_get_eid(), + igraph_are_connected(), igraph_get_eids() +- Added igraph_get_eid2() +- Johnson's shortest path algorithm added: + igraph_shortest_paths_johnson() +- Average nearest neighbor degree calculation, + igraph_avg_nearest_neighbor_degree() +- Weighted degree (also called strength) calculation, + igraph_strength() +- Some functions to support bipartite graphs: igraph_full_bipartite(), + igraph_bipartite_projection(), igraph_create_bipartite(), + igraph_incidence(), igraph_get_incidence(), + igraph_bipartite_projection_size(), igraph_is_bipartite() +- Added the label propagation community detection algorithm by + Raghavan et al., igraph_community_label_propagation() +- Added an example that shows how to set the random number generator's + seed from C (examples/simple/random_seed.c) +- Added a function to "unfold" a tree, igraph_unfold_tree() +- C attribute handler updates: added functions to query many + vertices/edges at once +- Three dimensional DrL layout, igraph_layout_drl_3d() + +Bugs corrected in the C layer +----------------------------- + +- Fixed a bug in igraph_isomorphic_function_vf2(), affecting all VF2 + graph isomorphism functions +- VL graph generator in igraph_degree_sequence_game() checks now that + the sum of the degrees is even +- Many small corrections to make igraph compile with Microsoft Visual + Studio 2003, 2005 and 2008 +- Many fixes for supporting various compilers, e.g. GCC 4.4 and Sun's + C compiler +- Fix a bug when a graph was imported from LGL and exported to NCOL + format (\#289596) +- Fixed memory leaks in igraph_automorphisms(), + igraph_shortest_paths_bellman_ford(), + igraph_independent_vertex_sets() +- The GraphML reader checks the name of the attributes to avoid adding + a duplicate 'id' attribute +- It is possible to change the 'ncv' ARPACK parameter for + igraph_community_leading_eigenvector() +- Fixed a bug in igraph_path_length_hist(), 'unconnected' was wrong + for unconnected and undirected graphs. + +igraph 0.5.1 +============ + +Released July 14, 2008 + +See also the release notes at +http://igraph.sf.net/relnotes-0.5.1.html + +New in the R interface +---------------------- + +- A new layout generator called DrL. +- Uniform sampling of random connected undirected graphs with a + given degree sequence. +- Edge labels are plotted at 1/3 of the edge, this is better if + the graph has mutual edges. +- Initial and experimental vertex shape support in 'plot'. +- New function, 'graph.adjlist' creates igraph graphs from + adjacency lists. +- Conversion to/from graphNEL graphs, from the 'graph' R package. +- Fastgreedy community detection can utilize edge weights now, this + was missing from the R interface. +- The 'arrow.width' graphical parameter was added. +- graph.data.frame has a new argument 'vertices'. +- graph.adjacency and get.adjacency support sparse matrices, + the 'Matrix' package is required to use this functionality. +- graph.adjacency adds column/row names as 'name' attribute. +- Weighted shortest paths using Dijkstra's or the Belmann-Ford + algorithm. +- Shortest path functions return 'Inf' for unreachable vertices. +- New function 'is.mutual' to find mutual edges in a directed graph. +- Added inverse log-weighted similarity measure (a.k.a. Adamic/Adar + similarity). +- preference.game and asymmetric.preference.game were + rewritten, they are O(|V|+|E|) now, instead of O(|V|^2). +- Edge weight support in function 'get.shortest.paths', it uses + Dijkstra's algorithm. + +Bugs corrected in the R interface +--------------------------------- + +- A bug was corrected in write.pajek.bgraph. +- Several bugs were corrected in graph.adjacency. +- Pajek reader bug corrected, used to segfault if '\*Vertices' + was missing. +- Directedness is handled correctly when writing GML files. + (But note that 'correct' conflicts the standard here.) +- Corrected a bug when calculating weighted, directed PageRank on an + undirected graph. (Which does not make sense anyway.) +- Several bugs were fixed in the Reingold-Tilford layout to avoid + edge crossings. +- A bug was fixed in the GraphML reader, when the value of a graph + attribute was not specified. +- Fixed a bug in the graph isomorphism routine for small (3-4 vertices) + graphs. +- Corrected the random sampling implementation (igraph_random_sample), + now it always generates unique numbers. This affects the + Gnm Erdos-Renyi generator, it always generates simple graphs now. +- The basic igraph constructor (igraph_empty_attrs, all functions + are expected to call this internally) now checks whether the number + of vertices is finite. +- The LGL, NCOL and Pajek graph readers handle errors properly now. +- The non-symmetric ARPACK solver returns results in a consistent form + now. +- The fast greedy community detection routine now checks that the graph + is simple. +- The LGL and NCOL parsers were corrected to work with all + kinds of end-of-line encodings. +- Hub & authority score calculations initialize ARPACK parameters now. +- Fixed a bug in the Walktrap community detection routine, when applied + to unconnected graphs. +- Several small memory leaks were removed, and a big one from the Spinglass + community structure detection function + +New in the Python interface +--------------------------- + +- A new layout generator called DrL. +- Uniform sampling of random connected undirected graphs with a + given degree sequence. +- Methods parameters accepting igraph.IN, igraph.OUT and igraph.ALL + constants now also accept these as strings ("in", "out" and "all"). + Prefix matches also allowed as long as the prefix match is unique. +- Graph.shortest_paths() now supports edge weights (Dijkstra's and + Bellman-Ford algorithm implemented) +- Graph.get_shortest_paths() also supports edge weights + (only Dijkstra's algorithm yet) +- Added Graph.is_mutual() to find mutual edges in a directed graph. +- Added inverse log-weighted similarity measure (a.k.a. Adamic/Adar + similarity). +- preference.game and asymmetric.preference.game were + rewritten, they are O(|V|+|E|) now, instead of O(|V|^2). +- ARPACK options can now be modified from the Python interface + (thanks to Kurt Jacobson) +- Layout.to_radial() added -- now you can create a top-down tree + layout by the Reingold-Tilford algorithm and then turn it to a + radial tree layout +- Added Graph.write_pajek() to save graphs in Pajek format +- Some vertex and edge related methods can now also be accessed via + the methods of VertexSeq and EdgeSeq, restricted to the current + vertex/edge sequence of course +- Visualisations now support triangle shaped vertices +- Added Graph.mincut() +- Added Graph.Weighted_Adjacency() to create graphs from weighted + adjacency matrices +- Kamada-Kawai and Fruchterman-Reingold layouts now accept initial + vertex positions +- Graph.Preference() and Graph.Asymmetric_Preference() were + rewritten, they are O(|V|+|E|) now, instead of O(|V|^2). + +Bugs corrected in the Python interface +-------------------------------------- + +- Graph.constraint() now properly returns floats instead of integers + (thanks to Eytan Bakshy) +- Graphs given by adjacency matrices are now finally loaded and saved + properly +- Graph.Preference() now accepts floats in type distributions +- A small bug in Graph.community_edge_betweenness() corrected +- Some bugs in numeric attribute handling resolved +- VertexSeq and EdgeSeq objects can now be subsetted by lists and + tuples as well +- Fixed a bug when dealing with extremely small layout sizes +- Eigenvector centality now always return positive values +- Graph.authority_score() now really returns the authority scores + instead of the hub scores (blame copypasting) +- Pajek reader bug corrected, used to segfault if '\*Vertices' + was missing. +- Directedness is handled correctly when writing GML files. + (But note that 'correct' conflicts the standard here.) +- Corrected a bug when calculating weighted, directed PageRank on an + undirected graph. (Which does not make sense anyway.) +- Several bugs were fixed in the Reingold-Tilford layout to avoid + edge crossings. +- A bug was fixed in the GraphML reader, when the value of a graph + attribute was not specified. +- Fixed a bug in the graph isomorphism routine for small (3-4 vertices) + graphs. +- Corrected the random sampling implementation (igraph_random_sample), + now it always generates unique numbers. This affects the + Gnm Erdos-Renyi generator, it always generates simple graphs now. +- The LGL, NCOL and Pajek graph readers handle errors properly now. +- The non-symmetric ARPACK solver returns results in a consistent form + now. +- The fast greedy community detection routine now checks that the graph + is simple. +- The LGL and NCOL parsers were corrected to work with all + kinds of end-of-line encodings. +- Hub & authority score calculations initialize ARPACK parameters now. +- Fixed a bug in the Walktrap community detection routine, when applied + to unconnected graphs. +- Several small memory leaks were removed, and a big one from the Spinglass + community structure detection function + +New in the C layer +------------------ + +- A new layout generator called DrL. +- Uniform sampling of random connected undirected graphs with a + given degree sequence. +- Some stochastic test results are ignored (for spinglass community + detection, some Erdos-Renyi generator tests) +- Weighted shortest paths, Dijkstra's algorithm. +- The unweighted shortest path routine returns 'Inf' for unreachable + vertices. +- New function, igraph_adjlist can create igraph graphs from + adjacency lists. +- New function, igraph_weighted_adjacency can create weighted graphs + from weight matrices. +- New function, igraph_is_mutual to search for mutual edges. +- Added inverse log-weighted similarity measure (a.k.a. Adamic/Adar + similarity). +- igraph_preference_game and igraph_asymmetric_preference_game were + rewritten, they are O(|V|+|E|) now, instead of O(|V|^2). +- The Bellman-Ford shortest path algorithm was added. +- Added weighted variant of igraph_get_shortest_paths, based on + Dijkstra's algorithm. +- Several small memory leaks were removed, and a big one from the Spinglass + community structure detection function + +Bugs corrected in the C layer +----------------------------- + +- Several bugs were corrected in the (still experimental) C attribute + handler. +- Pajek reader bug corrected, used to segfault if '\*Vertices' + was missing. +- Directedness is handled correctly when writing GML files. + (But note that 'correct' conflicts the standard here.) +- Corrected a bug when calculating weighted, directed PageRank on an + undirected graph. (Which does not make sense anyway.) +- Some code polish to make igraph compile with GCC 4.3 +- Several bugs were fixed in the Reingold-Tilford layout to avoid + edge crossings. +- A bug was fixed in the GraphML reader, when the value of a graph + attribute was not specified. +- Fixed a bug in the graph isomorphism routine for small (3-4 vertices) + graphs. +- Corrected the random sampling implementation (igraph_random_sample), + now it always generates unique numbers. This affects the + Gnm Erdos-Renyi generator, it always generates simple graphs now. +- The basic igraph constructor (igraph_empty_attrs, all functions + are expected to call this internally) now checks whether the number + of vertices is finite. +- The LGL, NCOL and Pajek graph readers handle errors properly now. +- The non-symmetric ARPACK solver returns results in a consistent form + now. +- The fast greedy community detection routine now checks that the graph + is simple. +- The LGL and NCOL parsers were corrected to work with all + kinds of end-of-line encodings. +- Hub & authority score calculations initialize ARPACK parameters now.x +- Fixed a bug in the Walktrap community detection routine, when applied + to unconnected graphs. + +igraph 0.5 +========= + +Released February 14, 2008 + +See also the release notes at http://igraph.sf.net/relnotes-0.5.html + +New in the R interface +---------------------- + +- The 'rescale', 'asp' and 'frame' graphical parameters were added +- Create graphs from a formula notation (graph.formula) +- Handle graph attributes properly +- Calculate the actual minimum cut for undirected graphs +- Adjacency lists, get.adjlist and get.adjedgelist added +- Eigenvector centrality computation is much faster now +- Proper R warnings, instead of writing the warning to the terminal +- R checks graphical parameters now, the unknown ones are not just + ignored, but an error message is given +- plot.igraph has an 'add' argument now to compose plots with multiple + graphs +- plot.igraph supports the 'main' and 'sub' arguments +- layout.norm is public now, it can normalize a layout +- It is possible to supply startup positions to layout generators +- Always free memory when CTRL+C/ESC is pressed, in all operating + systems +- plot.igraph can plot square vertices now, see the 'shape' parameter +- graph.adjacency rewritten when creating weighted graphs +- We use match.arg whenever possible. This means that character scalar + options can be abbreviated and they are always case insensitive + +- VF2 graph isomorphism routines can check subgraph isomorphism now, + and they are able to return matching(s) +- The BLISS graph isomorphism algorithm is included in igraph now. See + canonical.permutation, graph.isomorphic.bliss +- We use ARPACK for eigenvalue/eigenvector calculation. This means that the + following functions were rewritten: page.rank, + leading.eigenvector.community.\*, evcent. New functions based on + ARPACK: hub.score, authority.score, arpack. +- Edge weights for Fruchterman-Reingold layout (layout.fruchterman.reingold). +- Line graph calculation (line.graph) +- Kautz and de Bruijn graph generators (graph.kautz, graph.de.bruijn) +- Support for writing graphs in DOT format +- Jaccard and Dice similarity coefficients added (similarity.jaccard, + similarity.dice) +- Counting the multiplicity of edges (count.multiple) +- The graphopt layout algorithm was added, layout.graphopt +- Generation of "famous" graphs (graph.famous). +- Create graphs from LCF notation (graph.cf). +- Dyad census and triad cencus functions (dyad.census, triad.census) +- Cheking for simple graphs (is.simple) +- Create full citation networks (graph.full.citation) +- Create a histogram of path lengths (path.length.hist) +- Forest fire model added (forest.fire.game) +- DIMACS reader can handle different file types now +- Biconnected components and articulation points (biconnected.components, + articulation.points) +- Kleinberg's hub and authority scores (hub.score, authority.score) +- as.undirected handles attributes now +- Geometric random graph generator (grg.game) can return the + coordinates of the vertices +- Function added to convert leading eigenvector community structure result to + a membership vector (community.le.to.membership) +- Weighted fast greedy community detection +- Weighted page rank calculation +- Functions for estimating closeness, betweenness, edge betweenness by + introducing a cutoff for path lengths (closeness.estimate, + betweenness.estimate, edge.betweenness.estimate) +- Weighted modularity calculation +- Function for permuting vertices (permute.vertices) +- Betweenness and closeness calculations are speeded up +- read.graph can handle all possible line terminators now (\r, \n, \r\n, \n\r) +- Error handling was rewritten for walktrap community detection, + the calculation can be interrupted now +- The maxflow/mincut functions allow to supply NULL pointer for edge + capacities, implying unit capacities for all edges + +Bugs corrected in the R interface +--------------------------------- + +- Fixed a bug in cohesive.blocks, cohesive blocks were sometimes not + calculated correctly + +New in the Python interface +--------------------------- + +- Added shell interface: igraph can now be invoked by calling the script called + igraph from the command line. The script launches the Python interpreter and + automatically imports igraph functions into the main namespace +- Pickling (serialization) support for Graph objects +- Plotting functionality based on the Cairo graphics library (so you need to + install python-cairo if you want to use it). Currently the following + objects can be plotted: graphs, adjacency matrices and dendrograms. Some + crude support for plotting histograms is also implemented. Plots can be + saved in PNG, SVG and PDF formats. +- Unified Graph.layout method for accessing layout algorithms +- Added interfaces to walktrap community detection and the BLISS isomorphism + algorithm +- Added dyad and triad census functionality and motif counting +- VertexSeq and EdgeSeq objects can now be restricted to subsets of the + whole network (e.g., you can select vertices/edges based on attributes, + degree, centrality and so on) + +New in the C library +-------------------- + +- Many types (stack, matrix, dqueue, etc.) are templates now + They were also rewritten to provide a better organized interface +- VF2 graph isomorphism routines can check subgraph isomorphism now, + and they are able to return matching(s) +- The BLISS graph isomorphism algorithm is included in igraph now. See + igraph_canonical_permutation, igraph_isomorphic_bliss +- We use ARPACK for eigenvalue/eigenvector calculation. This means that the + following functions were rewritten: igraph_pagerank, + igraph_community_leading_eigenvector_\*. New functions based on + ARPACK: igraph_eigenvector_centrality, igraph_hub_score, + igraph_authority_score, igraph_arpack_rssolve, igraph_arpack_rnsolve +- Experimental C attribute interface added. I.e. it is possible to use + graph/vertex/edge attributes from C code now. + +- Edge weights for Fruchterman-Reingold layout. +- Line graph calculation. +- Kautz and de Bruijn graph generators +- Support for writing graphs in DOT format +- Jaccard and Dice similarity coefficients added +- igraph_count_multiple added +- igraph_is_loop and igraph_is_multiple "return" boolean vectors +- The graphopt layout algorithm was added, igraph_layout_graphopt +- Generation of "famous" graphs, igraph_famous +- Create graphs from LCF notation, igraph_lcf, igraph_lcf_vector +- igraph_add_edge adds a single edge to the graph +- Dyad census and triad cencus functions added +- igraph_is_simple added +- progress handlers are allowed to stop calculation +- igraph_full_citation to create full citation networks +- igraph_path_length_hist, create a histogram of path lengths +- forest fire model added +- DIMACS reader can handle different file types now +- Adjacency list types made public now (igraph_adjlist_t, igraph_adjedgelist_t) +- Biconnected components and articulation points can be computed +- Eigenvector centrality computation +- Kleinberg's hub and authority scores +- igraph_to_undirected handles attributes now +- Geometric random graph generator can return the coordinates of the vertices +- Function added to convert leading eigenvector community structure result to + a membership vector (igraph_le_community_to_membership) +- Weighted fast greedy community detection +- Weighted page rank calculation +- Functions for estimating closeness, betweenness, edge betweenness by + introducing a cutoff for path lengths +- Weighted modularity calculation +- igraph_permute_vertices added +- Betweenness ans closeness calculations are speeded up +- Startup positions can be supplied to the Kamada-Kawai layout + algorithms +- igraph_read_graph_\* functions can handle all possible line + terminators now (\r, \n, \r\n, \n\r) +- Error handling was rewritten for walktrap community detection, + the calculation can be interrupted now +- The maxflow/mincut functions allow to supply a null pointer for edge + capacities, implying unit capacities for all edges + +Bugs corrected in the C library +------------------------------- + +- Memory leak fixed in adjacency list handling +- Memory leak fixed in maximal independent vertex set calculation +- Fixed a bug when rewiring undirected graphs with igraph_rewire +- Fixed edge betweenness community structure detection for unconnected graphs +- Make igraph compile with Sun Studio +- Betweenness bug fixed, when not computing for all vertices +- memory usage of clique finding reduced +- Corrected bugs for motif counts when not all motifs were counted, + but a 'cut' vector was used +- Bugs fixed in trait games and cited type game +- Accept underscore as letter in GML files +- GML file directedness notation reversed, more logical this way + +igraph 0.4.5 +========= + +Released January 1, 2008 + +New: +- Cohesive block finding in the R interface, thanks to Peter McMahan + for contributing his code. See James Moody and Douglas R. White, + 2003, in Structural Cohesion and Embeddedness: A Hierarchical + Conception of Social Groups American Sociological Review 68(1):1-25 +- Biconnected components and articulation points. +- R interface: better printing of attributes. +- R interface: graph attributes can be used via '$'. + +New in the C library: +- igraph_vector_bool_t data type. + +Bug fixed: +- Erdos-Renyi random graph generators rewritten. + +igraph 0.4.4 +========= + +Released October 3, 2007 + +This release should work seemlessly with the new R 2.6.0 version. +Some other bugs were also fixed: +- A bug was fixed in the Erdos-Renyi graph generator, which sometimes + added an extra vertex. +- MSVC compilation issues were fixed. +- MinGW compilation fixes. + +igraph 0.4.3 +========= + +Released August 13, 2007 + +The next one in the sequence of bugfix releases. Thanks to many people +sending bug reports. Here are the changes: +- Some memory leaks removed when using attributes from R or Python. +- GraphML parser: entities and character data in multiple chunks are now handled correctly. +- A bug corrected in edge betweenness community structure detection, + it failed if called many times from the same program/session. +- Bug corrected in 'adjacent edges' edge iterator. +- Python interface: edge and vertex attribute deletion bug corrected. +- Edge betweeness community structure: handle unconnected graphs properly. +- Fixed bug related to fast greedy community detection in unconnected graphs. +- Use a different kind of parser (Push) for reading GraphML files. This is almost + invisible for users but fixed a nondeterministic bug when reading in GraphML + files. +- R interface: plot now handles properly if called with a vector as the edge.width + argument for directed graphs. +- R interface: bug (typo) corrected for walktrap.community and weighted graphs. +- Test suite should run correctly on Cygwin now. + +igraph 0.4.2 +========= + +Released June 7, 2007 + +This is another bugfix release, as there was a serious bug in the +R package of the previous version: it could not read and write graphs +to files in any format under MS Windows. + +Some other bits added: +- circular Reingold-Tilford layout generator for trees +- corrected a bug, Pajek files are written properly under MS Windows now. +- arrow.size graphical edge parameter added in the R interface. + +igraph 0.4.1 +========= + +Released May 23, 2007 + +This is a minor release, it corrects a number of bugs, mostly in the +R package. + +igraph 0.4 +========= + +Released May 21, 2007 + +The major new additions in this release is a bunch of community +detection algorithms and support for the GML file format. Here +is the complete list of changes: + + +New in the C library +-------------------- + +- internal representation changed +- neighbors always returns an ordered list +- igraph_is_loop and igraph_is_multiple added + +- topological sorting +- VF2 isomorphism algorithm +- support for reading the file format of the Graph Database for isomorphism +- igraph_mincut cat calculate the actual minimum cut +- girth calculation added, thanks to Keith Briggs +- support for reading and writing GML files + +- Walktrap community detection algorithm added, thanks to Matthieu Latapy + and Pascal Pons +- edge betweenness based community detection algorithm added +- fast greedy algorithm for community detection by Clauset et al. added + thanks to Aaron Clauset for sharing his code +- leading eigenvector community detection algorithm by Mark Newman added +- igraph_community_to_membership supporting function added, creates + a membership vector from a community structure merge tree +- modularity calculation added + +New in the R interface +---------------------- + +- as the internal representation changed, graphs stored with 'save' + with an older igraph version cannot be read back with the new + version reliably. +- neighbors returns ordered lists + +- topological sorting +- VF2 isomorphism algorithm +- support for reading graphs from the Graph Database for isomorphism +- girth calculation added, thanks to Keith Briggs +- support for reading and writing GML files + +- Walktrap community detection algorithm added, thanks to Matthieu Latapy + and Pascal Pons +- edge betweenness based community detection algorithm added +- fast greedy algorithm for community detection by Clauset et al. added + thanks to Aaron Clauset for sharing his code +- leading eigenvector community detection algorithm by Mark Newman added +- functions for creating denrdograms from the output of the + community detection algorithms added +- community.membership supporting function added, creates + a membership vector from a community structure merge tree +- modularity calculation added + +- graphics parameter handling is completely rewritten, uniform handling + of colors and fonts, make sure you read ?igraph.plotting +- new plotting parameter for edges: arrow.mode +- a bug corrected when playing a nonlinear barabasi.game +- better looking plotting in 3d using rglplot: edges are 3d too +- rglplot layout is allowed to be two dimensional now +- rglplot suspends updates while drawing, this makes it faster +- loop edges are correctly plotted by all three plotting functions + +- better printing of attributes when printing graphs +- summary of a graph prints attribute names +- is.igraph rewritten to make it possible to inherit from the 'igraph' class +- somewhat better looking progress meter for functions which support it + +Others +------ + +- proper support for Debian packages (re)added +- many functions benefit from the new internal representation and are + faster now: transitivity, reciprocity, graph operator functions like + intersection and union, etc. +- igraph compiles with Microsoft Visual C++ now +- there were some internal changes to make igraph a real graph algorithm + platform in the near future, but these are undocumented now + +Bugs corrected +-------------- + +- corrected a bug when reading Pajek files: directed graphs were read as undirected + +Debian package repository available +================================== + +Debian Linux users can now install and update the C interface +using the standard package manager. Just add the following two +lines to /etc/apt/sources.list and install the libigraph and +libigraph-dev packages. Packages for the Python interface are +coming soon. + +deb http://cneurocvs.rmki.kfki.hu /packages/binary/ + +deb-src http://cneurocvs.rmki.kfki.hu /packages/source/ + +igraph 0.3.3 +============ + +Released February 28, 2007 + +New in the C library +-------------------- + +* igraph_connect_neighborhood, nomen est omen +* igraph_watts_strogatz_game and igraph_rewire_edges +* K-core decomposition: igraph_coreness +* Clique and independent vertex set related functions: + igraph_cliques, igraph_independent_vertex_sets, + igraph_maximal_cliques, igraph_maximal_independent_vertex_sets, + igraph_independence_number, igraph_clique_number, + Some of these function were ported from the very_nauty library + of Keith Briggs, thanks Keith! +* The GraphML file format now supports graph attributes +* Transitivity calculation speeded up +* Correct transitivity calculation for multigraphs (ie. non-simple graphs) + +New in the R interface +---------------------- + +* connect.neighborhood +* watts.strogatz.game and rewire.edges +* K-core decomposition: graph.coreness +* added the 'innei' and 'outnei' shorthands for vertex sequence indexing + see help(iterators) +* Clique and independent vertex set related functions: + cliques, largest.cliques, maximal.cliques, clique.number, + independent.vertex.sets, largest.independent.vertex.sets, + maximal.independent.vertex.sets, independence.number +* The GraphML file format now supports graph attributes +* edge.lty argument added to plot.igraph and tkplot +* Transitivity calculation speeded up +* Correct transitivity calculation for multigraphs (ie. non-simple graphs) +* alpha.centrality added, calculates Bonacich alpha centrality, see docs. + +Bugs corrected +-------------- + +* 'make install' installs the library correctly on Cygwin now +* Pajek parser corrected to read files with MacOS newline characters correctly +* overflow bug in transitivity calculation for large graphs corrected +* an internal memcpy/memmove bug causing some segfaults removed +* R interface: tkplot bug with graphs containing a 'name' attribute +* R interface: attribute handling bug when adding vertices +* R interface: color selection bug corrected +* R interface: plot.igraph when plotting loops + +Python interface documentation +==================== + +Jan 8, 2007 + +The documentation of the Python interface is available. +See section 'documentation' in the menu on the left. + +igraph 0.3.2 +========= + +Released Dec 19, 2006 + +This is a new major release, it contains many new things: + +Changes in the C library +------------------------ + +- igraph_maxdegree added, calculates the maximum degree in the graph +- igraph_grg_game, geometric random graphs +- igraph_density, graph density calculation +- push-relabel maximum flow algorithm added, igraph_maxflow_value +- minimum cut functions added based on maximum flow: + igraph_st_mincut_value, igraph_mincut_value, the Stoer-Wagner + algorithm is implemented for undirected graphs +- vertex connectivity functions, usually based on maximum flow: + igraph_st_vertex_connectivity, igraph_vertex_connectivity +- edge connectivity functions, usually based on maximum flow: + igraph_st_edge_connectivity, igraph_edge_connectivity +- other functions based on maximum flow: igraph_edge_disjoint_paths, + igraph_vertex_disjoint_paths, igraph_adhesion, igraph_cohesion +- dimacs file format added +- igraph_to_directed handles attributes +- igraph_constraint calculation corrected, it handles weighted graphs +- spinglass-based community structure detection, the Joerg Reichardt -- + Stefan Bornholdt algorithm added: igraph_spinglass_community, + igraph_spinglass_my_community +- igraph_extended_chordal_rings, it creates extended chordal rings +- 'no' argument added to igraph_clusters, it is possible to calculate + the number of clusters without calculating the clusters themselves +- minimum spanning tree functions keep attributes now and also the + direction of the edges is kept in directed graphs +- there are separate functions to calculate different types of + transitivity now +- igraph_delete_vertices rewritten to allocate less memory for the new + graph +- neighborhood related functions added: igraph_neighborhood, + igraph_neighborhood_size, igraph_neighborhood_graphs +- two new games added based on different node types: + igraph_preference_game and igraph_asymmetric_preference_game +- Laplacian of a graph can be calculated by the igraph_laplacian function + +Changes in the R interface +-------------------------- + +- bonpow function ported from SNA to calculate Bonacich power centrality +- get.adjacency supports attributes now, this means that it sets the + colnames and rownames attributes and can return attribute values in + the matrix instead of 0/1 +- grg.game, geometric random graphs +- graph.density, graph density calculation +- edge and vertex attributes can be added easily now when added new + edges with add.edges or new vertices with add.vertices +- graph.data.frame creates graph from data frames, this can be used to + create graphs with edge attributes easily +- plot.igraph and tkplot can plot self-loop edges now +- graph.edgelist to create a graph from an edge list, can also handle + edge lists with symbolic names +- get.edgelist has now a 'names' argument and can return symbolic + vertex names instead of vertex IDs, by default id uses the 'name' + vertex attribute is returned +- printing graphs on screen also prints symbolic symbolic names + (the 'name' attribute if present) +- maximum flow and minimum cut functions: graph.maxflow, graph.mincut +- vertex and edge connectivity: edge.connectivity, vertex.connectivity +- edge and vertex disjoint paths: edge.disjoint.paths, + vertex.disjoint.paths +- White's cohesion and adhesion measure: graph.adhesion, graph.cohesion +- dimacs file format added +- as.directed handles attributes now +- constraint corrected, it handles weighted graphs as well now +- weighted attribute to graph.adjacency +- spinglass-based community structure detection, the Joerg Reichardt -- + Stefan Bornholdt algorithm added: spinglass.community +- graph.extended.chordal.ring, extended chordal ring generation +- no.clusters calculates the number of clusters without calculating + the clusters themselves +- minimum spanning tree functions updated to keep attributes +- transitivity can calculate local transitivity as well +- neighborhood related functions added: neighborhood, + neighborhood.size, graph.neighborhood +- new graph generators based on vertex types: preference.game and + asymmetric.preference.game + +Bugs corrected +-------------- + +- attribute handling bug when deleting edges corrected +- GraphML escaping and NaN handling corrected +- bug corrected to make it possible compile the R package without the + libxml2 library +- a bug in Erdos-Renyi graph generation corrected: it had problems + with generating large directed graphs +- bug in constraint calculation corrected, it works well now +- fixed memory leaks in igraph_read_graph_graphml +- error handling bug corrected in igraph_read_graph_graphml +- bug corrected in R version of graph.laplacian when normalized + Laplacian is requested +- memory leak corrected in get.all.shortest.paths in the R package + +igraph 0.2.1 +========= + +Released Aug 23, 2006 + +This is a bug-fix release. Bugs fixed: +- igraph_reciprocity (reciprocity in R) corrected to avoid segfaults +- some docs updates +- various R package updated to make it conform to the CRAN rules + +igraph 0.2 +========= + +Released Aug 18, 2006 + +Release time at last! There are many new things in igraph 0.2, the +most important ones: +- reading writing Pajek and GraphML formats with attributes + (not all Pajek and GraphML files are supported, see documentation + for details) +- iterators totally rewritten, it is much faster and cleaner now +- the RANDEDU fast motif search algorithm is implemented +- many new graph generators, both games and regular graphs +- many new structural properties: transitivity, reciprocity, etc. +- graph operators: union, intersection, difference, structural holes, etc. +- conversion between directed and undirected graphs +- new layout algorithms for trees and large graphs, 3D layouts + +and many more. + +New things in the R package: +- support for CTRL+C +- new functions: Graph Laplacian, Burt's constraint, etc. +- vertex/edge sequences totally rewritten, smart indexing (see manual) +- new R manual and tutorial: 'Network Analysis with igraph', still + under development but useful +- very basic 3D plotting using OpenGL + +Although this release was somewhat tested on Linux, MS Windows, Mac +OSX, Solaris 8 and FreeBSD, no heavy testing was done, so it might +contain bugs, and we kindly ask you to send bug reports to make igraph +better. + +igraph mailing lists +==================== + +Aug 18, 2006 + +I've set up two igraph mailing lists: igraph-help for +general igraph questions and discussion and +igraph-anonunce for announcements. See +http://lists.nongnu.org/mailman/listinfo/igraph-help and +http://lists.nongnu.org/mailman/listinfo/igraph-announce +for subscription information, archives, etc. + +igraph 0.1 +========= + +Released Jan 30, 2006 + +After about a year of development this is the first "official" release +of the igraph library. This release should be considered as beta +software, but it should be useful in general. Please send your +questions and comments. diff --git a/README.md b/README.md new file mode 100644 index 0000000..185df33 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +[![Build Status on Azure Pipelines](https://dev.azure.com/igraph-team/igraph/_apis/build/status/igraph.igraph?branchName=master)](https://dev.azure.com/igraph-team/igraph/_build/latest?definitionId=1&branchName=master) +![Build Status on Github Actions](https://github.com/igraph/igraph/workflows/MINGW/badge.svg?branch=master) +[![codecov](https://codecov.io/gh/igraph/igraph/branch/master/graph/badge.svg?token=xGFabHJE2I)](https://codecov.io/gh/igraph/igraph) +[![DOI](https://zenodo.org/badge/8546198.svg)](https://zenodo.org/badge/latestdoi/8546198) + +The igraph library +------------------ + +igraph is a C library for complex network analysis and graph theory, with +emphasis on efficiency, portability and ease of use. + +See https://igraph.org for installation instructions and documentation. + +igraph can also be used from: + + - R — https://github.com/igraph/rigraph + - Python — https://github.com/igraph/python-igraph + - Mathematica — https://github.com/szhorvat/IGraphM + +igraph is a collaborative work of many people from all around the world — +see the [list of contributors here](./CONTRIBUTORS.md). If you would like +to contribute yourself, [click here to see how you can +help](./CONTRIBUTING.md). + +Citation +-------- + +If you use igraph in your research, please cite + +> Csardi, G., & Nepusz, T. (2006). The igraph software package for complex network research. InterJournal, Complex Systems, 1695. diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..bdcb7c7 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,13 @@ +# Need help with the igraph C library? + +_This repository is **only** about the C library of `igraph`. Do you use `igraph` from a different language? Then please see the repositories for the [R interface](https://github.com/igraph/rigraph/), the [Python interface](https://github.com/igraph/python-igraph/) or the [Mathematica interface](https://github.com/szhorvat/IGraphM)._ + +Having problems with igraph? + + - First, check our [documentation](https://igraph.org/c/html/latest/) for answers. + * Problems with installing `igraph`? Please check our [installation instructions](https://igraph.org/c/html/latest/igraph-Installation.html). + * Problems compiling your own code? Please check our [tutorial](https://igraph.org/c/html/latest/igraph-Tutorial.html) on writing your first `igraph` program. + - Do you have a question about `igraph`? Please post your question on our [support forum](https://igraph.discourse.group/). + - If you **found a bug**, please go ahead and [open a new issue](https://github.com/igraph/igraph/issues). + + We use the [issue tracker](https://github.com/igraph/igraph/issues) for bug reports and feature requests, and the [support forum](https://igraph.discourse.group/) for questions. diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..8e1d892 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,359 @@ +# Specify the list of .xml files that are used as-is +set( + DOCBOOK_SOURCES + fdl.xml + gpl.xml + igraph-docs.xml + installation.xml + introduction.xml + licenses.xml + pmt.xml + tutorial.xml +) + +# Specify the list of .xxml files that have to be piped through doxrox to +# obtain the final set of .xml files that serve as an input to DocBook +set( + DOXROX_SOURCES + adjlist.xxml + arpack.xxml + attributes.xxml + basicigraph.xxml + bipartite.xxml + bitset.xxml + cliques.xxml + coloring.xxml + community.xxml + cycles.xxml + dqueue.xxml + embedding.xxml + error.xxml + flows.xxml + foreign.xxml + generators.xxml + graphlets.xxml + heap.xxml + hrg.xxml + isomorphism.xxml + iterators.xxml + layout.xxml + matrix.xxml + memory.xxml + motifs.xxml + nongraph.xxml + operators.xxml + progress.xxml + psumtree.xxml + random.xxml + separators.xxml + sparsemat.xxml + spatialgames.xxml + stack.xxml + status.xxml + structural.xxml + strvector.xxml + threading.xxml + vector.xxml + vectorlist.xxml + visitors.xxml +) + +# Specify the igraph source files that may contain documentation chunks +file( + GLOB_RECURSE IGRAPH_SOURCES_FOR_DOXROX + LIST_DIRECTORIES FALSE + ${CMAKE_SOURCE_DIR}/include/*.h + ${CMAKE_BINARY_DIR}/include/*.h + ${CMAKE_SOURCE_DIR}/src/*.c + ${CMAKE_SOURCE_DIR}/src/*.cc + ${CMAKE_SOURCE_DIR}/src/*.cpp + ${CMAKE_SOURCE_DIR}/src/*.h + ${CMAKE_SOURCE_DIR}/src/*.pmt +) + +# Specify the igraph source files that are used as examples in the +# documentation +file( + GLOB DOCBOOK_EXAMPLES + LIST_DIRECTORIES FALSE + RELATIVE ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/examples/simple/*.c + ${CMAKE_SOURCE_DIR}/examples/tutorial/*.c +) + +# You should not need to change anything below this line if you are simply +# trying to add new files to produce documentation from + +# Documentation build requires Python and source-highlight +find_package(Python3) +find_program(SOURCE_HIGHLIGHT_COMMAND source-highlight) + +# HTML documentation additionally requires xmlto from DocBook +find_program(XMLTO_COMMAND xmlto) + +# PDF documentation additionally requires xsltproc, xmllint and Apache FOP +find_program(FOP_COMMAND fop) +find_program(XMLLINT_COMMAND xmllint) +find_program(XSLTPROC_COMMAND xsltproc) + +# GNU Texinfo documentation additionally requires the docbook2X package, +# makeinfo (and xmllint as well). The docbook2texi command from docbook2X +# is renamed to docbook2x-texi by many Linux distros to avoid conflict with +# a command of the same name from the incompatible docbook-tools package. +# We look for both command names, and prefer docbook2x-texi if found. +# At the moment we do not validate that docbook2texi is from docbook2X +# instead of docbook-tools. Such validation will be possible with CMake >= 3.25. +find_program(DOCBOOK2XTEXI_COMMAND NAMES docbook2x-texi docbook2texi) +find_program(MAKEINFO_COMMAND makeinfo) + +if(Python3_FOUND AND SOURCE_HIGHLIGHT_COMMAND) + set(DOC_BUILD_SUPPORTED TRUE) +else() + set(DOC_BUILD_SUPPORTED FALSE) +endif() + +if(DOC_BUILD_SUPPORTED AND XMLTO_COMMAND) + set(HTML_DOC_BUILD_SUPPORTED TRUE) +else() + set(HTML_DOC_BUILD_SUPPORTED FALSE) +endif() + +if(DOC_BUILD_SUPPORTED AND XMLLINT_COMMAND AND XSLTPROC_COMMAND AND FOP_COMMAND) + set(PDF_DOC_BUILD_SUPPORTED TRUE) +else() + set(PDF_DOC_BUILD_SUPPORTED FALSE) +endif() + +if(DOC_BUILD_SUPPORTED AND XMLLINT_COMMAND AND DOCBOOK2XTEXI_COMMAND AND MAKEINFO_COMMAND) + set(INFO_DOC_BUILD_SUPPORTED TRUE) +else() + set(INFO_DOC_BUILD_SUPPORTED FALSE) +endif() + +if(DOC_BUILD_SUPPORTED) + set(DOXROX_COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/doxrox.py) + set(DOXROX_RULES ${CMAKE_CURRENT_SOURCE_DIR}/c-docbook.re) + set(DOXROX_CHUNKS ${CMAKE_CURRENT_BINARY_DIR}/chunks.pickle) + set(DOXROX_CACHE ${CMAKE_CURRENT_BINARY_DIR}/doxrox.cache) + + set(DOCBOOK_INPUTS "") + set(DOCBOOK_GENERATED_INPUTS "") + + # Specify that each DocBook .xml file is to be copied to the build folder + # TODO(ntamas): currently this works with out-of-tree builds only + set(IGRAPH_VERSION ${PACKAGE_VERSION}) # for replacement in igraph-docs.xml + foreach(DOCBOOK_SOURCE ${DOCBOOK_SOURCES}) + set(DOCBOOK_INPUT "${CMAKE_CURRENT_BINARY_DIR}/${DOCBOOK_SOURCE}") + list(APPEND DOCBOOK_INPUTS "${DOCBOOK_INPUT}") + configure_file(${DOCBOOK_SOURCE} ${DOCBOOK_INPUT}) + endforeach() + + # Specify that .xxml files should be piped through doxrox.py to get a + # DocBook-compatible .xml file. This step inserts the documentation chunks + # extracted from the igraph source to the DocBook sources + foreach(DOXROX_SOURCE ${DOXROX_SOURCES}) + string(REGEX REPLACE "[.]xxml$" ".xml" DOXROX_OUTPUT ${DOXROX_SOURCE}) + set(COMMENT "Generating ${DOXROX_OUTPUT} from ${DOXROX_SOURCE}") + + string(PREPEND DOXROX_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/") + list(APPEND DOCBOOK_INPUTS "${DOXROX_OUTPUT}") + list(APPEND DOCBOOK_GENERATED_INPUTS "${DOXROX_OUTPUT}") + + add_custom_command( + OUTPUT ${DOXROX_OUTPUT} + COMMAND ${DOXROX_COMMAND} + ARGS + -t ${CMAKE_CURRENT_SOURCE_DIR}/${DOXROX_SOURCE} + --chunks ${DOXROX_CHUNKS} + -o ${DOXROX_OUTPUT} + MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${DOXROX_SOURCE} + DEPENDS ${DOXROX_CHUNKS} + COMMENT ${COMMENT} + ) + endforeach() + + # When all .xxml and .xml files have been processed, we have to send them + # through a custom Python script that extracts the ID references and produces + # a ctags-compatible "tags" file. This will then be used later by + # source-highlight to cross-reference the known tokens from the source code + # of the examples + list(JOIN DOCBOOK_GENERATED_INPUTS ";" DOCBOOK_GENERATED_INPUTS_AS_STRING) + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/tags" + COMMAND ${CMAKE_COMMAND} + ARGS + -DINPUT_FILES="${DOCBOOK_GENERATED_INPUTS_AS_STRING}" + -DOUTPUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/tags + -P ${CMAKE_SOURCE_DIR}/etc/cmake/generate_tags_file.cmake + DEPENDS ${DOCBOOK_GENERATED_INPUTS} + COMMENT "Creating tags file from DocBook xmls" + ) + + # Specify that each example source file is to be piped through source-higlight + # to produce an .xml representation that can be used in the DocBook + # documentation + foreach(DOCBOOK_EXAMPLE_SOURCE ${DOCBOOK_EXAMPLES}) + string(REGEX REPLACE "[.]c$" ".c.xml" DOCBOOK_EXAMPLE_OUTPUT ${DOCBOOK_EXAMPLE_SOURCE}) + set(COMMENT "Highlighting source code in ${DOCBOOK_EXAMPLE_SOURCE}") + + set(DOCBOOK_EXAMPLE_OUTPUT "${CMAKE_BINARY_DIR}/${DOCBOOK_EXAMPLE_SOURCE}.xml") + list(APPEND DOCBOOK_INPUTS "${DOCBOOK_EXAMPLE_OUTPUT}") + + get_filename_component(DOCBOOK_EXAMPLE_OUTPUT_DIR "${DOCBOOK_EXAMPLE_OUTPUT}" DIRECTORY) + + add_custom_command( + OUTPUT ${DOCBOOK_EXAMPLE_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E make_directory ${DOCBOOK_EXAMPLE_OUTPUT_DIR} + COMMAND ${Python3_EXECUTABLE} + ARGS + ${CMAKE_SOURCE_DIR}/tools/strip_licenses_from_examples.py + ${CMAKE_SOURCE_DIR}/${DOCBOOK_EXAMPLE_SOURCE} + ${CMAKE_BINARY_DIR}/${DOCBOOK_EXAMPLE_SOURCE} + COMMAND ${SOURCE_HIGHLIGHT_COMMAND} + ARGS + --src-lang c + --out-format docbook + --input ${CMAKE_BINARY_DIR}/${DOCBOOK_EXAMPLE_SOURCE} + --output ${DOCBOOK_EXAMPLE_OUTPUT} + --gen-references inline + --ctags="" + --outlang-def ${CMAKE_SOURCE_DIR}/doc/docbook.outlang + MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/${DOCBOOK_EXAMPLE_SOURCE} + DEPENDS tags + COMMENT ${COMMENT} + ) + endforeach() + + add_custom_command( + OUTPUT ${DOXROX_CHUNKS} ${DOXROX_CACHE} + COMMAND ${DOXROX_COMMAND} + ARGS + -e ${DOXROX_RULES} + -o ${DOXROX_CHUNKS} + --cache ${DOXROX_CACHE} + ${IGRAPH_SOURCES_FOR_DOXROX} + MAIN_DEPENDENCY ${DOXROX_RULES} + DEPENDS ${IGRAPH_SOURCES_FOR_DOXROX} + COMMENT "Parsing documentation chunks from source code" + ) + + set(DOCXML_STAMP ${CMAKE_CURRENT_BINARY_DIR}/xmlstamp) + add_custom_command( + OUTPUT ${DOCXML_STAMP} + COMMAND ${CMAKE_COMMAND} -E touch ${DOCXML_STAMP} + MAIN_DEPENDENCY igraph-docs.xml + DEPENDS ${DOCBOOK_INPUTS} + ) + add_custom_target(docxml DEPENDS ${DOCXML_STAMP}) + + if(HTML_DOC_BUILD_SUPPORTED) + set(HTML_STAMP ${CMAKE_CURRENT_BINARY_DIR}/html/stamp) + + add_custom_command( + OUTPUT ${HTML_STAMP} + COMMAND ${XMLTO_COMMAND} -x ${CMAKE_CURRENT_SOURCE_DIR}/gtk-doc.xsl -o html xhtml igraph-docs.xml + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/html/*.css ${CMAKE_CURRENT_BINARY_DIR}/html + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/html/*.js ${CMAKE_CURRENT_BINARY_DIR}/html + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/html/*.png ${CMAKE_CURRENT_BINARY_DIR}/html + COMMAND ${CMAKE_COMMAND} -E touch ${HTML_STAMP} + MAIN_DEPENDENCY igraph-docs.xml + # The DEPENDS clause below needs to list both the xmlstamp file and the + # target that creates it. The former is needed to make Ninja rebuild the + # HTML files if the source is modified. The latter is needed to make the + # XCode build system happy. + DEPENDS ${DOCXML_STAMP} docxml + COMMENT "Generating HTML documentation with xmlto" + ) + + add_custom_target(html DEPENDS ${HTML_STAMP}) + set(HTML_TARGET html) + endif() + + add_custom_command( + OUTPUT igraph-docs-with-resolved-includes.xml + COMMAND ${XMLLINT_COMMAND} + ARGS + --xinclude + --output igraph-docs-with-resolved-includes-tmp.xml + igraph-docs.xml + COMMAND ${Python3_EXECUTABLE} + ARGS + ${CMAKE_SOURCE_DIR}/tools/removeexamples.py + igraph-docs-with-resolved-includes-tmp.xml + igraph-docs-with-resolved-includes.xml + COMMAND ${CMAKE_COMMAND} + ARGS + -E remove igraph-docs-with-resolved-includes-tmp.xml + MAIN_DEPENDENCY igraph-docs.xml + # The DEPENDS clause below needs to list both the xmlstamp file and the + # target that creates it. The former is needed to make Ninja rebuild the + # PDF file if the source is modified. The latter is needed to make the + # XCode build system happy. + DEPENDS ${DOCXML_STAMP} docxml + ) + + # Intermediate custom target because Xcode projects cannot have commands that + # depend on intermediate files from other commands + add_custom_target( + _generate-resolved-docbook-xml DEPENDS igraph-docs-with-resolved-includes.xml + COMMENT "Resolving includes in DocBook XML source" + ) + + if(PDF_DOC_BUILD_SUPPORTED) + add_custom_command( + OUTPUT igraph-docs.fo + COMMAND ${XSLTPROC_COMMAND} + ARGS + --output igraph-docs.fo + --stringparam paper.type A4 + http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl + igraph-docs-with-resolved-includes.xml + DEPENDS _generate-resolved-docbook-xml + COMMENT "Converting DocBook XML to Apache FOP format" + ) + + add_custom_command( + OUTPUT igraph-docs.pdf + COMMAND ${FOP_COMMAND} + ARGS -fo igraph-docs.fo -pdf igraph-docs.pdf + MAIN_DEPENDENCY igraph-docs.fo + COMMENT "Generating PDF documentation with Apache FOP" + ) + + add_custom_target(pdf DEPENDS igraph-docs.pdf) + set(PDF_TARGET pdf) + endif() + + if(INFO_DOC_BUILD_SUPPORTED) + add_custom_command( + OUTPUT igraph-docs.texi + COMMAND ${DOCBOOK2XTEXI_COMMAND} + ARGS + --encoding=utf-8//TRANSLIT + --string-param output-file=igraph-docs + --string-param directory-category=Libraries + --string-param directory-description='A fast graph library \(C\)' + igraph-docs-with-resolved-includes.xml + DEPENDS _generate-resolved-docbook-xml + COMMENT "Converting DocBook XML to GNU Texinfo format" + ) + + add_custom_command( + OUTPUT igraph-docs.info + COMMAND ${MAKEINFO_COMMAND} + ARGS --no-split igraph-docs.texi + MAIN_DEPENDENCY igraph-docs.texi + COMMENT "Generating info documentation with GNU Makeinfo" + ) + + add_custom_target(info DEPENDS igraph-docs.info) + set(INFO_TARGET info) + endif() + + + add_custom_target(doc DEPENDS ${HTML_TARGET} ${PDF_TARGET} ${INFO_TARGET}) +endif() + +set(HTML_DOC_BUILD_SUPPORTED ${HTML_DOC_BUILD_SUPPORTED} PARENT_SCOPE) +set(PDF_DOC_BUILD_SUPPORTED ${PDF_DOC_BUILD_SUPPORTED} PARENT_SCOPE) +set(INFO_DOC_BUILD_SUPPORTED ${INFO_DOC_BUILD_SUPPORTED} PARENT_SCOPE) diff --git a/doc/adjlist.xxml b/doc/adjlist.xxml new file mode 100644 index 0000000..7e0717d --- /dev/null +++ b/doc/adjlist.xxml @@ -0,0 +1,51 @@ + + +]> + +
+Adjacency lists + + + +
Adjacent vertices + + + + + + + + + + +
+ +
Incident edges + + + + + +
+ +
Lazy adjacency list for vertices + + + + + + +
+ +
Lazy incidence list for edges + + + + + + +
+ +
diff --git a/doc/arpack.xxml b/doc/arpack.xxml new file mode 100644 index 0000000..ea16e20 --- /dev/null +++ b/doc/arpack.xxml @@ -0,0 +1,54 @@ + + + +]> + + +Using BLAS, LAPACK and ARPACK for igraph matrices and graphs + +
+ + + + + + +
+ +
+ +
Matrix factorization, solving linear systems + + + +
+
Eigenvalues and eigenvectors of matrices + + + +
+
+ +
+ +
Data structures + + + + + + +
+ +
ARPACK solvers + + + +
+ +
+ +
diff --git a/doc/attributes.xxml b/doc/attributes.xxml new file mode 100644 index 0000000..06dfd57 --- /dev/null +++ b/doc/attributes.xxml @@ -0,0 +1,126 @@ + + +]> + + +Graph, vertex and edge attributes + + + +
+The Attribute Handler Interface + + + + +
+ +
+Handling attribute combination lists + + + + + + + +
+ +
+Accessing attributes from C + + +
Query attributes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+Set attributes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
Remove attributes + + + + + + + + + + + +
+ +
Custom attribute combination functions + + + +
+ +
+ +
diff --git a/doc/basicigraph.xxml b/doc/basicigraph.xxml new file mode 100644 index 0000000..86c4fe6 --- /dev/null +++ b/doc/basicigraph.xxml @@ -0,0 +1,219 @@ + + +]> + + +Basic data types and interface + +
The &igraph; data model + +The &igraph; library can handle directed and +undirected graphs. The &igraph; graphs are multisets +of ordered (if directed) or unordered (if undirected) labeled pairs. +The labels of the pairs plus the number of vertices always starts with +zero and ends with the number of edges minus one. In addition to that, +a table of metadata is also attached to every graph, its most +important entries being the number of vertices in the graph and whether +the graph is directed or undirected. + + + +Like the edges, the &igraph; vertices are also +labeled by numbers between zero and the number of vertices minus one. +So, to summarize, a directed graph can be imagined like this: + + + ( vertices: 6, + directed: yes, + { + (0,2), + (2,2), + (3,2), + (3,3), + (3,4), + (3,4), + (4,3), + (4,1) + } + ) + + +Here the edges are ordered pairs or vertex ids, and the graph is a multiset +of edges plus some metadata. + + + +An undirected graph is like this: + + + ( vertices: 6, + directed: no, + { + (0,2), + (2,2), + (2,3), + (3,3), + (3,4), + (3,4), + (3,4), + (1,4) + } + ) + + +Here, an edge is an unordered pair of two vertex IDs. A graph is a multiset +of edges plus metadata, just like in the directed case. + + +It is possible to convert between directed and undirected graphs, +see the +igraph_to_directed() +and +igraph_to_undirected() functions. + + +&igraph; aims to robustly support multigraphs, i.e. graphs which +have more than one edge between some pairs of vertices, as well as +graphs with self-loops. Most functions which do not support such graphs +will check their input and issue an error if it is not valid. Those +rare functions which do not perform this check clearly indicate this +in their documentation. To eliminate multiple edges from a graph, you can use + + igraph_simplify(). + +
+ +
General conventions of &igraph; functions + +&igraph; has a simple and consistent interface. Most functions check +their input for validity and display an informative error message +when something goes wrong. In order to support this, the majority of functions +return an error code. In basic usage, this code can be ignored, as the +default behaviour is to abort the program immediately upon error. See +the section on error handling for +more information on this topic. + + + +Results are typically returned through output arguments, +i.e. pointers to a data structure into which the result will be written. +In almost all cases, this data structure is expected to be pre-initialized. +A few simple functions communicate their result directly through their return +value—these functions can never encounter an error. + +
+ +
Atomic data types + +igraph_integer_t + + +&igraph; introduces a few aliases to standard C data types that are then used +throughout the library. The most important of these types is +igraph_integer_t, which is an alias to either a 32-bit or a 64-bit +signed integer, depending on whether &igraph; was compiled +in 32-bit or 64-bit mode. The size of igraph_integer_t also +influences the maximum number of vertices that an &igraph; graph can represent +as the number of vertices is stored in a variable of type +igraph_integer_t. + + +Since the size of a variable of type igraph_integer_t may +change depending on how &igraph; is compiled, you cannot simply use +%d or %ld as a placeholder for &igraph; integers in +printf format strings. &igraph; provides the +IGRAPH_PRId macro, which maps to d, ld +or lld depending on the size of igraph_integer_t, and +you must use this macro in printf format strings to avoid compiler +warnings. + + +igraph_uint_t + +Similarly to how igraph_integer_t maps to the standard size +signed integer in the library, igraph_uint_t maps to a 32-bit or +a 64-bit unsigned integer. It is guaranteed that the size of +igraph_integer_t is the same as the size of igraph_uint_t. +&igraph; provides IGRAPH_PRIu as a format string placeholder for +variables of type igraph_uint_t. + + +igraph_real_t + +Real numbers (i.e. quantities that can potentially be fractional or +infinite) are represented with a type named igraph_real_t. Currently +igraph_real_t is always aliased to double, but it is +still good practice to use igraph_real_t in your own code for sake +of consistency. + +igraph_bool_t + +Boolean values are represented with a type named igraph_bool_t. +It tries to be as small as possible since it only needs to represent a truth +value. For printing purposes, you can treat it as an integer and use +%d in format strings as a placeholder for an igraph_bool_t. + + +IGRAPH_INTEGER_MAX +IGRAPH_INTEGER_MIN +IGRAPH_UINT_MAX +IGRAPH_UINT_MIN + +Upper and lower limits of igraph_integer_t and +igraph_uint_t are provided by the constants named +IGRAPH_INTEGER_MIN, IGRAPH_INTEGER_MAX, +IGRAPH_UINT_MIN and IGRAPH_UINT_MAX. + + +
+ +
The basic interface + + +
Graph constructors and destructors + + + + +
+ +
Basic query operations + + + + + + + + + + + + + + + +
+ +
Adding and deleting vertices and edges + + + + + + +
+ +
+ +
Miscellaneous macros and helper functions + + + + + +
+ +
diff --git a/doc/bibdatabase.xml b/doc/bibdatabase.xml new file mode 100644 index 0000000..1479cd4 --- /dev/null +++ b/doc/bibdatabase.xml @@ -0,0 +1,51 @@ + + + + + + + + Albert-László + Barabási + RékaAlbert + + Emergence of scaling in random networks + Science + 1999 + 286 + 509-512 + + + + + LászlóZalányi + GáborCsárdi + TamásKiss + MátéLengyel + RebeccaWarner + JanTobochnik + PéterÉrdi + + Properties of a random attachment growing network + Phyisical Review E + 2003 + 68 + 066104 + + + + + L. R.Ford Jr. + D. R.Fulkerson + + Maximal ow through a network + Canadian J. Math. + 1956 + 8 + 399--404 + + + + diff --git a/doc/bipartite.xxml b/doc/bipartite.xxml new file mode 100644 index 0000000..90bda4c --- /dev/null +++ b/doc/bipartite.xxml @@ -0,0 +1,41 @@ + + +]> + + +Bipartite, i.e. two-mode graphs + +
+ +
+ +
Create two-mode networks + + + + +
+ +
Bipartite adjacency matrices + + +
+ +
Project two-mode graphs + + +
+ +
Other operations on bipartite graphs + +
+ +
Deprecated functions + + + +
+ +
diff --git a/doc/bitset.xxml b/doc/bitset.xxml new file mode 100644 index 0000000..5e9bb19 --- /dev/null +++ b/doc/bitset.xxml @@ -0,0 +1,59 @@ + + +]> + +
+Bitsets + +
+ +
+ +
+ + + + +
+ +
+ + + + + + + +
+ +
Bitset operations + + + + + + + + + + + + + + + +
+ +
Bitset properties + + +
+ +
Resizing operations + + +
+ +
diff --git a/doc/c-docbook.re b/doc/c-docbook.re new file mode 100644 index 0000000..dacf1a4 --- /dev/null +++ b/doc/c-docbook.re @@ -0,0 +1,735 @@ +REPLACE ----- remove the " * " prefix first -----------------*- mode:python -*- +^[ ]\*[ ] +WITH -------------------------------------------------------------------------- +REPLACE ----- remove the " *" lines ------------------------------------------- +^[ ]\*\s*\n +WITH -------------------------------------------------------------------------- +\n +REPLACE IN typed_list.pmt ----- for the typed list template functions --------- + +FUNCTION\( +(?P[^\)]*) +\)\s* + +WITH + +igraph_vector_list_\g + +REPLACE IN typed_list.pmt ----- typed list template item type ----------------- + +ITEM_TYPE + +WITH + +igraph_vector_t + +REPLACE IN typed_list.pmt ----- typed list template type ---------------------- + +TYPE + +WITH + +igraph_vector_list_t + +REPLACE IN *.pmt ----- for the template functions ----------------------------- + +FUNCTION\( +(?P[^, \)]*)\s*,\s* +(?P[^\)]*) +\)\s* + +WITH + +\g_\g + +REPLACE IN *.pmt ----- template type ------------------------------------------ + +TYPE\( +(?P[^\)]*) +\) + +WITH + +\g_t + +REPLACE IN *.pmt ----- template base type, we cowardly assume real number ----- + +BASE + +WITH + +igraph_real_t + +REPLACE ----- function object, extract its signature -------------------------- + +(?P\A.*?) # head of the comment +\\function\s+ # \function keyword +(?P(?P
(igraph_)|(IGRAPH_)|())(?P\w+)) # the keyword, remove igraph_ prefix
+[\s]*(?P[^\n]*?)\n        # brief description
+(?P.*?)\*\/               # tail of the comment
+\s*
+(IGRAPH_EXPORT\s+)?              # strip IGRAPH_EXPORT from prototype
+(?P.*?\))                   # function head
+(?=(\s*;)|(\s*\{))               # prototype ends with ; function head with {
+.*\Z                             # and the remainder
+
+WITH --------------------------------------------------------------------------
+
+
+<function>\g<name></function> — \g<brief> +\g + + +\g; + + + +\g +\g + +
+ +REPLACE ----- for functions (not used currently) ------------------- + +(?P[^<]*)\n + +RUN --------------------------------------------------------------------------- + +dr_params=string.split(matched.group("params"), ',') +dr_out="" +for dr_i in dr_params: + dr_i=string.strip(dr_i) + if dr_i=="...": + dr_out=dr_out+"" + else: + dr_words=re.match(r"([\w\*\&\s]+)(\b\w+)$", dr_i).groups() + dr_out=dr_out+""+dr_words[0]+""+dr_words[1]+ \ + "\n" +actch=actch[0:matched.start()]+dr_out+actch[matched.end():] + +REPLACE ----- function parameter descriptions, head --------------------------- + +(?P\A.*?) # head of the comment +\\param\b # first \param commant + +WITH -------------------------------------------------------------------------- + +\g +Arguments: + +\\param + +REPLACE ----- function parameter descriptions, tail --------------------------- + +# the end of the params is either an empty line after the last \param +# command or a \return or \sa statement (others might be added later) +# or the end of the comment + +\\param\b # the last \param command +(?P.*?) # the text of the \param command +(?P # this marks the end of the \param text + (\\return\b)|(\\sa\b)| # it is either a \return or \sa or + (\n\s*?\n)| # (at least) one empty line or + (\*\/)) # the end of the comment +(?P.*?\Z) # remaining part + +WITH + +\\param\g +\g\g + +REPLACE ----- function parameter descriptions --------------------------------- + +\\param\b\s* # \param command +(?P(\w+)|(...))\s+ # name of the parameter +(?P.*?) # text of the \param command +(?=(\\param)|()| + (\n\s*\n)) + + +WITH -------------------------------------------------------------------------- + + \g: + + \g + +REPLACE ----- \return command ------------------------------------------------- + +# a return statement ends with an empty line or the end of the comment +\\return\b\s* # \return command +(?P.*?) # the text +(?=(\n\s*?\n)| # empty line or + (\*\/)| # the end of the comment or + (\\sa\b)) # \sa command + +WITH ----------------------------------------------------------------------TODO + +Returns: + + + \g + + + +REPLACE ----- variables ------------------------------------------------------- + +(?P\A.*?) # head of the comment +\\var\s+ # \var keyword + argument +(?P(?P
(igraph_)|(IGRAPH_)|())(?P\w+))
+[\s]*(?P[^\n]*?)\n         # brief description
+(?P.*?)\*\/                # tail of the comment
+\s*
+(IGRAPH_EXPORT\s+)?               # strip IGRAPH_EXPORT
+(?P[^;]*;)                   # the definition of the variable
+.*\Z                              # and the remainder
+
+WITH --------------------------------------------------------------------------
+
+
<function>\g<name></function> — \g<brief> +\g + + +\g + + +\g\g + +
+ +REPLACE ----- \define --------------------------------------------------------- + +(?P\A.*?) # head of the comment +\\define\s+ # \define command +(?P(?P
(igraph_)|(IGRAPH_)|())(?P\w+))
+[\s]*(?P[^\n]*?)\n         # brief description
+(?P.*?)\*\/                # tail of the comment
+\s*                               # whitespace
+(?P\#define\s+[\w0-9,]+\s*   # macro name
+(\([\w0-9,. ]+\))?)               # macro args (optional)
+.*\Z                              # drop the remainder
+
+WITH --------------------------------------------------------------------------
+
+
<function>\g<name></function> — \g<brief> +\g + + +\g + + +\g\g + +
+ +REPLACE ----- \section without title ------------------------------------------ + +(?P\A.*?) # head of the comment +\\section\s+(?P\w+)\s*$ # \section + argument +(?P.*?)\*\/ # tail of the comment +.*\Z # and the remainder, this is dropped + +WITH + +\g +\g + +REPLACE ----- \section with title --------------------------------------------- + +(?P\A.*?) # head of the comment +\\section\s+(?P\w+) # \section + argument +(?P.*?) # section title +\n\s*?\n # empty line +(?P<after>.*?)\*\/ # tail of the comment +.*\Z # and the remainder, this is dropped + +WITH + +<title>\g<title> +\g +\g + +REPLACE ----- \section with title --------------------------------------------- + +(?P\A.*?) # head of the comment +\\section\s+(?P\w+) # \section + argument +(?P.*?)\s*\*\/ # section title +.*\Z # and the remainder, this is dropped + +WITH + +<title>\g<title> +\g + +REPLACE ----- an enumeration typedef ------------------------------------------ + +(?P\A.*?) # head of the comment +\\typedef\s+ # \typedef command +(?P(?P
(igraph_)|(IGRAPH_)|())(?P\w+))
+[\s]*(?P[^\n]*?)\n         # brief description
+(?P.*?)                    # tail of the comment
+ \*\/\s*                          # closing the comment
+(?Ptypedef\s*enum\s*\{       # typedef enum
+ [^\}]*\}\s*\w+\s*;)                  # rest of the definition
+.*\Z
+
+WITH --------------------------------------------------------------------------
+
+
<function>\g<name></function> — \g<brief> +\g + + +\g + + + +\g\g + +
+ +REPLACE ----- enumeration value descriptions, head ---------------------------- + +(?P\A.*?) # head of the comment +\\enumval\b # first \param commant + +WITH -------------------------------------------------------------------------- + +\g +Values: + +\\enumval + +REPLACE ----- enumeration value descriptions, tail ---------------------------- + +\\enumval\b # the last \enumval command +(?P.*?) # the text of the \enumval command +(?P # this marks the end of the \enumval text + (\\return\b)|(\\sa\b)| # it is either a \return or \sa or + (\n\s*?\n)| # (at least) one empty line or + (\*\/)) # the end of the comment +(?P.*?\Z) # remaining part + +WITH + +\\enumval\g +\g\g + +REPLACE ----- enumeration value descriptions ---------------------------------- + +\\enumval\b\s* # \enumval command +(?P(\w+)|(...))\s+ # name of the parameter +(?P.*?) # text of the \enumval command +(?=(\\enumval)|()| + (\n\s*\n)) + +WITH -------------------------------------------------------------------------- + + \g: + + \g + +REPLACE ----- \struct --------------------------------------------------------- + +(?P\A.*?) # head of the comment +\\struct\s+ # \struct command +(?P(?P
(igraph_)|(IGRAPH_)|())(?P[\w_]+))
+[\s]*(?P[^\n]*?)(?=\n)     # brief description
+(?P.*?)                    # tail of the command
+\*\/\s*                           # closing the comment
+(?Ptypedef \s*struct\s*\w+\s*\{
+ .*\}\s*\w+\s*;)
+.*\Z
+
+WITH --------------------------------------------------------------------------
+
+
<function>\g<name></function> — \g<brief> +\g + + +\g + + + +\g\g + +
+ +REPLACE IN *.h ----- structure member descriptions, one block ----------------- + +^[\s]*\n +(?P.*?) # empty line+text +(?P\\member\b.*?) # member commands +(?= # this marks the end of the \member text + (\\return\b)|(\\sa\b)| # it is either a \return or \sa or + (^[\s]*\n)| # (at least) one empty line or + (\*\/)) # the end of the comment + +WITH -------------------------------------------------------------------------- + + +\g +Values: + +\g + + +REPLACE IN *.h ----- structure member descriptions ---------------------------- + +\\member\b\s* # \enumval command +(?P(\w+)|(...))\s+ # name of the parameter +(?P.*?) # text of the \enumval command +(?=(\\member)|()| + (\n\s*\n)) + +WITH -------------------------------------------------------------------------- + + \g: + + \g + +REPLACE ----- \typedef function ----------------------------------------------- + +(?P\A.*?) # comment head +\\typedef\s+ # \typedef command +(?P(?P
(igraph_)|(IGRAPH_)|())(?P\w+))
+[\s]*(?P[^\n]*?)\n         # brief description
+(?P.*?)                    # comment tail
+\*\/                              # end of comment block
+\s*
+(?Ptypedef\s+[^;]*;)         # the typedef definition
+.*\Z
+
+WITH --------------------------------------------------------------------------
+
+
<function>\g<name></function> — \g<brief> +\g + +\g + + +\g\g + +
+ +REPLACE ----- ignore doxygen \ingroup command --------------------------------- + +\\ingroup\s+\w+ + +WITH -------------------------------------------------------------------------- + +REPLACE ----- ignore doxygen \defgroup command -------------------------------- + +\\defgroup\s+\w+ + +WITH -------------------------------------------------------------------------- + +REPLACE ----- add the contents of \brief to the description ------------------- + +\\brief\b + +WITH -------------------------------------------------------------------------- + +REPLACE ----- \varname command ------------------------------------------------ + +\\varname\b\s* +(?P\w+\b) + +WITH + +\g + +REPLACE ----- references, \ref command, special case for igraph_vector_int ---- + +\\ref\b\s* +igraph_vector_int_(?P\w+)(?P([\(][\)])?) + +WITH -------------------------------------------------------------------------- + +igraph_vector_int_\g\g + +REPLACE ----- references, \ref command ---------------------------------------- + +\\ref\b\s* +(?P\w+)(?P([\(][\)])?) + +WITH -------------------------------------------------------------------------- + +\g\g + +REPLACE ----- \sa command ----------------------------------------------------- + +\\sa\b +\s* +(?P.*?) +(?=(\n\s*?\n)|(\*\/)) + +WITH ----------------------------------------------------------------------TODO + +See also: + + + \g + + + +REPLACE ----- \em command ----------------------------------------------------- + +\\em\b +\s* +(?P[^\s]+) + +WITH + +\g + +REPLACE ----- \emb command ---------------------------------------------------- + +\\emb\b + +WITH + + + +REPLACE ----- \eme command ---------------------------------------------------- + +\\eme\b + +WITH + + + +REPLACE ----- \verbatim ------------------------------------------------------- + +\\verbatim\b + +WITH + + + +REPLACE ----- \endverbatim ---------------------------------------------------- + +\\endverbatim\b + +WITH + + + +REPLACE ----- \clist ---------------------------------------------------------- + +\\clist\b + +WITH + + + +REPLACE ----- \cli ------------------------------------------------------------ + +\\cli\s+(?P.*?)$ +(?P.*?) +(?=(\\cli)|(\\endclist)) + +WITH -------------------------------------------------------------------------- + +\g + +\g + + +REPLACE ----- \endclist ------------------------------------------------------- + +\\endclist\b + +WITH + + + +REPLACE ----- \olist ---------------------------------------------------------- + +\\olist\b + +WITH + + + +REPLACE ----- \oli ------------------------------------------------------------ + +\\oli\s+(?P.*?) +(?=(\\oli)|(\\endolist)) + +WITH + + +\g + + +REPLACE ----- \endolist ------------------------------------------------------- + +\\endolist\b + +WITH + + + +REPLACE ----- \ilist ---------------------------------------------------------- + +\\ilist\b + +WITH + + + +REPLACE ----- \ili ------------------------------------------------------------ + +\\ili\s+(?P.*?) +(?=(\\ili)|(\\endilist)) + +WITH + + +\g + + +REPLACE ----- \endilist ------------------------------------------------------- + +\\endilist\b + +WITH + + + +REPLACE ----- doxygen \c command is for ---------------------------- + +\\c\s+(?P[\w\-^\']+)\b + +WITH + +\g + +REPLACE ----- doxygen \p command is for --------------------------- + +\\p\s+(?P\w+)\b + +WITH + +\g + +REPLACE ----- doxygen \type command is for ----------------------------- + +\\type\s+(?P\w+)\b + +WITH + +\g + +REPLACE ----- doxygen \a command is for ----------------------------- + +\\a\s+(?P\w+)\b + +WITH + +\g + +REPLACE ----- doxygen \quote command is for --------------------------- + +\\quote\s+ + +WITH + + + +REPLACE ----- doxygen \endquote command is for ----------------------- + +\s*\\endquote\b + +WITH + + + +REPLACE ----- replace with ----------------------------------- + +<(?P/?)code> + +WITH -------------------------------------------------------------------------- + +<\gliteral> + +REPLACE ----- add http:// and https:// links ---------------------------------- + +(?Phttps?:\/\/[-\+=&;%@.:/~()?'\w_]*[-\+=&;%@/~'\w_]) + +WITH -------------------------------------------------------------------------- + +\g + +REPLACE ----- blockquote ------------------------------------------------------ + +\\blockquote + +WITH -------------------------------------------------------------------------- + +
+ +REPLACE ----- blockquote ------------------------------------------------------ + +\\endblockquote + +WITH -------------------------------------------------------------------------- + +
+ +REPLACE ----- example file --------------------------------------------------- + +\\example\b\s* +(?P[^\n]*?)\n + +WITH -------------------------------------------------------------------------- + + + File <code>\g<filename></code> + + + + +REPLACE ----- \deprecated-by -------------------------------------------------- + +\\deprecated-by\b\s* +(?P[^ \n]+)\s* +(?P[^\n]+)\n + +WITH -------------------------------------------------------------------------- + +
+ +Deprecated since version \g. Please do not use this function in new +code; use \g() +instead. + + + +REPLACE ----- \deprecated ----------------------------------------------------- + +\\deprecated\b\s* +(?P[^\n]*?)\n + +WITH -------------------------------------------------------------------------- + + + +Deprecated since version \g. Please do not use this function in new +code. + + + +REPLACE ----- \experimental --------------------------------------------------- + +\\experimental\b\s*\n + +WITH -------------------------------------------------------------------------- + + + +This function is experimental and its signature is not considered final yet. +We reserve the right to change the function signature without changing the +major version of igraph. Use it at your own risk. + + diff --git a/doc/cliques.xxml b/doc/cliques.xxml new file mode 100644 index 0000000..22e8b74 --- /dev/null +++ b/doc/cliques.xxml @@ -0,0 +1,45 @@ + +]> + + +Cliques and independent vertex sets + + +These functions calculate various graph properties related +to cliques and independent vertex sets. + + +
Cliques + + + + + + + + + + + + + + +
+ +
Weighted cliques + + + +
+ +
Independent vertex sets + + + + + +
+ +
diff --git a/doc/coloring.xxml b/doc/coloring.xxml new file mode 100644 index 0000000..62f0e14 --- /dev/null +++ b/doc/coloring.xxml @@ -0,0 +1,14 @@ + + +]> + + +Graph coloring + + + + + + diff --git a/doc/community.xxml b/doc/community.xxml new file mode 100644 index 0000000..0299ab0 --- /dev/null +++ b/doc/community.xxml @@ -0,0 +1,63 @@ + + +]> + + +Detecting community structure + + + +
Community structure based on statistical mechanics + + +
+ +
Community structure based on eigenvectors of matrices + + + + +
+ +
Walktrap: Community structure based on random walks + +
+ +
Edge betweenness based community detection + + +
+ +
Community structure based on the optimization of modularity + + + +
+ +
Fluid communities + +
+ +
Label propagation + +
+ +
The InfoMAP algorithm + +
+ +
Voronoi communities + +
+ +
diff --git a/doc/cycles.xxml b/doc/cycles.xxml new file mode 100644 index 0000000..9c2ab69 --- /dev/null +++ b/doc/cycles.xxml @@ -0,0 +1,22 @@ + + +]> + + +Graph cycles + +
Eulerian cycles and paths + + + + +
+ +
Cycle bases + + +
+ +
diff --git a/doc/docbook.outlang b/doc/docbook.outlang new file mode 100644 index 0000000..44e14af --- /dev/null +++ b/doc/docbook.outlang @@ -0,0 +1,36 @@ +# by Stuart Rackham +# http://www.methods.co.nz/asciidoc/source-highlight-filter.html + +extension "xml" + +bold "$text" +italics "$text" + +anchor "$text" +postline_reference "$text -> $linenum" +postdoc_reference "$text -> $linenum" +reference "$text" + +doctemplate +" +
+ +$title + +" +" +
+" +end + +nodoctemplate +"" +" +" +end + +translations +"&" "&" +"<" "<" +">" ">" +end diff --git a/doc/doxrox.py b/doc/doxrox.py new file mode 100755 index 0000000..54898ea --- /dev/null +++ b/doc/doxrox.py @@ -0,0 +1,567 @@ +#! /usr/bin/env python3 + +# 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 +# +################################################################### + +"""DocBook XML generator for igraph. + +The generator parses one or more input files for documentation chunks +(embedded in the source code as Doxygen-style comments), and processes +them with a set of regex-based rules. The processed chunks are then +substituted into a template file containing +directives. + +When a template file is not provided, the generator will read the input +files, process them with the ruleset and save a dictionary mapping chunk +names to the corresponding processed chunks into a Python pickle. This +can be used to speed up the processing of multiple input files as you can +generate the chunks once and then re-use them for multiple input files. +""" + +import os +import re +import sys + +from argparse import ArgumentParser +from collections import defaultdict +from contextlib import contextmanager +from dataclasses import dataclass +from enum import Enum +from fnmatch import fnmatch +from hashlib import sha1 +from operator import itemgetter +from pathlib import Path +from pickle import dump, load +from time import time +from typing import Any, Callable, Dict, Iterator, List, Optional, Pattern + +#: Constant indicating the start of a comment that doxrox.py will process +DOXHEAD: str = r"/\*\*" + +#: Stores whether we want verbose output +verbose: bool = False + + +def fatal(message: str, code: int = 1): + """Prints an error message and exits the program with the given error code.""" + print(message, file=sys.stderr) + sys.exit(code) + + +######################################################################### +# The main function +######################################################################### + + +def main(): + """Main entry point of the script.""" + + global verbose + + # get command line arguments + parser = create_argument_parser() + arguments = parser.parse_args() + + outputfile: str = arguments.output_file + inputs: List[str] = arguments.inputs + + verbose = arguments.verbose + + if ( + arguments.template_file in inputs + or arguments.rules_file in inputs + or outputfile in inputs + ): + fatal("Special file is also used as an input file", 2) + + # open the cache file if needed + cache = ChunkCache(arguments.cache_file) if arguments.cache_file else None + + # get all regular expressions + rules: List[Rule] + if arguments.rules_file: + with operation("Reading regular expressions...") as op: + rules = read_regex_rules_file(arguments.rules_file) + op("{0} rules read".format(len(rules))) + else: + rules = [] + + # parse all input files and extract chunks, apply rules + if arguments.chunk_file: + with operation("Reading pickled chunks...") as op: + try: + with open(arguments.chunk_file, "rb") as f: + all_chunks = load(f) + except IOError: + fatal("Error reading chunk file: " + arguments.chunk_file, 9) + op("{0} chunks read".format(len(all_chunks))) + else: + all_chunks = {} + + rule_timings = defaultdict(list) + + for ifile in inputs: + with operation("Parsing input file {0}...".format(ifile)) as op: + try: + with open(ifile, "r") as f: + contents = f.read() + except IOError: + fatal("Error reading input file: " + ifile, 3) + + if cache: + key = cache.key_of(contents) + chunks = cache.get(key) + else: + key, chunks = None, None + + if chunks is not None: + op("{0} chunks read from cache".format(len(chunks))) + else: + chunks = collect_chunks_from_input_file( + ifile, contents, rules, rule_timings + ) + op("{0} chunks parsed".format(len(chunks))) + if key and cache: + cache.put(key, chunks) + + for name, chunk in chunks.items(): + if name in all_chunks: + fatal( + "Multiple files provide chunks for {0!r}".format(name), code=4 + ) + all_chunks[name] = chunk + + if arguments.timing_stats and rule_timings: + rule_timings = {name: sum(dts) / len(dts) for name, dts in rule_timings.items()} + for name, dt in sorted(rule_timings.items(), key=itemgetter(1), reverse=True): + print("{0}: {1:.3f}us".format(name, dt)) + print("======") + + if cache: + cache.close() + + if arguments.template_file: + # substitute the template file + with operation("Reading template file..."): + try: + with open(arguments.template_file, "r") as tfile: + tstring = tfile.read() + except IOError: + fatal("Error reading the template file: " + arguments.template_file, 7) + + with operation("Substituting template file..."): + chunk_iterator = re.finditer( + r"", tstring + ) + outstring = [] + last = 0 + for match in chunk_iterator: + try: + chunk = all_chunks[match.group(1)] + except KeyError: + fatal("Chunk not found: {0}".format(match.group(1)), code=4) + outstring.append(tstring[last : match.start()]) + outstring.append(chunk) + last = match.end() + outstring.append(tstring[last:]) + outstring = "".join(outstring) + + # write output file + with operation("Writing output file..."): + try: + with open(outputfile, "w") as ofile: + ofile.write(outstring) + except IOError: + fatal("Error writing output file:" + outputfile, 8) + else: + # no template file given so just save the chunks as a pickle into the + # output file + with operation("Writing output file..."): + try: + with open(outputfile, "wb") as ofile: + dump(all_chunks, ofile) + except IOError: + fatal("Error writing output file:" + outputfile, 5) + + +######################################################################### +# Argument parser +######################################################################### + + +def create_argument_parser() -> ArgumentParser: + """Creates the command line argument parser that the script uses.""" + parser = ArgumentParser(description=(sys.modules[__name__].__doc__ or "").strip()) + + parser.add_argument( + "--cache", + metavar="FILE", + dest="cache_file", + help="optional cache file to store chunks from already processed files", + ) + parser.add_argument( + "-t", + "--template", + metavar="FILE", + dest="template_file", + help="template file to process", + ) + parser.add_argument( + "-e", + "--rules", + metavar="FILE", + dest="rules_file", + help="file containing matching and replacement rules", + ) + parser.add_argument( + "-o", + "--output", + metavar="FILE", + dest="output_file", + required=True, + help="name of the output file", + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + default=False, + dest="verbose", + help="enable verbose output", + ) + parser.add_argument( + "--chunks", + dest="chunk_file", + metavar="FILE", + help="name of a previously saved chunk file", + ) + parser.add_argument( + "--timing-stats", + dest="timing_stats", + action="store_true", + default=False, + help="print the average time it takes to process regex rules from the rules file", + ) + parser.add_argument( + "inputs", metavar="INPUT", nargs="*", help="input files to process" + ) + + return parser + + +################# +# classes and functions to read the regular expression rules +################# + + +class RuleType(Enum): + REPLACE = "replace" + RUN = "run" + + +@dataclass +class Rule: + regex: Pattern[str] + """The regular expression that the rule will attempt to match.""" + + replacement: str + """The replacement string for the match, or the code to execute on the + match. + """ + + type: RuleType + """Type of the rule""" + + name: Optional[str] + """Name of the rule, for debugging purposes.""" + + glob: Optional[str] = None + """Optional glob pattern that specifies which input files the rule + applies to. + """ + + def applies_to_filename(self, filename: str) -> bool: + """Returns whether the rule applies to files with the given name.""" + if self.glob: + return fnmatch(filename, self.glob) + else: + return True + + +def read_regex_rules_file(filename) -> List[Rule]: + """Parses the file containing the regex-based rules that we use to chop + up the input source files into chunks that can later be fed into a + DocBook document. + + Parameters: + filename: name of the input file + + Returns: + the rules that were parsed from the input file + """ + + rules: List[Rule] = [] + + def parse_error(lineno): + """Helper function to indicate a parse error at the given line.""" + fatal( + "Parse error in regex file ({0}), line {1}".format(filename, lineno), code=4 + ) + + def store( + rule: List[str], + replacement: List[str], + rule_name: Optional[str], + rule_type: RuleType, + glob: Optional[str], + ) -> None: + """Helper function to append the current rule to the result.""" + regex = re.compile("".join(rule), re.VERBOSE | re.MULTILINE | re.DOTALL) + replacement_str = "".join(replacement)[:-1] + rules.append(Rule(regex, replacement_str, rule_type, rule_name, glob)) + + mode = "empty" + regex, replacement = [], [] + rule_name: Optional[str] = None + rule_type: Optional[RuleType] = None + glob: Optional[str] = None + + try: + with open(filename, "r") as f: + for lineno, line in enumerate(f, 1): + if line.startswith("REPLACE"): + # a new pattern block starts + if mode not in ("empty", "with"): + parse_error(lineno) + else: + if regex and rule_type: + store(regex, replacement, rule_name, rule_type, glob) + regex.clear() + replacement.clear() + mode = "replace" + + match = re.match( + r"^REPLACE( IN (?P[^\s]+))?\s+-+\s+(?P.*)\s+-", + line, + ) + rule_name = match.group("name") if match else None + glob = match.group("glob") if match else None + + elif line.startswith("WITH") or line.startswith("RUN"): + # the second half of the pattern block starts + if mode != "replace": + parse_error(lineno) + else: + mode = "with" + rule_type = ( + RuleType.REPLACE if line.startswith("WITH") else RuleType.RUN + ) + + elif re.match(r"^\s*$", line): + # empty line, do nothing + pass + + else: + # normal line, append + if mode == "replace": + regex.append(line) + elif mode == "with": + replacement.append(line) + else: + parse_error(lineno) + + if regex != "" and rule_type: + store(regex, replacement, rule_name, rule_type, glob) + + except IOError: + fatal("Error reading regex file: " + filename, code=4) + + return rules + + +################# +# parse an input file string +################# +def collect_chunks_from_input_file( + path: str, strinput: str, rules: List[Rule], rule_timings +) -> Dict[str, str]: + result: Dict[str, str] = {} + + # split the file + chunks = re.split(DOXHEAD, strinput) + chunks = chunks[1:] + + # get the filename part of the path + filename = os.path.basename(path) + + # apply all rules to the chunks + for chunk in chunks: + name: Optional[str] = None + + for rule in rules: + start = time() + + if not name and "name" in rule.regex.groupindex: + # The regex might provide us with a chunk name so try figuring + # out what the "name" group might match to + matched = rule.regex.search(chunk) + if matched: + try: + name = matched.group("name") + except IndexError: + name = "" + + if rule.applies_to_filename(filename): + if rule.type is RuleType.REPLACE: + # This is a simple regex replacement rule + try: + chunk = rule.regex.sub(rule.replacement, chunk) + except IndexError: + print("Index error:" + chunk[0:60] + "...") + print("Pattern:\n" + rule.regex.pattern) + print("Current state:" + chunk[0:60] + "...") + fatal("Parsing error", code=6) + elif rule.type is RuleType.RUN: + # This is a piece of Python code that has to be executed on + # the part that matched + matched = rule.regex.search(chunk) + if matched: + exec(rule.replacement) + else: + fatal("Invalid rule type: {0!r}".format(rule.type), code=6) + + rule_timings[rule.name].append((time() - start) * 1000000) + + if not name: + # print("Chunk without a name ignored:" + ch[0:60] + "...") + continue + + result[name] = chunk.strip() + + return result + + +@contextmanager +def operation(message: str) -> Iterator[Callable[[Any], None]]: + """Helper function to show progress messages for a potentially long-running + operation in verbose mode. + + Parameters: + message (str): the message to show + """ + global verbose + + if verbose: + print(message, end="") + + result = [None] + + def set_result(obj: Any) -> None: + result[0] = obj + + success = False + try: + yield set_result + success = True + finally: + if verbose and success: + if result[0] is None: + print(" done.") + else: + print(" done, {0}.".format(result[0])) + + +class ChunkCache: + """Simple on-disk cache that stores SHA256 hashes of files along with the + DocBook documentation chunks that were parsed from them. + """ + + _data: Optional[Dict[str, Dict[str, str]]] + _dirty: bool + _path: Path + + def __init__(self, filename: str, hash=sha1): + """Constructor. + + Parameters: + filename: name of the file on the disk where the cache resides + hash: the hash function to use + """ + self._data = None + self._dirty = False + self._hash = hash + self._path = Path(filename) + + def _load(self) -> None: + """Populates the in-memory copy of the cache from the disk.""" + if self._path.exists(): + try: + with self._path.open("rb") as fp: + self._data = load(fp) + except (IOError, EOFError): + # cache corrupted + self._data = {} + else: + self._data = {} + self._dirty = False + + def close(self) -> None: + """Closes the cache and flushes its contents back to the disk if it + changed recently. + """ + if self._dirty: + self.flush() + + def flush(self) -> None: + """Flushes the contents of the cache back to the disk.""" + with self._path.open("wb") as fp: + dump(self._data, fp) + self._dirty = False + + def get(self, key: str) -> Optional[Dict[str, str]]: + """Returns the chunks associated to the file with the given key, or + `None` if the key is not in the cache. + """ + if self._data is None: + self._load() + + assert self._data is not None + return self._data.get(key) + + def key_of(self, contents, encoding: str = "utf-8") -> str: + """Returns the hash key corresponding to the file with the given + contents. + """ + if not isinstance(contents, bytes): + contents = contents.encode(encoding) + + key = self._hash() + key.update(contents) + return key.hexdigest() + + def put(self, key: str, chunks: Dict[str, str]) -> None: + """Stores some chunks associated to the file with the given key.""" + assert self._data is not None + self._data[key] = chunks + self._dirty = True + + +if __name__ == "__main__": + main() diff --git a/doc/dqueue.xxml b/doc/dqueue.xxml new file mode 100644 index 0000000..4a39443 --- /dev/null +++ b/doc/dqueue.xxml @@ -0,0 +1,24 @@ + + +]> + +
+Double-ended queues + + + + + + + + + + + + + + + +
diff --git a/doc/embedding.xxml b/doc/embedding.xxml new file mode 100644 index 0000000..96af489 --- /dev/null +++ b/doc/embedding.xxml @@ -0,0 +1,16 @@ + + +]> + + +Embedding of graphs + +
Spectral embedding + + + +
+ +
diff --git a/doc/error.xxml b/doc/error.xxml new file mode 100644 index 0000000..184fdac --- /dev/null +++ b/doc/error.xxml @@ -0,0 +1,88 @@ + + +]> + + +Error handling + +
+ +
+ +
+ + + + + +
+ +
+ + + + +
+ +
+ + + + + + + + + +
+ +
+Advanced topics + +
+ + +
+ +
+ + + + + + + +
+ +
+ + + + +
+ +
+ +
+ +
+ + + + + + + + + +
+ +
+ +
+ +
+ +
diff --git a/doc/fdl.xml b/doc/fdl.xml new file mode 100644 index 0000000..8fbd700 --- /dev/null +++ b/doc/fdl.xml @@ -0,0 +1,420 @@ + + +
+ + Version 1.2, November 2002 + 200020012002 + Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + + +The GNU Free Documentation License + +
0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +
1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +
2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +
3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +
4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + + + + Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. + + List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. + + State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + Preserve all the copyright notices of the Document. + + Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. + + Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. + + Include an unaltered copy of this License. + + Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. + + Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. + + For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. + + Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. + + Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. + + Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. + + Preserve any Warranty Disclaimers. + + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +
5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + +
6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +
7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +
8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +
9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +
10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + +
G.1.1 ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +
+
diff --git a/doc/flows.xxml b/doc/flows.xxml new file mode 100644 index 0000000..293ff71 --- /dev/null +++ b/doc/flows.xxml @@ -0,0 +1,48 @@ + + +]> + + +Maximum flows, minimum cuts and related measures + +
Maximum flows + + + + +
+ +
Cuts and minimum cuts + + + + + + + +
+ +
Connectivity + + + + +
+ +
Edge- and vertex-disjoint paths + + +
+ +
Graph adhesion and cohesion + + +
+ +
Cohesive blocks + +
+ +
diff --git a/doc/foreign.xxml b/doc/foreign.xxml new file mode 100644 index 0000000..671e947 --- /dev/null +++ b/doc/foreign.xxml @@ -0,0 +1,64 @@ + + +]> + + +Reading and writing graphs from and to files + + + +
Simple edge list and similar formats + + + + + + + + +
+ +
Binary formats + +
+ +
GraphML format + + +
+ +
GML format + + +
+ +
Pajek format + + +
+ +
UCINET's DL file format + +
+ +
Graphviz format + +
+ +
LEDA format + +
+ +
Convenience functions for locale change + + +
+ +
Deprecated functions + + +
+ +
diff --git a/doc/generators.xxml b/doc/generators.xxml new file mode 100644 index 0000000..a693636 --- /dev/null +++ b/doc/generators.xxml @@ -0,0 +1,92 @@ + + +]> + + +Graph generators + + + +
Deterministic graph generators + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
Games: Randomized graph generators + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
Deprecated functions + + + +
+ +
diff --git a/doc/gpl.xml b/doc/gpl.xml new file mode 100644 index 0000000..1ded18f --- /dev/null +++ b/doc/gpl.xml @@ -0,0 +1,444 @@ + + +
+ + Version 2, June 1991 + 19891991 + Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + +THE GNU GENERAL PUBLIC LICENSE +
Preamble + + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + + + The precise terms and conditions for copying, distribution and +modification follow. + + +
+
GNU GENERAL PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + + + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + + + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + + You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + + + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + + + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + + + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + + + Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + + + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + + + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + + + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + + + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + + + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + + NO WARRANTY + + + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + + + END OF TERMS AND CONDITIONS + + +
+
How to Apply These Terms to Your New Programs + + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + +Also add information on how to contact you by electronic and paper mail. + + + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + + + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + + + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + + + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + + +
+
diff --git a/doc/graphlets.xxml b/doc/graphlets.xxml new file mode 100644 index 0000000..d666a9d --- /dev/null +++ b/doc/graphlets.xxml @@ -0,0 +1,20 @@ + + +]> + + +Graphlets + +
+ +
+ +
Performing graphlet decomposition + + + +
+ +
diff --git a/doc/gtk-doc.xsl b/doc/gtk-doc.xsl new file mode 100644 index 0000000..14d252e --- /dev/null +++ b/doc/gtk-doc.xsl @@ -0,0 +1,342 @@ + + + + + + + + + bibdatabase.xml + 1 + 0 + 2 + + book toc + chapter toc + section toc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.36 + + + + +FATAL-ERROR: You need the DocBook XSL Stylesheets version 1.36 or higher +to build the documentation. +Get a newer version at http://docbook.sourceforge.net/projects/xsl/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.66 + + + + + + + + + + + + 1.66 + + + + + + + + + + +
+ + + +
+
+ + + +
+
+ + + + + + + +
+ + + +
+ + + +

+ + + +

+
+ +

+ + + + + + + + +

+
+
+

+ +

+
+ + + +
+
+
+ + + +
+ +
+
+ +
diff --git a/doc/heap.xxml b/doc/heap.xxml new file mode 100644 index 0000000..1046915 --- /dev/null +++ b/doc/heap.xxml @@ -0,0 +1,21 @@ + + +]> + +
+Maximum and minimum heaps + + + + + + + + + + + + +
diff --git a/doc/hrg.xxml b/doc/hrg.xxml new file mode 100644 index 0000000..15a0810 --- /dev/null +++ b/doc/hrg.xxml @@ -0,0 +1,45 @@ + + +]> + + +Hierarchical random graphs + +
+ +
+ +
Representing HRGs + + + + + +
+ +
Fitting HRGs + + +
+ +
HRG sampling + + +
+ +
Conversion to and from igraph graphs + + +
+ +
Predicting missing edges + +
+ +
Deprecated functions + +
+ +
diff --git a/doc/html/home.png b/doc/html/home.png new file mode 100644 index 0000000000000000000000000000000000000000..17003611d9df2b066afc682cbde962f3a575002d GIT binary patch literal 654 zcmV;90&)F`P)~yY zO1cF+0vxb!W?!x?K+*#62Jq)nA4q`)5S6sgX4ao{=)(Mgq+YMr)7sjak|a^9)zS!j zlk{-n29mabXYF=7SYBQx&vO8xC}MYams+hxqtO7sImhPaCf@rq;I^3!#u*2aUP)55 zT2&N90xmEJ0s&fGT~(T<3d2xYmK9C>IP*x-M@ib*+0pFm>>uW37N2Wzaq-fCnIZE9 zpb8}0+uN+KuQM2oZVHfP8U6kQdo3?>Wo2dT)WeM9So8DqhLi#T0 z-i(>mfjhvbsYV`;4sgfJ-p>G-SqJ!fjR6BQYs1h*y9xaN0l{VB;o%`08yiy@)$8@~ z2PD1gcDuiy;j1tR0v#V8OH%W)25-YKyx(j#IXO9*YWf0mb8}QG6@b@;cHxh9{t7+@ o!Yd`f8L$sLH?yBt^q3C6015TtIu@BS5dZ)H07*qoM6N<$f*igdr~m)} literal 0 HcmV?d00001 diff --git a/doc/html/left.png b/doc/html/left.png new file mode 100644 index 0000000000000000000000000000000000000000..2d05b3d5b4aeec9384bbfe404bfc4ed0897051c4 GIT binary patch literal 459 zcmV;+0W|)JP)40xL?wO*>WZ(J#ML5j2<9jD6A%Q&kC}jOeEc;X{s;`zcnxLeZR6?6h#^ihmNF6NpGdilO$m<82oD9WQ|6nVv1`? z>KufRi{?QPXg;4;wroQu4?mN1Ydd@|kaQ|ZyWLK!)yi7Wb%=0{}lD)tfliHAUyWRQ+fD_;aV6j->y6!O_8bENg6P)Cd4HCN^TYHBC0dz3r5|}*T3c5!K}0^NPTey!^rYo;W&eW{b1SE%dR-1ljcju- zJITo5P_e{cPDWDszO|97o#m$fni3V4d%~7^?0HU4-k!+X`e~w55Q}HA=c?CM9`EK` z^o5GF_RsnG`ey+9wOf8O4bzg>7W*;jU~M?g`OZAA$mNp|Lz<$s+~N9!2`ir8RcClo$(Q~19INM~9}j;&*|enC yGd}kJak0wj?aUKd8;%}`i}SSew>!A-2iw}^5}Rh(M>+vRkipZ{&t;ucLK6U4uc96R literal 0 HcmV?d00001 diff --git a/doc/igraph-docs.xml b/doc/igraph-docs.xml new file mode 100644 index 0000000..1694955 --- /dev/null +++ b/doc/igraph-docs.xml @@ -0,0 +1,166 @@ + + + + + + + + +]> + + + + + &igraph; Reference Manual + &version; + + GáborCsárdi + + Department of Statistics, Harvard University +
1 Oxford street, Cambridge, MA, 02138 USA
+
+
+ + TamásNepusz + + Department of Biological Physics, Eötvös Loránd University +
1/a Pázmány Péter sétány, 1117 Budapest, Hungary
+
+
+ + VincentTraag + + Centre for Science and Technology Studies, Leiden University +
Room B5.31, Kolffpad 1, 2333 BN Leiden, Netherlands
+
+
+ + SzabolcsHorvát + + Department of Computer Science, Reykjavik University +
Menntavegur 1, 102 Reykjavík, Iceland
+
+
+ + FabioZanini + + Lowy Cancer Research Centre, University of New South Wales +
Room 211, Botany and High St, Kensington, NSW, 2033, Australia
+
+
+ + DanielNoom + + jitjit software development +
Amsterdam, Netherlands
+
+
+
+ + + This manual is for &igraph;, version &version;. + + + Copyright (C) 2005-2019 Gábor Csárdi and Tamás Nepusz. + Copyright (C) 2020-2024 igraph development team. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled + GNU Free Documentation License. + + +
+ + + + + + + + + + + + + + + Data structure library: vector, matrix, other data types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Advanced igraph programming + + + + + + + + + + + +
diff --git a/doc/igraph.3 b/doc/igraph.3 new file mode 100644 index 0000000..bee9abe --- /dev/null +++ b/doc/igraph.3 @@ -0,0 +1,47 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.\" +.\" Copyright (C) 2006-2021 The igraph development team +.\" +.\" This is free software; you can redistribute it and/or modify it under +.\" the terms of the GNU General Public License as published by the Free +.\" Software Foundation; either version 2, or (at your option) any later +.\" version. +.\" +.\" This is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" for more details. +.\" +.\" You should have received a copy of the GNU General Public License with +.\" your Debian GNU/Linux system, in /usr/share/common-licenses/GPL, or with +.\" the dpkg source package as the file COPYING. If not, write to the Free +.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.TH IGRAPH 3 "May 2021" "igraph library" +.SH NAME +igraph \- a library for creating and manipulating graphs +.SH DESCRIPTION +.B igraph +is a C library for complex network analysis and graph theory, with emphasis on +efficiency, portability and ease of use. +.SH DOCUMENTATION +The full documentation can be downloaded from the homepage of the +library: +.RI < https://igraph.org/c/doc > +.SH BUGS +If you think you have found a bug in igraph, feel free to file a bug report +in the issue tracker at: +.RI < https://github.com/igraph/igraph/issues > + +.SH AUTHORS +Gabor Csardi , +.br +Tamas Nepusz , +.br +Vincent Traag , +.br +Szabolcs Horvat , +.br +Fabio Zanini , +.br +Daniel Noom diff --git a/doc/igraphlogo/igraph-white.svg.gz b/doc/igraphlogo/igraph-white.svg.gz new file mode 100644 index 0000000000000000000000000000000000000000..60b54058fef4f894642fda1310afb63dcb3f5706 GIT binary patch literal 6138 zcmVpy(Fy;*#}+CHo|cNZsS7*7_fyUWeh`tJJT={{q=7aiv^~=dpN(kyf~@!_wTniLU(m}wz^r}uI_dZXJ#;GCo}owOn%A9*Wa%$ zH@CN&y9eRCd-!cfyuErmQFAdLoDi8(%GqeotUU$A>BFbH-SXqt=8A?b{8kMeqUc*oM6RPzO?DjC#@IC9~7s1BCWHT0gHb)6R zDWQSp;ji&1%?}qR$@^g}-sjH`T9^<5* z_1C*kvvw>N*ITIm=4Q2B-d#fO6w&nP{rYOP_pCnHeD`;3;@Fc}K3L;4+c+G3{_t*j zwfO)mJAC-VW^)VJ4*u}TC63(Zp@it&qi5V#2!oAz%!dzQb?@miPv76IcW_JhA0M&5 z-)>28v;4Gz_^z!V+u=RD+kBv$E>7MqZ>D3II6tiKu#eN~LyR9E?Y2@hw^B-cf`S_T z#3RbFsm$$ax4c^Jma}>^BsE)u?>_(UKmO_Ks(!C8FVFvXv;9ZY#>IjJ%kSVvE>2Fq zp5WJ4m*?=dx69qv>sy==9rb_0yFyE+2YbXc*LhGKcDquyeKdAmUEZ$AboQ6s`sU`( zoTP3`hCHhat=l;3_VN0x?NYv7AIRxu`Q7T~;^Ym_Z?e1T>GgK={{D7zg+<8;pUkq8 z?~P#xm44txFHYp+4MOo3f`w3=TfFJfPzK`+h6L*35N+l}!3^2OiYA$esHLwsK*Q(; zm$NRaq8?}-0=#<`Eaj1y!Km2_2AAY4xs9W8D7POGL6p)D5i^0(yTM@d#KI7qjOf#l z<0#x=D5kEnL-R1Eiemg!XF86y5h&6;V95|EQAmPwS*+PB<_E9~<`DHBt={^IwjXbp7?!?L&jGt^XMvlgp?p{Co3vdMI4o-a=QVW5K07F zaFY_B@?*6*P_X?V40H7<&5?NyyKoP5O^6Sd2Y)6PwP)?~aB-gFiwpRy3&6Q#7!frN zXEj#ssHkceL`v<#Nr%x27GdLxLEFPf?vc^HaR=sZLg}bc9^N9t0=yjqFxVnMUe@(!T>U%T!w)00eRR*ByWyGXs!!ubjX{agV_yG)?j2WPS))c zM#d6m4BRQ;Q=WERDY;tAXJvx%4Xv0wZbXcTLA zzyivkhJ~zHf{iP;U^*tk!06-CXq3~b^Cvm+9_9R5Bx1zJI7c62N1Orn1S~<{a6&W2 z)9{lHc9eM>ise~yo{+p?k(2=tgcRi{3o1NY52HZvSf&-#qUc1L9>)S{7Dl6y8kiH- zAiJrUo(0qmXTDE~VW2!-Fj@pLBbut*rbQ~CiEeg6NjbuA$BMzji}vHhRq6$iqFG)- zoO5}99_pxSRzbw8wgYcml)FGzn+_WAWU(@=Z4}FcoP$x!Nt~N8+N~c7MpW*Z601YW@ManP%SdxnZAo{3J_jAVHiD zDPvuyWrl&}Fe8xzQ$Uu(pyq64RWgS?!M-B^JTx+^3lhm) zrrjCnAax@60t?E-aH{k+X~I;B&@=I09c4`L10q?kxq=s8MUO~s_|8(#ARyQ9^f$p< z@rlq}*tDKCSIH>4k0E<$+S8$yQ5}0kjx)d`aYV=hkYG4V(feQ%bBpT#P%I~*L?(cB z1Ert!R=hs)6_pS@bkKc6$fXLLPC7W*H_pn-e#lLY(!2*#*O=kOVEdD%G`#p(WJxI?Z{k=n%Fj7__@;KUS8S;eZk+PCN08f?_*}k& zQypsh+fg!`9MwP(mBniZ|nq-C(DalB6~tG;09HhEj!Ir6_vc$YbR@ zUgmtMXg*ibSbF%Bq~;71uX+}-s}x*AnRha_MN=`v9OlBTAdrx0z1!j{Y;WQ=%)m%V zTZ3_UvBaX;H8AL@a0{l~us?7dP_n(B#T;yx;t}f7SlBgP1&MZPP z$F{wOu)+(9>`N9VqYMdVHCIdtx(AZ8&_n%Yf4mB#rIWf6O7H-#BCG3KtN zu6}fl8!NS@c2k(BXTeHI(oHF~KhrRkoc2xLbZFmAnfd2#3b80Fn!71n5p`z2n?jWG z?7Jxz`CEZd`)-Ow>@}k~XxCXwZxd#el!(YE?J5>jvn?u**tnTFo{JGQ z9C)}#zVJx@Et=1@)I#~vQhRv-N^F_I=%lzdEeKT9NCS+?e*uOg%MKU|d$w(Wp}0m# z@3eDanwctQ;o<0%Enf*9IP~I_-H^-^0hnm539P}cKw`3m$(;+i-l7TQ4 zGYiK8!78trT$6+A3y3F0@)9QE@6^%~GBN`4rG zmBbWfG>U*Em=wb>CMWF*<7~s~ict3>LY0ovB6~tECRl6^RGKfcADE(VMk_KkPCIu+ z{Msa*DE*@jvKpLQ3NaJ9<`WBZ(&EH8jhZ3bOhHx+lM5|m6}>*Eo_dtAe4r|S164v+sIZz=`q!j5@+s4NqLH7 z+0r#KqPTqt3s8MyW$vj-$z8MYnVk4*Hl0;PLKa1yd_9%BZo7*L*d3Ho)pMdmP7+J8 z8Ud1YVx9CuLRGL(y9N$bFV(Y;#*XhVIN`$KW2NtH5-xoHmMB!hFvsvuZ$S4_B(htOE}DZR)t zY7a(k0-WQWfI2HU`eorV% z&FUx(#0%O8P<4oQ(U>1jubIS zT`(*CpDZqpW~V1H)_%Cd>A6TZ#SWSF2p|VI+aOq553M_t!f1CCL& zUM$konOkP*3~$3l%Y;WYs(B@1V~Q0^L62pw?RYG-g{(&yc!My9T>6zDHwZ@#^|NVU z_>u;`I1QvnLv>|B%kZm5oKU_W(R{Q+0)v7}J8Qf#N7V4_l10I!%4W<#O;Vy2?z!G_ ztV-&bbG_A}>>k$2&SBP);Cm!};yW1_l{raG$4!$De$1`O$uP)!Iz)inB`6g$+SCgz5Y3dZVC1#|5O$C)rsi!|QccgL z?s~qP;<`O7E*fnkh><$vsl|BH#{nc=E^*?$h!L4)qNd-Ygr`T@teAFc9JU&Y7w$Y& zYJ5d0B;`&ut;T-`XRWbauVg;0`OT0140+4bZdw}ZN)~M@)hMh zkr(~vk$YJaZ<(-qhm#DsuD&I-&a(#+g;GivQUXg(xku^v&Y0e{6t7WYMkN9D^O(v{ z#Wb0fGco7I`d~dpdJxFwVUkV0+5M8xyM;iPvz*%2N-GOKib3t7`YL3*{}OL8mj_ zr&mneMy_7)o1PL$3Ol!E9*{n#rsLDhedRSRQTj1f&IT+QXJeXcBL)`i>(Je?ryM*z zZAYn&(RO{g<{{Ki$5tfsWS~{r%}G*x%_lf_f{$pxmy3g+7O=fxpCC$+p6kWYn-1+? zM{KC(&Q6-ho;f&jK7FkrNLl0!gS?lleQrT}z82?V5o^%5t32QQgBo{*h3B=Z&%!&m z&wF%klry&CK%YV9_PJ-uiab=q4;>Na;;TX?7B%I%AHKYm;^EqnR5G*Ggf~i~9bO9w z&<{xxML7&({{Jh0pw+O5JoQQFN>R#dETTj>B)ZmceF?FbSqC4|zz zUFC`)7z1MlF&a5v+Xd*-Mr*Rj7Ah0;ok%mag(BK7K#M)z!O;_qyY7XvX9qpeBN&D5FIwUda_uEE*pL%pBt>n5u0aK0lF(b|Ae?#LIR z;-R&5=xkRak4HAxqn))mx_8QRe$CkPwG4yh%bN71Dg;t!RX(&f&%Uox8Yy;3iIf4W z&ms>iuLD=?(Ap++ZZdt-@pE@ct{y!TC&gx!K^o$fySp0d^u{K~S5NbnV3 z^}q4x25rO6KU#Tu2bM46yI6P7zMUfB&IYI9uo=FDg|s#I(;ZGYPigk34swT{xLz5R zlf#Gg7X@r_L89{z>5 z-gQAQa)QYTma|DoTx7Z3xAberl}MlvJml)@bR8 zp(g!a*f1>y9=qGMVxsJPoqmjoy{nGW$`!7Th#2Fnyd)2M;JY8RBZm}op+6ZY_*`U_ zD0Q6|3}$Ac>;@y489UhbOgRgPBjP|zFGzWdl6lOY$SoUCyj7i!hT#LJ9{Uq~eAbVY z;@_8bj`HV;tn;@8A)wfU#1qm=$i}_>1vAk3=T^_y-SJ;VJSl9-L-K49#`Zm=y!w6x z-m6I-HAml*hh>A2KaH_P|K$AR#UfvB*7)n(H|%Qp@NT)?EL}mBUT@+L z3O#Elq^!?5%xr$7eD!}2u-rZ=+b&9KZwO~S+NYxQkJRFq%Z}*g?c0ad?tJ{bJexpJ zV?1Nf{QWzBMVW0f?isRA{C`>Sck=l;vMopRsKJso_8GEI&ylGj@J=9k7hX-%F8*sV zF}<2TEyji6KfIw#g>8wK(H|NAhet~}S`DEPL|8{I_6h}hEY=3|)yp*Q_;9>N8F`0u z%Da__6xM$Aly_$%-IlbsNtCD|WXFzO`_wkc9}pyY%jjkFN5+3Fl)Yn|hIn2nUO@Ij z6?d^RO&4`NcGY;)( zvTD1IuW9jxtj-oErdQLa$)V)|!Y$>c^hd^jp+j^0@tpVRm0{c4$LxN*KbyN!h&RZP zT34!mJLM1XQN$Pb>QsI?mtBZYdbwYNq+WQoKUP!z$zJYA2kU1W_UI57k#04^F27bU zcf420yNVq74xZ!B50N#NFN*fF6)hbZ(8T&hXM8V*>!h)GJlyq)(`U6YyLm8zW&Ys M0Y{7LoH|kf0L?=s_y7O^ literal 0 HcmV?d00001 diff --git a/doc/igraphlogo/igraph.svg.gz b/doc/igraphlogo/igraph.svg.gz new file mode 100644 index 0000000000000000000000000000000000000000..b9f3d21c40c26c7723aa22f5b5fae35320ff5343 GIT binary patch literal 6101 zcmV;`7b@rn239g`&78istLoPMt{T7mXu z#Sg3P!+LXfabkw?WU;!t++3~it}jmh=da(M=99(4Zh3dLyxH8XE>7+?Cx86rcQ60r z^mOrRyISs6SBv-S-P^^V@BZ;{xx8O3e*bp2yFWiWd;k9Zu&&^+*)#cghW_7!|+dZ6_!JM7U3@me0{rGU%%a5oY?w(eRXky8C&_%hCiRQVjP@4YVYhH`MA$@wYlUh zE>70h+vWY+p-E!m-Mrh~zuWz7^$5tI`@8F-TpzCPu`T8JD?he}-{9%XtJRwaQmMT7&C3Hkni%gf z%lp+OHB(7D$?QII>-I^V?(bqxxc7hi0Ld=S7s1BCWHT0gHb)6RDxrbS;ji&1&5su+ z$@^g}-sjK1v`*`Evt3`WA#RuKF#2RY$XytRl#@GPg*3W#8@P}&5!e}JB%dZ3aD%RX zSg+pCg8Y8@u&VF6UtYtYZ#LVDlds;$pZehY&Gu@wZI7b-*?Y8s(yw5Kab04hqf~lW!*Y<<;dm{Os*=_s#ki zdqfxgpYW^D(&@n-G0k;;slIl*QipvsYF%C4uE})vm)-j2=Fc3YE=yi{Rwr7Qan`Nl z7pYwq7Z@0Yu`lN;N8yulD}HqfMp^^dD_sc6-=+QIb9AVYglFF5gZVPW}Y_6CYH512AUN)(ddSQc~kiunQTf;mLJN3*wn@Gc7Fv`!laGiGA5JSx^p z?sbQHm`=%clP7*3K$$vIvw}P}p^yksU48i~j1ZDpgdkMS_Cm8nyTEUMJk|)ZgxURIl^zpiowH+_G8CY>IIRaSzbb%b9sLp>Zoc~ zLBy-J18-cEyFgc)4kA;h;=;->w^1w)at=l@Cvk4ZXt#bSc%yR9l$eEDJ$A%Kwc9Dq zaD~L_D^9}vlU%@w@(Dtbh6!*`at2LZW;r@sl_icf^*!lrev zIZH;-eGJ)4)1D5sjOth;a-0DkiEo4~011Y@6ul2NF}JAx55;m4N@N07;{`bz`r*tcTpxD9w8?bd4Ea47T5CO2dnvW#(fv8=Iq$QBRqV z#;fnd_i}8_#XvYkk`P4dn7ig;-A*v865Zv@xo%Y;^{E=gio_Dih;|bN^Ex97>ou{% zlE`<81~a8|GIo@)xjWR#C}Rh)SnHG%lG3Pacg)j<+D1@z6%xENm#~e+y3Hs}- z-0&%gz*aE&AuIVfbVxAFTyugL=k;KfH7A&J4@5EK0fLQ^w>$AoL1zxT&drk`qwqC* zV9pRp2?;R{Imr`p%z`=5^VG1)J<1w=Pl?z>N@9rj6{E!{ckcteNFzwz-?*`cAbA~Z zgJEXIv^McIo7hLE3~Are_6GJQ6gwY6^TV_c2+txECnLE;if}N0sN2?(n zIRqk=gJuoDyP;H}S1F2K7xGy7j+Z%KDw@w# zG?pGdC8;?B#jBo0>?#Gb+r9 zA!HV{cQK=^&`kAeGe5I3jmyN!Sct)2kJ6*C0banH+K*_~jnQP4+D+jqNr`v|328S) zvp-7BId{-a>Cw4wVi9>$dJ3JpDTrB!l%{r5P^EEvL|Me1+D&1GO^mr~sk0wlRGT-l5|r_?awq!C8vFp*B#n-Q)d3Tn?fwgjOK0%XGERZ@1_u?Jo|2nMgCSG z)V`Zy5qr&O4%&6r(%Xa?B_$#78~iOfwVa zD#i$<5oH5}+|wsQIkO)}2+BJq^5!^|^-wdIj)Pug@s{kPU^rxkPBIXtVrJo3AXw!! zlWTHteF5>LNM6DwG$~m?IGce%y`pf#>FS!9oQ+nSMI8!7v|a;RMad6?u#%Xfj7AZV z1e0PI#^j`3VVrGPoe}DOM5xkHT4Ybi#RQAZflBj5)&o=Y&1glY#%brSh+mt;6QzID zK~{rvOCe@L*L-4OPFkE8r%^Lxn<>bwVRE5`tiodfA&hdCUPf`mpHSFx&d48Eds z9i>O`R!o{ng27qotTD1}X$zZUE(oPz@D)lU(>Ah{P45q~xmE_)JdxHJi?=A|Z<+PrjbYU6gVA}5KZSd9QlIxhp2z$U|tX`;=Z}8MOx^Cjrj! zOhBEU_6cP@Gs+%7H3srqNK^AS7L-}W8H*7t)yj)AqOsa8CwwJyhu;&*QnNZr1NoM$ zu~egT)`9siA!2i zqfZgG7}9qOsd5AvJ?kF+_O{X zo}0|8;Y#XOq}dq6tr<^KMtN;}#7gH|E40@g$|*KonYh{duJN@n#Wqf|diKrpc}m# zbcUzlqGiIP8r8fKu`$JprJ&m~*LK_%+CtVX3_L-YLoWSFkQ0O>oBG)_Fnmb^pPdHM zqoKMop=J2hBTgvqM>HSpkieke(#{%B%n>y_yJS%?sj?Y!P?MBsg?p~I9IKN0&bi*| zP<9V%W#=$zN$@?AKJiWl-pZUL!`t-{kDaxHk-&|x9MZT)$RjjZL6K=DBN%fLFU-uT zn&YO)2S4W4y2H z78i}S5yVIx^3-Cy>Ei&BE|)m)Uc`t@Gf~s;QNq)sY*tJ=H4aM+#S3?yDm7kF3Q4(B zO{?+W!C7l;*DINKYrgr>?;%fl+DS`8otzFJtA;;ytjRGtG35_3j&ZUc?`upZH*qf2cJ)MVA!7v1vzS$XQFaf&UNW>xJzcA>n5H0X4u^Yn^|+sM@m zzUnEFq_A^q<`>fE)O36rxv#vYDM~-a%HHr=EB3}T*G3F1Sl6MuV^2AFdfJXs9i#2~ za?L}ipN_3a=FUK?w40Np_?mZc?gSsvfG-CJKQCZ=!#+WjB0blGqt_kUUq@`H=FU!< z$euYkaz4G*5Tq>fhC$v-);_nOJ+H;NSi~Cic9rMNKd5n6Sa=?*`YgP2`@BczMmb|E zHuMQ}Zl8OmtjI$({Lm3$EM65dv8XB6_3-jmiidMYQpwC#6W%C|c6co$kYlmkb+Lx^ za(msOp4S$th0pw+Nr`kQQFN>W>lB)Zp@Ci(1A)RA(RI0Dpw4_7#K5% z(a8SVEaeE1(4{&v;g;P(-bO%0iYJhWW#fo;JT5>U?d$u<5 zceciZftZQW)}-iW>dfleNknPK;B54v-p}>AiE2BX?}$XSHXxKc@*-3`w6+eN?MmeF z$Oe0~vo=TfPI=DPj6JVq7))R0q?f7?NTF5v(AqrvzDjAN*d--W2FyN-Jgi&?uGpcq zP3YWYde!lBbxDpMJrgIzW|cu2;+4BQ8tV09RxY(HiFD2>gvF|<>WOJpOaxpe>~jm1 zy6UBM%WOho>=8%2cWSX)eb`x--5lty`^VSLYwm|T9B>}e>`@)$4n1)_GAbvB59==q*y4gj z;ky_;poqK#LB9;;o6r&#hL@$X948vN#9;xkan1W@bb%}oGnpN0H_Njw=tWL2Il*!^ zDT#~Bc$Z5z^=!B2OsbCBc=F_WSw6Xgn(iX z5>H4gAshGh-GGA_YJ#RKD=FSx66-vN&h6bPdE@5qFkHwY>m)e$`5c1141w_98cHcn8G9O=9_pO z@0n8lrQ$+hiX#l*-zhEz)GVc^~ErLfzI$B;h9`S_a9Aq2{h3(tK-w1k^L&!p(a)#ObM6v2`{+HWNinI%S5_sQWi%0uY z0`Fs;eZI(uZr;3kSnbZo&x^6i0oB42Rx3Ycx9rX)7zRM=p69{rK`|M+MsN9!7tbcod0 z+!rXdW3e`vub-!A$6v<-k&&k{r#vNzNJj0~pK{@BiQAI)7KvgrgzVURYoFR8`BQ)- z_l2HEf8_m-g|fGe(<`1`>VPy{>U<-Qu~bi`ZQU!RLAR8yhznq-o*4``ZT$(JlHUn@?82O?|-KI za{SSo_vwXUyV}R>e!BOXyGe*H$bMQkseU`<59d*Q7x&^+e({!Fh@bRnzXeG>b8mmD zru>tA+L6xFFSh5=Aqpa=`mOr39XFK_5dpP#J_KPP5BCkcEqSNsiSx1aDgWk@cKktZd*n>^Zh7&Tv>U3X_2 b^Oxyk{UCq6o8SE(Ye?eC(NO>Z{9O!T literal 0 HcmV?d00001 diff --git a/doc/igraphlogo/igraph2.svg.gz b/doc/igraphlogo/igraph2.svg.gz new file mode 100644 index 0000000000000000000000000000000000000000..69fbc409b2719e3cece4161726cb81da1a8b1a53 GIT binary patch literal 1952 zcmV;R2VeLfiwFo~^~^~C18HY+VQ^?NE^~Hg0PR@YZrex_ea}~LnU@$VQtTVMo3T7t zOb}pz*_WMJ?BjqYTM{P{DUg(9`Rn&I#hYx(v3HV5u)7uvkyX`~I#qSLTGv0^Z#T}J zDXXl=7cTJw*D-lgq*=aNxc~h1qo-V_s^dJ3H$`q1ZeF-Q+`PH|i|08%mL{%E>Kw9q z?fjH~s*-qToOkQG-bM5I;c)P?R_GVyYJTN-9!6Got2d70VA;Hi(q!Sb_Pc$#Y3$Nu zZZ>9X^17N6pUmCCJQ>UryLfhIl483p@~Rn`S8qqgWxDLG?PdEJwD^$g=Y-kNF`C#ITr@@D)YOY1elt7-GPHrZ-j5ARvJaIs^`iD=%t9YlR%1U_p` z+iAxt9yY48EIOMeEfTx4g`2I)c((neOJJHDp`vxauql31eeuu5?Okxw}WIftkeYA+4o3az^&Y8oz-|pe96FGv0K2apl zcqWsE$CDvJD&Q-aw0T&#l5;=MoU6}I+HT7$%50SZ9wwk}mP|Z`1f{jmlmzl$W=51V=cD7x-LZxkra^b#RHh-<+wkT6mcB-)XJ5v?N z#;iULN#QuF64_X6Od011khdW9s(qH4GhM4FZhwaxQ4@W=rkmc~l>&lx4PrPm|x{B9YUa+vaeL-dM>{4fI>Nt(- zcu12@f&>e?!UMBL|M>9nrp?9cB#Hi2l%KlPbR26C-=eG+u6xsquhS#~7jEPFCfg!h zmPY>y?nA=%iZf$dyThcXX-jh=)yb13O}3dGH2n_XB)h}2z%|SEcUx?k(xHV?f~CexNBs0WgT6(&Ep0K>^qMko23u| zoq3e|iqcSus}AZ_=l^Ida4NI=cVGxD7-xDG;4{2=giD_=O~lNTLi(Ih!mrMV17|@% z`p8`G;(9&c+%O9yT!z{m#E|~oA)ql9l910hVZM|~apy-!l`lgsd#M%*=V4^rtXDTr!hN)ag`)4sy2y-dwGrEpIW(GG46ft_Hg zHEbuuZiUc9kC($Y$+;G0E&`qI`{K-RzRt*flZ|Y7#cyUi=0D_ znP@2ZC^T4)Al~6Pf=^>TN{6aGZjkTG0@PMz4<;g<@9y89-mNamZAEWRlDoLf;=DeS z9U9U)6IFF->SW!^P{h&Op!sWFy!Hf$$a|O;pG@>t3r#g`Ur$sSp|5080Yi!%wVF+q z8x(7FyRYl9?DrzeBd~@kJL#}Sv!`Xe%{IsAV`zgpyj z*_e0E>uWfa!5EgUaBL*W1hm%s9Bui@^>PnP&7Hxh^jYr_TPZ@t6??t!&C4dkSHwG6 z8sl6o%lLTO%wv?EPy|X%s;0hu9B6OCYtTFSVbT|6#O=$wr>|0D~fAGlH9>rc{ut%gHi51v)Mo;IKp! zFVTFVX`@$9DUNh9d%z9NlCU&s)*kBc-iWzbdL&-$mHMwCxQp!G}bC+7bwjWBy-=UmRP7&s_Y>}4#{{we^X18bu*XnqwK-aPaR z8ka@QW?pDpaK%J8!7bkwk_vs&M~-~W^XJbvYe#MStD^h5^XK^%=t7V5U3796I6(W7 m$soj;K` + +]> + + +Installation + + + This chapter describes building igraph from source code and installing it. + The source archive of the latest stable release is always available + from the igraph website. + igraph is also included in many Linux distributions, as well as several package + managers such as vcpkg (convenient on Windows), + MacPorts (macOS) and + Homebrew (macOS), which provide an easier + means of installation. If you decide to use them, please consult their documentation + on how to install packages. + + +
+ Prerequisites + + To build igraph from sources, you will need at least: + + + + + CMake 3.18 or later + + + + + C and C++ compilers + + + + + Visual Studio 2015 and later are supported. Earlier Visual Studio + versions may or may not work. + + + Certain features also require the following libraries: + + + + + libxml2, + required for GraphML support + + + + + igraph bundles a number of libraries for convenience. However, it is + preferable to use external versions of these libraries, which may + improve performance. These are: + + + + + GMP (the bundled + alternative is Mini-GMP) + + + + + GLPK (version 4.57 or later) + + + + + ARPACK + + + + + plfit + + + + + A library providing a + BLAS API + (available by default on macOS; + OpenBLAS is one + option on other systems) + + + + + A library providing a + LAPACK + API (available by default on macOS; + OpenBLAS is one + option on other systems) + + + + + When building the development version of igraph, + bison, flex and + git are also required. Released versions do not + require these tools. + + + To run the tests, diff is also required. + +
+ +
+ Installation +
+ General build instructions + + igraph uses a + CMake-based + build system. To compile it, + + + + + Enter the directory where the igraph sources are: + +$ cd igraph + + + + + + Create a new directory. This is where igraph will be built: + +$ mkdir build +$ cd build + + + + + + Run CMake, which will automatically configure igraph, and + report the configuration: + +$ cmake .. + + To set a non-default installation location, such as + /opt/local, use: + cmake .. -DCMAKE_INSTALL_PREFIX=/opt/local + + + + + Check the output carefully, and ensure that all features you + need are enabled. If CMake could not find certain libraries, + some features such as GraphML support may have been + automatically disabled. + + + + + There are several ways to adjust the configuration: + + + + + Run ccmake . on Unix-like systems or + cmake-gui on Windows for a convenient + interface. + + + + + Simply edit the CMakeCache.txt file. + Some of the relevant options are listed below. + + + + + + + Once the configuration has been adjusted, run + cmake .. again. + + + + + Once igraph has been successfully configured, it can be built, + tested and installed using: + +$ cmake --build . +$ cmake --build . --target check +$ cmake --install . + + + + +
+ +
+ Specific instructions for Windows +
+ Microsoft Visual Studio + + With Visual Studio, the steps to build igraph are generally the + same as above. However, since the Visual Studio CMake generator is + a multi-configuration one, we must specify the configuration + (typically Release or Debug) with each build command using the + --config option: + + +mkdir build +cd build +cmake .. +cmake --build . --config Release +cmake --build . --target check --config Release + + + When building the development version, bison + and flex must be available on the system. + winflexbison + for Bison version 3.x can be useful for this purpose—make sure + that the executables are in the system PATH. + The easiest installation option is probably by installing + winflexbison3 from the + Chocolatey + package manager. + +
+ vcpkg + + Most external dependencies can be conveniently installed using + vcpkg. + Note that igraph bundles all dependencies + except libxml2, which is needed for GraphML + support. + + + In order to use vcpkg integrate it in the build environment by executing + vcpkg.exe integrate install on the command line. + When configuring igraph, point CMake to the correct + vcpkg.cmake file using -DCMAKE_TOOLCHAIN_FILE=..., + as instructed. + + + Additionally, it might be that you need to set the appropriate + so-called triplet using + -DVCPKG_TARGET_TRIPLET when running + cmake, for exampling, setting it to + x64-windows when using shared builds of packages or + x64-windows-static when using static builds. + Similarly, you also need to specify this target triplet when + installing packages. For example, to install + libxml2 as a shared library, use + vcpkg.exe install libxml2:x64-windows and to + install libxml2 as a static library, use + vcpkg.exe install libxml2:x64-windows-static. + In addition, there is the possibility to use a static library + with dynamic runtime linking using the + x64-windows-static-md triplet. + +
+
+
+ MSYS2 + + MSYS2 can be installed from msys2.org. After installing MSYS2, + ensure that it is up to date by opening a terminal and running + pacman -Syuu. + + + The instructions below assume that you want to compile for a 64-bit + target. + + + Install the following packages using pacman -S. + + + + + Minimal requirements: + mingw-w64-x86_64-toolchain, + mingw-w64-x86_64-cmake. + + + + + Optional dependencies that enable certain features: + mingw-w64-x86_64-gmp, + mingw-w64-x86_64-libxml2 + + + + + Optional external libraries for better performance: + mingw-w64-x86_64-openblas, + mingw-w64-x86_64-arpack, + mingw-w64-x86_64-glpk + + + + + Only needed for running the tests: diffutils + + + + + Required only when building the development version: + git, bison, + flex + + + + + The following command will install of these at once: + + +pacman -S \ + mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake \ + mingw-w64-x86_64-gmp mingw-w64-x86_64-libxml2 \ + mingw-w64-x86_64-openblas mingw-w64-x86_64-arpack \ + mingw-w64-x86_64-glpk diffutils git bison flex + + + In order to build igraph, follow the General + build instructions above, paying attention to the + following: + + + + + When using MSYS2, start the MSYS2 MinGW 64-bit + terminal, and not the MSYS2 + MSYS one. + + + + + Be sure to install the mingw-w64-x86_64-cmake + package and not the cmake one. The latter + will not work. + + + + + When running cmake, pass the option + -G"MSYS Makefiles". + + + + + Note that ccmake is not currently available. + cmake-gui can be used only if the + mingw-w64-x86_64-qt5 package is installed. + + + +
+
+ +
+ Notable configuration options + + The following options may be set to ON or + OFF. Some of them have an AUTO + setting, which chooses a reasonable default based on what libraries + are available on the current system. + + + + + igraph bundles some of its dependencies for convenience. The + IGRAPH_USE_INTERNAL_XXX flags control whether + these should be used instead of external versions. Set them to + ON to use the bundled + (vendored) versions. Generally, external versions + are preferable as they may be newer and usually provide better + performance. + + + + + IGRAPH_GLPK_SUPPORT: whether to make use of + the + GLPK + library. Some features, such as finding a minimum feedback arc + set or finding communities through exact modularity + optimization, require this. + + + + + IGRAPH_GRAPHML_SUPPORT: whether to enable + support for reading and writing + GraphML + files. Requires the + libxml2 library. + + + + + IGRAPH_OPENMP_SUPPORT: whether to use OpenMP + parallelization to accelerate certain functions such as PageRank + calculation. Compiler support is required. + + + + + IGRAPH_ENABLE_LTO: whether to build igraph + with link-time optimization, which improves performance. Not + supported with all compilers. + + + + + IGRAPH_ENABLE_TLS: whether to enable + thread-local storage. Required when using igraph from multiple + threads. + + + + + IGRAPH_WARNINGS_AS_ERRORS: whether to treat + compiler warnings as errors. We strive to eliminate all compiler + warnings during development so this switch is turned on by default. + If your compiler prints warnings for some parts of the code that we + did not anticipate, you can turn off this option to prevent the + warnings from stopping the compilation. + + + + + BUILD_SHARED_LIBS: + whether to build a shared library instead of a static one. + + + + + BLA_VENDOR: controls which library to use for + BLAS + and + LAPACK + functionality. + + + + + CMAKE_INSTALL_PREFIX: + the location where igraph will be installed. + + + +
+
+ +
+ Building the documentation + + Most users will not need to build the documentation, as the release + tarball contains pre-built HTML documentation in the doc + directory. + + + To build the documentation for the development version, simply build the + html, pdf or info + targets for the HTML, PDF and Info versions of the documentation, + respectively. + + +$ cmake --build . --target html + + + Building the HTML documentation requires Python 3, xmlto + and source-highlight. Building the PDF documentation also + requires xsltproc, xmllint and + fop. Building the Texinfo documentation also requires + the docbook2X package, xmllint and + makeinfo. + +
+ +
+ Notes for package maintainers + + This section is for people who package igraph for Linux distros or + other package managers. Please read it carefully before packaging + igraph. + +
+ Auto-detection of dependencies + + igraph bundles several of its dependencies (or simplified versions + of its dependencies). During configuration time, it checks whether + each dependency is present on the system. If yes, it uses it. + Otherwise, it falls back to the bundled (vendored) + version. In order to make configuration as deterministic as + possible, you may want to disable this auto-detection. To do so, set + each of the IGRAPH_USE_INTERNAL_XXX options + described above. Additionally, set BLA_VENDOR to + use the BLAS and LAPACK implementations of your choice. This should + be the same BLAS and LAPACK library that igraph's other dependencies + (e.g., ARPACK) are linked against. + + + For example, to force igraph to use external versions of all + dependencies except plfit, and to use OpenBLAS for BLAS/LAPACK, use + + + +$ cmake .. \ + -DIGRAPH_USE_INTERNAL_BLAS=OFF \ + -DIGRAPH_USE_INTERNAL_LAPACK=OFF \ + -DIGRAPH_USE_INTERNAL_ARPACK=OFF \ + -DIGRAPH_USE_INTERNAL_GLPK=OFF \ + -DIGRAPH_USE_INTERNAL_GMP=OFF \ + -DIGRAPH_USE_INTERNAL_PLFIT=ON \ + -DBLA_VENDOR=OpenBLAS \ + -DIGRAPH_GRAPHML_SUPPORT=ON + + +
+
+ Shared and static builds + + On Windows, shared and static builds should not be installed in the same + location. If you decide to do so anyway, keep in mind the following: + Both builds contain an igraph.lib file. The static one + should be renamed to avoid conflict. The headers from the static build + are incompatible with the shared library. The headers from the shared build + may be used with the static library, but IGRAPH_STATIC + must be defined when compiling programs that will link to igraph statically. + + + These issues do not affect Unix-like systems. + +
+
+ Cross-compiling + + When building igraph with an internal ARPACK, LAPACK or BLAS, it + makes use of f2c, which compiles and runs the arithchk + program at build time to detect the floating point characteristics of the + current system. It writes the results into the arith.h + header. However, running this program is not possible when cross-compiling + without providing a userspace emulator that can run executables of the + target platform on the host system. Therefore, when cross-compiling, you + either need to provide such an emulator with the + CMAKE_CROSSCOMPILING_EMULATOR option, or you need to + specify a pre-generated version of the arith.h header + file through the F2C_EXTERNAL_ARITH_HEADER + CMake option. An example version of this header follows for the + x86_64 and arm64 target architectures on macOS. Warning: Do not use this + version of arith.h on other systems or architectures. + + + +#define IEEE_8087 +#define Arith_Kind_ASL 1 +#define Long int +#define Intcast (int)(long) +#define Double_Align +#define X64_bit_pointers +#define NANCHECK +#define QNaN0 0x0 +#define QNaN1 0x7ff80000 + + + + igraph also checks whether the endianness of uint64_t + matches the endianness of double on the platform + being compiled. This is needed to ensure that certain functions in igraph's + random number generator work properly. However, it is not possible to + execute this check when cross-compiling without an emulator, so in this + case igraph simply assumes that the endianness matches (which is the case + for the vast majority of platforms anyway). The only case where you might + run into problems is when you cross-compile for Apple Silicon + (arm64) from an Intel-based Mac, in which case CMake + might not realize that you are cross-compiling and will try to execute + the check anyway. You can work around this by setting + IEEE754_DOUBLE_ENDIANNESS_MATCHES to ON + explicitly before invoking CMake. + + + Providing an emulator in CMAKE_CROSSCOMPILING_EMULATOR + has the added benefit that you can run the compiled unit tests on the + host platform. We have experimented with cross-compiling to 64-bit ARM + CPUs (aarch64) on 64-bit Intel CPUs (amd64), + and we can confirm that using qemu-aarch64 works as a + cross-compiling emulator in this setup. + +
+
+ Additional notes + + + + As of igraph 0.10, there is no tangible benefit to using an + external GMP, as igraph does not yet use GMP in any + performance-critical way. The bundled Mini-GMP is sufficient. + + + + + Link-time optimization noticeably improves the performance of + some igraph functions. To enable it, use + -DIGRAPH_ENABLE_LTO=ON. + The AUTO setting is also supported, and will + enable link-time optimization only if the current compiler + supports it. Note that this is detected by CMake, and the + detection is not always accurate. + + + + + We saw occasional hangs on Windows when igraph was built for a + 32-bit target with MinGW and linked to OpenBLAS. We believe this + to be an issue with OpenBLAS, not igraph. On this platform, you + may want to opt for a different BLAS/LAPACK or the bundled + BLAS/LAPACK. + + + +
+
+ +
diff --git a/doc/introduction.xml b/doc/introduction.xml new file mode 100644 index 0000000..18dc1a6 --- /dev/null +++ b/doc/introduction.xml @@ -0,0 +1,109 @@ + + +]> + + +Introduction + + +igraph is a library for creating and manipulating graphs. +You can look at it in two ways: first, igraph contains the implementation +of quite a lot of graph algorithms. These include classic graph +algorithms like graph isomorphism, graph girth and connectivity and +also the new wave graph algorithms like transitivity, graph motifs and +community structure detection. Skim through the table of contents +or the index of this book to get an impression of what is available. + + +Second, igraph provides a platform for developing and/or +implementing graph algorithms. It has an efficient data structure +for representing graphs, and a number of other data structures like +flexible vectors, stacks, heaps, queues, adjacency lists that are useful for implementing graph algorithms. In fact these data structures evolved along with the +implementation of the classic and non-classic graph algorithms which +make up the major part of the igraph library. This way, they were fine-tuned +and checked for correctness several times. + + + +Our main goal with developing igraph was to create a graph library +which is efficient on large, but not extremely large graphs. More +precisely, it is assumed that the graph(s) fit into the physical +memory of the computer. Nowadays this means graphs with +several million vertices and/or edges. Our definition of efficient is +that it runs fast, both in theory and (more importantly) in practice. + + + +We believe that one of the big strengths of igraph is that it can be +embedded into a higher-level language or environment. Three such +embeddings (or interfaces if you look at them another way) +are currently being developed by us: an R +package, a Python extension module, and a Mathematica (Wolfram Language) package. Others are +likely to come. High level languages such as R or Python make it +possible to use graph routines with much greater comfort, without +actually writing a single line of C code. They have some, usually very +small, speed penalty compared to the C version, but add ease of use and much +flexibility. This manual, however, covers only the C library. If you +want to use Python, R or the Wolfram Language, please see the documentation written +specifically for these interfaces and come back here only if you are +interested in some detail which is not covered in those documents. + + + +We still consider igraph as a child project. It has much room for +development and we are sure that it will improve a lot in the near +future. Any feedback we can get from the users is very important for +us, as most of the time these questions and comments guide us in what +to add and what to improve. + + + +igraph is open source and distributed under the terms of the GNU GPL +version 2 or (at your option) any later version. +We strongly believe that all the algorithms used in science, let that +be graph theory or not, should have an efficient open-source +implementation allowing use and modification for anyone. + + +
&igraph; is free software + + igraph library + + Copyright (C) 2003-2004 Gábor Csárdi <csardi.gabor@gmail.com> + + Copyright (C) 2005-2019 Gábor Csárdi <csardi.gabor@gmail.com> and Tamás Nepusz <ntamas@gmail.com> + + Copyright (C) 2020-2023 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. + +
+ +
Citing &igraph; + +To cite &igraph; in publications, please use the following +reference: + +Gábor Csárdi, Tamás Nepusz: The igraph software package for complex network +research. InterJournal Complex Systems, 1695, 2006. + +The igraph C library is assigned the DOI 10.5281/zenodo.3630268 on Zenodo. + + +
+ +
diff --git a/doc/isomorphism.xxml b/doc/isomorphism.xxml new file mode 100644 index 0000000..20c415a --- /dev/null +++ b/doc/isomorphism.xxml @@ -0,0 +1,72 @@ + + +]> + + +Graph isomorphism + +
The simple interface + + + +
+ +
The BLISS algorithm + + + + + + + + +
Deprecated aliases + +
+ +
+ +
The VF2 algorithm + + + + + + + + + + + + +
Deprecated aliases + + +
+ +
+ +
The LAD algorithm + + +
+ +
Functions for small graphs + + + + +
+ +
Utility functions + + +
+ +
Deprecated functions + +
+ +
diff --git a/doc/iterators.xxml b/doc/iterators.xxml new file mode 100644 index 0000000..09888c0 --- /dev/null +++ b/doc/iterators.xxml @@ -0,0 +1,108 @@ + + +]> + + +Vertex and edge selectors and sequences, iterators + +
+ +
+ +
Vertex selector constructors + + + + + + + + + + +
+ +
Generic vertex selector operations + + + + + +
+ +
Immediate vertex selectors + + + + + +
+ +
Vertex iterators + + +
+ + + + + +
+ +
Edge selector constructors + + + + + + + + + + + +
+ +
Immediate edge selectors + + + + + +
+ +
Generic edge selector operations + + + + + + +
+ +
Edge iterators + + +
+ + + + + +
+ +
Deprecated functions + + + + +
+ + + +
diff --git a/doc/layout.xxml b/doc/layout.xxml new file mode 100644 index 0000000..e75a553 --- /dev/null +++ b/doc/layout.xxml @@ -0,0 +1,56 @@ + + +]> + + +Generating layouts for graph drawing + +
2D layout generators + + + + + + + +
The DrL layout generator + + + + + + +
+ + + + + + +
+ +
Layouts for trees and acyclic graphs + + + + + + +
+ +
3D layout generators + + + + + + +
+ +
Merging layouts + +
+ +
diff --git a/doc/licenses.xml b/doc/licenses.xml new file mode 100644 index 0000000..45fa97b --- /dev/null +++ b/doc/licenses.xml @@ -0,0 +1,14 @@ + + +]> + + +Licenses for igraph and this manual + + + + + diff --git a/doc/licenses/Licence_CeCILL-B_V1-en.txt b/doc/licenses/Licence_CeCILL-B_V1-en.txt new file mode 100644 index 0000000..c02c70e --- /dev/null +++ b/doc/licenses/Licence_CeCILL-B_V1-en.txt @@ -0,0 +1,515 @@ + +CeCILL-B FREE SOFTWARE LICENSE AGREEMENT + + + Notice + +This Agreement is a Free Software license agreement that is the result +of discussions between its authors in order to ensure compliance with +the two main principles guiding its drafting: + + * firstly, compliance with the principles governing the distribution + of Free Software: access to source code, broad rights granted to + users, + * secondly, the election of a governing law, French law, with which + it is conformant, both as regards the law of torts and + intellectual property law, and the protection that it offers to + both authors and holders of the economic rights over software. + +The authors of the CeCILL-B (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre]) +license are: + +Commissariat à l'Energie Atomique - CEA, a public scientific, technical +and industrial research establishment, having its principal place of +business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France. + +Centre National de la Recherche Scientifique - CNRS, a public scientific +and technological establishment, having its principal place of business +at 3 rue Michel-Ange, 75794 Paris cedex 16, France. + +Institut National de Recherche en Informatique et en Automatique - +INRIA, a public scientific and technological establishment, having its +principal place of business at Domaine de Voluceau, Rocquencourt, BP +105, 78153 Le Chesnay cedex, France. + + + Preamble + +This Agreement is an open source software license intended to give users +significant freedom to modify and redistribute the software licensed +hereunder. + +The exercising of this freedom is conditional upon a strong obligation +of giving credits for everybody that distributes a software +incorporating a software ruled by the current license so as all +contributions to be properly identified and acknowledged. + +In consideration of access to the source code and the rights to copy, +modify and redistribute granted by the license, users are provided only +with a limited warranty and the software's author, the holder of the +economic rights, and the successive licensors only have limited liability. + +In this respect, the risks associated with loading, using, modifying +and/or developing or reproducing the software by the user are brought to +the user's attention, given its Free Software status, which may make it +complicated to use, with the result that its use is reserved for +developers and experienced professionals having in-depth computer +knowledge. Users are therefore encouraged to load and test the +suitability of the software as regards their requirements in conditions +enabling the security of their systems and/or data to be ensured and, +more generally, to use and operate it in the same conditions of +security. This Agreement may be freely reproduced and published, +provided it is not altered, and that no provisions are either added or +removed herefrom. + +This Agreement may apply to any or all software for which the holder of +the economic rights decides to submit the use thereof to its provisions. + + + Article 1 - DEFINITIONS + +For the purpose of this Agreement, when the following expressions +commence with a capital letter, they shall have the following meaning: + +Agreement: means this license agreement, and its possible subsequent +versions and annexes. + +Software: means the software in its Object Code and/or Source Code form +and, where applicable, its documentation, "as is" when the Licensee +accepts the Agreement. + +Initial Software: means the Software in its Source Code and possibly its +Object Code form and, where applicable, its documentation, "as is" when +it is first distributed under the terms and conditions of the Agreement. + +Modified Software: means the Software modified by at least one +Contribution. + +Source Code: means all the Software's instructions and program lines to +which access is required so as to modify the Software. + +Object Code: means the binary files originating from the compilation of +the Source Code. + +Holder: means the holder(s) of the economic rights over the Initial +Software. + +Licensee: means the Software user(s) having accepted the Agreement. + +Contributor: means a Licensee having made at least one Contribution. + +Licensor: means the Holder, or any other individual or legal entity, who +distributes the Software under the Agreement. + +Contribution: means any or all modifications, corrections, translations, +adaptations and/or new functions integrated into the Software by any or +all Contributors, as well as any or all Internal Modules. + +Module: means a set of sources files including their documentation that +enables supplementary functions or services in addition to those offered +by the Software. + +External Module: means any or all Modules, not derived from the +Software, so that this Module and the Software run in separate address +spaces, with one calling the other when they are run. + +Internal Module: means any or all Module, connected to the Software so +that they both execute in the same address space. + +Parties: mean both the Licensee and the Licensor. + +These expressions may be used both in singular and plural form. + + + Article 2 - PURPOSE + +The purpose of the Agreement is the grant by the Licensor to the +Licensee of a non-exclusive, transferable and worldwide license for the +Software as set forth in Article 5 hereinafter for the whole term of the +protection granted by the rights over said Software. + + + Article 3 - ACCEPTANCE + +3.1 The Licensee shall be deemed as having accepted the terms and +conditions of this Agreement upon the occurrence of the first of the +following events: + + * (i) loading the Software by any or all means, notably, by + downloading from a remote server, or by loading from a physical + medium; + * (ii) the first time the Licensee exercises any of the rights + granted hereunder. + +3.2 One copy of the Agreement, containing a notice relating to the +characteristics of the Software, to the limited warranty, and to the +fact that its use is restricted to experienced users has been provided +to the Licensee prior to its acceptance as set forth in Article 3.1 +hereinabove, and the Licensee hereby acknowledges that it has read and +understood it. + + + Article 4 - EFFECTIVE DATE AND TERM + + + 4.1 EFFECTIVE DATE + +The Agreement shall become effective on the date when it is accepted by +the Licensee as set forth in Article 3.1. + + + 4.2 TERM + +The Agreement shall remain in force for the entire legal term of +protection of the economic rights over the Software. + + + Article 5 - SCOPE OF RIGHTS GRANTED + +The Licensor hereby grants to the Licensee, who accepts, the following +rights over the Software for any or all use, and for the term of the +Agreement, on the basis of the terms and conditions set forth hereinafter. + +Besides, if the Licensor owns or comes to own one or more patents +protecting all or part of the functions of the Software or of its +components, the Licensor undertakes not to enforce the rights granted by +these patents against successive Licensees using, exploiting or +modifying the Software. If these patents are transferred, the Licensor +undertakes to have the transferees subscribe to the obligations set +forth in this paragraph. + + + 5.1 RIGHT OF USE + +The Licensee is authorized to use the Software, without any limitation +as to its fields of application, with it being hereinafter specified +that this comprises: + + 1. permanent or temporary reproduction of all or part of the Software + by any or all means and in any or all form. + + 2. loading, displaying, running, or storing the Software on any or + all medium. + + 3. entitlement to observe, study or test its operation so as to + determine the ideas and principles behind any or all constituent + elements of said Software. This shall apply when the Licensee + carries out any or all loading, displaying, running, transmission + or storage operation as regards the Software, that it is entitled + to carry out hereunder. + + + 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS + +The right to make Contributions includes the right to translate, adapt, +arrange, or make any or all modifications to the Software, and the right +to reproduce the resulting software. + +The Licensee is authorized to make any or all Contributions to the +Software provided that it includes an explicit notice that it is the +author of said Contribution and indicates the date of the creation thereof. + + + 5.3 RIGHT OF DISTRIBUTION + +In particular, the right of distribution includes the right to publish, +transmit and communicate the Software to the general public on any or +all medium, and by any or all means, and the right to market, either in +consideration of a fee, or free of charge, one or more copies of the +Software by any means. + +The Licensee is further authorized to distribute copies of the modified +or unmodified Software to third parties according to the terms and +conditions set forth hereinafter. + + + 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION + +The Licensee is authorized to distribute true copies of the Software in +Source Code or Object Code form, provided that said distribution +complies with all the provisions of the Agreement and is accompanied by: + + 1. a copy of the Agreement, + + 2. a notice relating to the limitation of both the Licensor's + warranty and liability as set forth in Articles 8 and 9, + +and that, in the event that only the Object Code of the Software is +redistributed, the Licensee allows effective access to the full Source +Code of the Software at a minimum during the entire period of its +distribution of the Software, it being understood that the additional +cost of acquiring the Source Code shall not exceed the cost of +transferring the data. + + + 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE + +If the Licensee makes any Contribution to the Software, the resulting +Modified Software may be distributed under a license agreement other +than this Agreement subject to compliance with the provisions of Article +5.3.4. + + + 5.3.3 DISTRIBUTION OF EXTERNAL MODULES + +When the Licensee has developed an External Module, the terms and +conditions of this Agreement do not apply to said External Module, that +may be distributed under a separate license agreement. + + + 5.3.4 CREDITS + +Any Licensee who may distribute a Modified Software hereby expressly +agrees to: + + 1. indicate in the related documentation that it is based on the + Software licensed hereunder, and reproduce the intellectual + property notice for the Software, + + 2. ensure that written indications of the Software intended use, + intellectual property notice and license hereunder are included in + easily accessible format from the Modified Software interface, + + 3. mention, on a freely accessible website describing the Modified + Software, at least throughout the distribution term thereof, that + it is based on the Software licensed hereunder, and reproduce the + Software intellectual property notice, + + 4. where it is distributed to a third party that may distribute a + Modified Software without having to make its source code + available, make its best efforts to ensure that said third party + agrees to comply with the obligations set forth in this Article . + +If the Software, whether or not modified, is distributed with an +External Module designed for use in connection with the Software, the +Licensee shall submit said External Module to the foregoing obligations. + + + 5.3.5 COMPATIBILITY WITH THE CeCILL AND CeCILL-C LICENSES + +Where a Modified Software contains a Contribution subject to the CeCILL +license, the provisions set forth in Article 5.3.4 shall be optional. + +A Modified Software may be distributed under the CeCILL-C license. In +such a case the provisions set forth in Article 5.3.4 shall be optional. + + + Article 6 - INTELLECTUAL PROPERTY + + + 6.1 OVER THE INITIAL SOFTWARE + +The Holder owns the economic rights over the Initial Software. Any or +all use of the Initial Software is subject to compliance with the terms +and conditions under which the Holder has elected to distribute its work +and no one shall be entitled to modify the terms and conditions for the +distribution of said Initial Software. + +The Holder undertakes that the Initial Software will remain ruled at +least by this Agreement, for the duration set forth in Article 4.2. + + + 6.2 OVER THE CONTRIBUTIONS + +The Licensee who develops a Contribution is the owner of the +intellectual property rights over this Contribution as defined by +applicable law. + + + 6.3 OVER THE EXTERNAL MODULES + +The Licensee who develops an External Module is the owner of the +intellectual property rights over this External Module as defined by +applicable law and is free to choose the type of agreement that shall +govern its distribution. + + + 6.4 JOINT PROVISIONS + +The Licensee expressly undertakes: + + 1. not to remove, or modify, in any manner, the intellectual property + notices attached to the Software; + + 2. to reproduce said notices, in an identical manner, in the copies + of the Software modified or not. + +The Licensee undertakes not to directly or indirectly infringe the +intellectual property rights of the Holder and/or Contributors on the +Software and to take, where applicable, vis-à-vis its staff, any and all +measures required to ensure respect of said intellectual property rights +of the Holder and/or Contributors. + + + Article 7 - RELATED SERVICES + +7.1 Under no circumstances shall the Agreement oblige the Licensor to +provide technical assistance or maintenance services for the Software. + +However, the Licensor is entitled to offer this type of services. The +terms and conditions of such technical assistance, and/or such +maintenance, shall be set forth in a separate instrument. Only the +Licensor offering said maintenance and/or technical assistance services +shall incur liability therefor. + +7.2 Similarly, any Licensor is entitled to offer to its licensees, under +its sole responsibility, a warranty, that shall only be binding upon +itself, for the redistribution of the Software and/or the Modified +Software, under terms and conditions that it is free to decide. Said +warranty, and the financial terms and conditions of its application, +shall be subject of a separate instrument executed between the Licensor +and the Licensee. + + + Article 8 - LIABILITY + +8.1 Subject to the provisions of Article 8.2, the Licensee shall be +entitled to claim compensation for any direct loss it may have suffered +from the Software as a result of a fault on the part of the relevant +Licensor, subject to providing evidence thereof. + +8.2 The Licensor's liability is limited to the commitments made under +this Agreement and shall not be incurred as a result of in particular: +(i) loss due the Licensee's total or partial failure to fulfill its +obligations, (ii) direct or consequential loss that is suffered by the +Licensee due to the use or performance of the Software, and (iii) more +generally, any consequential loss. In particular the Parties expressly +agree that any or all pecuniary or business loss (i.e. loss of data, +loss of profits, operating loss, loss of customers or orders, +opportunity cost, any disturbance to business activities) or any or all +legal proceedings instituted against the Licensee by a third party, +shall constitute consequential loss and shall not provide entitlement to +any or all compensation from the Licensor. + + + Article 9 - WARRANTY + +9.1 The Licensee acknowledges that the scientific and technical +state-of-the-art when the Software was distributed did not enable all +possible uses to be tested and verified, nor for the presence of +possible defects to be detected. In this respect, the Licensee's +attention has been drawn to the risks associated with loading, using, +modifying and/or developing and reproducing the Software which are +reserved for experienced users. + +The Licensee shall be responsible for verifying, by any or all means, +the suitability of the product for its requirements, its good working +order, and for ensuring that it shall not cause damage to either persons +or properties. + +9.2 The Licensor hereby represents, in good faith, that it is entitled +to grant all the rights over the Software (including in particular the +rights set forth in Article 5). + +9.3 The Licensee acknowledges that the Software is supplied "as is" by +the Licensor without any other express or tacit warranty, other than +that provided for in Article 9.2 and, in particular, without any warranty +as to its commercial value, its secured, safe, innovative or relevant +nature. + +Specifically, the Licensor does not warrant that the Software is free +from any error, that it will operate without interruption, that it will +be compatible with the Licensee's own equipment and software +configuration, nor that it will meet the Licensee's requirements. + +9.4 The Licensor does not either expressly or tacitly warrant that the +Software does not infringe any third party intellectual property right +relating to a patent, software or any other property right. Therefore, +the Licensor disclaims any and all liability towards the Licensee +arising out of any or all proceedings for infringement that may be +instituted in respect of the use, modification and redistribution of the +Software. Nevertheless, should such proceedings be instituted against +the Licensee, the Licensor shall provide it with technical and legal +assistance for its defense. Such technical and legal assistance shall be +decided on a case-by-case basis between the relevant Licensor and the +Licensee pursuant to a memorandum of understanding. The Licensor +disclaims any and all liability as regards the Licensee's use of the +name of the Software. No warranty is given as regards the existence of +prior rights over the name of the Software or as regards the existence +of a trademark. + + + Article 10 - TERMINATION + +10.1 In the event of a breach by the Licensee of its obligations +hereunder, the Licensor may automatically terminate this Agreement +thirty (30) days after notice has been sent to the Licensee and has +remained ineffective. + +10.2 A Licensee whose Agreement is terminated shall no longer be +authorized to use, modify or distribute the Software. However, any +licenses that it may have granted prior to termination of the Agreement +shall remain valid subject to their having been granted in compliance +with the terms and conditions hereof. + + + Article 11 - MISCELLANEOUS + + + 11.1 EXCUSABLE EVENTS + +Neither Party shall be liable for any or all delay, or failure to +perform the Agreement, that may be attributable to an event of force +majeure, an act of God or an outside cause, such as defective +functioning or interruptions of the electricity or telecommunications +networks, network paralysis following a virus attack, intervention by +government authorities, natural disasters, water damage, earthquakes, +fire, explosions, strikes and labor unrest, war, etc. + +11.2 Any failure by either Party, on one or more occasions, to invoke +one or more of the provisions hereof, shall under no circumstances be +interpreted as being a waiver by the interested Party of its right to +invoke said provision(s) subsequently. + +11.3 The Agreement cancels and replaces any or all previous agreements, +whether written or oral, between the Parties and having the same +purpose, and constitutes the entirety of the agreement between said +Parties concerning said purpose. No supplement or modification to the +terms and conditions hereof shall be effective as between the Parties +unless it is made in writing and signed by their duly authorized +representatives. + +11.4 In the event that one or more of the provisions hereof were to +conflict with a current or future applicable act or legislative text, +said act or legislative text shall prevail, and the Parties shall make +the necessary amendments so as to comply with said act or legislative +text. All other provisions shall remain effective. Similarly, invalidity +of a provision of the Agreement, for any reason whatsoever, shall not +cause the Agreement as a whole to be invalid. + + + 11.5 LANGUAGE + +The Agreement is drafted in both French and English and both versions +are deemed authentic. + + + Article 12 - NEW VERSIONS OF THE AGREEMENT + +12.1 Any person is authorized to duplicate and distribute copies of this +Agreement. + +12.2 So as to ensure coherence, the wording of this Agreement is +protected and may only be modified by the authors of the License, who +reserve the right to periodically publish updates or new versions of the +Agreement, each with a separate number. These subsequent versions may +address new issues encountered by Free Software. + +12.3 Any Software distributed under a given version of the Agreement may +only be subsequently distributed under the same version of the Agreement +or a subsequent version. + + + Article 13 - GOVERNING LAW AND JURISDICTION + +13.1 The Agreement is governed by French law. The Parties agree to +endeavor to seek an amicable solution to any disagreements or disputes +that may arise during the performance of the Agreement. + +13.2 Failing an amicable solution within two (2) months as from their +occurrence, and unless emergency proceedings are necessary, the +disagreements or disputes shall be referred to the Paris Courts having +jurisdiction, by the more diligent Party. + + +Version 1.0 dated 2006-09-05. diff --git a/doc/licenses/Licence_CeCILL-B_V1-fr.txt b/doc/licenses/Licence_CeCILL-B_V1-fr.txt new file mode 100644 index 0000000..d9bb943 --- /dev/null +++ b/doc/licenses/Licence_CeCILL-B_V1-fr.txt @@ -0,0 +1,519 @@ + +CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B + + + Avertissement + +Ce contrat est une licence de logiciel libre issue d'une concertation +entre ses auteurs afin que le respect de deux grands principes préside à +sa rédaction: + + * d'une part, le respect des principes de diffusion des logiciels + libres: accès au code source, droits étendus conférés aux + utilisateurs, + * d'autre part, la désignation d'un droit applicable, le droit + français, auquel elle est conforme, tant au regard du droit de la + responsabilité civile que du droit de la propriété intellectuelle + et de la protection qu'il offre aux auteurs et titulaires des + droits patrimoniaux sur un logiciel. + +Les auteurs de la licence CeCILL-B (pour Ce[a] C[nrs] I[nria] L[ogiciel] +L[ibre]) sont: + +Commissariat à l'Energie Atomique - CEA, établissement public de +recherche à caractère scientifique, technique et industriel, dont le +siège est situé 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris. + +Centre National de la Recherche Scientifique - CNRS, établissement +public à caractère scientifique et technologique, dont le siège est +situé 3 rue Michel-Ange, 75794 Paris cedex 16. + +Institut National de Recherche en Informatique et en Automatique - +INRIA, établissement public à caractère scientifique et technologique, +dont le siège est situé Domaine de Voluceau, Rocquencourt, BP 105, 78153 +Le Chesnay cedex. + + + Préambule + +Ce contrat est une licence de logiciel libre dont l'objectif est de +conférer aux utilisateurs une très large liberté de modification et de +redistribution du logiciel régi par cette licence. + +L'exercice de cette liberté est assorti d'une obligation forte de +citation à la charge de ceux qui distribueraient un logiciel incorporant +un logiciel régi par la présente licence afin d'assurer que les +contributions de tous soient correctement identifiées et reconnues. + +L'accessibilité au code source et les droits de copie, de modification +et de redistribution qui découlent de ce contrat ont pour contrepartie +de n'offrir aux utilisateurs qu'une garantie limitée et de ne faire +peser sur l'auteur du logiciel, le titulaire des droits patrimoniaux et +les concédants successifs qu'une responsabilité restreinte. + +A cet égard l'attention de l'utilisateur est attirée sur les risques +associés au chargement, à l'utilisation, à la modification et/ou au +développement et à la reproduction du logiciel par l'utilisateur étant +donné sa spécificité de logiciel libre, qui peut le rendre complexe à +manipuler et qui le réserve donc à des développeurs ou des +professionnels avertis possédant des connaissances informatiques +approfondies. Les utilisateurs sont donc invités à charger et tester +l'adéquation du logiciel à leurs besoins dans des conditions permettant +d'assurer la sécurité de leurs systèmes et/ou de leurs données et, plus +généralement, à l'utiliser et l'exploiter dans les mêmes conditions de +sécurité. Ce contrat peut être reproduit et diffusé librement, sous +réserve de le conserver en l'état, sans ajout ni suppression de clauses. + +Ce contrat est susceptible de s'appliquer à tout logiciel dont le +titulaire des droits patrimoniaux décide de soumettre l'exploitation aux +dispositions qu'il contient. + + + Article 1 - DEFINITIONS + +Dans ce contrat, les termes suivants, lorsqu'ils seront écrits avec une +lettre capitale, auront la signification suivante: + +Contrat: désigne le présent contrat de licence, ses éventuelles versions +postérieures et annexes. + +Logiciel: désigne le logiciel sous sa forme de Code Objet et/ou de Code +Source et le cas échéant sa documentation, dans leur état au moment de +l'acceptation du Contrat par le Licencié. + +Logiciel Initial: désigne le Logiciel sous sa forme de Code Source et +éventuellement de Code Objet et le cas échéant sa documentation, dans +leur état au moment de leur première diffusion sous les termes du Contrat. + +Logiciel Modifié: désigne le Logiciel modifié par au moins une +Contribution. + +Code Source: désigne l'ensemble des instructions et des lignes de +programme du Logiciel et auquel l'accès est nécessaire en vue de +modifier le Logiciel. + +Code Objet: désigne les fichiers binaires issus de la compilation du +Code Source. + +Titulaire: désigne le ou les détenteurs des droits patrimoniaux d'auteur +sur le Logiciel Initial. + +Licencié: désigne le ou les utilisateurs du Logiciel ayant accepté le +Contrat. + +Contributeur: désigne le Licencié auteur d'au moins une Contribution. + +Concédant: désigne le Titulaire ou toute personne physique ou morale +distribuant le Logiciel sous le Contrat. + +Contribution: désigne l'ensemble des modifications, corrections, +traductions, adaptations et/ou nouvelles fonctionnalités intégrées dans +le Logiciel par tout Contributeur, ainsi que tout Module Interne. + +Module: désigne un ensemble de fichiers sources y compris leur +documentation qui permet de réaliser des fonctionnalités ou services +supplémentaires à ceux fournis par le Logiciel. + +Module Externe: désigne tout Module, non dérivé du Logiciel, tel que ce +Module et le Logiciel s'exécutent dans des espaces d'adressage +différents, l'un appelant l'autre au moment de leur exécution. + +Module Interne: désigne tout Module lié au Logiciel de telle sorte +qu'ils s'exécutent dans le même espace d'adressage. + +Parties: désigne collectivement le Licencié et le Concédant. + +Ces termes s'entendent au singulier comme au pluriel. + + + Article 2 - OBJET + +Le Contrat a pour objet la concession par le Concédant au Licencié d'une +licence non exclusive, cessible et mondiale du Logiciel telle que +définie ci-après à l'article 5 pour toute la durée de protection des droits +portant sur ce Logiciel. + + + Article 3 - ACCEPTATION + +3.1 L'acceptation par le Licencié des termes du Contrat est réputée +acquise du fait du premier des faits suivants: + + * (i) le chargement du Logiciel par tout moyen notamment par + téléchargement à partir d'un serveur distant ou par chargement à + partir d'un support physique; + * (ii) le premier exercice par le Licencié de l'un quelconque des + droits concédés par le Contrat. + +3.2 Un exemplaire du Contrat, contenant notamment un avertissement +relatif aux spécificités du Logiciel, à la restriction de garantie et à +la limitation à un usage par des utilisateurs expérimentés a été mis à +disposition du Licencié préalablement à son acceptation telle que +définie à l'article 3.1 ci dessus et le Licencié reconnaît en avoir pris +connaissance. + + + Article 4 - ENTREE EN VIGUEUR ET DUREE + + + 4.1 ENTREE EN VIGUEUR + +Le Contrat entre en vigueur à la date de son acceptation par le Licencié +telle que définie en 3.1. + + + 4.2 DUREE + +Le Contrat produira ses effets pendant toute la durée légale de +protection des droits patrimoniaux portant sur le Logiciel. + + + Article 5 - ETENDUE DES DROITS CONCEDES + +Le Concédant concède au Licencié, qui accepte, les droits suivants sur +le Logiciel pour toutes destinations et pour la durée du Contrat dans +les conditions ci-après détaillées. + +Par ailleurs, si le Concédant détient ou venait à détenir un ou +plusieurs brevets d'invention protégeant tout ou partie des +fonctionnalités du Logiciel ou de ses composants, il s'engage à ne pas +opposer les éventuels droits conférés par ces brevets aux Licenciés +successifs qui utiliseraient, exploiteraient ou modifieraient le +Logiciel. En cas de cession de ces brevets, le Concédant s'engage à +faire reprendre les obligations du présent alinéa aux cessionnaires. + + + 5.1 DROIT D'UTILISATION + +Le Licencié est autorisé à utiliser le Logiciel, sans restriction quant +aux domaines d'application, étant ci-après précisé que cela comporte: + + 1. la reproduction permanente ou provisoire du Logiciel en tout ou + partie par tout moyen et sous toute forme. + + 2. le chargement, l'affichage, l'exécution, ou le stockage du + Logiciel sur tout support. + + 3. la possibilité d'en observer, d'en étudier, ou d'en tester le + fonctionnement afin de déterminer les idées et principes qui sont + à la base de n'importe quel élément de ce Logiciel; et ceci, + lorsque le Licencié effectue toute opération de chargement, + d'affichage, d'exécution, de transmission ou de stockage du + Logiciel qu'il est en droit d'effectuer en vertu du Contrat. + + + 5.2 DROIT D'APPORTER DES CONTRIBUTIONS + +Le droit d'apporter des Contributions comporte le droit de traduire, +d'adapter, d'arranger ou d'apporter toute autre modification au Logiciel +et le droit de reproduire le logiciel en résultant. + +Le Licencié est autorisé à apporter toute Contribution au Logiciel sous +réserve de mentionner, de façon explicite, son nom en tant qu'auteur de +cette Contribution et la date de création de celle-ci. + + + 5.3 DROIT DE DISTRIBUTION + +Le droit de distribution comporte notamment le droit de diffuser, de +transmettre et de communiquer le Logiciel au public sur tout support et +par tout moyen ainsi que le droit de mettre sur le marché à titre +onéreux ou gratuit, un ou des exemplaires du Logiciel par tout procédé. + +Le Licencié est autorisé à distribuer des copies du Logiciel, modifié ou +non, à des tiers dans les conditions ci-après détaillées. + + + 5.3.1 DISTRIBUTION DU LOGICIEL SANS MODIFICATION + +Le Licencié est autorisé à distribuer des copies conformes du Logiciel, +sous forme de Code Source ou de Code Objet, à condition que cette +distribution respecte les dispositions du Contrat dans leur totalité et +soit accompagnée: + + 1. d'un exemplaire du Contrat, + + 2. d'un avertissement relatif à la restriction de garantie et de + responsabilité du Concédant telle que prévue aux articles 8 + et 9, + +et que, dans le cas où seul le Code Objet du Logiciel est redistribué, +le Licencié permette un accès effectif au Code Source complet du +Logiciel pendant au moins toute la durée de sa distribution du Logiciel, +étant entendu que le coût additionnel d'acquisition du Code Source ne +devra pas excéder le simple coût de transfert des données. + + + 5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE + +Lorsque le Licencié apporte une Contribution au Logiciel, le Logiciel +Modifié peut être distribué sous un contrat de licence autre que le +présent Contrat sous réserve du respect des dispositions de l'article +5.3.4. + + + 5.3.3 DISTRIBUTION DES MODULES EXTERNES + +Lorsque le Licencié a développé un Module Externe les conditions du +Contrat ne s'appliquent pas à ce Module Externe, qui peut être distribué +sous un contrat de licence différent. + + + 5.3.4 CITATIONS + +Le Licencié qui distribue un Logiciel Modifié s'engage expressément: + + 1. à indiquer dans sa documentation qu'il a été réalisé à partir du + Logiciel régi par le Contrat, en reproduisant les mentions de + propriété intellectuelle du Logiciel, + + 2. à faire en sorte que l'utilisation du Logiciel, ses mentions de + propriété intellectuelle et le fait qu'il est régi par le Contrat + soient indiqués dans un texte facilement accessible depuis + l'interface du Logiciel Modifié, + + 3. à mentionner, sur un site Web librement accessible décrivant le + Logiciel Modifié, et pendant au moins toute la durée de sa + distribution, qu'il a été réalisé à partir du Logiciel régi par le + Contrat, en reproduisant les mentions de propriété intellectuelle + du Logiciel, + + 4. lorsqu'il le distribue à un tiers susceptible de distribuer + lui-même un Logiciel Modifié, sans avoir à en distribuer le code + source, à faire ses meilleurs efforts pour que les obligations du + présent article 5.3.4 soient reprises par le dit tiers. + +Lorsque le Logiciel modifié ou non est distribué avec un Module Externe +qui a été conçu pour l'utiliser, le Licencié doit soumettre le dit +Module Externe aux obligations précédentes. + + + 5.3.5 COMPATIBILITE AVEC LES LICENCES CeCILL et CeCILL-C + +Lorsqu'un Logiciel Modifié contient une Contribution soumise au contrat +de licence CeCILL, les stipulations prévues à l'article 5.3.4 sont +facultatives. + +Un Logiciel Modifié peut être distribué sous le contrat de licence +CeCILL-C. Les stipulations prévues à l'article 5.3.4 sont alors +facultatives. + + + Article 6 - PROPRIETE INTELLECTUELLE + + + 6.1 SUR LE LOGICIEL INITIAL + +Le Titulaire est détenteur des droits patrimoniaux sur le Logiciel +Initial. Toute utilisation du Logiciel Initial est soumise au respect +des conditions dans lesquelles le Titulaire a choisi de diffuser son +oeuvre et nul autre n'a la faculté de modifier les conditions de +diffusion de ce Logiciel Initial. + +Le Titulaire s'engage à ce que le Logiciel Initial reste au moins régi +par le Contrat et ce, pour la durée visée à l'article 4.2. + + + 6.2 SUR LES CONTRIBUTIONS + +Le Licencié qui a développé une Contribution est titulaire sur celle-ci +des droits de propriété intellectuelle dans les conditions définies par +la législation applicable. + + + 6.3 SUR LES MODULES EXTERNES + +Le Licencié qui a développé un Module Externe est titulaire sur celui-ci +des droits de propriété intellectuelle dans les conditions définies par +la législation applicable et reste libre du choix du contrat régissant +sa diffusion. + + + 6.4 DISPOSITIONS COMMUNES + +Le Licencié s'engage expressément: + + 1. à ne pas supprimer ou modifier de quelque manière que ce soit les + mentions de propriété intellectuelle apposées sur le Logiciel; + + 2. à reproduire à l'identique lesdites mentions de propriété + intellectuelle sur les copies du Logiciel modifié ou non. + +Le Licencié s'engage à ne pas porter atteinte, directement ou +indirectement, aux droits de propriété intellectuelle du Titulaire et/ou +des Contributeurs sur le Logiciel et à prendre, le cas échéant, à +l'égard de son personnel toutes les mesures nécessaires pour assurer le +respect des dits droits de propriété intellectuelle du Titulaire et/ou +des Contributeurs. + + + Article 7 - SERVICES ASSOCIES + +7.1 Le Contrat n'oblige en aucun cas le Concédant à la réalisation de +prestations d'assistance technique ou de maintenance du Logiciel. + +Cependant le Concédant reste libre de proposer ce type de services. Les +termes et conditions d'une telle assistance technique et/ou d'une telle +maintenance seront alors déterminés dans un acte séparé. Ces actes de +maintenance et/ou assistance technique n'engageront que la seule +responsabilité du Concédant qui les propose. + +7.2 De même, tout Concédant est libre de proposer, sous sa seule +responsabilité, à ses licenciés une garantie, qui n'engagera que lui, +lors de la redistribution du Logiciel et/ou du Logiciel Modifié et ce, +dans les conditions qu'il souhaite. Cette garantie et les modalités +financières de son application feront l'objet d'un acte séparé entre le +Concédant et le Licencié. + + + Article 8 - RESPONSABILITE + +8.1 Sous réserve des dispositions de l'article 8.2, le Licencié a la +faculté, sous réserve de prouver la faute du Concédant concerné, de +solliciter la réparation du préjudice direct qu'il subirait du fait du +Logiciel et dont il apportera la preuve. + +8.2 La responsabilité du Concédant est limitée aux engagements pris en +application du Contrat et ne saurait être engagée en raison notamment: +(i) des dommages dus à l'inexécution, totale ou partielle, de ses +obligations par le Licencié, (ii) des dommages directs ou indirects +découlant de l'utilisation ou des performances du Logiciel subis par le +Licencié et (iii) plus généralement d'un quelconque dommage indirect. En +particulier, les Parties conviennent expressément que tout préjudice +financier ou commercial (par exemple perte de données, perte de +bénéfices, perte d'exploitation, perte de clientèle ou de commandes, +manque à gagner, trouble commercial quelconque) ou toute action dirigée +contre le Licencié par un tiers, constitue un dommage indirect et +n'ouvre pas droit à réparation par le Concédant. + + + Article 9 - GARANTIE + +9.1 Le Licencié reconnaît que l'état actuel des connaissances +scientifiques et techniques au moment de la mise en circulation du +Logiciel ne permet pas d'en tester et d'en vérifier toutes les +utilisations ni de détecter l'existence d'éventuels défauts. L'attention +du Licencié a été attirée sur ce point sur les risques associés au +chargement, à l'utilisation, la modification et/ou au développement et à +la reproduction du Logiciel qui sont réservés à des utilisateurs avertis. + +Il relève de la responsabilité du Licencié de contrôler, par tous +moyens, l'adéquation du produit à ses besoins, son bon fonctionnement et +de s'assurer qu'il ne causera pas de dommages aux personnes et aux biens. + +9.2 Le Concédant déclare de bonne foi être en droit de concéder +l'ensemble des droits attachés au Logiciel (comprenant notamment les +droits visés à l'article 5). + +9.3 Le Licencié reconnaît que le Logiciel est fourni "en l'état" par le +Concédant sans autre garantie, expresse ou tacite, que celle prévue à +l'article 9.2 et notamment sans aucune garantie sur sa valeur commerciale, +son caractère sécurisé, innovant ou pertinent. + +En particulier, le Concédant ne garantit pas que le Logiciel est exempt +d'erreur, qu'il fonctionnera sans interruption, qu'il sera compatible +avec l'équipement du Licencié et sa configuration logicielle ni qu'il +remplira les besoins du Licencié. + +9.4 Le Concédant ne garantit pas, de manière expresse ou tacite, que le +Logiciel ne porte pas atteinte à un quelconque droit de propriété +intellectuelle d'un tiers portant sur un brevet, un logiciel ou sur tout +autre droit de propriété. Ainsi, le Concédant exclut toute garantie au +profit du Licencié contre les actions en contrefaçon qui pourraient être +diligentées au titre de l'utilisation, de la modification, et de la +redistribution du Logiciel. Néanmoins, si de telles actions sont +exercées contre le Licencié, le Concédant lui apportera son aide +technique et juridique pour sa défense. Cette aide technique et +juridique est déterminée au cas par cas entre le Concédant concerné et +le Licencié dans le cadre d'un protocole d'accord. Le Concédant dégage +toute responsabilité quant à l'utilisation de la dénomination du +Logiciel par le Licencié. Aucune garantie n'est apportée quant à +l'existence de droits antérieurs sur le nom du Logiciel et sur +l'existence d'une marque. + + + Article 10 - RESILIATION + +10.1 En cas de manquement par le Licencié aux obligations mises à sa +charge par le Contrat, le Concédant pourra résilier de plein droit le +Contrat trente (30) jours après notification adressée au Licencié et +restée sans effet. + +10.2 Le Licencié dont le Contrat est résilié n'est plus autorisé à +utiliser, modifier ou distribuer le Logiciel. Cependant, toutes les +licences qu'il aura concédées antérieurement à la résiliation du Contrat +resteront valides sous réserve qu'elles aient été effectuées en +conformité avec le Contrat. + + + Article 11 - DISPOSITIONS DIVERSES + + + 11.1 CAUSE EXTERIEURE + +Aucune des Parties ne sera responsable d'un retard ou d'une défaillance +d'exécution du Contrat qui serait dû à un cas de force majeure, un cas +fortuit ou une cause extérieure, telle que, notamment, le mauvais +fonctionnement ou les interruptions du réseau électrique ou de +télécommunication, la paralysie du réseau liée à une attaque +informatique, l'intervention des autorités gouvernementales, les +catastrophes naturelles, les dégâts des eaux, les tremblements de terre, +le feu, les explosions, les grèves et les conflits sociaux, l'état de +guerre... + +11.2 Le fait, par l'une ou l'autre des Parties, d'omettre en une ou +plusieurs occasions de se prévaloir d'une ou plusieurs dispositions du +Contrat, ne pourra en aucun cas impliquer renonciation par la Partie +intéressée à s'en prévaloir ultérieurement. + +11.3 Le Contrat annule et remplace toute convention antérieure, écrite +ou orale, entre les Parties sur le même objet et constitue l'accord +entier entre les Parties sur cet objet. Aucune addition ou modification +aux termes du Contrat n'aura d'effet à l'égard des Parties à moins +d'être faite par écrit et signée par leurs représentants dûment habilités. + +11.4 Dans l'hypothèse où une ou plusieurs des dispositions du Contrat +s'avèrerait contraire à une loi ou à un texte applicable, existants ou +futurs, cette loi ou ce texte prévaudrait, et les Parties feraient les +amendements nécessaires pour se conformer à cette loi ou à ce texte. +Toutes les autres dispositions resteront en vigueur. De même, la +nullité, pour quelque raison que ce soit, d'une des dispositions du +Contrat ne saurait entraîner la nullité de l'ensemble du Contrat. + + + 11.5 LANGUE + +Le Contrat est rédigé en langue française et en langue anglaise, ces +deux versions faisant également foi. + + + Article 12 - NOUVELLES VERSIONS DU CONTRAT + +12.1 Toute personne est autorisée à copier et distribuer des copies de +ce Contrat. + +12.2 Afin d'en préserver la cohérence, le texte du Contrat est protégé +et ne peut être modifié que par les auteurs de la licence, lesquels se +réservent le droit de publier périodiquement des mises à jour ou de +nouvelles versions du Contrat, qui posséderont chacune un numéro +distinct. Ces versions ultérieures seront susceptibles de prendre en +compte de nouvelles problématiques rencontrées par les logiciels libres. + +12.3 Tout Logiciel diffusé sous une version donnée du Contrat ne pourra +faire l'objet d'une diffusion ultérieure que sous la même version du +Contrat ou une version postérieure. + + + Article 13 - LOI APPLICABLE ET COMPETENCE TERRITORIALE + +13.1 Le Contrat est régi par la loi française. Les Parties conviennent +de tenter de régler à l'amiable les différends ou litiges qui +viendraient à se produire par suite ou à l'occasion du Contrat. + +13.2 A défaut d'accord amiable dans un délai de deux (2) mois à compter +de leur survenance et sauf situation relevant d'une procédure d'urgence, +les différends ou litiges seront portés par la Partie la plus diligente +devant les Tribunaux compétents de Paris. + + +Version 1.0 du 2006-09-05. diff --git a/doc/licenses/gpl-2.0.txt b/doc/licenses/gpl-2.0.txt new file mode 100644 index 0000000..d8cf7d4 --- /dev/null +++ b/doc/licenses/gpl-2.0.txt @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/doc/licenses/gpl-3.0.txt b/doc/licenses/gpl-3.0.txt new file mode 100644 index 0000000..810fce6 --- /dev/null +++ b/doc/licenses/gpl-3.0.txt @@ -0,0 +1,621 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS diff --git a/doc/licenses/lgpl-2.1.txt b/doc/licenses/lgpl-2.1.txt new file mode 100644 index 0000000..20fb9c7 --- /dev/null +++ b/doc/licenses/lgpl-2.1.txt @@ -0,0 +1,458 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/doc/licenses/lgpl-3.0.txt b/doc/licenses/lgpl-3.0.txt new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/doc/licenses/lgpl-3.0.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/doc/matrix.xxml b/doc/matrix.xxml new file mode 100644 index 0000000..e82294c --- /dev/null +++ b/doc/matrix.xxml @@ -0,0 +1,137 @@ + + +]> + +
+Matrices + +
+ +
+ +
+ + + + + +
+ +
Initializing elements + + +
+ +
+ + + + + +
+ +
Matrix views + + +
+ +
Copying matrices + + + +
+ +
Operations on rows and columns + + + + + + + + + +
+ +
Matrix operations + + + + + + + + + + + +
+ +
Matrix comparisons + + + + + + + +
+ +
Combining matrices + + +
+ +
Finding minimum and maximum + + + + + + +
+ +
Matrix properties + + + + + + + + +
+ +
Searching for elements + + +
+ +
Resizing operations + + + + + + +
+ +
Complex matrix operations + + + + + + + +
+ +
Deprecated functions + + + +
+ +
diff --git a/doc/memory.xxml b/doc/memory.xxml new file mode 100644 index 0000000..9d037a0 --- /dev/null +++ b/doc/memory.xxml @@ -0,0 +1,16 @@ + + +]> + + +Memory (de)allocation + + + + + + + + diff --git a/doc/motifs.xxml b/doc/motifs.xxml new file mode 100644 index 0000000..10b2ec6 --- /dev/null +++ b/doc/motifs.xxml @@ -0,0 +1,32 @@ + + +]> + + +Graph motifs, dyad census and triad census + + +This section deals with functions which find small induced subgraphs in a +graph. These were first defined for subgraphs of two and three vertices +by Holland and Leinhardt, and named dyad census and triad census. + + + + + +
Finding triangles + + +
+ +
Graph motifs + + + + + +
+ +
diff --git a/doc/nongraph.xxml b/doc/nongraph.xxml new file mode 100644 index 0000000..b464e71 --- /dev/null +++ b/doc/nongraph.xxml @@ -0,0 +1,44 @@ + + +]> + + +Non-graph related functions + +
igraph version number + +
+ +
Running mean of a time series + +
+ +
Random sampling from very long sequences + +
+ +
Random sampling of spatial points + + + +
+ +
Convex hull of a set of points on a plane + +
+ +
Fitting power-law distributions to empirical data + + + +
+ +
Comparing floats with a tolerance + + + +
+ +
diff --git a/doc/operators.xxml b/doc/operators.xxml new file mode 100644 index 0000000..910bbd4 --- /dev/null +++ b/doc/operators.xxml @@ -0,0 +1,43 @@ + + +]> + + +Graph operators + +
Union and intersection + + + + + + + +
+ +
Other set-like operators + + + +
+ +
Miscellaneous operators + + + + + + + + + + +
+ +
Deprecated functions + +
+ +
diff --git a/doc/pmt.xml b/doc/pmt.xml new file mode 100644 index 0000000..17f84a0 --- /dev/null +++ b/doc/pmt.xml @@ -0,0 +1,145 @@ + + +]> + +
+About template types + +Some of the container types listed in this section are defined for +many base types. This is similar to templates in C++ and generics in +Ada, but it is implemented via preprocessor macros since the C language +cannot handle it. Here is the list of template types and the all base +types they currently support: + + +vector + Vector is currently defined for igraph_real_t, + igraph_integer_t (int), char (char), + igraph_bool_t (bool) and igraph_complex_t + (complex). The default is igraph_real_t. + + +matrix + Matrix is currently defined for igraph_real_t, + igraph_integer_t (int), char (char), + igraph_bool_t (bool) and igraph_complex_t + (complex). The default is igraph_real_t. + + +array3 + Array3 is currently defined for igraph_real_t, + igraph_integer_t (int), char (char) and + igraph_bool_t (bool). The default is + igraph_real_t. + + +stack + Stack is currently defined for igraph_real_t, + igraph_integer_t (int), char (char), + igraph_bool_t (bool) and void* (ptr). + The default is igraph_real_t. + + +double-ended queue + Dqueue is currently defined for igraph_real_t, + igraph_integer_t (int), char (char) and + igraph_bool_t (bool). The default is + igraph_real_t. + + +heap + Heap is currently defined for igraph_real_t, + igraph_integer_t (int), char (char). + In addition both maximum and minimum heaps are available. + The default is the igraph_real_t maximum heap. + + +list of vectors + Lists of vectors are currently defined for vectors holding + igraph_real_t and igraph_integer_t (int). + The default is igraph_real_t. + + +list of matrices + Lists of matrices are currently defined for matrices holding + igraph_real_t only. + + + + + + +The name of the base element (in parentheses) is added to the function +names, except for the default type. + + + +Some examples: + + + + igraph_vector_t is a vector of + igraph_real_t elements. Its functions are + igraph_vector_init, + igraph_vector_destroy, + igraph_vector_sort, etc. + + + + igraph_vector_bool_t is a vector of + igraph_bool_t elements; initialize it with + igraph_vector_bool_init, destroy it with + igraph_vector_bool_destroy, etc. + + + + igraph_heap_t is a maximum heap with + igraph_real_t elements. The corresponding functions are + igraph_heap_init, + igraph_heap_pop, etc. + + + + igraph_heap_min_t is a minimum heap with + igraph_real_t elements. The corresponding functions are + called igraph_heap_min_init, + igraph_heap_min_pop, etc. + + + + igraph_heap_int_t is a maximum heap with igraph_integer_t + elements. Its functions have the igraph_heap_int_ prefix. + + + + igraph_heap_min_int_t is a minimum heap containing + igraph_integer_t elements. Its functions have the + igraph_heap_min_int_ prefix. + + + + igraph_vector_list_t is a list of (floating-point) vectors; each + element in this data structure is an igraph_vector_t. + Similarly, igraph_matrix_list_t is a list of (floating-point) + matrices; each element in this data structure is an igraph_matrix_t. + + + + igraph_vector_int_list_t is a list of integer vectors; each + element in this data structure is an igraph_vector_int_t. + + + + + + +Note that the VECTOR and the MATRIX macros can be used on all +vector and matrix types. VECTOR cannot be used +on lists of vectors, though, only on the individial +vectors in the list. + + +
diff --git a/doc/progress.xxml b/doc/progress.xxml new file mode 100644 index 0000000..90aaf37 --- /dev/null +++ b/doc/progress.xxml @@ -0,0 +1,38 @@ + + +]> + +
+Progress handlers + +
+ +
+ +
Setting up progress handlers + + + +
+ +
Invoking the progress handler + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
diff --git a/doc/psumtree.xxml b/doc/psumtree.xxml new file mode 100644 index 0000000..8869d4c --- /dev/null +++ b/doc/psumtree.xxml @@ -0,0 +1,20 @@ + + +]> + +
+Partial prefix sum trees + + + + + + + + + + + +
diff --git a/doc/random.xxml b/doc/random.xxml new file mode 100644 index 0000000..c339ab9 --- /dev/null +++ b/doc/random.xxml @@ -0,0 +1,54 @@ + + +]> + + +Random numbers + + + +
The default random number generator + + +
+ +
Creating random number generators + + + + + + +
+ +
Generating random numbers + + + + + + + + + +
+ +
Supported random number generators + +By default igraph uses the MT19937 generator. Prior to igraph version +0.6, the generator supplied by the standard C library was used. This +means the GLIBC2 generator on GNU libc 2 systems, and maybe the BSD RAND +generator on others. The RAND generator was removed due to poor statistical +properties in version 0.10. The PCG32 generator was added in version 0.10. + + + + + +
+ + + +
diff --git a/doc/separators.xxml b/doc/separators.xxml new file mode 100644 index 0000000..f6f9e88 --- /dev/null +++ b/doc/separators.xxml @@ -0,0 +1,16 @@ + + +]> + + +Vertex separators + + + + + + + + diff --git a/doc/sparsemat.xxml b/doc/sparsemat.xxml new file mode 100644 index 0000000..946cf5f --- /dev/null +++ b/doc/sparsemat.xxml @@ -0,0 +1,116 @@ + + +]> + +
+Sparse matrices + +
+ +
+ +
Creating sparse matrix objects + + + + + + + +
+ +
Query properties of a sparse matrix + + + + + + + + + + + + + + + + + + +
+ +
Operations on sparse matrices + + + + + + + + + + + + + + +
+ +
Operations on sparse matrix iterators + + + + + + + + +
+ +
Operations that change the internal representation + + +
+ +
Decompositions and solving linear systems + + + + + + + + + + + + + + +
+ +
Eigenvalues and eigenvectors + + +
+ +
Conversion to other data types + + + +
+ +
Writing to a file, or to the screen + +
+ +
Deprecated functions + + + +
+ +
diff --git a/doc/spatialgames.xxml b/doc/spatialgames.xxml new file mode 100644 index 0000000..5163c5b --- /dev/null +++ b/doc/spatialgames.xxml @@ -0,0 +1,23 @@ + + +]> + + +Games on graphs + +
Microscopic update rules + + + + +
+ +
Epidemic models + + + +
+ +
diff --git a/doc/stack.xxml b/doc/stack.xxml new file mode 100644 index 0000000..d62430d --- /dev/null +++ b/doc/stack.xxml @@ -0,0 +1,20 @@ + + +]> + +
+Stacks + + + + + + + + + + + +
diff --git a/doc/status.xxml b/doc/status.xxml new file mode 100644 index 0000000..bd0784b --- /dev/null +++ b/doc/status.xxml @@ -0,0 +1,27 @@ + + +]> + +
+Status handlers + +
+ +
+ +
Setting up status handlers + + + +
+ +
Invoking the status handler + + + + +
+ +
diff --git a/doc/structural.xxml b/doc/structural.xxml new file mode 100644 index 0000000..afcdf80 --- /dev/null +++ b/doc/structural.xxml @@ -0,0 +1,274 @@ + + +]> + + +Structural properties of graphs + + + +
Basic properties + +
+ +
Sparsifiers + +
+ + + + + +
Efficiency measures + + + +
+ +
Neighborhood of a vertex + + + +
+ +
Local scan statistics + +
"Us" statistics + + + +
+
"Them" statistics + + + +
+
Pre-calculated subsets + + +
+
+ +
Graph components + + + + + + + + + + + + + +
+ +
Degree sequences + + +
+ +
Centrality measures + + + + + + + + + + + + + + +
+ +
Range-limited centrality measures + + + + +
+ +
Subset-limited centrality measures + + +
+ +
Centralization + + + + + + + + + +
+ +
Similarity measures + + + + + + + + + +
+ +
Trees and forests + + + + + + + +
+ +
Transitivity or clustering coefficient + + + + + +
+ +
Directedness conversion + + +
+ +
Spectral properties + + + +
+ +
Non-simple graphs: Multiple and loop edges + + + + + + + + +
+ +
Mixing patterns + + + + + + +
+ +
K-cores and k-trusses + + +
+ +
Topological sorting, directed acyclic graphs + + + +
+ +
Maximum cardinality search and chordal graphs + + +
+ +
Matchings + + + +
+ +
Unfolding a graph into a tree + +
+ +
Other operations + + + + + + + + + + + + + + +
+ +
Deprecated functions + + + + + + + + + + +
+ +
diff --git a/doc/strvector.xxml b/doc/strvector.xxml new file mode 100644 index 0000000..6c72023 --- /dev/null +++ b/doc/strvector.xxml @@ -0,0 +1,38 @@ + + +]> + +
+String vectors + + + + + + + + + + + + + + + + + + + + + + +
+Deprecated functions + + + +
+ +
diff --git a/doc/threading.xxml b/doc/threading.xxml new file mode 100644 index 0000000..7813e44 --- /dev/null +++ b/doc/threading.xxml @@ -0,0 +1,45 @@ + + +]> + +
Using igraph in multi-threaded programs + + The igraph library is considered thread-safe if it has been compiled + with thread-local storage enabled, i.e. the IGRAPH_ENABLE_TLS + setting was toggled to ON and the current platform + supports this feature. To check whether an igraph build is thread-safe, use the + + IGRAPH_THREAD_SAFE + + macro. When linking to external versions of igraph's dependencies, it is + the responsibility of the user to check that these dependencies were also + compiled to be thread-safe. + + + + +
Thread-safe ARPACK library + +Note that igraph is only thread-safe if it was built with the internal +ARPACK library, i.e. the one that comes with igraph. The standard +ARPACK library is not thread-safe. + +
+ +
Thread-safety of random number generators + +The default random number generator that igraph uses is not +guaranteed to be thread-safe. You need to set a different random number generator +instance for every thread that you want to use igraph from. This is especially +important if you set the seed of the random number generator to ensure +reproducibility; sharing a random number generator between threads would break +reproducibility as the order in which the various threads are scheduled is +random, and therefore they would still receive random numbers in an unpredictable +order from the shared random number generator. + +
+ + +
diff --git a/doc/tutorial.xml b/doc/tutorial.xml new file mode 100644 index 0000000..142c94b --- /dev/null +++ b/doc/tutorial.xml @@ -0,0 +1,321 @@ + + +]> + + +Tutorial + +
Compiling programs using igraph + + +The following short example program demonstrates the basic usage of +the igraph library. Save it into a file named +igraph_test.c. + + + + +This example illustrates a couple of points: + + + + +First, programs +using the igraph library should include the +igraph.h header +file. + + +Second, igraph uses the +igraph_integer_t type for integers instead of +int or long int, and it also uses the +igraph_real_t type for real numbers instead of +double. Depending on how igraph was compiled, and whether you are +using a 32-bit or 64-bit system, igraph_integer_t may be a 32-bit +or 64-bit integer. + + +Third, igraph graph objects are represented by the igraph_t data +type. + + +Fourth, the igraph_erdos_renyi_game_gnm() +creates a graph and igraph_destroy() +destroys it, i.e. deallocates the memory associated to it. + + + + +For compiling this program you need a C compiler. Optionally, +CMake can be used to automate the compilation. + + +
+Compiling with CMake + + +It is convenient to use CMake because it can automatically discover the +necessary compilation flags on all operating systems. Many IDEs support +CMake, and can work with CMake projects directly. To create a CMake project +for this example program, create a file name CMakeLists.txt with the +following contents: + + +cmake_minimum_required(VERSION 3.18) +project(igraph_test) + +find_package(igraph REQUIRED) + +add_executable(igraph_test igraph_test.c) +target_link_libraries(igraph_test PUBLIC igraph::igraph) + + + + +To compile the project, create a new directory called build in +the root of the igraph source tree, and switch to it: + +mkdir build +cd build + + + + +Run CMake to configure the project: + +cmake .. + + + + +If igraph was installed at a non-standard location, specify its prefix +using the option. The prefix must be +the same directory that was specified as the +when compiling igraph. + + + +If configuration has succeeded, build the program using + +cmake --build . + + + +C++ must be enabled in igraph projects +Parts of igraph are implemented in C++; therefore, any CMake target that +depends on igraph should use the C++ linker. Furthermore, OpenMP support in +igraph works correctly only if C++ is enabled in the CMake project. The script +that finds igraph on the host machine will throw an error if C++ support is +not enabled in the CMake project. + +C++ support is enabled by default when no languages are explicitly +specified in CMake's project +command, e.g. project(igraph_test). If you do specify some languages explicitly, +make sure to also include CXX, e.g. project(igraph_test C CXX). + + + +
+ +
+Compiling without CMake + + +On most Unix-like systems, the default C compiler is called cc. +To compile the test program, you will need a command similar to the following: + +cc igraph_test.c -I/usr/local/include/igraph -L/usr/local/lib -ligraph -o igraph_test + + + + +The exact form depends on where igraph was installed on your +system, whether it was compiled as a shared or static library, and the external +libraries it was linked to. The directory after the switch +is the one containing the igraph.h file, while the one +following should contain the library file itself, usually a +file called libigraph.a (static library on macOS and +Linux), libigraph.so (shared library on Linux), +libigraph.dylib (shared library on macOS), +igraph.lib (static library on Windows) or +igraph.dll (shared library on Windows). If +igraph was compiled as a static library, it is also +necessary to manually link to all of its dependencies. + + + +If your system has the pkg-config utility you are +likely to get the necessary compile options by issuing the command + +pkg-config --libs --cflags igraph + +(if igraph was built as a shared library) or + +pkg-config --static --libs --cflags igraph + +(if igraph was built as a static library). + + +
+ +
+Running the program + + +On most systems, the executable can be run by simply typing its name like this: + +./igraph_test + +If you use dynamic linking and the igraph +library is not installed in a standard place, you may need to add its location to the +LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (macOS) +or PATH (Windows) environment variables. This is typically necessary +on Windows systems. + + +
+ +
+
Creating your first graphs + + +The functions generating graph objects are called graph +generators. Stochastic (i.e. randomized) graph generators are called +games. + + + +igraph can handle directed and undirected graphs. Most graph +generators are able to create both types of graphs and most other +functions are usually also capable of handling +both. E.g., igraph_get_shortest_paths(), +which calculates shortest paths from a vertex to other vertices, can calculate +directed or undirected paths. + + + +igraph has sophisticated ways for creating graphs. The simplest +graphs are deterministic regular structures like star graphs +(igraph_star()), +ring graphs (igraph_ring()), lattices +(igraph_square_lattice()) or trees +(igraph_kary_tree()). + + + +The following example creates an undirected regular circular lattice, +adds some random edges to it and calculates the average length of +shortest paths between all pairs of vertices in the graph before and +after adding the random edges. (The message is that some random edges +can reduce path lengths a lot.) + + + + +This example illustrates some new points. igraph uses +igraph_vector_t +and its related types (igraph_vector_int_t, igraph_vector_bool_t +and so on) instead of plain C arrays. igraph_vector_t is superior to +regular arrays in almost every sense. Vectors are created by the +igraph_vector_init() +function and, like graphs, they should be destroyed if not +needed any more by calling +igraph_vector_destroy() +on them. A vector can be indexed by the +VECTOR() function +(right now it is a macro). The elements of a vector are of type igraph_real_t +for igraph_vector_t, +and of type igraph_integer_t for igraph_vector_int_t. +As you might expect, igraph_vector_bool_t holds +igraph_bool_t values. Vectors can be resized and most igraph +functions returning the result in a vector automatically resize it to the size they need. + + + +igraph_square_lattice() +takes an integer vector argument specifying the dimensions of +the lattice. In this example we generate a 30x30 two dimensional +periodic lattice. See the documentation of +igraph_square_lattice() in +the reference manual for the other arguments. + + + +The vertices in a graph are identified by a vertex ID, an integer between +0 and N-1, where N is the number of vertices in the graph. The vertex count can be +retrieved using igraph_vcount(), +as in the example. + + + +The igraph_add_edges() +function simply takes a graph and a vector of +vertex IDs defining the new edges. The first edge is between the first +two vertex IDs in the vector, the second edge is between the second +two, etc. This way we add ten random edges to the lattice. + + + +Note that this example program may add loop edges, edges +pointing a vertex to itself, or multiple edges, more than one edge +between the same pair of vertices. +igraph_t can of course represent loops and multiple edges, although some +routines expect simple graphs, i.e. graphs which contain neither of these. This is because some +structural properties are ill-defined for non-simple graphs. Loop and multi-edges can be removed by calling +igraph_simplify(). + + +
+
Calculating various properties of graphs + +In our next example we will calculate various centrality measures in a +friendship graph. The friendship graph is from the famous Zachary karate +club study. (Do a web search on "Zachary karate" if you want to know more about +this.) Centrality measures quantify how central is the position of +individual vertices in the graph. + + + + +This example demonstrates some new operations. First of all, it shows a +way to create a graph a list of edges stored in a plain C array. +Function igraph_vector_view() +creates a view of a C array. It does not copy any data, +which means that you must not call +igraph_vector_destroy() +on a vector created this way. This vector is then used to create the +undirected graph. + + + +Then the degree, closeness and betweenness centrality of the vertices +is calculated and the highest values are printed. Note that the vector +result, into which these functions will write their +result, must be initialized first, and also that the functions resize +it to be able to hold the result. + + + +Notice that in order to print values of type igraph_integer_t, +we used the IGRAPH_PRId format macro constant. This +macro is similar to the standard PRI constants defined +in stdint.h, and expands to the correct printf +format specifier on each platform that igraph supports. + + + +The igraph_vss_all() argument +tells the functions to calculate the property for every vertex in the graph. +It is shorthand for a vertex selector, represented by type +igraph_vs_t. +Vertex selectors help perform operations on a subset of vertices. +You can read more about them in one +of the following chapters. + +
+ +
diff --git a/doc/vector.xxml b/doc/vector.xxml new file mode 100644 index 0000000..510edbd --- /dev/null +++ b/doc/vector.xxml @@ -0,0 +1,176 @@ + + +]> + +
+Vectors + +
+ +
+ +
+ + + + + + +
+ +
Initializing elements + + + +
+ +
+ + + + + + +
+ +
Vector views + +
+ +
Copying vectors + + + + +
+ +
Exchanging elements + + + + +
+ +
Vector operations + + + + + + + +
+ +
Vector comparisons + + + + + + + + + + + +
+ +
Finding minimum and maximum + + + + + + +
+ +
Vector properties + + + + + + + + + + +
+ +
Searching for elements + + + + + +
+ +
Resizing operations + + + + + + + + + +
+ +
Complex vector operations + + + + + + + +
+ +
Sorting + + + +
+ +
Set operations on sorted vectors + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + +
+ +
Deprecated functions + + + + + + +
+ +
diff --git a/doc/vectorlist.xxml b/doc/vectorlist.xxml new file mode 100644 index 0000000..6b0d2f3 --- /dev/null +++ b/doc/vectorlist.xxml @@ -0,0 +1,60 @@ + + +]> + +
+Lists of vectors, matrices and graphs + +
+ +
+ +
+ + + +
+ +
+ + + + + +
+ +
Vector properties + + + +
+ +
Resizing operations + + + + + + + + + + + + + + + +
+ +
Sorting and reordering + + + + + +
+ +
diff --git a/doc/version-greater-or-equal.xsl b/doc/version-greater-or-equal.xsl new file mode 100644 index 0000000..8edcd81 --- /dev/null +++ b/doc/version-greater-or-equal.xsl @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + 0 + + + + + + + + + + + 0 + + + 1 + + + + + + diff --git a/doc/visitors.xxml b/doc/visitors.xxml new file mode 100644 index 0000000..2f31761 --- /dev/null +++ b/doc/visitors.xxml @@ -0,0 +1,30 @@ + + +]> + + +Graph visitors + + + + + +
Random walks + +
+ +
+Deprecated functions + +
+ +
diff --git a/etc/cmake/BuildType.cmake b/etc/cmake/BuildType.cmake new file mode 100644 index 0000000..76dd902 --- /dev/null +++ b/etc/cmake/BuildType.cmake @@ -0,0 +1,12 @@ +# Taken from https://blog.kitware.com/cmake-and-the-default-build-type/ + +# Set the default build type to "Release" +set(default_build_type "Release") + +get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(NOT isMultiConfig AND NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() diff --git a/etc/cmake/CheckTLSSupport.cmake b/etc/cmake/CheckTLSSupport.cmake new file mode 100644 index 0000000..5a72791 --- /dev/null +++ b/etc/cmake/CheckTLSSupport.cmake @@ -0,0 +1,36 @@ +include(CheckCSourceCompiles) +include(CMakePushCheckState) + +macro(check_tls_support VAR) + if(NOT DEFINED "${VAR}") + cmake_push_check_state() + set(CMAKE_REQUIRED_QUIET 1) + + check_c_source_compiles(" + __thread int tls; + + int main(void) { + return 0; + }" HAVE_GCC_TLS) + + if(HAVE_GCC_TLS) + message(STATUS "Thread-local storage: supported (__thread)") + set(${VAR} "__thread" CACHE INTERNAL "Thread-local storage support keyword in compiler") + else() + check_c_source_compiles(" + __declspec(thread) int tls; + + int main(void) { + return 0; + }" HAVE_MSVC_TLS) + if(HAVE_MSVC_TLS) + message(STATUS "Thread-local storage: supported (__declspec(thread))") + set(${VAR} "__declspec(thread)" CACHE INTERNAL "Thread-local storage keyword in compiler") + else() + message(STATUS "Thread-local storage: not supported") + set(${VAR} "" CACHE INTERNAL "Thread-local storage keyword in compiler") + endif() + endif() + cmake_pop_check_state() + endif() +endmacro() diff --git a/etc/cmake/CodeCoverage.cmake b/etc/cmake/CodeCoverage.cmake new file mode 100644 index 0000000..1e36d7e --- /dev/null +++ b/etc/cmake/CodeCoverage.cmake @@ -0,0 +1,682 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# 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 copyright holder 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 HOLDER 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. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# 2019-05-06, Anatolii Kurotych +# - Remove unnecessary --coverage flag +# +# 2019-12-13, FeRD (Frank Dana) +# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor +# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. +# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY +# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list +# - Set lcov basedir with -b argument +# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be +# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) +# - Delete output dir, .info file on 'make clean' +# - Remove Python detection, since version mismatches will break gcovr +# - Minor cleanup (lowercase function names, update examples...) +# +# 2019-12-19, FeRD (Frank Dana) +# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets +# +# 2020-01-19, Bob Apthorpe +# - Added gfortran support +# +# 2020-02-17, FeRD (Frank Dana) +# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters +# in EXCLUDEs, and remove manual escaping from gcovr targets +# +# 2021-01-19, Robin Mueller +# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run +# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional +# flags to the gcovr command +# +# 2020-05-04, Mihchael Davis +# - Add -fprofile-abs-path to make gcno files contain absolute paths +# - Fix BASE_DIRECTORY not working when defined +# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt (best inside an if-condition +# using a CMake option() to enable it just optionally): +# include(CodeCoverage) +# +# 3. Append necessary compiler flags: +# append_coverage_compiler_flags() +# +# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og +# +# 4. If you need to exclude additional directories from the report, specify them +# using full paths in the COVERAGE_EXCLUDES variable before calling +# setup_target_for_coverage_*(). +# Example: +# set(COVERAGE_EXCLUDES +# '${PROJECT_SOURCE_DIR}/src/dir1/*' +# '/path/to/my/src/dir2/*') +# Or, use the EXCLUDE argument to setup_target_for_coverage_*(). +# Example: +# setup_target_for_coverage_lcov( +# NAME coverage +# EXECUTABLE testrunner +# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") +# +# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set +# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) +# Example: +# set(COVERAGE_EXCLUDES "dir1/*") +# setup_target_for_coverage_gcovr_html( +# NAME coverage +# EXECUTABLE testrunner +# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" +# EXCLUDE "dir2/*") +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# + +include(CMakeParseArguments) + +option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE) + +# Check prereqs +find_program( GCOV_PATH gcov ) +find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +find_program( FASTCOV_PATH NAMES fastcov fastcov.py ) +find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) +find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( CPPFILT_PATH NAMES c++filt ) + +if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") +endif() # NOT GCOV_PATH + +get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) +list(GET LANGUAGES 0 LANG) + +if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() +elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang") + # Do nothing; exit conditional without error if true + elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") + # Do nothing; exit conditional without error if true + else() + message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") + endif() +endif() + +set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" + CACHE INTERNAL "") +if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path) + if(HAVE_fprofile_abs_path) + set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") + endif() +endif() + +set(CMAKE_Fortran_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the Fortran compiler during coverage builds." + FORCE ) +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE ) +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE ) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE ) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE ) +mark_as_advanced( + CMAKE_Fortran_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") +endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +endif() + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_lcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# ) +function(setup_target_for_coverage_lcov) + + set(options NO_DEMANGLE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() # NOT LCOV_PATH + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() # NOT GENHTML_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(LCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Setting up commands which will be run to generate coverage data. + # Cleanup lcov + set(LCOV_CLEAN_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . + -b ${BASEDIR} --zerocounters + ) + # Create baseline to make sure untouched files show up in the report + set(LCOV_BASELINE_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b + ${BASEDIR} -o ${Coverage_NAME}.base + ) + # Run tests + set(LCOV_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Capturing lcov counters and generating report + set(LCOV_CAPTURE_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b + ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture + ) + # add baseline counters + set(LCOV_BASELINE_COUNT_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base + -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total + ) + # filter collected data to final coverage report + set(LCOV_FILTER_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove + ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info + ) + # Generate HTML output + set(LCOV_GEN_HTML_CMD + ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o + ${Coverage_NAME} ${Coverage_NAME}.info + ) + + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + message(STATUS "Command to clean up lcov: ") + string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}") + message(STATUS "${LCOV_CLEAN_CMD_SPACED}") + + message(STATUS "Command to create baseline: ") + string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}") + message(STATUS "${LCOV_BASELINE_CMD_SPACED}") + + message(STATUS "Command to run the tests: ") + string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}") + message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to capture counters and generate report: ") + string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}") + message(STATUS "${LCOV_CAPTURE_CMD_SPACED}") + + message(STATUS "Command to add baseline counters: ") + string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}") + message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}") + + message(STATUS "Command to filter collected data: ") + string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}") + message(STATUS "${LCOV_FILTER_CMD_SPACED}") + + message(STATUS "Command to generate lcov HTML output: ") + string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}") + message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}") + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + COMMAND ${LCOV_CLEAN_CMD} + COMMAND ${LCOV_BASELINE_CMD} + COMMAND ${LCOV_EXEC_TESTS_CMD} + COMMAND ${LCOV_CAPTURE_CMD} + COMMAND ${LCOV_BASELINE_COUNT_CMD} + COMMAND ${LCOV_FILTER_CMD} + COMMAND ${LCOV_GEN_HTML_CMD} + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.base + ${Coverage_NAME}.capture + ${Coverage_NAME}.total + ${Coverage_NAME}.info + ${Coverage_NAME}/index.html + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_lcov + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_xml( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the +# GCVOR command. +function(setup_target_for_coverage_gcovr_xml) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + # Set up commands which will be run to generate coverage data + # Run tests + set(GCOVR_XML_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Running gcovr + set(GCOVR_XML_CMD + ${GCOVR_PATH} --xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} + --object-directory=${PROJECT_BINARY_DIR} -o ${Coverage_NAME}.xml + ) + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + + message(STATUS "Command to run tests: ") + string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}") + message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to generate gcovr XML coverage data: ") + string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") + message(STATUS "${GCOVR_XML_CMD_SPACED}") + endif() + + add_custom_target(${Coverage_NAME} + COMMAND ${GCOVR_XML_EXEC_TESTS_CMD} + COMMAND ${GCOVR_XML_CMD} + + BYPRODUCTS ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) +endfunction() # setup_target_for_coverage_gcovr_xml + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_html( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the +# GCVOR command. +function(setup_target_for_coverage_gcovr_html) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + # Set up commands which will be run to generate coverage data + # Run tests + set(GCOVR_HTML_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Create folder + set(GCOVR_HTML_FOLDER_CMD + ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + ) + # Running gcovr + set(GCOVR_HTML_CMD + ${GCOVR_PATH} --html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}/index.html + ) + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + + message(STATUS "Command to run tests: ") + string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}") + message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to create a folder: ") + string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}") + message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}") + + message(STATUS "Command to generate gcovr HTML coverage data: ") + string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}") + message(STATUS "${GCOVR_HTML_CMD_SPACED}") + endif() + + add_custom_target(${Coverage_NAME} + COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD} + COMMAND ${GCOVR_HTML_FOLDER_CMD} + COMMAND ${GCOVR_HTML_CMD} + + BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_gcovr_html + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_fastcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude. +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# SKIP_HTML # Don't create html report +# ) +function(setup_target_for_coverage_fastcov) + + set(options NO_DEMANGLE SKIP_HTML) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT FASTCOV_PATH) + message(FATAL_ERROR "fastcov not found! Aborting...") + endif() + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (Patterns, not paths, for fastcov) + set(FASTCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES}) + list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES FASTCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Set up commands which will be run to generate coverage data + set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) + + set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} + --search-directory ${BASEDIR} + --process-gcno + --lcov + --output ${Coverage_NAME}.info + --exclude ${FASTCOV_EXCLUDES} + --exclude ${FASTCOV_EXCLUDES} + ) + + if(Coverage_SKIP_HTML) + set(FASTCOV_HTML_CMD ";") + else() + set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} + -o ${Coverage_NAME} ${Coverage_NAME}.info + ) + endif() + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):") + + message(" Running tests:") + string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}") + message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}") + + message(" Capturing fastcov counters and generating report:") + string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}") + message(" ${FASTCOV_CAPTURE_CMD_SPACED}") + + if(NOT Coverage_SKIP_HTML) + message(" Generating HTML report: ") + string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}") + message(" ${FASTCOV_HTML_CMD_SPACED}") + endif() + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + + # Cleanup fastcov + COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} + --search-directory ${BASEDIR} + --zerocounters + + COMMAND ${FASTCOV_EXEC_TESTS_CMD} + COMMAND ${FASTCOV_CAPTURE_CMD} + COMMAND ${FASTCOV_HTML_CMD} + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.info + ${Coverage_NAME}/index.html # report directory + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report." + ) + + set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info.") + if(NOT Coverage_SKIP_HTML) + string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.") + endif() + # Show where to find the fastcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG} + ) + +endfunction() # setup_target_for_coverage_fastcov + +function(append_coverage_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # append_coverage_compiler_flags diff --git a/etc/cmake/FindARPACK.cmake b/etc/cmake/FindARPACK.cmake new file mode 100644 index 0000000..26a9d2d --- /dev/null +++ b/etc/cmake/FindARPACK.cmake @@ -0,0 +1,85 @@ +# https://raw.githubusercontent.com/dune-project/dune-istl/master/cmake/modules/FindARPACK.cmake +# +# This file is taken from: +# +# DUNE, the Distributed and Unified Numerics Environment +# GPLv2 licensed +# +# .. cmake_module:: +# +# Module that checks whether ARPACK is available and usable. +# +# Variables used by this module which you may want to set: +# +# :ref:`ARPACK_ROOT` +# Path list to search for ARPACK. +# +# Sets the following variables: +# +# :code:`ARPACK_FOUND` +# True if ARPACK available. +# +# :code:`ARPACK_LIBRARIES` +# Link against these libraries to use ARPACK. +# +# .. cmake_variable:: ARPACK_ROOT +# +# You may set this variable to have :ref:`FindARPACK` look +# for the ARPACK package in the given path before inspecting +# system paths. +# + +# look for library, only at positions given by the user +find_library(ARPACK_LIBRARY + NAMES "arpack" + PATHS ${ARPACK_PREFIX} ${ARPACK_ROOT} + PATH_SUFFIXES "lib" "lib32" "lib64" + NO_DEFAULT_PATH +) + +# look for library files, including default paths +find_library(ARPACK_LIBRARY + NAMES "arpack" + PATH_SUFFIXES "lib" "lib32" "lib64" +) + +# check header usability +include(CMakePushCheckState) +cmake_push_check_state() + +# we need if clauses here because variable is set variable-NOTFOUND if the +# searches above were not successful; without them CMake print errors like: +# "CMake Error: The following variables are used in this project, but they +# are set to NOTFOUND. Please set them or make sure they are set and tested +# correctly in the CMake files." +if(ARPACK_LIBRARY) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${ARPACK_LIBRARY}) +endif() + +# end of header usability check +cmake_pop_check_state() + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "ARPACK" + DEFAULT_MSG + ARPACK_LIBRARY +) + +# hide the introduced cmake cached variables in cmake GUIs +mark_as_advanced(ARPACK_LIBRARY) + +# if headers are found, store results +if(ARPACK_FOUND) + set(ARPACK_LIBRARIES ${ARPACK_LIBRARY}) + # log result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determing location of ARPACK succeeded:\n" + "Libraries to link against: ${ARPACK_LIBRARIES}\n\n") +else() + # log errornous result + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determing location of ARPACK failed:\n" + "Libraries to link against: ${ARPACK_LIBRARIES}\n\n") +endif() diff --git a/etc/cmake/FindGLPK.cmake b/etc/cmake/FindGLPK.cmake new file mode 100644 index 0000000..c2e887a --- /dev/null +++ b/etc/cmake/FindGLPK.cmake @@ -0,0 +1,81 @@ +#[=======================================================================[.rst: +FindGLPK +-------- + +Finds the GLPK library. + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``GLPK_FOUND`` + True if the system has the GLPK library. +``GLPK_VERSION`` + The version of the GLPK library which was found. +``GLPK_INCLUDE_DIRS`` + Include directories needed to use Foo. +``GLPK_LIBRARIES`` + Libraries needed to link to Foo. + +Cache Variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``GLPK_INCLUDE_DIR`` + The directory containing ``glpk.h``. +``GLPK_LIBRARY`` + The path to the GLPK library. + +#]=======================================================================] + +find_path(GLPK_INCLUDE_DIR + NAMES glpk.h +) + +find_library(GLPK_LIBRARY + NAMES glpk +) + +# parse version from header +if(GLPK_INCLUDE_DIR) + set(GLPK_VERSION_FILE ${GLPK_INCLUDE_DIR}/glpk.h) + file(READ ${GLPK_VERSION_FILE} GLPK_VERSION_FILE_CONTENTS) + + string(REGEX MATCH "#define[ ]+GLP_MAJOR_VERSION[ ]+[0-9]+" + GLPK_VERSION_MAJOR "${GLPK_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "#define[ ]+GLP_MAJOR_VERSION[ ]+([0-9]+)" "\\1" + GLPK_VERSION_MAJOR "${GLPK_VERSION_MAJOR}") + + string(REGEX MATCH "#define[ ]+GLP_MINOR_VERSION[ ]+[0-9]+" + GLPK_VERSION_MINOR "${GLPK_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "#define[ ]+GLP_MINOR_VERSION[ ]+([0-9]+)" "\\1" + GLPK_VERSION_MINOR "${GLPK_VERSION_MINOR}") + + set(GLPK_VERSION "${GLPK_VERSION_MAJOR}.${GLPK_VERSION_MINOR}") + + # compatibility variables + set(GLPK_VERSION_STRING "${GLPK_VERSION}") +endif() + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLPK + FOUND_VAR GLPK_FOUND + REQUIRED_VARS + GLPK_LIBRARY + GLPK_INCLUDE_DIR + VERSION_VAR GLPK_VERSION +) + +# hide the introduced cmake cached variables in cmake GUIs +mark_as_advanced( + GLPK_INCLUDE_DIR + GLPK_LIBRARY +) + +if(GLPK_FOUND) + set(GLPK_LIBRARIES ${GLPK_LIBRARY}) + set(GLPK_INCLUDE_DIRS ${GLPK_INCLUDE_DIR}) +endif() diff --git a/etc/cmake/FindGMP.cmake b/etc/cmake/FindGMP.cmake new file mode 100644 index 0000000..f958a6c --- /dev/null +++ b/etc/cmake/FindGMP.cmake @@ -0,0 +1,36 @@ +# Inspired by http://code.google.com/p/origin/source/browse/trunk/cmake/FindGMP.cmake + +# Copyright (c) 2008-2010 Kent State University +# Copyright (c) 2011-2012 Texas A&M University +# +# This file is distributed under the MIT License. See +# http://www.opensource.org/licenses/mit-license.php for terms and conditions. +# +# Some modifications made by Tamas Nepusz to ensure that the module fits better +# with the de facto conventions of FindXXX.cmake scripts + +find_path(GMP_INCLUDE_DIR + NAMES gmp.h +) + +find_library(GMP_LIBRARY + NAMES gmp +) + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "GMP" + DEFAULT_MSG + GMP_LIBRARY + GMP_INCLUDE_DIR +) + +# hide the introduced cmake cached variables in cmake GUIs +mark_as_advanced(GMP_INCLUDE_DIR) +mark_as_advanced(GMP_LIBRARY) + +if(GMP_FOUND) + set(GMP_LIBRARIES ${GMP_LIBRARY}) + set(GMP_INCLUDE_DIRS ${GMP_INCLUDE_DIR}) +endif() diff --git a/etc/cmake/FindPLFIT.cmake b/etc/cmake/FindPLFIT.cmake new file mode 100644 index 0000000..0136937 --- /dev/null +++ b/etc/cmake/FindPLFIT.cmake @@ -0,0 +1,63 @@ +# Inspired by http://code.google.com/p/origin/source/browse/trunk/cmake/FindGMP.cmake + +# Copyright (c) 2021 Tamas Nepusz +# +# This file is distributed under the MIT License. See +# http://www.opensource.org/licenses/mit-license.php for terms and conditions. +# +# Some modifications made by Tamas Nepusz to ensure that the module fits better +# with the de facto conventions of FindXXX.cmake scripts + +find_path(PLFIT_INCLUDE_DIR + NAMES plfit.h + PATH_SUFFIXES plfit +) + +find_library(PLFIT_LIBRARY + NAMES plfit +) + +# parse version from header +if(PLFIT_INCLUDE_DIR) + set(PLFIT_VERSION_FILE ${PLFIT_INCLUDE_DIR}/plfit_version.h) + file(READ ${PLFIT_VERSION_FILE} PLFIT_VERSION_FILE_CONTENTS) + + string(REGEX MATCH "#define[ ]+PLFIT_VERSION_MAJOR[ ]+[0-9]+" + PLFIT_VERSION_MAJOR "${PLFIT_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "#define[ ]+PLFIT_VERSION_MAJOR[ ]+([0-9]+)" "\\1" + PLFIT_VERSION_MAJOR "${PLFIT_VERSION_MAJOR}") + + string(REGEX MATCH "#define[ ]+PLFIT_VERSION_MINOR[ ]+[0-9]+" + PLFIT_VERSION_MINOR "${PLFIT_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "#define[ ]+PLFIT_VERSION_MINOR[ ]+([0-9]+)" "\\1" + PLFIT_VERSION_MINOR "${PLFIT_VERSION_MINOR}") + + string(REGEX MATCH "#define[ ]+PLFIT_VERSION_PATCH[ ]+[0-9]+" + PLFIT_VERSION_PATCH "${PLFIT_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "#define[ ]+PLFIT_VERSION_PATCH[ ]+([0-9]+)" "\\1" + PLFIT_VERSION_PATCH "${PLFIT_VERSION_PATCH}") + + set(PLFIT_VERSION "${PLFIT_VERSION_MAJOR}.${PLFIT_VERSION_MINOR}.${PLFIT_VERSION_PATCH}") + + # compatibility variables + set(PLFIT_VERSION_STRING "${PLFIT_VERSION}") +endif() + +# behave like a CMake module is supposed to behave +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PLFIT + FOUND_VAR PLFIT_FOUND + REQUIRED_VARS + PLFIT_LIBRARY + PLFIT_INCLUDE_DIR + VERSION_VAR PLFIT_VERSION +) + +# hide the introduced cmake cached variables in cmake GUIs +mark_as_advanced(PLFIT_INCLUDE_DIR) +mark_as_advanced(PLFIT_LIBRARY) + +if(PLFIT_FOUND) + set(PLFIT_LIBRARIES ${PLFIT_LIBRARY}) + set(PLFIT_INCLUDE_DIRS ${PLFIT_INCLUDE_DIR}) +endif() diff --git a/etc/cmake/GetGitRevisionDescription.cmake b/etc/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 0000000..2ebfd40 --- /dev/null +++ b/etc/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,166 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" + @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + ${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + diff-index --quiet HEAD -- + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} "CLEAN" PARENT_SCOPE) + else() + set(${_var} "DIRTY" PARENT_SCOPE) + endif() +endfunction() diff --git a/etc/cmake/GetGitRevisionDescription.cmake.in b/etc/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 0000000..6d8b708 --- /dev/null +++ b/etc/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,41 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/etc/cmake/JoinPaths.cmake b/etc/cmake/JoinPaths.cmake new file mode 100644 index 0000000..32d6d66 --- /dev/null +++ b/etc/cmake/JoinPaths.cmake @@ -0,0 +1,26 @@ +# This module provides function for joining paths +# known from from most languages +# +# Original license: +# SPDX-License-Identifier: (MIT OR CC0-1.0) +# Explicit permission given to distribute this module under +# the terms of the project as described in /LICENSE.rst. +# Copyright 2020 Jan Tojnar +# https://github.com/jtojnar/cmake-snips +# +# Modelled after Python’s os.path.join +# https://docs.python.org/3.7/library/os.path.html#os.path.join +# Windows not supported +function(join_paths joined_path first_path_segment) + set(temp_path "${first_path_segment}") + foreach(current_segment IN LISTS ARGN) + if(NOT ("${current_segment}" STREQUAL "")) + if(IS_ABSOLUTE "${current_segment}") + set(temp_path "${current_segment}") + else() + set(temp_path "${temp_path}/${current_segment}") + endif() + endif() + endforeach() + set(${joined_path} "${temp_path}" PARENT_SCOPE) +endfunction() diff --git a/etc/cmake/PadString.cmake b/etc/cmake/PadString.cmake new file mode 100644 index 0000000..9c60dfc --- /dev/null +++ b/etc/cmake/PadString.cmake @@ -0,0 +1,39 @@ +# ------------------------------------------------------------------------------ +# Macro PAD_STRING +# +# This function pads a string on the left side with a specified character to +# reach the specified length. If the string length is already long enough or +# longer, the string will not be modified. +# +# PAD_STRING(OUT_VARIABLE DESIRED_LENGTH FILL_CHAR VALUE) +# +# OUT_VARIABLE: name of the resulting variable to create +# DESIRED_LENGTH: desired length of the generated string +# FILL_CHAR: character to use for padding +# VALUE: string to pad +# +# Copyright (C) 2011 by Johannes Wienke +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General +# Public License as published by the Free Software Foundation; +# either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# ------------------------------------------------------------------------------ +FUNCTION(PAD_STRING OUT_VARIABLE DESIRED_LENGTH FILL_CHAR VALUE) + STRING(LENGTH "${VALUE}" VALUE_LENGTH) + MATH(EXPR REQUIRED_PADS "${DESIRED_LENGTH} - ${VALUE_LENGTH}") + SET(PAD ${VALUE}) + IF(REQUIRED_PADS GREATER 0) + MATH(EXPR REQUIRED_MINUS_ONE "${REQUIRED_PADS} - 1") + FOREACH(FOO RANGE ${REQUIRED_MINUS_ONE}) + SET(PAD "${FILL_CHAR}${PAD}") + ENDFOREACH() + ENDIF() + SET(${OUT_VARIABLE} "${PAD}" PARENT_SCOPE) +ENDFUNCTION() diff --git a/etc/cmake/PreventInSourceBuilds.cmake b/etc/cmake/PreventInSourceBuilds.cmake new file mode 100644 index 0000000..c157d75 --- /dev/null +++ b/etc/cmake/PreventInSourceBuilds.cmake @@ -0,0 +1,34 @@ +# Original source of this script: +# https://raw.githubusercontent.com/InsightSoftwareConsortium/ITK/master/CMake/PreventInSourceBuilds.cmake +# +# Thanks to the ITK project! +# +# This function will prevent in-source builds +function(AssureOutOfSourceBuilds) + # make sure the user doesn't play dirty with symlinks + get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) + get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) + + # disallow in-source builds + if("${srcdir}" STREQUAL "${bindir}") + message("##########################################################################") + message("# igraph should not be configured & built in the igraph source directory") + message("# You must run cmake in a build directory.") + message("#") + message("# Example:") + message("# mkdir build; cd build; cmake ..; make") + message("#") + message("# NOTE: Given that you already tried to make an in-source build") + message("# CMake have already created several files & directories") + message("# in your source tree. If you are using git, run 'git clean -dfx'") + message("# to start from scratch. If you don't have git, remove") + message("# CMakeCache.txt and the CMakeFiles/ folder from the top of") + message("# the source tree.") + message("#") + message("##########################################################################") + message("") + message(FATAL_ERROR "Quitting configuration") + endif() +endfunction() + +AssureOutOfSourceBuilds() diff --git a/etc/cmake/UseCCacheWhenInstalled.cmake b/etc/cmake/UseCCacheWhenInstalled.cmake new file mode 100644 index 0000000..68a26d1 --- /dev/null +++ b/etc/cmake/UseCCacheWhenInstalled.cmake @@ -0,0 +1,7 @@ +option(USE_CCACHE "Use ccache to speed up compilation if it is installed" ON) +if(USE_CCACHE) + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") + endif() +endif() diff --git a/etc/cmake/attribute_support.cmake b/etc/cmake/attribute_support.cmake new file mode 100644 index 0000000..4b1d392 --- /dev/null +++ b/etc/cmake/attribute_support.cmake @@ -0,0 +1,27 @@ + +# Detect if certain attributes are supported by the compiler +# The result will be used to set macros in include/igraph_config.h + +# GCC-style enum value deprecation + +include(CheckCSourceCompiles) +include(CMakePushCheckState) + +# Only check with Clang and GCC as we assume that the -Werror option is supported +# For other compilers, assume that the attribute is unsupported. +if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") + cmake_push_check_state() + # Require compiling with no warning: + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") + check_c_source_compiles( + "enum { A __attribute__ ((deprecated)) = 0 }; int main(void) { return 0; }" + COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR + ) + cmake_pop_check_state() +else() + set(COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR FALSE) +endif() + +if(COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR) + set(IGRAPH_DEPRECATED_ENUMVAL "__attribute__ ((deprecated))") +endif() diff --git a/etc/cmake/benchmark_helpers.cmake b/etc/cmake/benchmark_helpers.cmake new file mode 100644 index 0000000..fa55a53 --- /dev/null +++ b/etc/cmake/benchmark_helpers.cmake @@ -0,0 +1,43 @@ +include(CMakeParseArguments) + +function(add_benchmark NAME NAMESPACE) + set(TARGET_NAME ${NAMESPACE}_${NAME}) + + add_executable(${TARGET_NAME} EXCLUDE_FROM_ALL ${PROJECT_SOURCE_DIR}/tests/benchmarks/${NAME}.c) + use_all_warnings(${TARGET_NAME}) + add_dependencies(build_benchmarks ${TARGET_NAME}) + target_link_libraries(${TARGET_NAME} PRIVATE igraph) + + # Some benchmarks include plfit_sampling.h from plfit. The following ensures + # that the correct version is included, depending on whether plfit is vendored + target_include_directories( + ${TARGET_NAME} PRIVATE + $<$:$> + $<$:${PLFIT_INCLUDE_DIR}> + ) + + if (MSVC) + # Add MSVC-specific include path for some headers that are missing on Windows + target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/msvc/include) + endif() + + add_custom_command( + TARGET benchmark + POST_BUILD + COMMAND ${TARGET_NAME} + COMMENT "Running benchmark: ${NAME}" + USES_TERMINAL + ) +endfunction() + +function(add_benchmarks) + cmake_parse_arguments( + PARSED "" "" "NAMES;LIBRARIES" ${ARGN} + ) + foreach(NAME ${PARSED_NAMES}) + add_benchmark(${NAME} benchmark) + if(PARSED_LIBRARIES) + target_link_libraries(benchmark_${NAME} PRIVATE ${PARSED_LIBRARIES}) + endif() + endforeach() +endfunction() diff --git a/etc/cmake/bit_operations_support.cmake b/etc/cmake/bit_operations_support.cmake new file mode 100644 index 0000000..5357519 --- /dev/null +++ b/etc/cmake/bit_operations_support.cmake @@ -0,0 +1,92 @@ +include(CheckCXXSourceCompiles) +include(CheckTypeSize) + +cmake_push_check_state(RESET) + +# Check whether the compiler supports the __popcnt64() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0xDEADBEEF; + volatile unsigned long long b; + b = __popcnt64(a); + return 0; + } + " + HAVE__POPCNT64 +) + +# Check whether the compiler supports the __popcnt() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long a = 0xDEADBEEF; + volatile unsigned long long b; + b = __popcnt(a); + return 0; + } + " + HAVE__POPCNT +) + +# Check whether the compiler supports the _BitScanForward64() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0xDEADBEEF; + unsigned long b; + volatile unsigned long c; + c = _BitScanForward64(&b, a) ? b : 64; + return 0; + } + " + HAVE__BITSCANFORWARD64 +) + +# Check whether the compiler supports the _BitScanForward() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long a = 0xDEADBEEF, b; + volatile unsigned long c; + c = _BitScanForward(&b, a) ? b : 64; + return 0; + } + " + HAVE__BITSCANFORWARD +) + +# Check whether the compiler supports the _BitScanReverse64() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0xDEADBEEF; + unsigned long b; + volatile unsigned long c; + c = _BitScanReverse64(&b, a) ? b : 64; + return 0; + } + " + HAVE__BITSCANREVERSE64 +) + +# Check whether the compiler supports the _BitScanReverse() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long a = 0xDEADBEEF, b; + volatile unsigned long c; + c = _BitScanReverse(&b, a) ? b : 64; + return 0; + } + " + HAVE__BITSCANREVERSE +) + +cmake_pop_check_state() diff --git a/etc/cmake/compilers.cmake b/etc/cmake/compilers.cmake new file mode 100644 index 0000000..124f9bc --- /dev/null +++ b/etc/cmake/compilers.cmake @@ -0,0 +1,119 @@ +include(CheckCCompilerFlag) + +# Enable POSIX features. This needs to be set here instead of in source files so +# that it affects CMake-based feature tests. +# +# See: +# - https://pubs.opengroup.org/onlinepubs/007904875/functions/xsh_chap02_02.html +# - https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html +add_compile_definitions(_POSIX_C_SOURCE=200809L) + +if(MSVC) + add_compile_options(/FS) + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) # necessary to compile for UWP +endif() + +if(NOT MSVC) + # Even though we will later use 'no-unknown-warning-option', we perform the test for + # 'unknown-warning-option', without the 'no-' prefix. This is necessary because GCC + # will accept any warning option starting with 'no-', and will not error, yet it still + # prints a message about the unrecognized option. + check_c_compiler_flag("-Wunknown-warning-option" COMPILER_SUPPORTS_UNKNOWN_WARNING_OPTION_FLAG) +endif() + +set( + IGRAPH_WARNINGS_AS_ERRORS ON CACHE BOOL + "Treat warnings as errors with GCC-like compilers" +) + +option(FORCE_COLORED_OUTPUT "Always produce ANSI-colored output (GNU/Clang only)." FALSE) +if(FORCE_COLORED_OUTPUT) + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + add_compile_options(-fdiagnostics-color=always) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + add_compile_options(-fcolor-diagnostics) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + add_compile_options(-fcolor-diagnostics) + endif() +endif() + +macro(use_all_warnings TARGET_NAME) + if(MSVC) + target_compile_options(${TARGET_NAME} PRIVATE + /W4 # enable most warnings, then disable: + /wd4244 # 'conversion' conversion from 'type1' to 'type2', possible loss of data + /wd4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data + /wd4996 # deprecated functions, e.g. 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. + /wd4456 # declaration of 'identifier' hides previous local declaration + /wd4800 # forcing value to 'true' or 'false' (performance warning) + /wd4204 # nonstandard extension used: non-constant aggregate initializer + /wd4701 # potentially uninitialized local variable + /wd4221 # nonstandard extension used: '...': cannot be initialized using address of automatic variable '...' + /wd4127 # conditional expression is constant + /wd4702 # unreachable code + ) + else() + # Notes: + # GCC does not complain when encountering an unsupported "no"-prefixed wanring option such as -Wno-foo. + # Clang does complain, but these complaints can be silenced with -Wno-unknown-warning-option. + # Therefore it is generally safe to use -Wno-... options that are only supported by recent GCC/Clang. + target_compile_options(${TARGET_NAME} PRIVATE + # GCC-style compilers: + $<$: + $<$:-Werror> + -Wall -Wextra -pedantic + -Wstrict-prototypes + -Wno-unused-function -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-sign-compare -Wno-constant-logical-operand + > + $<$:-Wno-unknown-warning-option> + # Intel compiler: + $<$: + # disable #279: controlling expression is constant; affecting assert(condition && "message") + # disable #592: variable "var" is used before its value is set; affecting IGRAPH_UNUSED + -wd279 -wd592 -diag-disable=remark + > + # Intel LLVM: + $<$: + -fp-model=precise # The default 'fast' mode is not compatible with igraph's extensive use of NaN/Inf + > + ) + endif() +endmacro() + +# Helper function to add preprocesor definition of IGRAPH_FILE_BASENAME +# to pass the filename without directory path for debugging use. +# +# Example: +# +# define_file_basename_for_sources(my_target) +# +# Will add -DIGRAPH_FILE_BASENAME="filename" for each source file depended +# on by my_target, where filename is the name of the file. +# +# Source: https://stackoverflow.com/a/27990434/156771 +function(define_file_basename_for_sources targetname) + get_target_property(source_files "${targetname}" SOURCES) + get_target_property(source_dir "${targetname}" SOURCE_DIR) + foreach(sourcefile ${source_files}) + # Turn relative paths into absolute + get_filename_component(source_full_path "${sourcefile}" ABSOLUTE BASE_DIR "${source_dir}") + + # Figure out whether the relative path from the source or the build folder + # is shorter + file(RELATIVE_PATH source_rel_path "${PROJECT_SOURCE_DIR}" "${source_full_path}") + file(RELATIVE_PATH binary_rel_path "${PROJECT_BINARY_DIR}" "${source_full_path}") + string(LENGTH "${source_rel_path}" source_rel_path_length) + string(LENGTH "${binary_rel_path}" binary_rel_path_length) + if(binary_rel_path_length LESS source_rel_path_length) + set(basename "${binary_rel_path}") + else() + set(basename "${source_rel_path}") + endif() + + # Add the IGRAPH_FILE_BASENAME=filename compile definition to the source file + set_property( + SOURCE "${sourcefile}" APPEND + PROPERTY COMPILE_DEFINITIONS "IGRAPH_FILE_BASENAME=\"${basename}\"" + ) + endforeach() +endfunction() diff --git a/etc/cmake/cpack_install_script.cmake b/etc/cmake/cpack_install_script.cmake new file mode 100644 index 0000000..ba50ad5 --- /dev/null +++ b/etc/cmake/cpack_install_script.cmake @@ -0,0 +1,82 @@ +# Custom CPack install script that allows us to whitelist files to be copied +# to the tarball from the root directory, instead of copying the entire root +# directory recursively + +if(CPACK_SOURCE_INSTALLED_DIRECTORIES) + # Make sure that the parser sources are built + execute_process( + COMMAND "${CMAKE_COMMAND}" + --build "${CPACK_PACKAGE_DIRECTORY}" + --target parsersources + RESULT_VARIABLE EXIT_CODE + ) + if(NOT EXIT_CODE EQUAL 0) + message(FATAL_ERROR "Failed to build the parser sources.") + endif() + + # Generate a version file in the build folder if we don't have one in the + # source folder + if(EXISTS "${SOURCE_DIR}/IGRAPH_VERSION") + set(IGRAPH_VERSION_FILE "${SOURCE_DIR}/IGRAPH_VERSION") + else() + execute_process( + COMMAND "${CMAKE_COMMAND}" + --build "${CPACK_PACKAGE_DIRECTORY}" + --target versionfile + RESULT_VARIABLE EXIT_CODE + ) + if(NOT EXIT_CODE EQUAL 0) + message(FATAL_ERROR "Failed to determine the version number of igraph that is being packaged.") + endif() + set(IGRAPH_VERSION_FILE "${CPACK_PACKAGE_DIRECTORY}/IGRAPH_VERSION") + endif() + + list(GET CPACK_BUILD_SOURCE_DIRS 0 SOURCE_DIR) + # This branch runs only if CPack generates the source package, and within + # this branch, CMAKE_CURRENT_BINARY_DIR refers to the root of the staging + # area where the tarball is assembled + file(GLOB FILES_TO_COPY "${SOURCE_DIR}/*.md") + file( + INSTALL ${FILES_TO_COPY} + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" + ) + file( + INSTALL + "${SOURCE_DIR}/AUTHORS" + "${SOURCE_DIR}/CMakeLists.txt" + "${SOURCE_DIR}/CONTRIBUTORS.txt" + "${SOURCE_DIR}/COPYING" + "${SOURCE_DIR}/ChangeLog" + "${SOURCE_DIR}/INSTALL" + "${SOURCE_DIR}/NEWS" + "${SOURCE_DIR}/ONEWS" + "${SOURCE_DIR}/igraph.pc.in" + "${IGRAPH_VERSION_FILE}" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" + ) + file( + INSTALL + "${SOURCE_DIR}/src/config.h.in" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/src" + ) + file( + INSTALL + "${CPACK_PACKAGE_DIRECTORY}/src/io/parsers" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/src/io" + ) + file( + INSTALL + "${SOURCE_DIR}/tools/removeexamples.py" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/tools" + ) + file( + INSTALL + "${SOURCE_DIR}/tools/strip_licenses_from_examples.py" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/tools" + ) + file( + INSTALL + "${CPACK_PACKAGE_DIRECTORY}/doc/html" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/doc" + ) +endif() diff --git a/etc/cmake/create_igraph_version_file.cmake b/etc/cmake/create_igraph_version_file.cmake new file mode 100644 index 0000000..a2f5675 --- /dev/null +++ b/etc/cmake/create_igraph_version_file.cmake @@ -0,0 +1,8 @@ +# CMake script that generates the IGRAPH_VERSION file in the build folder +# +# Script variables that need to be set before calling it via "cmake -P": +# +# * IGRAPH_VERSION should be set to the exact version number +# * VERSION_FILE_PATH should be set to the name of the version file + +FILE(WRITE "${VERSION_FILE_PATH}" "${IGRAPH_VERSION}") diff --git a/etc/cmake/debugging.cmake b/etc/cmake/debugging.cmake new file mode 100644 index 0000000..4cb2a63 --- /dev/null +++ b/etc/cmake/debugging.cmake @@ -0,0 +1,7 @@ +set( + IGRAPH_VERIFY_FINALLY_STACK + "" + CACHE + BOOL + "Verify that the 'finally' stack is cleaned up properly. Useful only in debugging; do not use in production." +) diff --git a/etc/cmake/dependencies.cmake b/etc/cmake/dependencies.cmake new file mode 100644 index 0000000..3e7df4f --- /dev/null +++ b/etc/cmake/dependencies.cmake @@ -0,0 +1,163 @@ +include(helpers) + +include(CheckSymbolExists) +include(CMakePushCheckState) + +# The threading library is not needed for igraph itself, but might be needed +# for tests +include(FindThreads) + +macro(find_dependencies) + # Declare the list of dependencies that _may_ be vendored + set(VENDORABLE_DEPENDENCIES BLAS GLPK LAPACK ARPACK GMP PLFIT) + + # Declare optional dependencies associated with IGRAPH_..._SUPPORT flags + # Note that GLPK is both vendorable and optional + set(OPTIONAL_DEPENDENCIES GLPK OpenMP) + + # Declare configuration options for dependencies + tristate(IGRAPH_USE_INTERNAL_GMP "Compile igraph with internal Mini-GMP" AUTO) + tristate(IGRAPH_USE_INTERNAL_ARPACK "Compile igraph with internal ARPACK" AUTO) + tristate(IGRAPH_USE_INTERNAL_BLAS "Compile igraph with internal BLAS" AUTO) + tristate(IGRAPH_USE_INTERNAL_GLPK "Compile igraph with internal GLPK" AUTO) + tristate(IGRAPH_USE_INTERNAL_LAPACK "Compile igraph with internal LAPACK" AUTO) + tristate(IGRAPH_USE_INTERNAL_PLFIT "Compile igraph with internal plfit" AUTO) + + # Declare dependencies + set(REQUIRED_DEPENDENCIES "") + set(OPTIONAL_DEPENDENCIES FLEX BISON OpenMP) + set(VENDORED_DEPENDENCIES "") + + # Declare minimum supported version for some dependencies + set(GLPK_VERSION_MIN "4.57") # 4.57 is the first version providing glp_on_error() + set(LIBXML2_VERSION_MIN "2.7.4") # 2.7.4 is the first version providing xmlStructuredErrorContext + set(PLFIT_VERSION_MIN "0.9.3") + + # Extend dependencies depending on whether we will be using the vendored + # copies or not + foreach(DEPENDENCY ${VENDORABLE_DEPENDENCIES}) + string(TOUPPER "${DEPENDENCY}" LIBNAME_UPPER) + + if(IGRAPH_USE_INTERNAL_${LIBNAME_UPPER} STREQUAL "AUTO") + find_package(${DEPENDENCY} ${${DEPENDENCY}_VERSION_MIN} QUIET) + if(${LIBNAME_UPPER}_FOUND) + set(IGRAPH_USE_INTERNAL_${LIBNAME_UPPER} OFF) + else() + set(IGRAPH_USE_INTERNAL_${LIBNAME_UPPER} ON) + endif() + endif() + + if(IGRAPH_USE_INTERNAL_${LIBNAME_UPPER}) + list(APPEND VENDORED_DEPENDENCIES ${DEPENDENCY}) + else() + list(APPEND REQUIRED_DEPENDENCIES ${DEPENDENCY}) + endif() + endforeach() + + # For optional dependencies, figure out whether we should attempt to + # link to them based on the value of the IGRAPH_..._SUPPORT option + foreach(DEPENDENCY ${OPTIONAL_DEPENDENCIES}) + string(TOUPPER "${DEPENDENCY}" LIBNAME_UPPER) + + if(IGRAPH_${LIBNAME_UPPER}_SUPPORT STREQUAL "AUTO") + find_package(${DEPENDENCY} ${${DEPENDENCY}_VERSION_MIN} QUIET) + if(${LIBNAME_UPPER}_FOUND) + set(IGRAPH_${LIBNAME_UPPER}_SUPPORT ON) + else() + set(IGRAPH_${LIBNAME_UPPER}_SUPPORT OFF) + endif() + endif() + endforeach() + + # GraphML support is treated separately because the library name is different + if(IGRAPH_GRAPHML_SUPPORT STREQUAL "AUTO") + find_package(LibXml2 ${LIBXML2_VERSION_MIN} QUIET) + if(LibXml2_FOUND) + set(IGRAPH_GRAPHML_SUPPORT ON) + else() + set(IGRAPH_GRAPHML_SUPPORT OFF) + endif() + endif() + + if(NOT IGRAPH_GLPK_SUPPORT) + if(IGRAPH_USE_INTERNAL_GLPK) + list(REMOVE_ITEM VENDORED_DEPENDENCIES GLPK) + else() + list(REMOVE_ITEM REQUIRED_DEPENDENCIES GLPK) + endif() + endif() + + if(IGRAPH_GRAPHML_SUPPORT) + list(APPEND REQUIRED_DEPENDENCIES LibXml2) + endif() + + # Find dependencies + foreach(DEPENDENCY ${REQUIRED_DEPENDENCIES} ${OPTIONAL_DEPENDENCIES}) + list(FIND REQUIRED_DEPENDENCIES "${DEPENDENCY}" INDEX) + set(NEED_THIS_DEPENDENCY NO) + + if(INDEX GREATER_EQUAL 0) + # This is a required dependency, search for it unconditionally. Do + # not use REQUIRED; we will report errors in a single batch at the end + # of the configuration process + set(NEED_THIS_DEPENDENCY YES) + else() + # This is an optional dependency, search for it only if the user did not + # turn it off explicitly + string(TOUPPER "${DEPENDENCY}" LIBNAME_UPPER) + if(NOT DEFINED IGRAPH_${LIBNAME_UPPER}_SUPPORT) + set(NEED_THIS_DEPENDENCY YES) + elseif(IGRAPH_${LIBNAME_UPPER}_SUPPORT) + set(NEED_THIS_DEPENDENCY YES) + endif() + endif() + + if(NEED_THIS_DEPENDENCY AND NOT DEFINED ${DEPENDENCY}_FOUND) + find_package(${DEPENDENCY} ${${DEPENDENCY}_VERSION_MIN}) + endif() + endforeach() + + # Override libraries of vendored dependencies even if they were somehow + # detected above + foreach(DEPENDENCY ${VENDORED_DEPENDENCIES}) + string(TOUPPER "${DEPENDENCY}" LIBNAME_UPPER) + string(TOLOWER "${DEPENDENCY}" LIBNAME_LOWER) + if(IGRAPH_USE_INTERNAL_${LIBNAME_UPPER}) + set(${LIBNAME_UPPER}_LIBRARIES "") + set(${LIBNAME_UPPER}_FOUND 1) + set(${LIBNAME_UPPER}_IS_VENDORED 1) + set(INTERNAL_${LIBNAME_UPPER} 1) + endif() + endforeach() + + # Export some aliases that will be used in config.h + set(HAVE_GLPK ${GLPK_FOUND}) + set(HAVE_GMP ${GMP_FOUND}) + set(HAVE_LIBXML ${LIBXML2_FOUND}) + + # Check whether we need to link to the math library + if(NOT DEFINED CACHE{NEED_LINKING_AGAINST_LIBM}) + cmake_push_check_state() + set(CMAKE_REQUIRED_QUIET ON) + check_symbol_exists(sinh "math.h" SINH_FUNCTION_EXISTS) + if(NOT SINH_FUNCTION_EXISTS) + unset(SINH_FUNCTION_EXISTS CACHE) + list(APPEND CMAKE_REQUIRED_LIBRARIES m) + check_symbol_exists(sinh "math.h" SINH_FUNCTION_EXISTS) + if(SINH_FUNCTION_EXISTS) + set(NEED_LINKING_AGAINST_LIBM True CACHE BOOL "" FORCE) + else() + message(FATAL_ERROR "Failed to figure out how to link to the math library on this platform") + endif() + endif() + unset(SINH_FUNCTION_EXISTS CACHE) + cmake_pop_check_state() + endif() + + if(NEED_LINKING_AGAINST_LIBM) + find_library(MATH_LIBRARY m) + endif() + + mark_as_advanced(MATH_LIBRARY) + mark_as_advanced(NEED_LINKING_AGAINST_LIBM) +endmacro() diff --git a/etc/cmake/features.cmake b/etc/cmake/features.cmake new file mode 100644 index 0000000..6663102 --- /dev/null +++ b/etc/cmake/features.cmake @@ -0,0 +1,25 @@ +include(helpers) + +include(tls) +include(lto) + +option(IGRAPH_GLPK_SUPPORT "Compile igraph with GLPK support" ON) +tristate(IGRAPH_GRAPHML_SUPPORT "Compile igraph with GraphML support" AUTO) +tristate(IGRAPH_OPENMP_SUPPORT "Use OpenMP for parallelization" AUTO) + +set(IGRAPH_INTEGER_SIZE AUTO CACHE STRING "Set size of igraph integers") +set_property(CACHE IGRAPH_INTEGER_SIZE PROPERTY STRINGS AUTO 32 64) + +if(IGRAPH_INTEGER_SIZE STREQUAL AUTO) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(IGRAPH_INTEGER_SIZE 64) + else() + set(IGRAPH_INTEGER_SIZE 32) + endif() +endif() + +option(FLEX_KEEP_LINE_NUMBERS "Keep references to the original line numbers in generated Flex/Bison parser files" OFF) +mark_as_advanced(FLEX_KEEP_LINE_NUMBERS) + +option(BUILD_FUZZING "Build fuzz targets and enable fuzzer instrumentation" OFF) +mark_as_advanced(BUILD_FUZZING) diff --git a/etc/cmake/fuzz_helpers.cmake b/etc/cmake/fuzz_helpers.cmake new file mode 100644 index 0000000..bc272e7 --- /dev/null +++ b/etc/cmake/fuzz_helpers.cmake @@ -0,0 +1,16 @@ + +function(add_fuzzer NAME) + set(TARGET_NAME fuzzer_${NAME}) + + add_executable(${TARGET_NAME} EXCLUDE_FROM_ALL ${PROJECT_SOURCE_DIR}/fuzzing/${NAME}.cpp) + + add_dependencies(build_fuzzers ${TARGET_NAME}) + + target_link_libraries(${TARGET_NAME} PRIVATE igraph) + + # The -fsanitize=fuzzer-no-link is already added by the top-level CMakeLists.txt + # for general fuzzer instrumentation. Additionally, we need -fsanitize=fuzzer + # for the fuzz targets, which do not contain a main() function, to link in the + # fuzz driver. See https://llvm.org/docs/LibFuzzer.html + target_link_options(${TARGET_NAME} PRIVATE -fsanitize=fuzzer) +endfunction() diff --git a/etc/cmake/generate_tags_file.cmake b/etc/cmake/generate_tags_file.cmake new file mode 100644 index 0000000..dbb8484 --- /dev/null +++ b/etc/cmake/generate_tags_file.cmake @@ -0,0 +1,46 @@ +# Creates a ctags-compatible tags file from a set of XML files by extracting +# the IDs found in the XML files. +# +# Parameters of the script: +# +# - INPUT_FILES: list of input files to process, with absolute pathnames +# - OUTPUT_FILE: the output file to write the tags into + +string(REPLACE " " ";" INPUT_FILE_LIST "${INPUT_FILES}") + +set(EXTRACTED_IDS "") + +foreach(INPUT_FILE ${INPUT_FILE_LIST}) + file(READ "${INPUT_FILE}" CONTENTS) + + # Replace newlines with semicolons. This is a hack and we should escape + # semicolons first if we wanted to do this properly; however, here we are + # only interested in XML IDs and they don't have semicolons + string(REPLACE "\n" ";" LINES "${CONTENTS}") + foreach(_line ${LINES}) + string(REGEX MATCHALL "id=\"[^-\"]*\"" MATCH_RESULT "${_line}") + if(MATCH_RESULT) + foreach(MATCH ${MATCH_RESULT}) + string(REGEX REPLACE "id=\"(.*)\"" "\\1" EXTRACTED_ID "${MATCH}") + list(APPEND EXTRACTED_IDS "${EXTRACTED_ID}") + endforeach() + endif() + endforeach() +endforeach() + +list(SORT EXTRACTED_IDS) +string(REPLACE ";" "\t\t\n" TAGS_OUTPUT "${EXTRACTED_IDS}") +string(APPEND TAGS_OUTPUT "\t\t\n") +string(SHA1 TAGS_OUTPUT_HASH "${TAGS_OUTPUT}") + +# Update the output file only if it changed; this prevents CMake from calling +# source-highlight if there is no point in rebuilding the highlighted +# source files +if(EXISTS "${OUTPUT_FILE}") + file(SHA1 "${OUTPUT_FILE}" OUTPUT_FILE_HASH) + if(NOT "${OUTPUT_FILE_HASH}" STREQUAL "${TAGS_OUTPUT_HASH}") + file(WRITE "${OUTPUT_FILE}" "${TAGS_OUTPUT}") + endif() +else() + file(WRITE "${OUTPUT_FILE}" "${TAGS_OUTPUT}") +endif() diff --git a/etc/cmake/helpers.cmake b/etc/cmake/helpers.cmake new file mode 100644 index 0000000..df9ddaf --- /dev/null +++ b/etc/cmake/helpers.cmake @@ -0,0 +1,6 @@ +macro(tristate OPTION_NAME DESCRIPTION DEFAULT_VALUE) + set(${OPTION_NAME} "${DEFAULT_VALUE}" CACHE STRING "${DESCRIPTION}") + set_property(CACHE ${OPTION_NAME} PROPERTY STRINGS AUTO ON OFF) +endmacro() + +include(PadString) diff --git a/etc/cmake/ieee754_endianness.cmake b/etc/cmake/ieee754_endianness.cmake new file mode 100644 index 0000000..fc66751 --- /dev/null +++ b/etc/cmake/ieee754_endianness.cmake @@ -0,0 +1,54 @@ +include(CheckCSourceRuns) + +cmake_push_check_state(RESET) + +# Check whether IEEE754 doubles are laid out in little-endian order. We do this +# only when not cross-compiling; during cross-compilation, the host architecture +# might have different endianness conventions than the target, and we are running +# the test on the host here +if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) + # If we are cross-compiling and we have no emulator, let's just assume that + # IEEE754 doubles use the same endianness as uint64_t + set(IEEE754_DOUBLE_ENDIANNESS_MATCHES YES) + message(WARNING "\ +igraph is being cross-compiled, therefore we cannot validate whether the \ +endianness of IEEE754 doubles is the same as the endianness of uint64_t. \ +Most likely it is, unless you are compiling for some esoteric platform, \ +in which case you need make sure that this is the case on your own.\ +") +else() + if(NOT DEFINED CACHE{IEEE754_DOUBLE_ENDIANNESS_MATCHES}) + try_run( + IEEE754_DOUBLE_ENDIANNESS_TEST_EXIT_CODE + IEEE754_DOUBLE_ENDIANNESS_TEST_COMPILES + ${CMAKE_BINARY_DIR} + ${PROJECT_SOURCE_DIR}/etc/cmake/ieee754_endianness_check.c + RUN_OUTPUT_VARIABLE IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT + ) + # Strip trailing newline, which is necessary on some platforms (such as node.js) + # to complete printing the output. + string(STRIP "${IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT}" IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT) + if(IEEE754_DOUBLE_ENDIANNESS_TEST_EXIT_CODE EQUAL 0) + if(IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT STREQUAL "OK") + set(TEST_RESULT YES) + else() + set(TEST_RESULT NO) + endif() + else() + message(FATAL_ERROR "IEEE754 double endianness test terminated abnormally.") + endif() + + set( + IEEE754_DOUBLE_ENDIANNESS_MATCHES ${TEST_RESULT} CACHE BOOL + "Specifies whether the endianness of IEEE754 doubles is the same as the endianness of uint64_t." + FORCE + ) + mark_as_advanced(IEEE754_DOUBLE_ENDIANNESS_MATCHES) + endif() +endif() + +cmake_pop_check_state() + +if(NOT IEEE754_DOUBLE_ENDIANNESS_MATCHES) + message(FATAL_ERROR "igraph only supports platforms where IEEE754 doubles have the same endianness as uint64_t.") +endif() diff --git a/etc/cmake/ieee754_endianness_check.c b/etc/cmake/ieee754_endianness_check.c new file mode 100644 index 0000000..6296825 --- /dev/null +++ b/etc/cmake/ieee754_endianness_check.c @@ -0,0 +1,24 @@ +/* Checks whether the endianness of IEEE754 doubles matches the endianness of + * uint64_t on the target system. This is needed to ensure that the trick we + * employ in igraph_rng_get_unif01() works. */ + +#include +#include + +union { + uint64_t as_uint64_t; + double as_double; +} value; + +int main(void) { + value.as_uint64_t = 4841376218035192321ULL; + if (value.as_double == 4510218239279617.0) { + /* endianness of uint64_t and double match */ + printf("OK\n"); + } + /* We always return 0, even for a negative result, this is because we + * need to tell on the CMake side whether a compiler misconfiguration + * aborted our program, which can then be detected from a nonzero exit + * code. */ + return 0; +} diff --git a/etc/cmake/igraph-config.cmake.in b/etc/cmake/igraph-config.cmake.in new file mode 100644 index 0000000..27ceda2 --- /dev/null +++ b/etc/cmake/igraph-config.cmake.in @@ -0,0 +1,40 @@ +# igraph-config.cmake +# +# igraph CMake package +# +# The following variables are set: +# +# - IGRAPH_VERSION - The igraph version string. +# - IGRAPH_INTEGER_SIZE - The integer size igraph was configured with (32 or 64). +# - IGRAPH_GLPK_SUPPORT - Whether igraph was compiled with GLPK support. +# - IGRAPH_GRAPHML_SUPPORT - Whether igraph was compiled with GraphML support. +# + +set(IGRAPH_VERSION "@PACKAGE_VERSION_BASE@") +set(IGRAPH_INTEGER_SIZE @IGRAPH_INTEGER_SIZE@) +set(IGRAPH_GLPK_SUPPORT @IGRAPH_GLPK_SUPPORT@) +set(IGRAPH_GRAPHML_SUPPORT @IGRAPH_GRAPHML_SUPPORT@) +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/igraph-targets.cmake") + +# Check whether C++ support is enabled; this is needed to ensure that programs +# that are dependent on igraph will get linked with the C++ linker and not the +# "plain" C linker +get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) +if("CXX" IN_LIST LANGUAGES) + # This is okay +else() + message(FATAL_ERROR "Please enable C++ support in your project if you are linking to igraph.") +endif() + +# Turn on CMP0012 because the following if() conditionals will use "ON" and +# "OFF" verbatim and they must be evaluated as booleans +cmake_policy(PUSH) +cmake_policy(SET CMP0012 NEW) +if(@IGRAPH_OPENMP_SUPPORT@) + find_package(OpenMP) +endif() +cmake_policy(POP) + +check_required_components(igraph) diff --git a/etc/cmake/lto.cmake b/etc/cmake/lto.cmake new file mode 100644 index 0000000..efcbd93 --- /dev/null +++ b/etc/cmake/lto.cmake @@ -0,0 +1,23 @@ +include(helpers) + +tristate(IGRAPH_ENABLE_LTO "Enable link-time optimization" OFF) + +include(CheckIPOSupported) + +if(IGRAPH_ENABLE_LTO) + # this matches both ON and AUTO + check_ipo_supported(RESULT IPO_SUPPORTED OUTPUT IPO_NOT_SUPPORTED_REASON) + if(IGRAPH_ENABLE_LTO STREQUAL "AUTO") + # autodetection + set(IGRAPH_ENABLE_LTO ${IPO_SUPPORTED}) + if(IPO_SUPPORTED) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + endif() + elseif(IPO_SUPPORTED) + # user wanted LTO and the compiler supports it + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + # user wanted LTO and the compiler does not support it + message(FATAL_ERROR "Link-time optimization not supported on this compiler") + endif() +endif() diff --git a/etc/cmake/packaging.cmake b/etc/cmake/packaging.cmake new file mode 100644 index 0000000..4887b6c --- /dev/null +++ b/etc/cmake/packaging.cmake @@ -0,0 +1,70 @@ +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "igraph library") +set(CPACK_PACKAGE_HOMEPAGE_URL "https://igraph.org") +set(CPACK_PACKAGE_VENDOR "The igraph development team") + +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING") + +if(TARGET html) + # Alias "dist" to "package_source" + add_custom_target(dist + COMMAND "${CMAKE_COMMAND}" + --build "${CMAKE_BINARY_DIR}" + --target package_source + VERBATIM + USES_TERMINAL + ) + + # We want to include the HTML docs in the source package so add a dependency + add_dependencies(dist html) +else() + add_custom_target(dist + COMMAND "${CMAKE_COMMAND}" -E false + COMMENT + "Cannot build source tarball since the HTML documentation was not built." + VERBATIM + USES_TERMINAL + ) +endif() + +############################################################################# +## Configuration of the source package +############################################################################# + +# Set source package name and format +set(CPACK_SOURCE_PACKAGE_FILE_NAME "igraph-${CMAKE_PROJECT_VERSION}") +set(CPACK_SOURCE_GENERATOR "TGZ") + +# Declare what to include in the source tarball. Unfortunately we can only +# declare full directories here, not individual files. +set( + CPACK_SOURCE_INSTALLED_DIRECTORIES + "${CMAKE_SOURCE_DIR}/doc;/doc" + "${CMAKE_SOURCE_DIR}/etc/cmake;/etc/cmake" + "${CMAKE_SOURCE_DIR}/examples;/examples" + "${CMAKE_SOURCE_DIR}/include;/include" + "${CMAKE_SOURCE_DIR}/interfaces;/interfaces" + "${CMAKE_SOURCE_DIR}/msvc/include;/msvc/include" + "${CMAKE_SOURCE_DIR}/src;/src" + "${CMAKE_SOURCE_DIR}/tests;/tests" + "${CMAKE_SOURCE_DIR}/vendor;/vendor" +) + +# CPack is pretty dumb as it can only copy full directories (sans the ignored +# files) to the target tarball by default. In some cases it is easier to +# whitelist files to be copied; we use CPACK_INSTALL_SCRIPT for that. +set(CPACK_INSTALL_SCRIPT "${CMAKE_SOURCE_DIR}/etc/cmake/cpack_install_script.cmake") + +# Ignore the build and all hidden folders +set( + CPACK_SOURCE_IGNORE_FILES + "\\\\..*/" + "\\\\.l$" + "\\\\.y$" + "${CMAKE_SOURCE_DIR}/build" +) + +############################################################################# +## Now we can include CPack +############################################################################# + +include(CPack) diff --git a/etc/cmake/pkgconfig_helpers.cmake b/etc/cmake/pkgconfig_helpers.cmake new file mode 100644 index 0000000..d0d9ecb --- /dev/null +++ b/etc/cmake/pkgconfig_helpers.cmake @@ -0,0 +1,75 @@ +# Helper functions for generating a nicely formatted igraph.pc file from +# igraph.pc.in + +include(JoinPaths) +include(CheckCXXSymbolExists) + +# Converts the name of a library file (or framework on macOS) into an +# appropriate linker flag (-lsomething or -framework something.framework). +# Returns the input intact if its extension does not look like a shared or +# static library extension. +function(convert_library_file_to_flags output_variable input) + get_filename_component(input_filename ${input} NAME_WE) + get_filename_component(input_extension ${input} LAST_EXT) + if(input_extension STREQUAL ${CMAKE_SHARED_LIBRARY_SUFFIX} OR input_extension STREQUAL ${CMAKE_STATIC_LIBRARY_SUFFIX}) + string(REGEX REPLACE "^${CMAKE_SHARED_LIBRARY_PREFIX}" "" input_stripped ${input_filename}) + set("${output_variable}" "-l${input_stripped}" PARENT_SCOPE) + elseif(APPLE AND input_extension STREQUAL ".framework") + set("${output_variable}" "-framework ${input_filename}" PARENT_SCOPE) + else() + set("${output_variable}" "${input}" PARENT_SCOPE) + endif() +endfunction() + +if(MATH_LIBRARY) + set(PKGCONFIG_LIBS_PRIVATE "-lm") +else() + set(PKGCONFIG_LIBS_PRIVATE "") +endif() +set(PKGCONFIG_REQUIRES_PRIVATE "") + +if(NOT MSVC) + check_cxx_symbol_exists(_LIBCPP_VERSION "vector" USING_LIBCXX) + check_cxx_symbol_exists(__GLIBCXX__ "vector" USING_LIBSTDCXX) + if(USING_LIBCXX) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lc++") + elseif(USING_LIBSTDCXX) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lstdc++") + endif() +endif() + +if(IGRAPH_GRAPHML_SUPPORT) + set(PKGCONFIG_REQUIRES_PRIVATE "${PKGCONFIG_REQUIRES_PRIVATE} libxml-2.0") +endif() +if(NOT IGRAPH_USE_INTERNAL_GMP) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lgmp") +endif() +if(NOT IGRAPH_USE_INTERNAL_BLAS) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lblas") +endif() +if(IGRAPH_GLPK_SUPPORT AND NOT IGRAPH_USE_INTERNAL_GLPK) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lglpk") +endif() +if(NOT IGRAPH_USE_INTERNAL_LAPACK) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -llapack") +endif() +if(NOT IGRAPH_USE_INTERNAL_ARPACK) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -larpack") +endif() +if(NOT IGRAPH_USE_INTERNAL_PLFIT) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lplfit") +endif() +if(IGRAPH_OPENMP_SUPPORT AND OpenMP_FOUND) + foreach(CURRENT_LIB ${OpenMP_C_LIB_NAMES}) + convert_library_file_to_flags(CURRENT_LIB "${OpenMP_${CURRENT_LIB}_LIBRARY}") + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} ${CURRENT_LIB}") + endforeach() +endif() + +join_paths(PKGCONFIG_LIBDIR "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}") +join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") +configure_file( + ${PROJECT_SOURCE_DIR}/igraph.pc.in + ${PROJECT_BINARY_DIR}/igraph.pc + @ONLY +) diff --git a/etc/cmake/run_legacy_test.cmake b/etc/cmake/run_legacy_test.cmake new file mode 100644 index 0000000..ef09642 --- /dev/null +++ b/etc/cmake/run_legacy_test.cmake @@ -0,0 +1,73 @@ +# Runs a legacy autotools-based test with a file containing the expected output +# +# Parameters of the script: +# +# - TEST_EXECUTABLE: full path of the compiled test executable +# - EXPECTED_OUTPUT_FILE: full path of the file containing the expected output +# - OBSERVED_OUTPUT_FILE: full path of the file where the observed output +# can be written +# - DIFF_FILE: full path of the file where the differences between the expectd +# and the observed output should be written +# - DIFF_TOOL: full path to a "diff" tool on the system of the user, if present +# - FC_TOOL: full path to a "fc" tool on the system of the user, if present +# - IGRAPH_VERSION: version string of igraph that should be replaced in +# expected outputs + +get_filename_component(WORK_DIR ${EXPECTED_OUTPUT_FILE} DIRECTORY) + +execute_process( + COMMAND ${CROSSCOMPILING_EMULATOR} ${TEST_EXECUTABLE} + WORKING_DIRECTORY ${WORK_DIR} + RESULT_VARIABLE ERROR_CODE + OUTPUT_VARIABLE OBSERVED_OUTPUT +) + +if(ERROR_CODE EQUAL 77) + message(STATUS "Test skipped") +elseif(ERROR_CODE) + set(MESSAGE "Test exited abnormally with error: ${ERROR_CODE}") + file(WRITE ${OBSERVED_OUTPUT_FILE} "${MESSAGE}\n=========================================\n${OBSERVED_OUTPUT}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E cat "${OBSERVED_OUTPUT_FILE}") + file(REMOVE ${DIFF_FILE}) + message(FATAL_ERROR "Exiting test.") +else() + string(REPLACE ${IGRAPH_VERSION} "\@VERSION\@" OBSERVED_OUTPUT "${OBSERVED_OUTPUT}") + file(WRITE ${OBSERVED_OUTPUT_FILE} "${OBSERVED_OUTPUT}") + + execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files --ignore-eol + ${EXPECTED_OUTPUT_FILE} ${OBSERVED_OUTPUT_FILE} + RESULT_VARIABLE ARE_DIFFERENT + ) + + if(ARE_DIFFERENT) + if(DIFF_TOOL) + execute_process( + COMMAND ${DIFF_TOOL} -u ${EXPECTED_OUTPUT_FILE} ${OBSERVED_OUTPUT_FILE} + OUTPUT_FILE ${DIFF_FILE} + ) + elseif(FC_TOOL) + file(TO_NATIVE_PATH "${EXPECTED_OUTPUT_FILE}" REAL_EXPECTED_OUTPUT_FILE) + file(TO_NATIVE_PATH "${OBSERVED_OUTPUT_FILE}" REAL_OBSERVED_OUTPUT_FILE) + execute_process( + COMMAND ${FC_TOOL} /A ${REAL_EXPECTED_OUTPUT_FILE} ${REAL_OBSERVED_OUTPUT_FILE} + OUTPUT_FILE ${DIFF_FILE} + ) + endif() + + message(STATUS "Test case output differs from the expected output") + if(EXISTS ${DIFF_FILE}) + message(STATUS "See diff below:") + message(STATUS "-------------------------------------------------------") + execute_process(COMMAND "${CMAKE_COMMAND}" -E cat "${DIFF_FILE}") + message(STATUS "-------------------------------------------------------") + else() + message(STATUS "Diff omitted; no diff tool was installed.") + endif() + message(FATAL_ERROR "Exiting test.") + else() + file(REMOVE ${DIFF_FILE}) + endif() + + file(REMOVE ${OBSERVED_OUTPUT_FILE}) +endif() diff --git a/etc/cmake/safe_math_support.cmake b/etc/cmake/safe_math_support.cmake new file mode 100644 index 0000000..a9e0ef4 --- /dev/null +++ b/etc/cmake/safe_math_support.cmake @@ -0,0 +1,18 @@ +include(CheckCXXSourceCompiles) + +# Check whether the compiler supports the __builtin_add_overflow() and __builtin_mul_overflow() +# builtins. These are present in recent GCC-compatible compilers. +cmake_push_check_state(RESET) + +check_cxx_source_compiles(" + int main(void) { + long long a=1, b=2, c; + __builtin_add_overflow(a, b, &c); + __builtin_mul_overflow(a, b, &c); + return 0; + } + " + HAVE_BUILTIN_OVERFLOW +) + +cmake_pop_check_state() diff --git a/etc/cmake/sanitizers.cmake b/etc/cmake/sanitizers.cmake new file mode 100644 index 0000000..3e1163a --- /dev/null +++ b/etc/cmake/sanitizers.cmake @@ -0,0 +1,88 @@ +# +# Copyright (C) 2018 by George Cave - gcave@stablecoder.ca +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +set( + USE_SANITIZER + "" + CACHE + STRING + "Compile with a sanitizer. Options are: Address, Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined'" + ) + +function(append value) + foreach(variable ${ARGN}) + set(${variable} "${${variable}} ${value}" PARENT_SCOPE) + endforeach(variable) +endfunction() + +if(USE_SANITIZER) + + if(UNIX) + + append("-fno-omit-frame-pointer" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + + if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG") + append("-Og" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + + if(USE_SANITIZER MATCHES "([Aa]ddress);([Uu]ndefined)" + OR USE_SANITIZER MATCHES "([Uu]ndefined);([Aa]ddress)") + message(STATUS "Building with Address, Undefined sanitizers") + append("-fsanitize=address,undefined" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + elseif("${USE_SANITIZER}" MATCHES "([Aa]ddress)") + # Optional: -fno-optimize-sibling-calls -fsanitize-address-use-after-scope + message(STATUS "Building with Address sanitizer") + append("-fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + elseif(USE_SANITIZER MATCHES "([Mm]emory([Ww]ith[Oo]rigins)?)") + # Optional: -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 + append("-fsanitize=memory" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(USE_SANITIZER MATCHES "([Mm]emory[Ww]ith[Oo]rigins)") + message(STATUS "Building with MemoryWithOrigins sanitizer") + append("-fsanitize-memory-track-origins" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + else() + message(STATUS "Building with Memory sanitizer") + endif() + elseif(USE_SANITIZER MATCHES "([Uu]ndefined)") + message(STATUS "Building with Undefined sanitizer") + append("-fsanitize=undefined" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + if(EXISTS "${BLACKLIST_FILE}") + append("-fsanitize-blacklist=${BLACKLIST_FILE}" CMAKE_C_FLAGS + CMAKE_CXX_FLAGS) + endif() + elseif(USE_SANITIZER MATCHES "([Tt]hread)") + message(STATUS "Building with Thread sanitizer") + append("-fsanitize=thread" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + elseif(USE_SANITIZER MATCHES "([Ll]eak)") + message(STATUS "Building with Leak sanitizer") + append("-fsanitize=leak" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + else() + message( + FATAL_ERROR "Unsupported value of USE_SANITIZER: ${USE_SANITIZER}") + endif() + elseif(MSVC) + if(USE_SANITIZER MATCHES "([Aa]ddress)") + message(STATUS "Building with Address sanitizer") + append("-fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + else() + message( + FATAL_ERROR + "This sanitizer not yet supported in the MSVC environment: ${USE_SANITIZER}" + ) + endif() + else() + message(FATAL_ERROR "USE_SANITIZER is not supported on this platform.") + endif() + +endif() diff --git a/etc/cmake/summary.cmake b/etc/cmake/summary.cmake new file mode 100644 index 0000000..4c189f1 --- /dev/null +++ b/etc/cmake/summary.cmake @@ -0,0 +1,105 @@ +function(print_bool HEADING VAR) + if(${VAR}) + set(LABEL "yes") + else() + set(LABEL "no") + endif() + print_str(${HEADING} ${LABEL}) +endfunction() + +function(print_str HEADING LABEL) + string(LENGTH "${HEADING}" HEADING_LENGTH) + math(EXPR REMAINING_WIDTH "30 - ${HEADING_LENGTH}") + + if("${LABEL}" STREQUAL "") + pad_string(PADDED ${REMAINING_WIDTH} " " "${ARGN}") + else() + pad_string(PADDED ${REMAINING_WIDTH} " " "${LABEL}") + endif() + + message(STATUS "${HEADING}: ${PADDED}") +endfunction() + +############################################################################# + +set(ALL_DEPENDENCIES ${REQUIRED_DEPENDENCIES} ${OPTIONAL_DEPENDENCIES} ${VENDORED_DEPENDENCIES}) +list(SORT ALL_DEPENDENCIES CASE INSENSITIVE) + +message(STATUS " ") +message(STATUS "-----[ Build configuration ]----") +print_str("Version" "${PACKAGE_VERSION}") +print_str("CMake build type" "${CMAKE_BUILD_TYPE}" "default") +if(BUILD_SHARED_LIBS) + message(STATUS "Library type: shared") +else() + message(STATUS "Library type: static") +endif() +if(${IGRAPH_INTEGER_SIZE} STREQUAL "AUTO") + print_str("igraph_integer_t size" "auto") +elseif(${IGRAPH_INTEGER_SIZE} STREQUAL 64) + print_str("igraph_integer_t size" "64 bits") +elseif(${IGRAPH_INTEGER_SIZE} STREQUAL 32) + print_str("igraph_integer_t size" "32 bits") +else() + print_str("igraph_integer_t size" "INVALID") +endif() +if(USE_CCACHE) + if(CCACHE_PROGRAM) + message(STATUS "Compiler cache: ccache") + endif() +else() + message(STATUS "Compiler cache: disabled") +endif() + +message(STATUS " ") +message(STATUS "----------[ Features ]----------") +print_bool("GLPK for optimization" IGRAPH_GLPK_SUPPORT) +print_bool("Reading GraphML files" IGRAPH_GRAPHML_SUPPORT) +print_bool("Thread-local storage" IGRAPH_ENABLE_TLS) +print_bool("Link-time optimization" IGRAPH_ENABLE_LTO) +message(STATUS " ") + +message(STATUS "--------[ Dependencies ]--------") +foreach(DEPENDENCY ${ALL_DEPENDENCIES}) + list(FIND VENDORED_DEPENDENCIES "${DEPENDENCY}" INDEX) + if(INDEX EQUAL -1) + print_bool("${DEPENDENCY}" ${DEPENDENCY}_FOUND) + else() + print_str("${DEPENDENCY}" "vendored") + endif() +endforeach() +message(STATUS " ") + +message(STATUS "-----------[ Testing ]----------") +if(DIFF_TOOL) + print_str("Diff tool" "diff") +elseif(FC_TOOL) + print_str("Diff tool" "fc") +else() + print_str("Diff tool" "not found") +endif() +print_str("Sanitizers" "${USE_SANITIZER}" "none") +print_bool("Code coverage" IGRAPH_ENABLE_CODE_COVERAGE) +print_bool("Verify 'finally' stack" IGRAPH_VERIFY_FINALLY_STACK) +message(STATUS " ") + +message(STATUS "--------[ Documentation ]-------") +print_bool("HTML" HTML_DOC_BUILD_SUPPORTED) +print_bool("PDF" PDF_DOC_BUILD_SUPPORTED) +print_bool("INFO" INFO_DOC_BUILD_SUPPORTED) +message(STATUS " ") + +set(MISSING_DEPENDENCIES) +foreach(DEPENDENCY ${REQUIRED_DEPENDENCIES}) + if(NOT ${DEPENDENCY}_FOUND) + list(APPEND MISSING_DEPENDENCIES ${DEPENDENCY}) + endif() +endforeach() + +if(MISSING_DEPENDENCIES) + list(JOIN MISSING_DEPENDENCIES ", " GLUED) + message(FATAL_ERROR "The following dependencies are missing: ${GLUED}") +else() + message(STATUS "igraph configured successfully.") + message(STATUS " ") +endif() diff --git a/etc/cmake/test_helpers.cmake b/etc/cmake/test_helpers.cmake new file mode 100644 index 0000000..f83bb9d --- /dev/null +++ b/etc/cmake/test_helpers.cmake @@ -0,0 +1,120 @@ +include(CMakeParseArguments) + +find_program(DIFF_TOOL diff) +if(NOT DIFF_TOOL) + find_program(FC_TOOL fc) +endif() + +function(add_legacy_test FOLDER NAME NAMESPACE) + set(TARGET_NAME ${NAMESPACE}_${NAME}) + set(TEST_NAME "${NAMESPACE}::${NAME}") + + add_executable(${TARGET_NAME} EXCLUDE_FROM_ALL ${PROJECT_SOURCE_DIR}/${FOLDER}/${NAME}.c) + use_all_warnings(${TARGET_NAME}) + add_dependencies(build_tests ${TARGET_NAME}) + # Specify linking with test_utilities *before* linking with igraph, to avoid + # duplicating libigraph.a. See https://github.com/igraph/igraph/issues/2394 + if (NAMESPACE STREQUAL "test") + target_link_libraries(${TARGET_NAME} PRIVATE test_utilities) + endif() + target_link_libraries(${TARGET_NAME} PRIVATE igraph) + + # Some tests depend on internal igraph headers so we also have to add src/ + # to the include path even though it's not part of the public API + target_include_directories( + ${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src + ) + + # Some tests include cs.h from CXSparse + target_include_directories( + ${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/vendor/cs + ) + + if (MSVC) + # Add MSVC-specific include path for some headers that are missing on Windows + target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/msvc/include) + endif() + + set(EXPECTED_OUTPUT_FILE ${CMAKE_SOURCE_DIR}/${FOLDER}/${NAME}.out) + set(OBSERVED_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.out) + set(DIFF_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.diff) + get_filename_component(WORK_DIR ${EXPECTED_OUTPUT_FILE} DIRECTORY) + + if(EXISTS ${EXPECTED_OUTPUT_FILE}) + get_property(CROSSCOMPILING_EMULATOR TARGET ${TARGET_NAME} PROPERTY CROSSCOMPILING_EMULATOR) + add_test( + NAME ${TEST_NAME} + COMMAND ${CMAKE_COMMAND} + -DTEST_EXECUTABLE=$ + -DEXPECTED_OUTPUT_FILE=${EXPECTED_OUTPUT_FILE} + -DOBSERVED_OUTPUT_FILE=${OBSERVED_OUTPUT_FILE} + -DDIFF_FILE=${DIFF_FILE} + -DDIFF_TOOL=${DIFF_TOOL} + -DFC_TOOL=${FC_TOOL} + -DIGRAPH_VERSION=${PACKAGE_VERSION} + "-DCROSSCOMPILING_EMULATOR=${CROSSCOMPILING_EMULATOR}" + -P ${CMAKE_SOURCE_DIR}/etc/cmake/run_legacy_test.cmake + ) + set_property(TEST ${TEST_NAME} PROPERTY SKIP_REGULAR_EXPRESSION "Test skipped") + else() + add_test( + NAME ${TEST_NAME} + COMMAND ${TARGET_NAME} + WORKING_DIRECTORY ${WORK_DIR} + ) + set_property(TEST ${TEST_NAME} PROPERTY SKIP_RETURN_CODE 77) + endif() + if (WIN32 AND BUILD_SHARED_LIBS) + # On Windows the built igraph.dll is not automatically found by the tests. We therefore + # add the dir that contains the built igraph.dll to the path environment variable + # so that igraph.dll is found when running the tests. + SET(IGRAPH_LIBDIR $) + + # The next line is necessitated by MinGW on Windows. MinGW uses forward slashes in + # IGRAPH_LIBDIR, but we need to supply CTest with backslashes because CTest is executed + # in a cmd.exe shell. We therefore explicitly ensure that that path is transformed to a + # native path. + file(TO_NATIVE_PATH "${IGRAPH_LIBDIR}" IGRAPH_LIBDIR) + + # Semicolons are used as list separators in CMake so we need to escape them in the PATH, + # otherwise the PATH envvar gets split by CMake before it passes the PATH on to CTest. + # We process each path separately to ensure it is a proper path. In particular, we need + # to ensure that a trailing backslash is not incorrectly interpreted as an escape + # character. Presumably, with cmake 3.20, this can be changed to using TO_NATIVE_PATH_LIST. + SET(TEST_PATHS) + foreach (PATH $ENV{PATH}) + file(TO_NATIVE_PATH "${PATH}" CORRECT_PATH) + # Remove trailing backslash + STRING(REGEX REPLACE "\\$" "" CORRECT_PATH ${CORRECT_PATH}) + list(APPEND TEST_PATHS ${CORRECT_PATH}) + endforeach() + + # Join all paths in a single string, separated by an escaped semi-colon. + string(JOIN "\;" CORRECT_PATHS ${TEST_PATHS}) + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES ENVIRONMENT "PATH=${IGRAPH_LIBDIR}\;${CORRECT_PATHS}" ) + endif() +endfunction() + +function(add_legacy_tests) + cmake_parse_arguments( + PARSED "" "FOLDER" "NAMES;LIBRARIES" ${ARGN} + ) + foreach(NAME ${PARSED_NAMES}) + add_legacy_test(${PARSED_FOLDER} ${NAME} test) + if(PARSED_LIBRARIES) + target_link_libraries(test_${NAME} PRIVATE ${PARSED_LIBRARIES}) + endif() + endforeach() +endfunction() + +function(add_examples) + cmake_parse_arguments( + PARSED "" "FOLDER" "NAMES;LIBRARIES" ${ARGN} + ) + foreach(NAME ${PARSED_NAMES}) + add_legacy_test(${PARSED_FOLDER} ${NAME} example) + if(PARSED_LIBRARIES) + target_link_libraries(example_${NAME} PRIVATE ${PARSED_LIBRARIES}) + endif() + endforeach() +endfunction() diff --git a/etc/cmake/tls.cmake b/etc/cmake/tls.cmake new file mode 100644 index 0000000..f0f4834 --- /dev/null +++ b/etc/cmake/tls.cmake @@ -0,0 +1,25 @@ +tristate(IGRAPH_ENABLE_TLS "Enable thread-local storage for igraph global variables" AUTO) + +include(CheckTLSSupport) +check_tls_support(TLS_KEYWORD) + +if(IGRAPH_ENABLE_TLS STREQUAL "AUTO") + if(TLS_KEYWORD) + set(IGRAPH_ENABLE_TLS ON) + else() + set(IGRAPH_ENABLE_TLS OFF) + endif() +endif() + +if(IGRAPH_ENABLE_TLS) + if(NOT TLS_KEYWORD) + message(FATAL_ERROR "Thread-local storage not supported on this compiler") + endif() + + # TODO: we should probably set this only if we are building igraph with + # internal-everything + set(IGRAPH_THREAD_SAFE YES) +else() + set(TLS_KEYWORD "") + set(IGRAPH_THREAD_SAFE NO) +endif() diff --git a/etc/cmake/uint128_support.cmake b/etc/cmake/uint128_support.cmake new file mode 100644 index 0000000..968c50a --- /dev/null +++ b/etc/cmake/uint128_support.cmake @@ -0,0 +1,43 @@ +include(CheckCXXSourceCompiles) +include(CheckTypeSize) + +cmake_push_check_state(RESET) + +# Check whether the compiler supports the _umul128() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0, b = 0; + unsigned long long c; + volatile unsigned long long d; + d = _umul128(a, b, &c); + return 0; + } + " + HAVE__UMUL128 +) + +# Check whether the compiler supports the __umulh() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0, b = 0; + volatile unsigned long long c; + c = __umulh(a, b); + return 0; + } + " + HAVE___UMULH +) + +# Check whether the compiler has __uint128_t +check_type_size("__uint128_t" UINT128 LANGUAGE CXX) +if(UINT128 EQUAL 16) + set(HAVE___UINT128_T ON) +else() + set(HAVE___UINT128_T OFF) +endif() + +cmake_pop_check_state() diff --git a/etc/cmake/version.cmake b/etc/cmake/version.cmake new file mode 100644 index 0000000..895441a --- /dev/null +++ b/etc/cmake/version.cmake @@ -0,0 +1,90 @@ +include(GetGitRevisionDescription) + +# At this point, igraph is either the main CMake project or a subproject of +# another project. CMAKE_SOURCE_DIR would point to the root of the main +# project if we are a subproject so we cannot use that; we need to use +# CMAKE_CURRENT_SOURCE_DIR to get the directory containing the CMakeLists.txt +# file that version.cmake was included from, which is the top-level +# CMakeLists.txt file of igraph itself +set(VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/IGRAPH_VERSION") +set(NEXT_VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/NEXT_VERSION") + +if(EXISTS "${VERSION_FILE}") + file(READ "${VERSION_FILE}" PACKAGE_VERSION) + string(STRIP "${PACKAGE_VERSION}" PACKAGE_VERSION) + message(STATUS "Version number: ${PACKAGE_VERSION}") +else() + find_package(Git QUIET) + if(Git_FOUND) + git_describe(PACKAGE_VERSION) + else() + set(PACKAGE_VERSION "NOTFOUND") + endif() + + if(PACKAGE_VERSION) + if(EXISTS "${NEXT_VERSION_FILE}") + file(READ "${NEXT_VERSION_FILE}" PACKAGE_VERSION) + string(STRIP "${PACKAGE_VERSION}" PACKAGE_VERSION) + get_git_head_revision(GIT_REFSPEC GIT_COMMIT_HASH) + string(SUBSTRING "${GIT_COMMIT_HASH}" 0 8 GIT_COMMIT_HASH_SHORT) + string(APPEND PACKAGE_VERSION "-dev+${GIT_COMMIT_HASH_SHORT}") + endif() + message(STATUS "Version number from Git: ${PACKAGE_VERSION}") + elseif(EXISTS "${NEXT_VERSION_FILE}") + file(READ "${NEXT_VERSION_FILE}" PACKAGE_VERSION) + string(STRIP "${PACKAGE_VERSION}" PACKAGE_VERSION) + string(APPEND PACKAGE_VERSION "-dev") + message(STATUS "Version number: ${PACKAGE_VERSION}") + else() + message(STATUS "Cannot find out the version number of this package; IGRAPH_VERSION is missing.") + message(STATUS "") + message(STATUS "The official igraph tarballs should contain this file, therefore you are") + message(STATUS "most likely trying to compile a development version yourself. The development") + message(STATUS "versions need Git to be able to determine the version number of igraph.") + message(STATUS "") + if(Git_FOUND) + message(STATUS "It seems like you do have Git but it failed to determine the package version number.") + message(STATUS "") + message(STATUS "Git was found at: ${GIT_EXECUTABLE}") + message(STATUS "The version number detection failed with: ${PACKAGE_VERSION}") + message(STATUS "") + message(STATUS "Most frequently this is caused by a shallow Git checkout that contains no tags in the history.") + else() + message(STATUS "Please install Git, make sure it is in your path, and then try again.") + endif() + message(STATUS "") + message(FATAL_ERROR "Configuration failed.") + endif() +endif() + +string(REGEX MATCH "^[^-]+" PACKAGE_VERSION_BASE "${PACKAGE_VERSION}") +string( + REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9+])" "\\1;\\2;\\3" + PACKAGE_VERSION_PARTS "${PACKAGE_VERSION_BASE}" +) +list(GET PACKAGE_VERSION_PARTS 0 PACKAGE_VERSION_MAJOR) +list(GET PACKAGE_VERSION_PARTS 1 PACKAGE_VERSION_MINOR) +list(GET PACKAGE_VERSION_PARTS 2 PACKAGE_VERSION_PATCH) + +if(PACKAGE_VERSION MATCHES "^[^-]+-") + string( + REGEX REPLACE "^[^-]+-([^+]*)" "\\1" PACKAGE_VERSION_PRERELEASE "${PACKAGE_VERSION}" + ) +else() + set(PACKAGE_VERSION_PRERELEASE "cmake-experimental") +endif() + +# Add a target that we can use to generate an IGRAPH_VERSION file in the build +# folder, for the sake of creating a tarball. This is needed only if igraph is +# the main project +if(NOT PROJECT_NAME) + add_custom_target( + versionfile + BYPRODUCTS "${CMAKE_BINARY_DIR}/IGRAPH_VERSION" + COMMAND "${CMAKE_COMMAND}" + -DIGRAPH_VERSION="${PACKAGE_VERSION}" + -DVERSION_FILE_PATH="${CMAKE_BINARY_DIR}/IGRAPH_VERSION" + -P "${CMAKE_SOURCE_DIR}/etc/cmake/create_igraph_version_file.cmake" + COMMENT "Generating IGRAPH_VERSION file in build folder" + ) +endif() diff --git a/examples/simple/adjlist.c b/examples/simple/adjlist.c new file mode 100644 index 0000000..a22c317 --- /dev/null +++ b/examples/simple/adjlist.c @@ -0,0 +1,48 @@ +/* -*- 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 + +int main(void) { + + igraph_t g, g2; + igraph_adjlist_t adjlist; + igraph_bool_t iso; + + /* Create a directed out-tree, convert it into an adjacency list + * representation, then reconstruct the graph from the tree and check + * whether the two are isomorphic (they should be) */ + + igraph_kary_tree(&g, 42, 3, IGRAPH_TREE_OUT); + igraph_adjlist_init(&g, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + igraph_adjlist(&g2, &adjlist, IGRAPH_OUT, /* duplicate = */ 0); + igraph_isomorphic(&g, &g2, &iso); + if (!iso) { + return 1; + } + igraph_adjlist_destroy(&adjlist); + igraph_destroy(&g2); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/ak-4102.max b/examples/simple/ak-4102.max new file mode 100644 index 0000000..0ff4ab6 --- /dev/null +++ b/examples/simple/ak-4102.max @@ -0,0 +1,24623 @@ +c very bad maxflow problem +p max 16414 24619 +n 1 s +n 2 t +a 3 4 4103 +a 3 4106 1 +a 4 5 4102 +a 4 4106 1 +a 5 6 4101 +a 5 4106 1 +a 6 7 4100 +a 6 4106 1 +a 7 8 4099 +a 7 4106 1 +a 8 9 4098 +a 8 4106 1 +a 9 10 4097 +a 9 4106 1 +a 10 11 4096 +a 10 4106 1 +a 11 12 4095 +a 11 4106 1 +a 12 13 4094 +a 12 4106 1 +a 13 14 4093 +a 13 4106 1 +a 14 15 4092 +a 14 4106 1 +a 15 16 4091 +a 15 4106 1 +a 16 17 4090 +a 16 4106 1 +a 17 18 4089 +a 17 4106 1 +a 18 19 4088 +a 18 4106 1 +a 19 20 4087 +a 19 4106 1 +a 20 21 4086 +a 20 4106 1 +a 21 22 4085 +a 21 4106 1 +a 22 23 4084 +a 22 4106 1 +a 23 24 4083 +a 23 4106 1 +a 24 25 4082 +a 24 4106 1 +a 25 26 4081 +a 25 4106 1 +a 26 27 4080 +a 26 4106 1 +a 27 28 4079 +a 27 4106 1 +a 28 29 4078 +a 28 4106 1 +a 29 30 4077 +a 29 4106 1 +a 30 31 4076 +a 30 4106 1 +a 31 32 4075 +a 31 4106 1 +a 32 33 4074 +a 32 4106 1 +a 33 34 4073 +a 33 4106 1 +a 34 35 4072 +a 34 4106 1 +a 35 36 4071 +a 35 4106 1 +a 36 37 4070 +a 36 4106 1 +a 37 38 4069 +a 37 4106 1 +a 38 39 4068 +a 38 4106 1 +a 39 40 4067 +a 39 4106 1 +a 40 41 4066 +a 40 4106 1 +a 41 42 4065 +a 41 4106 1 +a 42 43 4064 +a 42 4106 1 +a 43 44 4063 +a 43 4106 1 +a 44 45 4062 +a 44 4106 1 +a 45 46 4061 +a 45 4106 1 +a 46 47 4060 +a 46 4106 1 +a 47 48 4059 +a 47 4106 1 +a 48 49 4058 +a 48 4106 1 +a 49 50 4057 +a 49 4106 1 +a 50 51 4056 +a 50 4106 1 +a 51 52 4055 +a 51 4106 1 +a 52 53 4054 +a 52 4106 1 +a 53 54 4053 +a 53 4106 1 +a 54 55 4052 +a 54 4106 1 +a 55 56 4051 +a 55 4106 1 +a 56 57 4050 +a 56 4106 1 +a 57 58 4049 +a 57 4106 1 +a 58 59 4048 +a 58 4106 1 +a 59 60 4047 +a 59 4106 1 +a 60 61 4046 +a 60 4106 1 +a 61 62 4045 +a 61 4106 1 +a 62 63 4044 +a 62 4106 1 +a 63 64 4043 +a 63 4106 1 +a 64 65 4042 +a 64 4106 1 +a 65 66 4041 +a 65 4106 1 +a 66 67 4040 +a 66 4106 1 +a 67 68 4039 +a 67 4106 1 +a 68 69 4038 +a 68 4106 1 +a 69 70 4037 +a 69 4106 1 +a 70 71 4036 +a 70 4106 1 +a 71 72 4035 +a 71 4106 1 +a 72 73 4034 +a 72 4106 1 +a 73 74 4033 +a 73 4106 1 +a 74 75 4032 +a 74 4106 1 +a 75 76 4031 +a 75 4106 1 +a 76 77 4030 +a 76 4106 1 +a 77 78 4029 +a 77 4106 1 +a 78 79 4028 +a 78 4106 1 +a 79 80 4027 +a 79 4106 1 +a 80 81 4026 +a 80 4106 1 +a 81 82 4025 +a 81 4106 1 +a 82 83 4024 +a 82 4106 1 +a 83 84 4023 +a 83 4106 1 +a 84 85 4022 +a 84 4106 1 +a 85 86 4021 +a 85 4106 1 +a 86 87 4020 +a 86 4106 1 +a 87 88 4019 +a 87 4106 1 +a 88 89 4018 +a 88 4106 1 +a 89 90 4017 +a 89 4106 1 +a 90 91 4016 +a 90 4106 1 +a 91 92 4015 +a 91 4106 1 +a 92 93 4014 +a 92 4106 1 +a 93 94 4013 +a 93 4106 1 +a 94 95 4012 +a 94 4106 1 +a 95 96 4011 +a 95 4106 1 +a 96 97 4010 +a 96 4106 1 +a 97 98 4009 +a 97 4106 1 +a 98 99 4008 +a 98 4106 1 +a 99 100 4007 +a 99 4106 1 +a 100 101 4006 +a 100 4106 1 +a 101 102 4005 +a 101 4106 1 +a 102 103 4004 +a 102 4106 1 +a 103 104 4003 +a 103 4106 1 +a 104 105 4002 +a 104 4106 1 +a 105 106 4001 +a 105 4106 1 +a 106 107 4000 +a 106 4106 1 +a 107 108 3999 +a 107 4106 1 +a 108 109 3998 +a 108 4106 1 +a 109 110 3997 +a 109 4106 1 +a 110 111 3996 +a 110 4106 1 +a 111 112 3995 +a 111 4106 1 +a 112 113 3994 +a 112 4106 1 +a 113 114 3993 +a 113 4106 1 +a 114 115 3992 +a 114 4106 1 +a 115 116 3991 +a 115 4106 1 +a 116 117 3990 +a 116 4106 1 +a 117 118 3989 +a 117 4106 1 +a 118 119 3988 +a 118 4106 1 +a 119 120 3987 +a 119 4106 1 +a 120 121 3986 +a 120 4106 1 +a 121 122 3985 +a 121 4106 1 +a 122 123 3984 +a 122 4106 1 +a 123 124 3983 +a 123 4106 1 +a 124 125 3982 +a 124 4106 1 +a 125 126 3981 +a 125 4106 1 +a 126 127 3980 +a 126 4106 1 +a 127 128 3979 +a 127 4106 1 +a 128 129 3978 +a 128 4106 1 +a 129 130 3977 +a 129 4106 1 +a 130 131 3976 +a 130 4106 1 +a 131 132 3975 +a 131 4106 1 +a 132 133 3974 +a 132 4106 1 +a 133 134 3973 +a 133 4106 1 +a 134 135 3972 +a 134 4106 1 +a 135 136 3971 +a 135 4106 1 +a 136 137 3970 +a 136 4106 1 +a 137 138 3969 +a 137 4106 1 +a 138 139 3968 +a 138 4106 1 +a 139 140 3967 +a 139 4106 1 +a 140 141 3966 +a 140 4106 1 +a 141 142 3965 +a 141 4106 1 +a 142 143 3964 +a 142 4106 1 +a 143 144 3963 +a 143 4106 1 +a 144 145 3962 +a 144 4106 1 +a 145 146 3961 +a 145 4106 1 +a 146 147 3960 +a 146 4106 1 +a 147 148 3959 +a 147 4106 1 +a 148 149 3958 +a 148 4106 1 +a 149 150 3957 +a 149 4106 1 +a 150 151 3956 +a 150 4106 1 +a 151 152 3955 +a 151 4106 1 +a 152 153 3954 +a 152 4106 1 +a 153 154 3953 +a 153 4106 1 +a 154 155 3952 +a 154 4106 1 +a 155 156 3951 +a 155 4106 1 +a 156 157 3950 +a 156 4106 1 +a 157 158 3949 +a 157 4106 1 +a 158 159 3948 +a 158 4106 1 +a 159 160 3947 +a 159 4106 1 +a 160 161 3946 +a 160 4106 1 +a 161 162 3945 +a 161 4106 1 +a 162 163 3944 +a 162 4106 1 +a 163 164 3943 +a 163 4106 1 +a 164 165 3942 +a 164 4106 1 +a 165 166 3941 +a 165 4106 1 +a 166 167 3940 +a 166 4106 1 +a 167 168 3939 +a 167 4106 1 +a 168 169 3938 +a 168 4106 1 +a 169 170 3937 +a 169 4106 1 +a 170 171 3936 +a 170 4106 1 +a 171 172 3935 +a 171 4106 1 +a 172 173 3934 +a 172 4106 1 +a 173 174 3933 +a 173 4106 1 +a 174 175 3932 +a 174 4106 1 +a 175 176 3931 +a 175 4106 1 +a 176 177 3930 +a 176 4106 1 +a 177 178 3929 +a 177 4106 1 +a 178 179 3928 +a 178 4106 1 +a 179 180 3927 +a 179 4106 1 +a 180 181 3926 +a 180 4106 1 +a 181 182 3925 +a 181 4106 1 +a 182 183 3924 +a 182 4106 1 +a 183 184 3923 +a 183 4106 1 +a 184 185 3922 +a 184 4106 1 +a 185 186 3921 +a 185 4106 1 +a 186 187 3920 +a 186 4106 1 +a 187 188 3919 +a 187 4106 1 +a 188 189 3918 +a 188 4106 1 +a 189 190 3917 +a 189 4106 1 +a 190 191 3916 +a 190 4106 1 +a 191 192 3915 +a 191 4106 1 +a 192 193 3914 +a 192 4106 1 +a 193 194 3913 +a 193 4106 1 +a 194 195 3912 +a 194 4106 1 +a 195 196 3911 +a 195 4106 1 +a 196 197 3910 +a 196 4106 1 +a 197 198 3909 +a 197 4106 1 +a 198 199 3908 +a 198 4106 1 +a 199 200 3907 +a 199 4106 1 +a 200 201 3906 +a 200 4106 1 +a 201 202 3905 +a 201 4106 1 +a 202 203 3904 +a 202 4106 1 +a 203 204 3903 +a 203 4106 1 +a 204 205 3902 +a 204 4106 1 +a 205 206 3901 +a 205 4106 1 +a 206 207 3900 +a 206 4106 1 +a 207 208 3899 +a 207 4106 1 +a 208 209 3898 +a 208 4106 1 +a 209 210 3897 +a 209 4106 1 +a 210 211 3896 +a 210 4106 1 +a 211 212 3895 +a 211 4106 1 +a 212 213 3894 +a 212 4106 1 +a 213 214 3893 +a 213 4106 1 +a 214 215 3892 +a 214 4106 1 +a 215 216 3891 +a 215 4106 1 +a 216 217 3890 +a 216 4106 1 +a 217 218 3889 +a 217 4106 1 +a 218 219 3888 +a 218 4106 1 +a 219 220 3887 +a 219 4106 1 +a 220 221 3886 +a 220 4106 1 +a 221 222 3885 +a 221 4106 1 +a 222 223 3884 +a 222 4106 1 +a 223 224 3883 +a 223 4106 1 +a 224 225 3882 +a 224 4106 1 +a 225 226 3881 +a 225 4106 1 +a 226 227 3880 +a 226 4106 1 +a 227 228 3879 +a 227 4106 1 +a 228 229 3878 +a 228 4106 1 +a 229 230 3877 +a 229 4106 1 +a 230 231 3876 +a 230 4106 1 +a 231 232 3875 +a 231 4106 1 +a 232 233 3874 +a 232 4106 1 +a 233 234 3873 +a 233 4106 1 +a 234 235 3872 +a 234 4106 1 +a 235 236 3871 +a 235 4106 1 +a 236 237 3870 +a 236 4106 1 +a 237 238 3869 +a 237 4106 1 +a 238 239 3868 +a 238 4106 1 +a 239 240 3867 +a 239 4106 1 +a 240 241 3866 +a 240 4106 1 +a 241 242 3865 +a 241 4106 1 +a 242 243 3864 +a 242 4106 1 +a 243 244 3863 +a 243 4106 1 +a 244 245 3862 +a 244 4106 1 +a 245 246 3861 +a 245 4106 1 +a 246 247 3860 +a 246 4106 1 +a 247 248 3859 +a 247 4106 1 +a 248 249 3858 +a 248 4106 1 +a 249 250 3857 +a 249 4106 1 +a 250 251 3856 +a 250 4106 1 +a 251 252 3855 +a 251 4106 1 +a 252 253 3854 +a 252 4106 1 +a 253 254 3853 +a 253 4106 1 +a 254 255 3852 +a 254 4106 1 +a 255 256 3851 +a 255 4106 1 +a 256 257 3850 +a 256 4106 1 +a 257 258 3849 +a 257 4106 1 +a 258 259 3848 +a 258 4106 1 +a 259 260 3847 +a 259 4106 1 +a 260 261 3846 +a 260 4106 1 +a 261 262 3845 +a 261 4106 1 +a 262 263 3844 +a 262 4106 1 +a 263 264 3843 +a 263 4106 1 +a 264 265 3842 +a 264 4106 1 +a 265 266 3841 +a 265 4106 1 +a 266 267 3840 +a 266 4106 1 +a 267 268 3839 +a 267 4106 1 +a 268 269 3838 +a 268 4106 1 +a 269 270 3837 +a 269 4106 1 +a 270 271 3836 +a 270 4106 1 +a 271 272 3835 +a 271 4106 1 +a 272 273 3834 +a 272 4106 1 +a 273 274 3833 +a 273 4106 1 +a 274 275 3832 +a 274 4106 1 +a 275 276 3831 +a 275 4106 1 +a 276 277 3830 +a 276 4106 1 +a 277 278 3829 +a 277 4106 1 +a 278 279 3828 +a 278 4106 1 +a 279 280 3827 +a 279 4106 1 +a 280 281 3826 +a 280 4106 1 +a 281 282 3825 +a 281 4106 1 +a 282 283 3824 +a 282 4106 1 +a 283 284 3823 +a 283 4106 1 +a 284 285 3822 +a 284 4106 1 +a 285 286 3821 +a 285 4106 1 +a 286 287 3820 +a 286 4106 1 +a 287 288 3819 +a 287 4106 1 +a 288 289 3818 +a 288 4106 1 +a 289 290 3817 +a 289 4106 1 +a 290 291 3816 +a 290 4106 1 +a 291 292 3815 +a 291 4106 1 +a 292 293 3814 +a 292 4106 1 +a 293 294 3813 +a 293 4106 1 +a 294 295 3812 +a 294 4106 1 +a 295 296 3811 +a 295 4106 1 +a 296 297 3810 +a 296 4106 1 +a 297 298 3809 +a 297 4106 1 +a 298 299 3808 +a 298 4106 1 +a 299 300 3807 +a 299 4106 1 +a 300 301 3806 +a 300 4106 1 +a 301 302 3805 +a 301 4106 1 +a 302 303 3804 +a 302 4106 1 +a 303 304 3803 +a 303 4106 1 +a 304 305 3802 +a 304 4106 1 +a 305 306 3801 +a 305 4106 1 +a 306 307 3800 +a 306 4106 1 +a 307 308 3799 +a 307 4106 1 +a 308 309 3798 +a 308 4106 1 +a 309 310 3797 +a 309 4106 1 +a 310 311 3796 +a 310 4106 1 +a 311 312 3795 +a 311 4106 1 +a 312 313 3794 +a 312 4106 1 +a 313 314 3793 +a 313 4106 1 +a 314 315 3792 +a 314 4106 1 +a 315 316 3791 +a 315 4106 1 +a 316 317 3790 +a 316 4106 1 +a 317 318 3789 +a 317 4106 1 +a 318 319 3788 +a 318 4106 1 +a 319 320 3787 +a 319 4106 1 +a 320 321 3786 +a 320 4106 1 +a 321 322 3785 +a 321 4106 1 +a 322 323 3784 +a 322 4106 1 +a 323 324 3783 +a 323 4106 1 +a 324 325 3782 +a 324 4106 1 +a 325 326 3781 +a 325 4106 1 +a 326 327 3780 +a 326 4106 1 +a 327 328 3779 +a 327 4106 1 +a 328 329 3778 +a 328 4106 1 +a 329 330 3777 +a 329 4106 1 +a 330 331 3776 +a 330 4106 1 +a 331 332 3775 +a 331 4106 1 +a 332 333 3774 +a 332 4106 1 +a 333 334 3773 +a 333 4106 1 +a 334 335 3772 +a 334 4106 1 +a 335 336 3771 +a 335 4106 1 +a 336 337 3770 +a 336 4106 1 +a 337 338 3769 +a 337 4106 1 +a 338 339 3768 +a 338 4106 1 +a 339 340 3767 +a 339 4106 1 +a 340 341 3766 +a 340 4106 1 +a 341 342 3765 +a 341 4106 1 +a 342 343 3764 +a 342 4106 1 +a 343 344 3763 +a 343 4106 1 +a 344 345 3762 +a 344 4106 1 +a 345 346 3761 +a 345 4106 1 +a 346 347 3760 +a 346 4106 1 +a 347 348 3759 +a 347 4106 1 +a 348 349 3758 +a 348 4106 1 +a 349 350 3757 +a 349 4106 1 +a 350 351 3756 +a 350 4106 1 +a 351 352 3755 +a 351 4106 1 +a 352 353 3754 +a 352 4106 1 +a 353 354 3753 +a 353 4106 1 +a 354 355 3752 +a 354 4106 1 +a 355 356 3751 +a 355 4106 1 +a 356 357 3750 +a 356 4106 1 +a 357 358 3749 +a 357 4106 1 +a 358 359 3748 +a 358 4106 1 +a 359 360 3747 +a 359 4106 1 +a 360 361 3746 +a 360 4106 1 +a 361 362 3745 +a 361 4106 1 +a 362 363 3744 +a 362 4106 1 +a 363 364 3743 +a 363 4106 1 +a 364 365 3742 +a 364 4106 1 +a 365 366 3741 +a 365 4106 1 +a 366 367 3740 +a 366 4106 1 +a 367 368 3739 +a 367 4106 1 +a 368 369 3738 +a 368 4106 1 +a 369 370 3737 +a 369 4106 1 +a 370 371 3736 +a 370 4106 1 +a 371 372 3735 +a 371 4106 1 +a 372 373 3734 +a 372 4106 1 +a 373 374 3733 +a 373 4106 1 +a 374 375 3732 +a 374 4106 1 +a 375 376 3731 +a 375 4106 1 +a 376 377 3730 +a 376 4106 1 +a 377 378 3729 +a 377 4106 1 +a 378 379 3728 +a 378 4106 1 +a 379 380 3727 +a 379 4106 1 +a 380 381 3726 +a 380 4106 1 +a 381 382 3725 +a 381 4106 1 +a 382 383 3724 +a 382 4106 1 +a 383 384 3723 +a 383 4106 1 +a 384 385 3722 +a 384 4106 1 +a 385 386 3721 +a 385 4106 1 +a 386 387 3720 +a 386 4106 1 +a 387 388 3719 +a 387 4106 1 +a 388 389 3718 +a 388 4106 1 +a 389 390 3717 +a 389 4106 1 +a 390 391 3716 +a 390 4106 1 +a 391 392 3715 +a 391 4106 1 +a 392 393 3714 +a 392 4106 1 +a 393 394 3713 +a 393 4106 1 +a 394 395 3712 +a 394 4106 1 +a 395 396 3711 +a 395 4106 1 +a 396 397 3710 +a 396 4106 1 +a 397 398 3709 +a 397 4106 1 +a 398 399 3708 +a 398 4106 1 +a 399 400 3707 +a 399 4106 1 +a 400 401 3706 +a 400 4106 1 +a 401 402 3705 +a 401 4106 1 +a 402 403 3704 +a 402 4106 1 +a 403 404 3703 +a 403 4106 1 +a 404 405 3702 +a 404 4106 1 +a 405 406 3701 +a 405 4106 1 +a 406 407 3700 +a 406 4106 1 +a 407 408 3699 +a 407 4106 1 +a 408 409 3698 +a 408 4106 1 +a 409 410 3697 +a 409 4106 1 +a 410 411 3696 +a 410 4106 1 +a 411 412 3695 +a 411 4106 1 +a 412 413 3694 +a 412 4106 1 +a 413 414 3693 +a 413 4106 1 +a 414 415 3692 +a 414 4106 1 +a 415 416 3691 +a 415 4106 1 +a 416 417 3690 +a 416 4106 1 +a 417 418 3689 +a 417 4106 1 +a 418 419 3688 +a 418 4106 1 +a 419 420 3687 +a 419 4106 1 +a 420 421 3686 +a 420 4106 1 +a 421 422 3685 +a 421 4106 1 +a 422 423 3684 +a 422 4106 1 +a 423 424 3683 +a 423 4106 1 +a 424 425 3682 +a 424 4106 1 +a 425 426 3681 +a 425 4106 1 +a 426 427 3680 +a 426 4106 1 +a 427 428 3679 +a 427 4106 1 +a 428 429 3678 +a 428 4106 1 +a 429 430 3677 +a 429 4106 1 +a 430 431 3676 +a 430 4106 1 +a 431 432 3675 +a 431 4106 1 +a 432 433 3674 +a 432 4106 1 +a 433 434 3673 +a 433 4106 1 +a 434 435 3672 +a 434 4106 1 +a 435 436 3671 +a 435 4106 1 +a 436 437 3670 +a 436 4106 1 +a 437 438 3669 +a 437 4106 1 +a 438 439 3668 +a 438 4106 1 +a 439 440 3667 +a 439 4106 1 +a 440 441 3666 +a 440 4106 1 +a 441 442 3665 +a 441 4106 1 +a 442 443 3664 +a 442 4106 1 +a 443 444 3663 +a 443 4106 1 +a 444 445 3662 +a 444 4106 1 +a 445 446 3661 +a 445 4106 1 +a 446 447 3660 +a 446 4106 1 +a 447 448 3659 +a 447 4106 1 +a 448 449 3658 +a 448 4106 1 +a 449 450 3657 +a 449 4106 1 +a 450 451 3656 +a 450 4106 1 +a 451 452 3655 +a 451 4106 1 +a 452 453 3654 +a 452 4106 1 +a 453 454 3653 +a 453 4106 1 +a 454 455 3652 +a 454 4106 1 +a 455 456 3651 +a 455 4106 1 +a 456 457 3650 +a 456 4106 1 +a 457 458 3649 +a 457 4106 1 +a 458 459 3648 +a 458 4106 1 +a 459 460 3647 +a 459 4106 1 +a 460 461 3646 +a 460 4106 1 +a 461 462 3645 +a 461 4106 1 +a 462 463 3644 +a 462 4106 1 +a 463 464 3643 +a 463 4106 1 +a 464 465 3642 +a 464 4106 1 +a 465 466 3641 +a 465 4106 1 +a 466 467 3640 +a 466 4106 1 +a 467 468 3639 +a 467 4106 1 +a 468 469 3638 +a 468 4106 1 +a 469 470 3637 +a 469 4106 1 +a 470 471 3636 +a 470 4106 1 +a 471 472 3635 +a 471 4106 1 +a 472 473 3634 +a 472 4106 1 +a 473 474 3633 +a 473 4106 1 +a 474 475 3632 +a 474 4106 1 +a 475 476 3631 +a 475 4106 1 +a 476 477 3630 +a 476 4106 1 +a 477 478 3629 +a 477 4106 1 +a 478 479 3628 +a 478 4106 1 +a 479 480 3627 +a 479 4106 1 +a 480 481 3626 +a 480 4106 1 +a 481 482 3625 +a 481 4106 1 +a 482 483 3624 +a 482 4106 1 +a 483 484 3623 +a 483 4106 1 +a 484 485 3622 +a 484 4106 1 +a 485 486 3621 +a 485 4106 1 +a 486 487 3620 +a 486 4106 1 +a 487 488 3619 +a 487 4106 1 +a 488 489 3618 +a 488 4106 1 +a 489 490 3617 +a 489 4106 1 +a 490 491 3616 +a 490 4106 1 +a 491 492 3615 +a 491 4106 1 +a 492 493 3614 +a 492 4106 1 +a 493 494 3613 +a 493 4106 1 +a 494 495 3612 +a 494 4106 1 +a 495 496 3611 +a 495 4106 1 +a 496 497 3610 +a 496 4106 1 +a 497 498 3609 +a 497 4106 1 +a 498 499 3608 +a 498 4106 1 +a 499 500 3607 +a 499 4106 1 +a 500 501 3606 +a 500 4106 1 +a 501 502 3605 +a 501 4106 1 +a 502 503 3604 +a 502 4106 1 +a 503 504 3603 +a 503 4106 1 +a 504 505 3602 +a 504 4106 1 +a 505 506 3601 +a 505 4106 1 +a 506 507 3600 +a 506 4106 1 +a 507 508 3599 +a 507 4106 1 +a 508 509 3598 +a 508 4106 1 +a 509 510 3597 +a 509 4106 1 +a 510 511 3596 +a 510 4106 1 +a 511 512 3595 +a 511 4106 1 +a 512 513 3594 +a 512 4106 1 +a 513 514 3593 +a 513 4106 1 +a 514 515 3592 +a 514 4106 1 +a 515 516 3591 +a 515 4106 1 +a 516 517 3590 +a 516 4106 1 +a 517 518 3589 +a 517 4106 1 +a 518 519 3588 +a 518 4106 1 +a 519 520 3587 +a 519 4106 1 +a 520 521 3586 +a 520 4106 1 +a 521 522 3585 +a 521 4106 1 +a 522 523 3584 +a 522 4106 1 +a 523 524 3583 +a 523 4106 1 +a 524 525 3582 +a 524 4106 1 +a 525 526 3581 +a 525 4106 1 +a 526 527 3580 +a 526 4106 1 +a 527 528 3579 +a 527 4106 1 +a 528 529 3578 +a 528 4106 1 +a 529 530 3577 +a 529 4106 1 +a 530 531 3576 +a 530 4106 1 +a 531 532 3575 +a 531 4106 1 +a 532 533 3574 +a 532 4106 1 +a 533 534 3573 +a 533 4106 1 +a 534 535 3572 +a 534 4106 1 +a 535 536 3571 +a 535 4106 1 +a 536 537 3570 +a 536 4106 1 +a 537 538 3569 +a 537 4106 1 +a 538 539 3568 +a 538 4106 1 +a 539 540 3567 +a 539 4106 1 +a 540 541 3566 +a 540 4106 1 +a 541 542 3565 +a 541 4106 1 +a 542 543 3564 +a 542 4106 1 +a 543 544 3563 +a 543 4106 1 +a 544 545 3562 +a 544 4106 1 +a 545 546 3561 +a 545 4106 1 +a 546 547 3560 +a 546 4106 1 +a 547 548 3559 +a 547 4106 1 +a 548 549 3558 +a 548 4106 1 +a 549 550 3557 +a 549 4106 1 +a 550 551 3556 +a 550 4106 1 +a 551 552 3555 +a 551 4106 1 +a 552 553 3554 +a 552 4106 1 +a 553 554 3553 +a 553 4106 1 +a 554 555 3552 +a 554 4106 1 +a 555 556 3551 +a 555 4106 1 +a 556 557 3550 +a 556 4106 1 +a 557 558 3549 +a 557 4106 1 +a 558 559 3548 +a 558 4106 1 +a 559 560 3547 +a 559 4106 1 +a 560 561 3546 +a 560 4106 1 +a 561 562 3545 +a 561 4106 1 +a 562 563 3544 +a 562 4106 1 +a 563 564 3543 +a 563 4106 1 +a 564 565 3542 +a 564 4106 1 +a 565 566 3541 +a 565 4106 1 +a 566 567 3540 +a 566 4106 1 +a 567 568 3539 +a 567 4106 1 +a 568 569 3538 +a 568 4106 1 +a 569 570 3537 +a 569 4106 1 +a 570 571 3536 +a 570 4106 1 +a 571 572 3535 +a 571 4106 1 +a 572 573 3534 +a 572 4106 1 +a 573 574 3533 +a 573 4106 1 +a 574 575 3532 +a 574 4106 1 +a 575 576 3531 +a 575 4106 1 +a 576 577 3530 +a 576 4106 1 +a 577 578 3529 +a 577 4106 1 +a 578 579 3528 +a 578 4106 1 +a 579 580 3527 +a 579 4106 1 +a 580 581 3526 +a 580 4106 1 +a 581 582 3525 +a 581 4106 1 +a 582 583 3524 +a 582 4106 1 +a 583 584 3523 +a 583 4106 1 +a 584 585 3522 +a 584 4106 1 +a 585 586 3521 +a 585 4106 1 +a 586 587 3520 +a 586 4106 1 +a 587 588 3519 +a 587 4106 1 +a 588 589 3518 +a 588 4106 1 +a 589 590 3517 +a 589 4106 1 +a 590 591 3516 +a 590 4106 1 +a 591 592 3515 +a 591 4106 1 +a 592 593 3514 +a 592 4106 1 +a 593 594 3513 +a 593 4106 1 +a 594 595 3512 +a 594 4106 1 +a 595 596 3511 +a 595 4106 1 +a 596 597 3510 +a 596 4106 1 +a 597 598 3509 +a 597 4106 1 +a 598 599 3508 +a 598 4106 1 +a 599 600 3507 +a 599 4106 1 +a 600 601 3506 +a 600 4106 1 +a 601 602 3505 +a 601 4106 1 +a 602 603 3504 +a 602 4106 1 +a 603 604 3503 +a 603 4106 1 +a 604 605 3502 +a 604 4106 1 +a 605 606 3501 +a 605 4106 1 +a 606 607 3500 +a 606 4106 1 +a 607 608 3499 +a 607 4106 1 +a 608 609 3498 +a 608 4106 1 +a 609 610 3497 +a 609 4106 1 +a 610 611 3496 +a 610 4106 1 +a 611 612 3495 +a 611 4106 1 +a 612 613 3494 +a 612 4106 1 +a 613 614 3493 +a 613 4106 1 +a 614 615 3492 +a 614 4106 1 +a 615 616 3491 +a 615 4106 1 +a 616 617 3490 +a 616 4106 1 +a 617 618 3489 +a 617 4106 1 +a 618 619 3488 +a 618 4106 1 +a 619 620 3487 +a 619 4106 1 +a 620 621 3486 +a 620 4106 1 +a 621 622 3485 +a 621 4106 1 +a 622 623 3484 +a 622 4106 1 +a 623 624 3483 +a 623 4106 1 +a 624 625 3482 +a 624 4106 1 +a 625 626 3481 +a 625 4106 1 +a 626 627 3480 +a 626 4106 1 +a 627 628 3479 +a 627 4106 1 +a 628 629 3478 +a 628 4106 1 +a 629 630 3477 +a 629 4106 1 +a 630 631 3476 +a 630 4106 1 +a 631 632 3475 +a 631 4106 1 +a 632 633 3474 +a 632 4106 1 +a 633 634 3473 +a 633 4106 1 +a 634 635 3472 +a 634 4106 1 +a 635 636 3471 +a 635 4106 1 +a 636 637 3470 +a 636 4106 1 +a 637 638 3469 +a 637 4106 1 +a 638 639 3468 +a 638 4106 1 +a 639 640 3467 +a 639 4106 1 +a 640 641 3466 +a 640 4106 1 +a 641 642 3465 +a 641 4106 1 +a 642 643 3464 +a 642 4106 1 +a 643 644 3463 +a 643 4106 1 +a 644 645 3462 +a 644 4106 1 +a 645 646 3461 +a 645 4106 1 +a 646 647 3460 +a 646 4106 1 +a 647 648 3459 +a 647 4106 1 +a 648 649 3458 +a 648 4106 1 +a 649 650 3457 +a 649 4106 1 +a 650 651 3456 +a 650 4106 1 +a 651 652 3455 +a 651 4106 1 +a 652 653 3454 +a 652 4106 1 +a 653 654 3453 +a 653 4106 1 +a 654 655 3452 +a 654 4106 1 +a 655 656 3451 +a 655 4106 1 +a 656 657 3450 +a 656 4106 1 +a 657 658 3449 +a 657 4106 1 +a 658 659 3448 +a 658 4106 1 +a 659 660 3447 +a 659 4106 1 +a 660 661 3446 +a 660 4106 1 +a 661 662 3445 +a 661 4106 1 +a 662 663 3444 +a 662 4106 1 +a 663 664 3443 +a 663 4106 1 +a 664 665 3442 +a 664 4106 1 +a 665 666 3441 +a 665 4106 1 +a 666 667 3440 +a 666 4106 1 +a 667 668 3439 +a 667 4106 1 +a 668 669 3438 +a 668 4106 1 +a 669 670 3437 +a 669 4106 1 +a 670 671 3436 +a 670 4106 1 +a 671 672 3435 +a 671 4106 1 +a 672 673 3434 +a 672 4106 1 +a 673 674 3433 +a 673 4106 1 +a 674 675 3432 +a 674 4106 1 +a 675 676 3431 +a 675 4106 1 +a 676 677 3430 +a 676 4106 1 +a 677 678 3429 +a 677 4106 1 +a 678 679 3428 +a 678 4106 1 +a 679 680 3427 +a 679 4106 1 +a 680 681 3426 +a 680 4106 1 +a 681 682 3425 +a 681 4106 1 +a 682 683 3424 +a 682 4106 1 +a 683 684 3423 +a 683 4106 1 +a 684 685 3422 +a 684 4106 1 +a 685 686 3421 +a 685 4106 1 +a 686 687 3420 +a 686 4106 1 +a 687 688 3419 +a 687 4106 1 +a 688 689 3418 +a 688 4106 1 +a 689 690 3417 +a 689 4106 1 +a 690 691 3416 +a 690 4106 1 +a 691 692 3415 +a 691 4106 1 +a 692 693 3414 +a 692 4106 1 +a 693 694 3413 +a 693 4106 1 +a 694 695 3412 +a 694 4106 1 +a 695 696 3411 +a 695 4106 1 +a 696 697 3410 +a 696 4106 1 +a 697 698 3409 +a 697 4106 1 +a 698 699 3408 +a 698 4106 1 +a 699 700 3407 +a 699 4106 1 +a 700 701 3406 +a 700 4106 1 +a 701 702 3405 +a 701 4106 1 +a 702 703 3404 +a 702 4106 1 +a 703 704 3403 +a 703 4106 1 +a 704 705 3402 +a 704 4106 1 +a 705 706 3401 +a 705 4106 1 +a 706 707 3400 +a 706 4106 1 +a 707 708 3399 +a 707 4106 1 +a 708 709 3398 +a 708 4106 1 +a 709 710 3397 +a 709 4106 1 +a 710 711 3396 +a 710 4106 1 +a 711 712 3395 +a 711 4106 1 +a 712 713 3394 +a 712 4106 1 +a 713 714 3393 +a 713 4106 1 +a 714 715 3392 +a 714 4106 1 +a 715 716 3391 +a 715 4106 1 +a 716 717 3390 +a 716 4106 1 +a 717 718 3389 +a 717 4106 1 +a 718 719 3388 +a 718 4106 1 +a 719 720 3387 +a 719 4106 1 +a 720 721 3386 +a 720 4106 1 +a 721 722 3385 +a 721 4106 1 +a 722 723 3384 +a 722 4106 1 +a 723 724 3383 +a 723 4106 1 +a 724 725 3382 +a 724 4106 1 +a 725 726 3381 +a 725 4106 1 +a 726 727 3380 +a 726 4106 1 +a 727 728 3379 +a 727 4106 1 +a 728 729 3378 +a 728 4106 1 +a 729 730 3377 +a 729 4106 1 +a 730 731 3376 +a 730 4106 1 +a 731 732 3375 +a 731 4106 1 +a 732 733 3374 +a 732 4106 1 +a 733 734 3373 +a 733 4106 1 +a 734 735 3372 +a 734 4106 1 +a 735 736 3371 +a 735 4106 1 +a 736 737 3370 +a 736 4106 1 +a 737 738 3369 +a 737 4106 1 +a 738 739 3368 +a 738 4106 1 +a 739 740 3367 +a 739 4106 1 +a 740 741 3366 +a 740 4106 1 +a 741 742 3365 +a 741 4106 1 +a 742 743 3364 +a 742 4106 1 +a 743 744 3363 +a 743 4106 1 +a 744 745 3362 +a 744 4106 1 +a 745 746 3361 +a 745 4106 1 +a 746 747 3360 +a 746 4106 1 +a 747 748 3359 +a 747 4106 1 +a 748 749 3358 +a 748 4106 1 +a 749 750 3357 +a 749 4106 1 +a 750 751 3356 +a 750 4106 1 +a 751 752 3355 +a 751 4106 1 +a 752 753 3354 +a 752 4106 1 +a 753 754 3353 +a 753 4106 1 +a 754 755 3352 +a 754 4106 1 +a 755 756 3351 +a 755 4106 1 +a 756 757 3350 +a 756 4106 1 +a 757 758 3349 +a 757 4106 1 +a 758 759 3348 +a 758 4106 1 +a 759 760 3347 +a 759 4106 1 +a 760 761 3346 +a 760 4106 1 +a 761 762 3345 +a 761 4106 1 +a 762 763 3344 +a 762 4106 1 +a 763 764 3343 +a 763 4106 1 +a 764 765 3342 +a 764 4106 1 +a 765 766 3341 +a 765 4106 1 +a 766 767 3340 +a 766 4106 1 +a 767 768 3339 +a 767 4106 1 +a 768 769 3338 +a 768 4106 1 +a 769 770 3337 +a 769 4106 1 +a 770 771 3336 +a 770 4106 1 +a 771 772 3335 +a 771 4106 1 +a 772 773 3334 +a 772 4106 1 +a 773 774 3333 +a 773 4106 1 +a 774 775 3332 +a 774 4106 1 +a 775 776 3331 +a 775 4106 1 +a 776 777 3330 +a 776 4106 1 +a 777 778 3329 +a 777 4106 1 +a 778 779 3328 +a 778 4106 1 +a 779 780 3327 +a 779 4106 1 +a 780 781 3326 +a 780 4106 1 +a 781 782 3325 +a 781 4106 1 +a 782 783 3324 +a 782 4106 1 +a 783 784 3323 +a 783 4106 1 +a 784 785 3322 +a 784 4106 1 +a 785 786 3321 +a 785 4106 1 +a 786 787 3320 +a 786 4106 1 +a 787 788 3319 +a 787 4106 1 +a 788 789 3318 +a 788 4106 1 +a 789 790 3317 +a 789 4106 1 +a 790 791 3316 +a 790 4106 1 +a 791 792 3315 +a 791 4106 1 +a 792 793 3314 +a 792 4106 1 +a 793 794 3313 +a 793 4106 1 +a 794 795 3312 +a 794 4106 1 +a 795 796 3311 +a 795 4106 1 +a 796 797 3310 +a 796 4106 1 +a 797 798 3309 +a 797 4106 1 +a 798 799 3308 +a 798 4106 1 +a 799 800 3307 +a 799 4106 1 +a 800 801 3306 +a 800 4106 1 +a 801 802 3305 +a 801 4106 1 +a 802 803 3304 +a 802 4106 1 +a 803 804 3303 +a 803 4106 1 +a 804 805 3302 +a 804 4106 1 +a 805 806 3301 +a 805 4106 1 +a 806 807 3300 +a 806 4106 1 +a 807 808 3299 +a 807 4106 1 +a 808 809 3298 +a 808 4106 1 +a 809 810 3297 +a 809 4106 1 +a 810 811 3296 +a 810 4106 1 +a 811 812 3295 +a 811 4106 1 +a 812 813 3294 +a 812 4106 1 +a 813 814 3293 +a 813 4106 1 +a 814 815 3292 +a 814 4106 1 +a 815 816 3291 +a 815 4106 1 +a 816 817 3290 +a 816 4106 1 +a 817 818 3289 +a 817 4106 1 +a 818 819 3288 +a 818 4106 1 +a 819 820 3287 +a 819 4106 1 +a 820 821 3286 +a 820 4106 1 +a 821 822 3285 +a 821 4106 1 +a 822 823 3284 +a 822 4106 1 +a 823 824 3283 +a 823 4106 1 +a 824 825 3282 +a 824 4106 1 +a 825 826 3281 +a 825 4106 1 +a 826 827 3280 +a 826 4106 1 +a 827 828 3279 +a 827 4106 1 +a 828 829 3278 +a 828 4106 1 +a 829 830 3277 +a 829 4106 1 +a 830 831 3276 +a 830 4106 1 +a 831 832 3275 +a 831 4106 1 +a 832 833 3274 +a 832 4106 1 +a 833 834 3273 +a 833 4106 1 +a 834 835 3272 +a 834 4106 1 +a 835 836 3271 +a 835 4106 1 +a 836 837 3270 +a 836 4106 1 +a 837 838 3269 +a 837 4106 1 +a 838 839 3268 +a 838 4106 1 +a 839 840 3267 +a 839 4106 1 +a 840 841 3266 +a 840 4106 1 +a 841 842 3265 +a 841 4106 1 +a 842 843 3264 +a 842 4106 1 +a 843 844 3263 +a 843 4106 1 +a 844 845 3262 +a 844 4106 1 +a 845 846 3261 +a 845 4106 1 +a 846 847 3260 +a 846 4106 1 +a 847 848 3259 +a 847 4106 1 +a 848 849 3258 +a 848 4106 1 +a 849 850 3257 +a 849 4106 1 +a 850 851 3256 +a 850 4106 1 +a 851 852 3255 +a 851 4106 1 +a 852 853 3254 +a 852 4106 1 +a 853 854 3253 +a 853 4106 1 +a 854 855 3252 +a 854 4106 1 +a 855 856 3251 +a 855 4106 1 +a 856 857 3250 +a 856 4106 1 +a 857 858 3249 +a 857 4106 1 +a 858 859 3248 +a 858 4106 1 +a 859 860 3247 +a 859 4106 1 +a 860 861 3246 +a 860 4106 1 +a 861 862 3245 +a 861 4106 1 +a 862 863 3244 +a 862 4106 1 +a 863 864 3243 +a 863 4106 1 +a 864 865 3242 +a 864 4106 1 +a 865 866 3241 +a 865 4106 1 +a 866 867 3240 +a 866 4106 1 +a 867 868 3239 +a 867 4106 1 +a 868 869 3238 +a 868 4106 1 +a 869 870 3237 +a 869 4106 1 +a 870 871 3236 +a 870 4106 1 +a 871 872 3235 +a 871 4106 1 +a 872 873 3234 +a 872 4106 1 +a 873 874 3233 +a 873 4106 1 +a 874 875 3232 +a 874 4106 1 +a 875 876 3231 +a 875 4106 1 +a 876 877 3230 +a 876 4106 1 +a 877 878 3229 +a 877 4106 1 +a 878 879 3228 +a 878 4106 1 +a 879 880 3227 +a 879 4106 1 +a 880 881 3226 +a 880 4106 1 +a 881 882 3225 +a 881 4106 1 +a 882 883 3224 +a 882 4106 1 +a 883 884 3223 +a 883 4106 1 +a 884 885 3222 +a 884 4106 1 +a 885 886 3221 +a 885 4106 1 +a 886 887 3220 +a 886 4106 1 +a 887 888 3219 +a 887 4106 1 +a 888 889 3218 +a 888 4106 1 +a 889 890 3217 +a 889 4106 1 +a 890 891 3216 +a 890 4106 1 +a 891 892 3215 +a 891 4106 1 +a 892 893 3214 +a 892 4106 1 +a 893 894 3213 +a 893 4106 1 +a 894 895 3212 +a 894 4106 1 +a 895 896 3211 +a 895 4106 1 +a 896 897 3210 +a 896 4106 1 +a 897 898 3209 +a 897 4106 1 +a 898 899 3208 +a 898 4106 1 +a 899 900 3207 +a 899 4106 1 +a 900 901 3206 +a 900 4106 1 +a 901 902 3205 +a 901 4106 1 +a 902 903 3204 +a 902 4106 1 +a 903 904 3203 +a 903 4106 1 +a 904 905 3202 +a 904 4106 1 +a 905 906 3201 +a 905 4106 1 +a 906 907 3200 +a 906 4106 1 +a 907 908 3199 +a 907 4106 1 +a 908 909 3198 +a 908 4106 1 +a 909 910 3197 +a 909 4106 1 +a 910 911 3196 +a 910 4106 1 +a 911 912 3195 +a 911 4106 1 +a 912 913 3194 +a 912 4106 1 +a 913 914 3193 +a 913 4106 1 +a 914 915 3192 +a 914 4106 1 +a 915 916 3191 +a 915 4106 1 +a 916 917 3190 +a 916 4106 1 +a 917 918 3189 +a 917 4106 1 +a 918 919 3188 +a 918 4106 1 +a 919 920 3187 +a 919 4106 1 +a 920 921 3186 +a 920 4106 1 +a 921 922 3185 +a 921 4106 1 +a 922 923 3184 +a 922 4106 1 +a 923 924 3183 +a 923 4106 1 +a 924 925 3182 +a 924 4106 1 +a 925 926 3181 +a 925 4106 1 +a 926 927 3180 +a 926 4106 1 +a 927 928 3179 +a 927 4106 1 +a 928 929 3178 +a 928 4106 1 +a 929 930 3177 +a 929 4106 1 +a 930 931 3176 +a 930 4106 1 +a 931 932 3175 +a 931 4106 1 +a 932 933 3174 +a 932 4106 1 +a 933 934 3173 +a 933 4106 1 +a 934 935 3172 +a 934 4106 1 +a 935 936 3171 +a 935 4106 1 +a 936 937 3170 +a 936 4106 1 +a 937 938 3169 +a 937 4106 1 +a 938 939 3168 +a 938 4106 1 +a 939 940 3167 +a 939 4106 1 +a 940 941 3166 +a 940 4106 1 +a 941 942 3165 +a 941 4106 1 +a 942 943 3164 +a 942 4106 1 +a 943 944 3163 +a 943 4106 1 +a 944 945 3162 +a 944 4106 1 +a 945 946 3161 +a 945 4106 1 +a 946 947 3160 +a 946 4106 1 +a 947 948 3159 +a 947 4106 1 +a 948 949 3158 +a 948 4106 1 +a 949 950 3157 +a 949 4106 1 +a 950 951 3156 +a 950 4106 1 +a 951 952 3155 +a 951 4106 1 +a 952 953 3154 +a 952 4106 1 +a 953 954 3153 +a 953 4106 1 +a 954 955 3152 +a 954 4106 1 +a 955 956 3151 +a 955 4106 1 +a 956 957 3150 +a 956 4106 1 +a 957 958 3149 +a 957 4106 1 +a 958 959 3148 +a 958 4106 1 +a 959 960 3147 +a 959 4106 1 +a 960 961 3146 +a 960 4106 1 +a 961 962 3145 +a 961 4106 1 +a 962 963 3144 +a 962 4106 1 +a 963 964 3143 +a 963 4106 1 +a 964 965 3142 +a 964 4106 1 +a 965 966 3141 +a 965 4106 1 +a 966 967 3140 +a 966 4106 1 +a 967 968 3139 +a 967 4106 1 +a 968 969 3138 +a 968 4106 1 +a 969 970 3137 +a 969 4106 1 +a 970 971 3136 +a 970 4106 1 +a 971 972 3135 +a 971 4106 1 +a 972 973 3134 +a 972 4106 1 +a 973 974 3133 +a 973 4106 1 +a 974 975 3132 +a 974 4106 1 +a 975 976 3131 +a 975 4106 1 +a 976 977 3130 +a 976 4106 1 +a 977 978 3129 +a 977 4106 1 +a 978 979 3128 +a 978 4106 1 +a 979 980 3127 +a 979 4106 1 +a 980 981 3126 +a 980 4106 1 +a 981 982 3125 +a 981 4106 1 +a 982 983 3124 +a 982 4106 1 +a 983 984 3123 +a 983 4106 1 +a 984 985 3122 +a 984 4106 1 +a 985 986 3121 +a 985 4106 1 +a 986 987 3120 +a 986 4106 1 +a 987 988 3119 +a 987 4106 1 +a 988 989 3118 +a 988 4106 1 +a 989 990 3117 +a 989 4106 1 +a 990 991 3116 +a 990 4106 1 +a 991 992 3115 +a 991 4106 1 +a 992 993 3114 +a 992 4106 1 +a 993 994 3113 +a 993 4106 1 +a 994 995 3112 +a 994 4106 1 +a 995 996 3111 +a 995 4106 1 +a 996 997 3110 +a 996 4106 1 +a 997 998 3109 +a 997 4106 1 +a 998 999 3108 +a 998 4106 1 +a 999 1000 3107 +a 999 4106 1 +a 1000 1001 3106 +a 1000 4106 1 +a 1001 1002 3105 +a 1001 4106 1 +a 1002 1003 3104 +a 1002 4106 1 +a 1003 1004 3103 +a 1003 4106 1 +a 1004 1005 3102 +a 1004 4106 1 +a 1005 1006 3101 +a 1005 4106 1 +a 1006 1007 3100 +a 1006 4106 1 +a 1007 1008 3099 +a 1007 4106 1 +a 1008 1009 3098 +a 1008 4106 1 +a 1009 1010 3097 +a 1009 4106 1 +a 1010 1011 3096 +a 1010 4106 1 +a 1011 1012 3095 +a 1011 4106 1 +a 1012 1013 3094 +a 1012 4106 1 +a 1013 1014 3093 +a 1013 4106 1 +a 1014 1015 3092 +a 1014 4106 1 +a 1015 1016 3091 +a 1015 4106 1 +a 1016 1017 3090 +a 1016 4106 1 +a 1017 1018 3089 +a 1017 4106 1 +a 1018 1019 3088 +a 1018 4106 1 +a 1019 1020 3087 +a 1019 4106 1 +a 1020 1021 3086 +a 1020 4106 1 +a 1021 1022 3085 +a 1021 4106 1 +a 1022 1023 3084 +a 1022 4106 1 +a 1023 1024 3083 +a 1023 4106 1 +a 1024 1025 3082 +a 1024 4106 1 +a 1025 1026 3081 +a 1025 4106 1 +a 1026 1027 3080 +a 1026 4106 1 +a 1027 1028 3079 +a 1027 4106 1 +a 1028 1029 3078 +a 1028 4106 1 +a 1029 1030 3077 +a 1029 4106 1 +a 1030 1031 3076 +a 1030 4106 1 +a 1031 1032 3075 +a 1031 4106 1 +a 1032 1033 3074 +a 1032 4106 1 +a 1033 1034 3073 +a 1033 4106 1 +a 1034 1035 3072 +a 1034 4106 1 +a 1035 1036 3071 +a 1035 4106 1 +a 1036 1037 3070 +a 1036 4106 1 +a 1037 1038 3069 +a 1037 4106 1 +a 1038 1039 3068 +a 1038 4106 1 +a 1039 1040 3067 +a 1039 4106 1 +a 1040 1041 3066 +a 1040 4106 1 +a 1041 1042 3065 +a 1041 4106 1 +a 1042 1043 3064 +a 1042 4106 1 +a 1043 1044 3063 +a 1043 4106 1 +a 1044 1045 3062 +a 1044 4106 1 +a 1045 1046 3061 +a 1045 4106 1 +a 1046 1047 3060 +a 1046 4106 1 +a 1047 1048 3059 +a 1047 4106 1 +a 1048 1049 3058 +a 1048 4106 1 +a 1049 1050 3057 +a 1049 4106 1 +a 1050 1051 3056 +a 1050 4106 1 +a 1051 1052 3055 +a 1051 4106 1 +a 1052 1053 3054 +a 1052 4106 1 +a 1053 1054 3053 +a 1053 4106 1 +a 1054 1055 3052 +a 1054 4106 1 +a 1055 1056 3051 +a 1055 4106 1 +a 1056 1057 3050 +a 1056 4106 1 +a 1057 1058 3049 +a 1057 4106 1 +a 1058 1059 3048 +a 1058 4106 1 +a 1059 1060 3047 +a 1059 4106 1 +a 1060 1061 3046 +a 1060 4106 1 +a 1061 1062 3045 +a 1061 4106 1 +a 1062 1063 3044 +a 1062 4106 1 +a 1063 1064 3043 +a 1063 4106 1 +a 1064 1065 3042 +a 1064 4106 1 +a 1065 1066 3041 +a 1065 4106 1 +a 1066 1067 3040 +a 1066 4106 1 +a 1067 1068 3039 +a 1067 4106 1 +a 1068 1069 3038 +a 1068 4106 1 +a 1069 1070 3037 +a 1069 4106 1 +a 1070 1071 3036 +a 1070 4106 1 +a 1071 1072 3035 +a 1071 4106 1 +a 1072 1073 3034 +a 1072 4106 1 +a 1073 1074 3033 +a 1073 4106 1 +a 1074 1075 3032 +a 1074 4106 1 +a 1075 1076 3031 +a 1075 4106 1 +a 1076 1077 3030 +a 1076 4106 1 +a 1077 1078 3029 +a 1077 4106 1 +a 1078 1079 3028 +a 1078 4106 1 +a 1079 1080 3027 +a 1079 4106 1 +a 1080 1081 3026 +a 1080 4106 1 +a 1081 1082 3025 +a 1081 4106 1 +a 1082 1083 3024 +a 1082 4106 1 +a 1083 1084 3023 +a 1083 4106 1 +a 1084 1085 3022 +a 1084 4106 1 +a 1085 1086 3021 +a 1085 4106 1 +a 1086 1087 3020 +a 1086 4106 1 +a 1087 1088 3019 +a 1087 4106 1 +a 1088 1089 3018 +a 1088 4106 1 +a 1089 1090 3017 +a 1089 4106 1 +a 1090 1091 3016 +a 1090 4106 1 +a 1091 1092 3015 +a 1091 4106 1 +a 1092 1093 3014 +a 1092 4106 1 +a 1093 1094 3013 +a 1093 4106 1 +a 1094 1095 3012 +a 1094 4106 1 +a 1095 1096 3011 +a 1095 4106 1 +a 1096 1097 3010 +a 1096 4106 1 +a 1097 1098 3009 +a 1097 4106 1 +a 1098 1099 3008 +a 1098 4106 1 +a 1099 1100 3007 +a 1099 4106 1 +a 1100 1101 3006 +a 1100 4106 1 +a 1101 1102 3005 +a 1101 4106 1 +a 1102 1103 3004 +a 1102 4106 1 +a 1103 1104 3003 +a 1103 4106 1 +a 1104 1105 3002 +a 1104 4106 1 +a 1105 1106 3001 +a 1105 4106 1 +a 1106 1107 3000 +a 1106 4106 1 +a 1107 1108 2999 +a 1107 4106 1 +a 1108 1109 2998 +a 1108 4106 1 +a 1109 1110 2997 +a 1109 4106 1 +a 1110 1111 2996 +a 1110 4106 1 +a 1111 1112 2995 +a 1111 4106 1 +a 1112 1113 2994 +a 1112 4106 1 +a 1113 1114 2993 +a 1113 4106 1 +a 1114 1115 2992 +a 1114 4106 1 +a 1115 1116 2991 +a 1115 4106 1 +a 1116 1117 2990 +a 1116 4106 1 +a 1117 1118 2989 +a 1117 4106 1 +a 1118 1119 2988 +a 1118 4106 1 +a 1119 1120 2987 +a 1119 4106 1 +a 1120 1121 2986 +a 1120 4106 1 +a 1121 1122 2985 +a 1121 4106 1 +a 1122 1123 2984 +a 1122 4106 1 +a 1123 1124 2983 +a 1123 4106 1 +a 1124 1125 2982 +a 1124 4106 1 +a 1125 1126 2981 +a 1125 4106 1 +a 1126 1127 2980 +a 1126 4106 1 +a 1127 1128 2979 +a 1127 4106 1 +a 1128 1129 2978 +a 1128 4106 1 +a 1129 1130 2977 +a 1129 4106 1 +a 1130 1131 2976 +a 1130 4106 1 +a 1131 1132 2975 +a 1131 4106 1 +a 1132 1133 2974 +a 1132 4106 1 +a 1133 1134 2973 +a 1133 4106 1 +a 1134 1135 2972 +a 1134 4106 1 +a 1135 1136 2971 +a 1135 4106 1 +a 1136 1137 2970 +a 1136 4106 1 +a 1137 1138 2969 +a 1137 4106 1 +a 1138 1139 2968 +a 1138 4106 1 +a 1139 1140 2967 +a 1139 4106 1 +a 1140 1141 2966 +a 1140 4106 1 +a 1141 1142 2965 +a 1141 4106 1 +a 1142 1143 2964 +a 1142 4106 1 +a 1143 1144 2963 +a 1143 4106 1 +a 1144 1145 2962 +a 1144 4106 1 +a 1145 1146 2961 +a 1145 4106 1 +a 1146 1147 2960 +a 1146 4106 1 +a 1147 1148 2959 +a 1147 4106 1 +a 1148 1149 2958 +a 1148 4106 1 +a 1149 1150 2957 +a 1149 4106 1 +a 1150 1151 2956 +a 1150 4106 1 +a 1151 1152 2955 +a 1151 4106 1 +a 1152 1153 2954 +a 1152 4106 1 +a 1153 1154 2953 +a 1153 4106 1 +a 1154 1155 2952 +a 1154 4106 1 +a 1155 1156 2951 +a 1155 4106 1 +a 1156 1157 2950 +a 1156 4106 1 +a 1157 1158 2949 +a 1157 4106 1 +a 1158 1159 2948 +a 1158 4106 1 +a 1159 1160 2947 +a 1159 4106 1 +a 1160 1161 2946 +a 1160 4106 1 +a 1161 1162 2945 +a 1161 4106 1 +a 1162 1163 2944 +a 1162 4106 1 +a 1163 1164 2943 +a 1163 4106 1 +a 1164 1165 2942 +a 1164 4106 1 +a 1165 1166 2941 +a 1165 4106 1 +a 1166 1167 2940 +a 1166 4106 1 +a 1167 1168 2939 +a 1167 4106 1 +a 1168 1169 2938 +a 1168 4106 1 +a 1169 1170 2937 +a 1169 4106 1 +a 1170 1171 2936 +a 1170 4106 1 +a 1171 1172 2935 +a 1171 4106 1 +a 1172 1173 2934 +a 1172 4106 1 +a 1173 1174 2933 +a 1173 4106 1 +a 1174 1175 2932 +a 1174 4106 1 +a 1175 1176 2931 +a 1175 4106 1 +a 1176 1177 2930 +a 1176 4106 1 +a 1177 1178 2929 +a 1177 4106 1 +a 1178 1179 2928 +a 1178 4106 1 +a 1179 1180 2927 +a 1179 4106 1 +a 1180 1181 2926 +a 1180 4106 1 +a 1181 1182 2925 +a 1181 4106 1 +a 1182 1183 2924 +a 1182 4106 1 +a 1183 1184 2923 +a 1183 4106 1 +a 1184 1185 2922 +a 1184 4106 1 +a 1185 1186 2921 +a 1185 4106 1 +a 1186 1187 2920 +a 1186 4106 1 +a 1187 1188 2919 +a 1187 4106 1 +a 1188 1189 2918 +a 1188 4106 1 +a 1189 1190 2917 +a 1189 4106 1 +a 1190 1191 2916 +a 1190 4106 1 +a 1191 1192 2915 +a 1191 4106 1 +a 1192 1193 2914 +a 1192 4106 1 +a 1193 1194 2913 +a 1193 4106 1 +a 1194 1195 2912 +a 1194 4106 1 +a 1195 1196 2911 +a 1195 4106 1 +a 1196 1197 2910 +a 1196 4106 1 +a 1197 1198 2909 +a 1197 4106 1 +a 1198 1199 2908 +a 1198 4106 1 +a 1199 1200 2907 +a 1199 4106 1 +a 1200 1201 2906 +a 1200 4106 1 +a 1201 1202 2905 +a 1201 4106 1 +a 1202 1203 2904 +a 1202 4106 1 +a 1203 1204 2903 +a 1203 4106 1 +a 1204 1205 2902 +a 1204 4106 1 +a 1205 1206 2901 +a 1205 4106 1 +a 1206 1207 2900 +a 1206 4106 1 +a 1207 1208 2899 +a 1207 4106 1 +a 1208 1209 2898 +a 1208 4106 1 +a 1209 1210 2897 +a 1209 4106 1 +a 1210 1211 2896 +a 1210 4106 1 +a 1211 1212 2895 +a 1211 4106 1 +a 1212 1213 2894 +a 1212 4106 1 +a 1213 1214 2893 +a 1213 4106 1 +a 1214 1215 2892 +a 1214 4106 1 +a 1215 1216 2891 +a 1215 4106 1 +a 1216 1217 2890 +a 1216 4106 1 +a 1217 1218 2889 +a 1217 4106 1 +a 1218 1219 2888 +a 1218 4106 1 +a 1219 1220 2887 +a 1219 4106 1 +a 1220 1221 2886 +a 1220 4106 1 +a 1221 1222 2885 +a 1221 4106 1 +a 1222 1223 2884 +a 1222 4106 1 +a 1223 1224 2883 +a 1223 4106 1 +a 1224 1225 2882 +a 1224 4106 1 +a 1225 1226 2881 +a 1225 4106 1 +a 1226 1227 2880 +a 1226 4106 1 +a 1227 1228 2879 +a 1227 4106 1 +a 1228 1229 2878 +a 1228 4106 1 +a 1229 1230 2877 +a 1229 4106 1 +a 1230 1231 2876 +a 1230 4106 1 +a 1231 1232 2875 +a 1231 4106 1 +a 1232 1233 2874 +a 1232 4106 1 +a 1233 1234 2873 +a 1233 4106 1 +a 1234 1235 2872 +a 1234 4106 1 +a 1235 1236 2871 +a 1235 4106 1 +a 1236 1237 2870 +a 1236 4106 1 +a 1237 1238 2869 +a 1237 4106 1 +a 1238 1239 2868 +a 1238 4106 1 +a 1239 1240 2867 +a 1239 4106 1 +a 1240 1241 2866 +a 1240 4106 1 +a 1241 1242 2865 +a 1241 4106 1 +a 1242 1243 2864 +a 1242 4106 1 +a 1243 1244 2863 +a 1243 4106 1 +a 1244 1245 2862 +a 1244 4106 1 +a 1245 1246 2861 +a 1245 4106 1 +a 1246 1247 2860 +a 1246 4106 1 +a 1247 1248 2859 +a 1247 4106 1 +a 1248 1249 2858 +a 1248 4106 1 +a 1249 1250 2857 +a 1249 4106 1 +a 1250 1251 2856 +a 1250 4106 1 +a 1251 1252 2855 +a 1251 4106 1 +a 1252 1253 2854 +a 1252 4106 1 +a 1253 1254 2853 +a 1253 4106 1 +a 1254 1255 2852 +a 1254 4106 1 +a 1255 1256 2851 +a 1255 4106 1 +a 1256 1257 2850 +a 1256 4106 1 +a 1257 1258 2849 +a 1257 4106 1 +a 1258 1259 2848 +a 1258 4106 1 +a 1259 1260 2847 +a 1259 4106 1 +a 1260 1261 2846 +a 1260 4106 1 +a 1261 1262 2845 +a 1261 4106 1 +a 1262 1263 2844 +a 1262 4106 1 +a 1263 1264 2843 +a 1263 4106 1 +a 1264 1265 2842 +a 1264 4106 1 +a 1265 1266 2841 +a 1265 4106 1 +a 1266 1267 2840 +a 1266 4106 1 +a 1267 1268 2839 +a 1267 4106 1 +a 1268 1269 2838 +a 1268 4106 1 +a 1269 1270 2837 +a 1269 4106 1 +a 1270 1271 2836 +a 1270 4106 1 +a 1271 1272 2835 +a 1271 4106 1 +a 1272 1273 2834 +a 1272 4106 1 +a 1273 1274 2833 +a 1273 4106 1 +a 1274 1275 2832 +a 1274 4106 1 +a 1275 1276 2831 +a 1275 4106 1 +a 1276 1277 2830 +a 1276 4106 1 +a 1277 1278 2829 +a 1277 4106 1 +a 1278 1279 2828 +a 1278 4106 1 +a 1279 1280 2827 +a 1279 4106 1 +a 1280 1281 2826 +a 1280 4106 1 +a 1281 1282 2825 +a 1281 4106 1 +a 1282 1283 2824 +a 1282 4106 1 +a 1283 1284 2823 +a 1283 4106 1 +a 1284 1285 2822 +a 1284 4106 1 +a 1285 1286 2821 +a 1285 4106 1 +a 1286 1287 2820 +a 1286 4106 1 +a 1287 1288 2819 +a 1287 4106 1 +a 1288 1289 2818 +a 1288 4106 1 +a 1289 1290 2817 +a 1289 4106 1 +a 1290 1291 2816 +a 1290 4106 1 +a 1291 1292 2815 +a 1291 4106 1 +a 1292 1293 2814 +a 1292 4106 1 +a 1293 1294 2813 +a 1293 4106 1 +a 1294 1295 2812 +a 1294 4106 1 +a 1295 1296 2811 +a 1295 4106 1 +a 1296 1297 2810 +a 1296 4106 1 +a 1297 1298 2809 +a 1297 4106 1 +a 1298 1299 2808 +a 1298 4106 1 +a 1299 1300 2807 +a 1299 4106 1 +a 1300 1301 2806 +a 1300 4106 1 +a 1301 1302 2805 +a 1301 4106 1 +a 1302 1303 2804 +a 1302 4106 1 +a 1303 1304 2803 +a 1303 4106 1 +a 1304 1305 2802 +a 1304 4106 1 +a 1305 1306 2801 +a 1305 4106 1 +a 1306 1307 2800 +a 1306 4106 1 +a 1307 1308 2799 +a 1307 4106 1 +a 1308 1309 2798 +a 1308 4106 1 +a 1309 1310 2797 +a 1309 4106 1 +a 1310 1311 2796 +a 1310 4106 1 +a 1311 1312 2795 +a 1311 4106 1 +a 1312 1313 2794 +a 1312 4106 1 +a 1313 1314 2793 +a 1313 4106 1 +a 1314 1315 2792 +a 1314 4106 1 +a 1315 1316 2791 +a 1315 4106 1 +a 1316 1317 2790 +a 1316 4106 1 +a 1317 1318 2789 +a 1317 4106 1 +a 1318 1319 2788 +a 1318 4106 1 +a 1319 1320 2787 +a 1319 4106 1 +a 1320 1321 2786 +a 1320 4106 1 +a 1321 1322 2785 +a 1321 4106 1 +a 1322 1323 2784 +a 1322 4106 1 +a 1323 1324 2783 +a 1323 4106 1 +a 1324 1325 2782 +a 1324 4106 1 +a 1325 1326 2781 +a 1325 4106 1 +a 1326 1327 2780 +a 1326 4106 1 +a 1327 1328 2779 +a 1327 4106 1 +a 1328 1329 2778 +a 1328 4106 1 +a 1329 1330 2777 +a 1329 4106 1 +a 1330 1331 2776 +a 1330 4106 1 +a 1331 1332 2775 +a 1331 4106 1 +a 1332 1333 2774 +a 1332 4106 1 +a 1333 1334 2773 +a 1333 4106 1 +a 1334 1335 2772 +a 1334 4106 1 +a 1335 1336 2771 +a 1335 4106 1 +a 1336 1337 2770 +a 1336 4106 1 +a 1337 1338 2769 +a 1337 4106 1 +a 1338 1339 2768 +a 1338 4106 1 +a 1339 1340 2767 +a 1339 4106 1 +a 1340 1341 2766 +a 1340 4106 1 +a 1341 1342 2765 +a 1341 4106 1 +a 1342 1343 2764 +a 1342 4106 1 +a 1343 1344 2763 +a 1343 4106 1 +a 1344 1345 2762 +a 1344 4106 1 +a 1345 1346 2761 +a 1345 4106 1 +a 1346 1347 2760 +a 1346 4106 1 +a 1347 1348 2759 +a 1347 4106 1 +a 1348 1349 2758 +a 1348 4106 1 +a 1349 1350 2757 +a 1349 4106 1 +a 1350 1351 2756 +a 1350 4106 1 +a 1351 1352 2755 +a 1351 4106 1 +a 1352 1353 2754 +a 1352 4106 1 +a 1353 1354 2753 +a 1353 4106 1 +a 1354 1355 2752 +a 1354 4106 1 +a 1355 1356 2751 +a 1355 4106 1 +a 1356 1357 2750 +a 1356 4106 1 +a 1357 1358 2749 +a 1357 4106 1 +a 1358 1359 2748 +a 1358 4106 1 +a 1359 1360 2747 +a 1359 4106 1 +a 1360 1361 2746 +a 1360 4106 1 +a 1361 1362 2745 +a 1361 4106 1 +a 1362 1363 2744 +a 1362 4106 1 +a 1363 1364 2743 +a 1363 4106 1 +a 1364 1365 2742 +a 1364 4106 1 +a 1365 1366 2741 +a 1365 4106 1 +a 1366 1367 2740 +a 1366 4106 1 +a 1367 1368 2739 +a 1367 4106 1 +a 1368 1369 2738 +a 1368 4106 1 +a 1369 1370 2737 +a 1369 4106 1 +a 1370 1371 2736 +a 1370 4106 1 +a 1371 1372 2735 +a 1371 4106 1 +a 1372 1373 2734 +a 1372 4106 1 +a 1373 1374 2733 +a 1373 4106 1 +a 1374 1375 2732 +a 1374 4106 1 +a 1375 1376 2731 +a 1375 4106 1 +a 1376 1377 2730 +a 1376 4106 1 +a 1377 1378 2729 +a 1377 4106 1 +a 1378 1379 2728 +a 1378 4106 1 +a 1379 1380 2727 +a 1379 4106 1 +a 1380 1381 2726 +a 1380 4106 1 +a 1381 1382 2725 +a 1381 4106 1 +a 1382 1383 2724 +a 1382 4106 1 +a 1383 1384 2723 +a 1383 4106 1 +a 1384 1385 2722 +a 1384 4106 1 +a 1385 1386 2721 +a 1385 4106 1 +a 1386 1387 2720 +a 1386 4106 1 +a 1387 1388 2719 +a 1387 4106 1 +a 1388 1389 2718 +a 1388 4106 1 +a 1389 1390 2717 +a 1389 4106 1 +a 1390 1391 2716 +a 1390 4106 1 +a 1391 1392 2715 +a 1391 4106 1 +a 1392 1393 2714 +a 1392 4106 1 +a 1393 1394 2713 +a 1393 4106 1 +a 1394 1395 2712 +a 1394 4106 1 +a 1395 1396 2711 +a 1395 4106 1 +a 1396 1397 2710 +a 1396 4106 1 +a 1397 1398 2709 +a 1397 4106 1 +a 1398 1399 2708 +a 1398 4106 1 +a 1399 1400 2707 +a 1399 4106 1 +a 1400 1401 2706 +a 1400 4106 1 +a 1401 1402 2705 +a 1401 4106 1 +a 1402 1403 2704 +a 1402 4106 1 +a 1403 1404 2703 +a 1403 4106 1 +a 1404 1405 2702 +a 1404 4106 1 +a 1405 1406 2701 +a 1405 4106 1 +a 1406 1407 2700 +a 1406 4106 1 +a 1407 1408 2699 +a 1407 4106 1 +a 1408 1409 2698 +a 1408 4106 1 +a 1409 1410 2697 +a 1409 4106 1 +a 1410 1411 2696 +a 1410 4106 1 +a 1411 1412 2695 +a 1411 4106 1 +a 1412 1413 2694 +a 1412 4106 1 +a 1413 1414 2693 +a 1413 4106 1 +a 1414 1415 2692 +a 1414 4106 1 +a 1415 1416 2691 +a 1415 4106 1 +a 1416 1417 2690 +a 1416 4106 1 +a 1417 1418 2689 +a 1417 4106 1 +a 1418 1419 2688 +a 1418 4106 1 +a 1419 1420 2687 +a 1419 4106 1 +a 1420 1421 2686 +a 1420 4106 1 +a 1421 1422 2685 +a 1421 4106 1 +a 1422 1423 2684 +a 1422 4106 1 +a 1423 1424 2683 +a 1423 4106 1 +a 1424 1425 2682 +a 1424 4106 1 +a 1425 1426 2681 +a 1425 4106 1 +a 1426 1427 2680 +a 1426 4106 1 +a 1427 1428 2679 +a 1427 4106 1 +a 1428 1429 2678 +a 1428 4106 1 +a 1429 1430 2677 +a 1429 4106 1 +a 1430 1431 2676 +a 1430 4106 1 +a 1431 1432 2675 +a 1431 4106 1 +a 1432 1433 2674 +a 1432 4106 1 +a 1433 1434 2673 +a 1433 4106 1 +a 1434 1435 2672 +a 1434 4106 1 +a 1435 1436 2671 +a 1435 4106 1 +a 1436 1437 2670 +a 1436 4106 1 +a 1437 1438 2669 +a 1437 4106 1 +a 1438 1439 2668 +a 1438 4106 1 +a 1439 1440 2667 +a 1439 4106 1 +a 1440 1441 2666 +a 1440 4106 1 +a 1441 1442 2665 +a 1441 4106 1 +a 1442 1443 2664 +a 1442 4106 1 +a 1443 1444 2663 +a 1443 4106 1 +a 1444 1445 2662 +a 1444 4106 1 +a 1445 1446 2661 +a 1445 4106 1 +a 1446 1447 2660 +a 1446 4106 1 +a 1447 1448 2659 +a 1447 4106 1 +a 1448 1449 2658 +a 1448 4106 1 +a 1449 1450 2657 +a 1449 4106 1 +a 1450 1451 2656 +a 1450 4106 1 +a 1451 1452 2655 +a 1451 4106 1 +a 1452 1453 2654 +a 1452 4106 1 +a 1453 1454 2653 +a 1453 4106 1 +a 1454 1455 2652 +a 1454 4106 1 +a 1455 1456 2651 +a 1455 4106 1 +a 1456 1457 2650 +a 1456 4106 1 +a 1457 1458 2649 +a 1457 4106 1 +a 1458 1459 2648 +a 1458 4106 1 +a 1459 1460 2647 +a 1459 4106 1 +a 1460 1461 2646 +a 1460 4106 1 +a 1461 1462 2645 +a 1461 4106 1 +a 1462 1463 2644 +a 1462 4106 1 +a 1463 1464 2643 +a 1463 4106 1 +a 1464 1465 2642 +a 1464 4106 1 +a 1465 1466 2641 +a 1465 4106 1 +a 1466 1467 2640 +a 1466 4106 1 +a 1467 1468 2639 +a 1467 4106 1 +a 1468 1469 2638 +a 1468 4106 1 +a 1469 1470 2637 +a 1469 4106 1 +a 1470 1471 2636 +a 1470 4106 1 +a 1471 1472 2635 +a 1471 4106 1 +a 1472 1473 2634 +a 1472 4106 1 +a 1473 1474 2633 +a 1473 4106 1 +a 1474 1475 2632 +a 1474 4106 1 +a 1475 1476 2631 +a 1475 4106 1 +a 1476 1477 2630 +a 1476 4106 1 +a 1477 1478 2629 +a 1477 4106 1 +a 1478 1479 2628 +a 1478 4106 1 +a 1479 1480 2627 +a 1479 4106 1 +a 1480 1481 2626 +a 1480 4106 1 +a 1481 1482 2625 +a 1481 4106 1 +a 1482 1483 2624 +a 1482 4106 1 +a 1483 1484 2623 +a 1483 4106 1 +a 1484 1485 2622 +a 1484 4106 1 +a 1485 1486 2621 +a 1485 4106 1 +a 1486 1487 2620 +a 1486 4106 1 +a 1487 1488 2619 +a 1487 4106 1 +a 1488 1489 2618 +a 1488 4106 1 +a 1489 1490 2617 +a 1489 4106 1 +a 1490 1491 2616 +a 1490 4106 1 +a 1491 1492 2615 +a 1491 4106 1 +a 1492 1493 2614 +a 1492 4106 1 +a 1493 1494 2613 +a 1493 4106 1 +a 1494 1495 2612 +a 1494 4106 1 +a 1495 1496 2611 +a 1495 4106 1 +a 1496 1497 2610 +a 1496 4106 1 +a 1497 1498 2609 +a 1497 4106 1 +a 1498 1499 2608 +a 1498 4106 1 +a 1499 1500 2607 +a 1499 4106 1 +a 1500 1501 2606 +a 1500 4106 1 +a 1501 1502 2605 +a 1501 4106 1 +a 1502 1503 2604 +a 1502 4106 1 +a 1503 1504 2603 +a 1503 4106 1 +a 1504 1505 2602 +a 1504 4106 1 +a 1505 1506 2601 +a 1505 4106 1 +a 1506 1507 2600 +a 1506 4106 1 +a 1507 1508 2599 +a 1507 4106 1 +a 1508 1509 2598 +a 1508 4106 1 +a 1509 1510 2597 +a 1509 4106 1 +a 1510 1511 2596 +a 1510 4106 1 +a 1511 1512 2595 +a 1511 4106 1 +a 1512 1513 2594 +a 1512 4106 1 +a 1513 1514 2593 +a 1513 4106 1 +a 1514 1515 2592 +a 1514 4106 1 +a 1515 1516 2591 +a 1515 4106 1 +a 1516 1517 2590 +a 1516 4106 1 +a 1517 1518 2589 +a 1517 4106 1 +a 1518 1519 2588 +a 1518 4106 1 +a 1519 1520 2587 +a 1519 4106 1 +a 1520 1521 2586 +a 1520 4106 1 +a 1521 1522 2585 +a 1521 4106 1 +a 1522 1523 2584 +a 1522 4106 1 +a 1523 1524 2583 +a 1523 4106 1 +a 1524 1525 2582 +a 1524 4106 1 +a 1525 1526 2581 +a 1525 4106 1 +a 1526 1527 2580 +a 1526 4106 1 +a 1527 1528 2579 +a 1527 4106 1 +a 1528 1529 2578 +a 1528 4106 1 +a 1529 1530 2577 +a 1529 4106 1 +a 1530 1531 2576 +a 1530 4106 1 +a 1531 1532 2575 +a 1531 4106 1 +a 1532 1533 2574 +a 1532 4106 1 +a 1533 1534 2573 +a 1533 4106 1 +a 1534 1535 2572 +a 1534 4106 1 +a 1535 1536 2571 +a 1535 4106 1 +a 1536 1537 2570 +a 1536 4106 1 +a 1537 1538 2569 +a 1537 4106 1 +a 1538 1539 2568 +a 1538 4106 1 +a 1539 1540 2567 +a 1539 4106 1 +a 1540 1541 2566 +a 1540 4106 1 +a 1541 1542 2565 +a 1541 4106 1 +a 1542 1543 2564 +a 1542 4106 1 +a 1543 1544 2563 +a 1543 4106 1 +a 1544 1545 2562 +a 1544 4106 1 +a 1545 1546 2561 +a 1545 4106 1 +a 1546 1547 2560 +a 1546 4106 1 +a 1547 1548 2559 +a 1547 4106 1 +a 1548 1549 2558 +a 1548 4106 1 +a 1549 1550 2557 +a 1549 4106 1 +a 1550 1551 2556 +a 1550 4106 1 +a 1551 1552 2555 +a 1551 4106 1 +a 1552 1553 2554 +a 1552 4106 1 +a 1553 1554 2553 +a 1553 4106 1 +a 1554 1555 2552 +a 1554 4106 1 +a 1555 1556 2551 +a 1555 4106 1 +a 1556 1557 2550 +a 1556 4106 1 +a 1557 1558 2549 +a 1557 4106 1 +a 1558 1559 2548 +a 1558 4106 1 +a 1559 1560 2547 +a 1559 4106 1 +a 1560 1561 2546 +a 1560 4106 1 +a 1561 1562 2545 +a 1561 4106 1 +a 1562 1563 2544 +a 1562 4106 1 +a 1563 1564 2543 +a 1563 4106 1 +a 1564 1565 2542 +a 1564 4106 1 +a 1565 1566 2541 +a 1565 4106 1 +a 1566 1567 2540 +a 1566 4106 1 +a 1567 1568 2539 +a 1567 4106 1 +a 1568 1569 2538 +a 1568 4106 1 +a 1569 1570 2537 +a 1569 4106 1 +a 1570 1571 2536 +a 1570 4106 1 +a 1571 1572 2535 +a 1571 4106 1 +a 1572 1573 2534 +a 1572 4106 1 +a 1573 1574 2533 +a 1573 4106 1 +a 1574 1575 2532 +a 1574 4106 1 +a 1575 1576 2531 +a 1575 4106 1 +a 1576 1577 2530 +a 1576 4106 1 +a 1577 1578 2529 +a 1577 4106 1 +a 1578 1579 2528 +a 1578 4106 1 +a 1579 1580 2527 +a 1579 4106 1 +a 1580 1581 2526 +a 1580 4106 1 +a 1581 1582 2525 +a 1581 4106 1 +a 1582 1583 2524 +a 1582 4106 1 +a 1583 1584 2523 +a 1583 4106 1 +a 1584 1585 2522 +a 1584 4106 1 +a 1585 1586 2521 +a 1585 4106 1 +a 1586 1587 2520 +a 1586 4106 1 +a 1587 1588 2519 +a 1587 4106 1 +a 1588 1589 2518 +a 1588 4106 1 +a 1589 1590 2517 +a 1589 4106 1 +a 1590 1591 2516 +a 1590 4106 1 +a 1591 1592 2515 +a 1591 4106 1 +a 1592 1593 2514 +a 1592 4106 1 +a 1593 1594 2513 +a 1593 4106 1 +a 1594 1595 2512 +a 1594 4106 1 +a 1595 1596 2511 +a 1595 4106 1 +a 1596 1597 2510 +a 1596 4106 1 +a 1597 1598 2509 +a 1597 4106 1 +a 1598 1599 2508 +a 1598 4106 1 +a 1599 1600 2507 +a 1599 4106 1 +a 1600 1601 2506 +a 1600 4106 1 +a 1601 1602 2505 +a 1601 4106 1 +a 1602 1603 2504 +a 1602 4106 1 +a 1603 1604 2503 +a 1603 4106 1 +a 1604 1605 2502 +a 1604 4106 1 +a 1605 1606 2501 +a 1605 4106 1 +a 1606 1607 2500 +a 1606 4106 1 +a 1607 1608 2499 +a 1607 4106 1 +a 1608 1609 2498 +a 1608 4106 1 +a 1609 1610 2497 +a 1609 4106 1 +a 1610 1611 2496 +a 1610 4106 1 +a 1611 1612 2495 +a 1611 4106 1 +a 1612 1613 2494 +a 1612 4106 1 +a 1613 1614 2493 +a 1613 4106 1 +a 1614 1615 2492 +a 1614 4106 1 +a 1615 1616 2491 +a 1615 4106 1 +a 1616 1617 2490 +a 1616 4106 1 +a 1617 1618 2489 +a 1617 4106 1 +a 1618 1619 2488 +a 1618 4106 1 +a 1619 1620 2487 +a 1619 4106 1 +a 1620 1621 2486 +a 1620 4106 1 +a 1621 1622 2485 +a 1621 4106 1 +a 1622 1623 2484 +a 1622 4106 1 +a 1623 1624 2483 +a 1623 4106 1 +a 1624 1625 2482 +a 1624 4106 1 +a 1625 1626 2481 +a 1625 4106 1 +a 1626 1627 2480 +a 1626 4106 1 +a 1627 1628 2479 +a 1627 4106 1 +a 1628 1629 2478 +a 1628 4106 1 +a 1629 1630 2477 +a 1629 4106 1 +a 1630 1631 2476 +a 1630 4106 1 +a 1631 1632 2475 +a 1631 4106 1 +a 1632 1633 2474 +a 1632 4106 1 +a 1633 1634 2473 +a 1633 4106 1 +a 1634 1635 2472 +a 1634 4106 1 +a 1635 1636 2471 +a 1635 4106 1 +a 1636 1637 2470 +a 1636 4106 1 +a 1637 1638 2469 +a 1637 4106 1 +a 1638 1639 2468 +a 1638 4106 1 +a 1639 1640 2467 +a 1639 4106 1 +a 1640 1641 2466 +a 1640 4106 1 +a 1641 1642 2465 +a 1641 4106 1 +a 1642 1643 2464 +a 1642 4106 1 +a 1643 1644 2463 +a 1643 4106 1 +a 1644 1645 2462 +a 1644 4106 1 +a 1645 1646 2461 +a 1645 4106 1 +a 1646 1647 2460 +a 1646 4106 1 +a 1647 1648 2459 +a 1647 4106 1 +a 1648 1649 2458 +a 1648 4106 1 +a 1649 1650 2457 +a 1649 4106 1 +a 1650 1651 2456 +a 1650 4106 1 +a 1651 1652 2455 +a 1651 4106 1 +a 1652 1653 2454 +a 1652 4106 1 +a 1653 1654 2453 +a 1653 4106 1 +a 1654 1655 2452 +a 1654 4106 1 +a 1655 1656 2451 +a 1655 4106 1 +a 1656 1657 2450 +a 1656 4106 1 +a 1657 1658 2449 +a 1657 4106 1 +a 1658 1659 2448 +a 1658 4106 1 +a 1659 1660 2447 +a 1659 4106 1 +a 1660 1661 2446 +a 1660 4106 1 +a 1661 1662 2445 +a 1661 4106 1 +a 1662 1663 2444 +a 1662 4106 1 +a 1663 1664 2443 +a 1663 4106 1 +a 1664 1665 2442 +a 1664 4106 1 +a 1665 1666 2441 +a 1665 4106 1 +a 1666 1667 2440 +a 1666 4106 1 +a 1667 1668 2439 +a 1667 4106 1 +a 1668 1669 2438 +a 1668 4106 1 +a 1669 1670 2437 +a 1669 4106 1 +a 1670 1671 2436 +a 1670 4106 1 +a 1671 1672 2435 +a 1671 4106 1 +a 1672 1673 2434 +a 1672 4106 1 +a 1673 1674 2433 +a 1673 4106 1 +a 1674 1675 2432 +a 1674 4106 1 +a 1675 1676 2431 +a 1675 4106 1 +a 1676 1677 2430 +a 1676 4106 1 +a 1677 1678 2429 +a 1677 4106 1 +a 1678 1679 2428 +a 1678 4106 1 +a 1679 1680 2427 +a 1679 4106 1 +a 1680 1681 2426 +a 1680 4106 1 +a 1681 1682 2425 +a 1681 4106 1 +a 1682 1683 2424 +a 1682 4106 1 +a 1683 1684 2423 +a 1683 4106 1 +a 1684 1685 2422 +a 1684 4106 1 +a 1685 1686 2421 +a 1685 4106 1 +a 1686 1687 2420 +a 1686 4106 1 +a 1687 1688 2419 +a 1687 4106 1 +a 1688 1689 2418 +a 1688 4106 1 +a 1689 1690 2417 +a 1689 4106 1 +a 1690 1691 2416 +a 1690 4106 1 +a 1691 1692 2415 +a 1691 4106 1 +a 1692 1693 2414 +a 1692 4106 1 +a 1693 1694 2413 +a 1693 4106 1 +a 1694 1695 2412 +a 1694 4106 1 +a 1695 1696 2411 +a 1695 4106 1 +a 1696 1697 2410 +a 1696 4106 1 +a 1697 1698 2409 +a 1697 4106 1 +a 1698 1699 2408 +a 1698 4106 1 +a 1699 1700 2407 +a 1699 4106 1 +a 1700 1701 2406 +a 1700 4106 1 +a 1701 1702 2405 +a 1701 4106 1 +a 1702 1703 2404 +a 1702 4106 1 +a 1703 1704 2403 +a 1703 4106 1 +a 1704 1705 2402 +a 1704 4106 1 +a 1705 1706 2401 +a 1705 4106 1 +a 1706 1707 2400 +a 1706 4106 1 +a 1707 1708 2399 +a 1707 4106 1 +a 1708 1709 2398 +a 1708 4106 1 +a 1709 1710 2397 +a 1709 4106 1 +a 1710 1711 2396 +a 1710 4106 1 +a 1711 1712 2395 +a 1711 4106 1 +a 1712 1713 2394 +a 1712 4106 1 +a 1713 1714 2393 +a 1713 4106 1 +a 1714 1715 2392 +a 1714 4106 1 +a 1715 1716 2391 +a 1715 4106 1 +a 1716 1717 2390 +a 1716 4106 1 +a 1717 1718 2389 +a 1717 4106 1 +a 1718 1719 2388 +a 1718 4106 1 +a 1719 1720 2387 +a 1719 4106 1 +a 1720 1721 2386 +a 1720 4106 1 +a 1721 1722 2385 +a 1721 4106 1 +a 1722 1723 2384 +a 1722 4106 1 +a 1723 1724 2383 +a 1723 4106 1 +a 1724 1725 2382 +a 1724 4106 1 +a 1725 1726 2381 +a 1725 4106 1 +a 1726 1727 2380 +a 1726 4106 1 +a 1727 1728 2379 +a 1727 4106 1 +a 1728 1729 2378 +a 1728 4106 1 +a 1729 1730 2377 +a 1729 4106 1 +a 1730 1731 2376 +a 1730 4106 1 +a 1731 1732 2375 +a 1731 4106 1 +a 1732 1733 2374 +a 1732 4106 1 +a 1733 1734 2373 +a 1733 4106 1 +a 1734 1735 2372 +a 1734 4106 1 +a 1735 1736 2371 +a 1735 4106 1 +a 1736 1737 2370 +a 1736 4106 1 +a 1737 1738 2369 +a 1737 4106 1 +a 1738 1739 2368 +a 1738 4106 1 +a 1739 1740 2367 +a 1739 4106 1 +a 1740 1741 2366 +a 1740 4106 1 +a 1741 1742 2365 +a 1741 4106 1 +a 1742 1743 2364 +a 1742 4106 1 +a 1743 1744 2363 +a 1743 4106 1 +a 1744 1745 2362 +a 1744 4106 1 +a 1745 1746 2361 +a 1745 4106 1 +a 1746 1747 2360 +a 1746 4106 1 +a 1747 1748 2359 +a 1747 4106 1 +a 1748 1749 2358 +a 1748 4106 1 +a 1749 1750 2357 +a 1749 4106 1 +a 1750 1751 2356 +a 1750 4106 1 +a 1751 1752 2355 +a 1751 4106 1 +a 1752 1753 2354 +a 1752 4106 1 +a 1753 1754 2353 +a 1753 4106 1 +a 1754 1755 2352 +a 1754 4106 1 +a 1755 1756 2351 +a 1755 4106 1 +a 1756 1757 2350 +a 1756 4106 1 +a 1757 1758 2349 +a 1757 4106 1 +a 1758 1759 2348 +a 1758 4106 1 +a 1759 1760 2347 +a 1759 4106 1 +a 1760 1761 2346 +a 1760 4106 1 +a 1761 1762 2345 +a 1761 4106 1 +a 1762 1763 2344 +a 1762 4106 1 +a 1763 1764 2343 +a 1763 4106 1 +a 1764 1765 2342 +a 1764 4106 1 +a 1765 1766 2341 +a 1765 4106 1 +a 1766 1767 2340 +a 1766 4106 1 +a 1767 1768 2339 +a 1767 4106 1 +a 1768 1769 2338 +a 1768 4106 1 +a 1769 1770 2337 +a 1769 4106 1 +a 1770 1771 2336 +a 1770 4106 1 +a 1771 1772 2335 +a 1771 4106 1 +a 1772 1773 2334 +a 1772 4106 1 +a 1773 1774 2333 +a 1773 4106 1 +a 1774 1775 2332 +a 1774 4106 1 +a 1775 1776 2331 +a 1775 4106 1 +a 1776 1777 2330 +a 1776 4106 1 +a 1777 1778 2329 +a 1777 4106 1 +a 1778 1779 2328 +a 1778 4106 1 +a 1779 1780 2327 +a 1779 4106 1 +a 1780 1781 2326 +a 1780 4106 1 +a 1781 1782 2325 +a 1781 4106 1 +a 1782 1783 2324 +a 1782 4106 1 +a 1783 1784 2323 +a 1783 4106 1 +a 1784 1785 2322 +a 1784 4106 1 +a 1785 1786 2321 +a 1785 4106 1 +a 1786 1787 2320 +a 1786 4106 1 +a 1787 1788 2319 +a 1787 4106 1 +a 1788 1789 2318 +a 1788 4106 1 +a 1789 1790 2317 +a 1789 4106 1 +a 1790 1791 2316 +a 1790 4106 1 +a 1791 1792 2315 +a 1791 4106 1 +a 1792 1793 2314 +a 1792 4106 1 +a 1793 1794 2313 +a 1793 4106 1 +a 1794 1795 2312 +a 1794 4106 1 +a 1795 1796 2311 +a 1795 4106 1 +a 1796 1797 2310 +a 1796 4106 1 +a 1797 1798 2309 +a 1797 4106 1 +a 1798 1799 2308 +a 1798 4106 1 +a 1799 1800 2307 +a 1799 4106 1 +a 1800 1801 2306 +a 1800 4106 1 +a 1801 1802 2305 +a 1801 4106 1 +a 1802 1803 2304 +a 1802 4106 1 +a 1803 1804 2303 +a 1803 4106 1 +a 1804 1805 2302 +a 1804 4106 1 +a 1805 1806 2301 +a 1805 4106 1 +a 1806 1807 2300 +a 1806 4106 1 +a 1807 1808 2299 +a 1807 4106 1 +a 1808 1809 2298 +a 1808 4106 1 +a 1809 1810 2297 +a 1809 4106 1 +a 1810 1811 2296 +a 1810 4106 1 +a 1811 1812 2295 +a 1811 4106 1 +a 1812 1813 2294 +a 1812 4106 1 +a 1813 1814 2293 +a 1813 4106 1 +a 1814 1815 2292 +a 1814 4106 1 +a 1815 1816 2291 +a 1815 4106 1 +a 1816 1817 2290 +a 1816 4106 1 +a 1817 1818 2289 +a 1817 4106 1 +a 1818 1819 2288 +a 1818 4106 1 +a 1819 1820 2287 +a 1819 4106 1 +a 1820 1821 2286 +a 1820 4106 1 +a 1821 1822 2285 +a 1821 4106 1 +a 1822 1823 2284 +a 1822 4106 1 +a 1823 1824 2283 +a 1823 4106 1 +a 1824 1825 2282 +a 1824 4106 1 +a 1825 1826 2281 +a 1825 4106 1 +a 1826 1827 2280 +a 1826 4106 1 +a 1827 1828 2279 +a 1827 4106 1 +a 1828 1829 2278 +a 1828 4106 1 +a 1829 1830 2277 +a 1829 4106 1 +a 1830 1831 2276 +a 1830 4106 1 +a 1831 1832 2275 +a 1831 4106 1 +a 1832 1833 2274 +a 1832 4106 1 +a 1833 1834 2273 +a 1833 4106 1 +a 1834 1835 2272 +a 1834 4106 1 +a 1835 1836 2271 +a 1835 4106 1 +a 1836 1837 2270 +a 1836 4106 1 +a 1837 1838 2269 +a 1837 4106 1 +a 1838 1839 2268 +a 1838 4106 1 +a 1839 1840 2267 +a 1839 4106 1 +a 1840 1841 2266 +a 1840 4106 1 +a 1841 1842 2265 +a 1841 4106 1 +a 1842 1843 2264 +a 1842 4106 1 +a 1843 1844 2263 +a 1843 4106 1 +a 1844 1845 2262 +a 1844 4106 1 +a 1845 1846 2261 +a 1845 4106 1 +a 1846 1847 2260 +a 1846 4106 1 +a 1847 1848 2259 +a 1847 4106 1 +a 1848 1849 2258 +a 1848 4106 1 +a 1849 1850 2257 +a 1849 4106 1 +a 1850 1851 2256 +a 1850 4106 1 +a 1851 1852 2255 +a 1851 4106 1 +a 1852 1853 2254 +a 1852 4106 1 +a 1853 1854 2253 +a 1853 4106 1 +a 1854 1855 2252 +a 1854 4106 1 +a 1855 1856 2251 +a 1855 4106 1 +a 1856 1857 2250 +a 1856 4106 1 +a 1857 1858 2249 +a 1857 4106 1 +a 1858 1859 2248 +a 1858 4106 1 +a 1859 1860 2247 +a 1859 4106 1 +a 1860 1861 2246 +a 1860 4106 1 +a 1861 1862 2245 +a 1861 4106 1 +a 1862 1863 2244 +a 1862 4106 1 +a 1863 1864 2243 +a 1863 4106 1 +a 1864 1865 2242 +a 1864 4106 1 +a 1865 1866 2241 +a 1865 4106 1 +a 1866 1867 2240 +a 1866 4106 1 +a 1867 1868 2239 +a 1867 4106 1 +a 1868 1869 2238 +a 1868 4106 1 +a 1869 1870 2237 +a 1869 4106 1 +a 1870 1871 2236 +a 1870 4106 1 +a 1871 1872 2235 +a 1871 4106 1 +a 1872 1873 2234 +a 1872 4106 1 +a 1873 1874 2233 +a 1873 4106 1 +a 1874 1875 2232 +a 1874 4106 1 +a 1875 1876 2231 +a 1875 4106 1 +a 1876 1877 2230 +a 1876 4106 1 +a 1877 1878 2229 +a 1877 4106 1 +a 1878 1879 2228 +a 1878 4106 1 +a 1879 1880 2227 +a 1879 4106 1 +a 1880 1881 2226 +a 1880 4106 1 +a 1881 1882 2225 +a 1881 4106 1 +a 1882 1883 2224 +a 1882 4106 1 +a 1883 1884 2223 +a 1883 4106 1 +a 1884 1885 2222 +a 1884 4106 1 +a 1885 1886 2221 +a 1885 4106 1 +a 1886 1887 2220 +a 1886 4106 1 +a 1887 1888 2219 +a 1887 4106 1 +a 1888 1889 2218 +a 1888 4106 1 +a 1889 1890 2217 +a 1889 4106 1 +a 1890 1891 2216 +a 1890 4106 1 +a 1891 1892 2215 +a 1891 4106 1 +a 1892 1893 2214 +a 1892 4106 1 +a 1893 1894 2213 +a 1893 4106 1 +a 1894 1895 2212 +a 1894 4106 1 +a 1895 1896 2211 +a 1895 4106 1 +a 1896 1897 2210 +a 1896 4106 1 +a 1897 1898 2209 +a 1897 4106 1 +a 1898 1899 2208 +a 1898 4106 1 +a 1899 1900 2207 +a 1899 4106 1 +a 1900 1901 2206 +a 1900 4106 1 +a 1901 1902 2205 +a 1901 4106 1 +a 1902 1903 2204 +a 1902 4106 1 +a 1903 1904 2203 +a 1903 4106 1 +a 1904 1905 2202 +a 1904 4106 1 +a 1905 1906 2201 +a 1905 4106 1 +a 1906 1907 2200 +a 1906 4106 1 +a 1907 1908 2199 +a 1907 4106 1 +a 1908 1909 2198 +a 1908 4106 1 +a 1909 1910 2197 +a 1909 4106 1 +a 1910 1911 2196 +a 1910 4106 1 +a 1911 1912 2195 +a 1911 4106 1 +a 1912 1913 2194 +a 1912 4106 1 +a 1913 1914 2193 +a 1913 4106 1 +a 1914 1915 2192 +a 1914 4106 1 +a 1915 1916 2191 +a 1915 4106 1 +a 1916 1917 2190 +a 1916 4106 1 +a 1917 1918 2189 +a 1917 4106 1 +a 1918 1919 2188 +a 1918 4106 1 +a 1919 1920 2187 +a 1919 4106 1 +a 1920 1921 2186 +a 1920 4106 1 +a 1921 1922 2185 +a 1921 4106 1 +a 1922 1923 2184 +a 1922 4106 1 +a 1923 1924 2183 +a 1923 4106 1 +a 1924 1925 2182 +a 1924 4106 1 +a 1925 1926 2181 +a 1925 4106 1 +a 1926 1927 2180 +a 1926 4106 1 +a 1927 1928 2179 +a 1927 4106 1 +a 1928 1929 2178 +a 1928 4106 1 +a 1929 1930 2177 +a 1929 4106 1 +a 1930 1931 2176 +a 1930 4106 1 +a 1931 1932 2175 +a 1931 4106 1 +a 1932 1933 2174 +a 1932 4106 1 +a 1933 1934 2173 +a 1933 4106 1 +a 1934 1935 2172 +a 1934 4106 1 +a 1935 1936 2171 +a 1935 4106 1 +a 1936 1937 2170 +a 1936 4106 1 +a 1937 1938 2169 +a 1937 4106 1 +a 1938 1939 2168 +a 1938 4106 1 +a 1939 1940 2167 +a 1939 4106 1 +a 1940 1941 2166 +a 1940 4106 1 +a 1941 1942 2165 +a 1941 4106 1 +a 1942 1943 2164 +a 1942 4106 1 +a 1943 1944 2163 +a 1943 4106 1 +a 1944 1945 2162 +a 1944 4106 1 +a 1945 1946 2161 +a 1945 4106 1 +a 1946 1947 2160 +a 1946 4106 1 +a 1947 1948 2159 +a 1947 4106 1 +a 1948 1949 2158 +a 1948 4106 1 +a 1949 1950 2157 +a 1949 4106 1 +a 1950 1951 2156 +a 1950 4106 1 +a 1951 1952 2155 +a 1951 4106 1 +a 1952 1953 2154 +a 1952 4106 1 +a 1953 1954 2153 +a 1953 4106 1 +a 1954 1955 2152 +a 1954 4106 1 +a 1955 1956 2151 +a 1955 4106 1 +a 1956 1957 2150 +a 1956 4106 1 +a 1957 1958 2149 +a 1957 4106 1 +a 1958 1959 2148 +a 1958 4106 1 +a 1959 1960 2147 +a 1959 4106 1 +a 1960 1961 2146 +a 1960 4106 1 +a 1961 1962 2145 +a 1961 4106 1 +a 1962 1963 2144 +a 1962 4106 1 +a 1963 1964 2143 +a 1963 4106 1 +a 1964 1965 2142 +a 1964 4106 1 +a 1965 1966 2141 +a 1965 4106 1 +a 1966 1967 2140 +a 1966 4106 1 +a 1967 1968 2139 +a 1967 4106 1 +a 1968 1969 2138 +a 1968 4106 1 +a 1969 1970 2137 +a 1969 4106 1 +a 1970 1971 2136 +a 1970 4106 1 +a 1971 1972 2135 +a 1971 4106 1 +a 1972 1973 2134 +a 1972 4106 1 +a 1973 1974 2133 +a 1973 4106 1 +a 1974 1975 2132 +a 1974 4106 1 +a 1975 1976 2131 +a 1975 4106 1 +a 1976 1977 2130 +a 1976 4106 1 +a 1977 1978 2129 +a 1977 4106 1 +a 1978 1979 2128 +a 1978 4106 1 +a 1979 1980 2127 +a 1979 4106 1 +a 1980 1981 2126 +a 1980 4106 1 +a 1981 1982 2125 +a 1981 4106 1 +a 1982 1983 2124 +a 1982 4106 1 +a 1983 1984 2123 +a 1983 4106 1 +a 1984 1985 2122 +a 1984 4106 1 +a 1985 1986 2121 +a 1985 4106 1 +a 1986 1987 2120 +a 1986 4106 1 +a 1987 1988 2119 +a 1987 4106 1 +a 1988 1989 2118 +a 1988 4106 1 +a 1989 1990 2117 +a 1989 4106 1 +a 1990 1991 2116 +a 1990 4106 1 +a 1991 1992 2115 +a 1991 4106 1 +a 1992 1993 2114 +a 1992 4106 1 +a 1993 1994 2113 +a 1993 4106 1 +a 1994 1995 2112 +a 1994 4106 1 +a 1995 1996 2111 +a 1995 4106 1 +a 1996 1997 2110 +a 1996 4106 1 +a 1997 1998 2109 +a 1997 4106 1 +a 1998 1999 2108 +a 1998 4106 1 +a 1999 2000 2107 +a 1999 4106 1 +a 2000 2001 2106 +a 2000 4106 1 +a 2001 2002 2105 +a 2001 4106 1 +a 2002 2003 2104 +a 2002 4106 1 +a 2003 2004 2103 +a 2003 4106 1 +a 2004 2005 2102 +a 2004 4106 1 +a 2005 2006 2101 +a 2005 4106 1 +a 2006 2007 2100 +a 2006 4106 1 +a 2007 2008 2099 +a 2007 4106 1 +a 2008 2009 2098 +a 2008 4106 1 +a 2009 2010 2097 +a 2009 4106 1 +a 2010 2011 2096 +a 2010 4106 1 +a 2011 2012 2095 +a 2011 4106 1 +a 2012 2013 2094 +a 2012 4106 1 +a 2013 2014 2093 +a 2013 4106 1 +a 2014 2015 2092 +a 2014 4106 1 +a 2015 2016 2091 +a 2015 4106 1 +a 2016 2017 2090 +a 2016 4106 1 +a 2017 2018 2089 +a 2017 4106 1 +a 2018 2019 2088 +a 2018 4106 1 +a 2019 2020 2087 +a 2019 4106 1 +a 2020 2021 2086 +a 2020 4106 1 +a 2021 2022 2085 +a 2021 4106 1 +a 2022 2023 2084 +a 2022 4106 1 +a 2023 2024 2083 +a 2023 4106 1 +a 2024 2025 2082 +a 2024 4106 1 +a 2025 2026 2081 +a 2025 4106 1 +a 2026 2027 2080 +a 2026 4106 1 +a 2027 2028 2079 +a 2027 4106 1 +a 2028 2029 2078 +a 2028 4106 1 +a 2029 2030 2077 +a 2029 4106 1 +a 2030 2031 2076 +a 2030 4106 1 +a 2031 2032 2075 +a 2031 4106 1 +a 2032 2033 2074 +a 2032 4106 1 +a 2033 2034 2073 +a 2033 4106 1 +a 2034 2035 2072 +a 2034 4106 1 +a 2035 2036 2071 +a 2035 4106 1 +a 2036 2037 2070 +a 2036 4106 1 +a 2037 2038 2069 +a 2037 4106 1 +a 2038 2039 2068 +a 2038 4106 1 +a 2039 2040 2067 +a 2039 4106 1 +a 2040 2041 2066 +a 2040 4106 1 +a 2041 2042 2065 +a 2041 4106 1 +a 2042 2043 2064 +a 2042 4106 1 +a 2043 2044 2063 +a 2043 4106 1 +a 2044 2045 2062 +a 2044 4106 1 +a 2045 2046 2061 +a 2045 4106 1 +a 2046 2047 2060 +a 2046 4106 1 +a 2047 2048 2059 +a 2047 4106 1 +a 2048 2049 2058 +a 2048 4106 1 +a 2049 2050 2057 +a 2049 4106 1 +a 2050 2051 2056 +a 2050 4106 1 +a 2051 2052 2055 +a 2051 4106 1 +a 2052 2053 2054 +a 2052 4106 1 +a 2053 2054 2053 +a 2053 4106 1 +a 2054 2055 2052 +a 2054 4106 1 +a 2055 2056 2051 +a 2055 4106 1 +a 2056 2057 2050 +a 2056 4106 1 +a 2057 2058 2049 +a 2057 4106 1 +a 2058 2059 2048 +a 2058 4106 1 +a 2059 2060 2047 +a 2059 4106 1 +a 2060 2061 2046 +a 2060 4106 1 +a 2061 2062 2045 +a 2061 4106 1 +a 2062 2063 2044 +a 2062 4106 1 +a 2063 2064 2043 +a 2063 4106 1 +a 2064 2065 2042 +a 2064 4106 1 +a 2065 2066 2041 +a 2065 4106 1 +a 2066 2067 2040 +a 2066 4106 1 +a 2067 2068 2039 +a 2067 4106 1 +a 2068 2069 2038 +a 2068 4106 1 +a 2069 2070 2037 +a 2069 4106 1 +a 2070 2071 2036 +a 2070 4106 1 +a 2071 2072 2035 +a 2071 4106 1 +a 2072 2073 2034 +a 2072 4106 1 +a 2073 2074 2033 +a 2073 4106 1 +a 2074 2075 2032 +a 2074 4106 1 +a 2075 2076 2031 +a 2075 4106 1 +a 2076 2077 2030 +a 2076 4106 1 +a 2077 2078 2029 +a 2077 4106 1 +a 2078 2079 2028 +a 2078 4106 1 +a 2079 2080 2027 +a 2079 4106 1 +a 2080 2081 2026 +a 2080 4106 1 +a 2081 2082 2025 +a 2081 4106 1 +a 2082 2083 2024 +a 2082 4106 1 +a 2083 2084 2023 +a 2083 4106 1 +a 2084 2085 2022 +a 2084 4106 1 +a 2085 2086 2021 +a 2085 4106 1 +a 2086 2087 2020 +a 2086 4106 1 +a 2087 2088 2019 +a 2087 4106 1 +a 2088 2089 2018 +a 2088 4106 1 +a 2089 2090 2017 +a 2089 4106 1 +a 2090 2091 2016 +a 2090 4106 1 +a 2091 2092 2015 +a 2091 4106 1 +a 2092 2093 2014 +a 2092 4106 1 +a 2093 2094 2013 +a 2093 4106 1 +a 2094 2095 2012 +a 2094 4106 1 +a 2095 2096 2011 +a 2095 4106 1 +a 2096 2097 2010 +a 2096 4106 1 +a 2097 2098 2009 +a 2097 4106 1 +a 2098 2099 2008 +a 2098 4106 1 +a 2099 2100 2007 +a 2099 4106 1 +a 2100 2101 2006 +a 2100 4106 1 +a 2101 2102 2005 +a 2101 4106 1 +a 2102 2103 2004 +a 2102 4106 1 +a 2103 2104 2003 +a 2103 4106 1 +a 2104 2105 2002 +a 2104 4106 1 +a 2105 2106 2001 +a 2105 4106 1 +a 2106 2107 2000 +a 2106 4106 1 +a 2107 2108 1999 +a 2107 4106 1 +a 2108 2109 1998 +a 2108 4106 1 +a 2109 2110 1997 +a 2109 4106 1 +a 2110 2111 1996 +a 2110 4106 1 +a 2111 2112 1995 +a 2111 4106 1 +a 2112 2113 1994 +a 2112 4106 1 +a 2113 2114 1993 +a 2113 4106 1 +a 2114 2115 1992 +a 2114 4106 1 +a 2115 2116 1991 +a 2115 4106 1 +a 2116 2117 1990 +a 2116 4106 1 +a 2117 2118 1989 +a 2117 4106 1 +a 2118 2119 1988 +a 2118 4106 1 +a 2119 2120 1987 +a 2119 4106 1 +a 2120 2121 1986 +a 2120 4106 1 +a 2121 2122 1985 +a 2121 4106 1 +a 2122 2123 1984 +a 2122 4106 1 +a 2123 2124 1983 +a 2123 4106 1 +a 2124 2125 1982 +a 2124 4106 1 +a 2125 2126 1981 +a 2125 4106 1 +a 2126 2127 1980 +a 2126 4106 1 +a 2127 2128 1979 +a 2127 4106 1 +a 2128 2129 1978 +a 2128 4106 1 +a 2129 2130 1977 +a 2129 4106 1 +a 2130 2131 1976 +a 2130 4106 1 +a 2131 2132 1975 +a 2131 4106 1 +a 2132 2133 1974 +a 2132 4106 1 +a 2133 2134 1973 +a 2133 4106 1 +a 2134 2135 1972 +a 2134 4106 1 +a 2135 2136 1971 +a 2135 4106 1 +a 2136 2137 1970 +a 2136 4106 1 +a 2137 2138 1969 +a 2137 4106 1 +a 2138 2139 1968 +a 2138 4106 1 +a 2139 2140 1967 +a 2139 4106 1 +a 2140 2141 1966 +a 2140 4106 1 +a 2141 2142 1965 +a 2141 4106 1 +a 2142 2143 1964 +a 2142 4106 1 +a 2143 2144 1963 +a 2143 4106 1 +a 2144 2145 1962 +a 2144 4106 1 +a 2145 2146 1961 +a 2145 4106 1 +a 2146 2147 1960 +a 2146 4106 1 +a 2147 2148 1959 +a 2147 4106 1 +a 2148 2149 1958 +a 2148 4106 1 +a 2149 2150 1957 +a 2149 4106 1 +a 2150 2151 1956 +a 2150 4106 1 +a 2151 2152 1955 +a 2151 4106 1 +a 2152 2153 1954 +a 2152 4106 1 +a 2153 2154 1953 +a 2153 4106 1 +a 2154 2155 1952 +a 2154 4106 1 +a 2155 2156 1951 +a 2155 4106 1 +a 2156 2157 1950 +a 2156 4106 1 +a 2157 2158 1949 +a 2157 4106 1 +a 2158 2159 1948 +a 2158 4106 1 +a 2159 2160 1947 +a 2159 4106 1 +a 2160 2161 1946 +a 2160 4106 1 +a 2161 2162 1945 +a 2161 4106 1 +a 2162 2163 1944 +a 2162 4106 1 +a 2163 2164 1943 +a 2163 4106 1 +a 2164 2165 1942 +a 2164 4106 1 +a 2165 2166 1941 +a 2165 4106 1 +a 2166 2167 1940 +a 2166 4106 1 +a 2167 2168 1939 +a 2167 4106 1 +a 2168 2169 1938 +a 2168 4106 1 +a 2169 2170 1937 +a 2169 4106 1 +a 2170 2171 1936 +a 2170 4106 1 +a 2171 2172 1935 +a 2171 4106 1 +a 2172 2173 1934 +a 2172 4106 1 +a 2173 2174 1933 +a 2173 4106 1 +a 2174 2175 1932 +a 2174 4106 1 +a 2175 2176 1931 +a 2175 4106 1 +a 2176 2177 1930 +a 2176 4106 1 +a 2177 2178 1929 +a 2177 4106 1 +a 2178 2179 1928 +a 2178 4106 1 +a 2179 2180 1927 +a 2179 4106 1 +a 2180 2181 1926 +a 2180 4106 1 +a 2181 2182 1925 +a 2181 4106 1 +a 2182 2183 1924 +a 2182 4106 1 +a 2183 2184 1923 +a 2183 4106 1 +a 2184 2185 1922 +a 2184 4106 1 +a 2185 2186 1921 +a 2185 4106 1 +a 2186 2187 1920 +a 2186 4106 1 +a 2187 2188 1919 +a 2187 4106 1 +a 2188 2189 1918 +a 2188 4106 1 +a 2189 2190 1917 +a 2189 4106 1 +a 2190 2191 1916 +a 2190 4106 1 +a 2191 2192 1915 +a 2191 4106 1 +a 2192 2193 1914 +a 2192 4106 1 +a 2193 2194 1913 +a 2193 4106 1 +a 2194 2195 1912 +a 2194 4106 1 +a 2195 2196 1911 +a 2195 4106 1 +a 2196 2197 1910 +a 2196 4106 1 +a 2197 2198 1909 +a 2197 4106 1 +a 2198 2199 1908 +a 2198 4106 1 +a 2199 2200 1907 +a 2199 4106 1 +a 2200 2201 1906 +a 2200 4106 1 +a 2201 2202 1905 +a 2201 4106 1 +a 2202 2203 1904 +a 2202 4106 1 +a 2203 2204 1903 +a 2203 4106 1 +a 2204 2205 1902 +a 2204 4106 1 +a 2205 2206 1901 +a 2205 4106 1 +a 2206 2207 1900 +a 2206 4106 1 +a 2207 2208 1899 +a 2207 4106 1 +a 2208 2209 1898 +a 2208 4106 1 +a 2209 2210 1897 +a 2209 4106 1 +a 2210 2211 1896 +a 2210 4106 1 +a 2211 2212 1895 +a 2211 4106 1 +a 2212 2213 1894 +a 2212 4106 1 +a 2213 2214 1893 +a 2213 4106 1 +a 2214 2215 1892 +a 2214 4106 1 +a 2215 2216 1891 +a 2215 4106 1 +a 2216 2217 1890 +a 2216 4106 1 +a 2217 2218 1889 +a 2217 4106 1 +a 2218 2219 1888 +a 2218 4106 1 +a 2219 2220 1887 +a 2219 4106 1 +a 2220 2221 1886 +a 2220 4106 1 +a 2221 2222 1885 +a 2221 4106 1 +a 2222 2223 1884 +a 2222 4106 1 +a 2223 2224 1883 +a 2223 4106 1 +a 2224 2225 1882 +a 2224 4106 1 +a 2225 2226 1881 +a 2225 4106 1 +a 2226 2227 1880 +a 2226 4106 1 +a 2227 2228 1879 +a 2227 4106 1 +a 2228 2229 1878 +a 2228 4106 1 +a 2229 2230 1877 +a 2229 4106 1 +a 2230 2231 1876 +a 2230 4106 1 +a 2231 2232 1875 +a 2231 4106 1 +a 2232 2233 1874 +a 2232 4106 1 +a 2233 2234 1873 +a 2233 4106 1 +a 2234 2235 1872 +a 2234 4106 1 +a 2235 2236 1871 +a 2235 4106 1 +a 2236 2237 1870 +a 2236 4106 1 +a 2237 2238 1869 +a 2237 4106 1 +a 2238 2239 1868 +a 2238 4106 1 +a 2239 2240 1867 +a 2239 4106 1 +a 2240 2241 1866 +a 2240 4106 1 +a 2241 2242 1865 +a 2241 4106 1 +a 2242 2243 1864 +a 2242 4106 1 +a 2243 2244 1863 +a 2243 4106 1 +a 2244 2245 1862 +a 2244 4106 1 +a 2245 2246 1861 +a 2245 4106 1 +a 2246 2247 1860 +a 2246 4106 1 +a 2247 2248 1859 +a 2247 4106 1 +a 2248 2249 1858 +a 2248 4106 1 +a 2249 2250 1857 +a 2249 4106 1 +a 2250 2251 1856 +a 2250 4106 1 +a 2251 2252 1855 +a 2251 4106 1 +a 2252 2253 1854 +a 2252 4106 1 +a 2253 2254 1853 +a 2253 4106 1 +a 2254 2255 1852 +a 2254 4106 1 +a 2255 2256 1851 +a 2255 4106 1 +a 2256 2257 1850 +a 2256 4106 1 +a 2257 2258 1849 +a 2257 4106 1 +a 2258 2259 1848 +a 2258 4106 1 +a 2259 2260 1847 +a 2259 4106 1 +a 2260 2261 1846 +a 2260 4106 1 +a 2261 2262 1845 +a 2261 4106 1 +a 2262 2263 1844 +a 2262 4106 1 +a 2263 2264 1843 +a 2263 4106 1 +a 2264 2265 1842 +a 2264 4106 1 +a 2265 2266 1841 +a 2265 4106 1 +a 2266 2267 1840 +a 2266 4106 1 +a 2267 2268 1839 +a 2267 4106 1 +a 2268 2269 1838 +a 2268 4106 1 +a 2269 2270 1837 +a 2269 4106 1 +a 2270 2271 1836 +a 2270 4106 1 +a 2271 2272 1835 +a 2271 4106 1 +a 2272 2273 1834 +a 2272 4106 1 +a 2273 2274 1833 +a 2273 4106 1 +a 2274 2275 1832 +a 2274 4106 1 +a 2275 2276 1831 +a 2275 4106 1 +a 2276 2277 1830 +a 2276 4106 1 +a 2277 2278 1829 +a 2277 4106 1 +a 2278 2279 1828 +a 2278 4106 1 +a 2279 2280 1827 +a 2279 4106 1 +a 2280 2281 1826 +a 2280 4106 1 +a 2281 2282 1825 +a 2281 4106 1 +a 2282 2283 1824 +a 2282 4106 1 +a 2283 2284 1823 +a 2283 4106 1 +a 2284 2285 1822 +a 2284 4106 1 +a 2285 2286 1821 +a 2285 4106 1 +a 2286 2287 1820 +a 2286 4106 1 +a 2287 2288 1819 +a 2287 4106 1 +a 2288 2289 1818 +a 2288 4106 1 +a 2289 2290 1817 +a 2289 4106 1 +a 2290 2291 1816 +a 2290 4106 1 +a 2291 2292 1815 +a 2291 4106 1 +a 2292 2293 1814 +a 2292 4106 1 +a 2293 2294 1813 +a 2293 4106 1 +a 2294 2295 1812 +a 2294 4106 1 +a 2295 2296 1811 +a 2295 4106 1 +a 2296 2297 1810 +a 2296 4106 1 +a 2297 2298 1809 +a 2297 4106 1 +a 2298 2299 1808 +a 2298 4106 1 +a 2299 2300 1807 +a 2299 4106 1 +a 2300 2301 1806 +a 2300 4106 1 +a 2301 2302 1805 +a 2301 4106 1 +a 2302 2303 1804 +a 2302 4106 1 +a 2303 2304 1803 +a 2303 4106 1 +a 2304 2305 1802 +a 2304 4106 1 +a 2305 2306 1801 +a 2305 4106 1 +a 2306 2307 1800 +a 2306 4106 1 +a 2307 2308 1799 +a 2307 4106 1 +a 2308 2309 1798 +a 2308 4106 1 +a 2309 2310 1797 +a 2309 4106 1 +a 2310 2311 1796 +a 2310 4106 1 +a 2311 2312 1795 +a 2311 4106 1 +a 2312 2313 1794 +a 2312 4106 1 +a 2313 2314 1793 +a 2313 4106 1 +a 2314 2315 1792 +a 2314 4106 1 +a 2315 2316 1791 +a 2315 4106 1 +a 2316 2317 1790 +a 2316 4106 1 +a 2317 2318 1789 +a 2317 4106 1 +a 2318 2319 1788 +a 2318 4106 1 +a 2319 2320 1787 +a 2319 4106 1 +a 2320 2321 1786 +a 2320 4106 1 +a 2321 2322 1785 +a 2321 4106 1 +a 2322 2323 1784 +a 2322 4106 1 +a 2323 2324 1783 +a 2323 4106 1 +a 2324 2325 1782 +a 2324 4106 1 +a 2325 2326 1781 +a 2325 4106 1 +a 2326 2327 1780 +a 2326 4106 1 +a 2327 2328 1779 +a 2327 4106 1 +a 2328 2329 1778 +a 2328 4106 1 +a 2329 2330 1777 +a 2329 4106 1 +a 2330 2331 1776 +a 2330 4106 1 +a 2331 2332 1775 +a 2331 4106 1 +a 2332 2333 1774 +a 2332 4106 1 +a 2333 2334 1773 +a 2333 4106 1 +a 2334 2335 1772 +a 2334 4106 1 +a 2335 2336 1771 +a 2335 4106 1 +a 2336 2337 1770 +a 2336 4106 1 +a 2337 2338 1769 +a 2337 4106 1 +a 2338 2339 1768 +a 2338 4106 1 +a 2339 2340 1767 +a 2339 4106 1 +a 2340 2341 1766 +a 2340 4106 1 +a 2341 2342 1765 +a 2341 4106 1 +a 2342 2343 1764 +a 2342 4106 1 +a 2343 2344 1763 +a 2343 4106 1 +a 2344 2345 1762 +a 2344 4106 1 +a 2345 2346 1761 +a 2345 4106 1 +a 2346 2347 1760 +a 2346 4106 1 +a 2347 2348 1759 +a 2347 4106 1 +a 2348 2349 1758 +a 2348 4106 1 +a 2349 2350 1757 +a 2349 4106 1 +a 2350 2351 1756 +a 2350 4106 1 +a 2351 2352 1755 +a 2351 4106 1 +a 2352 2353 1754 +a 2352 4106 1 +a 2353 2354 1753 +a 2353 4106 1 +a 2354 2355 1752 +a 2354 4106 1 +a 2355 2356 1751 +a 2355 4106 1 +a 2356 2357 1750 +a 2356 4106 1 +a 2357 2358 1749 +a 2357 4106 1 +a 2358 2359 1748 +a 2358 4106 1 +a 2359 2360 1747 +a 2359 4106 1 +a 2360 2361 1746 +a 2360 4106 1 +a 2361 2362 1745 +a 2361 4106 1 +a 2362 2363 1744 +a 2362 4106 1 +a 2363 2364 1743 +a 2363 4106 1 +a 2364 2365 1742 +a 2364 4106 1 +a 2365 2366 1741 +a 2365 4106 1 +a 2366 2367 1740 +a 2366 4106 1 +a 2367 2368 1739 +a 2367 4106 1 +a 2368 2369 1738 +a 2368 4106 1 +a 2369 2370 1737 +a 2369 4106 1 +a 2370 2371 1736 +a 2370 4106 1 +a 2371 2372 1735 +a 2371 4106 1 +a 2372 2373 1734 +a 2372 4106 1 +a 2373 2374 1733 +a 2373 4106 1 +a 2374 2375 1732 +a 2374 4106 1 +a 2375 2376 1731 +a 2375 4106 1 +a 2376 2377 1730 +a 2376 4106 1 +a 2377 2378 1729 +a 2377 4106 1 +a 2378 2379 1728 +a 2378 4106 1 +a 2379 2380 1727 +a 2379 4106 1 +a 2380 2381 1726 +a 2380 4106 1 +a 2381 2382 1725 +a 2381 4106 1 +a 2382 2383 1724 +a 2382 4106 1 +a 2383 2384 1723 +a 2383 4106 1 +a 2384 2385 1722 +a 2384 4106 1 +a 2385 2386 1721 +a 2385 4106 1 +a 2386 2387 1720 +a 2386 4106 1 +a 2387 2388 1719 +a 2387 4106 1 +a 2388 2389 1718 +a 2388 4106 1 +a 2389 2390 1717 +a 2389 4106 1 +a 2390 2391 1716 +a 2390 4106 1 +a 2391 2392 1715 +a 2391 4106 1 +a 2392 2393 1714 +a 2392 4106 1 +a 2393 2394 1713 +a 2393 4106 1 +a 2394 2395 1712 +a 2394 4106 1 +a 2395 2396 1711 +a 2395 4106 1 +a 2396 2397 1710 +a 2396 4106 1 +a 2397 2398 1709 +a 2397 4106 1 +a 2398 2399 1708 +a 2398 4106 1 +a 2399 2400 1707 +a 2399 4106 1 +a 2400 2401 1706 +a 2400 4106 1 +a 2401 2402 1705 +a 2401 4106 1 +a 2402 2403 1704 +a 2402 4106 1 +a 2403 2404 1703 +a 2403 4106 1 +a 2404 2405 1702 +a 2404 4106 1 +a 2405 2406 1701 +a 2405 4106 1 +a 2406 2407 1700 +a 2406 4106 1 +a 2407 2408 1699 +a 2407 4106 1 +a 2408 2409 1698 +a 2408 4106 1 +a 2409 2410 1697 +a 2409 4106 1 +a 2410 2411 1696 +a 2410 4106 1 +a 2411 2412 1695 +a 2411 4106 1 +a 2412 2413 1694 +a 2412 4106 1 +a 2413 2414 1693 +a 2413 4106 1 +a 2414 2415 1692 +a 2414 4106 1 +a 2415 2416 1691 +a 2415 4106 1 +a 2416 2417 1690 +a 2416 4106 1 +a 2417 2418 1689 +a 2417 4106 1 +a 2418 2419 1688 +a 2418 4106 1 +a 2419 2420 1687 +a 2419 4106 1 +a 2420 2421 1686 +a 2420 4106 1 +a 2421 2422 1685 +a 2421 4106 1 +a 2422 2423 1684 +a 2422 4106 1 +a 2423 2424 1683 +a 2423 4106 1 +a 2424 2425 1682 +a 2424 4106 1 +a 2425 2426 1681 +a 2425 4106 1 +a 2426 2427 1680 +a 2426 4106 1 +a 2427 2428 1679 +a 2427 4106 1 +a 2428 2429 1678 +a 2428 4106 1 +a 2429 2430 1677 +a 2429 4106 1 +a 2430 2431 1676 +a 2430 4106 1 +a 2431 2432 1675 +a 2431 4106 1 +a 2432 2433 1674 +a 2432 4106 1 +a 2433 2434 1673 +a 2433 4106 1 +a 2434 2435 1672 +a 2434 4106 1 +a 2435 2436 1671 +a 2435 4106 1 +a 2436 2437 1670 +a 2436 4106 1 +a 2437 2438 1669 +a 2437 4106 1 +a 2438 2439 1668 +a 2438 4106 1 +a 2439 2440 1667 +a 2439 4106 1 +a 2440 2441 1666 +a 2440 4106 1 +a 2441 2442 1665 +a 2441 4106 1 +a 2442 2443 1664 +a 2442 4106 1 +a 2443 2444 1663 +a 2443 4106 1 +a 2444 2445 1662 +a 2444 4106 1 +a 2445 2446 1661 +a 2445 4106 1 +a 2446 2447 1660 +a 2446 4106 1 +a 2447 2448 1659 +a 2447 4106 1 +a 2448 2449 1658 +a 2448 4106 1 +a 2449 2450 1657 +a 2449 4106 1 +a 2450 2451 1656 +a 2450 4106 1 +a 2451 2452 1655 +a 2451 4106 1 +a 2452 2453 1654 +a 2452 4106 1 +a 2453 2454 1653 +a 2453 4106 1 +a 2454 2455 1652 +a 2454 4106 1 +a 2455 2456 1651 +a 2455 4106 1 +a 2456 2457 1650 +a 2456 4106 1 +a 2457 2458 1649 +a 2457 4106 1 +a 2458 2459 1648 +a 2458 4106 1 +a 2459 2460 1647 +a 2459 4106 1 +a 2460 2461 1646 +a 2460 4106 1 +a 2461 2462 1645 +a 2461 4106 1 +a 2462 2463 1644 +a 2462 4106 1 +a 2463 2464 1643 +a 2463 4106 1 +a 2464 2465 1642 +a 2464 4106 1 +a 2465 2466 1641 +a 2465 4106 1 +a 2466 2467 1640 +a 2466 4106 1 +a 2467 2468 1639 +a 2467 4106 1 +a 2468 2469 1638 +a 2468 4106 1 +a 2469 2470 1637 +a 2469 4106 1 +a 2470 2471 1636 +a 2470 4106 1 +a 2471 2472 1635 +a 2471 4106 1 +a 2472 2473 1634 +a 2472 4106 1 +a 2473 2474 1633 +a 2473 4106 1 +a 2474 2475 1632 +a 2474 4106 1 +a 2475 2476 1631 +a 2475 4106 1 +a 2476 2477 1630 +a 2476 4106 1 +a 2477 2478 1629 +a 2477 4106 1 +a 2478 2479 1628 +a 2478 4106 1 +a 2479 2480 1627 +a 2479 4106 1 +a 2480 2481 1626 +a 2480 4106 1 +a 2481 2482 1625 +a 2481 4106 1 +a 2482 2483 1624 +a 2482 4106 1 +a 2483 2484 1623 +a 2483 4106 1 +a 2484 2485 1622 +a 2484 4106 1 +a 2485 2486 1621 +a 2485 4106 1 +a 2486 2487 1620 +a 2486 4106 1 +a 2487 2488 1619 +a 2487 4106 1 +a 2488 2489 1618 +a 2488 4106 1 +a 2489 2490 1617 +a 2489 4106 1 +a 2490 2491 1616 +a 2490 4106 1 +a 2491 2492 1615 +a 2491 4106 1 +a 2492 2493 1614 +a 2492 4106 1 +a 2493 2494 1613 +a 2493 4106 1 +a 2494 2495 1612 +a 2494 4106 1 +a 2495 2496 1611 +a 2495 4106 1 +a 2496 2497 1610 +a 2496 4106 1 +a 2497 2498 1609 +a 2497 4106 1 +a 2498 2499 1608 +a 2498 4106 1 +a 2499 2500 1607 +a 2499 4106 1 +a 2500 2501 1606 +a 2500 4106 1 +a 2501 2502 1605 +a 2501 4106 1 +a 2502 2503 1604 +a 2502 4106 1 +a 2503 2504 1603 +a 2503 4106 1 +a 2504 2505 1602 +a 2504 4106 1 +a 2505 2506 1601 +a 2505 4106 1 +a 2506 2507 1600 +a 2506 4106 1 +a 2507 2508 1599 +a 2507 4106 1 +a 2508 2509 1598 +a 2508 4106 1 +a 2509 2510 1597 +a 2509 4106 1 +a 2510 2511 1596 +a 2510 4106 1 +a 2511 2512 1595 +a 2511 4106 1 +a 2512 2513 1594 +a 2512 4106 1 +a 2513 2514 1593 +a 2513 4106 1 +a 2514 2515 1592 +a 2514 4106 1 +a 2515 2516 1591 +a 2515 4106 1 +a 2516 2517 1590 +a 2516 4106 1 +a 2517 2518 1589 +a 2517 4106 1 +a 2518 2519 1588 +a 2518 4106 1 +a 2519 2520 1587 +a 2519 4106 1 +a 2520 2521 1586 +a 2520 4106 1 +a 2521 2522 1585 +a 2521 4106 1 +a 2522 2523 1584 +a 2522 4106 1 +a 2523 2524 1583 +a 2523 4106 1 +a 2524 2525 1582 +a 2524 4106 1 +a 2525 2526 1581 +a 2525 4106 1 +a 2526 2527 1580 +a 2526 4106 1 +a 2527 2528 1579 +a 2527 4106 1 +a 2528 2529 1578 +a 2528 4106 1 +a 2529 2530 1577 +a 2529 4106 1 +a 2530 2531 1576 +a 2530 4106 1 +a 2531 2532 1575 +a 2531 4106 1 +a 2532 2533 1574 +a 2532 4106 1 +a 2533 2534 1573 +a 2533 4106 1 +a 2534 2535 1572 +a 2534 4106 1 +a 2535 2536 1571 +a 2535 4106 1 +a 2536 2537 1570 +a 2536 4106 1 +a 2537 2538 1569 +a 2537 4106 1 +a 2538 2539 1568 +a 2538 4106 1 +a 2539 2540 1567 +a 2539 4106 1 +a 2540 2541 1566 +a 2540 4106 1 +a 2541 2542 1565 +a 2541 4106 1 +a 2542 2543 1564 +a 2542 4106 1 +a 2543 2544 1563 +a 2543 4106 1 +a 2544 2545 1562 +a 2544 4106 1 +a 2545 2546 1561 +a 2545 4106 1 +a 2546 2547 1560 +a 2546 4106 1 +a 2547 2548 1559 +a 2547 4106 1 +a 2548 2549 1558 +a 2548 4106 1 +a 2549 2550 1557 +a 2549 4106 1 +a 2550 2551 1556 +a 2550 4106 1 +a 2551 2552 1555 +a 2551 4106 1 +a 2552 2553 1554 +a 2552 4106 1 +a 2553 2554 1553 +a 2553 4106 1 +a 2554 2555 1552 +a 2554 4106 1 +a 2555 2556 1551 +a 2555 4106 1 +a 2556 2557 1550 +a 2556 4106 1 +a 2557 2558 1549 +a 2557 4106 1 +a 2558 2559 1548 +a 2558 4106 1 +a 2559 2560 1547 +a 2559 4106 1 +a 2560 2561 1546 +a 2560 4106 1 +a 2561 2562 1545 +a 2561 4106 1 +a 2562 2563 1544 +a 2562 4106 1 +a 2563 2564 1543 +a 2563 4106 1 +a 2564 2565 1542 +a 2564 4106 1 +a 2565 2566 1541 +a 2565 4106 1 +a 2566 2567 1540 +a 2566 4106 1 +a 2567 2568 1539 +a 2567 4106 1 +a 2568 2569 1538 +a 2568 4106 1 +a 2569 2570 1537 +a 2569 4106 1 +a 2570 2571 1536 +a 2570 4106 1 +a 2571 2572 1535 +a 2571 4106 1 +a 2572 2573 1534 +a 2572 4106 1 +a 2573 2574 1533 +a 2573 4106 1 +a 2574 2575 1532 +a 2574 4106 1 +a 2575 2576 1531 +a 2575 4106 1 +a 2576 2577 1530 +a 2576 4106 1 +a 2577 2578 1529 +a 2577 4106 1 +a 2578 2579 1528 +a 2578 4106 1 +a 2579 2580 1527 +a 2579 4106 1 +a 2580 2581 1526 +a 2580 4106 1 +a 2581 2582 1525 +a 2581 4106 1 +a 2582 2583 1524 +a 2582 4106 1 +a 2583 2584 1523 +a 2583 4106 1 +a 2584 2585 1522 +a 2584 4106 1 +a 2585 2586 1521 +a 2585 4106 1 +a 2586 2587 1520 +a 2586 4106 1 +a 2587 2588 1519 +a 2587 4106 1 +a 2588 2589 1518 +a 2588 4106 1 +a 2589 2590 1517 +a 2589 4106 1 +a 2590 2591 1516 +a 2590 4106 1 +a 2591 2592 1515 +a 2591 4106 1 +a 2592 2593 1514 +a 2592 4106 1 +a 2593 2594 1513 +a 2593 4106 1 +a 2594 2595 1512 +a 2594 4106 1 +a 2595 2596 1511 +a 2595 4106 1 +a 2596 2597 1510 +a 2596 4106 1 +a 2597 2598 1509 +a 2597 4106 1 +a 2598 2599 1508 +a 2598 4106 1 +a 2599 2600 1507 +a 2599 4106 1 +a 2600 2601 1506 +a 2600 4106 1 +a 2601 2602 1505 +a 2601 4106 1 +a 2602 2603 1504 +a 2602 4106 1 +a 2603 2604 1503 +a 2603 4106 1 +a 2604 2605 1502 +a 2604 4106 1 +a 2605 2606 1501 +a 2605 4106 1 +a 2606 2607 1500 +a 2606 4106 1 +a 2607 2608 1499 +a 2607 4106 1 +a 2608 2609 1498 +a 2608 4106 1 +a 2609 2610 1497 +a 2609 4106 1 +a 2610 2611 1496 +a 2610 4106 1 +a 2611 2612 1495 +a 2611 4106 1 +a 2612 2613 1494 +a 2612 4106 1 +a 2613 2614 1493 +a 2613 4106 1 +a 2614 2615 1492 +a 2614 4106 1 +a 2615 2616 1491 +a 2615 4106 1 +a 2616 2617 1490 +a 2616 4106 1 +a 2617 2618 1489 +a 2617 4106 1 +a 2618 2619 1488 +a 2618 4106 1 +a 2619 2620 1487 +a 2619 4106 1 +a 2620 2621 1486 +a 2620 4106 1 +a 2621 2622 1485 +a 2621 4106 1 +a 2622 2623 1484 +a 2622 4106 1 +a 2623 2624 1483 +a 2623 4106 1 +a 2624 2625 1482 +a 2624 4106 1 +a 2625 2626 1481 +a 2625 4106 1 +a 2626 2627 1480 +a 2626 4106 1 +a 2627 2628 1479 +a 2627 4106 1 +a 2628 2629 1478 +a 2628 4106 1 +a 2629 2630 1477 +a 2629 4106 1 +a 2630 2631 1476 +a 2630 4106 1 +a 2631 2632 1475 +a 2631 4106 1 +a 2632 2633 1474 +a 2632 4106 1 +a 2633 2634 1473 +a 2633 4106 1 +a 2634 2635 1472 +a 2634 4106 1 +a 2635 2636 1471 +a 2635 4106 1 +a 2636 2637 1470 +a 2636 4106 1 +a 2637 2638 1469 +a 2637 4106 1 +a 2638 2639 1468 +a 2638 4106 1 +a 2639 2640 1467 +a 2639 4106 1 +a 2640 2641 1466 +a 2640 4106 1 +a 2641 2642 1465 +a 2641 4106 1 +a 2642 2643 1464 +a 2642 4106 1 +a 2643 2644 1463 +a 2643 4106 1 +a 2644 2645 1462 +a 2644 4106 1 +a 2645 2646 1461 +a 2645 4106 1 +a 2646 2647 1460 +a 2646 4106 1 +a 2647 2648 1459 +a 2647 4106 1 +a 2648 2649 1458 +a 2648 4106 1 +a 2649 2650 1457 +a 2649 4106 1 +a 2650 2651 1456 +a 2650 4106 1 +a 2651 2652 1455 +a 2651 4106 1 +a 2652 2653 1454 +a 2652 4106 1 +a 2653 2654 1453 +a 2653 4106 1 +a 2654 2655 1452 +a 2654 4106 1 +a 2655 2656 1451 +a 2655 4106 1 +a 2656 2657 1450 +a 2656 4106 1 +a 2657 2658 1449 +a 2657 4106 1 +a 2658 2659 1448 +a 2658 4106 1 +a 2659 2660 1447 +a 2659 4106 1 +a 2660 2661 1446 +a 2660 4106 1 +a 2661 2662 1445 +a 2661 4106 1 +a 2662 2663 1444 +a 2662 4106 1 +a 2663 2664 1443 +a 2663 4106 1 +a 2664 2665 1442 +a 2664 4106 1 +a 2665 2666 1441 +a 2665 4106 1 +a 2666 2667 1440 +a 2666 4106 1 +a 2667 2668 1439 +a 2667 4106 1 +a 2668 2669 1438 +a 2668 4106 1 +a 2669 2670 1437 +a 2669 4106 1 +a 2670 2671 1436 +a 2670 4106 1 +a 2671 2672 1435 +a 2671 4106 1 +a 2672 2673 1434 +a 2672 4106 1 +a 2673 2674 1433 +a 2673 4106 1 +a 2674 2675 1432 +a 2674 4106 1 +a 2675 2676 1431 +a 2675 4106 1 +a 2676 2677 1430 +a 2676 4106 1 +a 2677 2678 1429 +a 2677 4106 1 +a 2678 2679 1428 +a 2678 4106 1 +a 2679 2680 1427 +a 2679 4106 1 +a 2680 2681 1426 +a 2680 4106 1 +a 2681 2682 1425 +a 2681 4106 1 +a 2682 2683 1424 +a 2682 4106 1 +a 2683 2684 1423 +a 2683 4106 1 +a 2684 2685 1422 +a 2684 4106 1 +a 2685 2686 1421 +a 2685 4106 1 +a 2686 2687 1420 +a 2686 4106 1 +a 2687 2688 1419 +a 2687 4106 1 +a 2688 2689 1418 +a 2688 4106 1 +a 2689 2690 1417 +a 2689 4106 1 +a 2690 2691 1416 +a 2690 4106 1 +a 2691 2692 1415 +a 2691 4106 1 +a 2692 2693 1414 +a 2692 4106 1 +a 2693 2694 1413 +a 2693 4106 1 +a 2694 2695 1412 +a 2694 4106 1 +a 2695 2696 1411 +a 2695 4106 1 +a 2696 2697 1410 +a 2696 4106 1 +a 2697 2698 1409 +a 2697 4106 1 +a 2698 2699 1408 +a 2698 4106 1 +a 2699 2700 1407 +a 2699 4106 1 +a 2700 2701 1406 +a 2700 4106 1 +a 2701 2702 1405 +a 2701 4106 1 +a 2702 2703 1404 +a 2702 4106 1 +a 2703 2704 1403 +a 2703 4106 1 +a 2704 2705 1402 +a 2704 4106 1 +a 2705 2706 1401 +a 2705 4106 1 +a 2706 2707 1400 +a 2706 4106 1 +a 2707 2708 1399 +a 2707 4106 1 +a 2708 2709 1398 +a 2708 4106 1 +a 2709 2710 1397 +a 2709 4106 1 +a 2710 2711 1396 +a 2710 4106 1 +a 2711 2712 1395 +a 2711 4106 1 +a 2712 2713 1394 +a 2712 4106 1 +a 2713 2714 1393 +a 2713 4106 1 +a 2714 2715 1392 +a 2714 4106 1 +a 2715 2716 1391 +a 2715 4106 1 +a 2716 2717 1390 +a 2716 4106 1 +a 2717 2718 1389 +a 2717 4106 1 +a 2718 2719 1388 +a 2718 4106 1 +a 2719 2720 1387 +a 2719 4106 1 +a 2720 2721 1386 +a 2720 4106 1 +a 2721 2722 1385 +a 2721 4106 1 +a 2722 2723 1384 +a 2722 4106 1 +a 2723 2724 1383 +a 2723 4106 1 +a 2724 2725 1382 +a 2724 4106 1 +a 2725 2726 1381 +a 2725 4106 1 +a 2726 2727 1380 +a 2726 4106 1 +a 2727 2728 1379 +a 2727 4106 1 +a 2728 2729 1378 +a 2728 4106 1 +a 2729 2730 1377 +a 2729 4106 1 +a 2730 2731 1376 +a 2730 4106 1 +a 2731 2732 1375 +a 2731 4106 1 +a 2732 2733 1374 +a 2732 4106 1 +a 2733 2734 1373 +a 2733 4106 1 +a 2734 2735 1372 +a 2734 4106 1 +a 2735 2736 1371 +a 2735 4106 1 +a 2736 2737 1370 +a 2736 4106 1 +a 2737 2738 1369 +a 2737 4106 1 +a 2738 2739 1368 +a 2738 4106 1 +a 2739 2740 1367 +a 2739 4106 1 +a 2740 2741 1366 +a 2740 4106 1 +a 2741 2742 1365 +a 2741 4106 1 +a 2742 2743 1364 +a 2742 4106 1 +a 2743 2744 1363 +a 2743 4106 1 +a 2744 2745 1362 +a 2744 4106 1 +a 2745 2746 1361 +a 2745 4106 1 +a 2746 2747 1360 +a 2746 4106 1 +a 2747 2748 1359 +a 2747 4106 1 +a 2748 2749 1358 +a 2748 4106 1 +a 2749 2750 1357 +a 2749 4106 1 +a 2750 2751 1356 +a 2750 4106 1 +a 2751 2752 1355 +a 2751 4106 1 +a 2752 2753 1354 +a 2752 4106 1 +a 2753 2754 1353 +a 2753 4106 1 +a 2754 2755 1352 +a 2754 4106 1 +a 2755 2756 1351 +a 2755 4106 1 +a 2756 2757 1350 +a 2756 4106 1 +a 2757 2758 1349 +a 2757 4106 1 +a 2758 2759 1348 +a 2758 4106 1 +a 2759 2760 1347 +a 2759 4106 1 +a 2760 2761 1346 +a 2760 4106 1 +a 2761 2762 1345 +a 2761 4106 1 +a 2762 2763 1344 +a 2762 4106 1 +a 2763 2764 1343 +a 2763 4106 1 +a 2764 2765 1342 +a 2764 4106 1 +a 2765 2766 1341 +a 2765 4106 1 +a 2766 2767 1340 +a 2766 4106 1 +a 2767 2768 1339 +a 2767 4106 1 +a 2768 2769 1338 +a 2768 4106 1 +a 2769 2770 1337 +a 2769 4106 1 +a 2770 2771 1336 +a 2770 4106 1 +a 2771 2772 1335 +a 2771 4106 1 +a 2772 2773 1334 +a 2772 4106 1 +a 2773 2774 1333 +a 2773 4106 1 +a 2774 2775 1332 +a 2774 4106 1 +a 2775 2776 1331 +a 2775 4106 1 +a 2776 2777 1330 +a 2776 4106 1 +a 2777 2778 1329 +a 2777 4106 1 +a 2778 2779 1328 +a 2778 4106 1 +a 2779 2780 1327 +a 2779 4106 1 +a 2780 2781 1326 +a 2780 4106 1 +a 2781 2782 1325 +a 2781 4106 1 +a 2782 2783 1324 +a 2782 4106 1 +a 2783 2784 1323 +a 2783 4106 1 +a 2784 2785 1322 +a 2784 4106 1 +a 2785 2786 1321 +a 2785 4106 1 +a 2786 2787 1320 +a 2786 4106 1 +a 2787 2788 1319 +a 2787 4106 1 +a 2788 2789 1318 +a 2788 4106 1 +a 2789 2790 1317 +a 2789 4106 1 +a 2790 2791 1316 +a 2790 4106 1 +a 2791 2792 1315 +a 2791 4106 1 +a 2792 2793 1314 +a 2792 4106 1 +a 2793 2794 1313 +a 2793 4106 1 +a 2794 2795 1312 +a 2794 4106 1 +a 2795 2796 1311 +a 2795 4106 1 +a 2796 2797 1310 +a 2796 4106 1 +a 2797 2798 1309 +a 2797 4106 1 +a 2798 2799 1308 +a 2798 4106 1 +a 2799 2800 1307 +a 2799 4106 1 +a 2800 2801 1306 +a 2800 4106 1 +a 2801 2802 1305 +a 2801 4106 1 +a 2802 2803 1304 +a 2802 4106 1 +a 2803 2804 1303 +a 2803 4106 1 +a 2804 2805 1302 +a 2804 4106 1 +a 2805 2806 1301 +a 2805 4106 1 +a 2806 2807 1300 +a 2806 4106 1 +a 2807 2808 1299 +a 2807 4106 1 +a 2808 2809 1298 +a 2808 4106 1 +a 2809 2810 1297 +a 2809 4106 1 +a 2810 2811 1296 +a 2810 4106 1 +a 2811 2812 1295 +a 2811 4106 1 +a 2812 2813 1294 +a 2812 4106 1 +a 2813 2814 1293 +a 2813 4106 1 +a 2814 2815 1292 +a 2814 4106 1 +a 2815 2816 1291 +a 2815 4106 1 +a 2816 2817 1290 +a 2816 4106 1 +a 2817 2818 1289 +a 2817 4106 1 +a 2818 2819 1288 +a 2818 4106 1 +a 2819 2820 1287 +a 2819 4106 1 +a 2820 2821 1286 +a 2820 4106 1 +a 2821 2822 1285 +a 2821 4106 1 +a 2822 2823 1284 +a 2822 4106 1 +a 2823 2824 1283 +a 2823 4106 1 +a 2824 2825 1282 +a 2824 4106 1 +a 2825 2826 1281 +a 2825 4106 1 +a 2826 2827 1280 +a 2826 4106 1 +a 2827 2828 1279 +a 2827 4106 1 +a 2828 2829 1278 +a 2828 4106 1 +a 2829 2830 1277 +a 2829 4106 1 +a 2830 2831 1276 +a 2830 4106 1 +a 2831 2832 1275 +a 2831 4106 1 +a 2832 2833 1274 +a 2832 4106 1 +a 2833 2834 1273 +a 2833 4106 1 +a 2834 2835 1272 +a 2834 4106 1 +a 2835 2836 1271 +a 2835 4106 1 +a 2836 2837 1270 +a 2836 4106 1 +a 2837 2838 1269 +a 2837 4106 1 +a 2838 2839 1268 +a 2838 4106 1 +a 2839 2840 1267 +a 2839 4106 1 +a 2840 2841 1266 +a 2840 4106 1 +a 2841 2842 1265 +a 2841 4106 1 +a 2842 2843 1264 +a 2842 4106 1 +a 2843 2844 1263 +a 2843 4106 1 +a 2844 2845 1262 +a 2844 4106 1 +a 2845 2846 1261 +a 2845 4106 1 +a 2846 2847 1260 +a 2846 4106 1 +a 2847 2848 1259 +a 2847 4106 1 +a 2848 2849 1258 +a 2848 4106 1 +a 2849 2850 1257 +a 2849 4106 1 +a 2850 2851 1256 +a 2850 4106 1 +a 2851 2852 1255 +a 2851 4106 1 +a 2852 2853 1254 +a 2852 4106 1 +a 2853 2854 1253 +a 2853 4106 1 +a 2854 2855 1252 +a 2854 4106 1 +a 2855 2856 1251 +a 2855 4106 1 +a 2856 2857 1250 +a 2856 4106 1 +a 2857 2858 1249 +a 2857 4106 1 +a 2858 2859 1248 +a 2858 4106 1 +a 2859 2860 1247 +a 2859 4106 1 +a 2860 2861 1246 +a 2860 4106 1 +a 2861 2862 1245 +a 2861 4106 1 +a 2862 2863 1244 +a 2862 4106 1 +a 2863 2864 1243 +a 2863 4106 1 +a 2864 2865 1242 +a 2864 4106 1 +a 2865 2866 1241 +a 2865 4106 1 +a 2866 2867 1240 +a 2866 4106 1 +a 2867 2868 1239 +a 2867 4106 1 +a 2868 2869 1238 +a 2868 4106 1 +a 2869 2870 1237 +a 2869 4106 1 +a 2870 2871 1236 +a 2870 4106 1 +a 2871 2872 1235 +a 2871 4106 1 +a 2872 2873 1234 +a 2872 4106 1 +a 2873 2874 1233 +a 2873 4106 1 +a 2874 2875 1232 +a 2874 4106 1 +a 2875 2876 1231 +a 2875 4106 1 +a 2876 2877 1230 +a 2876 4106 1 +a 2877 2878 1229 +a 2877 4106 1 +a 2878 2879 1228 +a 2878 4106 1 +a 2879 2880 1227 +a 2879 4106 1 +a 2880 2881 1226 +a 2880 4106 1 +a 2881 2882 1225 +a 2881 4106 1 +a 2882 2883 1224 +a 2882 4106 1 +a 2883 2884 1223 +a 2883 4106 1 +a 2884 2885 1222 +a 2884 4106 1 +a 2885 2886 1221 +a 2885 4106 1 +a 2886 2887 1220 +a 2886 4106 1 +a 2887 2888 1219 +a 2887 4106 1 +a 2888 2889 1218 +a 2888 4106 1 +a 2889 2890 1217 +a 2889 4106 1 +a 2890 2891 1216 +a 2890 4106 1 +a 2891 2892 1215 +a 2891 4106 1 +a 2892 2893 1214 +a 2892 4106 1 +a 2893 2894 1213 +a 2893 4106 1 +a 2894 2895 1212 +a 2894 4106 1 +a 2895 2896 1211 +a 2895 4106 1 +a 2896 2897 1210 +a 2896 4106 1 +a 2897 2898 1209 +a 2897 4106 1 +a 2898 2899 1208 +a 2898 4106 1 +a 2899 2900 1207 +a 2899 4106 1 +a 2900 2901 1206 +a 2900 4106 1 +a 2901 2902 1205 +a 2901 4106 1 +a 2902 2903 1204 +a 2902 4106 1 +a 2903 2904 1203 +a 2903 4106 1 +a 2904 2905 1202 +a 2904 4106 1 +a 2905 2906 1201 +a 2905 4106 1 +a 2906 2907 1200 +a 2906 4106 1 +a 2907 2908 1199 +a 2907 4106 1 +a 2908 2909 1198 +a 2908 4106 1 +a 2909 2910 1197 +a 2909 4106 1 +a 2910 2911 1196 +a 2910 4106 1 +a 2911 2912 1195 +a 2911 4106 1 +a 2912 2913 1194 +a 2912 4106 1 +a 2913 2914 1193 +a 2913 4106 1 +a 2914 2915 1192 +a 2914 4106 1 +a 2915 2916 1191 +a 2915 4106 1 +a 2916 2917 1190 +a 2916 4106 1 +a 2917 2918 1189 +a 2917 4106 1 +a 2918 2919 1188 +a 2918 4106 1 +a 2919 2920 1187 +a 2919 4106 1 +a 2920 2921 1186 +a 2920 4106 1 +a 2921 2922 1185 +a 2921 4106 1 +a 2922 2923 1184 +a 2922 4106 1 +a 2923 2924 1183 +a 2923 4106 1 +a 2924 2925 1182 +a 2924 4106 1 +a 2925 2926 1181 +a 2925 4106 1 +a 2926 2927 1180 +a 2926 4106 1 +a 2927 2928 1179 +a 2927 4106 1 +a 2928 2929 1178 +a 2928 4106 1 +a 2929 2930 1177 +a 2929 4106 1 +a 2930 2931 1176 +a 2930 4106 1 +a 2931 2932 1175 +a 2931 4106 1 +a 2932 2933 1174 +a 2932 4106 1 +a 2933 2934 1173 +a 2933 4106 1 +a 2934 2935 1172 +a 2934 4106 1 +a 2935 2936 1171 +a 2935 4106 1 +a 2936 2937 1170 +a 2936 4106 1 +a 2937 2938 1169 +a 2937 4106 1 +a 2938 2939 1168 +a 2938 4106 1 +a 2939 2940 1167 +a 2939 4106 1 +a 2940 2941 1166 +a 2940 4106 1 +a 2941 2942 1165 +a 2941 4106 1 +a 2942 2943 1164 +a 2942 4106 1 +a 2943 2944 1163 +a 2943 4106 1 +a 2944 2945 1162 +a 2944 4106 1 +a 2945 2946 1161 +a 2945 4106 1 +a 2946 2947 1160 +a 2946 4106 1 +a 2947 2948 1159 +a 2947 4106 1 +a 2948 2949 1158 +a 2948 4106 1 +a 2949 2950 1157 +a 2949 4106 1 +a 2950 2951 1156 +a 2950 4106 1 +a 2951 2952 1155 +a 2951 4106 1 +a 2952 2953 1154 +a 2952 4106 1 +a 2953 2954 1153 +a 2953 4106 1 +a 2954 2955 1152 +a 2954 4106 1 +a 2955 2956 1151 +a 2955 4106 1 +a 2956 2957 1150 +a 2956 4106 1 +a 2957 2958 1149 +a 2957 4106 1 +a 2958 2959 1148 +a 2958 4106 1 +a 2959 2960 1147 +a 2959 4106 1 +a 2960 2961 1146 +a 2960 4106 1 +a 2961 2962 1145 +a 2961 4106 1 +a 2962 2963 1144 +a 2962 4106 1 +a 2963 2964 1143 +a 2963 4106 1 +a 2964 2965 1142 +a 2964 4106 1 +a 2965 2966 1141 +a 2965 4106 1 +a 2966 2967 1140 +a 2966 4106 1 +a 2967 2968 1139 +a 2967 4106 1 +a 2968 2969 1138 +a 2968 4106 1 +a 2969 2970 1137 +a 2969 4106 1 +a 2970 2971 1136 +a 2970 4106 1 +a 2971 2972 1135 +a 2971 4106 1 +a 2972 2973 1134 +a 2972 4106 1 +a 2973 2974 1133 +a 2973 4106 1 +a 2974 2975 1132 +a 2974 4106 1 +a 2975 2976 1131 +a 2975 4106 1 +a 2976 2977 1130 +a 2976 4106 1 +a 2977 2978 1129 +a 2977 4106 1 +a 2978 2979 1128 +a 2978 4106 1 +a 2979 2980 1127 +a 2979 4106 1 +a 2980 2981 1126 +a 2980 4106 1 +a 2981 2982 1125 +a 2981 4106 1 +a 2982 2983 1124 +a 2982 4106 1 +a 2983 2984 1123 +a 2983 4106 1 +a 2984 2985 1122 +a 2984 4106 1 +a 2985 2986 1121 +a 2985 4106 1 +a 2986 2987 1120 +a 2986 4106 1 +a 2987 2988 1119 +a 2987 4106 1 +a 2988 2989 1118 +a 2988 4106 1 +a 2989 2990 1117 +a 2989 4106 1 +a 2990 2991 1116 +a 2990 4106 1 +a 2991 2992 1115 +a 2991 4106 1 +a 2992 2993 1114 +a 2992 4106 1 +a 2993 2994 1113 +a 2993 4106 1 +a 2994 2995 1112 +a 2994 4106 1 +a 2995 2996 1111 +a 2995 4106 1 +a 2996 2997 1110 +a 2996 4106 1 +a 2997 2998 1109 +a 2997 4106 1 +a 2998 2999 1108 +a 2998 4106 1 +a 2999 3000 1107 +a 2999 4106 1 +a 3000 3001 1106 +a 3000 4106 1 +a 3001 3002 1105 +a 3001 4106 1 +a 3002 3003 1104 +a 3002 4106 1 +a 3003 3004 1103 +a 3003 4106 1 +a 3004 3005 1102 +a 3004 4106 1 +a 3005 3006 1101 +a 3005 4106 1 +a 3006 3007 1100 +a 3006 4106 1 +a 3007 3008 1099 +a 3007 4106 1 +a 3008 3009 1098 +a 3008 4106 1 +a 3009 3010 1097 +a 3009 4106 1 +a 3010 3011 1096 +a 3010 4106 1 +a 3011 3012 1095 +a 3011 4106 1 +a 3012 3013 1094 +a 3012 4106 1 +a 3013 3014 1093 +a 3013 4106 1 +a 3014 3015 1092 +a 3014 4106 1 +a 3015 3016 1091 +a 3015 4106 1 +a 3016 3017 1090 +a 3016 4106 1 +a 3017 3018 1089 +a 3017 4106 1 +a 3018 3019 1088 +a 3018 4106 1 +a 3019 3020 1087 +a 3019 4106 1 +a 3020 3021 1086 +a 3020 4106 1 +a 3021 3022 1085 +a 3021 4106 1 +a 3022 3023 1084 +a 3022 4106 1 +a 3023 3024 1083 +a 3023 4106 1 +a 3024 3025 1082 +a 3024 4106 1 +a 3025 3026 1081 +a 3025 4106 1 +a 3026 3027 1080 +a 3026 4106 1 +a 3027 3028 1079 +a 3027 4106 1 +a 3028 3029 1078 +a 3028 4106 1 +a 3029 3030 1077 +a 3029 4106 1 +a 3030 3031 1076 +a 3030 4106 1 +a 3031 3032 1075 +a 3031 4106 1 +a 3032 3033 1074 +a 3032 4106 1 +a 3033 3034 1073 +a 3033 4106 1 +a 3034 3035 1072 +a 3034 4106 1 +a 3035 3036 1071 +a 3035 4106 1 +a 3036 3037 1070 +a 3036 4106 1 +a 3037 3038 1069 +a 3037 4106 1 +a 3038 3039 1068 +a 3038 4106 1 +a 3039 3040 1067 +a 3039 4106 1 +a 3040 3041 1066 +a 3040 4106 1 +a 3041 3042 1065 +a 3041 4106 1 +a 3042 3043 1064 +a 3042 4106 1 +a 3043 3044 1063 +a 3043 4106 1 +a 3044 3045 1062 +a 3044 4106 1 +a 3045 3046 1061 +a 3045 4106 1 +a 3046 3047 1060 +a 3046 4106 1 +a 3047 3048 1059 +a 3047 4106 1 +a 3048 3049 1058 +a 3048 4106 1 +a 3049 3050 1057 +a 3049 4106 1 +a 3050 3051 1056 +a 3050 4106 1 +a 3051 3052 1055 +a 3051 4106 1 +a 3052 3053 1054 +a 3052 4106 1 +a 3053 3054 1053 +a 3053 4106 1 +a 3054 3055 1052 +a 3054 4106 1 +a 3055 3056 1051 +a 3055 4106 1 +a 3056 3057 1050 +a 3056 4106 1 +a 3057 3058 1049 +a 3057 4106 1 +a 3058 3059 1048 +a 3058 4106 1 +a 3059 3060 1047 +a 3059 4106 1 +a 3060 3061 1046 +a 3060 4106 1 +a 3061 3062 1045 +a 3061 4106 1 +a 3062 3063 1044 +a 3062 4106 1 +a 3063 3064 1043 +a 3063 4106 1 +a 3064 3065 1042 +a 3064 4106 1 +a 3065 3066 1041 +a 3065 4106 1 +a 3066 3067 1040 +a 3066 4106 1 +a 3067 3068 1039 +a 3067 4106 1 +a 3068 3069 1038 +a 3068 4106 1 +a 3069 3070 1037 +a 3069 4106 1 +a 3070 3071 1036 +a 3070 4106 1 +a 3071 3072 1035 +a 3071 4106 1 +a 3072 3073 1034 +a 3072 4106 1 +a 3073 3074 1033 +a 3073 4106 1 +a 3074 3075 1032 +a 3074 4106 1 +a 3075 3076 1031 +a 3075 4106 1 +a 3076 3077 1030 +a 3076 4106 1 +a 3077 3078 1029 +a 3077 4106 1 +a 3078 3079 1028 +a 3078 4106 1 +a 3079 3080 1027 +a 3079 4106 1 +a 3080 3081 1026 +a 3080 4106 1 +a 3081 3082 1025 +a 3081 4106 1 +a 3082 3083 1024 +a 3082 4106 1 +a 3083 3084 1023 +a 3083 4106 1 +a 3084 3085 1022 +a 3084 4106 1 +a 3085 3086 1021 +a 3085 4106 1 +a 3086 3087 1020 +a 3086 4106 1 +a 3087 3088 1019 +a 3087 4106 1 +a 3088 3089 1018 +a 3088 4106 1 +a 3089 3090 1017 +a 3089 4106 1 +a 3090 3091 1016 +a 3090 4106 1 +a 3091 3092 1015 +a 3091 4106 1 +a 3092 3093 1014 +a 3092 4106 1 +a 3093 3094 1013 +a 3093 4106 1 +a 3094 3095 1012 +a 3094 4106 1 +a 3095 3096 1011 +a 3095 4106 1 +a 3096 3097 1010 +a 3096 4106 1 +a 3097 3098 1009 +a 3097 4106 1 +a 3098 3099 1008 +a 3098 4106 1 +a 3099 3100 1007 +a 3099 4106 1 +a 3100 3101 1006 +a 3100 4106 1 +a 3101 3102 1005 +a 3101 4106 1 +a 3102 3103 1004 +a 3102 4106 1 +a 3103 3104 1003 +a 3103 4106 1 +a 3104 3105 1002 +a 3104 4106 1 +a 3105 3106 1001 +a 3105 4106 1 +a 3106 3107 1000 +a 3106 4106 1 +a 3107 3108 999 +a 3107 4106 1 +a 3108 3109 998 +a 3108 4106 1 +a 3109 3110 997 +a 3109 4106 1 +a 3110 3111 996 +a 3110 4106 1 +a 3111 3112 995 +a 3111 4106 1 +a 3112 3113 994 +a 3112 4106 1 +a 3113 3114 993 +a 3113 4106 1 +a 3114 3115 992 +a 3114 4106 1 +a 3115 3116 991 +a 3115 4106 1 +a 3116 3117 990 +a 3116 4106 1 +a 3117 3118 989 +a 3117 4106 1 +a 3118 3119 988 +a 3118 4106 1 +a 3119 3120 987 +a 3119 4106 1 +a 3120 3121 986 +a 3120 4106 1 +a 3121 3122 985 +a 3121 4106 1 +a 3122 3123 984 +a 3122 4106 1 +a 3123 3124 983 +a 3123 4106 1 +a 3124 3125 982 +a 3124 4106 1 +a 3125 3126 981 +a 3125 4106 1 +a 3126 3127 980 +a 3126 4106 1 +a 3127 3128 979 +a 3127 4106 1 +a 3128 3129 978 +a 3128 4106 1 +a 3129 3130 977 +a 3129 4106 1 +a 3130 3131 976 +a 3130 4106 1 +a 3131 3132 975 +a 3131 4106 1 +a 3132 3133 974 +a 3132 4106 1 +a 3133 3134 973 +a 3133 4106 1 +a 3134 3135 972 +a 3134 4106 1 +a 3135 3136 971 +a 3135 4106 1 +a 3136 3137 970 +a 3136 4106 1 +a 3137 3138 969 +a 3137 4106 1 +a 3138 3139 968 +a 3138 4106 1 +a 3139 3140 967 +a 3139 4106 1 +a 3140 3141 966 +a 3140 4106 1 +a 3141 3142 965 +a 3141 4106 1 +a 3142 3143 964 +a 3142 4106 1 +a 3143 3144 963 +a 3143 4106 1 +a 3144 3145 962 +a 3144 4106 1 +a 3145 3146 961 +a 3145 4106 1 +a 3146 3147 960 +a 3146 4106 1 +a 3147 3148 959 +a 3147 4106 1 +a 3148 3149 958 +a 3148 4106 1 +a 3149 3150 957 +a 3149 4106 1 +a 3150 3151 956 +a 3150 4106 1 +a 3151 3152 955 +a 3151 4106 1 +a 3152 3153 954 +a 3152 4106 1 +a 3153 3154 953 +a 3153 4106 1 +a 3154 3155 952 +a 3154 4106 1 +a 3155 3156 951 +a 3155 4106 1 +a 3156 3157 950 +a 3156 4106 1 +a 3157 3158 949 +a 3157 4106 1 +a 3158 3159 948 +a 3158 4106 1 +a 3159 3160 947 +a 3159 4106 1 +a 3160 3161 946 +a 3160 4106 1 +a 3161 3162 945 +a 3161 4106 1 +a 3162 3163 944 +a 3162 4106 1 +a 3163 3164 943 +a 3163 4106 1 +a 3164 3165 942 +a 3164 4106 1 +a 3165 3166 941 +a 3165 4106 1 +a 3166 3167 940 +a 3166 4106 1 +a 3167 3168 939 +a 3167 4106 1 +a 3168 3169 938 +a 3168 4106 1 +a 3169 3170 937 +a 3169 4106 1 +a 3170 3171 936 +a 3170 4106 1 +a 3171 3172 935 +a 3171 4106 1 +a 3172 3173 934 +a 3172 4106 1 +a 3173 3174 933 +a 3173 4106 1 +a 3174 3175 932 +a 3174 4106 1 +a 3175 3176 931 +a 3175 4106 1 +a 3176 3177 930 +a 3176 4106 1 +a 3177 3178 929 +a 3177 4106 1 +a 3178 3179 928 +a 3178 4106 1 +a 3179 3180 927 +a 3179 4106 1 +a 3180 3181 926 +a 3180 4106 1 +a 3181 3182 925 +a 3181 4106 1 +a 3182 3183 924 +a 3182 4106 1 +a 3183 3184 923 +a 3183 4106 1 +a 3184 3185 922 +a 3184 4106 1 +a 3185 3186 921 +a 3185 4106 1 +a 3186 3187 920 +a 3186 4106 1 +a 3187 3188 919 +a 3187 4106 1 +a 3188 3189 918 +a 3188 4106 1 +a 3189 3190 917 +a 3189 4106 1 +a 3190 3191 916 +a 3190 4106 1 +a 3191 3192 915 +a 3191 4106 1 +a 3192 3193 914 +a 3192 4106 1 +a 3193 3194 913 +a 3193 4106 1 +a 3194 3195 912 +a 3194 4106 1 +a 3195 3196 911 +a 3195 4106 1 +a 3196 3197 910 +a 3196 4106 1 +a 3197 3198 909 +a 3197 4106 1 +a 3198 3199 908 +a 3198 4106 1 +a 3199 3200 907 +a 3199 4106 1 +a 3200 3201 906 +a 3200 4106 1 +a 3201 3202 905 +a 3201 4106 1 +a 3202 3203 904 +a 3202 4106 1 +a 3203 3204 903 +a 3203 4106 1 +a 3204 3205 902 +a 3204 4106 1 +a 3205 3206 901 +a 3205 4106 1 +a 3206 3207 900 +a 3206 4106 1 +a 3207 3208 899 +a 3207 4106 1 +a 3208 3209 898 +a 3208 4106 1 +a 3209 3210 897 +a 3209 4106 1 +a 3210 3211 896 +a 3210 4106 1 +a 3211 3212 895 +a 3211 4106 1 +a 3212 3213 894 +a 3212 4106 1 +a 3213 3214 893 +a 3213 4106 1 +a 3214 3215 892 +a 3214 4106 1 +a 3215 3216 891 +a 3215 4106 1 +a 3216 3217 890 +a 3216 4106 1 +a 3217 3218 889 +a 3217 4106 1 +a 3218 3219 888 +a 3218 4106 1 +a 3219 3220 887 +a 3219 4106 1 +a 3220 3221 886 +a 3220 4106 1 +a 3221 3222 885 +a 3221 4106 1 +a 3222 3223 884 +a 3222 4106 1 +a 3223 3224 883 +a 3223 4106 1 +a 3224 3225 882 +a 3224 4106 1 +a 3225 3226 881 +a 3225 4106 1 +a 3226 3227 880 +a 3226 4106 1 +a 3227 3228 879 +a 3227 4106 1 +a 3228 3229 878 +a 3228 4106 1 +a 3229 3230 877 +a 3229 4106 1 +a 3230 3231 876 +a 3230 4106 1 +a 3231 3232 875 +a 3231 4106 1 +a 3232 3233 874 +a 3232 4106 1 +a 3233 3234 873 +a 3233 4106 1 +a 3234 3235 872 +a 3234 4106 1 +a 3235 3236 871 +a 3235 4106 1 +a 3236 3237 870 +a 3236 4106 1 +a 3237 3238 869 +a 3237 4106 1 +a 3238 3239 868 +a 3238 4106 1 +a 3239 3240 867 +a 3239 4106 1 +a 3240 3241 866 +a 3240 4106 1 +a 3241 3242 865 +a 3241 4106 1 +a 3242 3243 864 +a 3242 4106 1 +a 3243 3244 863 +a 3243 4106 1 +a 3244 3245 862 +a 3244 4106 1 +a 3245 3246 861 +a 3245 4106 1 +a 3246 3247 860 +a 3246 4106 1 +a 3247 3248 859 +a 3247 4106 1 +a 3248 3249 858 +a 3248 4106 1 +a 3249 3250 857 +a 3249 4106 1 +a 3250 3251 856 +a 3250 4106 1 +a 3251 3252 855 +a 3251 4106 1 +a 3252 3253 854 +a 3252 4106 1 +a 3253 3254 853 +a 3253 4106 1 +a 3254 3255 852 +a 3254 4106 1 +a 3255 3256 851 +a 3255 4106 1 +a 3256 3257 850 +a 3256 4106 1 +a 3257 3258 849 +a 3257 4106 1 +a 3258 3259 848 +a 3258 4106 1 +a 3259 3260 847 +a 3259 4106 1 +a 3260 3261 846 +a 3260 4106 1 +a 3261 3262 845 +a 3261 4106 1 +a 3262 3263 844 +a 3262 4106 1 +a 3263 3264 843 +a 3263 4106 1 +a 3264 3265 842 +a 3264 4106 1 +a 3265 3266 841 +a 3265 4106 1 +a 3266 3267 840 +a 3266 4106 1 +a 3267 3268 839 +a 3267 4106 1 +a 3268 3269 838 +a 3268 4106 1 +a 3269 3270 837 +a 3269 4106 1 +a 3270 3271 836 +a 3270 4106 1 +a 3271 3272 835 +a 3271 4106 1 +a 3272 3273 834 +a 3272 4106 1 +a 3273 3274 833 +a 3273 4106 1 +a 3274 3275 832 +a 3274 4106 1 +a 3275 3276 831 +a 3275 4106 1 +a 3276 3277 830 +a 3276 4106 1 +a 3277 3278 829 +a 3277 4106 1 +a 3278 3279 828 +a 3278 4106 1 +a 3279 3280 827 +a 3279 4106 1 +a 3280 3281 826 +a 3280 4106 1 +a 3281 3282 825 +a 3281 4106 1 +a 3282 3283 824 +a 3282 4106 1 +a 3283 3284 823 +a 3283 4106 1 +a 3284 3285 822 +a 3284 4106 1 +a 3285 3286 821 +a 3285 4106 1 +a 3286 3287 820 +a 3286 4106 1 +a 3287 3288 819 +a 3287 4106 1 +a 3288 3289 818 +a 3288 4106 1 +a 3289 3290 817 +a 3289 4106 1 +a 3290 3291 816 +a 3290 4106 1 +a 3291 3292 815 +a 3291 4106 1 +a 3292 3293 814 +a 3292 4106 1 +a 3293 3294 813 +a 3293 4106 1 +a 3294 3295 812 +a 3294 4106 1 +a 3295 3296 811 +a 3295 4106 1 +a 3296 3297 810 +a 3296 4106 1 +a 3297 3298 809 +a 3297 4106 1 +a 3298 3299 808 +a 3298 4106 1 +a 3299 3300 807 +a 3299 4106 1 +a 3300 3301 806 +a 3300 4106 1 +a 3301 3302 805 +a 3301 4106 1 +a 3302 3303 804 +a 3302 4106 1 +a 3303 3304 803 +a 3303 4106 1 +a 3304 3305 802 +a 3304 4106 1 +a 3305 3306 801 +a 3305 4106 1 +a 3306 3307 800 +a 3306 4106 1 +a 3307 3308 799 +a 3307 4106 1 +a 3308 3309 798 +a 3308 4106 1 +a 3309 3310 797 +a 3309 4106 1 +a 3310 3311 796 +a 3310 4106 1 +a 3311 3312 795 +a 3311 4106 1 +a 3312 3313 794 +a 3312 4106 1 +a 3313 3314 793 +a 3313 4106 1 +a 3314 3315 792 +a 3314 4106 1 +a 3315 3316 791 +a 3315 4106 1 +a 3316 3317 790 +a 3316 4106 1 +a 3317 3318 789 +a 3317 4106 1 +a 3318 3319 788 +a 3318 4106 1 +a 3319 3320 787 +a 3319 4106 1 +a 3320 3321 786 +a 3320 4106 1 +a 3321 3322 785 +a 3321 4106 1 +a 3322 3323 784 +a 3322 4106 1 +a 3323 3324 783 +a 3323 4106 1 +a 3324 3325 782 +a 3324 4106 1 +a 3325 3326 781 +a 3325 4106 1 +a 3326 3327 780 +a 3326 4106 1 +a 3327 3328 779 +a 3327 4106 1 +a 3328 3329 778 +a 3328 4106 1 +a 3329 3330 777 +a 3329 4106 1 +a 3330 3331 776 +a 3330 4106 1 +a 3331 3332 775 +a 3331 4106 1 +a 3332 3333 774 +a 3332 4106 1 +a 3333 3334 773 +a 3333 4106 1 +a 3334 3335 772 +a 3334 4106 1 +a 3335 3336 771 +a 3335 4106 1 +a 3336 3337 770 +a 3336 4106 1 +a 3337 3338 769 +a 3337 4106 1 +a 3338 3339 768 +a 3338 4106 1 +a 3339 3340 767 +a 3339 4106 1 +a 3340 3341 766 +a 3340 4106 1 +a 3341 3342 765 +a 3341 4106 1 +a 3342 3343 764 +a 3342 4106 1 +a 3343 3344 763 +a 3343 4106 1 +a 3344 3345 762 +a 3344 4106 1 +a 3345 3346 761 +a 3345 4106 1 +a 3346 3347 760 +a 3346 4106 1 +a 3347 3348 759 +a 3347 4106 1 +a 3348 3349 758 +a 3348 4106 1 +a 3349 3350 757 +a 3349 4106 1 +a 3350 3351 756 +a 3350 4106 1 +a 3351 3352 755 +a 3351 4106 1 +a 3352 3353 754 +a 3352 4106 1 +a 3353 3354 753 +a 3353 4106 1 +a 3354 3355 752 +a 3354 4106 1 +a 3355 3356 751 +a 3355 4106 1 +a 3356 3357 750 +a 3356 4106 1 +a 3357 3358 749 +a 3357 4106 1 +a 3358 3359 748 +a 3358 4106 1 +a 3359 3360 747 +a 3359 4106 1 +a 3360 3361 746 +a 3360 4106 1 +a 3361 3362 745 +a 3361 4106 1 +a 3362 3363 744 +a 3362 4106 1 +a 3363 3364 743 +a 3363 4106 1 +a 3364 3365 742 +a 3364 4106 1 +a 3365 3366 741 +a 3365 4106 1 +a 3366 3367 740 +a 3366 4106 1 +a 3367 3368 739 +a 3367 4106 1 +a 3368 3369 738 +a 3368 4106 1 +a 3369 3370 737 +a 3369 4106 1 +a 3370 3371 736 +a 3370 4106 1 +a 3371 3372 735 +a 3371 4106 1 +a 3372 3373 734 +a 3372 4106 1 +a 3373 3374 733 +a 3373 4106 1 +a 3374 3375 732 +a 3374 4106 1 +a 3375 3376 731 +a 3375 4106 1 +a 3376 3377 730 +a 3376 4106 1 +a 3377 3378 729 +a 3377 4106 1 +a 3378 3379 728 +a 3378 4106 1 +a 3379 3380 727 +a 3379 4106 1 +a 3380 3381 726 +a 3380 4106 1 +a 3381 3382 725 +a 3381 4106 1 +a 3382 3383 724 +a 3382 4106 1 +a 3383 3384 723 +a 3383 4106 1 +a 3384 3385 722 +a 3384 4106 1 +a 3385 3386 721 +a 3385 4106 1 +a 3386 3387 720 +a 3386 4106 1 +a 3387 3388 719 +a 3387 4106 1 +a 3388 3389 718 +a 3388 4106 1 +a 3389 3390 717 +a 3389 4106 1 +a 3390 3391 716 +a 3390 4106 1 +a 3391 3392 715 +a 3391 4106 1 +a 3392 3393 714 +a 3392 4106 1 +a 3393 3394 713 +a 3393 4106 1 +a 3394 3395 712 +a 3394 4106 1 +a 3395 3396 711 +a 3395 4106 1 +a 3396 3397 710 +a 3396 4106 1 +a 3397 3398 709 +a 3397 4106 1 +a 3398 3399 708 +a 3398 4106 1 +a 3399 3400 707 +a 3399 4106 1 +a 3400 3401 706 +a 3400 4106 1 +a 3401 3402 705 +a 3401 4106 1 +a 3402 3403 704 +a 3402 4106 1 +a 3403 3404 703 +a 3403 4106 1 +a 3404 3405 702 +a 3404 4106 1 +a 3405 3406 701 +a 3405 4106 1 +a 3406 3407 700 +a 3406 4106 1 +a 3407 3408 699 +a 3407 4106 1 +a 3408 3409 698 +a 3408 4106 1 +a 3409 3410 697 +a 3409 4106 1 +a 3410 3411 696 +a 3410 4106 1 +a 3411 3412 695 +a 3411 4106 1 +a 3412 3413 694 +a 3412 4106 1 +a 3413 3414 693 +a 3413 4106 1 +a 3414 3415 692 +a 3414 4106 1 +a 3415 3416 691 +a 3415 4106 1 +a 3416 3417 690 +a 3416 4106 1 +a 3417 3418 689 +a 3417 4106 1 +a 3418 3419 688 +a 3418 4106 1 +a 3419 3420 687 +a 3419 4106 1 +a 3420 3421 686 +a 3420 4106 1 +a 3421 3422 685 +a 3421 4106 1 +a 3422 3423 684 +a 3422 4106 1 +a 3423 3424 683 +a 3423 4106 1 +a 3424 3425 682 +a 3424 4106 1 +a 3425 3426 681 +a 3425 4106 1 +a 3426 3427 680 +a 3426 4106 1 +a 3427 3428 679 +a 3427 4106 1 +a 3428 3429 678 +a 3428 4106 1 +a 3429 3430 677 +a 3429 4106 1 +a 3430 3431 676 +a 3430 4106 1 +a 3431 3432 675 +a 3431 4106 1 +a 3432 3433 674 +a 3432 4106 1 +a 3433 3434 673 +a 3433 4106 1 +a 3434 3435 672 +a 3434 4106 1 +a 3435 3436 671 +a 3435 4106 1 +a 3436 3437 670 +a 3436 4106 1 +a 3437 3438 669 +a 3437 4106 1 +a 3438 3439 668 +a 3438 4106 1 +a 3439 3440 667 +a 3439 4106 1 +a 3440 3441 666 +a 3440 4106 1 +a 3441 3442 665 +a 3441 4106 1 +a 3442 3443 664 +a 3442 4106 1 +a 3443 3444 663 +a 3443 4106 1 +a 3444 3445 662 +a 3444 4106 1 +a 3445 3446 661 +a 3445 4106 1 +a 3446 3447 660 +a 3446 4106 1 +a 3447 3448 659 +a 3447 4106 1 +a 3448 3449 658 +a 3448 4106 1 +a 3449 3450 657 +a 3449 4106 1 +a 3450 3451 656 +a 3450 4106 1 +a 3451 3452 655 +a 3451 4106 1 +a 3452 3453 654 +a 3452 4106 1 +a 3453 3454 653 +a 3453 4106 1 +a 3454 3455 652 +a 3454 4106 1 +a 3455 3456 651 +a 3455 4106 1 +a 3456 3457 650 +a 3456 4106 1 +a 3457 3458 649 +a 3457 4106 1 +a 3458 3459 648 +a 3458 4106 1 +a 3459 3460 647 +a 3459 4106 1 +a 3460 3461 646 +a 3460 4106 1 +a 3461 3462 645 +a 3461 4106 1 +a 3462 3463 644 +a 3462 4106 1 +a 3463 3464 643 +a 3463 4106 1 +a 3464 3465 642 +a 3464 4106 1 +a 3465 3466 641 +a 3465 4106 1 +a 3466 3467 640 +a 3466 4106 1 +a 3467 3468 639 +a 3467 4106 1 +a 3468 3469 638 +a 3468 4106 1 +a 3469 3470 637 +a 3469 4106 1 +a 3470 3471 636 +a 3470 4106 1 +a 3471 3472 635 +a 3471 4106 1 +a 3472 3473 634 +a 3472 4106 1 +a 3473 3474 633 +a 3473 4106 1 +a 3474 3475 632 +a 3474 4106 1 +a 3475 3476 631 +a 3475 4106 1 +a 3476 3477 630 +a 3476 4106 1 +a 3477 3478 629 +a 3477 4106 1 +a 3478 3479 628 +a 3478 4106 1 +a 3479 3480 627 +a 3479 4106 1 +a 3480 3481 626 +a 3480 4106 1 +a 3481 3482 625 +a 3481 4106 1 +a 3482 3483 624 +a 3482 4106 1 +a 3483 3484 623 +a 3483 4106 1 +a 3484 3485 622 +a 3484 4106 1 +a 3485 3486 621 +a 3485 4106 1 +a 3486 3487 620 +a 3486 4106 1 +a 3487 3488 619 +a 3487 4106 1 +a 3488 3489 618 +a 3488 4106 1 +a 3489 3490 617 +a 3489 4106 1 +a 3490 3491 616 +a 3490 4106 1 +a 3491 3492 615 +a 3491 4106 1 +a 3492 3493 614 +a 3492 4106 1 +a 3493 3494 613 +a 3493 4106 1 +a 3494 3495 612 +a 3494 4106 1 +a 3495 3496 611 +a 3495 4106 1 +a 3496 3497 610 +a 3496 4106 1 +a 3497 3498 609 +a 3497 4106 1 +a 3498 3499 608 +a 3498 4106 1 +a 3499 3500 607 +a 3499 4106 1 +a 3500 3501 606 +a 3500 4106 1 +a 3501 3502 605 +a 3501 4106 1 +a 3502 3503 604 +a 3502 4106 1 +a 3503 3504 603 +a 3503 4106 1 +a 3504 3505 602 +a 3504 4106 1 +a 3505 3506 601 +a 3505 4106 1 +a 3506 3507 600 +a 3506 4106 1 +a 3507 3508 599 +a 3507 4106 1 +a 3508 3509 598 +a 3508 4106 1 +a 3509 3510 597 +a 3509 4106 1 +a 3510 3511 596 +a 3510 4106 1 +a 3511 3512 595 +a 3511 4106 1 +a 3512 3513 594 +a 3512 4106 1 +a 3513 3514 593 +a 3513 4106 1 +a 3514 3515 592 +a 3514 4106 1 +a 3515 3516 591 +a 3515 4106 1 +a 3516 3517 590 +a 3516 4106 1 +a 3517 3518 589 +a 3517 4106 1 +a 3518 3519 588 +a 3518 4106 1 +a 3519 3520 587 +a 3519 4106 1 +a 3520 3521 586 +a 3520 4106 1 +a 3521 3522 585 +a 3521 4106 1 +a 3522 3523 584 +a 3522 4106 1 +a 3523 3524 583 +a 3523 4106 1 +a 3524 3525 582 +a 3524 4106 1 +a 3525 3526 581 +a 3525 4106 1 +a 3526 3527 580 +a 3526 4106 1 +a 3527 3528 579 +a 3527 4106 1 +a 3528 3529 578 +a 3528 4106 1 +a 3529 3530 577 +a 3529 4106 1 +a 3530 3531 576 +a 3530 4106 1 +a 3531 3532 575 +a 3531 4106 1 +a 3532 3533 574 +a 3532 4106 1 +a 3533 3534 573 +a 3533 4106 1 +a 3534 3535 572 +a 3534 4106 1 +a 3535 3536 571 +a 3535 4106 1 +a 3536 3537 570 +a 3536 4106 1 +a 3537 3538 569 +a 3537 4106 1 +a 3538 3539 568 +a 3538 4106 1 +a 3539 3540 567 +a 3539 4106 1 +a 3540 3541 566 +a 3540 4106 1 +a 3541 3542 565 +a 3541 4106 1 +a 3542 3543 564 +a 3542 4106 1 +a 3543 3544 563 +a 3543 4106 1 +a 3544 3545 562 +a 3544 4106 1 +a 3545 3546 561 +a 3545 4106 1 +a 3546 3547 560 +a 3546 4106 1 +a 3547 3548 559 +a 3547 4106 1 +a 3548 3549 558 +a 3548 4106 1 +a 3549 3550 557 +a 3549 4106 1 +a 3550 3551 556 +a 3550 4106 1 +a 3551 3552 555 +a 3551 4106 1 +a 3552 3553 554 +a 3552 4106 1 +a 3553 3554 553 +a 3553 4106 1 +a 3554 3555 552 +a 3554 4106 1 +a 3555 3556 551 +a 3555 4106 1 +a 3556 3557 550 +a 3556 4106 1 +a 3557 3558 549 +a 3557 4106 1 +a 3558 3559 548 +a 3558 4106 1 +a 3559 3560 547 +a 3559 4106 1 +a 3560 3561 546 +a 3560 4106 1 +a 3561 3562 545 +a 3561 4106 1 +a 3562 3563 544 +a 3562 4106 1 +a 3563 3564 543 +a 3563 4106 1 +a 3564 3565 542 +a 3564 4106 1 +a 3565 3566 541 +a 3565 4106 1 +a 3566 3567 540 +a 3566 4106 1 +a 3567 3568 539 +a 3567 4106 1 +a 3568 3569 538 +a 3568 4106 1 +a 3569 3570 537 +a 3569 4106 1 +a 3570 3571 536 +a 3570 4106 1 +a 3571 3572 535 +a 3571 4106 1 +a 3572 3573 534 +a 3572 4106 1 +a 3573 3574 533 +a 3573 4106 1 +a 3574 3575 532 +a 3574 4106 1 +a 3575 3576 531 +a 3575 4106 1 +a 3576 3577 530 +a 3576 4106 1 +a 3577 3578 529 +a 3577 4106 1 +a 3578 3579 528 +a 3578 4106 1 +a 3579 3580 527 +a 3579 4106 1 +a 3580 3581 526 +a 3580 4106 1 +a 3581 3582 525 +a 3581 4106 1 +a 3582 3583 524 +a 3582 4106 1 +a 3583 3584 523 +a 3583 4106 1 +a 3584 3585 522 +a 3584 4106 1 +a 3585 3586 521 +a 3585 4106 1 +a 3586 3587 520 +a 3586 4106 1 +a 3587 3588 519 +a 3587 4106 1 +a 3588 3589 518 +a 3588 4106 1 +a 3589 3590 517 +a 3589 4106 1 +a 3590 3591 516 +a 3590 4106 1 +a 3591 3592 515 +a 3591 4106 1 +a 3592 3593 514 +a 3592 4106 1 +a 3593 3594 513 +a 3593 4106 1 +a 3594 3595 512 +a 3594 4106 1 +a 3595 3596 511 +a 3595 4106 1 +a 3596 3597 510 +a 3596 4106 1 +a 3597 3598 509 +a 3597 4106 1 +a 3598 3599 508 +a 3598 4106 1 +a 3599 3600 507 +a 3599 4106 1 +a 3600 3601 506 +a 3600 4106 1 +a 3601 3602 505 +a 3601 4106 1 +a 3602 3603 504 +a 3602 4106 1 +a 3603 3604 503 +a 3603 4106 1 +a 3604 3605 502 +a 3604 4106 1 +a 3605 3606 501 +a 3605 4106 1 +a 3606 3607 500 +a 3606 4106 1 +a 3607 3608 499 +a 3607 4106 1 +a 3608 3609 498 +a 3608 4106 1 +a 3609 3610 497 +a 3609 4106 1 +a 3610 3611 496 +a 3610 4106 1 +a 3611 3612 495 +a 3611 4106 1 +a 3612 3613 494 +a 3612 4106 1 +a 3613 3614 493 +a 3613 4106 1 +a 3614 3615 492 +a 3614 4106 1 +a 3615 3616 491 +a 3615 4106 1 +a 3616 3617 490 +a 3616 4106 1 +a 3617 3618 489 +a 3617 4106 1 +a 3618 3619 488 +a 3618 4106 1 +a 3619 3620 487 +a 3619 4106 1 +a 3620 3621 486 +a 3620 4106 1 +a 3621 3622 485 +a 3621 4106 1 +a 3622 3623 484 +a 3622 4106 1 +a 3623 3624 483 +a 3623 4106 1 +a 3624 3625 482 +a 3624 4106 1 +a 3625 3626 481 +a 3625 4106 1 +a 3626 3627 480 +a 3626 4106 1 +a 3627 3628 479 +a 3627 4106 1 +a 3628 3629 478 +a 3628 4106 1 +a 3629 3630 477 +a 3629 4106 1 +a 3630 3631 476 +a 3630 4106 1 +a 3631 3632 475 +a 3631 4106 1 +a 3632 3633 474 +a 3632 4106 1 +a 3633 3634 473 +a 3633 4106 1 +a 3634 3635 472 +a 3634 4106 1 +a 3635 3636 471 +a 3635 4106 1 +a 3636 3637 470 +a 3636 4106 1 +a 3637 3638 469 +a 3637 4106 1 +a 3638 3639 468 +a 3638 4106 1 +a 3639 3640 467 +a 3639 4106 1 +a 3640 3641 466 +a 3640 4106 1 +a 3641 3642 465 +a 3641 4106 1 +a 3642 3643 464 +a 3642 4106 1 +a 3643 3644 463 +a 3643 4106 1 +a 3644 3645 462 +a 3644 4106 1 +a 3645 3646 461 +a 3645 4106 1 +a 3646 3647 460 +a 3646 4106 1 +a 3647 3648 459 +a 3647 4106 1 +a 3648 3649 458 +a 3648 4106 1 +a 3649 3650 457 +a 3649 4106 1 +a 3650 3651 456 +a 3650 4106 1 +a 3651 3652 455 +a 3651 4106 1 +a 3652 3653 454 +a 3652 4106 1 +a 3653 3654 453 +a 3653 4106 1 +a 3654 3655 452 +a 3654 4106 1 +a 3655 3656 451 +a 3655 4106 1 +a 3656 3657 450 +a 3656 4106 1 +a 3657 3658 449 +a 3657 4106 1 +a 3658 3659 448 +a 3658 4106 1 +a 3659 3660 447 +a 3659 4106 1 +a 3660 3661 446 +a 3660 4106 1 +a 3661 3662 445 +a 3661 4106 1 +a 3662 3663 444 +a 3662 4106 1 +a 3663 3664 443 +a 3663 4106 1 +a 3664 3665 442 +a 3664 4106 1 +a 3665 3666 441 +a 3665 4106 1 +a 3666 3667 440 +a 3666 4106 1 +a 3667 3668 439 +a 3667 4106 1 +a 3668 3669 438 +a 3668 4106 1 +a 3669 3670 437 +a 3669 4106 1 +a 3670 3671 436 +a 3670 4106 1 +a 3671 3672 435 +a 3671 4106 1 +a 3672 3673 434 +a 3672 4106 1 +a 3673 3674 433 +a 3673 4106 1 +a 3674 3675 432 +a 3674 4106 1 +a 3675 3676 431 +a 3675 4106 1 +a 3676 3677 430 +a 3676 4106 1 +a 3677 3678 429 +a 3677 4106 1 +a 3678 3679 428 +a 3678 4106 1 +a 3679 3680 427 +a 3679 4106 1 +a 3680 3681 426 +a 3680 4106 1 +a 3681 3682 425 +a 3681 4106 1 +a 3682 3683 424 +a 3682 4106 1 +a 3683 3684 423 +a 3683 4106 1 +a 3684 3685 422 +a 3684 4106 1 +a 3685 3686 421 +a 3685 4106 1 +a 3686 3687 420 +a 3686 4106 1 +a 3687 3688 419 +a 3687 4106 1 +a 3688 3689 418 +a 3688 4106 1 +a 3689 3690 417 +a 3689 4106 1 +a 3690 3691 416 +a 3690 4106 1 +a 3691 3692 415 +a 3691 4106 1 +a 3692 3693 414 +a 3692 4106 1 +a 3693 3694 413 +a 3693 4106 1 +a 3694 3695 412 +a 3694 4106 1 +a 3695 3696 411 +a 3695 4106 1 +a 3696 3697 410 +a 3696 4106 1 +a 3697 3698 409 +a 3697 4106 1 +a 3698 3699 408 +a 3698 4106 1 +a 3699 3700 407 +a 3699 4106 1 +a 3700 3701 406 +a 3700 4106 1 +a 3701 3702 405 +a 3701 4106 1 +a 3702 3703 404 +a 3702 4106 1 +a 3703 3704 403 +a 3703 4106 1 +a 3704 3705 402 +a 3704 4106 1 +a 3705 3706 401 +a 3705 4106 1 +a 3706 3707 400 +a 3706 4106 1 +a 3707 3708 399 +a 3707 4106 1 +a 3708 3709 398 +a 3708 4106 1 +a 3709 3710 397 +a 3709 4106 1 +a 3710 3711 396 +a 3710 4106 1 +a 3711 3712 395 +a 3711 4106 1 +a 3712 3713 394 +a 3712 4106 1 +a 3713 3714 393 +a 3713 4106 1 +a 3714 3715 392 +a 3714 4106 1 +a 3715 3716 391 +a 3715 4106 1 +a 3716 3717 390 +a 3716 4106 1 +a 3717 3718 389 +a 3717 4106 1 +a 3718 3719 388 +a 3718 4106 1 +a 3719 3720 387 +a 3719 4106 1 +a 3720 3721 386 +a 3720 4106 1 +a 3721 3722 385 +a 3721 4106 1 +a 3722 3723 384 +a 3722 4106 1 +a 3723 3724 383 +a 3723 4106 1 +a 3724 3725 382 +a 3724 4106 1 +a 3725 3726 381 +a 3725 4106 1 +a 3726 3727 380 +a 3726 4106 1 +a 3727 3728 379 +a 3727 4106 1 +a 3728 3729 378 +a 3728 4106 1 +a 3729 3730 377 +a 3729 4106 1 +a 3730 3731 376 +a 3730 4106 1 +a 3731 3732 375 +a 3731 4106 1 +a 3732 3733 374 +a 3732 4106 1 +a 3733 3734 373 +a 3733 4106 1 +a 3734 3735 372 +a 3734 4106 1 +a 3735 3736 371 +a 3735 4106 1 +a 3736 3737 370 +a 3736 4106 1 +a 3737 3738 369 +a 3737 4106 1 +a 3738 3739 368 +a 3738 4106 1 +a 3739 3740 367 +a 3739 4106 1 +a 3740 3741 366 +a 3740 4106 1 +a 3741 3742 365 +a 3741 4106 1 +a 3742 3743 364 +a 3742 4106 1 +a 3743 3744 363 +a 3743 4106 1 +a 3744 3745 362 +a 3744 4106 1 +a 3745 3746 361 +a 3745 4106 1 +a 3746 3747 360 +a 3746 4106 1 +a 3747 3748 359 +a 3747 4106 1 +a 3748 3749 358 +a 3748 4106 1 +a 3749 3750 357 +a 3749 4106 1 +a 3750 3751 356 +a 3750 4106 1 +a 3751 3752 355 +a 3751 4106 1 +a 3752 3753 354 +a 3752 4106 1 +a 3753 3754 353 +a 3753 4106 1 +a 3754 3755 352 +a 3754 4106 1 +a 3755 3756 351 +a 3755 4106 1 +a 3756 3757 350 +a 3756 4106 1 +a 3757 3758 349 +a 3757 4106 1 +a 3758 3759 348 +a 3758 4106 1 +a 3759 3760 347 +a 3759 4106 1 +a 3760 3761 346 +a 3760 4106 1 +a 3761 3762 345 +a 3761 4106 1 +a 3762 3763 344 +a 3762 4106 1 +a 3763 3764 343 +a 3763 4106 1 +a 3764 3765 342 +a 3764 4106 1 +a 3765 3766 341 +a 3765 4106 1 +a 3766 3767 340 +a 3766 4106 1 +a 3767 3768 339 +a 3767 4106 1 +a 3768 3769 338 +a 3768 4106 1 +a 3769 3770 337 +a 3769 4106 1 +a 3770 3771 336 +a 3770 4106 1 +a 3771 3772 335 +a 3771 4106 1 +a 3772 3773 334 +a 3772 4106 1 +a 3773 3774 333 +a 3773 4106 1 +a 3774 3775 332 +a 3774 4106 1 +a 3775 3776 331 +a 3775 4106 1 +a 3776 3777 330 +a 3776 4106 1 +a 3777 3778 329 +a 3777 4106 1 +a 3778 3779 328 +a 3778 4106 1 +a 3779 3780 327 +a 3779 4106 1 +a 3780 3781 326 +a 3780 4106 1 +a 3781 3782 325 +a 3781 4106 1 +a 3782 3783 324 +a 3782 4106 1 +a 3783 3784 323 +a 3783 4106 1 +a 3784 3785 322 +a 3784 4106 1 +a 3785 3786 321 +a 3785 4106 1 +a 3786 3787 320 +a 3786 4106 1 +a 3787 3788 319 +a 3787 4106 1 +a 3788 3789 318 +a 3788 4106 1 +a 3789 3790 317 +a 3789 4106 1 +a 3790 3791 316 +a 3790 4106 1 +a 3791 3792 315 +a 3791 4106 1 +a 3792 3793 314 +a 3792 4106 1 +a 3793 3794 313 +a 3793 4106 1 +a 3794 3795 312 +a 3794 4106 1 +a 3795 3796 311 +a 3795 4106 1 +a 3796 3797 310 +a 3796 4106 1 +a 3797 3798 309 +a 3797 4106 1 +a 3798 3799 308 +a 3798 4106 1 +a 3799 3800 307 +a 3799 4106 1 +a 3800 3801 306 +a 3800 4106 1 +a 3801 3802 305 +a 3801 4106 1 +a 3802 3803 304 +a 3802 4106 1 +a 3803 3804 303 +a 3803 4106 1 +a 3804 3805 302 +a 3804 4106 1 +a 3805 3806 301 +a 3805 4106 1 +a 3806 3807 300 +a 3806 4106 1 +a 3807 3808 299 +a 3807 4106 1 +a 3808 3809 298 +a 3808 4106 1 +a 3809 3810 297 +a 3809 4106 1 +a 3810 3811 296 +a 3810 4106 1 +a 3811 3812 295 +a 3811 4106 1 +a 3812 3813 294 +a 3812 4106 1 +a 3813 3814 293 +a 3813 4106 1 +a 3814 3815 292 +a 3814 4106 1 +a 3815 3816 291 +a 3815 4106 1 +a 3816 3817 290 +a 3816 4106 1 +a 3817 3818 289 +a 3817 4106 1 +a 3818 3819 288 +a 3818 4106 1 +a 3819 3820 287 +a 3819 4106 1 +a 3820 3821 286 +a 3820 4106 1 +a 3821 3822 285 +a 3821 4106 1 +a 3822 3823 284 +a 3822 4106 1 +a 3823 3824 283 +a 3823 4106 1 +a 3824 3825 282 +a 3824 4106 1 +a 3825 3826 281 +a 3825 4106 1 +a 3826 3827 280 +a 3826 4106 1 +a 3827 3828 279 +a 3827 4106 1 +a 3828 3829 278 +a 3828 4106 1 +a 3829 3830 277 +a 3829 4106 1 +a 3830 3831 276 +a 3830 4106 1 +a 3831 3832 275 +a 3831 4106 1 +a 3832 3833 274 +a 3832 4106 1 +a 3833 3834 273 +a 3833 4106 1 +a 3834 3835 272 +a 3834 4106 1 +a 3835 3836 271 +a 3835 4106 1 +a 3836 3837 270 +a 3836 4106 1 +a 3837 3838 269 +a 3837 4106 1 +a 3838 3839 268 +a 3838 4106 1 +a 3839 3840 267 +a 3839 4106 1 +a 3840 3841 266 +a 3840 4106 1 +a 3841 3842 265 +a 3841 4106 1 +a 3842 3843 264 +a 3842 4106 1 +a 3843 3844 263 +a 3843 4106 1 +a 3844 3845 262 +a 3844 4106 1 +a 3845 3846 261 +a 3845 4106 1 +a 3846 3847 260 +a 3846 4106 1 +a 3847 3848 259 +a 3847 4106 1 +a 3848 3849 258 +a 3848 4106 1 +a 3849 3850 257 +a 3849 4106 1 +a 3850 3851 256 +a 3850 4106 1 +a 3851 3852 255 +a 3851 4106 1 +a 3852 3853 254 +a 3852 4106 1 +a 3853 3854 253 +a 3853 4106 1 +a 3854 3855 252 +a 3854 4106 1 +a 3855 3856 251 +a 3855 4106 1 +a 3856 3857 250 +a 3856 4106 1 +a 3857 3858 249 +a 3857 4106 1 +a 3858 3859 248 +a 3858 4106 1 +a 3859 3860 247 +a 3859 4106 1 +a 3860 3861 246 +a 3860 4106 1 +a 3861 3862 245 +a 3861 4106 1 +a 3862 3863 244 +a 3862 4106 1 +a 3863 3864 243 +a 3863 4106 1 +a 3864 3865 242 +a 3864 4106 1 +a 3865 3866 241 +a 3865 4106 1 +a 3866 3867 240 +a 3866 4106 1 +a 3867 3868 239 +a 3867 4106 1 +a 3868 3869 238 +a 3868 4106 1 +a 3869 3870 237 +a 3869 4106 1 +a 3870 3871 236 +a 3870 4106 1 +a 3871 3872 235 +a 3871 4106 1 +a 3872 3873 234 +a 3872 4106 1 +a 3873 3874 233 +a 3873 4106 1 +a 3874 3875 232 +a 3874 4106 1 +a 3875 3876 231 +a 3875 4106 1 +a 3876 3877 230 +a 3876 4106 1 +a 3877 3878 229 +a 3877 4106 1 +a 3878 3879 228 +a 3878 4106 1 +a 3879 3880 227 +a 3879 4106 1 +a 3880 3881 226 +a 3880 4106 1 +a 3881 3882 225 +a 3881 4106 1 +a 3882 3883 224 +a 3882 4106 1 +a 3883 3884 223 +a 3883 4106 1 +a 3884 3885 222 +a 3884 4106 1 +a 3885 3886 221 +a 3885 4106 1 +a 3886 3887 220 +a 3886 4106 1 +a 3887 3888 219 +a 3887 4106 1 +a 3888 3889 218 +a 3888 4106 1 +a 3889 3890 217 +a 3889 4106 1 +a 3890 3891 216 +a 3890 4106 1 +a 3891 3892 215 +a 3891 4106 1 +a 3892 3893 214 +a 3892 4106 1 +a 3893 3894 213 +a 3893 4106 1 +a 3894 3895 212 +a 3894 4106 1 +a 3895 3896 211 +a 3895 4106 1 +a 3896 3897 210 +a 3896 4106 1 +a 3897 3898 209 +a 3897 4106 1 +a 3898 3899 208 +a 3898 4106 1 +a 3899 3900 207 +a 3899 4106 1 +a 3900 3901 206 +a 3900 4106 1 +a 3901 3902 205 +a 3901 4106 1 +a 3902 3903 204 +a 3902 4106 1 +a 3903 3904 203 +a 3903 4106 1 +a 3904 3905 202 +a 3904 4106 1 +a 3905 3906 201 +a 3905 4106 1 +a 3906 3907 200 +a 3906 4106 1 +a 3907 3908 199 +a 3907 4106 1 +a 3908 3909 198 +a 3908 4106 1 +a 3909 3910 197 +a 3909 4106 1 +a 3910 3911 196 +a 3910 4106 1 +a 3911 3912 195 +a 3911 4106 1 +a 3912 3913 194 +a 3912 4106 1 +a 3913 3914 193 +a 3913 4106 1 +a 3914 3915 192 +a 3914 4106 1 +a 3915 3916 191 +a 3915 4106 1 +a 3916 3917 190 +a 3916 4106 1 +a 3917 3918 189 +a 3917 4106 1 +a 3918 3919 188 +a 3918 4106 1 +a 3919 3920 187 +a 3919 4106 1 +a 3920 3921 186 +a 3920 4106 1 +a 3921 3922 185 +a 3921 4106 1 +a 3922 3923 184 +a 3922 4106 1 +a 3923 3924 183 +a 3923 4106 1 +a 3924 3925 182 +a 3924 4106 1 +a 3925 3926 181 +a 3925 4106 1 +a 3926 3927 180 +a 3926 4106 1 +a 3927 3928 179 +a 3927 4106 1 +a 3928 3929 178 +a 3928 4106 1 +a 3929 3930 177 +a 3929 4106 1 +a 3930 3931 176 +a 3930 4106 1 +a 3931 3932 175 +a 3931 4106 1 +a 3932 3933 174 +a 3932 4106 1 +a 3933 3934 173 +a 3933 4106 1 +a 3934 3935 172 +a 3934 4106 1 +a 3935 3936 171 +a 3935 4106 1 +a 3936 3937 170 +a 3936 4106 1 +a 3937 3938 169 +a 3937 4106 1 +a 3938 3939 168 +a 3938 4106 1 +a 3939 3940 167 +a 3939 4106 1 +a 3940 3941 166 +a 3940 4106 1 +a 3941 3942 165 +a 3941 4106 1 +a 3942 3943 164 +a 3942 4106 1 +a 3943 3944 163 +a 3943 4106 1 +a 3944 3945 162 +a 3944 4106 1 +a 3945 3946 161 +a 3945 4106 1 +a 3946 3947 160 +a 3946 4106 1 +a 3947 3948 159 +a 3947 4106 1 +a 3948 3949 158 +a 3948 4106 1 +a 3949 3950 157 +a 3949 4106 1 +a 3950 3951 156 +a 3950 4106 1 +a 3951 3952 155 +a 3951 4106 1 +a 3952 3953 154 +a 3952 4106 1 +a 3953 3954 153 +a 3953 4106 1 +a 3954 3955 152 +a 3954 4106 1 +a 3955 3956 151 +a 3955 4106 1 +a 3956 3957 150 +a 3956 4106 1 +a 3957 3958 149 +a 3957 4106 1 +a 3958 3959 148 +a 3958 4106 1 +a 3959 3960 147 +a 3959 4106 1 +a 3960 3961 146 +a 3960 4106 1 +a 3961 3962 145 +a 3961 4106 1 +a 3962 3963 144 +a 3962 4106 1 +a 3963 3964 143 +a 3963 4106 1 +a 3964 3965 142 +a 3964 4106 1 +a 3965 3966 141 +a 3965 4106 1 +a 3966 3967 140 +a 3966 4106 1 +a 3967 3968 139 +a 3967 4106 1 +a 3968 3969 138 +a 3968 4106 1 +a 3969 3970 137 +a 3969 4106 1 +a 3970 3971 136 +a 3970 4106 1 +a 3971 3972 135 +a 3971 4106 1 +a 3972 3973 134 +a 3972 4106 1 +a 3973 3974 133 +a 3973 4106 1 +a 3974 3975 132 +a 3974 4106 1 +a 3975 3976 131 +a 3975 4106 1 +a 3976 3977 130 +a 3976 4106 1 +a 3977 3978 129 +a 3977 4106 1 +a 3978 3979 128 +a 3978 4106 1 +a 3979 3980 127 +a 3979 4106 1 +a 3980 3981 126 +a 3980 4106 1 +a 3981 3982 125 +a 3981 4106 1 +a 3982 3983 124 +a 3982 4106 1 +a 3983 3984 123 +a 3983 4106 1 +a 3984 3985 122 +a 3984 4106 1 +a 3985 3986 121 +a 3985 4106 1 +a 3986 3987 120 +a 3986 4106 1 +a 3987 3988 119 +a 3987 4106 1 +a 3988 3989 118 +a 3988 4106 1 +a 3989 3990 117 +a 3989 4106 1 +a 3990 3991 116 +a 3990 4106 1 +a 3991 3992 115 +a 3991 4106 1 +a 3992 3993 114 +a 3992 4106 1 +a 3993 3994 113 +a 3993 4106 1 +a 3994 3995 112 +a 3994 4106 1 +a 3995 3996 111 +a 3995 4106 1 +a 3996 3997 110 +a 3996 4106 1 +a 3997 3998 109 +a 3997 4106 1 +a 3998 3999 108 +a 3998 4106 1 +a 3999 4000 107 +a 3999 4106 1 +a 4000 4001 106 +a 4000 4106 1 +a 4001 4002 105 +a 4001 4106 1 +a 4002 4003 104 +a 4002 4106 1 +a 4003 4004 103 +a 4003 4106 1 +a 4004 4005 102 +a 4004 4106 1 +a 4005 4006 101 +a 4005 4106 1 +a 4006 4007 100 +a 4006 4106 1 +a 4007 4008 99 +a 4007 4106 1 +a 4008 4009 98 +a 4008 4106 1 +a 4009 4010 97 +a 4009 4106 1 +a 4010 4011 96 +a 4010 4106 1 +a 4011 4012 95 +a 4011 4106 1 +a 4012 4013 94 +a 4012 4106 1 +a 4013 4014 93 +a 4013 4106 1 +a 4014 4015 92 +a 4014 4106 1 +a 4015 4016 91 +a 4015 4106 1 +a 4016 4017 90 +a 4016 4106 1 +a 4017 4018 89 +a 4017 4106 1 +a 4018 4019 88 +a 4018 4106 1 +a 4019 4020 87 +a 4019 4106 1 +a 4020 4021 86 +a 4020 4106 1 +a 4021 4022 85 +a 4021 4106 1 +a 4022 4023 84 +a 4022 4106 1 +a 4023 4024 83 +a 4023 4106 1 +a 4024 4025 82 +a 4024 4106 1 +a 4025 4026 81 +a 4025 4106 1 +a 4026 4027 80 +a 4026 4106 1 +a 4027 4028 79 +a 4027 4106 1 +a 4028 4029 78 +a 4028 4106 1 +a 4029 4030 77 +a 4029 4106 1 +a 4030 4031 76 +a 4030 4106 1 +a 4031 4032 75 +a 4031 4106 1 +a 4032 4033 74 +a 4032 4106 1 +a 4033 4034 73 +a 4033 4106 1 +a 4034 4035 72 +a 4034 4106 1 +a 4035 4036 71 +a 4035 4106 1 +a 4036 4037 70 +a 4036 4106 1 +a 4037 4038 69 +a 4037 4106 1 +a 4038 4039 68 +a 4038 4106 1 +a 4039 4040 67 +a 4039 4106 1 +a 4040 4041 66 +a 4040 4106 1 +a 4041 4042 65 +a 4041 4106 1 +a 4042 4043 64 +a 4042 4106 1 +a 4043 4044 63 +a 4043 4106 1 +a 4044 4045 62 +a 4044 4106 1 +a 4045 4046 61 +a 4045 4106 1 +a 4046 4047 60 +a 4046 4106 1 +a 4047 4048 59 +a 4047 4106 1 +a 4048 4049 58 +a 4048 4106 1 +a 4049 4050 57 +a 4049 4106 1 +a 4050 4051 56 +a 4050 4106 1 +a 4051 4052 55 +a 4051 4106 1 +a 4052 4053 54 +a 4052 4106 1 +a 4053 4054 53 +a 4053 4106 1 +a 4054 4055 52 +a 4054 4106 1 +a 4055 4056 51 +a 4055 4106 1 +a 4056 4057 50 +a 4056 4106 1 +a 4057 4058 49 +a 4057 4106 1 +a 4058 4059 48 +a 4058 4106 1 +a 4059 4060 47 +a 4059 4106 1 +a 4060 4061 46 +a 4060 4106 1 +a 4061 4062 45 +a 4061 4106 1 +a 4062 4063 44 +a 4062 4106 1 +a 4063 4064 43 +a 4063 4106 1 +a 4064 4065 42 +a 4064 4106 1 +a 4065 4066 41 +a 4065 4106 1 +a 4066 4067 40 +a 4066 4106 1 +a 4067 4068 39 +a 4067 4106 1 +a 4068 4069 38 +a 4068 4106 1 +a 4069 4070 37 +a 4069 4106 1 +a 4070 4071 36 +a 4070 4106 1 +a 4071 4072 35 +a 4071 4106 1 +a 4072 4073 34 +a 4072 4106 1 +a 4073 4074 33 +a 4073 4106 1 +a 4074 4075 32 +a 4074 4106 1 +a 4075 4076 31 +a 4075 4106 1 +a 4076 4077 30 +a 4076 4106 1 +a 4077 4078 29 +a 4077 4106 1 +a 4078 4079 28 +a 4078 4106 1 +a 4079 4080 27 +a 4079 4106 1 +a 4080 4081 26 +a 4080 4106 1 +a 4081 4082 25 +a 4081 4106 1 +a 4082 4083 24 +a 4082 4106 1 +a 4083 4084 23 +a 4083 4106 1 +a 4084 4085 22 +a 4084 4106 1 +a 4085 4086 21 +a 4085 4106 1 +a 4086 4087 20 +a 4086 4106 1 +a 4087 4088 19 +a 4087 4106 1 +a 4088 4089 18 +a 4088 4106 1 +a 4089 4090 17 +a 4089 4106 1 +a 4090 4091 16 +a 4090 4106 1 +a 4091 4092 15 +a 4091 4106 1 +a 4092 4093 14 +a 4092 4106 1 +a 4093 4094 13 +a 4093 4106 1 +a 4094 4095 12 +a 4094 4106 1 +a 4095 4096 11 +a 4095 4106 1 +a 4096 4097 10 +a 4096 4106 1 +a 4097 4098 9 +a 4097 4106 1 +a 4098 4099 8 +a 4098 4106 1 +a 4099 4100 7 +a 4099 4106 1 +a 4100 4101 6 +a 4100 4106 1 +a 4101 4102 5 +a 4101 4106 1 +a 4102 4103 4 +a 4102 4106 1 +a 4103 4104 3 +a 4103 4106 1 +a 4104 4105 2 +a 4104 4106 1 +a 4105 8208 1 +a 4105 4106 1 +a 4106 4107 4103 +a 4107 4108 4103 +a 4108 4109 4103 +a 4109 4110 4103 +a 4110 4111 4103 +a 4111 4112 4103 +a 4112 4113 4103 +a 4113 4114 4103 +a 4114 4115 4103 +a 4115 4116 4103 +a 4116 4117 4103 +a 4117 4118 4103 +a 4118 4119 4103 +a 4119 4120 4103 +a 4120 4121 4103 +a 4121 4122 4103 +a 4122 4123 4103 +a 4123 4124 4103 +a 4124 4125 4103 +a 4125 4126 4103 +a 4126 4127 4103 +a 4127 4128 4103 +a 4128 4129 4103 +a 4129 4130 4103 +a 4130 4131 4103 +a 4131 4132 4103 +a 4132 4133 4103 +a 4133 4134 4103 +a 4134 4135 4103 +a 4135 4136 4103 +a 4136 4137 4103 +a 4137 4138 4103 +a 4138 4139 4103 +a 4139 4140 4103 +a 4140 4141 4103 +a 4141 4142 4103 +a 4142 4143 4103 +a 4143 4144 4103 +a 4144 4145 4103 +a 4145 4146 4103 +a 4146 4147 4103 +a 4147 4148 4103 +a 4148 4149 4103 +a 4149 4150 4103 +a 4150 4151 4103 +a 4151 4152 4103 +a 4152 4153 4103 +a 4153 4154 4103 +a 4154 4155 4103 +a 4155 4156 4103 +a 4156 4157 4103 +a 4157 4158 4103 +a 4158 4159 4103 +a 4159 4160 4103 +a 4160 4161 4103 +a 4161 4162 4103 +a 4162 4163 4103 +a 4163 4164 4103 +a 4164 4165 4103 +a 4165 4166 4103 +a 4166 4167 4103 +a 4167 4168 4103 +a 4168 4169 4103 +a 4169 4170 4103 +a 4170 4171 4103 +a 4171 4172 4103 +a 4172 4173 4103 +a 4173 4174 4103 +a 4174 4175 4103 +a 4175 4176 4103 +a 4176 4177 4103 +a 4177 4178 4103 +a 4178 4179 4103 +a 4179 4180 4103 +a 4180 4181 4103 +a 4181 4182 4103 +a 4182 4183 4103 +a 4183 4184 4103 +a 4184 4185 4103 +a 4185 4186 4103 +a 4186 4187 4103 +a 4187 4188 4103 +a 4188 4189 4103 +a 4189 4190 4103 +a 4190 4191 4103 +a 4191 4192 4103 +a 4192 4193 4103 +a 4193 4194 4103 +a 4194 4195 4103 +a 4195 4196 4103 +a 4196 4197 4103 +a 4197 4198 4103 +a 4198 4199 4103 +a 4199 4200 4103 +a 4200 4201 4103 +a 4201 4202 4103 +a 4202 4203 4103 +a 4203 4204 4103 +a 4204 4205 4103 +a 4205 4206 4103 +a 4206 4207 4103 +a 4207 4208 4103 +a 4208 4209 4103 +a 4209 4210 4103 +a 4210 4211 4103 +a 4211 4212 4103 +a 4212 4213 4103 +a 4213 4214 4103 +a 4214 4215 4103 +a 4215 4216 4103 +a 4216 4217 4103 +a 4217 4218 4103 +a 4218 4219 4103 +a 4219 4220 4103 +a 4220 4221 4103 +a 4221 4222 4103 +a 4222 4223 4103 +a 4223 4224 4103 +a 4224 4225 4103 +a 4225 4226 4103 +a 4226 4227 4103 +a 4227 4228 4103 +a 4228 4229 4103 +a 4229 4230 4103 +a 4230 4231 4103 +a 4231 4232 4103 +a 4232 4233 4103 +a 4233 4234 4103 +a 4234 4235 4103 +a 4235 4236 4103 +a 4236 4237 4103 +a 4237 4238 4103 +a 4238 4239 4103 +a 4239 4240 4103 +a 4240 4241 4103 +a 4241 4242 4103 +a 4242 4243 4103 +a 4243 4244 4103 +a 4244 4245 4103 +a 4245 4246 4103 +a 4246 4247 4103 +a 4247 4248 4103 +a 4248 4249 4103 +a 4249 4250 4103 +a 4250 4251 4103 +a 4251 4252 4103 +a 4252 4253 4103 +a 4253 4254 4103 +a 4254 4255 4103 +a 4255 4256 4103 +a 4256 4257 4103 +a 4257 4258 4103 +a 4258 4259 4103 +a 4259 4260 4103 +a 4260 4261 4103 +a 4261 4262 4103 +a 4262 4263 4103 +a 4263 4264 4103 +a 4264 4265 4103 +a 4265 4266 4103 +a 4266 4267 4103 +a 4267 4268 4103 +a 4268 4269 4103 +a 4269 4270 4103 +a 4270 4271 4103 +a 4271 4272 4103 +a 4272 4273 4103 +a 4273 4274 4103 +a 4274 4275 4103 +a 4275 4276 4103 +a 4276 4277 4103 +a 4277 4278 4103 +a 4278 4279 4103 +a 4279 4280 4103 +a 4280 4281 4103 +a 4281 4282 4103 +a 4282 4283 4103 +a 4283 4284 4103 +a 4284 4285 4103 +a 4285 4286 4103 +a 4286 4287 4103 +a 4287 4288 4103 +a 4288 4289 4103 +a 4289 4290 4103 +a 4290 4291 4103 +a 4291 4292 4103 +a 4292 4293 4103 +a 4293 4294 4103 +a 4294 4295 4103 +a 4295 4296 4103 +a 4296 4297 4103 +a 4297 4298 4103 +a 4298 4299 4103 +a 4299 4300 4103 +a 4300 4301 4103 +a 4301 4302 4103 +a 4302 4303 4103 +a 4303 4304 4103 +a 4304 4305 4103 +a 4305 4306 4103 +a 4306 4307 4103 +a 4307 4308 4103 +a 4308 4309 4103 +a 4309 4310 4103 +a 4310 4311 4103 +a 4311 4312 4103 +a 4312 4313 4103 +a 4313 4314 4103 +a 4314 4315 4103 +a 4315 4316 4103 +a 4316 4317 4103 +a 4317 4318 4103 +a 4318 4319 4103 +a 4319 4320 4103 +a 4320 4321 4103 +a 4321 4322 4103 +a 4322 4323 4103 +a 4323 4324 4103 +a 4324 4325 4103 +a 4325 4326 4103 +a 4326 4327 4103 +a 4327 4328 4103 +a 4328 4329 4103 +a 4329 4330 4103 +a 4330 4331 4103 +a 4331 4332 4103 +a 4332 4333 4103 +a 4333 4334 4103 +a 4334 4335 4103 +a 4335 4336 4103 +a 4336 4337 4103 +a 4337 4338 4103 +a 4338 4339 4103 +a 4339 4340 4103 +a 4340 4341 4103 +a 4341 4342 4103 +a 4342 4343 4103 +a 4343 4344 4103 +a 4344 4345 4103 +a 4345 4346 4103 +a 4346 4347 4103 +a 4347 4348 4103 +a 4348 4349 4103 +a 4349 4350 4103 +a 4350 4351 4103 +a 4351 4352 4103 +a 4352 4353 4103 +a 4353 4354 4103 +a 4354 4355 4103 +a 4355 4356 4103 +a 4356 4357 4103 +a 4357 4358 4103 +a 4358 4359 4103 +a 4359 4360 4103 +a 4360 4361 4103 +a 4361 4362 4103 +a 4362 4363 4103 +a 4363 4364 4103 +a 4364 4365 4103 +a 4365 4366 4103 +a 4366 4367 4103 +a 4367 4368 4103 +a 4368 4369 4103 +a 4369 4370 4103 +a 4370 4371 4103 +a 4371 4372 4103 +a 4372 4373 4103 +a 4373 4374 4103 +a 4374 4375 4103 +a 4375 4376 4103 +a 4376 4377 4103 +a 4377 4378 4103 +a 4378 4379 4103 +a 4379 4380 4103 +a 4380 4381 4103 +a 4381 4382 4103 +a 4382 4383 4103 +a 4383 4384 4103 +a 4384 4385 4103 +a 4385 4386 4103 +a 4386 4387 4103 +a 4387 4388 4103 +a 4388 4389 4103 +a 4389 4390 4103 +a 4390 4391 4103 +a 4391 4392 4103 +a 4392 4393 4103 +a 4393 4394 4103 +a 4394 4395 4103 +a 4395 4396 4103 +a 4396 4397 4103 +a 4397 4398 4103 +a 4398 4399 4103 +a 4399 4400 4103 +a 4400 4401 4103 +a 4401 4402 4103 +a 4402 4403 4103 +a 4403 4404 4103 +a 4404 4405 4103 +a 4405 4406 4103 +a 4406 4407 4103 +a 4407 4408 4103 +a 4408 4409 4103 +a 4409 4410 4103 +a 4410 4411 4103 +a 4411 4412 4103 +a 4412 4413 4103 +a 4413 4414 4103 +a 4414 4415 4103 +a 4415 4416 4103 +a 4416 4417 4103 +a 4417 4418 4103 +a 4418 4419 4103 +a 4419 4420 4103 +a 4420 4421 4103 +a 4421 4422 4103 +a 4422 4423 4103 +a 4423 4424 4103 +a 4424 4425 4103 +a 4425 4426 4103 +a 4426 4427 4103 +a 4427 4428 4103 +a 4428 4429 4103 +a 4429 4430 4103 +a 4430 4431 4103 +a 4431 4432 4103 +a 4432 4433 4103 +a 4433 4434 4103 +a 4434 4435 4103 +a 4435 4436 4103 +a 4436 4437 4103 +a 4437 4438 4103 +a 4438 4439 4103 +a 4439 4440 4103 +a 4440 4441 4103 +a 4441 4442 4103 +a 4442 4443 4103 +a 4443 4444 4103 +a 4444 4445 4103 +a 4445 4446 4103 +a 4446 4447 4103 +a 4447 4448 4103 +a 4448 4449 4103 +a 4449 4450 4103 +a 4450 4451 4103 +a 4451 4452 4103 +a 4452 4453 4103 +a 4453 4454 4103 +a 4454 4455 4103 +a 4455 4456 4103 +a 4456 4457 4103 +a 4457 4458 4103 +a 4458 4459 4103 +a 4459 4460 4103 +a 4460 4461 4103 +a 4461 4462 4103 +a 4462 4463 4103 +a 4463 4464 4103 +a 4464 4465 4103 +a 4465 4466 4103 +a 4466 4467 4103 +a 4467 4468 4103 +a 4468 4469 4103 +a 4469 4470 4103 +a 4470 4471 4103 +a 4471 4472 4103 +a 4472 4473 4103 +a 4473 4474 4103 +a 4474 4475 4103 +a 4475 4476 4103 +a 4476 4477 4103 +a 4477 4478 4103 +a 4478 4479 4103 +a 4479 4480 4103 +a 4480 4481 4103 +a 4481 4482 4103 +a 4482 4483 4103 +a 4483 4484 4103 +a 4484 4485 4103 +a 4485 4486 4103 +a 4486 4487 4103 +a 4487 4488 4103 +a 4488 4489 4103 +a 4489 4490 4103 +a 4490 4491 4103 +a 4491 4492 4103 +a 4492 4493 4103 +a 4493 4494 4103 +a 4494 4495 4103 +a 4495 4496 4103 +a 4496 4497 4103 +a 4497 4498 4103 +a 4498 4499 4103 +a 4499 4500 4103 +a 4500 4501 4103 +a 4501 4502 4103 +a 4502 4503 4103 +a 4503 4504 4103 +a 4504 4505 4103 +a 4505 4506 4103 +a 4506 4507 4103 +a 4507 4508 4103 +a 4508 4509 4103 +a 4509 4510 4103 +a 4510 4511 4103 +a 4511 4512 4103 +a 4512 4513 4103 +a 4513 4514 4103 +a 4514 4515 4103 +a 4515 4516 4103 +a 4516 4517 4103 +a 4517 4518 4103 +a 4518 4519 4103 +a 4519 4520 4103 +a 4520 4521 4103 +a 4521 4522 4103 +a 4522 4523 4103 +a 4523 4524 4103 +a 4524 4525 4103 +a 4525 4526 4103 +a 4526 4527 4103 +a 4527 4528 4103 +a 4528 4529 4103 +a 4529 4530 4103 +a 4530 4531 4103 +a 4531 4532 4103 +a 4532 4533 4103 +a 4533 4534 4103 +a 4534 4535 4103 +a 4535 4536 4103 +a 4536 4537 4103 +a 4537 4538 4103 +a 4538 4539 4103 +a 4539 4540 4103 +a 4540 4541 4103 +a 4541 4542 4103 +a 4542 4543 4103 +a 4543 4544 4103 +a 4544 4545 4103 +a 4545 4546 4103 +a 4546 4547 4103 +a 4547 4548 4103 +a 4548 4549 4103 +a 4549 4550 4103 +a 4550 4551 4103 +a 4551 4552 4103 +a 4552 4553 4103 +a 4553 4554 4103 +a 4554 4555 4103 +a 4555 4556 4103 +a 4556 4557 4103 +a 4557 4558 4103 +a 4558 4559 4103 +a 4559 4560 4103 +a 4560 4561 4103 +a 4561 4562 4103 +a 4562 4563 4103 +a 4563 4564 4103 +a 4564 4565 4103 +a 4565 4566 4103 +a 4566 4567 4103 +a 4567 4568 4103 +a 4568 4569 4103 +a 4569 4570 4103 +a 4570 4571 4103 +a 4571 4572 4103 +a 4572 4573 4103 +a 4573 4574 4103 +a 4574 4575 4103 +a 4575 4576 4103 +a 4576 4577 4103 +a 4577 4578 4103 +a 4578 4579 4103 +a 4579 4580 4103 +a 4580 4581 4103 +a 4581 4582 4103 +a 4582 4583 4103 +a 4583 4584 4103 +a 4584 4585 4103 +a 4585 4586 4103 +a 4586 4587 4103 +a 4587 4588 4103 +a 4588 4589 4103 +a 4589 4590 4103 +a 4590 4591 4103 +a 4591 4592 4103 +a 4592 4593 4103 +a 4593 4594 4103 +a 4594 4595 4103 +a 4595 4596 4103 +a 4596 4597 4103 +a 4597 4598 4103 +a 4598 4599 4103 +a 4599 4600 4103 +a 4600 4601 4103 +a 4601 4602 4103 +a 4602 4603 4103 +a 4603 4604 4103 +a 4604 4605 4103 +a 4605 4606 4103 +a 4606 4607 4103 +a 4607 4608 4103 +a 4608 4609 4103 +a 4609 4610 4103 +a 4610 4611 4103 +a 4611 4612 4103 +a 4612 4613 4103 +a 4613 4614 4103 +a 4614 4615 4103 +a 4615 4616 4103 +a 4616 4617 4103 +a 4617 4618 4103 +a 4618 4619 4103 +a 4619 4620 4103 +a 4620 4621 4103 +a 4621 4622 4103 +a 4622 4623 4103 +a 4623 4624 4103 +a 4624 4625 4103 +a 4625 4626 4103 +a 4626 4627 4103 +a 4627 4628 4103 +a 4628 4629 4103 +a 4629 4630 4103 +a 4630 4631 4103 +a 4631 4632 4103 +a 4632 4633 4103 +a 4633 4634 4103 +a 4634 4635 4103 +a 4635 4636 4103 +a 4636 4637 4103 +a 4637 4638 4103 +a 4638 4639 4103 +a 4639 4640 4103 +a 4640 4641 4103 +a 4641 4642 4103 +a 4642 4643 4103 +a 4643 4644 4103 +a 4644 4645 4103 +a 4645 4646 4103 +a 4646 4647 4103 +a 4647 4648 4103 +a 4648 4649 4103 +a 4649 4650 4103 +a 4650 4651 4103 +a 4651 4652 4103 +a 4652 4653 4103 +a 4653 4654 4103 +a 4654 4655 4103 +a 4655 4656 4103 +a 4656 4657 4103 +a 4657 4658 4103 +a 4658 4659 4103 +a 4659 4660 4103 +a 4660 4661 4103 +a 4661 4662 4103 +a 4662 4663 4103 +a 4663 4664 4103 +a 4664 4665 4103 +a 4665 4666 4103 +a 4666 4667 4103 +a 4667 4668 4103 +a 4668 4669 4103 +a 4669 4670 4103 +a 4670 4671 4103 +a 4671 4672 4103 +a 4672 4673 4103 +a 4673 4674 4103 +a 4674 4675 4103 +a 4675 4676 4103 +a 4676 4677 4103 +a 4677 4678 4103 +a 4678 4679 4103 +a 4679 4680 4103 +a 4680 4681 4103 +a 4681 4682 4103 +a 4682 4683 4103 +a 4683 4684 4103 +a 4684 4685 4103 +a 4685 4686 4103 +a 4686 4687 4103 +a 4687 4688 4103 +a 4688 4689 4103 +a 4689 4690 4103 +a 4690 4691 4103 +a 4691 4692 4103 +a 4692 4693 4103 +a 4693 4694 4103 +a 4694 4695 4103 +a 4695 4696 4103 +a 4696 4697 4103 +a 4697 4698 4103 +a 4698 4699 4103 +a 4699 4700 4103 +a 4700 4701 4103 +a 4701 4702 4103 +a 4702 4703 4103 +a 4703 4704 4103 +a 4704 4705 4103 +a 4705 4706 4103 +a 4706 4707 4103 +a 4707 4708 4103 +a 4708 4709 4103 +a 4709 4710 4103 +a 4710 4711 4103 +a 4711 4712 4103 +a 4712 4713 4103 +a 4713 4714 4103 +a 4714 4715 4103 +a 4715 4716 4103 +a 4716 4717 4103 +a 4717 4718 4103 +a 4718 4719 4103 +a 4719 4720 4103 +a 4720 4721 4103 +a 4721 4722 4103 +a 4722 4723 4103 +a 4723 4724 4103 +a 4724 4725 4103 +a 4725 4726 4103 +a 4726 4727 4103 +a 4727 4728 4103 +a 4728 4729 4103 +a 4729 4730 4103 +a 4730 4731 4103 +a 4731 4732 4103 +a 4732 4733 4103 +a 4733 4734 4103 +a 4734 4735 4103 +a 4735 4736 4103 +a 4736 4737 4103 +a 4737 4738 4103 +a 4738 4739 4103 +a 4739 4740 4103 +a 4740 4741 4103 +a 4741 4742 4103 +a 4742 4743 4103 +a 4743 4744 4103 +a 4744 4745 4103 +a 4745 4746 4103 +a 4746 4747 4103 +a 4747 4748 4103 +a 4748 4749 4103 +a 4749 4750 4103 +a 4750 4751 4103 +a 4751 4752 4103 +a 4752 4753 4103 +a 4753 4754 4103 +a 4754 4755 4103 +a 4755 4756 4103 +a 4756 4757 4103 +a 4757 4758 4103 +a 4758 4759 4103 +a 4759 4760 4103 +a 4760 4761 4103 +a 4761 4762 4103 +a 4762 4763 4103 +a 4763 4764 4103 +a 4764 4765 4103 +a 4765 4766 4103 +a 4766 4767 4103 +a 4767 4768 4103 +a 4768 4769 4103 +a 4769 4770 4103 +a 4770 4771 4103 +a 4771 4772 4103 +a 4772 4773 4103 +a 4773 4774 4103 +a 4774 4775 4103 +a 4775 4776 4103 +a 4776 4777 4103 +a 4777 4778 4103 +a 4778 4779 4103 +a 4779 4780 4103 +a 4780 4781 4103 +a 4781 4782 4103 +a 4782 4783 4103 +a 4783 4784 4103 +a 4784 4785 4103 +a 4785 4786 4103 +a 4786 4787 4103 +a 4787 4788 4103 +a 4788 4789 4103 +a 4789 4790 4103 +a 4790 4791 4103 +a 4791 4792 4103 +a 4792 4793 4103 +a 4793 4794 4103 +a 4794 4795 4103 +a 4795 4796 4103 +a 4796 4797 4103 +a 4797 4798 4103 +a 4798 4799 4103 +a 4799 4800 4103 +a 4800 4801 4103 +a 4801 4802 4103 +a 4802 4803 4103 +a 4803 4804 4103 +a 4804 4805 4103 +a 4805 4806 4103 +a 4806 4807 4103 +a 4807 4808 4103 +a 4808 4809 4103 +a 4809 4810 4103 +a 4810 4811 4103 +a 4811 4812 4103 +a 4812 4813 4103 +a 4813 4814 4103 +a 4814 4815 4103 +a 4815 4816 4103 +a 4816 4817 4103 +a 4817 4818 4103 +a 4818 4819 4103 +a 4819 4820 4103 +a 4820 4821 4103 +a 4821 4822 4103 +a 4822 4823 4103 +a 4823 4824 4103 +a 4824 4825 4103 +a 4825 4826 4103 +a 4826 4827 4103 +a 4827 4828 4103 +a 4828 4829 4103 +a 4829 4830 4103 +a 4830 4831 4103 +a 4831 4832 4103 +a 4832 4833 4103 +a 4833 4834 4103 +a 4834 4835 4103 +a 4835 4836 4103 +a 4836 4837 4103 +a 4837 4838 4103 +a 4838 4839 4103 +a 4839 4840 4103 +a 4840 4841 4103 +a 4841 4842 4103 +a 4842 4843 4103 +a 4843 4844 4103 +a 4844 4845 4103 +a 4845 4846 4103 +a 4846 4847 4103 +a 4847 4848 4103 +a 4848 4849 4103 +a 4849 4850 4103 +a 4850 4851 4103 +a 4851 4852 4103 +a 4852 4853 4103 +a 4853 4854 4103 +a 4854 4855 4103 +a 4855 4856 4103 +a 4856 4857 4103 +a 4857 4858 4103 +a 4858 4859 4103 +a 4859 4860 4103 +a 4860 4861 4103 +a 4861 4862 4103 +a 4862 4863 4103 +a 4863 4864 4103 +a 4864 4865 4103 +a 4865 4866 4103 +a 4866 4867 4103 +a 4867 4868 4103 +a 4868 4869 4103 +a 4869 4870 4103 +a 4870 4871 4103 +a 4871 4872 4103 +a 4872 4873 4103 +a 4873 4874 4103 +a 4874 4875 4103 +a 4875 4876 4103 +a 4876 4877 4103 +a 4877 4878 4103 +a 4878 4879 4103 +a 4879 4880 4103 +a 4880 4881 4103 +a 4881 4882 4103 +a 4882 4883 4103 +a 4883 4884 4103 +a 4884 4885 4103 +a 4885 4886 4103 +a 4886 4887 4103 +a 4887 4888 4103 +a 4888 4889 4103 +a 4889 4890 4103 +a 4890 4891 4103 +a 4891 4892 4103 +a 4892 4893 4103 +a 4893 4894 4103 +a 4894 4895 4103 +a 4895 4896 4103 +a 4896 4897 4103 +a 4897 4898 4103 +a 4898 4899 4103 +a 4899 4900 4103 +a 4900 4901 4103 +a 4901 4902 4103 +a 4902 4903 4103 +a 4903 4904 4103 +a 4904 4905 4103 +a 4905 4906 4103 +a 4906 4907 4103 +a 4907 4908 4103 +a 4908 4909 4103 +a 4909 4910 4103 +a 4910 4911 4103 +a 4911 4912 4103 +a 4912 4913 4103 +a 4913 4914 4103 +a 4914 4915 4103 +a 4915 4916 4103 +a 4916 4917 4103 +a 4917 4918 4103 +a 4918 4919 4103 +a 4919 4920 4103 +a 4920 4921 4103 +a 4921 4922 4103 +a 4922 4923 4103 +a 4923 4924 4103 +a 4924 4925 4103 +a 4925 4926 4103 +a 4926 4927 4103 +a 4927 4928 4103 +a 4928 4929 4103 +a 4929 4930 4103 +a 4930 4931 4103 +a 4931 4932 4103 +a 4932 4933 4103 +a 4933 4934 4103 +a 4934 4935 4103 +a 4935 4936 4103 +a 4936 4937 4103 +a 4937 4938 4103 +a 4938 4939 4103 +a 4939 4940 4103 +a 4940 4941 4103 +a 4941 4942 4103 +a 4942 4943 4103 +a 4943 4944 4103 +a 4944 4945 4103 +a 4945 4946 4103 +a 4946 4947 4103 +a 4947 4948 4103 +a 4948 4949 4103 +a 4949 4950 4103 +a 4950 4951 4103 +a 4951 4952 4103 +a 4952 4953 4103 +a 4953 4954 4103 +a 4954 4955 4103 +a 4955 4956 4103 +a 4956 4957 4103 +a 4957 4958 4103 +a 4958 4959 4103 +a 4959 4960 4103 +a 4960 4961 4103 +a 4961 4962 4103 +a 4962 4963 4103 +a 4963 4964 4103 +a 4964 4965 4103 +a 4965 4966 4103 +a 4966 4967 4103 +a 4967 4968 4103 +a 4968 4969 4103 +a 4969 4970 4103 +a 4970 4971 4103 +a 4971 4972 4103 +a 4972 4973 4103 +a 4973 4974 4103 +a 4974 4975 4103 +a 4975 4976 4103 +a 4976 4977 4103 +a 4977 4978 4103 +a 4978 4979 4103 +a 4979 4980 4103 +a 4980 4981 4103 +a 4981 4982 4103 +a 4982 4983 4103 +a 4983 4984 4103 +a 4984 4985 4103 +a 4985 4986 4103 +a 4986 4987 4103 +a 4987 4988 4103 +a 4988 4989 4103 +a 4989 4990 4103 +a 4990 4991 4103 +a 4991 4992 4103 +a 4992 4993 4103 +a 4993 4994 4103 +a 4994 4995 4103 +a 4995 4996 4103 +a 4996 4997 4103 +a 4997 4998 4103 +a 4998 4999 4103 +a 4999 5000 4103 +a 5000 5001 4103 +a 5001 5002 4103 +a 5002 5003 4103 +a 5003 5004 4103 +a 5004 5005 4103 +a 5005 5006 4103 +a 5006 5007 4103 +a 5007 5008 4103 +a 5008 5009 4103 +a 5009 5010 4103 +a 5010 5011 4103 +a 5011 5012 4103 +a 5012 5013 4103 +a 5013 5014 4103 +a 5014 5015 4103 +a 5015 5016 4103 +a 5016 5017 4103 +a 5017 5018 4103 +a 5018 5019 4103 +a 5019 5020 4103 +a 5020 5021 4103 +a 5021 5022 4103 +a 5022 5023 4103 +a 5023 5024 4103 +a 5024 5025 4103 +a 5025 5026 4103 +a 5026 5027 4103 +a 5027 5028 4103 +a 5028 5029 4103 +a 5029 5030 4103 +a 5030 5031 4103 +a 5031 5032 4103 +a 5032 5033 4103 +a 5033 5034 4103 +a 5034 5035 4103 +a 5035 5036 4103 +a 5036 5037 4103 +a 5037 5038 4103 +a 5038 5039 4103 +a 5039 5040 4103 +a 5040 5041 4103 +a 5041 5042 4103 +a 5042 5043 4103 +a 5043 5044 4103 +a 5044 5045 4103 +a 5045 5046 4103 +a 5046 5047 4103 +a 5047 5048 4103 +a 5048 5049 4103 +a 5049 5050 4103 +a 5050 5051 4103 +a 5051 5052 4103 +a 5052 5053 4103 +a 5053 5054 4103 +a 5054 5055 4103 +a 5055 5056 4103 +a 5056 5057 4103 +a 5057 5058 4103 +a 5058 5059 4103 +a 5059 5060 4103 +a 5060 5061 4103 +a 5061 5062 4103 +a 5062 5063 4103 +a 5063 5064 4103 +a 5064 5065 4103 +a 5065 5066 4103 +a 5066 5067 4103 +a 5067 5068 4103 +a 5068 5069 4103 +a 5069 5070 4103 +a 5070 5071 4103 +a 5071 5072 4103 +a 5072 5073 4103 +a 5073 5074 4103 +a 5074 5075 4103 +a 5075 5076 4103 +a 5076 5077 4103 +a 5077 5078 4103 +a 5078 5079 4103 +a 5079 5080 4103 +a 5080 5081 4103 +a 5081 5082 4103 +a 5082 5083 4103 +a 5083 5084 4103 +a 5084 5085 4103 +a 5085 5086 4103 +a 5086 5087 4103 +a 5087 5088 4103 +a 5088 5089 4103 +a 5089 5090 4103 +a 5090 5091 4103 +a 5091 5092 4103 +a 5092 5093 4103 +a 5093 5094 4103 +a 5094 5095 4103 +a 5095 5096 4103 +a 5096 5097 4103 +a 5097 5098 4103 +a 5098 5099 4103 +a 5099 5100 4103 +a 5100 5101 4103 +a 5101 5102 4103 +a 5102 5103 4103 +a 5103 5104 4103 +a 5104 5105 4103 +a 5105 5106 4103 +a 5106 5107 4103 +a 5107 5108 4103 +a 5108 5109 4103 +a 5109 5110 4103 +a 5110 5111 4103 +a 5111 5112 4103 +a 5112 5113 4103 +a 5113 5114 4103 +a 5114 5115 4103 +a 5115 5116 4103 +a 5116 5117 4103 +a 5117 5118 4103 +a 5118 5119 4103 +a 5119 5120 4103 +a 5120 5121 4103 +a 5121 5122 4103 +a 5122 5123 4103 +a 5123 5124 4103 +a 5124 5125 4103 +a 5125 5126 4103 +a 5126 5127 4103 +a 5127 5128 4103 +a 5128 5129 4103 +a 5129 5130 4103 +a 5130 5131 4103 +a 5131 5132 4103 +a 5132 5133 4103 +a 5133 5134 4103 +a 5134 5135 4103 +a 5135 5136 4103 +a 5136 5137 4103 +a 5137 5138 4103 +a 5138 5139 4103 +a 5139 5140 4103 +a 5140 5141 4103 +a 5141 5142 4103 +a 5142 5143 4103 +a 5143 5144 4103 +a 5144 5145 4103 +a 5145 5146 4103 +a 5146 5147 4103 +a 5147 5148 4103 +a 5148 5149 4103 +a 5149 5150 4103 +a 5150 5151 4103 +a 5151 5152 4103 +a 5152 5153 4103 +a 5153 5154 4103 +a 5154 5155 4103 +a 5155 5156 4103 +a 5156 5157 4103 +a 5157 5158 4103 +a 5158 5159 4103 +a 5159 5160 4103 +a 5160 5161 4103 +a 5161 5162 4103 +a 5162 5163 4103 +a 5163 5164 4103 +a 5164 5165 4103 +a 5165 5166 4103 +a 5166 5167 4103 +a 5167 5168 4103 +a 5168 5169 4103 +a 5169 5170 4103 +a 5170 5171 4103 +a 5171 5172 4103 +a 5172 5173 4103 +a 5173 5174 4103 +a 5174 5175 4103 +a 5175 5176 4103 +a 5176 5177 4103 +a 5177 5178 4103 +a 5178 5179 4103 +a 5179 5180 4103 +a 5180 5181 4103 +a 5181 5182 4103 +a 5182 5183 4103 +a 5183 5184 4103 +a 5184 5185 4103 +a 5185 5186 4103 +a 5186 5187 4103 +a 5187 5188 4103 +a 5188 5189 4103 +a 5189 5190 4103 +a 5190 5191 4103 +a 5191 5192 4103 +a 5192 5193 4103 +a 5193 5194 4103 +a 5194 5195 4103 +a 5195 5196 4103 +a 5196 5197 4103 +a 5197 5198 4103 +a 5198 5199 4103 +a 5199 5200 4103 +a 5200 5201 4103 +a 5201 5202 4103 +a 5202 5203 4103 +a 5203 5204 4103 +a 5204 5205 4103 +a 5205 5206 4103 +a 5206 5207 4103 +a 5207 5208 4103 +a 5208 5209 4103 +a 5209 5210 4103 +a 5210 5211 4103 +a 5211 5212 4103 +a 5212 5213 4103 +a 5213 5214 4103 +a 5214 5215 4103 +a 5215 5216 4103 +a 5216 5217 4103 +a 5217 5218 4103 +a 5218 5219 4103 +a 5219 5220 4103 +a 5220 5221 4103 +a 5221 5222 4103 +a 5222 5223 4103 +a 5223 5224 4103 +a 5224 5225 4103 +a 5225 5226 4103 +a 5226 5227 4103 +a 5227 5228 4103 +a 5228 5229 4103 +a 5229 5230 4103 +a 5230 5231 4103 +a 5231 5232 4103 +a 5232 5233 4103 +a 5233 5234 4103 +a 5234 5235 4103 +a 5235 5236 4103 +a 5236 5237 4103 +a 5237 5238 4103 +a 5238 5239 4103 +a 5239 5240 4103 +a 5240 5241 4103 +a 5241 5242 4103 +a 5242 5243 4103 +a 5243 5244 4103 +a 5244 5245 4103 +a 5245 5246 4103 +a 5246 5247 4103 +a 5247 5248 4103 +a 5248 5249 4103 +a 5249 5250 4103 +a 5250 5251 4103 +a 5251 5252 4103 +a 5252 5253 4103 +a 5253 5254 4103 +a 5254 5255 4103 +a 5255 5256 4103 +a 5256 5257 4103 +a 5257 5258 4103 +a 5258 5259 4103 +a 5259 5260 4103 +a 5260 5261 4103 +a 5261 5262 4103 +a 5262 5263 4103 +a 5263 5264 4103 +a 5264 5265 4103 +a 5265 5266 4103 +a 5266 5267 4103 +a 5267 5268 4103 +a 5268 5269 4103 +a 5269 5270 4103 +a 5270 5271 4103 +a 5271 5272 4103 +a 5272 5273 4103 +a 5273 5274 4103 +a 5274 5275 4103 +a 5275 5276 4103 +a 5276 5277 4103 +a 5277 5278 4103 +a 5278 5279 4103 +a 5279 5280 4103 +a 5280 5281 4103 +a 5281 5282 4103 +a 5282 5283 4103 +a 5283 5284 4103 +a 5284 5285 4103 +a 5285 5286 4103 +a 5286 5287 4103 +a 5287 5288 4103 +a 5288 5289 4103 +a 5289 5290 4103 +a 5290 5291 4103 +a 5291 5292 4103 +a 5292 5293 4103 +a 5293 5294 4103 +a 5294 5295 4103 +a 5295 5296 4103 +a 5296 5297 4103 +a 5297 5298 4103 +a 5298 5299 4103 +a 5299 5300 4103 +a 5300 5301 4103 +a 5301 5302 4103 +a 5302 5303 4103 +a 5303 5304 4103 +a 5304 5305 4103 +a 5305 5306 4103 +a 5306 5307 4103 +a 5307 5308 4103 +a 5308 5309 4103 +a 5309 5310 4103 +a 5310 5311 4103 +a 5311 5312 4103 +a 5312 5313 4103 +a 5313 5314 4103 +a 5314 5315 4103 +a 5315 5316 4103 +a 5316 5317 4103 +a 5317 5318 4103 +a 5318 5319 4103 +a 5319 5320 4103 +a 5320 5321 4103 +a 5321 5322 4103 +a 5322 5323 4103 +a 5323 5324 4103 +a 5324 5325 4103 +a 5325 5326 4103 +a 5326 5327 4103 +a 5327 5328 4103 +a 5328 5329 4103 +a 5329 5330 4103 +a 5330 5331 4103 +a 5331 5332 4103 +a 5332 5333 4103 +a 5333 5334 4103 +a 5334 5335 4103 +a 5335 5336 4103 +a 5336 5337 4103 +a 5337 5338 4103 +a 5338 5339 4103 +a 5339 5340 4103 +a 5340 5341 4103 +a 5341 5342 4103 +a 5342 5343 4103 +a 5343 5344 4103 +a 5344 5345 4103 +a 5345 5346 4103 +a 5346 5347 4103 +a 5347 5348 4103 +a 5348 5349 4103 +a 5349 5350 4103 +a 5350 5351 4103 +a 5351 5352 4103 +a 5352 5353 4103 +a 5353 5354 4103 +a 5354 5355 4103 +a 5355 5356 4103 +a 5356 5357 4103 +a 5357 5358 4103 +a 5358 5359 4103 +a 5359 5360 4103 +a 5360 5361 4103 +a 5361 5362 4103 +a 5362 5363 4103 +a 5363 5364 4103 +a 5364 5365 4103 +a 5365 5366 4103 +a 5366 5367 4103 +a 5367 5368 4103 +a 5368 5369 4103 +a 5369 5370 4103 +a 5370 5371 4103 +a 5371 5372 4103 +a 5372 5373 4103 +a 5373 5374 4103 +a 5374 5375 4103 +a 5375 5376 4103 +a 5376 5377 4103 +a 5377 5378 4103 +a 5378 5379 4103 +a 5379 5380 4103 +a 5380 5381 4103 +a 5381 5382 4103 +a 5382 5383 4103 +a 5383 5384 4103 +a 5384 5385 4103 +a 5385 5386 4103 +a 5386 5387 4103 +a 5387 5388 4103 +a 5388 5389 4103 +a 5389 5390 4103 +a 5390 5391 4103 +a 5391 5392 4103 +a 5392 5393 4103 +a 5393 5394 4103 +a 5394 5395 4103 +a 5395 5396 4103 +a 5396 5397 4103 +a 5397 5398 4103 +a 5398 5399 4103 +a 5399 5400 4103 +a 5400 5401 4103 +a 5401 5402 4103 +a 5402 5403 4103 +a 5403 5404 4103 +a 5404 5405 4103 +a 5405 5406 4103 +a 5406 5407 4103 +a 5407 5408 4103 +a 5408 5409 4103 +a 5409 5410 4103 +a 5410 5411 4103 +a 5411 5412 4103 +a 5412 5413 4103 +a 5413 5414 4103 +a 5414 5415 4103 +a 5415 5416 4103 +a 5416 5417 4103 +a 5417 5418 4103 +a 5418 5419 4103 +a 5419 5420 4103 +a 5420 5421 4103 +a 5421 5422 4103 +a 5422 5423 4103 +a 5423 5424 4103 +a 5424 5425 4103 +a 5425 5426 4103 +a 5426 5427 4103 +a 5427 5428 4103 +a 5428 5429 4103 +a 5429 5430 4103 +a 5430 5431 4103 +a 5431 5432 4103 +a 5432 5433 4103 +a 5433 5434 4103 +a 5434 5435 4103 +a 5435 5436 4103 +a 5436 5437 4103 +a 5437 5438 4103 +a 5438 5439 4103 +a 5439 5440 4103 +a 5440 5441 4103 +a 5441 5442 4103 +a 5442 5443 4103 +a 5443 5444 4103 +a 5444 5445 4103 +a 5445 5446 4103 +a 5446 5447 4103 +a 5447 5448 4103 +a 5448 5449 4103 +a 5449 5450 4103 +a 5450 5451 4103 +a 5451 5452 4103 +a 5452 5453 4103 +a 5453 5454 4103 +a 5454 5455 4103 +a 5455 5456 4103 +a 5456 5457 4103 +a 5457 5458 4103 +a 5458 5459 4103 +a 5459 5460 4103 +a 5460 5461 4103 +a 5461 5462 4103 +a 5462 5463 4103 +a 5463 5464 4103 +a 5464 5465 4103 +a 5465 5466 4103 +a 5466 5467 4103 +a 5467 5468 4103 +a 5468 5469 4103 +a 5469 5470 4103 +a 5470 5471 4103 +a 5471 5472 4103 +a 5472 5473 4103 +a 5473 5474 4103 +a 5474 5475 4103 +a 5475 5476 4103 +a 5476 5477 4103 +a 5477 5478 4103 +a 5478 5479 4103 +a 5479 5480 4103 +a 5480 5481 4103 +a 5481 5482 4103 +a 5482 5483 4103 +a 5483 5484 4103 +a 5484 5485 4103 +a 5485 5486 4103 +a 5486 5487 4103 +a 5487 5488 4103 +a 5488 5489 4103 +a 5489 5490 4103 +a 5490 5491 4103 +a 5491 5492 4103 +a 5492 5493 4103 +a 5493 5494 4103 +a 5494 5495 4103 +a 5495 5496 4103 +a 5496 5497 4103 +a 5497 5498 4103 +a 5498 5499 4103 +a 5499 5500 4103 +a 5500 5501 4103 +a 5501 5502 4103 +a 5502 5503 4103 +a 5503 5504 4103 +a 5504 5505 4103 +a 5505 5506 4103 +a 5506 5507 4103 +a 5507 5508 4103 +a 5508 5509 4103 +a 5509 5510 4103 +a 5510 5511 4103 +a 5511 5512 4103 +a 5512 5513 4103 +a 5513 5514 4103 +a 5514 5515 4103 +a 5515 5516 4103 +a 5516 5517 4103 +a 5517 5518 4103 +a 5518 5519 4103 +a 5519 5520 4103 +a 5520 5521 4103 +a 5521 5522 4103 +a 5522 5523 4103 +a 5523 5524 4103 +a 5524 5525 4103 +a 5525 5526 4103 +a 5526 5527 4103 +a 5527 5528 4103 +a 5528 5529 4103 +a 5529 5530 4103 +a 5530 5531 4103 +a 5531 5532 4103 +a 5532 5533 4103 +a 5533 5534 4103 +a 5534 5535 4103 +a 5535 5536 4103 +a 5536 5537 4103 +a 5537 5538 4103 +a 5538 5539 4103 +a 5539 5540 4103 +a 5540 5541 4103 +a 5541 5542 4103 +a 5542 5543 4103 +a 5543 5544 4103 +a 5544 5545 4103 +a 5545 5546 4103 +a 5546 5547 4103 +a 5547 5548 4103 +a 5548 5549 4103 +a 5549 5550 4103 +a 5550 5551 4103 +a 5551 5552 4103 +a 5552 5553 4103 +a 5553 5554 4103 +a 5554 5555 4103 +a 5555 5556 4103 +a 5556 5557 4103 +a 5557 5558 4103 +a 5558 5559 4103 +a 5559 5560 4103 +a 5560 5561 4103 +a 5561 5562 4103 +a 5562 5563 4103 +a 5563 5564 4103 +a 5564 5565 4103 +a 5565 5566 4103 +a 5566 5567 4103 +a 5567 5568 4103 +a 5568 5569 4103 +a 5569 5570 4103 +a 5570 5571 4103 +a 5571 5572 4103 +a 5572 5573 4103 +a 5573 5574 4103 +a 5574 5575 4103 +a 5575 5576 4103 +a 5576 5577 4103 +a 5577 5578 4103 +a 5578 5579 4103 +a 5579 5580 4103 +a 5580 5581 4103 +a 5581 5582 4103 +a 5582 5583 4103 +a 5583 5584 4103 +a 5584 5585 4103 +a 5585 5586 4103 +a 5586 5587 4103 +a 5587 5588 4103 +a 5588 5589 4103 +a 5589 5590 4103 +a 5590 5591 4103 +a 5591 5592 4103 +a 5592 5593 4103 +a 5593 5594 4103 +a 5594 5595 4103 +a 5595 5596 4103 +a 5596 5597 4103 +a 5597 5598 4103 +a 5598 5599 4103 +a 5599 5600 4103 +a 5600 5601 4103 +a 5601 5602 4103 +a 5602 5603 4103 +a 5603 5604 4103 +a 5604 5605 4103 +a 5605 5606 4103 +a 5606 5607 4103 +a 5607 5608 4103 +a 5608 5609 4103 +a 5609 5610 4103 +a 5610 5611 4103 +a 5611 5612 4103 +a 5612 5613 4103 +a 5613 5614 4103 +a 5614 5615 4103 +a 5615 5616 4103 +a 5616 5617 4103 +a 5617 5618 4103 +a 5618 5619 4103 +a 5619 5620 4103 +a 5620 5621 4103 +a 5621 5622 4103 +a 5622 5623 4103 +a 5623 5624 4103 +a 5624 5625 4103 +a 5625 5626 4103 +a 5626 5627 4103 +a 5627 5628 4103 +a 5628 5629 4103 +a 5629 5630 4103 +a 5630 5631 4103 +a 5631 5632 4103 +a 5632 5633 4103 +a 5633 5634 4103 +a 5634 5635 4103 +a 5635 5636 4103 +a 5636 5637 4103 +a 5637 5638 4103 +a 5638 5639 4103 +a 5639 5640 4103 +a 5640 5641 4103 +a 5641 5642 4103 +a 5642 5643 4103 +a 5643 5644 4103 +a 5644 5645 4103 +a 5645 5646 4103 +a 5646 5647 4103 +a 5647 5648 4103 +a 5648 5649 4103 +a 5649 5650 4103 +a 5650 5651 4103 +a 5651 5652 4103 +a 5652 5653 4103 +a 5653 5654 4103 +a 5654 5655 4103 +a 5655 5656 4103 +a 5656 5657 4103 +a 5657 5658 4103 +a 5658 5659 4103 +a 5659 5660 4103 +a 5660 5661 4103 +a 5661 5662 4103 +a 5662 5663 4103 +a 5663 5664 4103 +a 5664 5665 4103 +a 5665 5666 4103 +a 5666 5667 4103 +a 5667 5668 4103 +a 5668 5669 4103 +a 5669 5670 4103 +a 5670 5671 4103 +a 5671 5672 4103 +a 5672 5673 4103 +a 5673 5674 4103 +a 5674 5675 4103 +a 5675 5676 4103 +a 5676 5677 4103 +a 5677 5678 4103 +a 5678 5679 4103 +a 5679 5680 4103 +a 5680 5681 4103 +a 5681 5682 4103 +a 5682 5683 4103 +a 5683 5684 4103 +a 5684 5685 4103 +a 5685 5686 4103 +a 5686 5687 4103 +a 5687 5688 4103 +a 5688 5689 4103 +a 5689 5690 4103 +a 5690 5691 4103 +a 5691 5692 4103 +a 5692 5693 4103 +a 5693 5694 4103 +a 5694 5695 4103 +a 5695 5696 4103 +a 5696 5697 4103 +a 5697 5698 4103 +a 5698 5699 4103 +a 5699 5700 4103 +a 5700 5701 4103 +a 5701 5702 4103 +a 5702 5703 4103 +a 5703 5704 4103 +a 5704 5705 4103 +a 5705 5706 4103 +a 5706 5707 4103 +a 5707 5708 4103 +a 5708 5709 4103 +a 5709 5710 4103 +a 5710 5711 4103 +a 5711 5712 4103 +a 5712 5713 4103 +a 5713 5714 4103 +a 5714 5715 4103 +a 5715 5716 4103 +a 5716 5717 4103 +a 5717 5718 4103 +a 5718 5719 4103 +a 5719 5720 4103 +a 5720 5721 4103 +a 5721 5722 4103 +a 5722 5723 4103 +a 5723 5724 4103 +a 5724 5725 4103 +a 5725 5726 4103 +a 5726 5727 4103 +a 5727 5728 4103 +a 5728 5729 4103 +a 5729 5730 4103 +a 5730 5731 4103 +a 5731 5732 4103 +a 5732 5733 4103 +a 5733 5734 4103 +a 5734 5735 4103 +a 5735 5736 4103 +a 5736 5737 4103 +a 5737 5738 4103 +a 5738 5739 4103 +a 5739 5740 4103 +a 5740 5741 4103 +a 5741 5742 4103 +a 5742 5743 4103 +a 5743 5744 4103 +a 5744 5745 4103 +a 5745 5746 4103 +a 5746 5747 4103 +a 5747 5748 4103 +a 5748 5749 4103 +a 5749 5750 4103 +a 5750 5751 4103 +a 5751 5752 4103 +a 5752 5753 4103 +a 5753 5754 4103 +a 5754 5755 4103 +a 5755 5756 4103 +a 5756 5757 4103 +a 5757 5758 4103 +a 5758 5759 4103 +a 5759 5760 4103 +a 5760 5761 4103 +a 5761 5762 4103 +a 5762 5763 4103 +a 5763 5764 4103 +a 5764 5765 4103 +a 5765 5766 4103 +a 5766 5767 4103 +a 5767 5768 4103 +a 5768 5769 4103 +a 5769 5770 4103 +a 5770 5771 4103 +a 5771 5772 4103 +a 5772 5773 4103 +a 5773 5774 4103 +a 5774 5775 4103 +a 5775 5776 4103 +a 5776 5777 4103 +a 5777 5778 4103 +a 5778 5779 4103 +a 5779 5780 4103 +a 5780 5781 4103 +a 5781 5782 4103 +a 5782 5783 4103 +a 5783 5784 4103 +a 5784 5785 4103 +a 5785 5786 4103 +a 5786 5787 4103 +a 5787 5788 4103 +a 5788 5789 4103 +a 5789 5790 4103 +a 5790 5791 4103 +a 5791 5792 4103 +a 5792 5793 4103 +a 5793 5794 4103 +a 5794 5795 4103 +a 5795 5796 4103 +a 5796 5797 4103 +a 5797 5798 4103 +a 5798 5799 4103 +a 5799 5800 4103 +a 5800 5801 4103 +a 5801 5802 4103 +a 5802 5803 4103 +a 5803 5804 4103 +a 5804 5805 4103 +a 5805 5806 4103 +a 5806 5807 4103 +a 5807 5808 4103 +a 5808 5809 4103 +a 5809 5810 4103 +a 5810 5811 4103 +a 5811 5812 4103 +a 5812 5813 4103 +a 5813 5814 4103 +a 5814 5815 4103 +a 5815 5816 4103 +a 5816 5817 4103 +a 5817 5818 4103 +a 5818 5819 4103 +a 5819 5820 4103 +a 5820 5821 4103 +a 5821 5822 4103 +a 5822 5823 4103 +a 5823 5824 4103 +a 5824 5825 4103 +a 5825 5826 4103 +a 5826 5827 4103 +a 5827 5828 4103 +a 5828 5829 4103 +a 5829 5830 4103 +a 5830 5831 4103 +a 5831 5832 4103 +a 5832 5833 4103 +a 5833 5834 4103 +a 5834 5835 4103 +a 5835 5836 4103 +a 5836 5837 4103 +a 5837 5838 4103 +a 5838 5839 4103 +a 5839 5840 4103 +a 5840 5841 4103 +a 5841 5842 4103 +a 5842 5843 4103 +a 5843 5844 4103 +a 5844 5845 4103 +a 5845 5846 4103 +a 5846 5847 4103 +a 5847 5848 4103 +a 5848 5849 4103 +a 5849 5850 4103 +a 5850 5851 4103 +a 5851 5852 4103 +a 5852 5853 4103 +a 5853 5854 4103 +a 5854 5855 4103 +a 5855 5856 4103 +a 5856 5857 4103 +a 5857 5858 4103 +a 5858 5859 4103 +a 5859 5860 4103 +a 5860 5861 4103 +a 5861 5862 4103 +a 5862 5863 4103 +a 5863 5864 4103 +a 5864 5865 4103 +a 5865 5866 4103 +a 5866 5867 4103 +a 5867 5868 4103 +a 5868 5869 4103 +a 5869 5870 4103 +a 5870 5871 4103 +a 5871 5872 4103 +a 5872 5873 4103 +a 5873 5874 4103 +a 5874 5875 4103 +a 5875 5876 4103 +a 5876 5877 4103 +a 5877 5878 4103 +a 5878 5879 4103 +a 5879 5880 4103 +a 5880 5881 4103 +a 5881 5882 4103 +a 5882 5883 4103 +a 5883 5884 4103 +a 5884 5885 4103 +a 5885 5886 4103 +a 5886 5887 4103 +a 5887 5888 4103 +a 5888 5889 4103 +a 5889 5890 4103 +a 5890 5891 4103 +a 5891 5892 4103 +a 5892 5893 4103 +a 5893 5894 4103 +a 5894 5895 4103 +a 5895 5896 4103 +a 5896 5897 4103 +a 5897 5898 4103 +a 5898 5899 4103 +a 5899 5900 4103 +a 5900 5901 4103 +a 5901 5902 4103 +a 5902 5903 4103 +a 5903 5904 4103 +a 5904 5905 4103 +a 5905 5906 4103 +a 5906 5907 4103 +a 5907 5908 4103 +a 5908 5909 4103 +a 5909 5910 4103 +a 5910 5911 4103 +a 5911 5912 4103 +a 5912 5913 4103 +a 5913 5914 4103 +a 5914 5915 4103 +a 5915 5916 4103 +a 5916 5917 4103 +a 5917 5918 4103 +a 5918 5919 4103 +a 5919 5920 4103 +a 5920 5921 4103 +a 5921 5922 4103 +a 5922 5923 4103 +a 5923 5924 4103 +a 5924 5925 4103 +a 5925 5926 4103 +a 5926 5927 4103 +a 5927 5928 4103 +a 5928 5929 4103 +a 5929 5930 4103 +a 5930 5931 4103 +a 5931 5932 4103 +a 5932 5933 4103 +a 5933 5934 4103 +a 5934 5935 4103 +a 5935 5936 4103 +a 5936 5937 4103 +a 5937 5938 4103 +a 5938 5939 4103 +a 5939 5940 4103 +a 5940 5941 4103 +a 5941 5942 4103 +a 5942 5943 4103 +a 5943 5944 4103 +a 5944 5945 4103 +a 5945 5946 4103 +a 5946 5947 4103 +a 5947 5948 4103 +a 5948 5949 4103 +a 5949 5950 4103 +a 5950 5951 4103 +a 5951 5952 4103 +a 5952 5953 4103 +a 5953 5954 4103 +a 5954 5955 4103 +a 5955 5956 4103 +a 5956 5957 4103 +a 5957 5958 4103 +a 5958 5959 4103 +a 5959 5960 4103 +a 5960 5961 4103 +a 5961 5962 4103 +a 5962 5963 4103 +a 5963 5964 4103 +a 5964 5965 4103 +a 5965 5966 4103 +a 5966 5967 4103 +a 5967 5968 4103 +a 5968 5969 4103 +a 5969 5970 4103 +a 5970 5971 4103 +a 5971 5972 4103 +a 5972 5973 4103 +a 5973 5974 4103 +a 5974 5975 4103 +a 5975 5976 4103 +a 5976 5977 4103 +a 5977 5978 4103 +a 5978 5979 4103 +a 5979 5980 4103 +a 5980 5981 4103 +a 5981 5982 4103 +a 5982 5983 4103 +a 5983 5984 4103 +a 5984 5985 4103 +a 5985 5986 4103 +a 5986 5987 4103 +a 5987 5988 4103 +a 5988 5989 4103 +a 5989 5990 4103 +a 5990 5991 4103 +a 5991 5992 4103 +a 5992 5993 4103 +a 5993 5994 4103 +a 5994 5995 4103 +a 5995 5996 4103 +a 5996 5997 4103 +a 5997 5998 4103 +a 5998 5999 4103 +a 5999 6000 4103 +a 6000 6001 4103 +a 6001 6002 4103 +a 6002 6003 4103 +a 6003 6004 4103 +a 6004 6005 4103 +a 6005 6006 4103 +a 6006 6007 4103 +a 6007 6008 4103 +a 6008 6009 4103 +a 6009 6010 4103 +a 6010 6011 4103 +a 6011 6012 4103 +a 6012 6013 4103 +a 6013 6014 4103 +a 6014 6015 4103 +a 6015 6016 4103 +a 6016 6017 4103 +a 6017 6018 4103 +a 6018 6019 4103 +a 6019 6020 4103 +a 6020 6021 4103 +a 6021 6022 4103 +a 6022 6023 4103 +a 6023 6024 4103 +a 6024 6025 4103 +a 6025 6026 4103 +a 6026 6027 4103 +a 6027 6028 4103 +a 6028 6029 4103 +a 6029 6030 4103 +a 6030 6031 4103 +a 6031 6032 4103 +a 6032 6033 4103 +a 6033 6034 4103 +a 6034 6035 4103 +a 6035 6036 4103 +a 6036 6037 4103 +a 6037 6038 4103 +a 6038 6039 4103 +a 6039 6040 4103 +a 6040 6041 4103 +a 6041 6042 4103 +a 6042 6043 4103 +a 6043 6044 4103 +a 6044 6045 4103 +a 6045 6046 4103 +a 6046 6047 4103 +a 6047 6048 4103 +a 6048 6049 4103 +a 6049 6050 4103 +a 6050 6051 4103 +a 6051 6052 4103 +a 6052 6053 4103 +a 6053 6054 4103 +a 6054 6055 4103 +a 6055 6056 4103 +a 6056 6057 4103 +a 6057 6058 4103 +a 6058 6059 4103 +a 6059 6060 4103 +a 6060 6061 4103 +a 6061 6062 4103 +a 6062 6063 4103 +a 6063 6064 4103 +a 6064 6065 4103 +a 6065 6066 4103 +a 6066 6067 4103 +a 6067 6068 4103 +a 6068 6069 4103 +a 6069 6070 4103 +a 6070 6071 4103 +a 6071 6072 4103 +a 6072 6073 4103 +a 6073 6074 4103 +a 6074 6075 4103 +a 6075 6076 4103 +a 6076 6077 4103 +a 6077 6078 4103 +a 6078 6079 4103 +a 6079 6080 4103 +a 6080 6081 4103 +a 6081 6082 4103 +a 6082 6083 4103 +a 6083 6084 4103 +a 6084 6085 4103 +a 6085 6086 4103 +a 6086 6087 4103 +a 6087 6088 4103 +a 6088 6089 4103 +a 6089 6090 4103 +a 6090 6091 4103 +a 6091 6092 4103 +a 6092 6093 4103 +a 6093 6094 4103 +a 6094 6095 4103 +a 6095 6096 4103 +a 6096 6097 4103 +a 6097 6098 4103 +a 6098 6099 4103 +a 6099 6100 4103 +a 6100 6101 4103 +a 6101 6102 4103 +a 6102 6103 4103 +a 6103 6104 4103 +a 6104 6105 4103 +a 6105 6106 4103 +a 6106 6107 4103 +a 6107 6108 4103 +a 6108 6109 4103 +a 6109 6110 4103 +a 6110 6111 4103 +a 6111 6112 4103 +a 6112 6113 4103 +a 6113 6114 4103 +a 6114 6115 4103 +a 6115 6116 4103 +a 6116 6117 4103 +a 6117 6118 4103 +a 6118 6119 4103 +a 6119 6120 4103 +a 6120 6121 4103 +a 6121 6122 4103 +a 6122 6123 4103 +a 6123 6124 4103 +a 6124 6125 4103 +a 6125 6126 4103 +a 6126 6127 4103 +a 6127 6128 4103 +a 6128 6129 4103 +a 6129 6130 4103 +a 6130 6131 4103 +a 6131 6132 4103 +a 6132 6133 4103 +a 6133 6134 4103 +a 6134 6135 4103 +a 6135 6136 4103 +a 6136 6137 4103 +a 6137 6138 4103 +a 6138 6139 4103 +a 6139 6140 4103 +a 6140 6141 4103 +a 6141 6142 4103 +a 6142 6143 4103 +a 6143 6144 4103 +a 6144 6145 4103 +a 6145 6146 4103 +a 6146 6147 4103 +a 6147 6148 4103 +a 6148 6149 4103 +a 6149 6150 4103 +a 6150 6151 4103 +a 6151 6152 4103 +a 6152 6153 4103 +a 6153 6154 4103 +a 6154 6155 4103 +a 6155 6156 4103 +a 6156 6157 4103 +a 6157 6158 4103 +a 6158 6159 4103 +a 6159 6160 4103 +a 6160 6161 4103 +a 6161 6162 4103 +a 6162 6163 4103 +a 6163 6164 4103 +a 6164 6165 4103 +a 6165 6166 4103 +a 6166 6167 4103 +a 6167 6168 4103 +a 6168 6169 4103 +a 6169 6170 4103 +a 6170 6171 4103 +a 6171 6172 4103 +a 6172 6173 4103 +a 6173 6174 4103 +a 6174 6175 4103 +a 6175 6176 4103 +a 6176 6177 4103 +a 6177 6178 4103 +a 6178 6179 4103 +a 6179 6180 4103 +a 6180 6181 4103 +a 6181 6182 4103 +a 6182 6183 4103 +a 6183 6184 4103 +a 6184 6185 4103 +a 6185 6186 4103 +a 6186 6187 4103 +a 6187 6188 4103 +a 6188 6189 4103 +a 6189 6190 4103 +a 6190 6191 4103 +a 6191 6192 4103 +a 6192 6193 4103 +a 6193 6194 4103 +a 6194 6195 4103 +a 6195 6196 4103 +a 6196 6197 4103 +a 6197 6198 4103 +a 6198 6199 4103 +a 6199 6200 4103 +a 6200 6201 4103 +a 6201 6202 4103 +a 6202 6203 4103 +a 6203 6204 4103 +a 6204 6205 4103 +a 6205 6206 4103 +a 6206 6207 4103 +a 6207 6208 4103 +a 6208 6209 4103 +a 6209 6210 4103 +a 6210 6211 4103 +a 6211 6212 4103 +a 6212 6213 4103 +a 6213 6214 4103 +a 6214 6215 4103 +a 6215 6216 4103 +a 6216 6217 4103 +a 6217 6218 4103 +a 6218 6219 4103 +a 6219 6220 4103 +a 6220 6221 4103 +a 6221 6222 4103 +a 6222 6223 4103 +a 6223 6224 4103 +a 6224 6225 4103 +a 6225 6226 4103 +a 6226 6227 4103 +a 6227 6228 4103 +a 6228 6229 4103 +a 6229 6230 4103 +a 6230 6231 4103 +a 6231 6232 4103 +a 6232 6233 4103 +a 6233 6234 4103 +a 6234 6235 4103 +a 6235 6236 4103 +a 6236 6237 4103 +a 6237 6238 4103 +a 6238 6239 4103 +a 6239 6240 4103 +a 6240 6241 4103 +a 6241 6242 4103 +a 6242 6243 4103 +a 6243 6244 4103 +a 6244 6245 4103 +a 6245 6246 4103 +a 6246 6247 4103 +a 6247 6248 4103 +a 6248 6249 4103 +a 6249 6250 4103 +a 6250 6251 4103 +a 6251 6252 4103 +a 6252 6253 4103 +a 6253 6254 4103 +a 6254 6255 4103 +a 6255 6256 4103 +a 6256 6257 4103 +a 6257 6258 4103 +a 6258 6259 4103 +a 6259 6260 4103 +a 6260 6261 4103 +a 6261 6262 4103 +a 6262 6263 4103 +a 6263 6264 4103 +a 6264 6265 4103 +a 6265 6266 4103 +a 6266 6267 4103 +a 6267 6268 4103 +a 6268 6269 4103 +a 6269 6270 4103 +a 6270 6271 4103 +a 6271 6272 4103 +a 6272 6273 4103 +a 6273 6274 4103 +a 6274 6275 4103 +a 6275 6276 4103 +a 6276 6277 4103 +a 6277 6278 4103 +a 6278 6279 4103 +a 6279 6280 4103 +a 6280 6281 4103 +a 6281 6282 4103 +a 6282 6283 4103 +a 6283 6284 4103 +a 6284 6285 4103 +a 6285 6286 4103 +a 6286 6287 4103 +a 6287 6288 4103 +a 6288 6289 4103 +a 6289 6290 4103 +a 6290 6291 4103 +a 6291 6292 4103 +a 6292 6293 4103 +a 6293 6294 4103 +a 6294 6295 4103 +a 6295 6296 4103 +a 6296 6297 4103 +a 6297 6298 4103 +a 6298 6299 4103 +a 6299 6300 4103 +a 6300 6301 4103 +a 6301 6302 4103 +a 6302 6303 4103 +a 6303 6304 4103 +a 6304 6305 4103 +a 6305 6306 4103 +a 6306 6307 4103 +a 6307 6308 4103 +a 6308 6309 4103 +a 6309 6310 4103 +a 6310 6311 4103 +a 6311 6312 4103 +a 6312 6313 4103 +a 6313 6314 4103 +a 6314 6315 4103 +a 6315 6316 4103 +a 6316 6317 4103 +a 6317 6318 4103 +a 6318 6319 4103 +a 6319 6320 4103 +a 6320 6321 4103 +a 6321 6322 4103 +a 6322 6323 4103 +a 6323 6324 4103 +a 6324 6325 4103 +a 6325 6326 4103 +a 6326 6327 4103 +a 6327 6328 4103 +a 6328 6329 4103 +a 6329 6330 4103 +a 6330 6331 4103 +a 6331 6332 4103 +a 6332 6333 4103 +a 6333 6334 4103 +a 6334 6335 4103 +a 6335 6336 4103 +a 6336 6337 4103 +a 6337 6338 4103 +a 6338 6339 4103 +a 6339 6340 4103 +a 6340 6341 4103 +a 6341 6342 4103 +a 6342 6343 4103 +a 6343 6344 4103 +a 6344 6345 4103 +a 6345 6346 4103 +a 6346 6347 4103 +a 6347 6348 4103 +a 6348 6349 4103 +a 6349 6350 4103 +a 6350 6351 4103 +a 6351 6352 4103 +a 6352 6353 4103 +a 6353 6354 4103 +a 6354 6355 4103 +a 6355 6356 4103 +a 6356 6357 4103 +a 6357 6358 4103 +a 6358 6359 4103 +a 6359 6360 4103 +a 6360 6361 4103 +a 6361 6362 4103 +a 6362 6363 4103 +a 6363 6364 4103 +a 6364 6365 4103 +a 6365 6366 4103 +a 6366 6367 4103 +a 6367 6368 4103 +a 6368 6369 4103 +a 6369 6370 4103 +a 6370 6371 4103 +a 6371 6372 4103 +a 6372 6373 4103 +a 6373 6374 4103 +a 6374 6375 4103 +a 6375 6376 4103 +a 6376 6377 4103 +a 6377 6378 4103 +a 6378 6379 4103 +a 6379 6380 4103 +a 6380 6381 4103 +a 6381 6382 4103 +a 6382 6383 4103 +a 6383 6384 4103 +a 6384 6385 4103 +a 6385 6386 4103 +a 6386 6387 4103 +a 6387 6388 4103 +a 6388 6389 4103 +a 6389 6390 4103 +a 6390 6391 4103 +a 6391 6392 4103 +a 6392 6393 4103 +a 6393 6394 4103 +a 6394 6395 4103 +a 6395 6396 4103 +a 6396 6397 4103 +a 6397 6398 4103 +a 6398 6399 4103 +a 6399 6400 4103 +a 6400 6401 4103 +a 6401 6402 4103 +a 6402 6403 4103 +a 6403 6404 4103 +a 6404 6405 4103 +a 6405 6406 4103 +a 6406 6407 4103 +a 6407 6408 4103 +a 6408 6409 4103 +a 6409 6410 4103 +a 6410 6411 4103 +a 6411 6412 4103 +a 6412 6413 4103 +a 6413 6414 4103 +a 6414 6415 4103 +a 6415 6416 4103 +a 6416 6417 4103 +a 6417 6418 4103 +a 6418 6419 4103 +a 6419 6420 4103 +a 6420 6421 4103 +a 6421 6422 4103 +a 6422 6423 4103 +a 6423 6424 4103 +a 6424 6425 4103 +a 6425 6426 4103 +a 6426 6427 4103 +a 6427 6428 4103 +a 6428 6429 4103 +a 6429 6430 4103 +a 6430 6431 4103 +a 6431 6432 4103 +a 6432 6433 4103 +a 6433 6434 4103 +a 6434 6435 4103 +a 6435 6436 4103 +a 6436 6437 4103 +a 6437 6438 4103 +a 6438 6439 4103 +a 6439 6440 4103 +a 6440 6441 4103 +a 6441 6442 4103 +a 6442 6443 4103 +a 6443 6444 4103 +a 6444 6445 4103 +a 6445 6446 4103 +a 6446 6447 4103 +a 6447 6448 4103 +a 6448 6449 4103 +a 6449 6450 4103 +a 6450 6451 4103 +a 6451 6452 4103 +a 6452 6453 4103 +a 6453 6454 4103 +a 6454 6455 4103 +a 6455 6456 4103 +a 6456 6457 4103 +a 6457 6458 4103 +a 6458 6459 4103 +a 6459 6460 4103 +a 6460 6461 4103 +a 6461 6462 4103 +a 6462 6463 4103 +a 6463 6464 4103 +a 6464 6465 4103 +a 6465 6466 4103 +a 6466 6467 4103 +a 6467 6468 4103 +a 6468 6469 4103 +a 6469 6470 4103 +a 6470 6471 4103 +a 6471 6472 4103 +a 6472 6473 4103 +a 6473 6474 4103 +a 6474 6475 4103 +a 6475 6476 4103 +a 6476 6477 4103 +a 6477 6478 4103 +a 6478 6479 4103 +a 6479 6480 4103 +a 6480 6481 4103 +a 6481 6482 4103 +a 6482 6483 4103 +a 6483 6484 4103 +a 6484 6485 4103 +a 6485 6486 4103 +a 6486 6487 4103 +a 6487 6488 4103 +a 6488 6489 4103 +a 6489 6490 4103 +a 6490 6491 4103 +a 6491 6492 4103 +a 6492 6493 4103 +a 6493 6494 4103 +a 6494 6495 4103 +a 6495 6496 4103 +a 6496 6497 4103 +a 6497 6498 4103 +a 6498 6499 4103 +a 6499 6500 4103 +a 6500 6501 4103 +a 6501 6502 4103 +a 6502 6503 4103 +a 6503 6504 4103 +a 6504 6505 4103 +a 6505 6506 4103 +a 6506 6507 4103 +a 6507 6508 4103 +a 6508 6509 4103 +a 6509 6510 4103 +a 6510 6511 4103 +a 6511 6512 4103 +a 6512 6513 4103 +a 6513 6514 4103 +a 6514 6515 4103 +a 6515 6516 4103 +a 6516 6517 4103 +a 6517 6518 4103 +a 6518 6519 4103 +a 6519 6520 4103 +a 6520 6521 4103 +a 6521 6522 4103 +a 6522 6523 4103 +a 6523 6524 4103 +a 6524 6525 4103 +a 6525 6526 4103 +a 6526 6527 4103 +a 6527 6528 4103 +a 6528 6529 4103 +a 6529 6530 4103 +a 6530 6531 4103 +a 6531 6532 4103 +a 6532 6533 4103 +a 6533 6534 4103 +a 6534 6535 4103 +a 6535 6536 4103 +a 6536 6537 4103 +a 6537 6538 4103 +a 6538 6539 4103 +a 6539 6540 4103 +a 6540 6541 4103 +a 6541 6542 4103 +a 6542 6543 4103 +a 6543 6544 4103 +a 6544 6545 4103 +a 6545 6546 4103 +a 6546 6547 4103 +a 6547 6548 4103 +a 6548 6549 4103 +a 6549 6550 4103 +a 6550 6551 4103 +a 6551 6552 4103 +a 6552 6553 4103 +a 6553 6554 4103 +a 6554 6555 4103 +a 6555 6556 4103 +a 6556 6557 4103 +a 6557 6558 4103 +a 6558 6559 4103 +a 6559 6560 4103 +a 6560 6561 4103 +a 6561 6562 4103 +a 6562 6563 4103 +a 6563 6564 4103 +a 6564 6565 4103 +a 6565 6566 4103 +a 6566 6567 4103 +a 6567 6568 4103 +a 6568 6569 4103 +a 6569 6570 4103 +a 6570 6571 4103 +a 6571 6572 4103 +a 6572 6573 4103 +a 6573 6574 4103 +a 6574 6575 4103 +a 6575 6576 4103 +a 6576 6577 4103 +a 6577 6578 4103 +a 6578 6579 4103 +a 6579 6580 4103 +a 6580 6581 4103 +a 6581 6582 4103 +a 6582 6583 4103 +a 6583 6584 4103 +a 6584 6585 4103 +a 6585 6586 4103 +a 6586 6587 4103 +a 6587 6588 4103 +a 6588 6589 4103 +a 6589 6590 4103 +a 6590 6591 4103 +a 6591 6592 4103 +a 6592 6593 4103 +a 6593 6594 4103 +a 6594 6595 4103 +a 6595 6596 4103 +a 6596 6597 4103 +a 6597 6598 4103 +a 6598 6599 4103 +a 6599 6600 4103 +a 6600 6601 4103 +a 6601 6602 4103 +a 6602 6603 4103 +a 6603 6604 4103 +a 6604 6605 4103 +a 6605 6606 4103 +a 6606 6607 4103 +a 6607 6608 4103 +a 6608 6609 4103 +a 6609 6610 4103 +a 6610 6611 4103 +a 6611 6612 4103 +a 6612 6613 4103 +a 6613 6614 4103 +a 6614 6615 4103 +a 6615 6616 4103 +a 6616 6617 4103 +a 6617 6618 4103 +a 6618 6619 4103 +a 6619 6620 4103 +a 6620 6621 4103 +a 6621 6622 4103 +a 6622 6623 4103 +a 6623 6624 4103 +a 6624 6625 4103 +a 6625 6626 4103 +a 6626 6627 4103 +a 6627 6628 4103 +a 6628 6629 4103 +a 6629 6630 4103 +a 6630 6631 4103 +a 6631 6632 4103 +a 6632 6633 4103 +a 6633 6634 4103 +a 6634 6635 4103 +a 6635 6636 4103 +a 6636 6637 4103 +a 6637 6638 4103 +a 6638 6639 4103 +a 6639 6640 4103 +a 6640 6641 4103 +a 6641 6642 4103 +a 6642 6643 4103 +a 6643 6644 4103 +a 6644 6645 4103 +a 6645 6646 4103 +a 6646 6647 4103 +a 6647 6648 4103 +a 6648 6649 4103 +a 6649 6650 4103 +a 6650 6651 4103 +a 6651 6652 4103 +a 6652 6653 4103 +a 6653 6654 4103 +a 6654 6655 4103 +a 6655 6656 4103 +a 6656 6657 4103 +a 6657 6658 4103 +a 6658 6659 4103 +a 6659 6660 4103 +a 6660 6661 4103 +a 6661 6662 4103 +a 6662 6663 4103 +a 6663 6664 4103 +a 6664 6665 4103 +a 6665 6666 4103 +a 6666 6667 4103 +a 6667 6668 4103 +a 6668 6669 4103 +a 6669 6670 4103 +a 6670 6671 4103 +a 6671 6672 4103 +a 6672 6673 4103 +a 6673 6674 4103 +a 6674 6675 4103 +a 6675 6676 4103 +a 6676 6677 4103 +a 6677 6678 4103 +a 6678 6679 4103 +a 6679 6680 4103 +a 6680 6681 4103 +a 6681 6682 4103 +a 6682 6683 4103 +a 6683 6684 4103 +a 6684 6685 4103 +a 6685 6686 4103 +a 6686 6687 4103 +a 6687 6688 4103 +a 6688 6689 4103 +a 6689 6690 4103 +a 6690 6691 4103 +a 6691 6692 4103 +a 6692 6693 4103 +a 6693 6694 4103 +a 6694 6695 4103 +a 6695 6696 4103 +a 6696 6697 4103 +a 6697 6698 4103 +a 6698 6699 4103 +a 6699 6700 4103 +a 6700 6701 4103 +a 6701 6702 4103 +a 6702 6703 4103 +a 6703 6704 4103 +a 6704 6705 4103 +a 6705 6706 4103 +a 6706 6707 4103 +a 6707 6708 4103 +a 6708 6709 4103 +a 6709 6710 4103 +a 6710 6711 4103 +a 6711 6712 4103 +a 6712 6713 4103 +a 6713 6714 4103 +a 6714 6715 4103 +a 6715 6716 4103 +a 6716 6717 4103 +a 6717 6718 4103 +a 6718 6719 4103 +a 6719 6720 4103 +a 6720 6721 4103 +a 6721 6722 4103 +a 6722 6723 4103 +a 6723 6724 4103 +a 6724 6725 4103 +a 6725 6726 4103 +a 6726 6727 4103 +a 6727 6728 4103 +a 6728 6729 4103 +a 6729 6730 4103 +a 6730 6731 4103 +a 6731 6732 4103 +a 6732 6733 4103 +a 6733 6734 4103 +a 6734 6735 4103 +a 6735 6736 4103 +a 6736 6737 4103 +a 6737 6738 4103 +a 6738 6739 4103 +a 6739 6740 4103 +a 6740 6741 4103 +a 6741 6742 4103 +a 6742 6743 4103 +a 6743 6744 4103 +a 6744 6745 4103 +a 6745 6746 4103 +a 6746 6747 4103 +a 6747 6748 4103 +a 6748 6749 4103 +a 6749 6750 4103 +a 6750 6751 4103 +a 6751 6752 4103 +a 6752 6753 4103 +a 6753 6754 4103 +a 6754 6755 4103 +a 6755 6756 4103 +a 6756 6757 4103 +a 6757 6758 4103 +a 6758 6759 4103 +a 6759 6760 4103 +a 6760 6761 4103 +a 6761 6762 4103 +a 6762 6763 4103 +a 6763 6764 4103 +a 6764 6765 4103 +a 6765 6766 4103 +a 6766 6767 4103 +a 6767 6768 4103 +a 6768 6769 4103 +a 6769 6770 4103 +a 6770 6771 4103 +a 6771 6772 4103 +a 6772 6773 4103 +a 6773 6774 4103 +a 6774 6775 4103 +a 6775 6776 4103 +a 6776 6777 4103 +a 6777 6778 4103 +a 6778 6779 4103 +a 6779 6780 4103 +a 6780 6781 4103 +a 6781 6782 4103 +a 6782 6783 4103 +a 6783 6784 4103 +a 6784 6785 4103 +a 6785 6786 4103 +a 6786 6787 4103 +a 6787 6788 4103 +a 6788 6789 4103 +a 6789 6790 4103 +a 6790 6791 4103 +a 6791 6792 4103 +a 6792 6793 4103 +a 6793 6794 4103 +a 6794 6795 4103 +a 6795 6796 4103 +a 6796 6797 4103 +a 6797 6798 4103 +a 6798 6799 4103 +a 6799 6800 4103 +a 6800 6801 4103 +a 6801 6802 4103 +a 6802 6803 4103 +a 6803 6804 4103 +a 6804 6805 4103 +a 6805 6806 4103 +a 6806 6807 4103 +a 6807 6808 4103 +a 6808 6809 4103 +a 6809 6810 4103 +a 6810 6811 4103 +a 6811 6812 4103 +a 6812 6813 4103 +a 6813 6814 4103 +a 6814 6815 4103 +a 6815 6816 4103 +a 6816 6817 4103 +a 6817 6818 4103 +a 6818 6819 4103 +a 6819 6820 4103 +a 6820 6821 4103 +a 6821 6822 4103 +a 6822 6823 4103 +a 6823 6824 4103 +a 6824 6825 4103 +a 6825 6826 4103 +a 6826 6827 4103 +a 6827 6828 4103 +a 6828 6829 4103 +a 6829 6830 4103 +a 6830 6831 4103 +a 6831 6832 4103 +a 6832 6833 4103 +a 6833 6834 4103 +a 6834 6835 4103 +a 6835 6836 4103 +a 6836 6837 4103 +a 6837 6838 4103 +a 6838 6839 4103 +a 6839 6840 4103 +a 6840 6841 4103 +a 6841 6842 4103 +a 6842 6843 4103 +a 6843 6844 4103 +a 6844 6845 4103 +a 6845 6846 4103 +a 6846 6847 4103 +a 6847 6848 4103 +a 6848 6849 4103 +a 6849 6850 4103 +a 6850 6851 4103 +a 6851 6852 4103 +a 6852 6853 4103 +a 6853 6854 4103 +a 6854 6855 4103 +a 6855 6856 4103 +a 6856 6857 4103 +a 6857 6858 4103 +a 6858 6859 4103 +a 6859 6860 4103 +a 6860 6861 4103 +a 6861 6862 4103 +a 6862 6863 4103 +a 6863 6864 4103 +a 6864 6865 4103 +a 6865 6866 4103 +a 6866 6867 4103 +a 6867 6868 4103 +a 6868 6869 4103 +a 6869 6870 4103 +a 6870 6871 4103 +a 6871 6872 4103 +a 6872 6873 4103 +a 6873 6874 4103 +a 6874 6875 4103 +a 6875 6876 4103 +a 6876 6877 4103 +a 6877 6878 4103 +a 6878 6879 4103 +a 6879 6880 4103 +a 6880 6881 4103 +a 6881 6882 4103 +a 6882 6883 4103 +a 6883 6884 4103 +a 6884 6885 4103 +a 6885 6886 4103 +a 6886 6887 4103 +a 6887 6888 4103 +a 6888 6889 4103 +a 6889 6890 4103 +a 6890 6891 4103 +a 6891 6892 4103 +a 6892 6893 4103 +a 6893 6894 4103 +a 6894 6895 4103 +a 6895 6896 4103 +a 6896 6897 4103 +a 6897 6898 4103 +a 6898 6899 4103 +a 6899 6900 4103 +a 6900 6901 4103 +a 6901 6902 4103 +a 6902 6903 4103 +a 6903 6904 4103 +a 6904 6905 4103 +a 6905 6906 4103 +a 6906 6907 4103 +a 6907 6908 4103 +a 6908 6909 4103 +a 6909 6910 4103 +a 6910 6911 4103 +a 6911 6912 4103 +a 6912 6913 4103 +a 6913 6914 4103 +a 6914 6915 4103 +a 6915 6916 4103 +a 6916 6917 4103 +a 6917 6918 4103 +a 6918 6919 4103 +a 6919 6920 4103 +a 6920 6921 4103 +a 6921 6922 4103 +a 6922 6923 4103 +a 6923 6924 4103 +a 6924 6925 4103 +a 6925 6926 4103 +a 6926 6927 4103 +a 6927 6928 4103 +a 6928 6929 4103 +a 6929 6930 4103 +a 6930 6931 4103 +a 6931 6932 4103 +a 6932 6933 4103 +a 6933 6934 4103 +a 6934 6935 4103 +a 6935 6936 4103 +a 6936 6937 4103 +a 6937 6938 4103 +a 6938 6939 4103 +a 6939 6940 4103 +a 6940 6941 4103 +a 6941 6942 4103 +a 6942 6943 4103 +a 6943 6944 4103 +a 6944 6945 4103 +a 6945 6946 4103 +a 6946 6947 4103 +a 6947 6948 4103 +a 6948 6949 4103 +a 6949 6950 4103 +a 6950 6951 4103 +a 6951 6952 4103 +a 6952 6953 4103 +a 6953 6954 4103 +a 6954 6955 4103 +a 6955 6956 4103 +a 6956 6957 4103 +a 6957 6958 4103 +a 6958 6959 4103 +a 6959 6960 4103 +a 6960 6961 4103 +a 6961 6962 4103 +a 6962 6963 4103 +a 6963 6964 4103 +a 6964 6965 4103 +a 6965 6966 4103 +a 6966 6967 4103 +a 6967 6968 4103 +a 6968 6969 4103 +a 6969 6970 4103 +a 6970 6971 4103 +a 6971 6972 4103 +a 6972 6973 4103 +a 6973 6974 4103 +a 6974 6975 4103 +a 6975 6976 4103 +a 6976 6977 4103 +a 6977 6978 4103 +a 6978 6979 4103 +a 6979 6980 4103 +a 6980 6981 4103 +a 6981 6982 4103 +a 6982 6983 4103 +a 6983 6984 4103 +a 6984 6985 4103 +a 6985 6986 4103 +a 6986 6987 4103 +a 6987 6988 4103 +a 6988 6989 4103 +a 6989 6990 4103 +a 6990 6991 4103 +a 6991 6992 4103 +a 6992 6993 4103 +a 6993 6994 4103 +a 6994 6995 4103 +a 6995 6996 4103 +a 6996 6997 4103 +a 6997 6998 4103 +a 6998 6999 4103 +a 6999 7000 4103 +a 7000 7001 4103 +a 7001 7002 4103 +a 7002 7003 4103 +a 7003 7004 4103 +a 7004 7005 4103 +a 7005 7006 4103 +a 7006 7007 4103 +a 7007 7008 4103 +a 7008 7009 4103 +a 7009 7010 4103 +a 7010 7011 4103 +a 7011 7012 4103 +a 7012 7013 4103 +a 7013 7014 4103 +a 7014 7015 4103 +a 7015 7016 4103 +a 7016 7017 4103 +a 7017 7018 4103 +a 7018 7019 4103 +a 7019 7020 4103 +a 7020 7021 4103 +a 7021 7022 4103 +a 7022 7023 4103 +a 7023 7024 4103 +a 7024 7025 4103 +a 7025 7026 4103 +a 7026 7027 4103 +a 7027 7028 4103 +a 7028 7029 4103 +a 7029 7030 4103 +a 7030 7031 4103 +a 7031 7032 4103 +a 7032 7033 4103 +a 7033 7034 4103 +a 7034 7035 4103 +a 7035 7036 4103 +a 7036 7037 4103 +a 7037 7038 4103 +a 7038 7039 4103 +a 7039 7040 4103 +a 7040 7041 4103 +a 7041 7042 4103 +a 7042 7043 4103 +a 7043 7044 4103 +a 7044 7045 4103 +a 7045 7046 4103 +a 7046 7047 4103 +a 7047 7048 4103 +a 7048 7049 4103 +a 7049 7050 4103 +a 7050 7051 4103 +a 7051 7052 4103 +a 7052 7053 4103 +a 7053 7054 4103 +a 7054 7055 4103 +a 7055 7056 4103 +a 7056 7057 4103 +a 7057 7058 4103 +a 7058 7059 4103 +a 7059 7060 4103 +a 7060 7061 4103 +a 7061 7062 4103 +a 7062 7063 4103 +a 7063 7064 4103 +a 7064 7065 4103 +a 7065 7066 4103 +a 7066 7067 4103 +a 7067 7068 4103 +a 7068 7069 4103 +a 7069 7070 4103 +a 7070 7071 4103 +a 7071 7072 4103 +a 7072 7073 4103 +a 7073 7074 4103 +a 7074 7075 4103 +a 7075 7076 4103 +a 7076 7077 4103 +a 7077 7078 4103 +a 7078 7079 4103 +a 7079 7080 4103 +a 7080 7081 4103 +a 7081 7082 4103 +a 7082 7083 4103 +a 7083 7084 4103 +a 7084 7085 4103 +a 7085 7086 4103 +a 7086 7087 4103 +a 7087 7088 4103 +a 7088 7089 4103 +a 7089 7090 4103 +a 7090 7091 4103 +a 7091 7092 4103 +a 7092 7093 4103 +a 7093 7094 4103 +a 7094 7095 4103 +a 7095 7096 4103 +a 7096 7097 4103 +a 7097 7098 4103 +a 7098 7099 4103 +a 7099 7100 4103 +a 7100 7101 4103 +a 7101 7102 4103 +a 7102 7103 4103 +a 7103 7104 4103 +a 7104 7105 4103 +a 7105 7106 4103 +a 7106 7107 4103 +a 7107 7108 4103 +a 7108 7109 4103 +a 7109 7110 4103 +a 7110 7111 4103 +a 7111 7112 4103 +a 7112 7113 4103 +a 7113 7114 4103 +a 7114 7115 4103 +a 7115 7116 4103 +a 7116 7117 4103 +a 7117 7118 4103 +a 7118 7119 4103 +a 7119 7120 4103 +a 7120 7121 4103 +a 7121 7122 4103 +a 7122 7123 4103 +a 7123 7124 4103 +a 7124 7125 4103 +a 7125 7126 4103 +a 7126 7127 4103 +a 7127 7128 4103 +a 7128 7129 4103 +a 7129 7130 4103 +a 7130 7131 4103 +a 7131 7132 4103 +a 7132 7133 4103 +a 7133 7134 4103 +a 7134 7135 4103 +a 7135 7136 4103 +a 7136 7137 4103 +a 7137 7138 4103 +a 7138 7139 4103 +a 7139 7140 4103 +a 7140 7141 4103 +a 7141 7142 4103 +a 7142 7143 4103 +a 7143 7144 4103 +a 7144 7145 4103 +a 7145 7146 4103 +a 7146 7147 4103 +a 7147 7148 4103 +a 7148 7149 4103 +a 7149 7150 4103 +a 7150 7151 4103 +a 7151 7152 4103 +a 7152 7153 4103 +a 7153 7154 4103 +a 7154 7155 4103 +a 7155 7156 4103 +a 7156 7157 4103 +a 7157 7158 4103 +a 7158 7159 4103 +a 7159 7160 4103 +a 7160 7161 4103 +a 7161 7162 4103 +a 7162 7163 4103 +a 7163 7164 4103 +a 7164 7165 4103 +a 7165 7166 4103 +a 7166 7167 4103 +a 7167 7168 4103 +a 7168 7169 4103 +a 7169 7170 4103 +a 7170 7171 4103 +a 7171 7172 4103 +a 7172 7173 4103 +a 7173 7174 4103 +a 7174 7175 4103 +a 7175 7176 4103 +a 7176 7177 4103 +a 7177 7178 4103 +a 7178 7179 4103 +a 7179 7180 4103 +a 7180 7181 4103 +a 7181 7182 4103 +a 7182 7183 4103 +a 7183 7184 4103 +a 7184 7185 4103 +a 7185 7186 4103 +a 7186 7187 4103 +a 7187 7188 4103 +a 7188 7189 4103 +a 7189 7190 4103 +a 7190 7191 4103 +a 7191 7192 4103 +a 7192 7193 4103 +a 7193 7194 4103 +a 7194 7195 4103 +a 7195 7196 4103 +a 7196 7197 4103 +a 7197 7198 4103 +a 7198 7199 4103 +a 7199 7200 4103 +a 7200 7201 4103 +a 7201 7202 4103 +a 7202 7203 4103 +a 7203 7204 4103 +a 7204 7205 4103 +a 7205 7206 4103 +a 7206 7207 4103 +a 7207 7208 4103 +a 7208 7209 4103 +a 7209 7210 4103 +a 7210 7211 4103 +a 7211 7212 4103 +a 7212 7213 4103 +a 7213 7214 4103 +a 7214 7215 4103 +a 7215 7216 4103 +a 7216 7217 4103 +a 7217 7218 4103 +a 7218 7219 4103 +a 7219 7220 4103 +a 7220 7221 4103 +a 7221 7222 4103 +a 7222 7223 4103 +a 7223 7224 4103 +a 7224 7225 4103 +a 7225 7226 4103 +a 7226 7227 4103 +a 7227 7228 4103 +a 7228 7229 4103 +a 7229 7230 4103 +a 7230 7231 4103 +a 7231 7232 4103 +a 7232 7233 4103 +a 7233 7234 4103 +a 7234 7235 4103 +a 7235 7236 4103 +a 7236 7237 4103 +a 7237 7238 4103 +a 7238 7239 4103 +a 7239 7240 4103 +a 7240 7241 4103 +a 7241 7242 4103 +a 7242 7243 4103 +a 7243 7244 4103 +a 7244 7245 4103 +a 7245 7246 4103 +a 7246 7247 4103 +a 7247 7248 4103 +a 7248 7249 4103 +a 7249 7250 4103 +a 7250 7251 4103 +a 7251 7252 4103 +a 7252 7253 4103 +a 7253 7254 4103 +a 7254 7255 4103 +a 7255 7256 4103 +a 7256 7257 4103 +a 7257 7258 4103 +a 7258 7259 4103 +a 7259 7260 4103 +a 7260 7261 4103 +a 7261 7262 4103 +a 7262 7263 4103 +a 7263 7264 4103 +a 7264 7265 4103 +a 7265 7266 4103 +a 7266 7267 4103 +a 7267 7268 4103 +a 7268 7269 4103 +a 7269 7270 4103 +a 7270 7271 4103 +a 7271 7272 4103 +a 7272 7273 4103 +a 7273 7274 4103 +a 7274 7275 4103 +a 7275 7276 4103 +a 7276 7277 4103 +a 7277 7278 4103 +a 7278 7279 4103 +a 7279 7280 4103 +a 7280 7281 4103 +a 7281 7282 4103 +a 7282 7283 4103 +a 7283 7284 4103 +a 7284 7285 4103 +a 7285 7286 4103 +a 7286 7287 4103 +a 7287 7288 4103 +a 7288 7289 4103 +a 7289 7290 4103 +a 7290 7291 4103 +a 7291 7292 4103 +a 7292 7293 4103 +a 7293 7294 4103 +a 7294 7295 4103 +a 7295 7296 4103 +a 7296 7297 4103 +a 7297 7298 4103 +a 7298 7299 4103 +a 7299 7300 4103 +a 7300 7301 4103 +a 7301 7302 4103 +a 7302 7303 4103 +a 7303 7304 4103 +a 7304 7305 4103 +a 7305 7306 4103 +a 7306 7307 4103 +a 7307 7308 4103 +a 7308 7309 4103 +a 7309 7310 4103 +a 7310 7311 4103 +a 7311 7312 4103 +a 7312 7313 4103 +a 7313 7314 4103 +a 7314 7315 4103 +a 7315 7316 4103 +a 7316 7317 4103 +a 7317 7318 4103 +a 7318 7319 4103 +a 7319 7320 4103 +a 7320 7321 4103 +a 7321 7322 4103 +a 7322 7323 4103 +a 7323 7324 4103 +a 7324 7325 4103 +a 7325 7326 4103 +a 7326 7327 4103 +a 7327 7328 4103 +a 7328 7329 4103 +a 7329 7330 4103 +a 7330 7331 4103 +a 7331 7332 4103 +a 7332 7333 4103 +a 7333 7334 4103 +a 7334 7335 4103 +a 7335 7336 4103 +a 7336 7337 4103 +a 7337 7338 4103 +a 7338 7339 4103 +a 7339 7340 4103 +a 7340 7341 4103 +a 7341 7342 4103 +a 7342 7343 4103 +a 7343 7344 4103 +a 7344 7345 4103 +a 7345 7346 4103 +a 7346 7347 4103 +a 7347 7348 4103 +a 7348 7349 4103 +a 7349 7350 4103 +a 7350 7351 4103 +a 7351 7352 4103 +a 7352 7353 4103 +a 7353 7354 4103 +a 7354 7355 4103 +a 7355 7356 4103 +a 7356 7357 4103 +a 7357 7358 4103 +a 7358 7359 4103 +a 7359 7360 4103 +a 7360 7361 4103 +a 7361 7362 4103 +a 7362 7363 4103 +a 7363 7364 4103 +a 7364 7365 4103 +a 7365 7366 4103 +a 7366 7367 4103 +a 7367 7368 4103 +a 7368 7369 4103 +a 7369 7370 4103 +a 7370 7371 4103 +a 7371 7372 4103 +a 7372 7373 4103 +a 7373 7374 4103 +a 7374 7375 4103 +a 7375 7376 4103 +a 7376 7377 4103 +a 7377 7378 4103 +a 7378 7379 4103 +a 7379 7380 4103 +a 7380 7381 4103 +a 7381 7382 4103 +a 7382 7383 4103 +a 7383 7384 4103 +a 7384 7385 4103 +a 7385 7386 4103 +a 7386 7387 4103 +a 7387 7388 4103 +a 7388 7389 4103 +a 7389 7390 4103 +a 7390 7391 4103 +a 7391 7392 4103 +a 7392 7393 4103 +a 7393 7394 4103 +a 7394 7395 4103 +a 7395 7396 4103 +a 7396 7397 4103 +a 7397 7398 4103 +a 7398 7399 4103 +a 7399 7400 4103 +a 7400 7401 4103 +a 7401 7402 4103 +a 7402 7403 4103 +a 7403 7404 4103 +a 7404 7405 4103 +a 7405 7406 4103 +a 7406 7407 4103 +a 7407 7408 4103 +a 7408 7409 4103 +a 7409 7410 4103 +a 7410 7411 4103 +a 7411 7412 4103 +a 7412 7413 4103 +a 7413 7414 4103 +a 7414 7415 4103 +a 7415 7416 4103 +a 7416 7417 4103 +a 7417 7418 4103 +a 7418 7419 4103 +a 7419 7420 4103 +a 7420 7421 4103 +a 7421 7422 4103 +a 7422 7423 4103 +a 7423 7424 4103 +a 7424 7425 4103 +a 7425 7426 4103 +a 7426 7427 4103 +a 7427 7428 4103 +a 7428 7429 4103 +a 7429 7430 4103 +a 7430 7431 4103 +a 7431 7432 4103 +a 7432 7433 4103 +a 7433 7434 4103 +a 7434 7435 4103 +a 7435 7436 4103 +a 7436 7437 4103 +a 7437 7438 4103 +a 7438 7439 4103 +a 7439 7440 4103 +a 7440 7441 4103 +a 7441 7442 4103 +a 7442 7443 4103 +a 7443 7444 4103 +a 7444 7445 4103 +a 7445 7446 4103 +a 7446 7447 4103 +a 7447 7448 4103 +a 7448 7449 4103 +a 7449 7450 4103 +a 7450 7451 4103 +a 7451 7452 4103 +a 7452 7453 4103 +a 7453 7454 4103 +a 7454 7455 4103 +a 7455 7456 4103 +a 7456 7457 4103 +a 7457 7458 4103 +a 7458 7459 4103 +a 7459 7460 4103 +a 7460 7461 4103 +a 7461 7462 4103 +a 7462 7463 4103 +a 7463 7464 4103 +a 7464 7465 4103 +a 7465 7466 4103 +a 7466 7467 4103 +a 7467 7468 4103 +a 7468 7469 4103 +a 7469 7470 4103 +a 7470 7471 4103 +a 7471 7472 4103 +a 7472 7473 4103 +a 7473 7474 4103 +a 7474 7475 4103 +a 7475 7476 4103 +a 7476 7477 4103 +a 7477 7478 4103 +a 7478 7479 4103 +a 7479 7480 4103 +a 7480 7481 4103 +a 7481 7482 4103 +a 7482 7483 4103 +a 7483 7484 4103 +a 7484 7485 4103 +a 7485 7486 4103 +a 7486 7487 4103 +a 7487 7488 4103 +a 7488 7489 4103 +a 7489 7490 4103 +a 7490 7491 4103 +a 7491 7492 4103 +a 7492 7493 4103 +a 7493 7494 4103 +a 7494 7495 4103 +a 7495 7496 4103 +a 7496 7497 4103 +a 7497 7498 4103 +a 7498 7499 4103 +a 7499 7500 4103 +a 7500 7501 4103 +a 7501 7502 4103 +a 7502 7503 4103 +a 7503 7504 4103 +a 7504 7505 4103 +a 7505 7506 4103 +a 7506 7507 4103 +a 7507 7508 4103 +a 7508 7509 4103 +a 7509 7510 4103 +a 7510 7511 4103 +a 7511 7512 4103 +a 7512 7513 4103 +a 7513 7514 4103 +a 7514 7515 4103 +a 7515 7516 4103 +a 7516 7517 4103 +a 7517 7518 4103 +a 7518 7519 4103 +a 7519 7520 4103 +a 7520 7521 4103 +a 7521 7522 4103 +a 7522 7523 4103 +a 7523 7524 4103 +a 7524 7525 4103 +a 7525 7526 4103 +a 7526 7527 4103 +a 7527 7528 4103 +a 7528 7529 4103 +a 7529 7530 4103 +a 7530 7531 4103 +a 7531 7532 4103 +a 7532 7533 4103 +a 7533 7534 4103 +a 7534 7535 4103 +a 7535 7536 4103 +a 7536 7537 4103 +a 7537 7538 4103 +a 7538 7539 4103 +a 7539 7540 4103 +a 7540 7541 4103 +a 7541 7542 4103 +a 7542 7543 4103 +a 7543 7544 4103 +a 7544 7545 4103 +a 7545 7546 4103 +a 7546 7547 4103 +a 7547 7548 4103 +a 7548 7549 4103 +a 7549 7550 4103 +a 7550 7551 4103 +a 7551 7552 4103 +a 7552 7553 4103 +a 7553 7554 4103 +a 7554 7555 4103 +a 7555 7556 4103 +a 7556 7557 4103 +a 7557 7558 4103 +a 7558 7559 4103 +a 7559 7560 4103 +a 7560 7561 4103 +a 7561 7562 4103 +a 7562 7563 4103 +a 7563 7564 4103 +a 7564 7565 4103 +a 7565 7566 4103 +a 7566 7567 4103 +a 7567 7568 4103 +a 7568 7569 4103 +a 7569 7570 4103 +a 7570 7571 4103 +a 7571 7572 4103 +a 7572 7573 4103 +a 7573 7574 4103 +a 7574 7575 4103 +a 7575 7576 4103 +a 7576 7577 4103 +a 7577 7578 4103 +a 7578 7579 4103 +a 7579 7580 4103 +a 7580 7581 4103 +a 7581 7582 4103 +a 7582 7583 4103 +a 7583 7584 4103 +a 7584 7585 4103 +a 7585 7586 4103 +a 7586 7587 4103 +a 7587 7588 4103 +a 7588 7589 4103 +a 7589 7590 4103 +a 7590 7591 4103 +a 7591 7592 4103 +a 7592 7593 4103 +a 7593 7594 4103 +a 7594 7595 4103 +a 7595 7596 4103 +a 7596 7597 4103 +a 7597 7598 4103 +a 7598 7599 4103 +a 7599 7600 4103 +a 7600 7601 4103 +a 7601 7602 4103 +a 7602 7603 4103 +a 7603 7604 4103 +a 7604 7605 4103 +a 7605 7606 4103 +a 7606 7607 4103 +a 7607 7608 4103 +a 7608 7609 4103 +a 7609 7610 4103 +a 7610 7611 4103 +a 7611 7612 4103 +a 7612 7613 4103 +a 7613 7614 4103 +a 7614 7615 4103 +a 7615 7616 4103 +a 7616 7617 4103 +a 7617 7618 4103 +a 7618 7619 4103 +a 7619 7620 4103 +a 7620 7621 4103 +a 7621 7622 4103 +a 7622 7623 4103 +a 7623 7624 4103 +a 7624 7625 4103 +a 7625 7626 4103 +a 7626 7627 4103 +a 7627 7628 4103 +a 7628 7629 4103 +a 7629 7630 4103 +a 7630 7631 4103 +a 7631 7632 4103 +a 7632 7633 4103 +a 7633 7634 4103 +a 7634 7635 4103 +a 7635 7636 4103 +a 7636 7637 4103 +a 7637 7638 4103 +a 7638 7639 4103 +a 7639 7640 4103 +a 7640 7641 4103 +a 7641 7642 4103 +a 7642 7643 4103 +a 7643 7644 4103 +a 7644 7645 4103 +a 7645 7646 4103 +a 7646 7647 4103 +a 7647 7648 4103 +a 7648 7649 4103 +a 7649 7650 4103 +a 7650 7651 4103 +a 7651 7652 4103 +a 7652 7653 4103 +a 7653 7654 4103 +a 7654 7655 4103 +a 7655 7656 4103 +a 7656 7657 4103 +a 7657 7658 4103 +a 7658 7659 4103 +a 7659 7660 4103 +a 7660 7661 4103 +a 7661 7662 4103 +a 7662 7663 4103 +a 7663 7664 4103 +a 7664 7665 4103 +a 7665 7666 4103 +a 7666 7667 4103 +a 7667 7668 4103 +a 7668 7669 4103 +a 7669 7670 4103 +a 7670 7671 4103 +a 7671 7672 4103 +a 7672 7673 4103 +a 7673 7674 4103 +a 7674 7675 4103 +a 7675 7676 4103 +a 7676 7677 4103 +a 7677 7678 4103 +a 7678 7679 4103 +a 7679 7680 4103 +a 7680 7681 4103 +a 7681 7682 4103 +a 7682 7683 4103 +a 7683 7684 4103 +a 7684 7685 4103 +a 7685 7686 4103 +a 7686 7687 4103 +a 7687 7688 4103 +a 7688 7689 4103 +a 7689 7690 4103 +a 7690 7691 4103 +a 7691 7692 4103 +a 7692 7693 4103 +a 7693 7694 4103 +a 7694 7695 4103 +a 7695 7696 4103 +a 7696 7697 4103 +a 7697 7698 4103 +a 7698 7699 4103 +a 7699 7700 4103 +a 7700 7701 4103 +a 7701 7702 4103 +a 7702 7703 4103 +a 7703 7704 4103 +a 7704 7705 4103 +a 7705 7706 4103 +a 7706 7707 4103 +a 7707 7708 4103 +a 7708 7709 4103 +a 7709 7710 4103 +a 7710 7711 4103 +a 7711 7712 4103 +a 7712 7713 4103 +a 7713 7714 4103 +a 7714 7715 4103 +a 7715 7716 4103 +a 7716 7717 4103 +a 7717 7718 4103 +a 7718 7719 4103 +a 7719 7720 4103 +a 7720 7721 4103 +a 7721 7722 4103 +a 7722 7723 4103 +a 7723 7724 4103 +a 7724 7725 4103 +a 7725 7726 4103 +a 7726 7727 4103 +a 7727 7728 4103 +a 7728 7729 4103 +a 7729 7730 4103 +a 7730 7731 4103 +a 7731 7732 4103 +a 7732 7733 4103 +a 7733 7734 4103 +a 7734 7735 4103 +a 7735 7736 4103 +a 7736 7737 4103 +a 7737 7738 4103 +a 7738 7739 4103 +a 7739 7740 4103 +a 7740 7741 4103 +a 7741 7742 4103 +a 7742 7743 4103 +a 7743 7744 4103 +a 7744 7745 4103 +a 7745 7746 4103 +a 7746 7747 4103 +a 7747 7748 4103 +a 7748 7749 4103 +a 7749 7750 4103 +a 7750 7751 4103 +a 7751 7752 4103 +a 7752 7753 4103 +a 7753 7754 4103 +a 7754 7755 4103 +a 7755 7756 4103 +a 7756 7757 4103 +a 7757 7758 4103 +a 7758 7759 4103 +a 7759 7760 4103 +a 7760 7761 4103 +a 7761 7762 4103 +a 7762 7763 4103 +a 7763 7764 4103 +a 7764 7765 4103 +a 7765 7766 4103 +a 7766 7767 4103 +a 7767 7768 4103 +a 7768 7769 4103 +a 7769 7770 4103 +a 7770 7771 4103 +a 7771 7772 4103 +a 7772 7773 4103 +a 7773 7774 4103 +a 7774 7775 4103 +a 7775 7776 4103 +a 7776 7777 4103 +a 7777 7778 4103 +a 7778 7779 4103 +a 7779 7780 4103 +a 7780 7781 4103 +a 7781 7782 4103 +a 7782 7783 4103 +a 7783 7784 4103 +a 7784 7785 4103 +a 7785 7786 4103 +a 7786 7787 4103 +a 7787 7788 4103 +a 7788 7789 4103 +a 7789 7790 4103 +a 7790 7791 4103 +a 7791 7792 4103 +a 7792 7793 4103 +a 7793 7794 4103 +a 7794 7795 4103 +a 7795 7796 4103 +a 7796 7797 4103 +a 7797 7798 4103 +a 7798 7799 4103 +a 7799 7800 4103 +a 7800 7801 4103 +a 7801 7802 4103 +a 7802 7803 4103 +a 7803 7804 4103 +a 7804 7805 4103 +a 7805 7806 4103 +a 7806 7807 4103 +a 7807 7808 4103 +a 7808 7809 4103 +a 7809 7810 4103 +a 7810 7811 4103 +a 7811 7812 4103 +a 7812 7813 4103 +a 7813 7814 4103 +a 7814 7815 4103 +a 7815 7816 4103 +a 7816 7817 4103 +a 7817 7818 4103 +a 7818 7819 4103 +a 7819 7820 4103 +a 7820 7821 4103 +a 7821 7822 4103 +a 7822 7823 4103 +a 7823 7824 4103 +a 7824 7825 4103 +a 7825 7826 4103 +a 7826 7827 4103 +a 7827 7828 4103 +a 7828 7829 4103 +a 7829 7830 4103 +a 7830 7831 4103 +a 7831 7832 4103 +a 7832 7833 4103 +a 7833 7834 4103 +a 7834 7835 4103 +a 7835 7836 4103 +a 7836 7837 4103 +a 7837 7838 4103 +a 7838 7839 4103 +a 7839 7840 4103 +a 7840 7841 4103 +a 7841 7842 4103 +a 7842 7843 4103 +a 7843 7844 4103 +a 7844 7845 4103 +a 7845 7846 4103 +a 7846 7847 4103 +a 7847 7848 4103 +a 7848 7849 4103 +a 7849 7850 4103 +a 7850 7851 4103 +a 7851 7852 4103 +a 7852 7853 4103 +a 7853 7854 4103 +a 7854 7855 4103 +a 7855 7856 4103 +a 7856 7857 4103 +a 7857 7858 4103 +a 7858 7859 4103 +a 7859 7860 4103 +a 7860 7861 4103 +a 7861 7862 4103 +a 7862 7863 4103 +a 7863 7864 4103 +a 7864 7865 4103 +a 7865 7866 4103 +a 7866 7867 4103 +a 7867 7868 4103 +a 7868 7869 4103 +a 7869 7870 4103 +a 7870 7871 4103 +a 7871 7872 4103 +a 7872 7873 4103 +a 7873 7874 4103 +a 7874 7875 4103 +a 7875 7876 4103 +a 7876 7877 4103 +a 7877 7878 4103 +a 7878 7879 4103 +a 7879 7880 4103 +a 7880 7881 4103 +a 7881 7882 4103 +a 7882 7883 4103 +a 7883 7884 4103 +a 7884 7885 4103 +a 7885 7886 4103 +a 7886 7887 4103 +a 7887 7888 4103 +a 7888 7889 4103 +a 7889 7890 4103 +a 7890 7891 4103 +a 7891 7892 4103 +a 7892 7893 4103 +a 7893 7894 4103 +a 7894 7895 4103 +a 7895 7896 4103 +a 7896 7897 4103 +a 7897 7898 4103 +a 7898 7899 4103 +a 7899 7900 4103 +a 7900 7901 4103 +a 7901 7902 4103 +a 7902 7903 4103 +a 7903 7904 4103 +a 7904 7905 4103 +a 7905 7906 4103 +a 7906 7907 4103 +a 7907 7908 4103 +a 7908 7909 4103 +a 7909 7910 4103 +a 7910 7911 4103 +a 7911 7912 4103 +a 7912 7913 4103 +a 7913 7914 4103 +a 7914 7915 4103 +a 7915 7916 4103 +a 7916 7917 4103 +a 7917 7918 4103 +a 7918 7919 4103 +a 7919 7920 4103 +a 7920 7921 4103 +a 7921 7922 4103 +a 7922 7923 4103 +a 7923 7924 4103 +a 7924 7925 4103 +a 7925 7926 4103 +a 7926 7927 4103 +a 7927 7928 4103 +a 7928 7929 4103 +a 7929 7930 4103 +a 7930 7931 4103 +a 7931 7932 4103 +a 7932 7933 4103 +a 7933 7934 4103 +a 7934 7935 4103 +a 7935 7936 4103 +a 7936 7937 4103 +a 7937 7938 4103 +a 7938 7939 4103 +a 7939 7940 4103 +a 7940 7941 4103 +a 7941 7942 4103 +a 7942 7943 4103 +a 7943 7944 4103 +a 7944 7945 4103 +a 7945 7946 4103 +a 7946 7947 4103 +a 7947 7948 4103 +a 7948 7949 4103 +a 7949 7950 4103 +a 7950 7951 4103 +a 7951 7952 4103 +a 7952 7953 4103 +a 7953 7954 4103 +a 7954 7955 4103 +a 7955 7956 4103 +a 7956 7957 4103 +a 7957 7958 4103 +a 7958 7959 4103 +a 7959 7960 4103 +a 7960 7961 4103 +a 7961 7962 4103 +a 7962 7963 4103 +a 7963 7964 4103 +a 7964 7965 4103 +a 7965 7966 4103 +a 7966 7967 4103 +a 7967 7968 4103 +a 7968 7969 4103 +a 7969 7970 4103 +a 7970 7971 4103 +a 7971 7972 4103 +a 7972 7973 4103 +a 7973 7974 4103 +a 7974 7975 4103 +a 7975 7976 4103 +a 7976 7977 4103 +a 7977 7978 4103 +a 7978 7979 4103 +a 7979 7980 4103 +a 7980 7981 4103 +a 7981 7982 4103 +a 7982 7983 4103 +a 7983 7984 4103 +a 7984 7985 4103 +a 7985 7986 4103 +a 7986 7987 4103 +a 7987 7988 4103 +a 7988 7989 4103 +a 7989 7990 4103 +a 7990 7991 4103 +a 7991 7992 4103 +a 7992 7993 4103 +a 7993 7994 4103 +a 7994 7995 4103 +a 7995 7996 4103 +a 7996 7997 4103 +a 7997 7998 4103 +a 7998 7999 4103 +a 7999 8000 4103 +a 8000 8001 4103 +a 8001 8002 4103 +a 8002 8003 4103 +a 8003 8004 4103 +a 8004 8005 4103 +a 8005 8006 4103 +a 8006 8007 4103 +a 8007 8008 4103 +a 8008 8009 4103 +a 8009 8010 4103 +a 8010 8011 4103 +a 8011 8012 4103 +a 8012 8013 4103 +a 8013 8014 4103 +a 8014 8015 4103 +a 8015 8016 4103 +a 8016 8017 4103 +a 8017 8018 4103 +a 8018 8019 4103 +a 8019 8020 4103 +a 8020 8021 4103 +a 8021 8022 4103 +a 8022 8023 4103 +a 8023 8024 4103 +a 8024 8025 4103 +a 8025 8026 4103 +a 8026 8027 4103 +a 8027 8028 4103 +a 8028 8029 4103 +a 8029 8030 4103 +a 8030 8031 4103 +a 8031 8032 4103 +a 8032 8033 4103 +a 8033 8034 4103 +a 8034 8035 4103 +a 8035 8036 4103 +a 8036 8037 4103 +a 8037 8038 4103 +a 8038 8039 4103 +a 8039 8040 4103 +a 8040 8041 4103 +a 8041 8042 4103 +a 8042 8043 4103 +a 8043 8044 4103 +a 8044 8045 4103 +a 8045 8046 4103 +a 8046 8047 4103 +a 8047 8048 4103 +a 8048 8049 4103 +a 8049 8050 4103 +a 8050 8051 4103 +a 8051 8052 4103 +a 8052 8053 4103 +a 8053 8054 4103 +a 8054 8055 4103 +a 8055 8056 4103 +a 8056 8057 4103 +a 8057 8058 4103 +a 8058 8059 4103 +a 8059 8060 4103 +a 8060 8061 4103 +a 8061 8062 4103 +a 8062 8063 4103 +a 8063 8064 4103 +a 8064 8065 4103 +a 8065 8066 4103 +a 8066 8067 4103 +a 8067 8068 4103 +a 8068 8069 4103 +a 8069 8070 4103 +a 8070 8071 4103 +a 8071 8072 4103 +a 8072 8073 4103 +a 8073 8074 4103 +a 8074 8075 4103 +a 8075 8076 4103 +a 8076 8077 4103 +a 8077 8078 4103 +a 8078 8079 4103 +a 8079 8080 4103 +a 8080 8081 4103 +a 8081 8082 4103 +a 8082 8083 4103 +a 8083 8084 4103 +a 8084 8085 4103 +a 8085 8086 4103 +a 8086 8087 4103 +a 8087 8088 4103 +a 8088 8089 4103 +a 8089 8090 4103 +a 8090 8091 4103 +a 8091 8092 4103 +a 8092 8093 4103 +a 8093 8094 4103 +a 8094 8095 4103 +a 8095 8096 4103 +a 8096 8097 4103 +a 8097 8098 4103 +a 8098 8099 4103 +a 8099 8100 4103 +a 8100 8101 4103 +a 8101 8102 4103 +a 8102 8103 4103 +a 8103 8104 4103 +a 8104 8105 4103 +a 8105 8106 4103 +a 8106 8107 4103 +a 8107 8108 4103 +a 8108 8109 4103 +a 8109 8110 4103 +a 8110 8111 4103 +a 8111 8112 4103 +a 8112 8113 4103 +a 8113 8114 4103 +a 8114 8115 4103 +a 8115 8116 4103 +a 8116 8117 4103 +a 8117 8118 4103 +a 8118 8119 4103 +a 8119 8120 4103 +a 8120 8121 4103 +a 8121 8122 4103 +a 8122 8123 4103 +a 8123 8124 4103 +a 8124 8125 4103 +a 8125 8126 4103 +a 8126 8127 4103 +a 8127 8128 4103 +a 8128 8129 4103 +a 8129 8130 4103 +a 8130 8131 4103 +a 8131 8132 4103 +a 8132 8133 4103 +a 8133 8134 4103 +a 8134 8135 4103 +a 8135 8136 4103 +a 8136 8137 4103 +a 8137 8138 4103 +a 8138 8139 4103 +a 8139 8140 4103 +a 8140 8141 4103 +a 8141 8142 4103 +a 8142 8143 4103 +a 8143 8144 4103 +a 8144 8145 4103 +a 8145 8146 4103 +a 8146 8147 4103 +a 8147 8148 4103 +a 8148 8149 4103 +a 8149 8150 4103 +a 8150 8151 4103 +a 8151 8152 4103 +a 8152 8153 4103 +a 8153 8154 4103 +a 8154 8155 4103 +a 8155 8156 4103 +a 8156 8157 4103 +a 8157 8158 4103 +a 8158 8159 4103 +a 8159 8160 4103 +a 8160 8161 4103 +a 8161 8162 4103 +a 8162 8163 4103 +a 8163 8164 4103 +a 8164 8165 4103 +a 8165 8166 4103 +a 8166 8167 4103 +a 8167 8168 4103 +a 8168 8169 4103 +a 8169 8170 4103 +a 8170 8171 4103 +a 8171 8172 4103 +a 8172 8173 4103 +a 8173 8174 4103 +a 8174 8175 4103 +a 8175 8176 4103 +a 8176 8177 4103 +a 8177 8178 4103 +a 8178 8179 4103 +a 8179 8180 4103 +a 8180 8181 4103 +a 8181 8182 4103 +a 8182 8183 4103 +a 8183 8184 4103 +a 8184 8185 4103 +a 8185 8186 4103 +a 8186 8187 4103 +a 8187 8188 4103 +a 8188 8189 4103 +a 8189 8190 4103 +a 8190 8191 4103 +a 8191 8192 4103 +a 8192 8193 4103 +a 8193 8194 4103 +a 8194 8195 4103 +a 8195 8196 4103 +a 8196 8197 4103 +a 8197 8198 4103 +a 8198 8199 4103 +a 8199 8200 4103 +a 8200 8201 4103 +a 8201 8202 4103 +a 8202 8203 4103 +a 8203 8204 4103 +a 8204 8205 4103 +a 8205 8206 4103 +a 8206 8207 4103 +a 8207 8208 4103 +a 8209 8210 4102 +a 8210 8211 4102 +a 8211 8212 4102 +a 8212 8213 4102 +a 8213 8214 4102 +a 8214 8215 4102 +a 8215 8216 4102 +a 8216 8217 4102 +a 8217 8218 4102 +a 8218 8219 4102 +a 8219 8220 4102 +a 8220 8221 4102 +a 8221 8222 4102 +a 8222 8223 4102 +a 8223 8224 4102 +a 8224 8225 4102 +a 8225 8226 4102 +a 8226 8227 4102 +a 8227 8228 4102 +a 8228 8229 4102 +a 8229 8230 4102 +a 8230 8231 4102 +a 8231 8232 4102 +a 8232 8233 4102 +a 8233 8234 4102 +a 8234 8235 4102 +a 8235 8236 4102 +a 8236 8237 4102 +a 8237 8238 4102 +a 8238 8239 4102 +a 8239 8240 4102 +a 8240 8241 4102 +a 8241 8242 4102 +a 8242 8243 4102 +a 8243 8244 4102 +a 8244 8245 4102 +a 8245 8246 4102 +a 8246 8247 4102 +a 8247 8248 4102 +a 8248 8249 4102 +a 8249 8250 4102 +a 8250 8251 4102 +a 8251 8252 4102 +a 8252 8253 4102 +a 8253 8254 4102 +a 8254 8255 4102 +a 8255 8256 4102 +a 8256 8257 4102 +a 8257 8258 4102 +a 8258 8259 4102 +a 8259 8260 4102 +a 8260 8261 4102 +a 8261 8262 4102 +a 8262 8263 4102 +a 8263 8264 4102 +a 8264 8265 4102 +a 8265 8266 4102 +a 8266 8267 4102 +a 8267 8268 4102 +a 8268 8269 4102 +a 8269 8270 4102 +a 8270 8271 4102 +a 8271 8272 4102 +a 8272 8273 4102 +a 8273 8274 4102 +a 8274 8275 4102 +a 8275 8276 4102 +a 8276 8277 4102 +a 8277 8278 4102 +a 8278 8279 4102 +a 8279 8280 4102 +a 8280 8281 4102 +a 8281 8282 4102 +a 8282 8283 4102 +a 8283 8284 4102 +a 8284 8285 4102 +a 8285 8286 4102 +a 8286 8287 4102 +a 8287 8288 4102 +a 8288 8289 4102 +a 8289 8290 4102 +a 8290 8291 4102 +a 8291 8292 4102 +a 8292 8293 4102 +a 8293 8294 4102 +a 8294 8295 4102 +a 8295 8296 4102 +a 8296 8297 4102 +a 8297 8298 4102 +a 8298 8299 4102 +a 8299 8300 4102 +a 8300 8301 4102 +a 8301 8302 4102 +a 8302 8303 4102 +a 8303 8304 4102 +a 8304 8305 4102 +a 8305 8306 4102 +a 8306 8307 4102 +a 8307 8308 4102 +a 8308 8309 4102 +a 8309 8310 4102 +a 8310 8311 4102 +a 8311 8312 4102 +a 8312 8313 4102 +a 8313 8314 4102 +a 8314 8315 4102 +a 8315 8316 4102 +a 8316 8317 4102 +a 8317 8318 4102 +a 8318 8319 4102 +a 8319 8320 4102 +a 8320 8321 4102 +a 8321 8322 4102 +a 8322 8323 4102 +a 8323 8324 4102 +a 8324 8325 4102 +a 8325 8326 4102 +a 8326 8327 4102 +a 8327 8328 4102 +a 8328 8329 4102 +a 8329 8330 4102 +a 8330 8331 4102 +a 8331 8332 4102 +a 8332 8333 4102 +a 8333 8334 4102 +a 8334 8335 4102 +a 8335 8336 4102 +a 8336 8337 4102 +a 8337 8338 4102 +a 8338 8339 4102 +a 8339 8340 4102 +a 8340 8341 4102 +a 8341 8342 4102 +a 8342 8343 4102 +a 8343 8344 4102 +a 8344 8345 4102 +a 8345 8346 4102 +a 8346 8347 4102 +a 8347 8348 4102 +a 8348 8349 4102 +a 8349 8350 4102 +a 8350 8351 4102 +a 8351 8352 4102 +a 8352 8353 4102 +a 8353 8354 4102 +a 8354 8355 4102 +a 8355 8356 4102 +a 8356 8357 4102 +a 8357 8358 4102 +a 8358 8359 4102 +a 8359 8360 4102 +a 8360 8361 4102 +a 8361 8362 4102 +a 8362 8363 4102 +a 8363 8364 4102 +a 8364 8365 4102 +a 8365 8366 4102 +a 8366 8367 4102 +a 8367 8368 4102 +a 8368 8369 4102 +a 8369 8370 4102 +a 8370 8371 4102 +a 8371 8372 4102 +a 8372 8373 4102 +a 8373 8374 4102 +a 8374 8375 4102 +a 8375 8376 4102 +a 8376 8377 4102 +a 8377 8378 4102 +a 8378 8379 4102 +a 8379 8380 4102 +a 8380 8381 4102 +a 8381 8382 4102 +a 8382 8383 4102 +a 8383 8384 4102 +a 8384 8385 4102 +a 8385 8386 4102 +a 8386 8387 4102 +a 8387 8388 4102 +a 8388 8389 4102 +a 8389 8390 4102 +a 8390 8391 4102 +a 8391 8392 4102 +a 8392 8393 4102 +a 8393 8394 4102 +a 8394 8395 4102 +a 8395 8396 4102 +a 8396 8397 4102 +a 8397 8398 4102 +a 8398 8399 4102 +a 8399 8400 4102 +a 8400 8401 4102 +a 8401 8402 4102 +a 8402 8403 4102 +a 8403 8404 4102 +a 8404 8405 4102 +a 8405 8406 4102 +a 8406 8407 4102 +a 8407 8408 4102 +a 8408 8409 4102 +a 8409 8410 4102 +a 8410 8411 4102 +a 8411 8412 4102 +a 8412 8413 4102 +a 8413 8414 4102 +a 8414 8415 4102 +a 8415 8416 4102 +a 8416 8417 4102 +a 8417 8418 4102 +a 8418 8419 4102 +a 8419 8420 4102 +a 8420 8421 4102 +a 8421 8422 4102 +a 8422 8423 4102 +a 8423 8424 4102 +a 8424 8425 4102 +a 8425 8426 4102 +a 8426 8427 4102 +a 8427 8428 4102 +a 8428 8429 4102 +a 8429 8430 4102 +a 8430 8431 4102 +a 8431 8432 4102 +a 8432 8433 4102 +a 8433 8434 4102 +a 8434 8435 4102 +a 8435 8436 4102 +a 8436 8437 4102 +a 8437 8438 4102 +a 8438 8439 4102 +a 8439 8440 4102 +a 8440 8441 4102 +a 8441 8442 4102 +a 8442 8443 4102 +a 8443 8444 4102 +a 8444 8445 4102 +a 8445 8446 4102 +a 8446 8447 4102 +a 8447 8448 4102 +a 8448 8449 4102 +a 8449 8450 4102 +a 8450 8451 4102 +a 8451 8452 4102 +a 8452 8453 4102 +a 8453 8454 4102 +a 8454 8455 4102 +a 8455 8456 4102 +a 8456 8457 4102 +a 8457 8458 4102 +a 8458 8459 4102 +a 8459 8460 4102 +a 8460 8461 4102 +a 8461 8462 4102 +a 8462 8463 4102 +a 8463 8464 4102 +a 8464 8465 4102 +a 8465 8466 4102 +a 8466 8467 4102 +a 8467 8468 4102 +a 8468 8469 4102 +a 8469 8470 4102 +a 8470 8471 4102 +a 8471 8472 4102 +a 8472 8473 4102 +a 8473 8474 4102 +a 8474 8475 4102 +a 8475 8476 4102 +a 8476 8477 4102 +a 8477 8478 4102 +a 8478 8479 4102 +a 8479 8480 4102 +a 8480 8481 4102 +a 8481 8482 4102 +a 8482 8483 4102 +a 8483 8484 4102 +a 8484 8485 4102 +a 8485 8486 4102 +a 8486 8487 4102 +a 8487 8488 4102 +a 8488 8489 4102 +a 8489 8490 4102 +a 8490 8491 4102 +a 8491 8492 4102 +a 8492 8493 4102 +a 8493 8494 4102 +a 8494 8495 4102 +a 8495 8496 4102 +a 8496 8497 4102 +a 8497 8498 4102 +a 8498 8499 4102 +a 8499 8500 4102 +a 8500 8501 4102 +a 8501 8502 4102 +a 8502 8503 4102 +a 8503 8504 4102 +a 8504 8505 4102 +a 8505 8506 4102 +a 8506 8507 4102 +a 8507 8508 4102 +a 8508 8509 4102 +a 8509 8510 4102 +a 8510 8511 4102 +a 8511 8512 4102 +a 8512 8513 4102 +a 8513 8514 4102 +a 8514 8515 4102 +a 8515 8516 4102 +a 8516 8517 4102 +a 8517 8518 4102 +a 8518 8519 4102 +a 8519 8520 4102 +a 8520 8521 4102 +a 8521 8522 4102 +a 8522 8523 4102 +a 8523 8524 4102 +a 8524 8525 4102 +a 8525 8526 4102 +a 8526 8527 4102 +a 8527 8528 4102 +a 8528 8529 4102 +a 8529 8530 4102 +a 8530 8531 4102 +a 8531 8532 4102 +a 8532 8533 4102 +a 8533 8534 4102 +a 8534 8535 4102 +a 8535 8536 4102 +a 8536 8537 4102 +a 8537 8538 4102 +a 8538 8539 4102 +a 8539 8540 4102 +a 8540 8541 4102 +a 8541 8542 4102 +a 8542 8543 4102 +a 8543 8544 4102 +a 8544 8545 4102 +a 8545 8546 4102 +a 8546 8547 4102 +a 8547 8548 4102 +a 8548 8549 4102 +a 8549 8550 4102 +a 8550 8551 4102 +a 8551 8552 4102 +a 8552 8553 4102 +a 8553 8554 4102 +a 8554 8555 4102 +a 8555 8556 4102 +a 8556 8557 4102 +a 8557 8558 4102 +a 8558 8559 4102 +a 8559 8560 4102 +a 8560 8561 4102 +a 8561 8562 4102 +a 8562 8563 4102 +a 8563 8564 4102 +a 8564 8565 4102 +a 8565 8566 4102 +a 8566 8567 4102 +a 8567 8568 4102 +a 8568 8569 4102 +a 8569 8570 4102 +a 8570 8571 4102 +a 8571 8572 4102 +a 8572 8573 4102 +a 8573 8574 4102 +a 8574 8575 4102 +a 8575 8576 4102 +a 8576 8577 4102 +a 8577 8578 4102 +a 8578 8579 4102 +a 8579 8580 4102 +a 8580 8581 4102 +a 8581 8582 4102 +a 8582 8583 4102 +a 8583 8584 4102 +a 8584 8585 4102 +a 8585 8586 4102 +a 8586 8587 4102 +a 8587 8588 4102 +a 8588 8589 4102 +a 8589 8590 4102 +a 8590 8591 4102 +a 8591 8592 4102 +a 8592 8593 4102 +a 8593 8594 4102 +a 8594 8595 4102 +a 8595 8596 4102 +a 8596 8597 4102 +a 8597 8598 4102 +a 8598 8599 4102 +a 8599 8600 4102 +a 8600 8601 4102 +a 8601 8602 4102 +a 8602 8603 4102 +a 8603 8604 4102 +a 8604 8605 4102 +a 8605 8606 4102 +a 8606 8607 4102 +a 8607 8608 4102 +a 8608 8609 4102 +a 8609 8610 4102 +a 8610 8611 4102 +a 8611 8612 4102 +a 8612 8613 4102 +a 8613 8614 4102 +a 8614 8615 4102 +a 8615 8616 4102 +a 8616 8617 4102 +a 8617 8618 4102 +a 8618 8619 4102 +a 8619 8620 4102 +a 8620 8621 4102 +a 8621 8622 4102 +a 8622 8623 4102 +a 8623 8624 4102 +a 8624 8625 4102 +a 8625 8626 4102 +a 8626 8627 4102 +a 8627 8628 4102 +a 8628 8629 4102 +a 8629 8630 4102 +a 8630 8631 4102 +a 8631 8632 4102 +a 8632 8633 4102 +a 8633 8634 4102 +a 8634 8635 4102 +a 8635 8636 4102 +a 8636 8637 4102 +a 8637 8638 4102 +a 8638 8639 4102 +a 8639 8640 4102 +a 8640 8641 4102 +a 8641 8642 4102 +a 8642 8643 4102 +a 8643 8644 4102 +a 8644 8645 4102 +a 8645 8646 4102 +a 8646 8647 4102 +a 8647 8648 4102 +a 8648 8649 4102 +a 8649 8650 4102 +a 8650 8651 4102 +a 8651 8652 4102 +a 8652 8653 4102 +a 8653 8654 4102 +a 8654 8655 4102 +a 8655 8656 4102 +a 8656 8657 4102 +a 8657 8658 4102 +a 8658 8659 4102 +a 8659 8660 4102 +a 8660 8661 4102 +a 8661 8662 4102 +a 8662 8663 4102 +a 8663 8664 4102 +a 8664 8665 4102 +a 8665 8666 4102 +a 8666 8667 4102 +a 8667 8668 4102 +a 8668 8669 4102 +a 8669 8670 4102 +a 8670 8671 4102 +a 8671 8672 4102 +a 8672 8673 4102 +a 8673 8674 4102 +a 8674 8675 4102 +a 8675 8676 4102 +a 8676 8677 4102 +a 8677 8678 4102 +a 8678 8679 4102 +a 8679 8680 4102 +a 8680 8681 4102 +a 8681 8682 4102 +a 8682 8683 4102 +a 8683 8684 4102 +a 8684 8685 4102 +a 8685 8686 4102 +a 8686 8687 4102 +a 8687 8688 4102 +a 8688 8689 4102 +a 8689 8690 4102 +a 8690 8691 4102 +a 8691 8692 4102 +a 8692 8693 4102 +a 8693 8694 4102 +a 8694 8695 4102 +a 8695 8696 4102 +a 8696 8697 4102 +a 8697 8698 4102 +a 8698 8699 4102 +a 8699 8700 4102 +a 8700 8701 4102 +a 8701 8702 4102 +a 8702 8703 4102 +a 8703 8704 4102 +a 8704 8705 4102 +a 8705 8706 4102 +a 8706 8707 4102 +a 8707 8708 4102 +a 8708 8709 4102 +a 8709 8710 4102 +a 8710 8711 4102 +a 8711 8712 4102 +a 8712 8713 4102 +a 8713 8714 4102 +a 8714 8715 4102 +a 8715 8716 4102 +a 8716 8717 4102 +a 8717 8718 4102 +a 8718 8719 4102 +a 8719 8720 4102 +a 8720 8721 4102 +a 8721 8722 4102 +a 8722 8723 4102 +a 8723 8724 4102 +a 8724 8725 4102 +a 8725 8726 4102 +a 8726 8727 4102 +a 8727 8728 4102 +a 8728 8729 4102 +a 8729 8730 4102 +a 8730 8731 4102 +a 8731 8732 4102 +a 8732 8733 4102 +a 8733 8734 4102 +a 8734 8735 4102 +a 8735 8736 4102 +a 8736 8737 4102 +a 8737 8738 4102 +a 8738 8739 4102 +a 8739 8740 4102 +a 8740 8741 4102 +a 8741 8742 4102 +a 8742 8743 4102 +a 8743 8744 4102 +a 8744 8745 4102 +a 8745 8746 4102 +a 8746 8747 4102 +a 8747 8748 4102 +a 8748 8749 4102 +a 8749 8750 4102 +a 8750 8751 4102 +a 8751 8752 4102 +a 8752 8753 4102 +a 8753 8754 4102 +a 8754 8755 4102 +a 8755 8756 4102 +a 8756 8757 4102 +a 8757 8758 4102 +a 8758 8759 4102 +a 8759 8760 4102 +a 8760 8761 4102 +a 8761 8762 4102 +a 8762 8763 4102 +a 8763 8764 4102 +a 8764 8765 4102 +a 8765 8766 4102 +a 8766 8767 4102 +a 8767 8768 4102 +a 8768 8769 4102 +a 8769 8770 4102 +a 8770 8771 4102 +a 8771 8772 4102 +a 8772 8773 4102 +a 8773 8774 4102 +a 8774 8775 4102 +a 8775 8776 4102 +a 8776 8777 4102 +a 8777 8778 4102 +a 8778 8779 4102 +a 8779 8780 4102 +a 8780 8781 4102 +a 8781 8782 4102 +a 8782 8783 4102 +a 8783 8784 4102 +a 8784 8785 4102 +a 8785 8786 4102 +a 8786 8787 4102 +a 8787 8788 4102 +a 8788 8789 4102 +a 8789 8790 4102 +a 8790 8791 4102 +a 8791 8792 4102 +a 8792 8793 4102 +a 8793 8794 4102 +a 8794 8795 4102 +a 8795 8796 4102 +a 8796 8797 4102 +a 8797 8798 4102 +a 8798 8799 4102 +a 8799 8800 4102 +a 8800 8801 4102 +a 8801 8802 4102 +a 8802 8803 4102 +a 8803 8804 4102 +a 8804 8805 4102 +a 8805 8806 4102 +a 8806 8807 4102 +a 8807 8808 4102 +a 8808 8809 4102 +a 8809 8810 4102 +a 8810 8811 4102 +a 8811 8812 4102 +a 8812 8813 4102 +a 8813 8814 4102 +a 8814 8815 4102 +a 8815 8816 4102 +a 8816 8817 4102 +a 8817 8818 4102 +a 8818 8819 4102 +a 8819 8820 4102 +a 8820 8821 4102 +a 8821 8822 4102 +a 8822 8823 4102 +a 8823 8824 4102 +a 8824 8825 4102 +a 8825 8826 4102 +a 8826 8827 4102 +a 8827 8828 4102 +a 8828 8829 4102 +a 8829 8830 4102 +a 8830 8831 4102 +a 8831 8832 4102 +a 8832 8833 4102 +a 8833 8834 4102 +a 8834 8835 4102 +a 8835 8836 4102 +a 8836 8837 4102 +a 8837 8838 4102 +a 8838 8839 4102 +a 8839 8840 4102 +a 8840 8841 4102 +a 8841 8842 4102 +a 8842 8843 4102 +a 8843 8844 4102 +a 8844 8845 4102 +a 8845 8846 4102 +a 8846 8847 4102 +a 8847 8848 4102 +a 8848 8849 4102 +a 8849 8850 4102 +a 8850 8851 4102 +a 8851 8852 4102 +a 8852 8853 4102 +a 8853 8854 4102 +a 8854 8855 4102 +a 8855 8856 4102 +a 8856 8857 4102 +a 8857 8858 4102 +a 8858 8859 4102 +a 8859 8860 4102 +a 8860 8861 4102 +a 8861 8862 4102 +a 8862 8863 4102 +a 8863 8864 4102 +a 8864 8865 4102 +a 8865 8866 4102 +a 8866 8867 4102 +a 8867 8868 4102 +a 8868 8869 4102 +a 8869 8870 4102 +a 8870 8871 4102 +a 8871 8872 4102 +a 8872 8873 4102 +a 8873 8874 4102 +a 8874 8875 4102 +a 8875 8876 4102 +a 8876 8877 4102 +a 8877 8878 4102 +a 8878 8879 4102 +a 8879 8880 4102 +a 8880 8881 4102 +a 8881 8882 4102 +a 8882 8883 4102 +a 8883 8884 4102 +a 8884 8885 4102 +a 8885 8886 4102 +a 8886 8887 4102 +a 8887 8888 4102 +a 8888 8889 4102 +a 8889 8890 4102 +a 8890 8891 4102 +a 8891 8892 4102 +a 8892 8893 4102 +a 8893 8894 4102 +a 8894 8895 4102 +a 8895 8896 4102 +a 8896 8897 4102 +a 8897 8898 4102 +a 8898 8899 4102 +a 8899 8900 4102 +a 8900 8901 4102 +a 8901 8902 4102 +a 8902 8903 4102 +a 8903 8904 4102 +a 8904 8905 4102 +a 8905 8906 4102 +a 8906 8907 4102 +a 8907 8908 4102 +a 8908 8909 4102 +a 8909 8910 4102 +a 8910 8911 4102 +a 8911 8912 4102 +a 8912 8913 4102 +a 8913 8914 4102 +a 8914 8915 4102 +a 8915 8916 4102 +a 8916 8917 4102 +a 8917 8918 4102 +a 8918 8919 4102 +a 8919 8920 4102 +a 8920 8921 4102 +a 8921 8922 4102 +a 8922 8923 4102 +a 8923 8924 4102 +a 8924 8925 4102 +a 8925 8926 4102 +a 8926 8927 4102 +a 8927 8928 4102 +a 8928 8929 4102 +a 8929 8930 4102 +a 8930 8931 4102 +a 8931 8932 4102 +a 8932 8933 4102 +a 8933 8934 4102 +a 8934 8935 4102 +a 8935 8936 4102 +a 8936 8937 4102 +a 8937 8938 4102 +a 8938 8939 4102 +a 8939 8940 4102 +a 8940 8941 4102 +a 8941 8942 4102 +a 8942 8943 4102 +a 8943 8944 4102 +a 8944 8945 4102 +a 8945 8946 4102 +a 8946 8947 4102 +a 8947 8948 4102 +a 8948 8949 4102 +a 8949 8950 4102 +a 8950 8951 4102 +a 8951 8952 4102 +a 8952 8953 4102 +a 8953 8954 4102 +a 8954 8955 4102 +a 8955 8956 4102 +a 8956 8957 4102 +a 8957 8958 4102 +a 8958 8959 4102 +a 8959 8960 4102 +a 8960 8961 4102 +a 8961 8962 4102 +a 8962 8963 4102 +a 8963 8964 4102 +a 8964 8965 4102 +a 8965 8966 4102 +a 8966 8967 4102 +a 8967 8968 4102 +a 8968 8969 4102 +a 8969 8970 4102 +a 8970 8971 4102 +a 8971 8972 4102 +a 8972 8973 4102 +a 8973 8974 4102 +a 8974 8975 4102 +a 8975 8976 4102 +a 8976 8977 4102 +a 8977 8978 4102 +a 8978 8979 4102 +a 8979 8980 4102 +a 8980 8981 4102 +a 8981 8982 4102 +a 8982 8983 4102 +a 8983 8984 4102 +a 8984 8985 4102 +a 8985 8986 4102 +a 8986 8987 4102 +a 8987 8988 4102 +a 8988 8989 4102 +a 8989 8990 4102 +a 8990 8991 4102 +a 8991 8992 4102 +a 8992 8993 4102 +a 8993 8994 4102 +a 8994 8995 4102 +a 8995 8996 4102 +a 8996 8997 4102 +a 8997 8998 4102 +a 8998 8999 4102 +a 8999 9000 4102 +a 9000 9001 4102 +a 9001 9002 4102 +a 9002 9003 4102 +a 9003 9004 4102 +a 9004 9005 4102 +a 9005 9006 4102 +a 9006 9007 4102 +a 9007 9008 4102 +a 9008 9009 4102 +a 9009 9010 4102 +a 9010 9011 4102 +a 9011 9012 4102 +a 9012 9013 4102 +a 9013 9014 4102 +a 9014 9015 4102 +a 9015 9016 4102 +a 9016 9017 4102 +a 9017 9018 4102 +a 9018 9019 4102 +a 9019 9020 4102 +a 9020 9021 4102 +a 9021 9022 4102 +a 9022 9023 4102 +a 9023 9024 4102 +a 9024 9025 4102 +a 9025 9026 4102 +a 9026 9027 4102 +a 9027 9028 4102 +a 9028 9029 4102 +a 9029 9030 4102 +a 9030 9031 4102 +a 9031 9032 4102 +a 9032 9033 4102 +a 9033 9034 4102 +a 9034 9035 4102 +a 9035 9036 4102 +a 9036 9037 4102 +a 9037 9038 4102 +a 9038 9039 4102 +a 9039 9040 4102 +a 9040 9041 4102 +a 9041 9042 4102 +a 9042 9043 4102 +a 9043 9044 4102 +a 9044 9045 4102 +a 9045 9046 4102 +a 9046 9047 4102 +a 9047 9048 4102 +a 9048 9049 4102 +a 9049 9050 4102 +a 9050 9051 4102 +a 9051 9052 4102 +a 9052 9053 4102 +a 9053 9054 4102 +a 9054 9055 4102 +a 9055 9056 4102 +a 9056 9057 4102 +a 9057 9058 4102 +a 9058 9059 4102 +a 9059 9060 4102 +a 9060 9061 4102 +a 9061 9062 4102 +a 9062 9063 4102 +a 9063 9064 4102 +a 9064 9065 4102 +a 9065 9066 4102 +a 9066 9067 4102 +a 9067 9068 4102 +a 9068 9069 4102 +a 9069 9070 4102 +a 9070 9071 4102 +a 9071 9072 4102 +a 9072 9073 4102 +a 9073 9074 4102 +a 9074 9075 4102 +a 9075 9076 4102 +a 9076 9077 4102 +a 9077 9078 4102 +a 9078 9079 4102 +a 9079 9080 4102 +a 9080 9081 4102 +a 9081 9082 4102 +a 9082 9083 4102 +a 9083 9084 4102 +a 9084 9085 4102 +a 9085 9086 4102 +a 9086 9087 4102 +a 9087 9088 4102 +a 9088 9089 4102 +a 9089 9090 4102 +a 9090 9091 4102 +a 9091 9092 4102 +a 9092 9093 4102 +a 9093 9094 4102 +a 9094 9095 4102 +a 9095 9096 4102 +a 9096 9097 4102 +a 9097 9098 4102 +a 9098 9099 4102 +a 9099 9100 4102 +a 9100 9101 4102 +a 9101 9102 4102 +a 9102 9103 4102 +a 9103 9104 4102 +a 9104 9105 4102 +a 9105 9106 4102 +a 9106 9107 4102 +a 9107 9108 4102 +a 9108 9109 4102 +a 9109 9110 4102 +a 9110 9111 4102 +a 9111 9112 4102 +a 9112 9113 4102 +a 9113 9114 4102 +a 9114 9115 4102 +a 9115 9116 4102 +a 9116 9117 4102 +a 9117 9118 4102 +a 9118 9119 4102 +a 9119 9120 4102 +a 9120 9121 4102 +a 9121 9122 4102 +a 9122 9123 4102 +a 9123 9124 4102 +a 9124 9125 4102 +a 9125 9126 4102 +a 9126 9127 4102 +a 9127 9128 4102 +a 9128 9129 4102 +a 9129 9130 4102 +a 9130 9131 4102 +a 9131 9132 4102 +a 9132 9133 4102 +a 9133 9134 4102 +a 9134 9135 4102 +a 9135 9136 4102 +a 9136 9137 4102 +a 9137 9138 4102 +a 9138 9139 4102 +a 9139 9140 4102 +a 9140 9141 4102 +a 9141 9142 4102 +a 9142 9143 4102 +a 9143 9144 4102 +a 9144 9145 4102 +a 9145 9146 4102 +a 9146 9147 4102 +a 9147 9148 4102 +a 9148 9149 4102 +a 9149 9150 4102 +a 9150 9151 4102 +a 9151 9152 4102 +a 9152 9153 4102 +a 9153 9154 4102 +a 9154 9155 4102 +a 9155 9156 4102 +a 9156 9157 4102 +a 9157 9158 4102 +a 9158 9159 4102 +a 9159 9160 4102 +a 9160 9161 4102 +a 9161 9162 4102 +a 9162 9163 4102 +a 9163 9164 4102 +a 9164 9165 4102 +a 9165 9166 4102 +a 9166 9167 4102 +a 9167 9168 4102 +a 9168 9169 4102 +a 9169 9170 4102 +a 9170 9171 4102 +a 9171 9172 4102 +a 9172 9173 4102 +a 9173 9174 4102 +a 9174 9175 4102 +a 9175 9176 4102 +a 9176 9177 4102 +a 9177 9178 4102 +a 9178 9179 4102 +a 9179 9180 4102 +a 9180 9181 4102 +a 9181 9182 4102 +a 9182 9183 4102 +a 9183 9184 4102 +a 9184 9185 4102 +a 9185 9186 4102 +a 9186 9187 4102 +a 9187 9188 4102 +a 9188 9189 4102 +a 9189 9190 4102 +a 9190 9191 4102 +a 9191 9192 4102 +a 9192 9193 4102 +a 9193 9194 4102 +a 9194 9195 4102 +a 9195 9196 4102 +a 9196 9197 4102 +a 9197 9198 4102 +a 9198 9199 4102 +a 9199 9200 4102 +a 9200 9201 4102 +a 9201 9202 4102 +a 9202 9203 4102 +a 9203 9204 4102 +a 9204 9205 4102 +a 9205 9206 4102 +a 9206 9207 4102 +a 9207 9208 4102 +a 9208 9209 4102 +a 9209 9210 4102 +a 9210 9211 4102 +a 9211 9212 4102 +a 9212 9213 4102 +a 9213 9214 4102 +a 9214 9215 4102 +a 9215 9216 4102 +a 9216 9217 4102 +a 9217 9218 4102 +a 9218 9219 4102 +a 9219 9220 4102 +a 9220 9221 4102 +a 9221 9222 4102 +a 9222 9223 4102 +a 9223 9224 4102 +a 9224 9225 4102 +a 9225 9226 4102 +a 9226 9227 4102 +a 9227 9228 4102 +a 9228 9229 4102 +a 9229 9230 4102 +a 9230 9231 4102 +a 9231 9232 4102 +a 9232 9233 4102 +a 9233 9234 4102 +a 9234 9235 4102 +a 9235 9236 4102 +a 9236 9237 4102 +a 9237 9238 4102 +a 9238 9239 4102 +a 9239 9240 4102 +a 9240 9241 4102 +a 9241 9242 4102 +a 9242 9243 4102 +a 9243 9244 4102 +a 9244 9245 4102 +a 9245 9246 4102 +a 9246 9247 4102 +a 9247 9248 4102 +a 9248 9249 4102 +a 9249 9250 4102 +a 9250 9251 4102 +a 9251 9252 4102 +a 9252 9253 4102 +a 9253 9254 4102 +a 9254 9255 4102 +a 9255 9256 4102 +a 9256 9257 4102 +a 9257 9258 4102 +a 9258 9259 4102 +a 9259 9260 4102 +a 9260 9261 4102 +a 9261 9262 4102 +a 9262 9263 4102 +a 9263 9264 4102 +a 9264 9265 4102 +a 9265 9266 4102 +a 9266 9267 4102 +a 9267 9268 4102 +a 9268 9269 4102 +a 9269 9270 4102 +a 9270 9271 4102 +a 9271 9272 4102 +a 9272 9273 4102 +a 9273 9274 4102 +a 9274 9275 4102 +a 9275 9276 4102 +a 9276 9277 4102 +a 9277 9278 4102 +a 9278 9279 4102 +a 9279 9280 4102 +a 9280 9281 4102 +a 9281 9282 4102 +a 9282 9283 4102 +a 9283 9284 4102 +a 9284 9285 4102 +a 9285 9286 4102 +a 9286 9287 4102 +a 9287 9288 4102 +a 9288 9289 4102 +a 9289 9290 4102 +a 9290 9291 4102 +a 9291 9292 4102 +a 9292 9293 4102 +a 9293 9294 4102 +a 9294 9295 4102 +a 9295 9296 4102 +a 9296 9297 4102 +a 9297 9298 4102 +a 9298 9299 4102 +a 9299 9300 4102 +a 9300 9301 4102 +a 9301 9302 4102 +a 9302 9303 4102 +a 9303 9304 4102 +a 9304 9305 4102 +a 9305 9306 4102 +a 9306 9307 4102 +a 9307 9308 4102 +a 9308 9309 4102 +a 9309 9310 4102 +a 9310 9311 4102 +a 9311 9312 4102 +a 9312 9313 4102 +a 9313 9314 4102 +a 9314 9315 4102 +a 9315 9316 4102 +a 9316 9317 4102 +a 9317 9318 4102 +a 9318 9319 4102 +a 9319 9320 4102 +a 9320 9321 4102 +a 9321 9322 4102 +a 9322 9323 4102 +a 9323 9324 4102 +a 9324 9325 4102 +a 9325 9326 4102 +a 9326 9327 4102 +a 9327 9328 4102 +a 9328 9329 4102 +a 9329 9330 4102 +a 9330 9331 4102 +a 9331 9332 4102 +a 9332 9333 4102 +a 9333 9334 4102 +a 9334 9335 4102 +a 9335 9336 4102 +a 9336 9337 4102 +a 9337 9338 4102 +a 9338 9339 4102 +a 9339 9340 4102 +a 9340 9341 4102 +a 9341 9342 4102 +a 9342 9343 4102 +a 9343 9344 4102 +a 9344 9345 4102 +a 9345 9346 4102 +a 9346 9347 4102 +a 9347 9348 4102 +a 9348 9349 4102 +a 9349 9350 4102 +a 9350 9351 4102 +a 9351 9352 4102 +a 9352 9353 4102 +a 9353 9354 4102 +a 9354 9355 4102 +a 9355 9356 4102 +a 9356 9357 4102 +a 9357 9358 4102 +a 9358 9359 4102 +a 9359 9360 4102 +a 9360 9361 4102 +a 9361 9362 4102 +a 9362 9363 4102 +a 9363 9364 4102 +a 9364 9365 4102 +a 9365 9366 4102 +a 9366 9367 4102 +a 9367 9368 4102 +a 9368 9369 4102 +a 9369 9370 4102 +a 9370 9371 4102 +a 9371 9372 4102 +a 9372 9373 4102 +a 9373 9374 4102 +a 9374 9375 4102 +a 9375 9376 4102 +a 9376 9377 4102 +a 9377 9378 4102 +a 9378 9379 4102 +a 9379 9380 4102 +a 9380 9381 4102 +a 9381 9382 4102 +a 9382 9383 4102 +a 9383 9384 4102 +a 9384 9385 4102 +a 9385 9386 4102 +a 9386 9387 4102 +a 9387 9388 4102 +a 9388 9389 4102 +a 9389 9390 4102 +a 9390 9391 4102 +a 9391 9392 4102 +a 9392 9393 4102 +a 9393 9394 4102 +a 9394 9395 4102 +a 9395 9396 4102 +a 9396 9397 4102 +a 9397 9398 4102 +a 9398 9399 4102 +a 9399 9400 4102 +a 9400 9401 4102 +a 9401 9402 4102 +a 9402 9403 4102 +a 9403 9404 4102 +a 9404 9405 4102 +a 9405 9406 4102 +a 9406 9407 4102 +a 9407 9408 4102 +a 9408 9409 4102 +a 9409 9410 4102 +a 9410 9411 4102 +a 9411 9412 4102 +a 9412 9413 4102 +a 9413 9414 4102 +a 9414 9415 4102 +a 9415 9416 4102 +a 9416 9417 4102 +a 9417 9418 4102 +a 9418 9419 4102 +a 9419 9420 4102 +a 9420 9421 4102 +a 9421 9422 4102 +a 9422 9423 4102 +a 9423 9424 4102 +a 9424 9425 4102 +a 9425 9426 4102 +a 9426 9427 4102 +a 9427 9428 4102 +a 9428 9429 4102 +a 9429 9430 4102 +a 9430 9431 4102 +a 9431 9432 4102 +a 9432 9433 4102 +a 9433 9434 4102 +a 9434 9435 4102 +a 9435 9436 4102 +a 9436 9437 4102 +a 9437 9438 4102 +a 9438 9439 4102 +a 9439 9440 4102 +a 9440 9441 4102 +a 9441 9442 4102 +a 9442 9443 4102 +a 9443 9444 4102 +a 9444 9445 4102 +a 9445 9446 4102 +a 9446 9447 4102 +a 9447 9448 4102 +a 9448 9449 4102 +a 9449 9450 4102 +a 9450 9451 4102 +a 9451 9452 4102 +a 9452 9453 4102 +a 9453 9454 4102 +a 9454 9455 4102 +a 9455 9456 4102 +a 9456 9457 4102 +a 9457 9458 4102 +a 9458 9459 4102 +a 9459 9460 4102 +a 9460 9461 4102 +a 9461 9462 4102 +a 9462 9463 4102 +a 9463 9464 4102 +a 9464 9465 4102 +a 9465 9466 4102 +a 9466 9467 4102 +a 9467 9468 4102 +a 9468 9469 4102 +a 9469 9470 4102 +a 9470 9471 4102 +a 9471 9472 4102 +a 9472 9473 4102 +a 9473 9474 4102 +a 9474 9475 4102 +a 9475 9476 4102 +a 9476 9477 4102 +a 9477 9478 4102 +a 9478 9479 4102 +a 9479 9480 4102 +a 9480 9481 4102 +a 9481 9482 4102 +a 9482 9483 4102 +a 9483 9484 4102 +a 9484 9485 4102 +a 9485 9486 4102 +a 9486 9487 4102 +a 9487 9488 4102 +a 9488 9489 4102 +a 9489 9490 4102 +a 9490 9491 4102 +a 9491 9492 4102 +a 9492 9493 4102 +a 9493 9494 4102 +a 9494 9495 4102 +a 9495 9496 4102 +a 9496 9497 4102 +a 9497 9498 4102 +a 9498 9499 4102 +a 9499 9500 4102 +a 9500 9501 4102 +a 9501 9502 4102 +a 9502 9503 4102 +a 9503 9504 4102 +a 9504 9505 4102 +a 9505 9506 4102 +a 9506 9507 4102 +a 9507 9508 4102 +a 9508 9509 4102 +a 9509 9510 4102 +a 9510 9511 4102 +a 9511 9512 4102 +a 9512 9513 4102 +a 9513 9514 4102 +a 9514 9515 4102 +a 9515 9516 4102 +a 9516 9517 4102 +a 9517 9518 4102 +a 9518 9519 4102 +a 9519 9520 4102 +a 9520 9521 4102 +a 9521 9522 4102 +a 9522 9523 4102 +a 9523 9524 4102 +a 9524 9525 4102 +a 9525 9526 4102 +a 9526 9527 4102 +a 9527 9528 4102 +a 9528 9529 4102 +a 9529 9530 4102 +a 9530 9531 4102 +a 9531 9532 4102 +a 9532 9533 4102 +a 9533 9534 4102 +a 9534 9535 4102 +a 9535 9536 4102 +a 9536 9537 4102 +a 9537 9538 4102 +a 9538 9539 4102 +a 9539 9540 4102 +a 9540 9541 4102 +a 9541 9542 4102 +a 9542 9543 4102 +a 9543 9544 4102 +a 9544 9545 4102 +a 9545 9546 4102 +a 9546 9547 4102 +a 9547 9548 4102 +a 9548 9549 4102 +a 9549 9550 4102 +a 9550 9551 4102 +a 9551 9552 4102 +a 9552 9553 4102 +a 9553 9554 4102 +a 9554 9555 4102 +a 9555 9556 4102 +a 9556 9557 4102 +a 9557 9558 4102 +a 9558 9559 4102 +a 9559 9560 4102 +a 9560 9561 4102 +a 9561 9562 4102 +a 9562 9563 4102 +a 9563 9564 4102 +a 9564 9565 4102 +a 9565 9566 4102 +a 9566 9567 4102 +a 9567 9568 4102 +a 9568 9569 4102 +a 9569 9570 4102 +a 9570 9571 4102 +a 9571 9572 4102 +a 9572 9573 4102 +a 9573 9574 4102 +a 9574 9575 4102 +a 9575 9576 4102 +a 9576 9577 4102 +a 9577 9578 4102 +a 9578 9579 4102 +a 9579 9580 4102 +a 9580 9581 4102 +a 9581 9582 4102 +a 9582 9583 4102 +a 9583 9584 4102 +a 9584 9585 4102 +a 9585 9586 4102 +a 9586 9587 4102 +a 9587 9588 4102 +a 9588 9589 4102 +a 9589 9590 4102 +a 9590 9591 4102 +a 9591 9592 4102 +a 9592 9593 4102 +a 9593 9594 4102 +a 9594 9595 4102 +a 9595 9596 4102 +a 9596 9597 4102 +a 9597 9598 4102 +a 9598 9599 4102 +a 9599 9600 4102 +a 9600 9601 4102 +a 9601 9602 4102 +a 9602 9603 4102 +a 9603 9604 4102 +a 9604 9605 4102 +a 9605 9606 4102 +a 9606 9607 4102 +a 9607 9608 4102 +a 9608 9609 4102 +a 9609 9610 4102 +a 9610 9611 4102 +a 9611 9612 4102 +a 9612 9613 4102 +a 9613 9614 4102 +a 9614 9615 4102 +a 9615 9616 4102 +a 9616 9617 4102 +a 9617 9618 4102 +a 9618 9619 4102 +a 9619 9620 4102 +a 9620 9621 4102 +a 9621 9622 4102 +a 9622 9623 4102 +a 9623 9624 4102 +a 9624 9625 4102 +a 9625 9626 4102 +a 9626 9627 4102 +a 9627 9628 4102 +a 9628 9629 4102 +a 9629 9630 4102 +a 9630 9631 4102 +a 9631 9632 4102 +a 9632 9633 4102 +a 9633 9634 4102 +a 9634 9635 4102 +a 9635 9636 4102 +a 9636 9637 4102 +a 9637 9638 4102 +a 9638 9639 4102 +a 9639 9640 4102 +a 9640 9641 4102 +a 9641 9642 4102 +a 9642 9643 4102 +a 9643 9644 4102 +a 9644 9645 4102 +a 9645 9646 4102 +a 9646 9647 4102 +a 9647 9648 4102 +a 9648 9649 4102 +a 9649 9650 4102 +a 9650 9651 4102 +a 9651 9652 4102 +a 9652 9653 4102 +a 9653 9654 4102 +a 9654 9655 4102 +a 9655 9656 4102 +a 9656 9657 4102 +a 9657 9658 4102 +a 9658 9659 4102 +a 9659 9660 4102 +a 9660 9661 4102 +a 9661 9662 4102 +a 9662 9663 4102 +a 9663 9664 4102 +a 9664 9665 4102 +a 9665 9666 4102 +a 9666 9667 4102 +a 9667 9668 4102 +a 9668 9669 4102 +a 9669 9670 4102 +a 9670 9671 4102 +a 9671 9672 4102 +a 9672 9673 4102 +a 9673 9674 4102 +a 9674 9675 4102 +a 9675 9676 4102 +a 9676 9677 4102 +a 9677 9678 4102 +a 9678 9679 4102 +a 9679 9680 4102 +a 9680 9681 4102 +a 9681 9682 4102 +a 9682 9683 4102 +a 9683 9684 4102 +a 9684 9685 4102 +a 9685 9686 4102 +a 9686 9687 4102 +a 9687 9688 4102 +a 9688 9689 4102 +a 9689 9690 4102 +a 9690 9691 4102 +a 9691 9692 4102 +a 9692 9693 4102 +a 9693 9694 4102 +a 9694 9695 4102 +a 9695 9696 4102 +a 9696 9697 4102 +a 9697 9698 4102 +a 9698 9699 4102 +a 9699 9700 4102 +a 9700 9701 4102 +a 9701 9702 4102 +a 9702 9703 4102 +a 9703 9704 4102 +a 9704 9705 4102 +a 9705 9706 4102 +a 9706 9707 4102 +a 9707 9708 4102 +a 9708 9709 4102 +a 9709 9710 4102 +a 9710 9711 4102 +a 9711 9712 4102 +a 9712 9713 4102 +a 9713 9714 4102 +a 9714 9715 4102 +a 9715 9716 4102 +a 9716 9717 4102 +a 9717 9718 4102 +a 9718 9719 4102 +a 9719 9720 4102 +a 9720 9721 4102 +a 9721 9722 4102 +a 9722 9723 4102 +a 9723 9724 4102 +a 9724 9725 4102 +a 9725 9726 4102 +a 9726 9727 4102 +a 9727 9728 4102 +a 9728 9729 4102 +a 9729 9730 4102 +a 9730 9731 4102 +a 9731 9732 4102 +a 9732 9733 4102 +a 9733 9734 4102 +a 9734 9735 4102 +a 9735 9736 4102 +a 9736 9737 4102 +a 9737 9738 4102 +a 9738 9739 4102 +a 9739 9740 4102 +a 9740 9741 4102 +a 9741 9742 4102 +a 9742 9743 4102 +a 9743 9744 4102 +a 9744 9745 4102 +a 9745 9746 4102 +a 9746 9747 4102 +a 9747 9748 4102 +a 9748 9749 4102 +a 9749 9750 4102 +a 9750 9751 4102 +a 9751 9752 4102 +a 9752 9753 4102 +a 9753 9754 4102 +a 9754 9755 4102 +a 9755 9756 4102 +a 9756 9757 4102 +a 9757 9758 4102 +a 9758 9759 4102 +a 9759 9760 4102 +a 9760 9761 4102 +a 9761 9762 4102 +a 9762 9763 4102 +a 9763 9764 4102 +a 9764 9765 4102 +a 9765 9766 4102 +a 9766 9767 4102 +a 9767 9768 4102 +a 9768 9769 4102 +a 9769 9770 4102 +a 9770 9771 4102 +a 9771 9772 4102 +a 9772 9773 4102 +a 9773 9774 4102 +a 9774 9775 4102 +a 9775 9776 4102 +a 9776 9777 4102 +a 9777 9778 4102 +a 9778 9779 4102 +a 9779 9780 4102 +a 9780 9781 4102 +a 9781 9782 4102 +a 9782 9783 4102 +a 9783 9784 4102 +a 9784 9785 4102 +a 9785 9786 4102 +a 9786 9787 4102 +a 9787 9788 4102 +a 9788 9789 4102 +a 9789 9790 4102 +a 9790 9791 4102 +a 9791 9792 4102 +a 9792 9793 4102 +a 9793 9794 4102 +a 9794 9795 4102 +a 9795 9796 4102 +a 9796 9797 4102 +a 9797 9798 4102 +a 9798 9799 4102 +a 9799 9800 4102 +a 9800 9801 4102 +a 9801 9802 4102 +a 9802 9803 4102 +a 9803 9804 4102 +a 9804 9805 4102 +a 9805 9806 4102 +a 9806 9807 4102 +a 9807 9808 4102 +a 9808 9809 4102 +a 9809 9810 4102 +a 9810 9811 4102 +a 9811 9812 4102 +a 9812 9813 4102 +a 9813 9814 4102 +a 9814 9815 4102 +a 9815 9816 4102 +a 9816 9817 4102 +a 9817 9818 4102 +a 9818 9819 4102 +a 9819 9820 4102 +a 9820 9821 4102 +a 9821 9822 4102 +a 9822 9823 4102 +a 9823 9824 4102 +a 9824 9825 4102 +a 9825 9826 4102 +a 9826 9827 4102 +a 9827 9828 4102 +a 9828 9829 4102 +a 9829 9830 4102 +a 9830 9831 4102 +a 9831 9832 4102 +a 9832 9833 4102 +a 9833 9834 4102 +a 9834 9835 4102 +a 9835 9836 4102 +a 9836 9837 4102 +a 9837 9838 4102 +a 9838 9839 4102 +a 9839 9840 4102 +a 9840 9841 4102 +a 9841 9842 4102 +a 9842 9843 4102 +a 9843 9844 4102 +a 9844 9845 4102 +a 9845 9846 4102 +a 9846 9847 4102 +a 9847 9848 4102 +a 9848 9849 4102 +a 9849 9850 4102 +a 9850 9851 4102 +a 9851 9852 4102 +a 9852 9853 4102 +a 9853 9854 4102 +a 9854 9855 4102 +a 9855 9856 4102 +a 9856 9857 4102 +a 9857 9858 4102 +a 9858 9859 4102 +a 9859 9860 4102 +a 9860 9861 4102 +a 9861 9862 4102 +a 9862 9863 4102 +a 9863 9864 4102 +a 9864 9865 4102 +a 9865 9866 4102 +a 9866 9867 4102 +a 9867 9868 4102 +a 9868 9869 4102 +a 9869 9870 4102 +a 9870 9871 4102 +a 9871 9872 4102 +a 9872 9873 4102 +a 9873 9874 4102 +a 9874 9875 4102 +a 9875 9876 4102 +a 9876 9877 4102 +a 9877 9878 4102 +a 9878 9879 4102 +a 9879 9880 4102 +a 9880 9881 4102 +a 9881 9882 4102 +a 9882 9883 4102 +a 9883 9884 4102 +a 9884 9885 4102 +a 9885 9886 4102 +a 9886 9887 4102 +a 9887 9888 4102 +a 9888 9889 4102 +a 9889 9890 4102 +a 9890 9891 4102 +a 9891 9892 4102 +a 9892 9893 4102 +a 9893 9894 4102 +a 9894 9895 4102 +a 9895 9896 4102 +a 9896 9897 4102 +a 9897 9898 4102 +a 9898 9899 4102 +a 9899 9900 4102 +a 9900 9901 4102 +a 9901 9902 4102 +a 9902 9903 4102 +a 9903 9904 4102 +a 9904 9905 4102 +a 9905 9906 4102 +a 9906 9907 4102 +a 9907 9908 4102 +a 9908 9909 4102 +a 9909 9910 4102 +a 9910 9911 4102 +a 9911 9912 4102 +a 9912 9913 4102 +a 9913 9914 4102 +a 9914 9915 4102 +a 9915 9916 4102 +a 9916 9917 4102 +a 9917 9918 4102 +a 9918 9919 4102 +a 9919 9920 4102 +a 9920 9921 4102 +a 9921 9922 4102 +a 9922 9923 4102 +a 9923 9924 4102 +a 9924 9925 4102 +a 9925 9926 4102 +a 9926 9927 4102 +a 9927 9928 4102 +a 9928 9929 4102 +a 9929 9930 4102 +a 9930 9931 4102 +a 9931 9932 4102 +a 9932 9933 4102 +a 9933 9934 4102 +a 9934 9935 4102 +a 9935 9936 4102 +a 9936 9937 4102 +a 9937 9938 4102 +a 9938 9939 4102 +a 9939 9940 4102 +a 9940 9941 4102 +a 9941 9942 4102 +a 9942 9943 4102 +a 9943 9944 4102 +a 9944 9945 4102 +a 9945 9946 4102 +a 9946 9947 4102 +a 9947 9948 4102 +a 9948 9949 4102 +a 9949 9950 4102 +a 9950 9951 4102 +a 9951 9952 4102 +a 9952 9953 4102 +a 9953 9954 4102 +a 9954 9955 4102 +a 9955 9956 4102 +a 9956 9957 4102 +a 9957 9958 4102 +a 9958 9959 4102 +a 9959 9960 4102 +a 9960 9961 4102 +a 9961 9962 4102 +a 9962 9963 4102 +a 9963 9964 4102 +a 9964 9965 4102 +a 9965 9966 4102 +a 9966 9967 4102 +a 9967 9968 4102 +a 9968 9969 4102 +a 9969 9970 4102 +a 9970 9971 4102 +a 9971 9972 4102 +a 9972 9973 4102 +a 9973 9974 4102 +a 9974 9975 4102 +a 9975 9976 4102 +a 9976 9977 4102 +a 9977 9978 4102 +a 9978 9979 4102 +a 9979 9980 4102 +a 9980 9981 4102 +a 9981 9982 4102 +a 9982 9983 4102 +a 9983 9984 4102 +a 9984 9985 4102 +a 9985 9986 4102 +a 9986 9987 4102 +a 9987 9988 4102 +a 9988 9989 4102 +a 9989 9990 4102 +a 9990 9991 4102 +a 9991 9992 4102 +a 9992 9993 4102 +a 9993 9994 4102 +a 9994 9995 4102 +a 9995 9996 4102 +a 9996 9997 4102 +a 9997 9998 4102 +a 9998 9999 4102 +a 9999 10000 4102 +a 10000 10001 4102 +a 10001 10002 4102 +a 10002 10003 4102 +a 10003 10004 4102 +a 10004 10005 4102 +a 10005 10006 4102 +a 10006 10007 4102 +a 10007 10008 4102 +a 10008 10009 4102 +a 10009 10010 4102 +a 10010 10011 4102 +a 10011 10012 4102 +a 10012 10013 4102 +a 10013 10014 4102 +a 10014 10015 4102 +a 10015 10016 4102 +a 10016 10017 4102 +a 10017 10018 4102 +a 10018 10019 4102 +a 10019 10020 4102 +a 10020 10021 4102 +a 10021 10022 4102 +a 10022 10023 4102 +a 10023 10024 4102 +a 10024 10025 4102 +a 10025 10026 4102 +a 10026 10027 4102 +a 10027 10028 4102 +a 10028 10029 4102 +a 10029 10030 4102 +a 10030 10031 4102 +a 10031 10032 4102 +a 10032 10033 4102 +a 10033 10034 4102 +a 10034 10035 4102 +a 10035 10036 4102 +a 10036 10037 4102 +a 10037 10038 4102 +a 10038 10039 4102 +a 10039 10040 4102 +a 10040 10041 4102 +a 10041 10042 4102 +a 10042 10043 4102 +a 10043 10044 4102 +a 10044 10045 4102 +a 10045 10046 4102 +a 10046 10047 4102 +a 10047 10048 4102 +a 10048 10049 4102 +a 10049 10050 4102 +a 10050 10051 4102 +a 10051 10052 4102 +a 10052 10053 4102 +a 10053 10054 4102 +a 10054 10055 4102 +a 10055 10056 4102 +a 10056 10057 4102 +a 10057 10058 4102 +a 10058 10059 4102 +a 10059 10060 4102 +a 10060 10061 4102 +a 10061 10062 4102 +a 10062 10063 4102 +a 10063 10064 4102 +a 10064 10065 4102 +a 10065 10066 4102 +a 10066 10067 4102 +a 10067 10068 4102 +a 10068 10069 4102 +a 10069 10070 4102 +a 10070 10071 4102 +a 10071 10072 4102 +a 10072 10073 4102 +a 10073 10074 4102 +a 10074 10075 4102 +a 10075 10076 4102 +a 10076 10077 4102 +a 10077 10078 4102 +a 10078 10079 4102 +a 10079 10080 4102 +a 10080 10081 4102 +a 10081 10082 4102 +a 10082 10083 4102 +a 10083 10084 4102 +a 10084 10085 4102 +a 10085 10086 4102 +a 10086 10087 4102 +a 10087 10088 4102 +a 10088 10089 4102 +a 10089 10090 4102 +a 10090 10091 4102 +a 10091 10092 4102 +a 10092 10093 4102 +a 10093 10094 4102 +a 10094 10095 4102 +a 10095 10096 4102 +a 10096 10097 4102 +a 10097 10098 4102 +a 10098 10099 4102 +a 10099 10100 4102 +a 10100 10101 4102 +a 10101 10102 4102 +a 10102 10103 4102 +a 10103 10104 4102 +a 10104 10105 4102 +a 10105 10106 4102 +a 10106 10107 4102 +a 10107 10108 4102 +a 10108 10109 4102 +a 10109 10110 4102 +a 10110 10111 4102 +a 10111 10112 4102 +a 10112 10113 4102 +a 10113 10114 4102 +a 10114 10115 4102 +a 10115 10116 4102 +a 10116 10117 4102 +a 10117 10118 4102 +a 10118 10119 4102 +a 10119 10120 4102 +a 10120 10121 4102 +a 10121 10122 4102 +a 10122 10123 4102 +a 10123 10124 4102 +a 10124 10125 4102 +a 10125 10126 4102 +a 10126 10127 4102 +a 10127 10128 4102 +a 10128 10129 4102 +a 10129 10130 4102 +a 10130 10131 4102 +a 10131 10132 4102 +a 10132 10133 4102 +a 10133 10134 4102 +a 10134 10135 4102 +a 10135 10136 4102 +a 10136 10137 4102 +a 10137 10138 4102 +a 10138 10139 4102 +a 10139 10140 4102 +a 10140 10141 4102 +a 10141 10142 4102 +a 10142 10143 4102 +a 10143 10144 4102 +a 10144 10145 4102 +a 10145 10146 4102 +a 10146 10147 4102 +a 10147 10148 4102 +a 10148 10149 4102 +a 10149 10150 4102 +a 10150 10151 4102 +a 10151 10152 4102 +a 10152 10153 4102 +a 10153 10154 4102 +a 10154 10155 4102 +a 10155 10156 4102 +a 10156 10157 4102 +a 10157 10158 4102 +a 10158 10159 4102 +a 10159 10160 4102 +a 10160 10161 4102 +a 10161 10162 4102 +a 10162 10163 4102 +a 10163 10164 4102 +a 10164 10165 4102 +a 10165 10166 4102 +a 10166 10167 4102 +a 10167 10168 4102 +a 10168 10169 4102 +a 10169 10170 4102 +a 10170 10171 4102 +a 10171 10172 4102 +a 10172 10173 4102 +a 10173 10174 4102 +a 10174 10175 4102 +a 10175 10176 4102 +a 10176 10177 4102 +a 10177 10178 4102 +a 10178 10179 4102 +a 10179 10180 4102 +a 10180 10181 4102 +a 10181 10182 4102 +a 10182 10183 4102 +a 10183 10184 4102 +a 10184 10185 4102 +a 10185 10186 4102 +a 10186 10187 4102 +a 10187 10188 4102 +a 10188 10189 4102 +a 10189 10190 4102 +a 10190 10191 4102 +a 10191 10192 4102 +a 10192 10193 4102 +a 10193 10194 4102 +a 10194 10195 4102 +a 10195 10196 4102 +a 10196 10197 4102 +a 10197 10198 4102 +a 10198 10199 4102 +a 10199 10200 4102 +a 10200 10201 4102 +a 10201 10202 4102 +a 10202 10203 4102 +a 10203 10204 4102 +a 10204 10205 4102 +a 10205 10206 4102 +a 10206 10207 4102 +a 10207 10208 4102 +a 10208 10209 4102 +a 10209 10210 4102 +a 10210 10211 4102 +a 10211 10212 4102 +a 10212 10213 4102 +a 10213 10214 4102 +a 10214 10215 4102 +a 10215 10216 4102 +a 10216 10217 4102 +a 10217 10218 4102 +a 10218 10219 4102 +a 10219 10220 4102 +a 10220 10221 4102 +a 10221 10222 4102 +a 10222 10223 4102 +a 10223 10224 4102 +a 10224 10225 4102 +a 10225 10226 4102 +a 10226 10227 4102 +a 10227 10228 4102 +a 10228 10229 4102 +a 10229 10230 4102 +a 10230 10231 4102 +a 10231 10232 4102 +a 10232 10233 4102 +a 10233 10234 4102 +a 10234 10235 4102 +a 10235 10236 4102 +a 10236 10237 4102 +a 10237 10238 4102 +a 10238 10239 4102 +a 10239 10240 4102 +a 10240 10241 4102 +a 10241 10242 4102 +a 10242 10243 4102 +a 10243 10244 4102 +a 10244 10245 4102 +a 10245 10246 4102 +a 10246 10247 4102 +a 10247 10248 4102 +a 10248 10249 4102 +a 10249 10250 4102 +a 10250 10251 4102 +a 10251 10252 4102 +a 10252 10253 4102 +a 10253 10254 4102 +a 10254 10255 4102 +a 10255 10256 4102 +a 10256 10257 4102 +a 10257 10258 4102 +a 10258 10259 4102 +a 10259 10260 4102 +a 10260 10261 4102 +a 10261 10262 4102 +a 10262 10263 4102 +a 10263 10264 4102 +a 10264 10265 4102 +a 10265 10266 4102 +a 10266 10267 4102 +a 10267 10268 4102 +a 10268 10269 4102 +a 10269 10270 4102 +a 10270 10271 4102 +a 10271 10272 4102 +a 10272 10273 4102 +a 10273 10274 4102 +a 10274 10275 4102 +a 10275 10276 4102 +a 10276 10277 4102 +a 10277 10278 4102 +a 10278 10279 4102 +a 10279 10280 4102 +a 10280 10281 4102 +a 10281 10282 4102 +a 10282 10283 4102 +a 10283 10284 4102 +a 10284 10285 4102 +a 10285 10286 4102 +a 10286 10287 4102 +a 10287 10288 4102 +a 10288 10289 4102 +a 10289 10290 4102 +a 10290 10291 4102 +a 10291 10292 4102 +a 10292 10293 4102 +a 10293 10294 4102 +a 10294 10295 4102 +a 10295 10296 4102 +a 10296 10297 4102 +a 10297 10298 4102 +a 10298 10299 4102 +a 10299 10300 4102 +a 10300 10301 4102 +a 10301 10302 4102 +a 10302 10303 4102 +a 10303 10304 4102 +a 10304 10305 4102 +a 10305 10306 4102 +a 10306 10307 4102 +a 10307 10308 4102 +a 10308 10309 4102 +a 10309 10310 4102 +a 10310 10311 4102 +a 10311 10312 4102 +a 10312 10313 4102 +a 10313 10314 4102 +a 10314 10315 4102 +a 10315 10316 4102 +a 10316 10317 4102 +a 10317 10318 4102 +a 10318 10319 4102 +a 10319 10320 4102 +a 10320 10321 4102 +a 10321 10322 4102 +a 10322 10323 4102 +a 10323 10324 4102 +a 10324 10325 4102 +a 10325 10326 4102 +a 10326 10327 4102 +a 10327 10328 4102 +a 10328 10329 4102 +a 10329 10330 4102 +a 10330 10331 4102 +a 10331 10332 4102 +a 10332 10333 4102 +a 10333 10334 4102 +a 10334 10335 4102 +a 10335 10336 4102 +a 10336 10337 4102 +a 10337 10338 4102 +a 10338 10339 4102 +a 10339 10340 4102 +a 10340 10341 4102 +a 10341 10342 4102 +a 10342 10343 4102 +a 10343 10344 4102 +a 10344 10345 4102 +a 10345 10346 4102 +a 10346 10347 4102 +a 10347 10348 4102 +a 10348 10349 4102 +a 10349 10350 4102 +a 10350 10351 4102 +a 10351 10352 4102 +a 10352 10353 4102 +a 10353 10354 4102 +a 10354 10355 4102 +a 10355 10356 4102 +a 10356 10357 4102 +a 10357 10358 4102 +a 10358 10359 4102 +a 10359 10360 4102 +a 10360 10361 4102 +a 10361 10362 4102 +a 10362 10363 4102 +a 10363 10364 4102 +a 10364 10365 4102 +a 10365 10366 4102 +a 10366 10367 4102 +a 10367 10368 4102 +a 10368 10369 4102 +a 10369 10370 4102 +a 10370 10371 4102 +a 10371 10372 4102 +a 10372 10373 4102 +a 10373 10374 4102 +a 10374 10375 4102 +a 10375 10376 4102 +a 10376 10377 4102 +a 10377 10378 4102 +a 10378 10379 4102 +a 10379 10380 4102 +a 10380 10381 4102 +a 10381 10382 4102 +a 10382 10383 4102 +a 10383 10384 4102 +a 10384 10385 4102 +a 10385 10386 4102 +a 10386 10387 4102 +a 10387 10388 4102 +a 10388 10389 4102 +a 10389 10390 4102 +a 10390 10391 4102 +a 10391 10392 4102 +a 10392 10393 4102 +a 10393 10394 4102 +a 10394 10395 4102 +a 10395 10396 4102 +a 10396 10397 4102 +a 10397 10398 4102 +a 10398 10399 4102 +a 10399 10400 4102 +a 10400 10401 4102 +a 10401 10402 4102 +a 10402 10403 4102 +a 10403 10404 4102 +a 10404 10405 4102 +a 10405 10406 4102 +a 10406 10407 4102 +a 10407 10408 4102 +a 10408 10409 4102 +a 10409 10410 4102 +a 10410 10411 4102 +a 10411 10412 4102 +a 10412 10413 4102 +a 10413 10414 4102 +a 10414 10415 4102 +a 10415 10416 4102 +a 10416 10417 4102 +a 10417 10418 4102 +a 10418 10419 4102 +a 10419 10420 4102 +a 10420 10421 4102 +a 10421 10422 4102 +a 10422 10423 4102 +a 10423 10424 4102 +a 10424 10425 4102 +a 10425 10426 4102 +a 10426 10427 4102 +a 10427 10428 4102 +a 10428 10429 4102 +a 10429 10430 4102 +a 10430 10431 4102 +a 10431 10432 4102 +a 10432 10433 4102 +a 10433 10434 4102 +a 10434 10435 4102 +a 10435 10436 4102 +a 10436 10437 4102 +a 10437 10438 4102 +a 10438 10439 4102 +a 10439 10440 4102 +a 10440 10441 4102 +a 10441 10442 4102 +a 10442 10443 4102 +a 10443 10444 4102 +a 10444 10445 4102 +a 10445 10446 4102 +a 10446 10447 4102 +a 10447 10448 4102 +a 10448 10449 4102 +a 10449 10450 4102 +a 10450 10451 4102 +a 10451 10452 4102 +a 10452 10453 4102 +a 10453 10454 4102 +a 10454 10455 4102 +a 10455 10456 4102 +a 10456 10457 4102 +a 10457 10458 4102 +a 10458 10459 4102 +a 10459 10460 4102 +a 10460 10461 4102 +a 10461 10462 4102 +a 10462 10463 4102 +a 10463 10464 4102 +a 10464 10465 4102 +a 10465 10466 4102 +a 10466 10467 4102 +a 10467 10468 4102 +a 10468 10469 4102 +a 10469 10470 4102 +a 10470 10471 4102 +a 10471 10472 4102 +a 10472 10473 4102 +a 10473 10474 4102 +a 10474 10475 4102 +a 10475 10476 4102 +a 10476 10477 4102 +a 10477 10478 4102 +a 10478 10479 4102 +a 10479 10480 4102 +a 10480 10481 4102 +a 10481 10482 4102 +a 10482 10483 4102 +a 10483 10484 4102 +a 10484 10485 4102 +a 10485 10486 4102 +a 10486 10487 4102 +a 10487 10488 4102 +a 10488 10489 4102 +a 10489 10490 4102 +a 10490 10491 4102 +a 10491 10492 4102 +a 10492 10493 4102 +a 10493 10494 4102 +a 10494 10495 4102 +a 10495 10496 4102 +a 10496 10497 4102 +a 10497 10498 4102 +a 10498 10499 4102 +a 10499 10500 4102 +a 10500 10501 4102 +a 10501 10502 4102 +a 10502 10503 4102 +a 10503 10504 4102 +a 10504 10505 4102 +a 10505 10506 4102 +a 10506 10507 4102 +a 10507 10508 4102 +a 10508 10509 4102 +a 10509 10510 4102 +a 10510 10511 4102 +a 10511 10512 4102 +a 10512 10513 4102 +a 10513 10514 4102 +a 10514 10515 4102 +a 10515 10516 4102 +a 10516 10517 4102 +a 10517 10518 4102 +a 10518 10519 4102 +a 10519 10520 4102 +a 10520 10521 4102 +a 10521 10522 4102 +a 10522 10523 4102 +a 10523 10524 4102 +a 10524 10525 4102 +a 10525 10526 4102 +a 10526 10527 4102 +a 10527 10528 4102 +a 10528 10529 4102 +a 10529 10530 4102 +a 10530 10531 4102 +a 10531 10532 4102 +a 10532 10533 4102 +a 10533 10534 4102 +a 10534 10535 4102 +a 10535 10536 4102 +a 10536 10537 4102 +a 10537 10538 4102 +a 10538 10539 4102 +a 10539 10540 4102 +a 10540 10541 4102 +a 10541 10542 4102 +a 10542 10543 4102 +a 10543 10544 4102 +a 10544 10545 4102 +a 10545 10546 4102 +a 10546 10547 4102 +a 10547 10548 4102 +a 10548 10549 4102 +a 10549 10550 4102 +a 10550 10551 4102 +a 10551 10552 4102 +a 10552 10553 4102 +a 10553 10554 4102 +a 10554 10555 4102 +a 10555 10556 4102 +a 10556 10557 4102 +a 10557 10558 4102 +a 10558 10559 4102 +a 10559 10560 4102 +a 10560 10561 4102 +a 10561 10562 4102 +a 10562 10563 4102 +a 10563 10564 4102 +a 10564 10565 4102 +a 10565 10566 4102 +a 10566 10567 4102 +a 10567 10568 4102 +a 10568 10569 4102 +a 10569 10570 4102 +a 10570 10571 4102 +a 10571 10572 4102 +a 10572 10573 4102 +a 10573 10574 4102 +a 10574 10575 4102 +a 10575 10576 4102 +a 10576 10577 4102 +a 10577 10578 4102 +a 10578 10579 4102 +a 10579 10580 4102 +a 10580 10581 4102 +a 10581 10582 4102 +a 10582 10583 4102 +a 10583 10584 4102 +a 10584 10585 4102 +a 10585 10586 4102 +a 10586 10587 4102 +a 10587 10588 4102 +a 10588 10589 4102 +a 10589 10590 4102 +a 10590 10591 4102 +a 10591 10592 4102 +a 10592 10593 4102 +a 10593 10594 4102 +a 10594 10595 4102 +a 10595 10596 4102 +a 10596 10597 4102 +a 10597 10598 4102 +a 10598 10599 4102 +a 10599 10600 4102 +a 10600 10601 4102 +a 10601 10602 4102 +a 10602 10603 4102 +a 10603 10604 4102 +a 10604 10605 4102 +a 10605 10606 4102 +a 10606 10607 4102 +a 10607 10608 4102 +a 10608 10609 4102 +a 10609 10610 4102 +a 10610 10611 4102 +a 10611 10612 4102 +a 10612 10613 4102 +a 10613 10614 4102 +a 10614 10615 4102 +a 10615 10616 4102 +a 10616 10617 4102 +a 10617 10618 4102 +a 10618 10619 4102 +a 10619 10620 4102 +a 10620 10621 4102 +a 10621 10622 4102 +a 10622 10623 4102 +a 10623 10624 4102 +a 10624 10625 4102 +a 10625 10626 4102 +a 10626 10627 4102 +a 10627 10628 4102 +a 10628 10629 4102 +a 10629 10630 4102 +a 10630 10631 4102 +a 10631 10632 4102 +a 10632 10633 4102 +a 10633 10634 4102 +a 10634 10635 4102 +a 10635 10636 4102 +a 10636 10637 4102 +a 10637 10638 4102 +a 10638 10639 4102 +a 10639 10640 4102 +a 10640 10641 4102 +a 10641 10642 4102 +a 10642 10643 4102 +a 10643 10644 4102 +a 10644 10645 4102 +a 10645 10646 4102 +a 10646 10647 4102 +a 10647 10648 4102 +a 10648 10649 4102 +a 10649 10650 4102 +a 10650 10651 4102 +a 10651 10652 4102 +a 10652 10653 4102 +a 10653 10654 4102 +a 10654 10655 4102 +a 10655 10656 4102 +a 10656 10657 4102 +a 10657 10658 4102 +a 10658 10659 4102 +a 10659 10660 4102 +a 10660 10661 4102 +a 10661 10662 4102 +a 10662 10663 4102 +a 10663 10664 4102 +a 10664 10665 4102 +a 10665 10666 4102 +a 10666 10667 4102 +a 10667 10668 4102 +a 10668 10669 4102 +a 10669 10670 4102 +a 10670 10671 4102 +a 10671 10672 4102 +a 10672 10673 4102 +a 10673 10674 4102 +a 10674 10675 4102 +a 10675 10676 4102 +a 10676 10677 4102 +a 10677 10678 4102 +a 10678 10679 4102 +a 10679 10680 4102 +a 10680 10681 4102 +a 10681 10682 4102 +a 10682 10683 4102 +a 10683 10684 4102 +a 10684 10685 4102 +a 10685 10686 4102 +a 10686 10687 4102 +a 10687 10688 4102 +a 10688 10689 4102 +a 10689 10690 4102 +a 10690 10691 4102 +a 10691 10692 4102 +a 10692 10693 4102 +a 10693 10694 4102 +a 10694 10695 4102 +a 10695 10696 4102 +a 10696 10697 4102 +a 10697 10698 4102 +a 10698 10699 4102 +a 10699 10700 4102 +a 10700 10701 4102 +a 10701 10702 4102 +a 10702 10703 4102 +a 10703 10704 4102 +a 10704 10705 4102 +a 10705 10706 4102 +a 10706 10707 4102 +a 10707 10708 4102 +a 10708 10709 4102 +a 10709 10710 4102 +a 10710 10711 4102 +a 10711 10712 4102 +a 10712 10713 4102 +a 10713 10714 4102 +a 10714 10715 4102 +a 10715 10716 4102 +a 10716 10717 4102 +a 10717 10718 4102 +a 10718 10719 4102 +a 10719 10720 4102 +a 10720 10721 4102 +a 10721 10722 4102 +a 10722 10723 4102 +a 10723 10724 4102 +a 10724 10725 4102 +a 10725 10726 4102 +a 10726 10727 4102 +a 10727 10728 4102 +a 10728 10729 4102 +a 10729 10730 4102 +a 10730 10731 4102 +a 10731 10732 4102 +a 10732 10733 4102 +a 10733 10734 4102 +a 10734 10735 4102 +a 10735 10736 4102 +a 10736 10737 4102 +a 10737 10738 4102 +a 10738 10739 4102 +a 10739 10740 4102 +a 10740 10741 4102 +a 10741 10742 4102 +a 10742 10743 4102 +a 10743 10744 4102 +a 10744 10745 4102 +a 10745 10746 4102 +a 10746 10747 4102 +a 10747 10748 4102 +a 10748 10749 4102 +a 10749 10750 4102 +a 10750 10751 4102 +a 10751 10752 4102 +a 10752 10753 4102 +a 10753 10754 4102 +a 10754 10755 4102 +a 10755 10756 4102 +a 10756 10757 4102 +a 10757 10758 4102 +a 10758 10759 4102 +a 10759 10760 4102 +a 10760 10761 4102 +a 10761 10762 4102 +a 10762 10763 4102 +a 10763 10764 4102 +a 10764 10765 4102 +a 10765 10766 4102 +a 10766 10767 4102 +a 10767 10768 4102 +a 10768 10769 4102 +a 10769 10770 4102 +a 10770 10771 4102 +a 10771 10772 4102 +a 10772 10773 4102 +a 10773 10774 4102 +a 10774 10775 4102 +a 10775 10776 4102 +a 10776 10777 4102 +a 10777 10778 4102 +a 10778 10779 4102 +a 10779 10780 4102 +a 10780 10781 4102 +a 10781 10782 4102 +a 10782 10783 4102 +a 10783 10784 4102 +a 10784 10785 4102 +a 10785 10786 4102 +a 10786 10787 4102 +a 10787 10788 4102 +a 10788 10789 4102 +a 10789 10790 4102 +a 10790 10791 4102 +a 10791 10792 4102 +a 10792 10793 4102 +a 10793 10794 4102 +a 10794 10795 4102 +a 10795 10796 4102 +a 10796 10797 4102 +a 10797 10798 4102 +a 10798 10799 4102 +a 10799 10800 4102 +a 10800 10801 4102 +a 10801 10802 4102 +a 10802 10803 4102 +a 10803 10804 4102 +a 10804 10805 4102 +a 10805 10806 4102 +a 10806 10807 4102 +a 10807 10808 4102 +a 10808 10809 4102 +a 10809 10810 4102 +a 10810 10811 4102 +a 10811 10812 4102 +a 10812 10813 4102 +a 10813 10814 4102 +a 10814 10815 4102 +a 10815 10816 4102 +a 10816 10817 4102 +a 10817 10818 4102 +a 10818 10819 4102 +a 10819 10820 4102 +a 10820 10821 4102 +a 10821 10822 4102 +a 10822 10823 4102 +a 10823 10824 4102 +a 10824 10825 4102 +a 10825 10826 4102 +a 10826 10827 4102 +a 10827 10828 4102 +a 10828 10829 4102 +a 10829 10830 4102 +a 10830 10831 4102 +a 10831 10832 4102 +a 10832 10833 4102 +a 10833 10834 4102 +a 10834 10835 4102 +a 10835 10836 4102 +a 10836 10837 4102 +a 10837 10838 4102 +a 10838 10839 4102 +a 10839 10840 4102 +a 10840 10841 4102 +a 10841 10842 4102 +a 10842 10843 4102 +a 10843 10844 4102 +a 10844 10845 4102 +a 10845 10846 4102 +a 10846 10847 4102 +a 10847 10848 4102 +a 10848 10849 4102 +a 10849 10850 4102 +a 10850 10851 4102 +a 10851 10852 4102 +a 10852 10853 4102 +a 10853 10854 4102 +a 10854 10855 4102 +a 10855 10856 4102 +a 10856 10857 4102 +a 10857 10858 4102 +a 10858 10859 4102 +a 10859 10860 4102 +a 10860 10861 4102 +a 10861 10862 4102 +a 10862 10863 4102 +a 10863 10864 4102 +a 10864 10865 4102 +a 10865 10866 4102 +a 10866 10867 4102 +a 10867 10868 4102 +a 10868 10869 4102 +a 10869 10870 4102 +a 10870 10871 4102 +a 10871 10872 4102 +a 10872 10873 4102 +a 10873 10874 4102 +a 10874 10875 4102 +a 10875 10876 4102 +a 10876 10877 4102 +a 10877 10878 4102 +a 10878 10879 4102 +a 10879 10880 4102 +a 10880 10881 4102 +a 10881 10882 4102 +a 10882 10883 4102 +a 10883 10884 4102 +a 10884 10885 4102 +a 10885 10886 4102 +a 10886 10887 4102 +a 10887 10888 4102 +a 10888 10889 4102 +a 10889 10890 4102 +a 10890 10891 4102 +a 10891 10892 4102 +a 10892 10893 4102 +a 10893 10894 4102 +a 10894 10895 4102 +a 10895 10896 4102 +a 10896 10897 4102 +a 10897 10898 4102 +a 10898 10899 4102 +a 10899 10900 4102 +a 10900 10901 4102 +a 10901 10902 4102 +a 10902 10903 4102 +a 10903 10904 4102 +a 10904 10905 4102 +a 10905 10906 4102 +a 10906 10907 4102 +a 10907 10908 4102 +a 10908 10909 4102 +a 10909 10910 4102 +a 10910 10911 4102 +a 10911 10912 4102 +a 10912 10913 4102 +a 10913 10914 4102 +a 10914 10915 4102 +a 10915 10916 4102 +a 10916 10917 4102 +a 10917 10918 4102 +a 10918 10919 4102 +a 10919 10920 4102 +a 10920 10921 4102 +a 10921 10922 4102 +a 10922 10923 4102 +a 10923 10924 4102 +a 10924 10925 4102 +a 10925 10926 4102 +a 10926 10927 4102 +a 10927 10928 4102 +a 10928 10929 4102 +a 10929 10930 4102 +a 10930 10931 4102 +a 10931 10932 4102 +a 10932 10933 4102 +a 10933 10934 4102 +a 10934 10935 4102 +a 10935 10936 4102 +a 10936 10937 4102 +a 10937 10938 4102 +a 10938 10939 4102 +a 10939 10940 4102 +a 10940 10941 4102 +a 10941 10942 4102 +a 10942 10943 4102 +a 10943 10944 4102 +a 10944 10945 4102 +a 10945 10946 4102 +a 10946 10947 4102 +a 10947 10948 4102 +a 10948 10949 4102 +a 10949 10950 4102 +a 10950 10951 4102 +a 10951 10952 4102 +a 10952 10953 4102 +a 10953 10954 4102 +a 10954 10955 4102 +a 10955 10956 4102 +a 10956 10957 4102 +a 10957 10958 4102 +a 10958 10959 4102 +a 10959 10960 4102 +a 10960 10961 4102 +a 10961 10962 4102 +a 10962 10963 4102 +a 10963 10964 4102 +a 10964 10965 4102 +a 10965 10966 4102 +a 10966 10967 4102 +a 10967 10968 4102 +a 10968 10969 4102 +a 10969 10970 4102 +a 10970 10971 4102 +a 10971 10972 4102 +a 10972 10973 4102 +a 10973 10974 4102 +a 10974 10975 4102 +a 10975 10976 4102 +a 10976 10977 4102 +a 10977 10978 4102 +a 10978 10979 4102 +a 10979 10980 4102 +a 10980 10981 4102 +a 10981 10982 4102 +a 10982 10983 4102 +a 10983 10984 4102 +a 10984 10985 4102 +a 10985 10986 4102 +a 10986 10987 4102 +a 10987 10988 4102 +a 10988 10989 4102 +a 10989 10990 4102 +a 10990 10991 4102 +a 10991 10992 4102 +a 10992 10993 4102 +a 10993 10994 4102 +a 10994 10995 4102 +a 10995 10996 4102 +a 10996 10997 4102 +a 10997 10998 4102 +a 10998 10999 4102 +a 10999 11000 4102 +a 11000 11001 4102 +a 11001 11002 4102 +a 11002 11003 4102 +a 11003 11004 4102 +a 11004 11005 4102 +a 11005 11006 4102 +a 11006 11007 4102 +a 11007 11008 4102 +a 11008 11009 4102 +a 11009 11010 4102 +a 11010 11011 4102 +a 11011 11012 4102 +a 11012 11013 4102 +a 11013 11014 4102 +a 11014 11015 4102 +a 11015 11016 4102 +a 11016 11017 4102 +a 11017 11018 4102 +a 11018 11019 4102 +a 11019 11020 4102 +a 11020 11021 4102 +a 11021 11022 4102 +a 11022 11023 4102 +a 11023 11024 4102 +a 11024 11025 4102 +a 11025 11026 4102 +a 11026 11027 4102 +a 11027 11028 4102 +a 11028 11029 4102 +a 11029 11030 4102 +a 11030 11031 4102 +a 11031 11032 4102 +a 11032 11033 4102 +a 11033 11034 4102 +a 11034 11035 4102 +a 11035 11036 4102 +a 11036 11037 4102 +a 11037 11038 4102 +a 11038 11039 4102 +a 11039 11040 4102 +a 11040 11041 4102 +a 11041 11042 4102 +a 11042 11043 4102 +a 11043 11044 4102 +a 11044 11045 4102 +a 11045 11046 4102 +a 11046 11047 4102 +a 11047 11048 4102 +a 11048 11049 4102 +a 11049 11050 4102 +a 11050 11051 4102 +a 11051 11052 4102 +a 11052 11053 4102 +a 11053 11054 4102 +a 11054 11055 4102 +a 11055 11056 4102 +a 11056 11057 4102 +a 11057 11058 4102 +a 11058 11059 4102 +a 11059 11060 4102 +a 11060 11061 4102 +a 11061 11062 4102 +a 11062 11063 4102 +a 11063 11064 4102 +a 11064 11065 4102 +a 11065 11066 4102 +a 11066 11067 4102 +a 11067 11068 4102 +a 11068 11069 4102 +a 11069 11070 4102 +a 11070 11071 4102 +a 11071 11072 4102 +a 11072 11073 4102 +a 11073 11074 4102 +a 11074 11075 4102 +a 11075 11076 4102 +a 11076 11077 4102 +a 11077 11078 4102 +a 11078 11079 4102 +a 11079 11080 4102 +a 11080 11081 4102 +a 11081 11082 4102 +a 11082 11083 4102 +a 11083 11084 4102 +a 11084 11085 4102 +a 11085 11086 4102 +a 11086 11087 4102 +a 11087 11088 4102 +a 11088 11089 4102 +a 11089 11090 4102 +a 11090 11091 4102 +a 11091 11092 4102 +a 11092 11093 4102 +a 11093 11094 4102 +a 11094 11095 4102 +a 11095 11096 4102 +a 11096 11097 4102 +a 11097 11098 4102 +a 11098 11099 4102 +a 11099 11100 4102 +a 11100 11101 4102 +a 11101 11102 4102 +a 11102 11103 4102 +a 11103 11104 4102 +a 11104 11105 4102 +a 11105 11106 4102 +a 11106 11107 4102 +a 11107 11108 4102 +a 11108 11109 4102 +a 11109 11110 4102 +a 11110 11111 4102 +a 11111 11112 4102 +a 11112 11113 4102 +a 11113 11114 4102 +a 11114 11115 4102 +a 11115 11116 4102 +a 11116 11117 4102 +a 11117 11118 4102 +a 11118 11119 4102 +a 11119 11120 4102 +a 11120 11121 4102 +a 11121 11122 4102 +a 11122 11123 4102 +a 11123 11124 4102 +a 11124 11125 4102 +a 11125 11126 4102 +a 11126 11127 4102 +a 11127 11128 4102 +a 11128 11129 4102 +a 11129 11130 4102 +a 11130 11131 4102 +a 11131 11132 4102 +a 11132 11133 4102 +a 11133 11134 4102 +a 11134 11135 4102 +a 11135 11136 4102 +a 11136 11137 4102 +a 11137 11138 4102 +a 11138 11139 4102 +a 11139 11140 4102 +a 11140 11141 4102 +a 11141 11142 4102 +a 11142 11143 4102 +a 11143 11144 4102 +a 11144 11145 4102 +a 11145 11146 4102 +a 11146 11147 4102 +a 11147 11148 4102 +a 11148 11149 4102 +a 11149 11150 4102 +a 11150 11151 4102 +a 11151 11152 4102 +a 11152 11153 4102 +a 11153 11154 4102 +a 11154 11155 4102 +a 11155 11156 4102 +a 11156 11157 4102 +a 11157 11158 4102 +a 11158 11159 4102 +a 11159 11160 4102 +a 11160 11161 4102 +a 11161 11162 4102 +a 11162 11163 4102 +a 11163 11164 4102 +a 11164 11165 4102 +a 11165 11166 4102 +a 11166 11167 4102 +a 11167 11168 4102 +a 11168 11169 4102 +a 11169 11170 4102 +a 11170 11171 4102 +a 11171 11172 4102 +a 11172 11173 4102 +a 11173 11174 4102 +a 11174 11175 4102 +a 11175 11176 4102 +a 11176 11177 4102 +a 11177 11178 4102 +a 11178 11179 4102 +a 11179 11180 4102 +a 11180 11181 4102 +a 11181 11182 4102 +a 11182 11183 4102 +a 11183 11184 4102 +a 11184 11185 4102 +a 11185 11186 4102 +a 11186 11187 4102 +a 11187 11188 4102 +a 11188 11189 4102 +a 11189 11190 4102 +a 11190 11191 4102 +a 11191 11192 4102 +a 11192 11193 4102 +a 11193 11194 4102 +a 11194 11195 4102 +a 11195 11196 4102 +a 11196 11197 4102 +a 11197 11198 4102 +a 11198 11199 4102 +a 11199 11200 4102 +a 11200 11201 4102 +a 11201 11202 4102 +a 11202 11203 4102 +a 11203 11204 4102 +a 11204 11205 4102 +a 11205 11206 4102 +a 11206 11207 4102 +a 11207 11208 4102 +a 11208 11209 4102 +a 11209 11210 4102 +a 11210 11211 4102 +a 11211 11212 4102 +a 11212 11213 4102 +a 11213 11214 4102 +a 11214 11215 4102 +a 11215 11216 4102 +a 11216 11217 4102 +a 11217 11218 4102 +a 11218 11219 4102 +a 11219 11220 4102 +a 11220 11221 4102 +a 11221 11222 4102 +a 11222 11223 4102 +a 11223 11224 4102 +a 11224 11225 4102 +a 11225 11226 4102 +a 11226 11227 4102 +a 11227 11228 4102 +a 11228 11229 4102 +a 11229 11230 4102 +a 11230 11231 4102 +a 11231 11232 4102 +a 11232 11233 4102 +a 11233 11234 4102 +a 11234 11235 4102 +a 11235 11236 4102 +a 11236 11237 4102 +a 11237 11238 4102 +a 11238 11239 4102 +a 11239 11240 4102 +a 11240 11241 4102 +a 11241 11242 4102 +a 11242 11243 4102 +a 11243 11244 4102 +a 11244 11245 4102 +a 11245 11246 4102 +a 11246 11247 4102 +a 11247 11248 4102 +a 11248 11249 4102 +a 11249 11250 4102 +a 11250 11251 4102 +a 11251 11252 4102 +a 11252 11253 4102 +a 11253 11254 4102 +a 11254 11255 4102 +a 11255 11256 4102 +a 11256 11257 4102 +a 11257 11258 4102 +a 11258 11259 4102 +a 11259 11260 4102 +a 11260 11261 4102 +a 11261 11262 4102 +a 11262 11263 4102 +a 11263 11264 4102 +a 11264 11265 4102 +a 11265 11266 4102 +a 11266 11267 4102 +a 11267 11268 4102 +a 11268 11269 4102 +a 11269 11270 4102 +a 11270 11271 4102 +a 11271 11272 4102 +a 11272 11273 4102 +a 11273 11274 4102 +a 11274 11275 4102 +a 11275 11276 4102 +a 11276 11277 4102 +a 11277 11278 4102 +a 11278 11279 4102 +a 11279 11280 4102 +a 11280 11281 4102 +a 11281 11282 4102 +a 11282 11283 4102 +a 11283 11284 4102 +a 11284 11285 4102 +a 11285 11286 4102 +a 11286 11287 4102 +a 11287 11288 4102 +a 11288 11289 4102 +a 11289 11290 4102 +a 11290 11291 4102 +a 11291 11292 4102 +a 11292 11293 4102 +a 11293 11294 4102 +a 11294 11295 4102 +a 11295 11296 4102 +a 11296 11297 4102 +a 11297 11298 4102 +a 11298 11299 4102 +a 11299 11300 4102 +a 11300 11301 4102 +a 11301 11302 4102 +a 11302 11303 4102 +a 11303 11304 4102 +a 11304 11305 4102 +a 11305 11306 4102 +a 11306 11307 4102 +a 11307 11308 4102 +a 11308 11309 4102 +a 11309 11310 4102 +a 11310 11311 4102 +a 11311 11312 4102 +a 11312 11313 4102 +a 11313 11314 4102 +a 11314 11315 4102 +a 11315 11316 4102 +a 11316 11317 4102 +a 11317 11318 4102 +a 11318 11319 4102 +a 11319 11320 4102 +a 11320 11321 4102 +a 11321 11322 4102 +a 11322 11323 4102 +a 11323 11324 4102 +a 11324 11325 4102 +a 11325 11326 4102 +a 11326 11327 4102 +a 11327 11328 4102 +a 11328 11329 4102 +a 11329 11330 4102 +a 11330 11331 4102 +a 11331 11332 4102 +a 11332 11333 4102 +a 11333 11334 4102 +a 11334 11335 4102 +a 11335 11336 4102 +a 11336 11337 4102 +a 11337 11338 4102 +a 11338 11339 4102 +a 11339 11340 4102 +a 11340 11341 4102 +a 11341 11342 4102 +a 11342 11343 4102 +a 11343 11344 4102 +a 11344 11345 4102 +a 11345 11346 4102 +a 11346 11347 4102 +a 11347 11348 4102 +a 11348 11349 4102 +a 11349 11350 4102 +a 11350 11351 4102 +a 11351 11352 4102 +a 11352 11353 4102 +a 11353 11354 4102 +a 11354 11355 4102 +a 11355 11356 4102 +a 11356 11357 4102 +a 11357 11358 4102 +a 11358 11359 4102 +a 11359 11360 4102 +a 11360 11361 4102 +a 11361 11362 4102 +a 11362 11363 4102 +a 11363 11364 4102 +a 11364 11365 4102 +a 11365 11366 4102 +a 11366 11367 4102 +a 11367 11368 4102 +a 11368 11369 4102 +a 11369 11370 4102 +a 11370 11371 4102 +a 11371 11372 4102 +a 11372 11373 4102 +a 11373 11374 4102 +a 11374 11375 4102 +a 11375 11376 4102 +a 11376 11377 4102 +a 11377 11378 4102 +a 11378 11379 4102 +a 11379 11380 4102 +a 11380 11381 4102 +a 11381 11382 4102 +a 11382 11383 4102 +a 11383 11384 4102 +a 11384 11385 4102 +a 11385 11386 4102 +a 11386 11387 4102 +a 11387 11388 4102 +a 11388 11389 4102 +a 11389 11390 4102 +a 11390 11391 4102 +a 11391 11392 4102 +a 11392 11393 4102 +a 11393 11394 4102 +a 11394 11395 4102 +a 11395 11396 4102 +a 11396 11397 4102 +a 11397 11398 4102 +a 11398 11399 4102 +a 11399 11400 4102 +a 11400 11401 4102 +a 11401 11402 4102 +a 11402 11403 4102 +a 11403 11404 4102 +a 11404 11405 4102 +a 11405 11406 4102 +a 11406 11407 4102 +a 11407 11408 4102 +a 11408 11409 4102 +a 11409 11410 4102 +a 11410 11411 4102 +a 11411 11412 4102 +a 11412 11413 4102 +a 11413 11414 4102 +a 11414 11415 4102 +a 11415 11416 4102 +a 11416 11417 4102 +a 11417 11418 4102 +a 11418 11419 4102 +a 11419 11420 4102 +a 11420 11421 4102 +a 11421 11422 4102 +a 11422 11423 4102 +a 11423 11424 4102 +a 11424 11425 4102 +a 11425 11426 4102 +a 11426 11427 4102 +a 11427 11428 4102 +a 11428 11429 4102 +a 11429 11430 4102 +a 11430 11431 4102 +a 11431 11432 4102 +a 11432 11433 4102 +a 11433 11434 4102 +a 11434 11435 4102 +a 11435 11436 4102 +a 11436 11437 4102 +a 11437 11438 4102 +a 11438 11439 4102 +a 11439 11440 4102 +a 11440 11441 4102 +a 11441 11442 4102 +a 11442 11443 4102 +a 11443 11444 4102 +a 11444 11445 4102 +a 11445 11446 4102 +a 11446 11447 4102 +a 11447 11448 4102 +a 11448 11449 4102 +a 11449 11450 4102 +a 11450 11451 4102 +a 11451 11452 4102 +a 11452 11453 4102 +a 11453 11454 4102 +a 11454 11455 4102 +a 11455 11456 4102 +a 11456 11457 4102 +a 11457 11458 4102 +a 11458 11459 4102 +a 11459 11460 4102 +a 11460 11461 4102 +a 11461 11462 4102 +a 11462 11463 4102 +a 11463 11464 4102 +a 11464 11465 4102 +a 11465 11466 4102 +a 11466 11467 4102 +a 11467 11468 4102 +a 11468 11469 4102 +a 11469 11470 4102 +a 11470 11471 4102 +a 11471 11472 4102 +a 11472 11473 4102 +a 11473 11474 4102 +a 11474 11475 4102 +a 11475 11476 4102 +a 11476 11477 4102 +a 11477 11478 4102 +a 11478 11479 4102 +a 11479 11480 4102 +a 11480 11481 4102 +a 11481 11482 4102 +a 11482 11483 4102 +a 11483 11484 4102 +a 11484 11485 4102 +a 11485 11486 4102 +a 11486 11487 4102 +a 11487 11488 4102 +a 11488 11489 4102 +a 11489 11490 4102 +a 11490 11491 4102 +a 11491 11492 4102 +a 11492 11493 4102 +a 11493 11494 4102 +a 11494 11495 4102 +a 11495 11496 4102 +a 11496 11497 4102 +a 11497 11498 4102 +a 11498 11499 4102 +a 11499 11500 4102 +a 11500 11501 4102 +a 11501 11502 4102 +a 11502 11503 4102 +a 11503 11504 4102 +a 11504 11505 4102 +a 11505 11506 4102 +a 11506 11507 4102 +a 11507 11508 4102 +a 11508 11509 4102 +a 11509 11510 4102 +a 11510 11511 4102 +a 11511 11512 4102 +a 11512 11513 4102 +a 11513 11514 4102 +a 11514 11515 4102 +a 11515 11516 4102 +a 11516 11517 4102 +a 11517 11518 4102 +a 11518 11519 4102 +a 11519 11520 4102 +a 11520 11521 4102 +a 11521 11522 4102 +a 11522 11523 4102 +a 11523 11524 4102 +a 11524 11525 4102 +a 11525 11526 4102 +a 11526 11527 4102 +a 11527 11528 4102 +a 11528 11529 4102 +a 11529 11530 4102 +a 11530 11531 4102 +a 11531 11532 4102 +a 11532 11533 4102 +a 11533 11534 4102 +a 11534 11535 4102 +a 11535 11536 4102 +a 11536 11537 4102 +a 11537 11538 4102 +a 11538 11539 4102 +a 11539 11540 4102 +a 11540 11541 4102 +a 11541 11542 4102 +a 11542 11543 4102 +a 11543 11544 4102 +a 11544 11545 4102 +a 11545 11546 4102 +a 11546 11547 4102 +a 11547 11548 4102 +a 11548 11549 4102 +a 11549 11550 4102 +a 11550 11551 4102 +a 11551 11552 4102 +a 11552 11553 4102 +a 11553 11554 4102 +a 11554 11555 4102 +a 11555 11556 4102 +a 11556 11557 4102 +a 11557 11558 4102 +a 11558 11559 4102 +a 11559 11560 4102 +a 11560 11561 4102 +a 11561 11562 4102 +a 11562 11563 4102 +a 11563 11564 4102 +a 11564 11565 4102 +a 11565 11566 4102 +a 11566 11567 4102 +a 11567 11568 4102 +a 11568 11569 4102 +a 11569 11570 4102 +a 11570 11571 4102 +a 11571 11572 4102 +a 11572 11573 4102 +a 11573 11574 4102 +a 11574 11575 4102 +a 11575 11576 4102 +a 11576 11577 4102 +a 11577 11578 4102 +a 11578 11579 4102 +a 11579 11580 4102 +a 11580 11581 4102 +a 11581 11582 4102 +a 11582 11583 4102 +a 11583 11584 4102 +a 11584 11585 4102 +a 11585 11586 4102 +a 11586 11587 4102 +a 11587 11588 4102 +a 11588 11589 4102 +a 11589 11590 4102 +a 11590 11591 4102 +a 11591 11592 4102 +a 11592 11593 4102 +a 11593 11594 4102 +a 11594 11595 4102 +a 11595 11596 4102 +a 11596 11597 4102 +a 11597 11598 4102 +a 11598 11599 4102 +a 11599 11600 4102 +a 11600 11601 4102 +a 11601 11602 4102 +a 11602 11603 4102 +a 11603 11604 4102 +a 11604 11605 4102 +a 11605 11606 4102 +a 11606 11607 4102 +a 11607 11608 4102 +a 11608 11609 4102 +a 11609 11610 4102 +a 11610 11611 4102 +a 11611 11612 4102 +a 11612 11613 4102 +a 11613 11614 4102 +a 11614 11615 4102 +a 11615 11616 4102 +a 11616 11617 4102 +a 11617 11618 4102 +a 11618 11619 4102 +a 11619 11620 4102 +a 11620 11621 4102 +a 11621 11622 4102 +a 11622 11623 4102 +a 11623 11624 4102 +a 11624 11625 4102 +a 11625 11626 4102 +a 11626 11627 4102 +a 11627 11628 4102 +a 11628 11629 4102 +a 11629 11630 4102 +a 11630 11631 4102 +a 11631 11632 4102 +a 11632 11633 4102 +a 11633 11634 4102 +a 11634 11635 4102 +a 11635 11636 4102 +a 11636 11637 4102 +a 11637 11638 4102 +a 11638 11639 4102 +a 11639 11640 4102 +a 11640 11641 4102 +a 11641 11642 4102 +a 11642 11643 4102 +a 11643 11644 4102 +a 11644 11645 4102 +a 11645 11646 4102 +a 11646 11647 4102 +a 11647 11648 4102 +a 11648 11649 4102 +a 11649 11650 4102 +a 11650 11651 4102 +a 11651 11652 4102 +a 11652 11653 4102 +a 11653 11654 4102 +a 11654 11655 4102 +a 11655 11656 4102 +a 11656 11657 4102 +a 11657 11658 4102 +a 11658 11659 4102 +a 11659 11660 4102 +a 11660 11661 4102 +a 11661 11662 4102 +a 11662 11663 4102 +a 11663 11664 4102 +a 11664 11665 4102 +a 11665 11666 4102 +a 11666 11667 4102 +a 11667 11668 4102 +a 11668 11669 4102 +a 11669 11670 4102 +a 11670 11671 4102 +a 11671 11672 4102 +a 11672 11673 4102 +a 11673 11674 4102 +a 11674 11675 4102 +a 11675 11676 4102 +a 11676 11677 4102 +a 11677 11678 4102 +a 11678 11679 4102 +a 11679 11680 4102 +a 11680 11681 4102 +a 11681 11682 4102 +a 11682 11683 4102 +a 11683 11684 4102 +a 11684 11685 4102 +a 11685 11686 4102 +a 11686 11687 4102 +a 11687 11688 4102 +a 11688 11689 4102 +a 11689 11690 4102 +a 11690 11691 4102 +a 11691 11692 4102 +a 11692 11693 4102 +a 11693 11694 4102 +a 11694 11695 4102 +a 11695 11696 4102 +a 11696 11697 4102 +a 11697 11698 4102 +a 11698 11699 4102 +a 11699 11700 4102 +a 11700 11701 4102 +a 11701 11702 4102 +a 11702 11703 4102 +a 11703 11704 4102 +a 11704 11705 4102 +a 11705 11706 4102 +a 11706 11707 4102 +a 11707 11708 4102 +a 11708 11709 4102 +a 11709 11710 4102 +a 11710 11711 4102 +a 11711 11712 4102 +a 11712 11713 4102 +a 11713 11714 4102 +a 11714 11715 4102 +a 11715 11716 4102 +a 11716 11717 4102 +a 11717 11718 4102 +a 11718 11719 4102 +a 11719 11720 4102 +a 11720 11721 4102 +a 11721 11722 4102 +a 11722 11723 4102 +a 11723 11724 4102 +a 11724 11725 4102 +a 11725 11726 4102 +a 11726 11727 4102 +a 11727 11728 4102 +a 11728 11729 4102 +a 11729 11730 4102 +a 11730 11731 4102 +a 11731 11732 4102 +a 11732 11733 4102 +a 11733 11734 4102 +a 11734 11735 4102 +a 11735 11736 4102 +a 11736 11737 4102 +a 11737 11738 4102 +a 11738 11739 4102 +a 11739 11740 4102 +a 11740 11741 4102 +a 11741 11742 4102 +a 11742 11743 4102 +a 11743 11744 4102 +a 11744 11745 4102 +a 11745 11746 4102 +a 11746 11747 4102 +a 11747 11748 4102 +a 11748 11749 4102 +a 11749 11750 4102 +a 11750 11751 4102 +a 11751 11752 4102 +a 11752 11753 4102 +a 11753 11754 4102 +a 11754 11755 4102 +a 11755 11756 4102 +a 11756 11757 4102 +a 11757 11758 4102 +a 11758 11759 4102 +a 11759 11760 4102 +a 11760 11761 4102 +a 11761 11762 4102 +a 11762 11763 4102 +a 11763 11764 4102 +a 11764 11765 4102 +a 11765 11766 4102 +a 11766 11767 4102 +a 11767 11768 4102 +a 11768 11769 4102 +a 11769 11770 4102 +a 11770 11771 4102 +a 11771 11772 4102 +a 11772 11773 4102 +a 11773 11774 4102 +a 11774 11775 4102 +a 11775 11776 4102 +a 11776 11777 4102 +a 11777 11778 4102 +a 11778 11779 4102 +a 11779 11780 4102 +a 11780 11781 4102 +a 11781 11782 4102 +a 11782 11783 4102 +a 11783 11784 4102 +a 11784 11785 4102 +a 11785 11786 4102 +a 11786 11787 4102 +a 11787 11788 4102 +a 11788 11789 4102 +a 11789 11790 4102 +a 11790 11791 4102 +a 11791 11792 4102 +a 11792 11793 4102 +a 11793 11794 4102 +a 11794 11795 4102 +a 11795 11796 4102 +a 11796 11797 4102 +a 11797 11798 4102 +a 11798 11799 4102 +a 11799 11800 4102 +a 11800 11801 4102 +a 11801 11802 4102 +a 11802 11803 4102 +a 11803 11804 4102 +a 11804 11805 4102 +a 11805 11806 4102 +a 11806 11807 4102 +a 11807 11808 4102 +a 11808 11809 4102 +a 11809 11810 4102 +a 11810 11811 4102 +a 11811 11812 4102 +a 11812 11813 4102 +a 11813 11814 4102 +a 11814 11815 4102 +a 11815 11816 4102 +a 11816 11817 4102 +a 11817 11818 4102 +a 11818 11819 4102 +a 11819 11820 4102 +a 11820 11821 4102 +a 11821 11822 4102 +a 11822 11823 4102 +a 11823 11824 4102 +a 11824 11825 4102 +a 11825 11826 4102 +a 11826 11827 4102 +a 11827 11828 4102 +a 11828 11829 4102 +a 11829 11830 4102 +a 11830 11831 4102 +a 11831 11832 4102 +a 11832 11833 4102 +a 11833 11834 4102 +a 11834 11835 4102 +a 11835 11836 4102 +a 11836 11837 4102 +a 11837 11838 4102 +a 11838 11839 4102 +a 11839 11840 4102 +a 11840 11841 4102 +a 11841 11842 4102 +a 11842 11843 4102 +a 11843 11844 4102 +a 11844 11845 4102 +a 11845 11846 4102 +a 11846 11847 4102 +a 11847 11848 4102 +a 11848 11849 4102 +a 11849 11850 4102 +a 11850 11851 4102 +a 11851 11852 4102 +a 11852 11853 4102 +a 11853 11854 4102 +a 11854 11855 4102 +a 11855 11856 4102 +a 11856 11857 4102 +a 11857 11858 4102 +a 11858 11859 4102 +a 11859 11860 4102 +a 11860 11861 4102 +a 11861 11862 4102 +a 11862 11863 4102 +a 11863 11864 4102 +a 11864 11865 4102 +a 11865 11866 4102 +a 11866 11867 4102 +a 11867 11868 4102 +a 11868 11869 4102 +a 11869 11870 4102 +a 11870 11871 4102 +a 11871 11872 4102 +a 11872 11873 4102 +a 11873 11874 4102 +a 11874 11875 4102 +a 11875 11876 4102 +a 11876 11877 4102 +a 11877 11878 4102 +a 11878 11879 4102 +a 11879 11880 4102 +a 11880 11881 4102 +a 11881 11882 4102 +a 11882 11883 4102 +a 11883 11884 4102 +a 11884 11885 4102 +a 11885 11886 4102 +a 11886 11887 4102 +a 11887 11888 4102 +a 11888 11889 4102 +a 11889 11890 4102 +a 11890 11891 4102 +a 11891 11892 4102 +a 11892 11893 4102 +a 11893 11894 4102 +a 11894 11895 4102 +a 11895 11896 4102 +a 11896 11897 4102 +a 11897 11898 4102 +a 11898 11899 4102 +a 11899 11900 4102 +a 11900 11901 4102 +a 11901 11902 4102 +a 11902 11903 4102 +a 11903 11904 4102 +a 11904 11905 4102 +a 11905 11906 4102 +a 11906 11907 4102 +a 11907 11908 4102 +a 11908 11909 4102 +a 11909 11910 4102 +a 11910 11911 4102 +a 11911 11912 4102 +a 11912 11913 4102 +a 11913 11914 4102 +a 11914 11915 4102 +a 11915 11916 4102 +a 11916 11917 4102 +a 11917 11918 4102 +a 11918 11919 4102 +a 11919 11920 4102 +a 11920 11921 4102 +a 11921 11922 4102 +a 11922 11923 4102 +a 11923 11924 4102 +a 11924 11925 4102 +a 11925 11926 4102 +a 11926 11927 4102 +a 11927 11928 4102 +a 11928 11929 4102 +a 11929 11930 4102 +a 11930 11931 4102 +a 11931 11932 4102 +a 11932 11933 4102 +a 11933 11934 4102 +a 11934 11935 4102 +a 11935 11936 4102 +a 11936 11937 4102 +a 11937 11938 4102 +a 11938 11939 4102 +a 11939 11940 4102 +a 11940 11941 4102 +a 11941 11942 4102 +a 11942 11943 4102 +a 11943 11944 4102 +a 11944 11945 4102 +a 11945 11946 4102 +a 11946 11947 4102 +a 11947 11948 4102 +a 11948 11949 4102 +a 11949 11950 4102 +a 11950 11951 4102 +a 11951 11952 4102 +a 11952 11953 4102 +a 11953 11954 4102 +a 11954 11955 4102 +a 11955 11956 4102 +a 11956 11957 4102 +a 11957 11958 4102 +a 11958 11959 4102 +a 11959 11960 4102 +a 11960 11961 4102 +a 11961 11962 4102 +a 11962 11963 4102 +a 11963 11964 4102 +a 11964 11965 4102 +a 11965 11966 4102 +a 11966 11967 4102 +a 11967 11968 4102 +a 11968 11969 4102 +a 11969 11970 4102 +a 11970 11971 4102 +a 11971 11972 4102 +a 11972 11973 4102 +a 11973 11974 4102 +a 11974 11975 4102 +a 11975 11976 4102 +a 11976 11977 4102 +a 11977 11978 4102 +a 11978 11979 4102 +a 11979 11980 4102 +a 11980 11981 4102 +a 11981 11982 4102 +a 11982 11983 4102 +a 11983 11984 4102 +a 11984 11985 4102 +a 11985 11986 4102 +a 11986 11987 4102 +a 11987 11988 4102 +a 11988 11989 4102 +a 11989 11990 4102 +a 11990 11991 4102 +a 11991 11992 4102 +a 11992 11993 4102 +a 11993 11994 4102 +a 11994 11995 4102 +a 11995 11996 4102 +a 11996 11997 4102 +a 11997 11998 4102 +a 11998 11999 4102 +a 11999 12000 4102 +a 12000 12001 4102 +a 12001 12002 4102 +a 12002 12003 4102 +a 12003 12004 4102 +a 12004 12005 4102 +a 12005 12006 4102 +a 12006 12007 4102 +a 12007 12008 4102 +a 12008 12009 4102 +a 12009 12010 4102 +a 12010 12011 4102 +a 12011 12012 4102 +a 12012 12013 4102 +a 12013 12014 4102 +a 12014 12015 4102 +a 12015 12016 4102 +a 12016 12017 4102 +a 12017 12018 4102 +a 12018 12019 4102 +a 12019 12020 4102 +a 12020 12021 4102 +a 12021 12022 4102 +a 12022 12023 4102 +a 12023 12024 4102 +a 12024 12025 4102 +a 12025 12026 4102 +a 12026 12027 4102 +a 12027 12028 4102 +a 12028 12029 4102 +a 12029 12030 4102 +a 12030 12031 4102 +a 12031 12032 4102 +a 12032 12033 4102 +a 12033 12034 4102 +a 12034 12035 4102 +a 12035 12036 4102 +a 12036 12037 4102 +a 12037 12038 4102 +a 12038 12039 4102 +a 12039 12040 4102 +a 12040 12041 4102 +a 12041 12042 4102 +a 12042 12043 4102 +a 12043 12044 4102 +a 12044 12045 4102 +a 12045 12046 4102 +a 12046 12047 4102 +a 12047 12048 4102 +a 12048 12049 4102 +a 12049 12050 4102 +a 12050 12051 4102 +a 12051 12052 4102 +a 12052 12053 4102 +a 12053 12054 4102 +a 12054 12055 4102 +a 12055 12056 4102 +a 12056 12057 4102 +a 12057 12058 4102 +a 12058 12059 4102 +a 12059 12060 4102 +a 12060 12061 4102 +a 12061 12062 4102 +a 12062 12063 4102 +a 12063 12064 4102 +a 12064 12065 4102 +a 12065 12066 4102 +a 12066 12067 4102 +a 12067 12068 4102 +a 12068 12069 4102 +a 12069 12070 4102 +a 12070 12071 4102 +a 12071 12072 4102 +a 12072 12073 4102 +a 12073 12074 4102 +a 12074 12075 4102 +a 12075 12076 4102 +a 12076 12077 4102 +a 12077 12078 4102 +a 12078 12079 4102 +a 12079 12080 4102 +a 12080 12081 4102 +a 12081 12082 4102 +a 12082 12083 4102 +a 12083 12084 4102 +a 12084 12085 4102 +a 12085 12086 4102 +a 12086 12087 4102 +a 12087 12088 4102 +a 12088 12089 4102 +a 12089 12090 4102 +a 12090 12091 4102 +a 12091 12092 4102 +a 12092 12093 4102 +a 12093 12094 4102 +a 12094 12095 4102 +a 12095 12096 4102 +a 12096 12097 4102 +a 12097 12098 4102 +a 12098 12099 4102 +a 12099 12100 4102 +a 12100 12101 4102 +a 12101 12102 4102 +a 12102 12103 4102 +a 12103 12104 4102 +a 12104 12105 4102 +a 12105 12106 4102 +a 12106 12107 4102 +a 12107 12108 4102 +a 12108 12109 4102 +a 12109 12110 4102 +a 12110 12111 4102 +a 12111 12112 4102 +a 12112 12113 4102 +a 12113 12114 4102 +a 12114 12115 4102 +a 12115 12116 4102 +a 12116 12117 4102 +a 12117 12118 4102 +a 12118 12119 4102 +a 12119 12120 4102 +a 12120 12121 4102 +a 12121 12122 4102 +a 12122 12123 4102 +a 12123 12124 4102 +a 12124 12125 4102 +a 12125 12126 4102 +a 12126 12127 4102 +a 12127 12128 4102 +a 12128 12129 4102 +a 12129 12130 4102 +a 12130 12131 4102 +a 12131 12132 4102 +a 12132 12133 4102 +a 12133 12134 4102 +a 12134 12135 4102 +a 12135 12136 4102 +a 12136 12137 4102 +a 12137 12138 4102 +a 12138 12139 4102 +a 12139 12140 4102 +a 12140 12141 4102 +a 12141 12142 4102 +a 12142 12143 4102 +a 12143 12144 4102 +a 12144 12145 4102 +a 12145 12146 4102 +a 12146 12147 4102 +a 12147 12148 4102 +a 12148 12149 4102 +a 12149 12150 4102 +a 12150 12151 4102 +a 12151 12152 4102 +a 12152 12153 4102 +a 12153 12154 4102 +a 12154 12155 4102 +a 12155 12156 4102 +a 12156 12157 4102 +a 12157 12158 4102 +a 12158 12159 4102 +a 12159 12160 4102 +a 12160 12161 4102 +a 12161 12162 4102 +a 12162 12163 4102 +a 12163 12164 4102 +a 12164 12165 4102 +a 12165 12166 4102 +a 12166 12167 4102 +a 12167 12168 4102 +a 12168 12169 4102 +a 12169 12170 4102 +a 12170 12171 4102 +a 12171 12172 4102 +a 12172 12173 4102 +a 12173 12174 4102 +a 12174 12175 4102 +a 12175 12176 4102 +a 12176 12177 4102 +a 12177 12178 4102 +a 12178 12179 4102 +a 12179 12180 4102 +a 12180 12181 4102 +a 12181 12182 4102 +a 12182 12183 4102 +a 12183 12184 4102 +a 12184 12185 4102 +a 12185 12186 4102 +a 12186 12187 4102 +a 12187 12188 4102 +a 12188 12189 4102 +a 12189 12190 4102 +a 12190 12191 4102 +a 12191 12192 4102 +a 12192 12193 4102 +a 12193 12194 4102 +a 12194 12195 4102 +a 12195 12196 4102 +a 12196 12197 4102 +a 12197 12198 4102 +a 12198 12199 4102 +a 12199 12200 4102 +a 12200 12201 4102 +a 12201 12202 4102 +a 12202 12203 4102 +a 12203 12204 4102 +a 12204 12205 4102 +a 12205 12206 4102 +a 12206 12207 4102 +a 12207 12208 4102 +a 12208 12209 4102 +a 12209 12210 4102 +a 12210 12211 4102 +a 12211 12212 4102 +a 12212 12213 4102 +a 12213 12214 4102 +a 12214 12215 4102 +a 12215 12216 4102 +a 12216 12217 4102 +a 12217 12218 4102 +a 12218 12219 4102 +a 12219 12220 4102 +a 12220 12221 4102 +a 12221 12222 4102 +a 12222 12223 4102 +a 12223 12224 4102 +a 12224 12225 4102 +a 12225 12226 4102 +a 12226 12227 4102 +a 12227 12228 4102 +a 12228 12229 4102 +a 12229 12230 4102 +a 12230 12231 4102 +a 12231 12232 4102 +a 12232 12233 4102 +a 12233 12234 4102 +a 12234 12235 4102 +a 12235 12236 4102 +a 12236 12237 4102 +a 12237 12238 4102 +a 12238 12239 4102 +a 12239 12240 4102 +a 12240 12241 4102 +a 12241 12242 4102 +a 12242 12243 4102 +a 12243 12244 4102 +a 12244 12245 4102 +a 12245 12246 4102 +a 12246 12247 4102 +a 12247 12248 4102 +a 12248 12249 4102 +a 12249 12250 4102 +a 12250 12251 4102 +a 12251 12252 4102 +a 12252 12253 4102 +a 12253 12254 4102 +a 12254 12255 4102 +a 12255 12256 4102 +a 12256 12257 4102 +a 12257 12258 4102 +a 12258 12259 4102 +a 12259 12260 4102 +a 12260 12261 4102 +a 12261 12262 4102 +a 12262 12263 4102 +a 12263 12264 4102 +a 12264 12265 4102 +a 12265 12266 4102 +a 12266 12267 4102 +a 12267 12268 4102 +a 12268 12269 4102 +a 12269 12270 4102 +a 12270 12271 4102 +a 12271 12272 4102 +a 12272 12273 4102 +a 12273 12274 4102 +a 12274 12275 4102 +a 12275 12276 4102 +a 12276 12277 4102 +a 12277 12278 4102 +a 12278 12279 4102 +a 12279 12280 4102 +a 12280 12281 4102 +a 12281 12282 4102 +a 12282 12283 4102 +a 12283 12284 4102 +a 12284 12285 4102 +a 12285 12286 4102 +a 12286 12287 4102 +a 12287 12288 4102 +a 12288 12289 4102 +a 12289 12290 4102 +a 12290 12291 4102 +a 12291 12292 4102 +a 12292 12293 4102 +a 12293 12294 4102 +a 12294 12295 4102 +a 12295 12296 4102 +a 12296 12297 4102 +a 12297 12298 4102 +a 12298 12299 4102 +a 12299 12300 4102 +a 12300 12301 4102 +a 12301 12302 4102 +a 12302 12303 4102 +a 12303 12304 4102 +a 12304 12305 4102 +a 12305 12306 4102 +a 12306 12307 4102 +a 12307 12308 4102 +a 12308 12309 4102 +a 12309 12310 4102 +a 12310 12311 4102 +a 12311 12312 4102 +a 12312 12313 4102 +a 12313 12314 4102 +a 12314 12315 4102 +a 12315 12316 4102 +a 12316 12317 4102 +a 12317 12318 4102 +a 12318 12319 4102 +a 12319 12320 4102 +a 12320 12321 4102 +a 12321 12322 4102 +a 12322 12323 4102 +a 12323 12324 4102 +a 12324 12325 4102 +a 12325 12326 4102 +a 12326 12327 4102 +a 12327 12328 4102 +a 12328 12329 4102 +a 12329 12330 4102 +a 12330 12331 4102 +a 12331 12332 4102 +a 12332 12333 4102 +a 12333 12334 4102 +a 12334 12335 4102 +a 12335 12336 4102 +a 12336 12337 4102 +a 12337 12338 4102 +a 12338 12339 4102 +a 12339 12340 4102 +a 12340 12341 4102 +a 12341 12342 4102 +a 12342 12343 4102 +a 12343 12344 4102 +a 12344 12345 4102 +a 12345 12346 4102 +a 12346 12347 4102 +a 12347 12348 4102 +a 12348 12349 4102 +a 12349 12350 4102 +a 12350 12351 4102 +a 12351 12352 4102 +a 12352 12353 4102 +a 12353 12354 4102 +a 12354 12355 4102 +a 12355 12356 4102 +a 12356 12357 4102 +a 12357 12358 4102 +a 12358 12359 4102 +a 12359 12360 4102 +a 12360 12361 4102 +a 12361 12362 4102 +a 12362 12363 4102 +a 12363 12364 4102 +a 12364 12365 4102 +a 12365 12366 4102 +a 12366 12367 4102 +a 12367 12368 4102 +a 12368 12369 4102 +a 12369 12370 4102 +a 12370 12371 4102 +a 12371 12372 4102 +a 12372 12373 4102 +a 12373 12374 4102 +a 12374 12375 4102 +a 12375 12376 4102 +a 12376 12377 4102 +a 12377 12378 4102 +a 12378 12379 4102 +a 12379 12380 4102 +a 12380 12381 4102 +a 12381 12382 4102 +a 12382 12383 4102 +a 12383 12384 4102 +a 12384 12385 4102 +a 12385 12386 4102 +a 12386 12387 4102 +a 12387 12388 4102 +a 12388 12389 4102 +a 12389 12390 4102 +a 12390 12391 4102 +a 12391 12392 4102 +a 12392 12393 4102 +a 12393 12394 4102 +a 12394 12395 4102 +a 12395 12396 4102 +a 12396 12397 4102 +a 12397 12398 4102 +a 12398 12399 4102 +a 12399 12400 4102 +a 12400 12401 4102 +a 12401 12402 4102 +a 12402 12403 4102 +a 12403 12404 4102 +a 12404 12405 4102 +a 12405 12406 4102 +a 12406 12407 4102 +a 12407 12408 4102 +a 12408 12409 4102 +a 12409 12410 4102 +a 12410 12411 4102 +a 12411 12412 4102 +a 12412 12413 4102 +a 12413 12414 4102 +a 12414 12415 4102 +a 12415 12416 4102 +a 12416 12417 4102 +a 12417 12418 4102 +a 12418 12419 4102 +a 12419 12420 4102 +a 12420 12421 4102 +a 12421 12422 4102 +a 12422 12423 4102 +a 12423 12424 4102 +a 12424 12425 4102 +a 12425 12426 4102 +a 12426 12427 4102 +a 12427 12428 4102 +a 12428 12429 4102 +a 12429 12430 4102 +a 12430 12431 4102 +a 12431 12432 4102 +a 12432 12433 4102 +a 12433 12434 4102 +a 12434 12435 4102 +a 12435 12436 4102 +a 12436 12437 4102 +a 12437 12438 4102 +a 12438 12439 4102 +a 12439 12440 4102 +a 12440 12441 4102 +a 12441 12442 4102 +a 12442 12443 4102 +a 12443 12444 4102 +a 12444 12445 4102 +a 12445 12446 4102 +a 12446 12447 4102 +a 12447 12448 4102 +a 12448 12449 4102 +a 12449 12450 4102 +a 12450 12451 4102 +a 12451 12452 4102 +a 12452 12453 4102 +a 12453 12454 4102 +a 12454 12455 4102 +a 12455 12456 4102 +a 12456 12457 4102 +a 12457 12458 4102 +a 12458 12459 4102 +a 12459 12460 4102 +a 12460 12461 4102 +a 12461 12462 4102 +a 12462 12463 4102 +a 12463 12464 4102 +a 12464 12465 4102 +a 12465 12466 4102 +a 12466 12467 4102 +a 12467 12468 4102 +a 12468 12469 4102 +a 12469 12470 4102 +a 12470 12471 4102 +a 12471 12472 4102 +a 12472 12473 4102 +a 12473 12474 4102 +a 12474 12475 4102 +a 12475 12476 4102 +a 12476 12477 4102 +a 12477 12478 4102 +a 12478 12479 4102 +a 12479 12480 4102 +a 12480 12481 4102 +a 12481 12482 4102 +a 12482 12483 4102 +a 12483 12484 4102 +a 12484 12485 4102 +a 12485 12486 4102 +a 12486 12487 4102 +a 12487 12488 4102 +a 12488 12489 4102 +a 12489 12490 4102 +a 12490 12491 4102 +a 12491 12492 4102 +a 12492 12493 4102 +a 12493 12494 4102 +a 12494 12495 4102 +a 12495 12496 4102 +a 12496 12497 4102 +a 12497 12498 4102 +a 12498 12499 4102 +a 12499 12500 4102 +a 12500 12501 4102 +a 12501 12502 4102 +a 12502 12503 4102 +a 12503 12504 4102 +a 12504 12505 4102 +a 12505 12506 4102 +a 12506 12507 4102 +a 12507 12508 4102 +a 12508 12509 4102 +a 12509 12510 4102 +a 12510 12511 4102 +a 12511 12512 4102 +a 12512 12513 4102 +a 12513 12514 4102 +a 12514 12515 4102 +a 12515 12516 4102 +a 12516 12517 4102 +a 12517 12518 4102 +a 12518 12519 4102 +a 12519 12520 4102 +a 12520 12521 4102 +a 12521 12522 4102 +a 12522 12523 4102 +a 12523 12524 4102 +a 12524 12525 4102 +a 12525 12526 4102 +a 12526 12527 4102 +a 12527 12528 4102 +a 12528 12529 4102 +a 12529 12530 4102 +a 12530 12531 4102 +a 12531 12532 4102 +a 12532 12533 4102 +a 12533 12534 4102 +a 12534 12535 4102 +a 12535 12536 4102 +a 12536 12537 4102 +a 12537 12538 4102 +a 12538 12539 4102 +a 12539 12540 4102 +a 12540 12541 4102 +a 12541 12542 4102 +a 12542 12543 4102 +a 12543 12544 4102 +a 12544 12545 4102 +a 12545 12546 4102 +a 12546 12547 4102 +a 12547 12548 4102 +a 12548 12549 4102 +a 12549 12550 4102 +a 12550 12551 4102 +a 12551 12552 4102 +a 12552 12553 4102 +a 12553 12554 4102 +a 12554 12555 4102 +a 12555 12556 4102 +a 12556 12557 4102 +a 12557 12558 4102 +a 12558 12559 4102 +a 12559 12560 4102 +a 12560 12561 4102 +a 12561 12562 4102 +a 12562 12563 4102 +a 12563 12564 4102 +a 12564 12565 4102 +a 12565 12566 4102 +a 12566 12567 4102 +a 12567 12568 4102 +a 12568 12569 4102 +a 12569 12570 4102 +a 12570 12571 4102 +a 12571 12572 4102 +a 12572 12573 4102 +a 12573 12574 4102 +a 12574 12575 4102 +a 12575 12576 4102 +a 12576 12577 4102 +a 12577 12578 4102 +a 12578 12579 4102 +a 12579 12580 4102 +a 12580 12581 4102 +a 12581 12582 4102 +a 12582 12583 4102 +a 12583 12584 4102 +a 12584 12585 4102 +a 12585 12586 4102 +a 12586 12587 4102 +a 12587 12588 4102 +a 12588 12589 4102 +a 12589 12590 4102 +a 12590 12591 4102 +a 12591 12592 4102 +a 12592 12593 4102 +a 12593 12594 4102 +a 12594 12595 4102 +a 12595 12596 4102 +a 12596 12597 4102 +a 12597 12598 4102 +a 12598 12599 4102 +a 12599 12600 4102 +a 12600 12601 4102 +a 12601 12602 4102 +a 12602 12603 4102 +a 12603 12604 4102 +a 12604 12605 4102 +a 12605 12606 4102 +a 12606 12607 4102 +a 12607 12608 4102 +a 12608 12609 4102 +a 12609 12610 4102 +a 12610 12611 4102 +a 12611 12612 4102 +a 12612 12613 4102 +a 12613 12614 4102 +a 12614 12615 4102 +a 12615 12616 4102 +a 12616 12617 4102 +a 12617 12618 4102 +a 12618 12619 4102 +a 12619 12620 4102 +a 12620 12621 4102 +a 12621 12622 4102 +a 12622 12623 4102 +a 12623 12624 4102 +a 12624 12625 4102 +a 12625 12626 4102 +a 12626 12627 4102 +a 12627 12628 4102 +a 12628 12629 4102 +a 12629 12630 4102 +a 12630 12631 4102 +a 12631 12632 4102 +a 12632 12633 4102 +a 12633 12634 4102 +a 12634 12635 4102 +a 12635 12636 4102 +a 12636 12637 4102 +a 12637 12638 4102 +a 12638 12639 4102 +a 12639 12640 4102 +a 12640 12641 4102 +a 12641 12642 4102 +a 12642 12643 4102 +a 12643 12644 4102 +a 12644 12645 4102 +a 12645 12646 4102 +a 12646 12647 4102 +a 12647 12648 4102 +a 12648 12649 4102 +a 12649 12650 4102 +a 12650 12651 4102 +a 12651 12652 4102 +a 12652 12653 4102 +a 12653 12654 4102 +a 12654 12655 4102 +a 12655 12656 4102 +a 12656 12657 4102 +a 12657 12658 4102 +a 12658 12659 4102 +a 12659 12660 4102 +a 12660 12661 4102 +a 12661 12662 4102 +a 12662 12663 4102 +a 12663 12664 4102 +a 12664 12665 4102 +a 12665 12666 4102 +a 12666 12667 4102 +a 12667 12668 4102 +a 12668 12669 4102 +a 12669 12670 4102 +a 12670 12671 4102 +a 12671 12672 4102 +a 12672 12673 4102 +a 12673 12674 4102 +a 12674 12675 4102 +a 12675 12676 4102 +a 12676 12677 4102 +a 12677 12678 4102 +a 12678 12679 4102 +a 12679 12680 4102 +a 12680 12681 4102 +a 12681 12682 4102 +a 12682 12683 4102 +a 12683 12684 4102 +a 12684 12685 4102 +a 12685 12686 4102 +a 12686 12687 4102 +a 12687 12688 4102 +a 12688 12689 4102 +a 12689 12690 4102 +a 12690 12691 4102 +a 12691 12692 4102 +a 12692 12693 4102 +a 12693 12694 4102 +a 12694 12695 4102 +a 12695 12696 4102 +a 12696 12697 4102 +a 12697 12698 4102 +a 12698 12699 4102 +a 12699 12700 4102 +a 12700 12701 4102 +a 12701 12702 4102 +a 12702 12703 4102 +a 12703 12704 4102 +a 12704 12705 4102 +a 12705 12706 4102 +a 12706 12707 4102 +a 12707 12708 4102 +a 12708 12709 4102 +a 12709 12710 4102 +a 12710 12711 4102 +a 12711 12712 4102 +a 12712 12713 4102 +a 12713 12714 4102 +a 12714 12715 4102 +a 12715 12716 4102 +a 12716 12717 4102 +a 12717 12718 4102 +a 12718 12719 4102 +a 12719 12720 4102 +a 12720 12721 4102 +a 12721 12722 4102 +a 12722 12723 4102 +a 12723 12724 4102 +a 12724 12725 4102 +a 12725 12726 4102 +a 12726 12727 4102 +a 12727 12728 4102 +a 12728 12729 4102 +a 12729 12730 4102 +a 12730 12731 4102 +a 12731 12732 4102 +a 12732 12733 4102 +a 12733 12734 4102 +a 12734 12735 4102 +a 12735 12736 4102 +a 12736 12737 4102 +a 12737 12738 4102 +a 12738 12739 4102 +a 12739 12740 4102 +a 12740 12741 4102 +a 12741 12742 4102 +a 12742 12743 4102 +a 12743 12744 4102 +a 12744 12745 4102 +a 12745 12746 4102 +a 12746 12747 4102 +a 12747 12748 4102 +a 12748 12749 4102 +a 12749 12750 4102 +a 12750 12751 4102 +a 12751 12752 4102 +a 12752 12753 4102 +a 12753 12754 4102 +a 12754 12755 4102 +a 12755 12756 4102 +a 12756 12757 4102 +a 12757 12758 4102 +a 12758 12759 4102 +a 12759 12760 4102 +a 12760 12761 4102 +a 12761 12762 4102 +a 12762 12763 4102 +a 12763 12764 4102 +a 12764 12765 4102 +a 12765 12766 4102 +a 12766 12767 4102 +a 12767 12768 4102 +a 12768 12769 4102 +a 12769 12770 4102 +a 12770 12771 4102 +a 12771 12772 4102 +a 12772 12773 4102 +a 12773 12774 4102 +a 12774 12775 4102 +a 12775 12776 4102 +a 12776 12777 4102 +a 12777 12778 4102 +a 12778 12779 4102 +a 12779 12780 4102 +a 12780 12781 4102 +a 12781 12782 4102 +a 12782 12783 4102 +a 12783 12784 4102 +a 12784 12785 4102 +a 12785 12786 4102 +a 12786 12787 4102 +a 12787 12788 4102 +a 12788 12789 4102 +a 12789 12790 4102 +a 12790 12791 4102 +a 12791 12792 4102 +a 12792 12793 4102 +a 12793 12794 4102 +a 12794 12795 4102 +a 12795 12796 4102 +a 12796 12797 4102 +a 12797 12798 4102 +a 12798 12799 4102 +a 12799 12800 4102 +a 12800 12801 4102 +a 12801 12802 4102 +a 12802 12803 4102 +a 12803 12804 4102 +a 12804 12805 4102 +a 12805 12806 4102 +a 12806 12807 4102 +a 12807 12808 4102 +a 12808 12809 4102 +a 12809 12810 4102 +a 12810 12811 4102 +a 12811 12812 4102 +a 12812 12813 4102 +a 12813 12814 4102 +a 12814 12815 4102 +a 12815 12816 4102 +a 12816 12817 4102 +a 12817 12818 4102 +a 12818 12819 4102 +a 12819 12820 4102 +a 12820 12821 4102 +a 12821 12822 4102 +a 12822 12823 4102 +a 12823 12824 4102 +a 12824 12825 4102 +a 12825 12826 4102 +a 12826 12827 4102 +a 12827 12828 4102 +a 12828 12829 4102 +a 12829 12830 4102 +a 12830 12831 4102 +a 12831 12832 4102 +a 12832 12833 4102 +a 12833 12834 4102 +a 12834 12835 4102 +a 12835 12836 4102 +a 12836 12837 4102 +a 12837 12838 4102 +a 12838 12839 4102 +a 12839 12840 4102 +a 12840 12841 4102 +a 12841 12842 4102 +a 12842 12843 4102 +a 12843 12844 4102 +a 12844 12845 4102 +a 12845 12846 4102 +a 12846 12847 4102 +a 12847 12848 4102 +a 12848 12849 4102 +a 12849 12850 4102 +a 12850 12851 4102 +a 12851 12852 4102 +a 12852 12853 4102 +a 12853 12854 4102 +a 12854 12855 4102 +a 12855 12856 4102 +a 12856 12857 4102 +a 12857 12858 4102 +a 12858 12859 4102 +a 12859 12860 4102 +a 12860 12861 4102 +a 12861 12862 4102 +a 12862 12863 4102 +a 12863 12864 4102 +a 12864 12865 4102 +a 12865 12866 4102 +a 12866 12867 4102 +a 12867 12868 4102 +a 12868 12869 4102 +a 12869 12870 4102 +a 12870 12871 4102 +a 12871 12872 4102 +a 12872 12873 4102 +a 12873 12874 4102 +a 12874 12875 4102 +a 12875 12876 4102 +a 12876 12877 4102 +a 12877 12878 4102 +a 12878 12879 4102 +a 12879 12880 4102 +a 12880 12881 4102 +a 12881 12882 4102 +a 12882 12883 4102 +a 12883 12884 4102 +a 12884 12885 4102 +a 12885 12886 4102 +a 12886 12887 4102 +a 12887 12888 4102 +a 12888 12889 4102 +a 12889 12890 4102 +a 12890 12891 4102 +a 12891 12892 4102 +a 12892 12893 4102 +a 12893 12894 4102 +a 12894 12895 4102 +a 12895 12896 4102 +a 12896 12897 4102 +a 12897 12898 4102 +a 12898 12899 4102 +a 12899 12900 4102 +a 12900 12901 4102 +a 12901 12902 4102 +a 12902 12903 4102 +a 12903 12904 4102 +a 12904 12905 4102 +a 12905 12906 4102 +a 12906 12907 4102 +a 12907 12908 4102 +a 12908 12909 4102 +a 12909 12910 4102 +a 12910 12911 4102 +a 12911 12912 4102 +a 12912 12913 4102 +a 12913 12914 4102 +a 12914 12915 4102 +a 12915 12916 4102 +a 12916 12917 4102 +a 12917 12918 4102 +a 12918 12919 4102 +a 12919 12920 4102 +a 12920 12921 4102 +a 12921 12922 4102 +a 12922 12923 4102 +a 12923 12924 4102 +a 12924 12925 4102 +a 12925 12926 4102 +a 12926 12927 4102 +a 12927 12928 4102 +a 12928 12929 4102 +a 12929 12930 4102 +a 12930 12931 4102 +a 12931 12932 4102 +a 12932 12933 4102 +a 12933 12934 4102 +a 12934 12935 4102 +a 12935 12936 4102 +a 12936 12937 4102 +a 12937 12938 4102 +a 12938 12939 4102 +a 12939 12940 4102 +a 12940 12941 4102 +a 12941 12942 4102 +a 12942 12943 4102 +a 12943 12944 4102 +a 12944 12945 4102 +a 12945 12946 4102 +a 12946 12947 4102 +a 12947 12948 4102 +a 12948 12949 4102 +a 12949 12950 4102 +a 12950 12951 4102 +a 12951 12952 4102 +a 12952 12953 4102 +a 12953 12954 4102 +a 12954 12955 4102 +a 12955 12956 4102 +a 12956 12957 4102 +a 12957 12958 4102 +a 12958 12959 4102 +a 12959 12960 4102 +a 12960 12961 4102 +a 12961 12962 4102 +a 12962 12963 4102 +a 12963 12964 4102 +a 12964 12965 4102 +a 12965 12966 4102 +a 12966 12967 4102 +a 12967 12968 4102 +a 12968 12969 4102 +a 12969 12970 4102 +a 12970 12971 4102 +a 12971 12972 4102 +a 12972 12973 4102 +a 12973 12974 4102 +a 12974 12975 4102 +a 12975 12976 4102 +a 12976 12977 4102 +a 12977 12978 4102 +a 12978 12979 4102 +a 12979 12980 4102 +a 12980 12981 4102 +a 12981 12982 4102 +a 12982 12983 4102 +a 12983 12984 4102 +a 12984 12985 4102 +a 12985 12986 4102 +a 12986 12987 4102 +a 12987 12988 4102 +a 12988 12989 4102 +a 12989 12990 4102 +a 12990 12991 4102 +a 12991 12992 4102 +a 12992 12993 4102 +a 12993 12994 4102 +a 12994 12995 4102 +a 12995 12996 4102 +a 12996 12997 4102 +a 12997 12998 4102 +a 12998 12999 4102 +a 12999 13000 4102 +a 13000 13001 4102 +a 13001 13002 4102 +a 13002 13003 4102 +a 13003 13004 4102 +a 13004 13005 4102 +a 13005 13006 4102 +a 13006 13007 4102 +a 13007 13008 4102 +a 13008 13009 4102 +a 13009 13010 4102 +a 13010 13011 4102 +a 13011 13012 4102 +a 13012 13013 4102 +a 13013 13014 4102 +a 13014 13015 4102 +a 13015 13016 4102 +a 13016 13017 4102 +a 13017 13018 4102 +a 13018 13019 4102 +a 13019 13020 4102 +a 13020 13021 4102 +a 13021 13022 4102 +a 13022 13023 4102 +a 13023 13024 4102 +a 13024 13025 4102 +a 13025 13026 4102 +a 13026 13027 4102 +a 13027 13028 4102 +a 13028 13029 4102 +a 13029 13030 4102 +a 13030 13031 4102 +a 13031 13032 4102 +a 13032 13033 4102 +a 13033 13034 4102 +a 13034 13035 4102 +a 13035 13036 4102 +a 13036 13037 4102 +a 13037 13038 4102 +a 13038 13039 4102 +a 13039 13040 4102 +a 13040 13041 4102 +a 13041 13042 4102 +a 13042 13043 4102 +a 13043 13044 4102 +a 13044 13045 4102 +a 13045 13046 4102 +a 13046 13047 4102 +a 13047 13048 4102 +a 13048 13049 4102 +a 13049 13050 4102 +a 13050 13051 4102 +a 13051 13052 4102 +a 13052 13053 4102 +a 13053 13054 4102 +a 13054 13055 4102 +a 13055 13056 4102 +a 13056 13057 4102 +a 13057 13058 4102 +a 13058 13059 4102 +a 13059 13060 4102 +a 13060 13061 4102 +a 13061 13062 4102 +a 13062 13063 4102 +a 13063 13064 4102 +a 13064 13065 4102 +a 13065 13066 4102 +a 13066 13067 4102 +a 13067 13068 4102 +a 13068 13069 4102 +a 13069 13070 4102 +a 13070 13071 4102 +a 13071 13072 4102 +a 13072 13073 4102 +a 13073 13074 4102 +a 13074 13075 4102 +a 13075 13076 4102 +a 13076 13077 4102 +a 13077 13078 4102 +a 13078 13079 4102 +a 13079 13080 4102 +a 13080 13081 4102 +a 13081 13082 4102 +a 13082 13083 4102 +a 13083 13084 4102 +a 13084 13085 4102 +a 13085 13086 4102 +a 13086 13087 4102 +a 13087 13088 4102 +a 13088 13089 4102 +a 13089 13090 4102 +a 13090 13091 4102 +a 13091 13092 4102 +a 13092 13093 4102 +a 13093 13094 4102 +a 13094 13095 4102 +a 13095 13096 4102 +a 13096 13097 4102 +a 13097 13098 4102 +a 13098 13099 4102 +a 13099 13100 4102 +a 13100 13101 4102 +a 13101 13102 4102 +a 13102 13103 4102 +a 13103 13104 4102 +a 13104 13105 4102 +a 13105 13106 4102 +a 13106 13107 4102 +a 13107 13108 4102 +a 13108 13109 4102 +a 13109 13110 4102 +a 13110 13111 4102 +a 13111 13112 4102 +a 13112 13113 4102 +a 13113 13114 4102 +a 13114 13115 4102 +a 13115 13116 4102 +a 13116 13117 4102 +a 13117 13118 4102 +a 13118 13119 4102 +a 13119 13120 4102 +a 13120 13121 4102 +a 13121 13122 4102 +a 13122 13123 4102 +a 13123 13124 4102 +a 13124 13125 4102 +a 13125 13126 4102 +a 13126 13127 4102 +a 13127 13128 4102 +a 13128 13129 4102 +a 13129 13130 4102 +a 13130 13131 4102 +a 13131 13132 4102 +a 13132 13133 4102 +a 13133 13134 4102 +a 13134 13135 4102 +a 13135 13136 4102 +a 13136 13137 4102 +a 13137 13138 4102 +a 13138 13139 4102 +a 13139 13140 4102 +a 13140 13141 4102 +a 13141 13142 4102 +a 13142 13143 4102 +a 13143 13144 4102 +a 13144 13145 4102 +a 13145 13146 4102 +a 13146 13147 4102 +a 13147 13148 4102 +a 13148 13149 4102 +a 13149 13150 4102 +a 13150 13151 4102 +a 13151 13152 4102 +a 13152 13153 4102 +a 13153 13154 4102 +a 13154 13155 4102 +a 13155 13156 4102 +a 13156 13157 4102 +a 13157 13158 4102 +a 13158 13159 4102 +a 13159 13160 4102 +a 13160 13161 4102 +a 13161 13162 4102 +a 13162 13163 4102 +a 13163 13164 4102 +a 13164 13165 4102 +a 13165 13166 4102 +a 13166 13167 4102 +a 13167 13168 4102 +a 13168 13169 4102 +a 13169 13170 4102 +a 13170 13171 4102 +a 13171 13172 4102 +a 13172 13173 4102 +a 13173 13174 4102 +a 13174 13175 4102 +a 13175 13176 4102 +a 13176 13177 4102 +a 13177 13178 4102 +a 13178 13179 4102 +a 13179 13180 4102 +a 13180 13181 4102 +a 13181 13182 4102 +a 13182 13183 4102 +a 13183 13184 4102 +a 13184 13185 4102 +a 13185 13186 4102 +a 13186 13187 4102 +a 13187 13188 4102 +a 13188 13189 4102 +a 13189 13190 4102 +a 13190 13191 4102 +a 13191 13192 4102 +a 13192 13193 4102 +a 13193 13194 4102 +a 13194 13195 4102 +a 13195 13196 4102 +a 13196 13197 4102 +a 13197 13198 4102 +a 13198 13199 4102 +a 13199 13200 4102 +a 13200 13201 4102 +a 13201 13202 4102 +a 13202 13203 4102 +a 13203 13204 4102 +a 13204 13205 4102 +a 13205 13206 4102 +a 13206 13207 4102 +a 13207 13208 4102 +a 13208 13209 4102 +a 13209 13210 4102 +a 13210 13211 4102 +a 13211 13212 4102 +a 13212 13213 4102 +a 13213 13214 4102 +a 13214 13215 4102 +a 13215 13216 4102 +a 13216 13217 4102 +a 13217 13218 4102 +a 13218 13219 4102 +a 13219 13220 4102 +a 13220 13221 4102 +a 13221 13222 4102 +a 13222 13223 4102 +a 13223 13224 4102 +a 13224 13225 4102 +a 13225 13226 4102 +a 13226 13227 4102 +a 13227 13228 4102 +a 13228 13229 4102 +a 13229 13230 4102 +a 13230 13231 4102 +a 13231 13232 4102 +a 13232 13233 4102 +a 13233 13234 4102 +a 13234 13235 4102 +a 13235 13236 4102 +a 13236 13237 4102 +a 13237 13238 4102 +a 13238 13239 4102 +a 13239 13240 4102 +a 13240 13241 4102 +a 13241 13242 4102 +a 13242 13243 4102 +a 13243 13244 4102 +a 13244 13245 4102 +a 13245 13246 4102 +a 13246 13247 4102 +a 13247 13248 4102 +a 13248 13249 4102 +a 13249 13250 4102 +a 13250 13251 4102 +a 13251 13252 4102 +a 13252 13253 4102 +a 13253 13254 4102 +a 13254 13255 4102 +a 13255 13256 4102 +a 13256 13257 4102 +a 13257 13258 4102 +a 13258 13259 4102 +a 13259 13260 4102 +a 13260 13261 4102 +a 13261 13262 4102 +a 13262 13263 4102 +a 13263 13264 4102 +a 13264 13265 4102 +a 13265 13266 4102 +a 13266 13267 4102 +a 13267 13268 4102 +a 13268 13269 4102 +a 13269 13270 4102 +a 13270 13271 4102 +a 13271 13272 4102 +a 13272 13273 4102 +a 13273 13274 4102 +a 13274 13275 4102 +a 13275 13276 4102 +a 13276 13277 4102 +a 13277 13278 4102 +a 13278 13279 4102 +a 13279 13280 4102 +a 13280 13281 4102 +a 13281 13282 4102 +a 13282 13283 4102 +a 13283 13284 4102 +a 13284 13285 4102 +a 13285 13286 4102 +a 13286 13287 4102 +a 13287 13288 4102 +a 13288 13289 4102 +a 13289 13290 4102 +a 13290 13291 4102 +a 13291 13292 4102 +a 13292 13293 4102 +a 13293 13294 4102 +a 13294 13295 4102 +a 13295 13296 4102 +a 13296 13297 4102 +a 13297 13298 4102 +a 13298 13299 4102 +a 13299 13300 4102 +a 13300 13301 4102 +a 13301 13302 4102 +a 13302 13303 4102 +a 13303 13304 4102 +a 13304 13305 4102 +a 13305 13306 4102 +a 13306 13307 4102 +a 13307 13308 4102 +a 13308 13309 4102 +a 13309 13310 4102 +a 13310 13311 4102 +a 13311 13312 4102 +a 13312 13313 4102 +a 13313 13314 4102 +a 13314 13315 4102 +a 13315 13316 4102 +a 13316 13317 4102 +a 13317 13318 4102 +a 13318 13319 4102 +a 13319 13320 4102 +a 13320 13321 4102 +a 13321 13322 4102 +a 13322 13323 4102 +a 13323 13324 4102 +a 13324 13325 4102 +a 13325 13326 4102 +a 13326 13327 4102 +a 13327 13328 4102 +a 13328 13329 4102 +a 13329 13330 4102 +a 13330 13331 4102 +a 13331 13332 4102 +a 13332 13333 4102 +a 13333 13334 4102 +a 13334 13335 4102 +a 13335 13336 4102 +a 13336 13337 4102 +a 13337 13338 4102 +a 13338 13339 4102 +a 13339 13340 4102 +a 13340 13341 4102 +a 13341 13342 4102 +a 13342 13343 4102 +a 13343 13344 4102 +a 13344 13345 4102 +a 13345 13346 4102 +a 13346 13347 4102 +a 13347 13348 4102 +a 13348 13349 4102 +a 13349 13350 4102 +a 13350 13351 4102 +a 13351 13352 4102 +a 13352 13353 4102 +a 13353 13354 4102 +a 13354 13355 4102 +a 13355 13356 4102 +a 13356 13357 4102 +a 13357 13358 4102 +a 13358 13359 4102 +a 13359 13360 4102 +a 13360 13361 4102 +a 13361 13362 4102 +a 13362 13363 4102 +a 13363 13364 4102 +a 13364 13365 4102 +a 13365 13366 4102 +a 13366 13367 4102 +a 13367 13368 4102 +a 13368 13369 4102 +a 13369 13370 4102 +a 13370 13371 4102 +a 13371 13372 4102 +a 13372 13373 4102 +a 13373 13374 4102 +a 13374 13375 4102 +a 13375 13376 4102 +a 13376 13377 4102 +a 13377 13378 4102 +a 13378 13379 4102 +a 13379 13380 4102 +a 13380 13381 4102 +a 13381 13382 4102 +a 13382 13383 4102 +a 13383 13384 4102 +a 13384 13385 4102 +a 13385 13386 4102 +a 13386 13387 4102 +a 13387 13388 4102 +a 13388 13389 4102 +a 13389 13390 4102 +a 13390 13391 4102 +a 13391 13392 4102 +a 13392 13393 4102 +a 13393 13394 4102 +a 13394 13395 4102 +a 13395 13396 4102 +a 13396 13397 4102 +a 13397 13398 4102 +a 13398 13399 4102 +a 13399 13400 4102 +a 13400 13401 4102 +a 13401 13402 4102 +a 13402 13403 4102 +a 13403 13404 4102 +a 13404 13405 4102 +a 13405 13406 4102 +a 13406 13407 4102 +a 13407 13408 4102 +a 13408 13409 4102 +a 13409 13410 4102 +a 13410 13411 4102 +a 13411 13412 4102 +a 13412 13413 4102 +a 13413 13414 4102 +a 13414 13415 4102 +a 13415 13416 4102 +a 13416 13417 4102 +a 13417 13418 4102 +a 13418 13419 4102 +a 13419 13420 4102 +a 13420 13421 4102 +a 13421 13422 4102 +a 13422 13423 4102 +a 13423 13424 4102 +a 13424 13425 4102 +a 13425 13426 4102 +a 13426 13427 4102 +a 13427 13428 4102 +a 13428 13429 4102 +a 13429 13430 4102 +a 13430 13431 4102 +a 13431 13432 4102 +a 13432 13433 4102 +a 13433 13434 4102 +a 13434 13435 4102 +a 13435 13436 4102 +a 13436 13437 4102 +a 13437 13438 4102 +a 13438 13439 4102 +a 13439 13440 4102 +a 13440 13441 4102 +a 13441 13442 4102 +a 13442 13443 4102 +a 13443 13444 4102 +a 13444 13445 4102 +a 13445 13446 4102 +a 13446 13447 4102 +a 13447 13448 4102 +a 13448 13449 4102 +a 13449 13450 4102 +a 13450 13451 4102 +a 13451 13452 4102 +a 13452 13453 4102 +a 13453 13454 4102 +a 13454 13455 4102 +a 13455 13456 4102 +a 13456 13457 4102 +a 13457 13458 4102 +a 13458 13459 4102 +a 13459 13460 4102 +a 13460 13461 4102 +a 13461 13462 4102 +a 13462 13463 4102 +a 13463 13464 4102 +a 13464 13465 4102 +a 13465 13466 4102 +a 13466 13467 4102 +a 13467 13468 4102 +a 13468 13469 4102 +a 13469 13470 4102 +a 13470 13471 4102 +a 13471 13472 4102 +a 13472 13473 4102 +a 13473 13474 4102 +a 13474 13475 4102 +a 13475 13476 4102 +a 13476 13477 4102 +a 13477 13478 4102 +a 13478 13479 4102 +a 13479 13480 4102 +a 13480 13481 4102 +a 13481 13482 4102 +a 13482 13483 4102 +a 13483 13484 4102 +a 13484 13485 4102 +a 13485 13486 4102 +a 13486 13487 4102 +a 13487 13488 4102 +a 13488 13489 4102 +a 13489 13490 4102 +a 13490 13491 4102 +a 13491 13492 4102 +a 13492 13493 4102 +a 13493 13494 4102 +a 13494 13495 4102 +a 13495 13496 4102 +a 13496 13497 4102 +a 13497 13498 4102 +a 13498 13499 4102 +a 13499 13500 4102 +a 13500 13501 4102 +a 13501 13502 4102 +a 13502 13503 4102 +a 13503 13504 4102 +a 13504 13505 4102 +a 13505 13506 4102 +a 13506 13507 4102 +a 13507 13508 4102 +a 13508 13509 4102 +a 13509 13510 4102 +a 13510 13511 4102 +a 13511 13512 4102 +a 13512 13513 4102 +a 13513 13514 4102 +a 13514 13515 4102 +a 13515 13516 4102 +a 13516 13517 4102 +a 13517 13518 4102 +a 13518 13519 4102 +a 13519 13520 4102 +a 13520 13521 4102 +a 13521 13522 4102 +a 13522 13523 4102 +a 13523 13524 4102 +a 13524 13525 4102 +a 13525 13526 4102 +a 13526 13527 4102 +a 13527 13528 4102 +a 13528 13529 4102 +a 13529 13530 4102 +a 13530 13531 4102 +a 13531 13532 4102 +a 13532 13533 4102 +a 13533 13534 4102 +a 13534 13535 4102 +a 13535 13536 4102 +a 13536 13537 4102 +a 13537 13538 4102 +a 13538 13539 4102 +a 13539 13540 4102 +a 13540 13541 4102 +a 13541 13542 4102 +a 13542 13543 4102 +a 13543 13544 4102 +a 13544 13545 4102 +a 13545 13546 4102 +a 13546 13547 4102 +a 13547 13548 4102 +a 13548 13549 4102 +a 13549 13550 4102 +a 13550 13551 4102 +a 13551 13552 4102 +a 13552 13553 4102 +a 13553 13554 4102 +a 13554 13555 4102 +a 13555 13556 4102 +a 13556 13557 4102 +a 13557 13558 4102 +a 13558 13559 4102 +a 13559 13560 4102 +a 13560 13561 4102 +a 13561 13562 4102 +a 13562 13563 4102 +a 13563 13564 4102 +a 13564 13565 4102 +a 13565 13566 4102 +a 13566 13567 4102 +a 13567 13568 4102 +a 13568 13569 4102 +a 13569 13570 4102 +a 13570 13571 4102 +a 13571 13572 4102 +a 13572 13573 4102 +a 13573 13574 4102 +a 13574 13575 4102 +a 13575 13576 4102 +a 13576 13577 4102 +a 13577 13578 4102 +a 13578 13579 4102 +a 13579 13580 4102 +a 13580 13581 4102 +a 13581 13582 4102 +a 13582 13583 4102 +a 13583 13584 4102 +a 13584 13585 4102 +a 13585 13586 4102 +a 13586 13587 4102 +a 13587 13588 4102 +a 13588 13589 4102 +a 13589 13590 4102 +a 13590 13591 4102 +a 13591 13592 4102 +a 13592 13593 4102 +a 13593 13594 4102 +a 13594 13595 4102 +a 13595 13596 4102 +a 13596 13597 4102 +a 13597 13598 4102 +a 13598 13599 4102 +a 13599 13600 4102 +a 13600 13601 4102 +a 13601 13602 4102 +a 13602 13603 4102 +a 13603 13604 4102 +a 13604 13605 4102 +a 13605 13606 4102 +a 13606 13607 4102 +a 13607 13608 4102 +a 13608 13609 4102 +a 13609 13610 4102 +a 13610 13611 4102 +a 13611 13612 4102 +a 13612 13613 4102 +a 13613 13614 4102 +a 13614 13615 4102 +a 13615 13616 4102 +a 13616 13617 4102 +a 13617 13618 4102 +a 13618 13619 4102 +a 13619 13620 4102 +a 13620 13621 4102 +a 13621 13622 4102 +a 13622 13623 4102 +a 13623 13624 4102 +a 13624 13625 4102 +a 13625 13626 4102 +a 13626 13627 4102 +a 13627 13628 4102 +a 13628 13629 4102 +a 13629 13630 4102 +a 13630 13631 4102 +a 13631 13632 4102 +a 13632 13633 4102 +a 13633 13634 4102 +a 13634 13635 4102 +a 13635 13636 4102 +a 13636 13637 4102 +a 13637 13638 4102 +a 13638 13639 4102 +a 13639 13640 4102 +a 13640 13641 4102 +a 13641 13642 4102 +a 13642 13643 4102 +a 13643 13644 4102 +a 13644 13645 4102 +a 13645 13646 4102 +a 13646 13647 4102 +a 13647 13648 4102 +a 13648 13649 4102 +a 13649 13650 4102 +a 13650 13651 4102 +a 13651 13652 4102 +a 13652 13653 4102 +a 13653 13654 4102 +a 13654 13655 4102 +a 13655 13656 4102 +a 13656 13657 4102 +a 13657 13658 4102 +a 13658 13659 4102 +a 13659 13660 4102 +a 13660 13661 4102 +a 13661 13662 4102 +a 13662 13663 4102 +a 13663 13664 4102 +a 13664 13665 4102 +a 13665 13666 4102 +a 13666 13667 4102 +a 13667 13668 4102 +a 13668 13669 4102 +a 13669 13670 4102 +a 13670 13671 4102 +a 13671 13672 4102 +a 13672 13673 4102 +a 13673 13674 4102 +a 13674 13675 4102 +a 13675 13676 4102 +a 13676 13677 4102 +a 13677 13678 4102 +a 13678 13679 4102 +a 13679 13680 4102 +a 13680 13681 4102 +a 13681 13682 4102 +a 13682 13683 4102 +a 13683 13684 4102 +a 13684 13685 4102 +a 13685 13686 4102 +a 13686 13687 4102 +a 13687 13688 4102 +a 13688 13689 4102 +a 13689 13690 4102 +a 13690 13691 4102 +a 13691 13692 4102 +a 13692 13693 4102 +a 13693 13694 4102 +a 13694 13695 4102 +a 13695 13696 4102 +a 13696 13697 4102 +a 13697 13698 4102 +a 13698 13699 4102 +a 13699 13700 4102 +a 13700 13701 4102 +a 13701 13702 4102 +a 13702 13703 4102 +a 13703 13704 4102 +a 13704 13705 4102 +a 13705 13706 4102 +a 13706 13707 4102 +a 13707 13708 4102 +a 13708 13709 4102 +a 13709 13710 4102 +a 13710 13711 4102 +a 13711 13712 4102 +a 13712 13713 4102 +a 13713 13714 4102 +a 13714 13715 4102 +a 13715 13716 4102 +a 13716 13717 4102 +a 13717 13718 4102 +a 13718 13719 4102 +a 13719 13720 4102 +a 13720 13721 4102 +a 13721 13722 4102 +a 13722 13723 4102 +a 13723 13724 4102 +a 13724 13725 4102 +a 13725 13726 4102 +a 13726 13727 4102 +a 13727 13728 4102 +a 13728 13729 4102 +a 13729 13730 4102 +a 13730 13731 4102 +a 13731 13732 4102 +a 13732 13733 4102 +a 13733 13734 4102 +a 13734 13735 4102 +a 13735 13736 4102 +a 13736 13737 4102 +a 13737 13738 4102 +a 13738 13739 4102 +a 13739 13740 4102 +a 13740 13741 4102 +a 13741 13742 4102 +a 13742 13743 4102 +a 13743 13744 4102 +a 13744 13745 4102 +a 13745 13746 4102 +a 13746 13747 4102 +a 13747 13748 4102 +a 13748 13749 4102 +a 13749 13750 4102 +a 13750 13751 4102 +a 13751 13752 4102 +a 13752 13753 4102 +a 13753 13754 4102 +a 13754 13755 4102 +a 13755 13756 4102 +a 13756 13757 4102 +a 13757 13758 4102 +a 13758 13759 4102 +a 13759 13760 4102 +a 13760 13761 4102 +a 13761 13762 4102 +a 13762 13763 4102 +a 13763 13764 4102 +a 13764 13765 4102 +a 13765 13766 4102 +a 13766 13767 4102 +a 13767 13768 4102 +a 13768 13769 4102 +a 13769 13770 4102 +a 13770 13771 4102 +a 13771 13772 4102 +a 13772 13773 4102 +a 13773 13774 4102 +a 13774 13775 4102 +a 13775 13776 4102 +a 13776 13777 4102 +a 13777 13778 4102 +a 13778 13779 4102 +a 13779 13780 4102 +a 13780 13781 4102 +a 13781 13782 4102 +a 13782 13783 4102 +a 13783 13784 4102 +a 13784 13785 4102 +a 13785 13786 4102 +a 13786 13787 4102 +a 13787 13788 4102 +a 13788 13789 4102 +a 13789 13790 4102 +a 13790 13791 4102 +a 13791 13792 4102 +a 13792 13793 4102 +a 13793 13794 4102 +a 13794 13795 4102 +a 13795 13796 4102 +a 13796 13797 4102 +a 13797 13798 4102 +a 13798 13799 4102 +a 13799 13800 4102 +a 13800 13801 4102 +a 13801 13802 4102 +a 13802 13803 4102 +a 13803 13804 4102 +a 13804 13805 4102 +a 13805 13806 4102 +a 13806 13807 4102 +a 13807 13808 4102 +a 13808 13809 4102 +a 13809 13810 4102 +a 13810 13811 4102 +a 13811 13812 4102 +a 13812 13813 4102 +a 13813 13814 4102 +a 13814 13815 4102 +a 13815 13816 4102 +a 13816 13817 4102 +a 13817 13818 4102 +a 13818 13819 4102 +a 13819 13820 4102 +a 13820 13821 4102 +a 13821 13822 4102 +a 13822 13823 4102 +a 13823 13824 4102 +a 13824 13825 4102 +a 13825 13826 4102 +a 13826 13827 4102 +a 13827 13828 4102 +a 13828 13829 4102 +a 13829 13830 4102 +a 13830 13831 4102 +a 13831 13832 4102 +a 13832 13833 4102 +a 13833 13834 4102 +a 13834 13835 4102 +a 13835 13836 4102 +a 13836 13837 4102 +a 13837 13838 4102 +a 13838 13839 4102 +a 13839 13840 4102 +a 13840 13841 4102 +a 13841 13842 4102 +a 13842 13843 4102 +a 13843 13844 4102 +a 13844 13845 4102 +a 13845 13846 4102 +a 13846 13847 4102 +a 13847 13848 4102 +a 13848 13849 4102 +a 13849 13850 4102 +a 13850 13851 4102 +a 13851 13852 4102 +a 13852 13853 4102 +a 13853 13854 4102 +a 13854 13855 4102 +a 13855 13856 4102 +a 13856 13857 4102 +a 13857 13858 4102 +a 13858 13859 4102 +a 13859 13860 4102 +a 13860 13861 4102 +a 13861 13862 4102 +a 13862 13863 4102 +a 13863 13864 4102 +a 13864 13865 4102 +a 13865 13866 4102 +a 13866 13867 4102 +a 13867 13868 4102 +a 13868 13869 4102 +a 13869 13870 4102 +a 13870 13871 4102 +a 13871 13872 4102 +a 13872 13873 4102 +a 13873 13874 4102 +a 13874 13875 4102 +a 13875 13876 4102 +a 13876 13877 4102 +a 13877 13878 4102 +a 13878 13879 4102 +a 13879 13880 4102 +a 13880 13881 4102 +a 13881 13882 4102 +a 13882 13883 4102 +a 13883 13884 4102 +a 13884 13885 4102 +a 13885 13886 4102 +a 13886 13887 4102 +a 13887 13888 4102 +a 13888 13889 4102 +a 13889 13890 4102 +a 13890 13891 4102 +a 13891 13892 4102 +a 13892 13893 4102 +a 13893 13894 4102 +a 13894 13895 4102 +a 13895 13896 4102 +a 13896 13897 4102 +a 13897 13898 4102 +a 13898 13899 4102 +a 13899 13900 4102 +a 13900 13901 4102 +a 13901 13902 4102 +a 13902 13903 4102 +a 13903 13904 4102 +a 13904 13905 4102 +a 13905 13906 4102 +a 13906 13907 4102 +a 13907 13908 4102 +a 13908 13909 4102 +a 13909 13910 4102 +a 13910 13911 4102 +a 13911 13912 4102 +a 13912 13913 4102 +a 13913 13914 4102 +a 13914 13915 4102 +a 13915 13916 4102 +a 13916 13917 4102 +a 13917 13918 4102 +a 13918 13919 4102 +a 13919 13920 4102 +a 13920 13921 4102 +a 13921 13922 4102 +a 13922 13923 4102 +a 13923 13924 4102 +a 13924 13925 4102 +a 13925 13926 4102 +a 13926 13927 4102 +a 13927 13928 4102 +a 13928 13929 4102 +a 13929 13930 4102 +a 13930 13931 4102 +a 13931 13932 4102 +a 13932 13933 4102 +a 13933 13934 4102 +a 13934 13935 4102 +a 13935 13936 4102 +a 13936 13937 4102 +a 13937 13938 4102 +a 13938 13939 4102 +a 13939 13940 4102 +a 13940 13941 4102 +a 13941 13942 4102 +a 13942 13943 4102 +a 13943 13944 4102 +a 13944 13945 4102 +a 13945 13946 4102 +a 13946 13947 4102 +a 13947 13948 4102 +a 13948 13949 4102 +a 13949 13950 4102 +a 13950 13951 4102 +a 13951 13952 4102 +a 13952 13953 4102 +a 13953 13954 4102 +a 13954 13955 4102 +a 13955 13956 4102 +a 13956 13957 4102 +a 13957 13958 4102 +a 13958 13959 4102 +a 13959 13960 4102 +a 13960 13961 4102 +a 13961 13962 4102 +a 13962 13963 4102 +a 13963 13964 4102 +a 13964 13965 4102 +a 13965 13966 4102 +a 13966 13967 4102 +a 13967 13968 4102 +a 13968 13969 4102 +a 13969 13970 4102 +a 13970 13971 4102 +a 13971 13972 4102 +a 13972 13973 4102 +a 13973 13974 4102 +a 13974 13975 4102 +a 13975 13976 4102 +a 13976 13977 4102 +a 13977 13978 4102 +a 13978 13979 4102 +a 13979 13980 4102 +a 13980 13981 4102 +a 13981 13982 4102 +a 13982 13983 4102 +a 13983 13984 4102 +a 13984 13985 4102 +a 13985 13986 4102 +a 13986 13987 4102 +a 13987 13988 4102 +a 13988 13989 4102 +a 13989 13990 4102 +a 13990 13991 4102 +a 13991 13992 4102 +a 13992 13993 4102 +a 13993 13994 4102 +a 13994 13995 4102 +a 13995 13996 4102 +a 13996 13997 4102 +a 13997 13998 4102 +a 13998 13999 4102 +a 13999 14000 4102 +a 14000 14001 4102 +a 14001 14002 4102 +a 14002 14003 4102 +a 14003 14004 4102 +a 14004 14005 4102 +a 14005 14006 4102 +a 14006 14007 4102 +a 14007 14008 4102 +a 14008 14009 4102 +a 14009 14010 4102 +a 14010 14011 4102 +a 14011 14012 4102 +a 14012 14013 4102 +a 14013 14014 4102 +a 14014 14015 4102 +a 14015 14016 4102 +a 14016 14017 4102 +a 14017 14018 4102 +a 14018 14019 4102 +a 14019 14020 4102 +a 14020 14021 4102 +a 14021 14022 4102 +a 14022 14023 4102 +a 14023 14024 4102 +a 14024 14025 4102 +a 14025 14026 4102 +a 14026 14027 4102 +a 14027 14028 4102 +a 14028 14029 4102 +a 14029 14030 4102 +a 14030 14031 4102 +a 14031 14032 4102 +a 14032 14033 4102 +a 14033 14034 4102 +a 14034 14035 4102 +a 14035 14036 4102 +a 14036 14037 4102 +a 14037 14038 4102 +a 14038 14039 4102 +a 14039 14040 4102 +a 14040 14041 4102 +a 14041 14042 4102 +a 14042 14043 4102 +a 14043 14044 4102 +a 14044 14045 4102 +a 14045 14046 4102 +a 14046 14047 4102 +a 14047 14048 4102 +a 14048 14049 4102 +a 14049 14050 4102 +a 14050 14051 4102 +a 14051 14052 4102 +a 14052 14053 4102 +a 14053 14054 4102 +a 14054 14055 4102 +a 14055 14056 4102 +a 14056 14057 4102 +a 14057 14058 4102 +a 14058 14059 4102 +a 14059 14060 4102 +a 14060 14061 4102 +a 14061 14062 4102 +a 14062 14063 4102 +a 14063 14064 4102 +a 14064 14065 4102 +a 14065 14066 4102 +a 14066 14067 4102 +a 14067 14068 4102 +a 14068 14069 4102 +a 14069 14070 4102 +a 14070 14071 4102 +a 14071 14072 4102 +a 14072 14073 4102 +a 14073 14074 4102 +a 14074 14075 4102 +a 14075 14076 4102 +a 14076 14077 4102 +a 14077 14078 4102 +a 14078 14079 4102 +a 14079 14080 4102 +a 14080 14081 4102 +a 14081 14082 4102 +a 14082 14083 4102 +a 14083 14084 4102 +a 14084 14085 4102 +a 14085 14086 4102 +a 14086 14087 4102 +a 14087 14088 4102 +a 14088 14089 4102 +a 14089 14090 4102 +a 14090 14091 4102 +a 14091 14092 4102 +a 14092 14093 4102 +a 14093 14094 4102 +a 14094 14095 4102 +a 14095 14096 4102 +a 14096 14097 4102 +a 14097 14098 4102 +a 14098 14099 4102 +a 14099 14100 4102 +a 14100 14101 4102 +a 14101 14102 4102 +a 14102 14103 4102 +a 14103 14104 4102 +a 14104 14105 4102 +a 14105 14106 4102 +a 14106 14107 4102 +a 14107 14108 4102 +a 14108 14109 4102 +a 14109 14110 4102 +a 14110 14111 4102 +a 14111 14112 4102 +a 14112 14113 4102 +a 14113 14114 4102 +a 14114 14115 4102 +a 14115 14116 4102 +a 14116 14117 4102 +a 14117 14118 4102 +a 14118 14119 4102 +a 14119 14120 4102 +a 14120 14121 4102 +a 14121 14122 4102 +a 14122 14123 4102 +a 14123 14124 4102 +a 14124 14125 4102 +a 14125 14126 4102 +a 14126 14127 4102 +a 14127 14128 4102 +a 14128 14129 4102 +a 14129 14130 4102 +a 14130 14131 4102 +a 14131 14132 4102 +a 14132 14133 4102 +a 14133 14134 4102 +a 14134 14135 4102 +a 14135 14136 4102 +a 14136 14137 4102 +a 14137 14138 4102 +a 14138 14139 4102 +a 14139 14140 4102 +a 14140 14141 4102 +a 14141 14142 4102 +a 14142 14143 4102 +a 14143 14144 4102 +a 14144 14145 4102 +a 14145 14146 4102 +a 14146 14147 4102 +a 14147 14148 4102 +a 14148 14149 4102 +a 14149 14150 4102 +a 14150 14151 4102 +a 14151 14152 4102 +a 14152 14153 4102 +a 14153 14154 4102 +a 14154 14155 4102 +a 14155 14156 4102 +a 14156 14157 4102 +a 14157 14158 4102 +a 14158 14159 4102 +a 14159 14160 4102 +a 14160 14161 4102 +a 14161 14162 4102 +a 14162 14163 4102 +a 14163 14164 4102 +a 14164 14165 4102 +a 14165 14166 4102 +a 14166 14167 4102 +a 14167 14168 4102 +a 14168 14169 4102 +a 14169 14170 4102 +a 14170 14171 4102 +a 14171 14172 4102 +a 14172 14173 4102 +a 14173 14174 4102 +a 14174 14175 4102 +a 14175 14176 4102 +a 14176 14177 4102 +a 14177 14178 4102 +a 14178 14179 4102 +a 14179 14180 4102 +a 14180 14181 4102 +a 14181 14182 4102 +a 14182 14183 4102 +a 14183 14184 4102 +a 14184 14185 4102 +a 14185 14186 4102 +a 14186 14187 4102 +a 14187 14188 4102 +a 14188 14189 4102 +a 14189 14190 4102 +a 14190 14191 4102 +a 14191 14192 4102 +a 14192 14193 4102 +a 14193 14194 4102 +a 14194 14195 4102 +a 14195 14196 4102 +a 14196 14197 4102 +a 14197 14198 4102 +a 14198 14199 4102 +a 14199 14200 4102 +a 14200 14201 4102 +a 14201 14202 4102 +a 14202 14203 4102 +a 14203 14204 4102 +a 14204 14205 4102 +a 14205 14206 4102 +a 14206 14207 4102 +a 14207 14208 4102 +a 14208 14209 4102 +a 14209 14210 4102 +a 14210 14211 4102 +a 14211 14212 4102 +a 14212 14213 4102 +a 14213 14214 4102 +a 14214 14215 4102 +a 14215 14216 4102 +a 14216 14217 4102 +a 14217 14218 4102 +a 14218 14219 4102 +a 14219 14220 4102 +a 14220 14221 4102 +a 14221 14222 4102 +a 14222 14223 4102 +a 14223 14224 4102 +a 14224 14225 4102 +a 14225 14226 4102 +a 14226 14227 4102 +a 14227 14228 4102 +a 14228 14229 4102 +a 14229 14230 4102 +a 14230 14231 4102 +a 14231 14232 4102 +a 14232 14233 4102 +a 14233 14234 4102 +a 14234 14235 4102 +a 14235 14236 4102 +a 14236 14237 4102 +a 14237 14238 4102 +a 14238 14239 4102 +a 14239 14240 4102 +a 14240 14241 4102 +a 14241 14242 4102 +a 14242 14243 4102 +a 14243 14244 4102 +a 14244 14245 4102 +a 14245 14246 4102 +a 14246 14247 4102 +a 14247 14248 4102 +a 14248 14249 4102 +a 14249 14250 4102 +a 14250 14251 4102 +a 14251 14252 4102 +a 14252 14253 4102 +a 14253 14254 4102 +a 14254 14255 4102 +a 14255 14256 4102 +a 14256 14257 4102 +a 14257 14258 4102 +a 14258 14259 4102 +a 14259 14260 4102 +a 14260 14261 4102 +a 14261 14262 4102 +a 14262 14263 4102 +a 14263 14264 4102 +a 14264 14265 4102 +a 14265 14266 4102 +a 14266 14267 4102 +a 14267 14268 4102 +a 14268 14269 4102 +a 14269 14270 4102 +a 14270 14271 4102 +a 14271 14272 4102 +a 14272 14273 4102 +a 14273 14274 4102 +a 14274 14275 4102 +a 14275 14276 4102 +a 14276 14277 4102 +a 14277 14278 4102 +a 14278 14279 4102 +a 14279 14280 4102 +a 14280 14281 4102 +a 14281 14282 4102 +a 14282 14283 4102 +a 14283 14284 4102 +a 14284 14285 4102 +a 14285 14286 4102 +a 14286 14287 4102 +a 14287 14288 4102 +a 14288 14289 4102 +a 14289 14290 4102 +a 14290 14291 4102 +a 14291 14292 4102 +a 14292 14293 4102 +a 14293 14294 4102 +a 14294 14295 4102 +a 14295 14296 4102 +a 14296 14297 4102 +a 14297 14298 4102 +a 14298 14299 4102 +a 14299 14300 4102 +a 14300 14301 4102 +a 14301 14302 4102 +a 14302 14303 4102 +a 14303 14304 4102 +a 14304 14305 4102 +a 14305 14306 4102 +a 14306 14307 4102 +a 14307 14308 4102 +a 14308 14309 4102 +a 14309 14310 4102 +a 14310 14311 4102 +a 14311 14312 4102 +a 14312 14313 4102 +a 14313 14314 4102 +a 14314 14315 4102 +a 14315 14316 4102 +a 14316 14317 4102 +a 14317 14318 4102 +a 14318 14319 4102 +a 14319 14320 4102 +a 14320 14321 4102 +a 14321 14322 4102 +a 14322 14323 4102 +a 14323 14324 4102 +a 14324 14325 4102 +a 14325 14326 4102 +a 14326 14327 4102 +a 14327 14328 4102 +a 14328 14329 4102 +a 14329 14330 4102 +a 14330 14331 4102 +a 14331 14332 4102 +a 14332 14333 4102 +a 14333 14334 4102 +a 14334 14335 4102 +a 14335 14336 4102 +a 14336 14337 4102 +a 14337 14338 4102 +a 14338 14339 4102 +a 14339 14340 4102 +a 14340 14341 4102 +a 14341 14342 4102 +a 14342 14343 4102 +a 14343 14344 4102 +a 14344 14345 4102 +a 14345 14346 4102 +a 14346 14347 4102 +a 14347 14348 4102 +a 14348 14349 4102 +a 14349 14350 4102 +a 14350 14351 4102 +a 14351 14352 4102 +a 14352 14353 4102 +a 14353 14354 4102 +a 14354 14355 4102 +a 14355 14356 4102 +a 14356 14357 4102 +a 14357 14358 4102 +a 14358 14359 4102 +a 14359 14360 4102 +a 14360 14361 4102 +a 14361 14362 4102 +a 14362 14363 4102 +a 14363 14364 4102 +a 14364 14365 4102 +a 14365 14366 4102 +a 14366 14367 4102 +a 14367 14368 4102 +a 14368 14369 4102 +a 14369 14370 4102 +a 14370 14371 4102 +a 14371 14372 4102 +a 14372 14373 4102 +a 14373 14374 4102 +a 14374 14375 4102 +a 14375 14376 4102 +a 14376 14377 4102 +a 14377 14378 4102 +a 14378 14379 4102 +a 14379 14380 4102 +a 14380 14381 4102 +a 14381 14382 4102 +a 14382 14383 4102 +a 14383 14384 4102 +a 14384 14385 4102 +a 14385 14386 4102 +a 14386 14387 4102 +a 14387 14388 4102 +a 14388 14389 4102 +a 14389 14390 4102 +a 14390 14391 4102 +a 14391 14392 4102 +a 14392 14393 4102 +a 14393 14394 4102 +a 14394 14395 4102 +a 14395 14396 4102 +a 14396 14397 4102 +a 14397 14398 4102 +a 14398 14399 4102 +a 14399 14400 4102 +a 14400 14401 4102 +a 14401 14402 4102 +a 14402 14403 4102 +a 14403 14404 4102 +a 14404 14405 4102 +a 14405 14406 4102 +a 14406 14407 4102 +a 14407 14408 4102 +a 14408 14409 4102 +a 14409 14410 4102 +a 14410 14411 4102 +a 14411 14412 4102 +a 14412 14413 4102 +a 14413 14414 4102 +a 14414 14415 4102 +a 14415 14416 4102 +a 14416 14417 4102 +a 14417 14418 4102 +a 14418 14419 4102 +a 14419 14420 4102 +a 14420 14421 4102 +a 14421 14422 4102 +a 14422 14423 4102 +a 14423 14424 4102 +a 14424 14425 4102 +a 14425 14426 4102 +a 14426 14427 4102 +a 14427 14428 4102 +a 14428 14429 4102 +a 14429 14430 4102 +a 14430 14431 4102 +a 14431 14432 4102 +a 14432 14433 4102 +a 14433 14434 4102 +a 14434 14435 4102 +a 14435 14436 4102 +a 14436 14437 4102 +a 14437 14438 4102 +a 14438 14439 4102 +a 14439 14440 4102 +a 14440 14441 4102 +a 14441 14442 4102 +a 14442 14443 4102 +a 14443 14444 4102 +a 14444 14445 4102 +a 14445 14446 4102 +a 14446 14447 4102 +a 14447 14448 4102 +a 14448 14449 4102 +a 14449 14450 4102 +a 14450 14451 4102 +a 14451 14452 4102 +a 14452 14453 4102 +a 14453 14454 4102 +a 14454 14455 4102 +a 14455 14456 4102 +a 14456 14457 4102 +a 14457 14458 4102 +a 14458 14459 4102 +a 14459 14460 4102 +a 14460 14461 4102 +a 14461 14462 4102 +a 14462 14463 4102 +a 14463 14464 4102 +a 14464 14465 4102 +a 14465 14466 4102 +a 14466 14467 4102 +a 14467 14468 4102 +a 14468 14469 4102 +a 14469 14470 4102 +a 14470 14471 4102 +a 14471 14472 4102 +a 14472 14473 4102 +a 14473 14474 4102 +a 14474 14475 4102 +a 14475 14476 4102 +a 14476 14477 4102 +a 14477 14478 4102 +a 14478 14479 4102 +a 14479 14480 4102 +a 14480 14481 4102 +a 14481 14482 4102 +a 14482 14483 4102 +a 14483 14484 4102 +a 14484 14485 4102 +a 14485 14486 4102 +a 14486 14487 4102 +a 14487 14488 4102 +a 14488 14489 4102 +a 14489 14490 4102 +a 14490 14491 4102 +a 14491 14492 4102 +a 14492 14493 4102 +a 14493 14494 4102 +a 14494 14495 4102 +a 14495 14496 4102 +a 14496 14497 4102 +a 14497 14498 4102 +a 14498 14499 4102 +a 14499 14500 4102 +a 14500 14501 4102 +a 14501 14502 4102 +a 14502 14503 4102 +a 14503 14504 4102 +a 14504 14505 4102 +a 14505 14506 4102 +a 14506 14507 4102 +a 14507 14508 4102 +a 14508 14509 4102 +a 14509 14510 4102 +a 14510 14511 4102 +a 14511 14512 4102 +a 14512 14513 4102 +a 14513 14514 4102 +a 14514 14515 4102 +a 14515 14516 4102 +a 14516 14517 4102 +a 14517 14518 4102 +a 14518 14519 4102 +a 14519 14520 4102 +a 14520 14521 4102 +a 14521 14522 4102 +a 14522 14523 4102 +a 14523 14524 4102 +a 14524 14525 4102 +a 14525 14526 4102 +a 14526 14527 4102 +a 14527 14528 4102 +a 14528 14529 4102 +a 14529 14530 4102 +a 14530 14531 4102 +a 14531 14532 4102 +a 14532 14533 4102 +a 14533 14534 4102 +a 14534 14535 4102 +a 14535 14536 4102 +a 14536 14537 4102 +a 14537 14538 4102 +a 14538 14539 4102 +a 14539 14540 4102 +a 14540 14541 4102 +a 14541 14542 4102 +a 14542 14543 4102 +a 14543 14544 4102 +a 14544 14545 4102 +a 14545 14546 4102 +a 14546 14547 4102 +a 14547 14548 4102 +a 14548 14549 4102 +a 14549 14550 4102 +a 14550 14551 4102 +a 14551 14552 4102 +a 14552 14553 4102 +a 14553 14554 4102 +a 14554 14555 4102 +a 14555 14556 4102 +a 14556 14557 4102 +a 14557 14558 4102 +a 14558 14559 4102 +a 14559 14560 4102 +a 14560 14561 4102 +a 14561 14562 4102 +a 14562 14563 4102 +a 14563 14564 4102 +a 14564 14565 4102 +a 14565 14566 4102 +a 14566 14567 4102 +a 14567 14568 4102 +a 14568 14569 4102 +a 14569 14570 4102 +a 14570 14571 4102 +a 14571 14572 4102 +a 14572 14573 4102 +a 14573 14574 4102 +a 14574 14575 4102 +a 14575 14576 4102 +a 14576 14577 4102 +a 14577 14578 4102 +a 14578 14579 4102 +a 14579 14580 4102 +a 14580 14581 4102 +a 14581 14582 4102 +a 14582 14583 4102 +a 14583 14584 4102 +a 14584 14585 4102 +a 14585 14586 4102 +a 14586 14587 4102 +a 14587 14588 4102 +a 14588 14589 4102 +a 14589 14590 4102 +a 14590 14591 4102 +a 14591 14592 4102 +a 14592 14593 4102 +a 14593 14594 4102 +a 14594 14595 4102 +a 14595 14596 4102 +a 14596 14597 4102 +a 14597 14598 4102 +a 14598 14599 4102 +a 14599 14600 4102 +a 14600 14601 4102 +a 14601 14602 4102 +a 14602 14603 4102 +a 14603 14604 4102 +a 14604 14605 4102 +a 14605 14606 4102 +a 14606 14607 4102 +a 14607 14608 4102 +a 14608 14609 4102 +a 14609 14610 4102 +a 14610 14611 4102 +a 14611 14612 4102 +a 14612 14613 4102 +a 14613 14614 4102 +a 14614 14615 4102 +a 14615 14616 4102 +a 14616 14617 4102 +a 14617 14618 4102 +a 14618 14619 4102 +a 14619 14620 4102 +a 14620 14621 4102 +a 14621 14622 4102 +a 14622 14623 4102 +a 14623 14624 4102 +a 14624 14625 4102 +a 14625 14626 4102 +a 14626 14627 4102 +a 14627 14628 4102 +a 14628 14629 4102 +a 14629 14630 4102 +a 14630 14631 4102 +a 14631 14632 4102 +a 14632 14633 4102 +a 14633 14634 4102 +a 14634 14635 4102 +a 14635 14636 4102 +a 14636 14637 4102 +a 14637 14638 4102 +a 14638 14639 4102 +a 14639 14640 4102 +a 14640 14641 4102 +a 14641 14642 4102 +a 14642 14643 4102 +a 14643 14644 4102 +a 14644 14645 4102 +a 14645 14646 4102 +a 14646 14647 4102 +a 14647 14648 4102 +a 14648 14649 4102 +a 14649 14650 4102 +a 14650 14651 4102 +a 14651 14652 4102 +a 14652 14653 4102 +a 14653 14654 4102 +a 14654 14655 4102 +a 14655 14656 4102 +a 14656 14657 4102 +a 14657 14658 4102 +a 14658 14659 4102 +a 14659 14660 4102 +a 14660 14661 4102 +a 14661 14662 4102 +a 14662 14663 4102 +a 14663 14664 4102 +a 14664 14665 4102 +a 14665 14666 4102 +a 14666 14667 4102 +a 14667 14668 4102 +a 14668 14669 4102 +a 14669 14670 4102 +a 14670 14671 4102 +a 14671 14672 4102 +a 14672 14673 4102 +a 14673 14674 4102 +a 14674 14675 4102 +a 14675 14676 4102 +a 14676 14677 4102 +a 14677 14678 4102 +a 14678 14679 4102 +a 14679 14680 4102 +a 14680 14681 4102 +a 14681 14682 4102 +a 14682 14683 4102 +a 14683 14684 4102 +a 14684 14685 4102 +a 14685 14686 4102 +a 14686 14687 4102 +a 14687 14688 4102 +a 14688 14689 4102 +a 14689 14690 4102 +a 14690 14691 4102 +a 14691 14692 4102 +a 14692 14693 4102 +a 14693 14694 4102 +a 14694 14695 4102 +a 14695 14696 4102 +a 14696 14697 4102 +a 14697 14698 4102 +a 14698 14699 4102 +a 14699 14700 4102 +a 14700 14701 4102 +a 14701 14702 4102 +a 14702 14703 4102 +a 14703 14704 4102 +a 14704 14705 4102 +a 14705 14706 4102 +a 14706 14707 4102 +a 14707 14708 4102 +a 14708 14709 4102 +a 14709 14710 4102 +a 14710 14711 4102 +a 14711 14712 4102 +a 14712 14713 4102 +a 14713 14714 4102 +a 14714 14715 4102 +a 14715 14716 4102 +a 14716 14717 4102 +a 14717 14718 4102 +a 14718 14719 4102 +a 14719 14720 4102 +a 14720 14721 4102 +a 14721 14722 4102 +a 14722 14723 4102 +a 14723 14724 4102 +a 14724 14725 4102 +a 14725 14726 4102 +a 14726 14727 4102 +a 14727 14728 4102 +a 14728 14729 4102 +a 14729 14730 4102 +a 14730 14731 4102 +a 14731 14732 4102 +a 14732 14733 4102 +a 14733 14734 4102 +a 14734 14735 4102 +a 14735 14736 4102 +a 14736 14737 4102 +a 14737 14738 4102 +a 14738 14739 4102 +a 14739 14740 4102 +a 14740 14741 4102 +a 14741 14742 4102 +a 14742 14743 4102 +a 14743 14744 4102 +a 14744 14745 4102 +a 14745 14746 4102 +a 14746 14747 4102 +a 14747 14748 4102 +a 14748 14749 4102 +a 14749 14750 4102 +a 14750 14751 4102 +a 14751 14752 4102 +a 14752 14753 4102 +a 14753 14754 4102 +a 14754 14755 4102 +a 14755 14756 4102 +a 14756 14757 4102 +a 14757 14758 4102 +a 14758 14759 4102 +a 14759 14760 4102 +a 14760 14761 4102 +a 14761 14762 4102 +a 14762 14763 4102 +a 14763 14764 4102 +a 14764 14765 4102 +a 14765 14766 4102 +a 14766 14767 4102 +a 14767 14768 4102 +a 14768 14769 4102 +a 14769 14770 4102 +a 14770 14771 4102 +a 14771 14772 4102 +a 14772 14773 4102 +a 14773 14774 4102 +a 14774 14775 4102 +a 14775 14776 4102 +a 14776 14777 4102 +a 14777 14778 4102 +a 14778 14779 4102 +a 14779 14780 4102 +a 14780 14781 4102 +a 14781 14782 4102 +a 14782 14783 4102 +a 14783 14784 4102 +a 14784 14785 4102 +a 14785 14786 4102 +a 14786 14787 4102 +a 14787 14788 4102 +a 14788 14789 4102 +a 14789 14790 4102 +a 14790 14791 4102 +a 14791 14792 4102 +a 14792 14793 4102 +a 14793 14794 4102 +a 14794 14795 4102 +a 14795 14796 4102 +a 14796 14797 4102 +a 14797 14798 4102 +a 14798 14799 4102 +a 14799 14800 4102 +a 14800 14801 4102 +a 14801 14802 4102 +a 14802 14803 4102 +a 14803 14804 4102 +a 14804 14805 4102 +a 14805 14806 4102 +a 14806 14807 4102 +a 14807 14808 4102 +a 14808 14809 4102 +a 14809 14810 4102 +a 14810 14811 4102 +a 14811 14812 4102 +a 14812 14813 4102 +a 14813 14814 4102 +a 14814 14815 4102 +a 14815 14816 4102 +a 14816 14817 4102 +a 14817 14818 4102 +a 14818 14819 4102 +a 14819 14820 4102 +a 14820 14821 4102 +a 14821 14822 4102 +a 14822 14823 4102 +a 14823 14824 4102 +a 14824 14825 4102 +a 14825 14826 4102 +a 14826 14827 4102 +a 14827 14828 4102 +a 14828 14829 4102 +a 14829 14830 4102 +a 14830 14831 4102 +a 14831 14832 4102 +a 14832 14833 4102 +a 14833 14834 4102 +a 14834 14835 4102 +a 14835 14836 4102 +a 14836 14837 4102 +a 14837 14838 4102 +a 14838 14839 4102 +a 14839 14840 4102 +a 14840 14841 4102 +a 14841 14842 4102 +a 14842 14843 4102 +a 14843 14844 4102 +a 14844 14845 4102 +a 14845 14846 4102 +a 14846 14847 4102 +a 14847 14848 4102 +a 14848 14849 4102 +a 14849 14850 4102 +a 14850 14851 4102 +a 14851 14852 4102 +a 14852 14853 4102 +a 14853 14854 4102 +a 14854 14855 4102 +a 14855 14856 4102 +a 14856 14857 4102 +a 14857 14858 4102 +a 14858 14859 4102 +a 14859 14860 4102 +a 14860 14861 4102 +a 14861 14862 4102 +a 14862 14863 4102 +a 14863 14864 4102 +a 14864 14865 4102 +a 14865 14866 4102 +a 14866 14867 4102 +a 14867 14868 4102 +a 14868 14869 4102 +a 14869 14870 4102 +a 14870 14871 4102 +a 14871 14872 4102 +a 14872 14873 4102 +a 14873 14874 4102 +a 14874 14875 4102 +a 14875 14876 4102 +a 14876 14877 4102 +a 14877 14878 4102 +a 14878 14879 4102 +a 14879 14880 4102 +a 14880 14881 4102 +a 14881 14882 4102 +a 14882 14883 4102 +a 14883 14884 4102 +a 14884 14885 4102 +a 14885 14886 4102 +a 14886 14887 4102 +a 14887 14888 4102 +a 14888 14889 4102 +a 14889 14890 4102 +a 14890 14891 4102 +a 14891 14892 4102 +a 14892 14893 4102 +a 14893 14894 4102 +a 14894 14895 4102 +a 14895 14896 4102 +a 14896 14897 4102 +a 14897 14898 4102 +a 14898 14899 4102 +a 14899 14900 4102 +a 14900 14901 4102 +a 14901 14902 4102 +a 14902 14903 4102 +a 14903 14904 4102 +a 14904 14905 4102 +a 14905 14906 4102 +a 14906 14907 4102 +a 14907 14908 4102 +a 14908 14909 4102 +a 14909 14910 4102 +a 14910 14911 4102 +a 14911 14912 4102 +a 14912 14913 4102 +a 14913 14914 4102 +a 14914 14915 4102 +a 14915 14916 4102 +a 14916 14917 4102 +a 14917 14918 4102 +a 14918 14919 4102 +a 14919 14920 4102 +a 14920 14921 4102 +a 14921 14922 4102 +a 14922 14923 4102 +a 14923 14924 4102 +a 14924 14925 4102 +a 14925 14926 4102 +a 14926 14927 4102 +a 14927 14928 4102 +a 14928 14929 4102 +a 14929 14930 4102 +a 14930 14931 4102 +a 14931 14932 4102 +a 14932 14933 4102 +a 14933 14934 4102 +a 14934 14935 4102 +a 14935 14936 4102 +a 14936 14937 4102 +a 14937 14938 4102 +a 14938 14939 4102 +a 14939 14940 4102 +a 14940 14941 4102 +a 14941 14942 4102 +a 14942 14943 4102 +a 14943 14944 4102 +a 14944 14945 4102 +a 14945 14946 4102 +a 14946 14947 4102 +a 14947 14948 4102 +a 14948 14949 4102 +a 14949 14950 4102 +a 14950 14951 4102 +a 14951 14952 4102 +a 14952 14953 4102 +a 14953 14954 4102 +a 14954 14955 4102 +a 14955 14956 4102 +a 14956 14957 4102 +a 14957 14958 4102 +a 14958 14959 4102 +a 14959 14960 4102 +a 14960 14961 4102 +a 14961 14962 4102 +a 14962 14963 4102 +a 14963 14964 4102 +a 14964 14965 4102 +a 14965 14966 4102 +a 14966 14967 4102 +a 14967 14968 4102 +a 14968 14969 4102 +a 14969 14970 4102 +a 14970 14971 4102 +a 14971 14972 4102 +a 14972 14973 4102 +a 14973 14974 4102 +a 14974 14975 4102 +a 14975 14976 4102 +a 14976 14977 4102 +a 14977 14978 4102 +a 14978 14979 4102 +a 14979 14980 4102 +a 14980 14981 4102 +a 14981 14982 4102 +a 14982 14983 4102 +a 14983 14984 4102 +a 14984 14985 4102 +a 14985 14986 4102 +a 14986 14987 4102 +a 14987 14988 4102 +a 14988 14989 4102 +a 14989 14990 4102 +a 14990 14991 4102 +a 14991 14992 4102 +a 14992 14993 4102 +a 14993 14994 4102 +a 14994 14995 4102 +a 14995 14996 4102 +a 14996 14997 4102 +a 14997 14998 4102 +a 14998 14999 4102 +a 14999 15000 4102 +a 15000 15001 4102 +a 15001 15002 4102 +a 15002 15003 4102 +a 15003 15004 4102 +a 15004 15005 4102 +a 15005 15006 4102 +a 15006 15007 4102 +a 15007 15008 4102 +a 15008 15009 4102 +a 15009 15010 4102 +a 15010 15011 4102 +a 15011 15012 4102 +a 15012 15013 4102 +a 15013 15014 4102 +a 15014 15015 4102 +a 15015 15016 4102 +a 15016 15017 4102 +a 15017 15018 4102 +a 15018 15019 4102 +a 15019 15020 4102 +a 15020 15021 4102 +a 15021 15022 4102 +a 15022 15023 4102 +a 15023 15024 4102 +a 15024 15025 4102 +a 15025 15026 4102 +a 15026 15027 4102 +a 15027 15028 4102 +a 15028 15029 4102 +a 15029 15030 4102 +a 15030 15031 4102 +a 15031 15032 4102 +a 15032 15033 4102 +a 15033 15034 4102 +a 15034 15035 4102 +a 15035 15036 4102 +a 15036 15037 4102 +a 15037 15038 4102 +a 15038 15039 4102 +a 15039 15040 4102 +a 15040 15041 4102 +a 15041 15042 4102 +a 15042 15043 4102 +a 15043 15044 4102 +a 15044 15045 4102 +a 15045 15046 4102 +a 15046 15047 4102 +a 15047 15048 4102 +a 15048 15049 4102 +a 15049 15050 4102 +a 15050 15051 4102 +a 15051 15052 4102 +a 15052 15053 4102 +a 15053 15054 4102 +a 15054 15055 4102 +a 15055 15056 4102 +a 15056 15057 4102 +a 15057 15058 4102 +a 15058 15059 4102 +a 15059 15060 4102 +a 15060 15061 4102 +a 15061 15062 4102 +a 15062 15063 4102 +a 15063 15064 4102 +a 15064 15065 4102 +a 15065 15066 4102 +a 15066 15067 4102 +a 15067 15068 4102 +a 15068 15069 4102 +a 15069 15070 4102 +a 15070 15071 4102 +a 15071 15072 4102 +a 15072 15073 4102 +a 15073 15074 4102 +a 15074 15075 4102 +a 15075 15076 4102 +a 15076 15077 4102 +a 15077 15078 4102 +a 15078 15079 4102 +a 15079 15080 4102 +a 15080 15081 4102 +a 15081 15082 4102 +a 15082 15083 4102 +a 15083 15084 4102 +a 15084 15085 4102 +a 15085 15086 4102 +a 15086 15087 4102 +a 15087 15088 4102 +a 15088 15089 4102 +a 15089 15090 4102 +a 15090 15091 4102 +a 15091 15092 4102 +a 15092 15093 4102 +a 15093 15094 4102 +a 15094 15095 4102 +a 15095 15096 4102 +a 15096 15097 4102 +a 15097 15098 4102 +a 15098 15099 4102 +a 15099 15100 4102 +a 15100 15101 4102 +a 15101 15102 4102 +a 15102 15103 4102 +a 15103 15104 4102 +a 15104 15105 4102 +a 15105 15106 4102 +a 15106 15107 4102 +a 15107 15108 4102 +a 15108 15109 4102 +a 15109 15110 4102 +a 15110 15111 4102 +a 15111 15112 4102 +a 15112 15113 4102 +a 15113 15114 4102 +a 15114 15115 4102 +a 15115 15116 4102 +a 15116 15117 4102 +a 15117 15118 4102 +a 15118 15119 4102 +a 15119 15120 4102 +a 15120 15121 4102 +a 15121 15122 4102 +a 15122 15123 4102 +a 15123 15124 4102 +a 15124 15125 4102 +a 15125 15126 4102 +a 15126 15127 4102 +a 15127 15128 4102 +a 15128 15129 4102 +a 15129 15130 4102 +a 15130 15131 4102 +a 15131 15132 4102 +a 15132 15133 4102 +a 15133 15134 4102 +a 15134 15135 4102 +a 15135 15136 4102 +a 15136 15137 4102 +a 15137 15138 4102 +a 15138 15139 4102 +a 15139 15140 4102 +a 15140 15141 4102 +a 15141 15142 4102 +a 15142 15143 4102 +a 15143 15144 4102 +a 15144 15145 4102 +a 15145 15146 4102 +a 15146 15147 4102 +a 15147 15148 4102 +a 15148 15149 4102 +a 15149 15150 4102 +a 15150 15151 4102 +a 15151 15152 4102 +a 15152 15153 4102 +a 15153 15154 4102 +a 15154 15155 4102 +a 15155 15156 4102 +a 15156 15157 4102 +a 15157 15158 4102 +a 15158 15159 4102 +a 15159 15160 4102 +a 15160 15161 4102 +a 15161 15162 4102 +a 15162 15163 4102 +a 15163 15164 4102 +a 15164 15165 4102 +a 15165 15166 4102 +a 15166 15167 4102 +a 15167 15168 4102 +a 15168 15169 4102 +a 15169 15170 4102 +a 15170 15171 4102 +a 15171 15172 4102 +a 15172 15173 4102 +a 15173 15174 4102 +a 15174 15175 4102 +a 15175 15176 4102 +a 15176 15177 4102 +a 15177 15178 4102 +a 15178 15179 4102 +a 15179 15180 4102 +a 15180 15181 4102 +a 15181 15182 4102 +a 15182 15183 4102 +a 15183 15184 4102 +a 15184 15185 4102 +a 15185 15186 4102 +a 15186 15187 4102 +a 15187 15188 4102 +a 15188 15189 4102 +a 15189 15190 4102 +a 15190 15191 4102 +a 15191 15192 4102 +a 15192 15193 4102 +a 15193 15194 4102 +a 15194 15195 4102 +a 15195 15196 4102 +a 15196 15197 4102 +a 15197 15198 4102 +a 15198 15199 4102 +a 15199 15200 4102 +a 15200 15201 4102 +a 15201 15202 4102 +a 15202 15203 4102 +a 15203 15204 4102 +a 15204 15205 4102 +a 15205 15206 4102 +a 15206 15207 4102 +a 15207 15208 4102 +a 15208 15209 4102 +a 15209 15210 4102 +a 15210 15211 4102 +a 15211 15212 4102 +a 15212 15213 4102 +a 15213 15214 4102 +a 15214 15215 4102 +a 15215 15216 4102 +a 15216 15217 4102 +a 15217 15218 4102 +a 15218 15219 4102 +a 15219 15220 4102 +a 15220 15221 4102 +a 15221 15222 4102 +a 15222 15223 4102 +a 15223 15224 4102 +a 15224 15225 4102 +a 15225 15226 4102 +a 15226 15227 4102 +a 15227 15228 4102 +a 15228 15229 4102 +a 15229 15230 4102 +a 15230 15231 4102 +a 15231 15232 4102 +a 15232 15233 4102 +a 15233 15234 4102 +a 15234 15235 4102 +a 15235 15236 4102 +a 15236 15237 4102 +a 15237 15238 4102 +a 15238 15239 4102 +a 15239 15240 4102 +a 15240 15241 4102 +a 15241 15242 4102 +a 15242 15243 4102 +a 15243 15244 4102 +a 15244 15245 4102 +a 15245 15246 4102 +a 15246 15247 4102 +a 15247 15248 4102 +a 15248 15249 4102 +a 15249 15250 4102 +a 15250 15251 4102 +a 15251 15252 4102 +a 15252 15253 4102 +a 15253 15254 4102 +a 15254 15255 4102 +a 15255 15256 4102 +a 15256 15257 4102 +a 15257 15258 4102 +a 15258 15259 4102 +a 15259 15260 4102 +a 15260 15261 4102 +a 15261 15262 4102 +a 15262 15263 4102 +a 15263 15264 4102 +a 15264 15265 4102 +a 15265 15266 4102 +a 15266 15267 4102 +a 15267 15268 4102 +a 15268 15269 4102 +a 15269 15270 4102 +a 15270 15271 4102 +a 15271 15272 4102 +a 15272 15273 4102 +a 15273 15274 4102 +a 15274 15275 4102 +a 15275 15276 4102 +a 15276 15277 4102 +a 15277 15278 4102 +a 15278 15279 4102 +a 15279 15280 4102 +a 15280 15281 4102 +a 15281 15282 4102 +a 15282 15283 4102 +a 15283 15284 4102 +a 15284 15285 4102 +a 15285 15286 4102 +a 15286 15287 4102 +a 15287 15288 4102 +a 15288 15289 4102 +a 15289 15290 4102 +a 15290 15291 4102 +a 15291 15292 4102 +a 15292 15293 4102 +a 15293 15294 4102 +a 15294 15295 4102 +a 15295 15296 4102 +a 15296 15297 4102 +a 15297 15298 4102 +a 15298 15299 4102 +a 15299 15300 4102 +a 15300 15301 4102 +a 15301 15302 4102 +a 15302 15303 4102 +a 15303 15304 4102 +a 15304 15305 4102 +a 15305 15306 4102 +a 15306 15307 4102 +a 15307 15308 4102 +a 15308 15309 4102 +a 15309 15310 4102 +a 15310 15311 4102 +a 15311 15312 4102 +a 15312 15313 4102 +a 15313 15314 4102 +a 15314 15315 4102 +a 15315 15316 4102 +a 15316 15317 4102 +a 15317 15318 4102 +a 15318 15319 4102 +a 15319 15320 4102 +a 15320 15321 4102 +a 15321 15322 4102 +a 15322 15323 4102 +a 15323 15324 4102 +a 15324 15325 4102 +a 15325 15326 4102 +a 15326 15327 4102 +a 15327 15328 4102 +a 15328 15329 4102 +a 15329 15330 4102 +a 15330 15331 4102 +a 15331 15332 4102 +a 15332 15333 4102 +a 15333 15334 4102 +a 15334 15335 4102 +a 15335 15336 4102 +a 15336 15337 4102 +a 15337 15338 4102 +a 15338 15339 4102 +a 15339 15340 4102 +a 15340 15341 4102 +a 15341 15342 4102 +a 15342 15343 4102 +a 15343 15344 4102 +a 15344 15345 4102 +a 15345 15346 4102 +a 15346 15347 4102 +a 15347 15348 4102 +a 15348 15349 4102 +a 15349 15350 4102 +a 15350 15351 4102 +a 15351 15352 4102 +a 15352 15353 4102 +a 15353 15354 4102 +a 15354 15355 4102 +a 15355 15356 4102 +a 15356 15357 4102 +a 15357 15358 4102 +a 15358 15359 4102 +a 15359 15360 4102 +a 15360 15361 4102 +a 15361 15362 4102 +a 15362 15363 4102 +a 15363 15364 4102 +a 15364 15365 4102 +a 15365 15366 4102 +a 15366 15367 4102 +a 15367 15368 4102 +a 15368 15369 4102 +a 15369 15370 4102 +a 15370 15371 4102 +a 15371 15372 4102 +a 15372 15373 4102 +a 15373 15374 4102 +a 15374 15375 4102 +a 15375 15376 4102 +a 15376 15377 4102 +a 15377 15378 4102 +a 15378 15379 4102 +a 15379 15380 4102 +a 15380 15381 4102 +a 15381 15382 4102 +a 15382 15383 4102 +a 15383 15384 4102 +a 15384 15385 4102 +a 15385 15386 4102 +a 15386 15387 4102 +a 15387 15388 4102 +a 15388 15389 4102 +a 15389 15390 4102 +a 15390 15391 4102 +a 15391 15392 4102 +a 15392 15393 4102 +a 15393 15394 4102 +a 15394 15395 4102 +a 15395 15396 4102 +a 15396 15397 4102 +a 15397 15398 4102 +a 15398 15399 4102 +a 15399 15400 4102 +a 15400 15401 4102 +a 15401 15402 4102 +a 15402 15403 4102 +a 15403 15404 4102 +a 15404 15405 4102 +a 15405 15406 4102 +a 15406 15407 4102 +a 15407 15408 4102 +a 15408 15409 4102 +a 15409 15410 4102 +a 15410 15411 4102 +a 15411 15412 4102 +a 15412 15413 4102 +a 15413 15414 4102 +a 15414 15415 4102 +a 15415 15416 4102 +a 15416 15417 4102 +a 15417 15418 4102 +a 15418 15419 4102 +a 15419 15420 4102 +a 15420 15421 4102 +a 15421 15422 4102 +a 15422 15423 4102 +a 15423 15424 4102 +a 15424 15425 4102 +a 15425 15426 4102 +a 15426 15427 4102 +a 15427 15428 4102 +a 15428 15429 4102 +a 15429 15430 4102 +a 15430 15431 4102 +a 15431 15432 4102 +a 15432 15433 4102 +a 15433 15434 4102 +a 15434 15435 4102 +a 15435 15436 4102 +a 15436 15437 4102 +a 15437 15438 4102 +a 15438 15439 4102 +a 15439 15440 4102 +a 15440 15441 4102 +a 15441 15442 4102 +a 15442 15443 4102 +a 15443 15444 4102 +a 15444 15445 4102 +a 15445 15446 4102 +a 15446 15447 4102 +a 15447 15448 4102 +a 15448 15449 4102 +a 15449 15450 4102 +a 15450 15451 4102 +a 15451 15452 4102 +a 15452 15453 4102 +a 15453 15454 4102 +a 15454 15455 4102 +a 15455 15456 4102 +a 15456 15457 4102 +a 15457 15458 4102 +a 15458 15459 4102 +a 15459 15460 4102 +a 15460 15461 4102 +a 15461 15462 4102 +a 15462 15463 4102 +a 15463 15464 4102 +a 15464 15465 4102 +a 15465 15466 4102 +a 15466 15467 4102 +a 15467 15468 4102 +a 15468 15469 4102 +a 15469 15470 4102 +a 15470 15471 4102 +a 15471 15472 4102 +a 15472 15473 4102 +a 15473 15474 4102 +a 15474 15475 4102 +a 15475 15476 4102 +a 15476 15477 4102 +a 15477 15478 4102 +a 15478 15479 4102 +a 15479 15480 4102 +a 15480 15481 4102 +a 15481 15482 4102 +a 15482 15483 4102 +a 15483 15484 4102 +a 15484 15485 4102 +a 15485 15486 4102 +a 15486 15487 4102 +a 15487 15488 4102 +a 15488 15489 4102 +a 15489 15490 4102 +a 15490 15491 4102 +a 15491 15492 4102 +a 15492 15493 4102 +a 15493 15494 4102 +a 15494 15495 4102 +a 15495 15496 4102 +a 15496 15497 4102 +a 15497 15498 4102 +a 15498 15499 4102 +a 15499 15500 4102 +a 15500 15501 4102 +a 15501 15502 4102 +a 15502 15503 4102 +a 15503 15504 4102 +a 15504 15505 4102 +a 15505 15506 4102 +a 15506 15507 4102 +a 15507 15508 4102 +a 15508 15509 4102 +a 15509 15510 4102 +a 15510 15511 4102 +a 15511 15512 4102 +a 15512 15513 4102 +a 15513 15514 4102 +a 15514 15515 4102 +a 15515 15516 4102 +a 15516 15517 4102 +a 15517 15518 4102 +a 15518 15519 4102 +a 15519 15520 4102 +a 15520 15521 4102 +a 15521 15522 4102 +a 15522 15523 4102 +a 15523 15524 4102 +a 15524 15525 4102 +a 15525 15526 4102 +a 15526 15527 4102 +a 15527 15528 4102 +a 15528 15529 4102 +a 15529 15530 4102 +a 15530 15531 4102 +a 15531 15532 4102 +a 15532 15533 4102 +a 15533 15534 4102 +a 15534 15535 4102 +a 15535 15536 4102 +a 15536 15537 4102 +a 15537 15538 4102 +a 15538 15539 4102 +a 15539 15540 4102 +a 15540 15541 4102 +a 15541 15542 4102 +a 15542 15543 4102 +a 15543 15544 4102 +a 15544 15545 4102 +a 15545 15546 4102 +a 15546 15547 4102 +a 15547 15548 4102 +a 15548 15549 4102 +a 15549 15550 4102 +a 15550 15551 4102 +a 15551 15552 4102 +a 15552 15553 4102 +a 15553 15554 4102 +a 15554 15555 4102 +a 15555 15556 4102 +a 15556 15557 4102 +a 15557 15558 4102 +a 15558 15559 4102 +a 15559 15560 4102 +a 15560 15561 4102 +a 15561 15562 4102 +a 15562 15563 4102 +a 15563 15564 4102 +a 15564 15565 4102 +a 15565 15566 4102 +a 15566 15567 4102 +a 15567 15568 4102 +a 15568 15569 4102 +a 15569 15570 4102 +a 15570 15571 4102 +a 15571 15572 4102 +a 15572 15573 4102 +a 15573 15574 4102 +a 15574 15575 4102 +a 15575 15576 4102 +a 15576 15577 4102 +a 15577 15578 4102 +a 15578 15579 4102 +a 15579 15580 4102 +a 15580 15581 4102 +a 15581 15582 4102 +a 15582 15583 4102 +a 15583 15584 4102 +a 15584 15585 4102 +a 15585 15586 4102 +a 15586 15587 4102 +a 15587 15588 4102 +a 15588 15589 4102 +a 15589 15590 4102 +a 15590 15591 4102 +a 15591 15592 4102 +a 15592 15593 4102 +a 15593 15594 4102 +a 15594 15595 4102 +a 15595 15596 4102 +a 15596 15597 4102 +a 15597 15598 4102 +a 15598 15599 4102 +a 15599 15600 4102 +a 15600 15601 4102 +a 15601 15602 4102 +a 15602 15603 4102 +a 15603 15604 4102 +a 15604 15605 4102 +a 15605 15606 4102 +a 15606 15607 4102 +a 15607 15608 4102 +a 15608 15609 4102 +a 15609 15610 4102 +a 15610 15611 4102 +a 15611 15612 4102 +a 15612 15613 4102 +a 15613 15614 4102 +a 15614 15615 4102 +a 15615 15616 4102 +a 15616 15617 4102 +a 15617 15618 4102 +a 15618 15619 4102 +a 15619 15620 4102 +a 15620 15621 4102 +a 15621 15622 4102 +a 15622 15623 4102 +a 15623 15624 4102 +a 15624 15625 4102 +a 15625 15626 4102 +a 15626 15627 4102 +a 15627 15628 4102 +a 15628 15629 4102 +a 15629 15630 4102 +a 15630 15631 4102 +a 15631 15632 4102 +a 15632 15633 4102 +a 15633 15634 4102 +a 15634 15635 4102 +a 15635 15636 4102 +a 15636 15637 4102 +a 15637 15638 4102 +a 15638 15639 4102 +a 15639 15640 4102 +a 15640 15641 4102 +a 15641 15642 4102 +a 15642 15643 4102 +a 15643 15644 4102 +a 15644 15645 4102 +a 15645 15646 4102 +a 15646 15647 4102 +a 15647 15648 4102 +a 15648 15649 4102 +a 15649 15650 4102 +a 15650 15651 4102 +a 15651 15652 4102 +a 15652 15653 4102 +a 15653 15654 4102 +a 15654 15655 4102 +a 15655 15656 4102 +a 15656 15657 4102 +a 15657 15658 4102 +a 15658 15659 4102 +a 15659 15660 4102 +a 15660 15661 4102 +a 15661 15662 4102 +a 15662 15663 4102 +a 15663 15664 4102 +a 15664 15665 4102 +a 15665 15666 4102 +a 15666 15667 4102 +a 15667 15668 4102 +a 15668 15669 4102 +a 15669 15670 4102 +a 15670 15671 4102 +a 15671 15672 4102 +a 15672 15673 4102 +a 15673 15674 4102 +a 15674 15675 4102 +a 15675 15676 4102 +a 15676 15677 4102 +a 15677 15678 4102 +a 15678 15679 4102 +a 15679 15680 4102 +a 15680 15681 4102 +a 15681 15682 4102 +a 15682 15683 4102 +a 15683 15684 4102 +a 15684 15685 4102 +a 15685 15686 4102 +a 15686 15687 4102 +a 15687 15688 4102 +a 15688 15689 4102 +a 15689 15690 4102 +a 15690 15691 4102 +a 15691 15692 4102 +a 15692 15693 4102 +a 15693 15694 4102 +a 15694 15695 4102 +a 15695 15696 4102 +a 15696 15697 4102 +a 15697 15698 4102 +a 15698 15699 4102 +a 15699 15700 4102 +a 15700 15701 4102 +a 15701 15702 4102 +a 15702 15703 4102 +a 15703 15704 4102 +a 15704 15705 4102 +a 15705 15706 4102 +a 15706 15707 4102 +a 15707 15708 4102 +a 15708 15709 4102 +a 15709 15710 4102 +a 15710 15711 4102 +a 15711 15712 4102 +a 15712 15713 4102 +a 15713 15714 4102 +a 15714 15715 4102 +a 15715 15716 4102 +a 15716 15717 4102 +a 15717 15718 4102 +a 15718 15719 4102 +a 15719 15720 4102 +a 15720 15721 4102 +a 15721 15722 4102 +a 15722 15723 4102 +a 15723 15724 4102 +a 15724 15725 4102 +a 15725 15726 4102 +a 15726 15727 4102 +a 15727 15728 4102 +a 15728 15729 4102 +a 15729 15730 4102 +a 15730 15731 4102 +a 15731 15732 4102 +a 15732 15733 4102 +a 15733 15734 4102 +a 15734 15735 4102 +a 15735 15736 4102 +a 15736 15737 4102 +a 15737 15738 4102 +a 15738 15739 4102 +a 15739 15740 4102 +a 15740 15741 4102 +a 15741 15742 4102 +a 15742 15743 4102 +a 15743 15744 4102 +a 15744 15745 4102 +a 15745 15746 4102 +a 15746 15747 4102 +a 15747 15748 4102 +a 15748 15749 4102 +a 15749 15750 4102 +a 15750 15751 4102 +a 15751 15752 4102 +a 15752 15753 4102 +a 15753 15754 4102 +a 15754 15755 4102 +a 15755 15756 4102 +a 15756 15757 4102 +a 15757 15758 4102 +a 15758 15759 4102 +a 15759 15760 4102 +a 15760 15761 4102 +a 15761 15762 4102 +a 15762 15763 4102 +a 15763 15764 4102 +a 15764 15765 4102 +a 15765 15766 4102 +a 15766 15767 4102 +a 15767 15768 4102 +a 15768 15769 4102 +a 15769 15770 4102 +a 15770 15771 4102 +a 15771 15772 4102 +a 15772 15773 4102 +a 15773 15774 4102 +a 15774 15775 4102 +a 15775 15776 4102 +a 15776 15777 4102 +a 15777 15778 4102 +a 15778 15779 4102 +a 15779 15780 4102 +a 15780 15781 4102 +a 15781 15782 4102 +a 15782 15783 4102 +a 15783 15784 4102 +a 15784 15785 4102 +a 15785 15786 4102 +a 15786 15787 4102 +a 15787 15788 4102 +a 15788 15789 4102 +a 15789 15790 4102 +a 15790 15791 4102 +a 15791 15792 4102 +a 15792 15793 4102 +a 15793 15794 4102 +a 15794 15795 4102 +a 15795 15796 4102 +a 15796 15797 4102 +a 15797 15798 4102 +a 15798 15799 4102 +a 15799 15800 4102 +a 15800 15801 4102 +a 15801 15802 4102 +a 15802 15803 4102 +a 15803 15804 4102 +a 15804 15805 4102 +a 15805 15806 4102 +a 15806 15807 4102 +a 15807 15808 4102 +a 15808 15809 4102 +a 15809 15810 4102 +a 15810 15811 4102 +a 15811 15812 4102 +a 15812 15813 4102 +a 15813 15814 4102 +a 15814 15815 4102 +a 15815 15816 4102 +a 15816 15817 4102 +a 15817 15818 4102 +a 15818 15819 4102 +a 15819 15820 4102 +a 15820 15821 4102 +a 15821 15822 4102 +a 15822 15823 4102 +a 15823 15824 4102 +a 15824 15825 4102 +a 15825 15826 4102 +a 15826 15827 4102 +a 15827 15828 4102 +a 15828 15829 4102 +a 15829 15830 4102 +a 15830 15831 4102 +a 15831 15832 4102 +a 15832 15833 4102 +a 15833 15834 4102 +a 15834 15835 4102 +a 15835 15836 4102 +a 15836 15837 4102 +a 15837 15838 4102 +a 15838 15839 4102 +a 15839 15840 4102 +a 15840 15841 4102 +a 15841 15842 4102 +a 15842 15843 4102 +a 15843 15844 4102 +a 15844 15845 4102 +a 15845 15846 4102 +a 15846 15847 4102 +a 15847 15848 4102 +a 15848 15849 4102 +a 15849 15850 4102 +a 15850 15851 4102 +a 15851 15852 4102 +a 15852 15853 4102 +a 15853 15854 4102 +a 15854 15855 4102 +a 15855 15856 4102 +a 15856 15857 4102 +a 15857 15858 4102 +a 15858 15859 4102 +a 15859 15860 4102 +a 15860 15861 4102 +a 15861 15862 4102 +a 15862 15863 4102 +a 15863 15864 4102 +a 15864 15865 4102 +a 15865 15866 4102 +a 15866 15867 4102 +a 15867 15868 4102 +a 15868 15869 4102 +a 15869 15870 4102 +a 15870 15871 4102 +a 15871 15872 4102 +a 15872 15873 4102 +a 15873 15874 4102 +a 15874 15875 4102 +a 15875 15876 4102 +a 15876 15877 4102 +a 15877 15878 4102 +a 15878 15879 4102 +a 15879 15880 4102 +a 15880 15881 4102 +a 15881 15882 4102 +a 15882 15883 4102 +a 15883 15884 4102 +a 15884 15885 4102 +a 15885 15886 4102 +a 15886 15887 4102 +a 15887 15888 4102 +a 15888 15889 4102 +a 15889 15890 4102 +a 15890 15891 4102 +a 15891 15892 4102 +a 15892 15893 4102 +a 15893 15894 4102 +a 15894 15895 4102 +a 15895 15896 4102 +a 15896 15897 4102 +a 15897 15898 4102 +a 15898 15899 4102 +a 15899 15900 4102 +a 15900 15901 4102 +a 15901 15902 4102 +a 15902 15903 4102 +a 15903 15904 4102 +a 15904 15905 4102 +a 15905 15906 4102 +a 15906 15907 4102 +a 15907 15908 4102 +a 15908 15909 4102 +a 15909 15910 4102 +a 15910 15911 4102 +a 15911 15912 4102 +a 15912 15913 4102 +a 15913 15914 4102 +a 15914 15915 4102 +a 15915 15916 4102 +a 15916 15917 4102 +a 15917 15918 4102 +a 15918 15919 4102 +a 15919 15920 4102 +a 15920 15921 4102 +a 15921 15922 4102 +a 15922 15923 4102 +a 15923 15924 4102 +a 15924 15925 4102 +a 15925 15926 4102 +a 15926 15927 4102 +a 15927 15928 4102 +a 15928 15929 4102 +a 15929 15930 4102 +a 15930 15931 4102 +a 15931 15932 4102 +a 15932 15933 4102 +a 15933 15934 4102 +a 15934 15935 4102 +a 15935 15936 4102 +a 15936 15937 4102 +a 15937 15938 4102 +a 15938 15939 4102 +a 15939 15940 4102 +a 15940 15941 4102 +a 15941 15942 4102 +a 15942 15943 4102 +a 15943 15944 4102 +a 15944 15945 4102 +a 15945 15946 4102 +a 15946 15947 4102 +a 15947 15948 4102 +a 15948 15949 4102 +a 15949 15950 4102 +a 15950 15951 4102 +a 15951 15952 4102 +a 15952 15953 4102 +a 15953 15954 4102 +a 15954 15955 4102 +a 15955 15956 4102 +a 15956 15957 4102 +a 15957 15958 4102 +a 15958 15959 4102 +a 15959 15960 4102 +a 15960 15961 4102 +a 15961 15962 4102 +a 15962 15963 4102 +a 15963 15964 4102 +a 15964 15965 4102 +a 15965 15966 4102 +a 15966 15967 4102 +a 15967 15968 4102 +a 15968 15969 4102 +a 15969 15970 4102 +a 15970 15971 4102 +a 15971 15972 4102 +a 15972 15973 4102 +a 15973 15974 4102 +a 15974 15975 4102 +a 15975 15976 4102 +a 15976 15977 4102 +a 15977 15978 4102 +a 15978 15979 4102 +a 15979 15980 4102 +a 15980 15981 4102 +a 15981 15982 4102 +a 15982 15983 4102 +a 15983 15984 4102 +a 15984 15985 4102 +a 15985 15986 4102 +a 15986 15987 4102 +a 15987 15988 4102 +a 15988 15989 4102 +a 15989 15990 4102 +a 15990 15991 4102 +a 15991 15992 4102 +a 15992 15993 4102 +a 15993 15994 4102 +a 15994 15995 4102 +a 15995 15996 4102 +a 15996 15997 4102 +a 15997 15998 4102 +a 15998 15999 4102 +a 15999 16000 4102 +a 16000 16001 4102 +a 16001 16002 4102 +a 16002 16003 4102 +a 16003 16004 4102 +a 16004 16005 4102 +a 16005 16006 4102 +a 16006 16007 4102 +a 16007 16008 4102 +a 16008 16009 4102 +a 16009 16010 4102 +a 16010 16011 4102 +a 16011 16012 4102 +a 16012 16013 4102 +a 16013 16014 4102 +a 16014 16015 4102 +a 16015 16016 4102 +a 16016 16017 4102 +a 16017 16018 4102 +a 16018 16019 4102 +a 16019 16020 4102 +a 16020 16021 4102 +a 16021 16022 4102 +a 16022 16023 4102 +a 16023 16024 4102 +a 16024 16025 4102 +a 16025 16026 4102 +a 16026 16027 4102 +a 16027 16028 4102 +a 16028 16029 4102 +a 16029 16030 4102 +a 16030 16031 4102 +a 16031 16032 4102 +a 16032 16033 4102 +a 16033 16034 4102 +a 16034 16035 4102 +a 16035 16036 4102 +a 16036 16037 4102 +a 16037 16038 4102 +a 16038 16039 4102 +a 16039 16040 4102 +a 16040 16041 4102 +a 16041 16042 4102 +a 16042 16043 4102 +a 16043 16044 4102 +a 16044 16045 4102 +a 16045 16046 4102 +a 16046 16047 4102 +a 16047 16048 4102 +a 16048 16049 4102 +a 16049 16050 4102 +a 16050 16051 4102 +a 16051 16052 4102 +a 16052 16053 4102 +a 16053 16054 4102 +a 16054 16055 4102 +a 16055 16056 4102 +a 16056 16057 4102 +a 16057 16058 4102 +a 16058 16059 4102 +a 16059 16060 4102 +a 16060 16061 4102 +a 16061 16062 4102 +a 16062 16063 4102 +a 16063 16064 4102 +a 16064 16065 4102 +a 16065 16066 4102 +a 16066 16067 4102 +a 16067 16068 4102 +a 16068 16069 4102 +a 16069 16070 4102 +a 16070 16071 4102 +a 16071 16072 4102 +a 16072 16073 4102 +a 16073 16074 4102 +a 16074 16075 4102 +a 16075 16076 4102 +a 16076 16077 4102 +a 16077 16078 4102 +a 16078 16079 4102 +a 16079 16080 4102 +a 16080 16081 4102 +a 16081 16082 4102 +a 16082 16083 4102 +a 16083 16084 4102 +a 16084 16085 4102 +a 16085 16086 4102 +a 16086 16087 4102 +a 16087 16088 4102 +a 16088 16089 4102 +a 16089 16090 4102 +a 16090 16091 4102 +a 16091 16092 4102 +a 16092 16093 4102 +a 16093 16094 4102 +a 16094 16095 4102 +a 16095 16096 4102 +a 16096 16097 4102 +a 16097 16098 4102 +a 16098 16099 4102 +a 16099 16100 4102 +a 16100 16101 4102 +a 16101 16102 4102 +a 16102 16103 4102 +a 16103 16104 4102 +a 16104 16105 4102 +a 16105 16106 4102 +a 16106 16107 4102 +a 16107 16108 4102 +a 16108 16109 4102 +a 16109 16110 4102 +a 16110 16111 4102 +a 16111 16112 4102 +a 16112 16113 4102 +a 16113 16114 4102 +a 16114 16115 4102 +a 16115 16116 4102 +a 16116 16117 4102 +a 16117 16118 4102 +a 16118 16119 4102 +a 16119 16120 4102 +a 16120 16121 4102 +a 16121 16122 4102 +a 16122 16123 4102 +a 16123 16124 4102 +a 16124 16125 4102 +a 16125 16126 4102 +a 16126 16127 4102 +a 16127 16128 4102 +a 16128 16129 4102 +a 16129 16130 4102 +a 16130 16131 4102 +a 16131 16132 4102 +a 16132 16133 4102 +a 16133 16134 4102 +a 16134 16135 4102 +a 16135 16136 4102 +a 16136 16137 4102 +a 16137 16138 4102 +a 16138 16139 4102 +a 16139 16140 4102 +a 16140 16141 4102 +a 16141 16142 4102 +a 16142 16143 4102 +a 16143 16144 4102 +a 16144 16145 4102 +a 16145 16146 4102 +a 16146 16147 4102 +a 16147 16148 4102 +a 16148 16149 4102 +a 16149 16150 4102 +a 16150 16151 4102 +a 16151 16152 4102 +a 16152 16153 4102 +a 16153 16154 4102 +a 16154 16155 4102 +a 16155 16156 4102 +a 16156 16157 4102 +a 16157 16158 4102 +a 16158 16159 4102 +a 16159 16160 4102 +a 16160 16161 4102 +a 16161 16162 4102 +a 16162 16163 4102 +a 16163 16164 4102 +a 16164 16165 4102 +a 16165 16166 4102 +a 16166 16167 4102 +a 16167 16168 4102 +a 16168 16169 4102 +a 16169 16170 4102 +a 16170 16171 4102 +a 16171 16172 4102 +a 16172 16173 4102 +a 16173 16174 4102 +a 16174 16175 4102 +a 16175 16176 4102 +a 16176 16177 4102 +a 16177 16178 4102 +a 16178 16179 4102 +a 16179 16180 4102 +a 16180 16181 4102 +a 16181 16182 4102 +a 16182 16183 4102 +a 16183 16184 4102 +a 16184 16185 4102 +a 16185 16186 4102 +a 16186 16187 4102 +a 16187 16188 4102 +a 16188 16189 4102 +a 16189 16190 4102 +a 16190 16191 4102 +a 16191 16192 4102 +a 16192 16193 4102 +a 16193 16194 4102 +a 16194 16195 4102 +a 16195 16196 4102 +a 16196 16197 4102 +a 16197 16198 4102 +a 16198 16199 4102 +a 16199 16200 4102 +a 16200 16201 4102 +a 16201 16202 4102 +a 16202 16203 4102 +a 16203 16204 4102 +a 16204 16205 4102 +a 16205 16206 4102 +a 16206 16207 4102 +a 16207 16208 4102 +a 16208 16209 4102 +a 16209 16210 4102 +a 16210 16211 4102 +a 16211 16212 4102 +a 16212 16213 4102 +a 16213 16214 4102 +a 16214 16215 4102 +a 16215 16216 4102 +a 16216 16217 4102 +a 16217 16218 4102 +a 16218 16219 4102 +a 16219 16220 4102 +a 16220 16221 4102 +a 16221 16222 4102 +a 16222 16223 4102 +a 16223 16224 4102 +a 16224 16225 4102 +a 16225 16226 4102 +a 16226 16227 4102 +a 16227 16228 4102 +a 16228 16229 4102 +a 16229 16230 4102 +a 16230 16231 4102 +a 16231 16232 4102 +a 16232 16233 4102 +a 16233 16234 4102 +a 16234 16235 4102 +a 16235 16236 4102 +a 16236 16237 4102 +a 16237 16238 4102 +a 16238 16239 4102 +a 16239 16240 4102 +a 16240 16241 4102 +a 16241 16242 4102 +a 16242 16243 4102 +a 16243 16244 4102 +a 16244 16245 4102 +a 16245 16246 4102 +a 16246 16247 4102 +a 16247 16248 4102 +a 16248 16249 4102 +a 16249 16250 4102 +a 16250 16251 4102 +a 16251 16252 4102 +a 16252 16253 4102 +a 16253 16254 4102 +a 16254 16255 4102 +a 16255 16256 4102 +a 16256 16257 4102 +a 16257 16258 4102 +a 16258 16259 4102 +a 16259 16260 4102 +a 16260 16261 4102 +a 16261 16262 4102 +a 16262 16263 4102 +a 16263 16264 4102 +a 16264 16265 4102 +a 16265 16266 4102 +a 16266 16267 4102 +a 16267 16268 4102 +a 16268 16269 4102 +a 16269 16270 4102 +a 16270 16271 4102 +a 16271 16272 4102 +a 16272 16273 4102 +a 16273 16274 4102 +a 16274 16275 4102 +a 16275 16276 4102 +a 16276 16277 4102 +a 16277 16278 4102 +a 16278 16279 4102 +a 16279 16280 4102 +a 16280 16281 4102 +a 16281 16282 4102 +a 16282 16283 4102 +a 16283 16284 4102 +a 16284 16285 4102 +a 16285 16286 4102 +a 16286 16287 4102 +a 16287 16288 4102 +a 16288 16289 4102 +a 16289 16290 4102 +a 16290 16291 4102 +a 16291 16292 4102 +a 16292 16293 4102 +a 16293 16294 4102 +a 16294 16295 4102 +a 16295 16296 4102 +a 16296 16297 4102 +a 16297 16298 4102 +a 16298 16299 4102 +a 16299 16300 4102 +a 16300 16301 4102 +a 16301 16302 4102 +a 16302 16303 4102 +a 16303 16304 4102 +a 16304 16305 4102 +a 16305 16306 4102 +a 16306 16307 4102 +a 16307 16308 4102 +a 16308 16309 4102 +a 16309 16310 4102 +a 16310 16311 4102 +a 16311 16312 4102 +a 16312 16313 4102 +a 16313 16314 4102 +a 16314 16315 4102 +a 16315 16316 4102 +a 16316 16317 4102 +a 16317 16318 4102 +a 16318 16319 4102 +a 16319 16320 4102 +a 16320 16321 4102 +a 16321 16322 4102 +a 16322 16323 4102 +a 16323 16324 4102 +a 16324 16325 4102 +a 16325 16326 4102 +a 16326 16327 4102 +a 16327 16328 4102 +a 16328 16329 4102 +a 16329 16330 4102 +a 16330 16331 4102 +a 16331 16332 4102 +a 16332 16333 4102 +a 16333 16334 4102 +a 16334 16335 4102 +a 16335 16336 4102 +a 16336 16337 4102 +a 16337 16338 4102 +a 16338 16339 4102 +a 16339 16340 4102 +a 16340 16341 4102 +a 16341 16342 4102 +a 16342 16343 4102 +a 16343 16344 4102 +a 16344 16345 4102 +a 16345 16346 4102 +a 16346 16347 4102 +a 16347 16348 4102 +a 16348 16349 4102 +a 16349 16350 4102 +a 16350 16351 4102 +a 16351 16352 4102 +a 16352 16353 4102 +a 16353 16354 4102 +a 16354 16355 4102 +a 16355 16356 4102 +a 16356 16357 4102 +a 16357 16358 4102 +a 16358 16359 4102 +a 16359 16360 4102 +a 16360 16361 4102 +a 16361 16362 4102 +a 16362 16363 4102 +a 16363 16364 4102 +a 16364 16365 4102 +a 16365 16366 4102 +a 16366 16367 4102 +a 16367 16368 4102 +a 16368 16369 4102 +a 16369 16370 4102 +a 16370 16371 4102 +a 16371 16372 4102 +a 16372 16373 4102 +a 16373 16374 4102 +a 16374 16375 4102 +a 16375 16376 4102 +a 16376 16377 4102 +a 16377 16378 4102 +a 16378 16379 4102 +a 16379 16380 4102 +a 16380 16381 4102 +a 16381 16382 4102 +a 16382 16383 4102 +a 16383 16384 4102 +a 16384 16385 4102 +a 16385 16386 4102 +a 16386 16387 4102 +a 16387 16388 4102 +a 16388 16389 4102 +a 16389 16390 4102 +a 16390 16391 4102 +a 16391 16392 4102 +a 16392 16393 4102 +a 16393 16394 4102 +a 16394 16395 4102 +a 16395 16396 4102 +a 16396 16397 4102 +a 16397 16398 4102 +a 16398 16399 4102 +a 16399 16400 4102 +a 16400 16401 4102 +a 16401 16402 4102 +a 16402 16403 4102 +a 16403 16404 4102 +a 16404 16405 4102 +a 16405 16406 4102 +a 16406 16407 4102 +a 16407 16408 4102 +a 16408 16409 4102 +a 16409 16410 4102 +a 16410 16411 4102 +a 16411 16412 4102 +a 16412 16413 4102 +a 16413 16414 4102 +a 8209 16414 1 +a 8210 16413 1 +a 8211 16412 1 +a 8212 16411 1 +a 8213 16410 1 +a 8214 16409 1 +a 8215 16408 1 +a 8216 16407 1 +a 8217 16406 1 +a 8218 16405 1 +a 8219 16404 1 +a 8220 16403 1 +a 8221 16402 1 +a 8222 16401 1 +a 8223 16400 1 +a 8224 16399 1 +a 8225 16398 1 +a 8226 16397 1 +a 8227 16396 1 +a 8228 16395 1 +a 8229 16394 1 +a 8230 16393 1 +a 8231 16392 1 +a 8232 16391 1 +a 8233 16390 1 +a 8234 16389 1 +a 8235 16388 1 +a 8236 16387 1 +a 8237 16386 1 +a 8238 16385 1 +a 8239 16384 1 +a 8240 16383 1 +a 8241 16382 1 +a 8242 16381 1 +a 8243 16380 1 +a 8244 16379 1 +a 8245 16378 1 +a 8246 16377 1 +a 8247 16376 1 +a 8248 16375 1 +a 8249 16374 1 +a 8250 16373 1 +a 8251 16372 1 +a 8252 16371 1 +a 8253 16370 1 +a 8254 16369 1 +a 8255 16368 1 +a 8256 16367 1 +a 8257 16366 1 +a 8258 16365 1 +a 8259 16364 1 +a 8260 16363 1 +a 8261 16362 1 +a 8262 16361 1 +a 8263 16360 1 +a 8264 16359 1 +a 8265 16358 1 +a 8266 16357 1 +a 8267 16356 1 +a 8268 16355 1 +a 8269 16354 1 +a 8270 16353 1 +a 8271 16352 1 +a 8272 16351 1 +a 8273 16350 1 +a 8274 16349 1 +a 8275 16348 1 +a 8276 16347 1 +a 8277 16346 1 +a 8278 16345 1 +a 8279 16344 1 +a 8280 16343 1 +a 8281 16342 1 +a 8282 16341 1 +a 8283 16340 1 +a 8284 16339 1 +a 8285 16338 1 +a 8286 16337 1 +a 8287 16336 1 +a 8288 16335 1 +a 8289 16334 1 +a 8290 16333 1 +a 8291 16332 1 +a 8292 16331 1 +a 8293 16330 1 +a 8294 16329 1 +a 8295 16328 1 +a 8296 16327 1 +a 8297 16326 1 +a 8298 16325 1 +a 8299 16324 1 +a 8300 16323 1 +a 8301 16322 1 +a 8302 16321 1 +a 8303 16320 1 +a 8304 16319 1 +a 8305 16318 1 +a 8306 16317 1 +a 8307 16316 1 +a 8308 16315 1 +a 8309 16314 1 +a 8310 16313 1 +a 8311 16312 1 +a 8312 16311 1 +a 8313 16310 1 +a 8314 16309 1 +a 8315 16308 1 +a 8316 16307 1 +a 8317 16306 1 +a 8318 16305 1 +a 8319 16304 1 +a 8320 16303 1 +a 8321 16302 1 +a 8322 16301 1 +a 8323 16300 1 +a 8324 16299 1 +a 8325 16298 1 +a 8326 16297 1 +a 8327 16296 1 +a 8328 16295 1 +a 8329 16294 1 +a 8330 16293 1 +a 8331 16292 1 +a 8332 16291 1 +a 8333 16290 1 +a 8334 16289 1 +a 8335 16288 1 +a 8336 16287 1 +a 8337 16286 1 +a 8338 16285 1 +a 8339 16284 1 +a 8340 16283 1 +a 8341 16282 1 +a 8342 16281 1 +a 8343 16280 1 +a 8344 16279 1 +a 8345 16278 1 +a 8346 16277 1 +a 8347 16276 1 +a 8348 16275 1 +a 8349 16274 1 +a 8350 16273 1 +a 8351 16272 1 +a 8352 16271 1 +a 8353 16270 1 +a 8354 16269 1 +a 8355 16268 1 +a 8356 16267 1 +a 8357 16266 1 +a 8358 16265 1 +a 8359 16264 1 +a 8360 16263 1 +a 8361 16262 1 +a 8362 16261 1 +a 8363 16260 1 +a 8364 16259 1 +a 8365 16258 1 +a 8366 16257 1 +a 8367 16256 1 +a 8368 16255 1 +a 8369 16254 1 +a 8370 16253 1 +a 8371 16252 1 +a 8372 16251 1 +a 8373 16250 1 +a 8374 16249 1 +a 8375 16248 1 +a 8376 16247 1 +a 8377 16246 1 +a 8378 16245 1 +a 8379 16244 1 +a 8380 16243 1 +a 8381 16242 1 +a 8382 16241 1 +a 8383 16240 1 +a 8384 16239 1 +a 8385 16238 1 +a 8386 16237 1 +a 8387 16236 1 +a 8388 16235 1 +a 8389 16234 1 +a 8390 16233 1 +a 8391 16232 1 +a 8392 16231 1 +a 8393 16230 1 +a 8394 16229 1 +a 8395 16228 1 +a 8396 16227 1 +a 8397 16226 1 +a 8398 16225 1 +a 8399 16224 1 +a 8400 16223 1 +a 8401 16222 1 +a 8402 16221 1 +a 8403 16220 1 +a 8404 16219 1 +a 8405 16218 1 +a 8406 16217 1 +a 8407 16216 1 +a 8408 16215 1 +a 8409 16214 1 +a 8410 16213 1 +a 8411 16212 1 +a 8412 16211 1 +a 8413 16210 1 +a 8414 16209 1 +a 8415 16208 1 +a 8416 16207 1 +a 8417 16206 1 +a 8418 16205 1 +a 8419 16204 1 +a 8420 16203 1 +a 8421 16202 1 +a 8422 16201 1 +a 8423 16200 1 +a 8424 16199 1 +a 8425 16198 1 +a 8426 16197 1 +a 8427 16196 1 +a 8428 16195 1 +a 8429 16194 1 +a 8430 16193 1 +a 8431 16192 1 +a 8432 16191 1 +a 8433 16190 1 +a 8434 16189 1 +a 8435 16188 1 +a 8436 16187 1 +a 8437 16186 1 +a 8438 16185 1 +a 8439 16184 1 +a 8440 16183 1 +a 8441 16182 1 +a 8442 16181 1 +a 8443 16180 1 +a 8444 16179 1 +a 8445 16178 1 +a 8446 16177 1 +a 8447 16176 1 +a 8448 16175 1 +a 8449 16174 1 +a 8450 16173 1 +a 8451 16172 1 +a 8452 16171 1 +a 8453 16170 1 +a 8454 16169 1 +a 8455 16168 1 +a 8456 16167 1 +a 8457 16166 1 +a 8458 16165 1 +a 8459 16164 1 +a 8460 16163 1 +a 8461 16162 1 +a 8462 16161 1 +a 8463 16160 1 +a 8464 16159 1 +a 8465 16158 1 +a 8466 16157 1 +a 8467 16156 1 +a 8468 16155 1 +a 8469 16154 1 +a 8470 16153 1 +a 8471 16152 1 +a 8472 16151 1 +a 8473 16150 1 +a 8474 16149 1 +a 8475 16148 1 +a 8476 16147 1 +a 8477 16146 1 +a 8478 16145 1 +a 8479 16144 1 +a 8480 16143 1 +a 8481 16142 1 +a 8482 16141 1 +a 8483 16140 1 +a 8484 16139 1 +a 8485 16138 1 +a 8486 16137 1 +a 8487 16136 1 +a 8488 16135 1 +a 8489 16134 1 +a 8490 16133 1 +a 8491 16132 1 +a 8492 16131 1 +a 8493 16130 1 +a 8494 16129 1 +a 8495 16128 1 +a 8496 16127 1 +a 8497 16126 1 +a 8498 16125 1 +a 8499 16124 1 +a 8500 16123 1 +a 8501 16122 1 +a 8502 16121 1 +a 8503 16120 1 +a 8504 16119 1 +a 8505 16118 1 +a 8506 16117 1 +a 8507 16116 1 +a 8508 16115 1 +a 8509 16114 1 +a 8510 16113 1 +a 8511 16112 1 +a 8512 16111 1 +a 8513 16110 1 +a 8514 16109 1 +a 8515 16108 1 +a 8516 16107 1 +a 8517 16106 1 +a 8518 16105 1 +a 8519 16104 1 +a 8520 16103 1 +a 8521 16102 1 +a 8522 16101 1 +a 8523 16100 1 +a 8524 16099 1 +a 8525 16098 1 +a 8526 16097 1 +a 8527 16096 1 +a 8528 16095 1 +a 8529 16094 1 +a 8530 16093 1 +a 8531 16092 1 +a 8532 16091 1 +a 8533 16090 1 +a 8534 16089 1 +a 8535 16088 1 +a 8536 16087 1 +a 8537 16086 1 +a 8538 16085 1 +a 8539 16084 1 +a 8540 16083 1 +a 8541 16082 1 +a 8542 16081 1 +a 8543 16080 1 +a 8544 16079 1 +a 8545 16078 1 +a 8546 16077 1 +a 8547 16076 1 +a 8548 16075 1 +a 8549 16074 1 +a 8550 16073 1 +a 8551 16072 1 +a 8552 16071 1 +a 8553 16070 1 +a 8554 16069 1 +a 8555 16068 1 +a 8556 16067 1 +a 8557 16066 1 +a 8558 16065 1 +a 8559 16064 1 +a 8560 16063 1 +a 8561 16062 1 +a 8562 16061 1 +a 8563 16060 1 +a 8564 16059 1 +a 8565 16058 1 +a 8566 16057 1 +a 8567 16056 1 +a 8568 16055 1 +a 8569 16054 1 +a 8570 16053 1 +a 8571 16052 1 +a 8572 16051 1 +a 8573 16050 1 +a 8574 16049 1 +a 8575 16048 1 +a 8576 16047 1 +a 8577 16046 1 +a 8578 16045 1 +a 8579 16044 1 +a 8580 16043 1 +a 8581 16042 1 +a 8582 16041 1 +a 8583 16040 1 +a 8584 16039 1 +a 8585 16038 1 +a 8586 16037 1 +a 8587 16036 1 +a 8588 16035 1 +a 8589 16034 1 +a 8590 16033 1 +a 8591 16032 1 +a 8592 16031 1 +a 8593 16030 1 +a 8594 16029 1 +a 8595 16028 1 +a 8596 16027 1 +a 8597 16026 1 +a 8598 16025 1 +a 8599 16024 1 +a 8600 16023 1 +a 8601 16022 1 +a 8602 16021 1 +a 8603 16020 1 +a 8604 16019 1 +a 8605 16018 1 +a 8606 16017 1 +a 8607 16016 1 +a 8608 16015 1 +a 8609 16014 1 +a 8610 16013 1 +a 8611 16012 1 +a 8612 16011 1 +a 8613 16010 1 +a 8614 16009 1 +a 8615 16008 1 +a 8616 16007 1 +a 8617 16006 1 +a 8618 16005 1 +a 8619 16004 1 +a 8620 16003 1 +a 8621 16002 1 +a 8622 16001 1 +a 8623 16000 1 +a 8624 15999 1 +a 8625 15998 1 +a 8626 15997 1 +a 8627 15996 1 +a 8628 15995 1 +a 8629 15994 1 +a 8630 15993 1 +a 8631 15992 1 +a 8632 15991 1 +a 8633 15990 1 +a 8634 15989 1 +a 8635 15988 1 +a 8636 15987 1 +a 8637 15986 1 +a 8638 15985 1 +a 8639 15984 1 +a 8640 15983 1 +a 8641 15982 1 +a 8642 15981 1 +a 8643 15980 1 +a 8644 15979 1 +a 8645 15978 1 +a 8646 15977 1 +a 8647 15976 1 +a 8648 15975 1 +a 8649 15974 1 +a 8650 15973 1 +a 8651 15972 1 +a 8652 15971 1 +a 8653 15970 1 +a 8654 15969 1 +a 8655 15968 1 +a 8656 15967 1 +a 8657 15966 1 +a 8658 15965 1 +a 8659 15964 1 +a 8660 15963 1 +a 8661 15962 1 +a 8662 15961 1 +a 8663 15960 1 +a 8664 15959 1 +a 8665 15958 1 +a 8666 15957 1 +a 8667 15956 1 +a 8668 15955 1 +a 8669 15954 1 +a 8670 15953 1 +a 8671 15952 1 +a 8672 15951 1 +a 8673 15950 1 +a 8674 15949 1 +a 8675 15948 1 +a 8676 15947 1 +a 8677 15946 1 +a 8678 15945 1 +a 8679 15944 1 +a 8680 15943 1 +a 8681 15942 1 +a 8682 15941 1 +a 8683 15940 1 +a 8684 15939 1 +a 8685 15938 1 +a 8686 15937 1 +a 8687 15936 1 +a 8688 15935 1 +a 8689 15934 1 +a 8690 15933 1 +a 8691 15932 1 +a 8692 15931 1 +a 8693 15930 1 +a 8694 15929 1 +a 8695 15928 1 +a 8696 15927 1 +a 8697 15926 1 +a 8698 15925 1 +a 8699 15924 1 +a 8700 15923 1 +a 8701 15922 1 +a 8702 15921 1 +a 8703 15920 1 +a 8704 15919 1 +a 8705 15918 1 +a 8706 15917 1 +a 8707 15916 1 +a 8708 15915 1 +a 8709 15914 1 +a 8710 15913 1 +a 8711 15912 1 +a 8712 15911 1 +a 8713 15910 1 +a 8714 15909 1 +a 8715 15908 1 +a 8716 15907 1 +a 8717 15906 1 +a 8718 15905 1 +a 8719 15904 1 +a 8720 15903 1 +a 8721 15902 1 +a 8722 15901 1 +a 8723 15900 1 +a 8724 15899 1 +a 8725 15898 1 +a 8726 15897 1 +a 8727 15896 1 +a 8728 15895 1 +a 8729 15894 1 +a 8730 15893 1 +a 8731 15892 1 +a 8732 15891 1 +a 8733 15890 1 +a 8734 15889 1 +a 8735 15888 1 +a 8736 15887 1 +a 8737 15886 1 +a 8738 15885 1 +a 8739 15884 1 +a 8740 15883 1 +a 8741 15882 1 +a 8742 15881 1 +a 8743 15880 1 +a 8744 15879 1 +a 8745 15878 1 +a 8746 15877 1 +a 8747 15876 1 +a 8748 15875 1 +a 8749 15874 1 +a 8750 15873 1 +a 8751 15872 1 +a 8752 15871 1 +a 8753 15870 1 +a 8754 15869 1 +a 8755 15868 1 +a 8756 15867 1 +a 8757 15866 1 +a 8758 15865 1 +a 8759 15864 1 +a 8760 15863 1 +a 8761 15862 1 +a 8762 15861 1 +a 8763 15860 1 +a 8764 15859 1 +a 8765 15858 1 +a 8766 15857 1 +a 8767 15856 1 +a 8768 15855 1 +a 8769 15854 1 +a 8770 15853 1 +a 8771 15852 1 +a 8772 15851 1 +a 8773 15850 1 +a 8774 15849 1 +a 8775 15848 1 +a 8776 15847 1 +a 8777 15846 1 +a 8778 15845 1 +a 8779 15844 1 +a 8780 15843 1 +a 8781 15842 1 +a 8782 15841 1 +a 8783 15840 1 +a 8784 15839 1 +a 8785 15838 1 +a 8786 15837 1 +a 8787 15836 1 +a 8788 15835 1 +a 8789 15834 1 +a 8790 15833 1 +a 8791 15832 1 +a 8792 15831 1 +a 8793 15830 1 +a 8794 15829 1 +a 8795 15828 1 +a 8796 15827 1 +a 8797 15826 1 +a 8798 15825 1 +a 8799 15824 1 +a 8800 15823 1 +a 8801 15822 1 +a 8802 15821 1 +a 8803 15820 1 +a 8804 15819 1 +a 8805 15818 1 +a 8806 15817 1 +a 8807 15816 1 +a 8808 15815 1 +a 8809 15814 1 +a 8810 15813 1 +a 8811 15812 1 +a 8812 15811 1 +a 8813 15810 1 +a 8814 15809 1 +a 8815 15808 1 +a 8816 15807 1 +a 8817 15806 1 +a 8818 15805 1 +a 8819 15804 1 +a 8820 15803 1 +a 8821 15802 1 +a 8822 15801 1 +a 8823 15800 1 +a 8824 15799 1 +a 8825 15798 1 +a 8826 15797 1 +a 8827 15796 1 +a 8828 15795 1 +a 8829 15794 1 +a 8830 15793 1 +a 8831 15792 1 +a 8832 15791 1 +a 8833 15790 1 +a 8834 15789 1 +a 8835 15788 1 +a 8836 15787 1 +a 8837 15786 1 +a 8838 15785 1 +a 8839 15784 1 +a 8840 15783 1 +a 8841 15782 1 +a 8842 15781 1 +a 8843 15780 1 +a 8844 15779 1 +a 8845 15778 1 +a 8846 15777 1 +a 8847 15776 1 +a 8848 15775 1 +a 8849 15774 1 +a 8850 15773 1 +a 8851 15772 1 +a 8852 15771 1 +a 8853 15770 1 +a 8854 15769 1 +a 8855 15768 1 +a 8856 15767 1 +a 8857 15766 1 +a 8858 15765 1 +a 8859 15764 1 +a 8860 15763 1 +a 8861 15762 1 +a 8862 15761 1 +a 8863 15760 1 +a 8864 15759 1 +a 8865 15758 1 +a 8866 15757 1 +a 8867 15756 1 +a 8868 15755 1 +a 8869 15754 1 +a 8870 15753 1 +a 8871 15752 1 +a 8872 15751 1 +a 8873 15750 1 +a 8874 15749 1 +a 8875 15748 1 +a 8876 15747 1 +a 8877 15746 1 +a 8878 15745 1 +a 8879 15744 1 +a 8880 15743 1 +a 8881 15742 1 +a 8882 15741 1 +a 8883 15740 1 +a 8884 15739 1 +a 8885 15738 1 +a 8886 15737 1 +a 8887 15736 1 +a 8888 15735 1 +a 8889 15734 1 +a 8890 15733 1 +a 8891 15732 1 +a 8892 15731 1 +a 8893 15730 1 +a 8894 15729 1 +a 8895 15728 1 +a 8896 15727 1 +a 8897 15726 1 +a 8898 15725 1 +a 8899 15724 1 +a 8900 15723 1 +a 8901 15722 1 +a 8902 15721 1 +a 8903 15720 1 +a 8904 15719 1 +a 8905 15718 1 +a 8906 15717 1 +a 8907 15716 1 +a 8908 15715 1 +a 8909 15714 1 +a 8910 15713 1 +a 8911 15712 1 +a 8912 15711 1 +a 8913 15710 1 +a 8914 15709 1 +a 8915 15708 1 +a 8916 15707 1 +a 8917 15706 1 +a 8918 15705 1 +a 8919 15704 1 +a 8920 15703 1 +a 8921 15702 1 +a 8922 15701 1 +a 8923 15700 1 +a 8924 15699 1 +a 8925 15698 1 +a 8926 15697 1 +a 8927 15696 1 +a 8928 15695 1 +a 8929 15694 1 +a 8930 15693 1 +a 8931 15692 1 +a 8932 15691 1 +a 8933 15690 1 +a 8934 15689 1 +a 8935 15688 1 +a 8936 15687 1 +a 8937 15686 1 +a 8938 15685 1 +a 8939 15684 1 +a 8940 15683 1 +a 8941 15682 1 +a 8942 15681 1 +a 8943 15680 1 +a 8944 15679 1 +a 8945 15678 1 +a 8946 15677 1 +a 8947 15676 1 +a 8948 15675 1 +a 8949 15674 1 +a 8950 15673 1 +a 8951 15672 1 +a 8952 15671 1 +a 8953 15670 1 +a 8954 15669 1 +a 8955 15668 1 +a 8956 15667 1 +a 8957 15666 1 +a 8958 15665 1 +a 8959 15664 1 +a 8960 15663 1 +a 8961 15662 1 +a 8962 15661 1 +a 8963 15660 1 +a 8964 15659 1 +a 8965 15658 1 +a 8966 15657 1 +a 8967 15656 1 +a 8968 15655 1 +a 8969 15654 1 +a 8970 15653 1 +a 8971 15652 1 +a 8972 15651 1 +a 8973 15650 1 +a 8974 15649 1 +a 8975 15648 1 +a 8976 15647 1 +a 8977 15646 1 +a 8978 15645 1 +a 8979 15644 1 +a 8980 15643 1 +a 8981 15642 1 +a 8982 15641 1 +a 8983 15640 1 +a 8984 15639 1 +a 8985 15638 1 +a 8986 15637 1 +a 8987 15636 1 +a 8988 15635 1 +a 8989 15634 1 +a 8990 15633 1 +a 8991 15632 1 +a 8992 15631 1 +a 8993 15630 1 +a 8994 15629 1 +a 8995 15628 1 +a 8996 15627 1 +a 8997 15626 1 +a 8998 15625 1 +a 8999 15624 1 +a 9000 15623 1 +a 9001 15622 1 +a 9002 15621 1 +a 9003 15620 1 +a 9004 15619 1 +a 9005 15618 1 +a 9006 15617 1 +a 9007 15616 1 +a 9008 15615 1 +a 9009 15614 1 +a 9010 15613 1 +a 9011 15612 1 +a 9012 15611 1 +a 9013 15610 1 +a 9014 15609 1 +a 9015 15608 1 +a 9016 15607 1 +a 9017 15606 1 +a 9018 15605 1 +a 9019 15604 1 +a 9020 15603 1 +a 9021 15602 1 +a 9022 15601 1 +a 9023 15600 1 +a 9024 15599 1 +a 9025 15598 1 +a 9026 15597 1 +a 9027 15596 1 +a 9028 15595 1 +a 9029 15594 1 +a 9030 15593 1 +a 9031 15592 1 +a 9032 15591 1 +a 9033 15590 1 +a 9034 15589 1 +a 9035 15588 1 +a 9036 15587 1 +a 9037 15586 1 +a 9038 15585 1 +a 9039 15584 1 +a 9040 15583 1 +a 9041 15582 1 +a 9042 15581 1 +a 9043 15580 1 +a 9044 15579 1 +a 9045 15578 1 +a 9046 15577 1 +a 9047 15576 1 +a 9048 15575 1 +a 9049 15574 1 +a 9050 15573 1 +a 9051 15572 1 +a 9052 15571 1 +a 9053 15570 1 +a 9054 15569 1 +a 9055 15568 1 +a 9056 15567 1 +a 9057 15566 1 +a 9058 15565 1 +a 9059 15564 1 +a 9060 15563 1 +a 9061 15562 1 +a 9062 15561 1 +a 9063 15560 1 +a 9064 15559 1 +a 9065 15558 1 +a 9066 15557 1 +a 9067 15556 1 +a 9068 15555 1 +a 9069 15554 1 +a 9070 15553 1 +a 9071 15552 1 +a 9072 15551 1 +a 9073 15550 1 +a 9074 15549 1 +a 9075 15548 1 +a 9076 15547 1 +a 9077 15546 1 +a 9078 15545 1 +a 9079 15544 1 +a 9080 15543 1 +a 9081 15542 1 +a 9082 15541 1 +a 9083 15540 1 +a 9084 15539 1 +a 9085 15538 1 +a 9086 15537 1 +a 9087 15536 1 +a 9088 15535 1 +a 9089 15534 1 +a 9090 15533 1 +a 9091 15532 1 +a 9092 15531 1 +a 9093 15530 1 +a 9094 15529 1 +a 9095 15528 1 +a 9096 15527 1 +a 9097 15526 1 +a 9098 15525 1 +a 9099 15524 1 +a 9100 15523 1 +a 9101 15522 1 +a 9102 15521 1 +a 9103 15520 1 +a 9104 15519 1 +a 9105 15518 1 +a 9106 15517 1 +a 9107 15516 1 +a 9108 15515 1 +a 9109 15514 1 +a 9110 15513 1 +a 9111 15512 1 +a 9112 15511 1 +a 9113 15510 1 +a 9114 15509 1 +a 9115 15508 1 +a 9116 15507 1 +a 9117 15506 1 +a 9118 15505 1 +a 9119 15504 1 +a 9120 15503 1 +a 9121 15502 1 +a 9122 15501 1 +a 9123 15500 1 +a 9124 15499 1 +a 9125 15498 1 +a 9126 15497 1 +a 9127 15496 1 +a 9128 15495 1 +a 9129 15494 1 +a 9130 15493 1 +a 9131 15492 1 +a 9132 15491 1 +a 9133 15490 1 +a 9134 15489 1 +a 9135 15488 1 +a 9136 15487 1 +a 9137 15486 1 +a 9138 15485 1 +a 9139 15484 1 +a 9140 15483 1 +a 9141 15482 1 +a 9142 15481 1 +a 9143 15480 1 +a 9144 15479 1 +a 9145 15478 1 +a 9146 15477 1 +a 9147 15476 1 +a 9148 15475 1 +a 9149 15474 1 +a 9150 15473 1 +a 9151 15472 1 +a 9152 15471 1 +a 9153 15470 1 +a 9154 15469 1 +a 9155 15468 1 +a 9156 15467 1 +a 9157 15466 1 +a 9158 15465 1 +a 9159 15464 1 +a 9160 15463 1 +a 9161 15462 1 +a 9162 15461 1 +a 9163 15460 1 +a 9164 15459 1 +a 9165 15458 1 +a 9166 15457 1 +a 9167 15456 1 +a 9168 15455 1 +a 9169 15454 1 +a 9170 15453 1 +a 9171 15452 1 +a 9172 15451 1 +a 9173 15450 1 +a 9174 15449 1 +a 9175 15448 1 +a 9176 15447 1 +a 9177 15446 1 +a 9178 15445 1 +a 9179 15444 1 +a 9180 15443 1 +a 9181 15442 1 +a 9182 15441 1 +a 9183 15440 1 +a 9184 15439 1 +a 9185 15438 1 +a 9186 15437 1 +a 9187 15436 1 +a 9188 15435 1 +a 9189 15434 1 +a 9190 15433 1 +a 9191 15432 1 +a 9192 15431 1 +a 9193 15430 1 +a 9194 15429 1 +a 9195 15428 1 +a 9196 15427 1 +a 9197 15426 1 +a 9198 15425 1 +a 9199 15424 1 +a 9200 15423 1 +a 9201 15422 1 +a 9202 15421 1 +a 9203 15420 1 +a 9204 15419 1 +a 9205 15418 1 +a 9206 15417 1 +a 9207 15416 1 +a 9208 15415 1 +a 9209 15414 1 +a 9210 15413 1 +a 9211 15412 1 +a 9212 15411 1 +a 9213 15410 1 +a 9214 15409 1 +a 9215 15408 1 +a 9216 15407 1 +a 9217 15406 1 +a 9218 15405 1 +a 9219 15404 1 +a 9220 15403 1 +a 9221 15402 1 +a 9222 15401 1 +a 9223 15400 1 +a 9224 15399 1 +a 9225 15398 1 +a 9226 15397 1 +a 9227 15396 1 +a 9228 15395 1 +a 9229 15394 1 +a 9230 15393 1 +a 9231 15392 1 +a 9232 15391 1 +a 9233 15390 1 +a 9234 15389 1 +a 9235 15388 1 +a 9236 15387 1 +a 9237 15386 1 +a 9238 15385 1 +a 9239 15384 1 +a 9240 15383 1 +a 9241 15382 1 +a 9242 15381 1 +a 9243 15380 1 +a 9244 15379 1 +a 9245 15378 1 +a 9246 15377 1 +a 9247 15376 1 +a 9248 15375 1 +a 9249 15374 1 +a 9250 15373 1 +a 9251 15372 1 +a 9252 15371 1 +a 9253 15370 1 +a 9254 15369 1 +a 9255 15368 1 +a 9256 15367 1 +a 9257 15366 1 +a 9258 15365 1 +a 9259 15364 1 +a 9260 15363 1 +a 9261 15362 1 +a 9262 15361 1 +a 9263 15360 1 +a 9264 15359 1 +a 9265 15358 1 +a 9266 15357 1 +a 9267 15356 1 +a 9268 15355 1 +a 9269 15354 1 +a 9270 15353 1 +a 9271 15352 1 +a 9272 15351 1 +a 9273 15350 1 +a 9274 15349 1 +a 9275 15348 1 +a 9276 15347 1 +a 9277 15346 1 +a 9278 15345 1 +a 9279 15344 1 +a 9280 15343 1 +a 9281 15342 1 +a 9282 15341 1 +a 9283 15340 1 +a 9284 15339 1 +a 9285 15338 1 +a 9286 15337 1 +a 9287 15336 1 +a 9288 15335 1 +a 9289 15334 1 +a 9290 15333 1 +a 9291 15332 1 +a 9292 15331 1 +a 9293 15330 1 +a 9294 15329 1 +a 9295 15328 1 +a 9296 15327 1 +a 9297 15326 1 +a 9298 15325 1 +a 9299 15324 1 +a 9300 15323 1 +a 9301 15322 1 +a 9302 15321 1 +a 9303 15320 1 +a 9304 15319 1 +a 9305 15318 1 +a 9306 15317 1 +a 9307 15316 1 +a 9308 15315 1 +a 9309 15314 1 +a 9310 15313 1 +a 9311 15312 1 +a 9312 15311 1 +a 9313 15310 1 +a 9314 15309 1 +a 9315 15308 1 +a 9316 15307 1 +a 9317 15306 1 +a 9318 15305 1 +a 9319 15304 1 +a 9320 15303 1 +a 9321 15302 1 +a 9322 15301 1 +a 9323 15300 1 +a 9324 15299 1 +a 9325 15298 1 +a 9326 15297 1 +a 9327 15296 1 +a 9328 15295 1 +a 9329 15294 1 +a 9330 15293 1 +a 9331 15292 1 +a 9332 15291 1 +a 9333 15290 1 +a 9334 15289 1 +a 9335 15288 1 +a 9336 15287 1 +a 9337 15286 1 +a 9338 15285 1 +a 9339 15284 1 +a 9340 15283 1 +a 9341 15282 1 +a 9342 15281 1 +a 9343 15280 1 +a 9344 15279 1 +a 9345 15278 1 +a 9346 15277 1 +a 9347 15276 1 +a 9348 15275 1 +a 9349 15274 1 +a 9350 15273 1 +a 9351 15272 1 +a 9352 15271 1 +a 9353 15270 1 +a 9354 15269 1 +a 9355 15268 1 +a 9356 15267 1 +a 9357 15266 1 +a 9358 15265 1 +a 9359 15264 1 +a 9360 15263 1 +a 9361 15262 1 +a 9362 15261 1 +a 9363 15260 1 +a 9364 15259 1 +a 9365 15258 1 +a 9366 15257 1 +a 9367 15256 1 +a 9368 15255 1 +a 9369 15254 1 +a 9370 15253 1 +a 9371 15252 1 +a 9372 15251 1 +a 9373 15250 1 +a 9374 15249 1 +a 9375 15248 1 +a 9376 15247 1 +a 9377 15246 1 +a 9378 15245 1 +a 9379 15244 1 +a 9380 15243 1 +a 9381 15242 1 +a 9382 15241 1 +a 9383 15240 1 +a 9384 15239 1 +a 9385 15238 1 +a 9386 15237 1 +a 9387 15236 1 +a 9388 15235 1 +a 9389 15234 1 +a 9390 15233 1 +a 9391 15232 1 +a 9392 15231 1 +a 9393 15230 1 +a 9394 15229 1 +a 9395 15228 1 +a 9396 15227 1 +a 9397 15226 1 +a 9398 15225 1 +a 9399 15224 1 +a 9400 15223 1 +a 9401 15222 1 +a 9402 15221 1 +a 9403 15220 1 +a 9404 15219 1 +a 9405 15218 1 +a 9406 15217 1 +a 9407 15216 1 +a 9408 15215 1 +a 9409 15214 1 +a 9410 15213 1 +a 9411 15212 1 +a 9412 15211 1 +a 9413 15210 1 +a 9414 15209 1 +a 9415 15208 1 +a 9416 15207 1 +a 9417 15206 1 +a 9418 15205 1 +a 9419 15204 1 +a 9420 15203 1 +a 9421 15202 1 +a 9422 15201 1 +a 9423 15200 1 +a 9424 15199 1 +a 9425 15198 1 +a 9426 15197 1 +a 9427 15196 1 +a 9428 15195 1 +a 9429 15194 1 +a 9430 15193 1 +a 9431 15192 1 +a 9432 15191 1 +a 9433 15190 1 +a 9434 15189 1 +a 9435 15188 1 +a 9436 15187 1 +a 9437 15186 1 +a 9438 15185 1 +a 9439 15184 1 +a 9440 15183 1 +a 9441 15182 1 +a 9442 15181 1 +a 9443 15180 1 +a 9444 15179 1 +a 9445 15178 1 +a 9446 15177 1 +a 9447 15176 1 +a 9448 15175 1 +a 9449 15174 1 +a 9450 15173 1 +a 9451 15172 1 +a 9452 15171 1 +a 9453 15170 1 +a 9454 15169 1 +a 9455 15168 1 +a 9456 15167 1 +a 9457 15166 1 +a 9458 15165 1 +a 9459 15164 1 +a 9460 15163 1 +a 9461 15162 1 +a 9462 15161 1 +a 9463 15160 1 +a 9464 15159 1 +a 9465 15158 1 +a 9466 15157 1 +a 9467 15156 1 +a 9468 15155 1 +a 9469 15154 1 +a 9470 15153 1 +a 9471 15152 1 +a 9472 15151 1 +a 9473 15150 1 +a 9474 15149 1 +a 9475 15148 1 +a 9476 15147 1 +a 9477 15146 1 +a 9478 15145 1 +a 9479 15144 1 +a 9480 15143 1 +a 9481 15142 1 +a 9482 15141 1 +a 9483 15140 1 +a 9484 15139 1 +a 9485 15138 1 +a 9486 15137 1 +a 9487 15136 1 +a 9488 15135 1 +a 9489 15134 1 +a 9490 15133 1 +a 9491 15132 1 +a 9492 15131 1 +a 9493 15130 1 +a 9494 15129 1 +a 9495 15128 1 +a 9496 15127 1 +a 9497 15126 1 +a 9498 15125 1 +a 9499 15124 1 +a 9500 15123 1 +a 9501 15122 1 +a 9502 15121 1 +a 9503 15120 1 +a 9504 15119 1 +a 9505 15118 1 +a 9506 15117 1 +a 9507 15116 1 +a 9508 15115 1 +a 9509 15114 1 +a 9510 15113 1 +a 9511 15112 1 +a 9512 15111 1 +a 9513 15110 1 +a 9514 15109 1 +a 9515 15108 1 +a 9516 15107 1 +a 9517 15106 1 +a 9518 15105 1 +a 9519 15104 1 +a 9520 15103 1 +a 9521 15102 1 +a 9522 15101 1 +a 9523 15100 1 +a 9524 15099 1 +a 9525 15098 1 +a 9526 15097 1 +a 9527 15096 1 +a 9528 15095 1 +a 9529 15094 1 +a 9530 15093 1 +a 9531 15092 1 +a 9532 15091 1 +a 9533 15090 1 +a 9534 15089 1 +a 9535 15088 1 +a 9536 15087 1 +a 9537 15086 1 +a 9538 15085 1 +a 9539 15084 1 +a 9540 15083 1 +a 9541 15082 1 +a 9542 15081 1 +a 9543 15080 1 +a 9544 15079 1 +a 9545 15078 1 +a 9546 15077 1 +a 9547 15076 1 +a 9548 15075 1 +a 9549 15074 1 +a 9550 15073 1 +a 9551 15072 1 +a 9552 15071 1 +a 9553 15070 1 +a 9554 15069 1 +a 9555 15068 1 +a 9556 15067 1 +a 9557 15066 1 +a 9558 15065 1 +a 9559 15064 1 +a 9560 15063 1 +a 9561 15062 1 +a 9562 15061 1 +a 9563 15060 1 +a 9564 15059 1 +a 9565 15058 1 +a 9566 15057 1 +a 9567 15056 1 +a 9568 15055 1 +a 9569 15054 1 +a 9570 15053 1 +a 9571 15052 1 +a 9572 15051 1 +a 9573 15050 1 +a 9574 15049 1 +a 9575 15048 1 +a 9576 15047 1 +a 9577 15046 1 +a 9578 15045 1 +a 9579 15044 1 +a 9580 15043 1 +a 9581 15042 1 +a 9582 15041 1 +a 9583 15040 1 +a 9584 15039 1 +a 9585 15038 1 +a 9586 15037 1 +a 9587 15036 1 +a 9588 15035 1 +a 9589 15034 1 +a 9590 15033 1 +a 9591 15032 1 +a 9592 15031 1 +a 9593 15030 1 +a 9594 15029 1 +a 9595 15028 1 +a 9596 15027 1 +a 9597 15026 1 +a 9598 15025 1 +a 9599 15024 1 +a 9600 15023 1 +a 9601 15022 1 +a 9602 15021 1 +a 9603 15020 1 +a 9604 15019 1 +a 9605 15018 1 +a 9606 15017 1 +a 9607 15016 1 +a 9608 15015 1 +a 9609 15014 1 +a 9610 15013 1 +a 9611 15012 1 +a 9612 15011 1 +a 9613 15010 1 +a 9614 15009 1 +a 9615 15008 1 +a 9616 15007 1 +a 9617 15006 1 +a 9618 15005 1 +a 9619 15004 1 +a 9620 15003 1 +a 9621 15002 1 +a 9622 15001 1 +a 9623 15000 1 +a 9624 14999 1 +a 9625 14998 1 +a 9626 14997 1 +a 9627 14996 1 +a 9628 14995 1 +a 9629 14994 1 +a 9630 14993 1 +a 9631 14992 1 +a 9632 14991 1 +a 9633 14990 1 +a 9634 14989 1 +a 9635 14988 1 +a 9636 14987 1 +a 9637 14986 1 +a 9638 14985 1 +a 9639 14984 1 +a 9640 14983 1 +a 9641 14982 1 +a 9642 14981 1 +a 9643 14980 1 +a 9644 14979 1 +a 9645 14978 1 +a 9646 14977 1 +a 9647 14976 1 +a 9648 14975 1 +a 9649 14974 1 +a 9650 14973 1 +a 9651 14972 1 +a 9652 14971 1 +a 9653 14970 1 +a 9654 14969 1 +a 9655 14968 1 +a 9656 14967 1 +a 9657 14966 1 +a 9658 14965 1 +a 9659 14964 1 +a 9660 14963 1 +a 9661 14962 1 +a 9662 14961 1 +a 9663 14960 1 +a 9664 14959 1 +a 9665 14958 1 +a 9666 14957 1 +a 9667 14956 1 +a 9668 14955 1 +a 9669 14954 1 +a 9670 14953 1 +a 9671 14952 1 +a 9672 14951 1 +a 9673 14950 1 +a 9674 14949 1 +a 9675 14948 1 +a 9676 14947 1 +a 9677 14946 1 +a 9678 14945 1 +a 9679 14944 1 +a 9680 14943 1 +a 9681 14942 1 +a 9682 14941 1 +a 9683 14940 1 +a 9684 14939 1 +a 9685 14938 1 +a 9686 14937 1 +a 9687 14936 1 +a 9688 14935 1 +a 9689 14934 1 +a 9690 14933 1 +a 9691 14932 1 +a 9692 14931 1 +a 9693 14930 1 +a 9694 14929 1 +a 9695 14928 1 +a 9696 14927 1 +a 9697 14926 1 +a 9698 14925 1 +a 9699 14924 1 +a 9700 14923 1 +a 9701 14922 1 +a 9702 14921 1 +a 9703 14920 1 +a 9704 14919 1 +a 9705 14918 1 +a 9706 14917 1 +a 9707 14916 1 +a 9708 14915 1 +a 9709 14914 1 +a 9710 14913 1 +a 9711 14912 1 +a 9712 14911 1 +a 9713 14910 1 +a 9714 14909 1 +a 9715 14908 1 +a 9716 14907 1 +a 9717 14906 1 +a 9718 14905 1 +a 9719 14904 1 +a 9720 14903 1 +a 9721 14902 1 +a 9722 14901 1 +a 9723 14900 1 +a 9724 14899 1 +a 9725 14898 1 +a 9726 14897 1 +a 9727 14896 1 +a 9728 14895 1 +a 9729 14894 1 +a 9730 14893 1 +a 9731 14892 1 +a 9732 14891 1 +a 9733 14890 1 +a 9734 14889 1 +a 9735 14888 1 +a 9736 14887 1 +a 9737 14886 1 +a 9738 14885 1 +a 9739 14884 1 +a 9740 14883 1 +a 9741 14882 1 +a 9742 14881 1 +a 9743 14880 1 +a 9744 14879 1 +a 9745 14878 1 +a 9746 14877 1 +a 9747 14876 1 +a 9748 14875 1 +a 9749 14874 1 +a 9750 14873 1 +a 9751 14872 1 +a 9752 14871 1 +a 9753 14870 1 +a 9754 14869 1 +a 9755 14868 1 +a 9756 14867 1 +a 9757 14866 1 +a 9758 14865 1 +a 9759 14864 1 +a 9760 14863 1 +a 9761 14862 1 +a 9762 14861 1 +a 9763 14860 1 +a 9764 14859 1 +a 9765 14858 1 +a 9766 14857 1 +a 9767 14856 1 +a 9768 14855 1 +a 9769 14854 1 +a 9770 14853 1 +a 9771 14852 1 +a 9772 14851 1 +a 9773 14850 1 +a 9774 14849 1 +a 9775 14848 1 +a 9776 14847 1 +a 9777 14846 1 +a 9778 14845 1 +a 9779 14844 1 +a 9780 14843 1 +a 9781 14842 1 +a 9782 14841 1 +a 9783 14840 1 +a 9784 14839 1 +a 9785 14838 1 +a 9786 14837 1 +a 9787 14836 1 +a 9788 14835 1 +a 9789 14834 1 +a 9790 14833 1 +a 9791 14832 1 +a 9792 14831 1 +a 9793 14830 1 +a 9794 14829 1 +a 9795 14828 1 +a 9796 14827 1 +a 9797 14826 1 +a 9798 14825 1 +a 9799 14824 1 +a 9800 14823 1 +a 9801 14822 1 +a 9802 14821 1 +a 9803 14820 1 +a 9804 14819 1 +a 9805 14818 1 +a 9806 14817 1 +a 9807 14816 1 +a 9808 14815 1 +a 9809 14814 1 +a 9810 14813 1 +a 9811 14812 1 +a 9812 14811 1 +a 9813 14810 1 +a 9814 14809 1 +a 9815 14808 1 +a 9816 14807 1 +a 9817 14806 1 +a 9818 14805 1 +a 9819 14804 1 +a 9820 14803 1 +a 9821 14802 1 +a 9822 14801 1 +a 9823 14800 1 +a 9824 14799 1 +a 9825 14798 1 +a 9826 14797 1 +a 9827 14796 1 +a 9828 14795 1 +a 9829 14794 1 +a 9830 14793 1 +a 9831 14792 1 +a 9832 14791 1 +a 9833 14790 1 +a 9834 14789 1 +a 9835 14788 1 +a 9836 14787 1 +a 9837 14786 1 +a 9838 14785 1 +a 9839 14784 1 +a 9840 14783 1 +a 9841 14782 1 +a 9842 14781 1 +a 9843 14780 1 +a 9844 14779 1 +a 9845 14778 1 +a 9846 14777 1 +a 9847 14776 1 +a 9848 14775 1 +a 9849 14774 1 +a 9850 14773 1 +a 9851 14772 1 +a 9852 14771 1 +a 9853 14770 1 +a 9854 14769 1 +a 9855 14768 1 +a 9856 14767 1 +a 9857 14766 1 +a 9858 14765 1 +a 9859 14764 1 +a 9860 14763 1 +a 9861 14762 1 +a 9862 14761 1 +a 9863 14760 1 +a 9864 14759 1 +a 9865 14758 1 +a 9866 14757 1 +a 9867 14756 1 +a 9868 14755 1 +a 9869 14754 1 +a 9870 14753 1 +a 9871 14752 1 +a 9872 14751 1 +a 9873 14750 1 +a 9874 14749 1 +a 9875 14748 1 +a 9876 14747 1 +a 9877 14746 1 +a 9878 14745 1 +a 9879 14744 1 +a 9880 14743 1 +a 9881 14742 1 +a 9882 14741 1 +a 9883 14740 1 +a 9884 14739 1 +a 9885 14738 1 +a 9886 14737 1 +a 9887 14736 1 +a 9888 14735 1 +a 9889 14734 1 +a 9890 14733 1 +a 9891 14732 1 +a 9892 14731 1 +a 9893 14730 1 +a 9894 14729 1 +a 9895 14728 1 +a 9896 14727 1 +a 9897 14726 1 +a 9898 14725 1 +a 9899 14724 1 +a 9900 14723 1 +a 9901 14722 1 +a 9902 14721 1 +a 9903 14720 1 +a 9904 14719 1 +a 9905 14718 1 +a 9906 14717 1 +a 9907 14716 1 +a 9908 14715 1 +a 9909 14714 1 +a 9910 14713 1 +a 9911 14712 1 +a 9912 14711 1 +a 9913 14710 1 +a 9914 14709 1 +a 9915 14708 1 +a 9916 14707 1 +a 9917 14706 1 +a 9918 14705 1 +a 9919 14704 1 +a 9920 14703 1 +a 9921 14702 1 +a 9922 14701 1 +a 9923 14700 1 +a 9924 14699 1 +a 9925 14698 1 +a 9926 14697 1 +a 9927 14696 1 +a 9928 14695 1 +a 9929 14694 1 +a 9930 14693 1 +a 9931 14692 1 +a 9932 14691 1 +a 9933 14690 1 +a 9934 14689 1 +a 9935 14688 1 +a 9936 14687 1 +a 9937 14686 1 +a 9938 14685 1 +a 9939 14684 1 +a 9940 14683 1 +a 9941 14682 1 +a 9942 14681 1 +a 9943 14680 1 +a 9944 14679 1 +a 9945 14678 1 +a 9946 14677 1 +a 9947 14676 1 +a 9948 14675 1 +a 9949 14674 1 +a 9950 14673 1 +a 9951 14672 1 +a 9952 14671 1 +a 9953 14670 1 +a 9954 14669 1 +a 9955 14668 1 +a 9956 14667 1 +a 9957 14666 1 +a 9958 14665 1 +a 9959 14664 1 +a 9960 14663 1 +a 9961 14662 1 +a 9962 14661 1 +a 9963 14660 1 +a 9964 14659 1 +a 9965 14658 1 +a 9966 14657 1 +a 9967 14656 1 +a 9968 14655 1 +a 9969 14654 1 +a 9970 14653 1 +a 9971 14652 1 +a 9972 14651 1 +a 9973 14650 1 +a 9974 14649 1 +a 9975 14648 1 +a 9976 14647 1 +a 9977 14646 1 +a 9978 14645 1 +a 9979 14644 1 +a 9980 14643 1 +a 9981 14642 1 +a 9982 14641 1 +a 9983 14640 1 +a 9984 14639 1 +a 9985 14638 1 +a 9986 14637 1 +a 9987 14636 1 +a 9988 14635 1 +a 9989 14634 1 +a 9990 14633 1 +a 9991 14632 1 +a 9992 14631 1 +a 9993 14630 1 +a 9994 14629 1 +a 9995 14628 1 +a 9996 14627 1 +a 9997 14626 1 +a 9998 14625 1 +a 9999 14624 1 +a 10000 14623 1 +a 10001 14622 1 +a 10002 14621 1 +a 10003 14620 1 +a 10004 14619 1 +a 10005 14618 1 +a 10006 14617 1 +a 10007 14616 1 +a 10008 14615 1 +a 10009 14614 1 +a 10010 14613 1 +a 10011 14612 1 +a 10012 14611 1 +a 10013 14610 1 +a 10014 14609 1 +a 10015 14608 1 +a 10016 14607 1 +a 10017 14606 1 +a 10018 14605 1 +a 10019 14604 1 +a 10020 14603 1 +a 10021 14602 1 +a 10022 14601 1 +a 10023 14600 1 +a 10024 14599 1 +a 10025 14598 1 +a 10026 14597 1 +a 10027 14596 1 +a 10028 14595 1 +a 10029 14594 1 +a 10030 14593 1 +a 10031 14592 1 +a 10032 14591 1 +a 10033 14590 1 +a 10034 14589 1 +a 10035 14588 1 +a 10036 14587 1 +a 10037 14586 1 +a 10038 14585 1 +a 10039 14584 1 +a 10040 14583 1 +a 10041 14582 1 +a 10042 14581 1 +a 10043 14580 1 +a 10044 14579 1 +a 10045 14578 1 +a 10046 14577 1 +a 10047 14576 1 +a 10048 14575 1 +a 10049 14574 1 +a 10050 14573 1 +a 10051 14572 1 +a 10052 14571 1 +a 10053 14570 1 +a 10054 14569 1 +a 10055 14568 1 +a 10056 14567 1 +a 10057 14566 1 +a 10058 14565 1 +a 10059 14564 1 +a 10060 14563 1 +a 10061 14562 1 +a 10062 14561 1 +a 10063 14560 1 +a 10064 14559 1 +a 10065 14558 1 +a 10066 14557 1 +a 10067 14556 1 +a 10068 14555 1 +a 10069 14554 1 +a 10070 14553 1 +a 10071 14552 1 +a 10072 14551 1 +a 10073 14550 1 +a 10074 14549 1 +a 10075 14548 1 +a 10076 14547 1 +a 10077 14546 1 +a 10078 14545 1 +a 10079 14544 1 +a 10080 14543 1 +a 10081 14542 1 +a 10082 14541 1 +a 10083 14540 1 +a 10084 14539 1 +a 10085 14538 1 +a 10086 14537 1 +a 10087 14536 1 +a 10088 14535 1 +a 10089 14534 1 +a 10090 14533 1 +a 10091 14532 1 +a 10092 14531 1 +a 10093 14530 1 +a 10094 14529 1 +a 10095 14528 1 +a 10096 14527 1 +a 10097 14526 1 +a 10098 14525 1 +a 10099 14524 1 +a 10100 14523 1 +a 10101 14522 1 +a 10102 14521 1 +a 10103 14520 1 +a 10104 14519 1 +a 10105 14518 1 +a 10106 14517 1 +a 10107 14516 1 +a 10108 14515 1 +a 10109 14514 1 +a 10110 14513 1 +a 10111 14512 1 +a 10112 14511 1 +a 10113 14510 1 +a 10114 14509 1 +a 10115 14508 1 +a 10116 14507 1 +a 10117 14506 1 +a 10118 14505 1 +a 10119 14504 1 +a 10120 14503 1 +a 10121 14502 1 +a 10122 14501 1 +a 10123 14500 1 +a 10124 14499 1 +a 10125 14498 1 +a 10126 14497 1 +a 10127 14496 1 +a 10128 14495 1 +a 10129 14494 1 +a 10130 14493 1 +a 10131 14492 1 +a 10132 14491 1 +a 10133 14490 1 +a 10134 14489 1 +a 10135 14488 1 +a 10136 14487 1 +a 10137 14486 1 +a 10138 14485 1 +a 10139 14484 1 +a 10140 14483 1 +a 10141 14482 1 +a 10142 14481 1 +a 10143 14480 1 +a 10144 14479 1 +a 10145 14478 1 +a 10146 14477 1 +a 10147 14476 1 +a 10148 14475 1 +a 10149 14474 1 +a 10150 14473 1 +a 10151 14472 1 +a 10152 14471 1 +a 10153 14470 1 +a 10154 14469 1 +a 10155 14468 1 +a 10156 14467 1 +a 10157 14466 1 +a 10158 14465 1 +a 10159 14464 1 +a 10160 14463 1 +a 10161 14462 1 +a 10162 14461 1 +a 10163 14460 1 +a 10164 14459 1 +a 10165 14458 1 +a 10166 14457 1 +a 10167 14456 1 +a 10168 14455 1 +a 10169 14454 1 +a 10170 14453 1 +a 10171 14452 1 +a 10172 14451 1 +a 10173 14450 1 +a 10174 14449 1 +a 10175 14448 1 +a 10176 14447 1 +a 10177 14446 1 +a 10178 14445 1 +a 10179 14444 1 +a 10180 14443 1 +a 10181 14442 1 +a 10182 14441 1 +a 10183 14440 1 +a 10184 14439 1 +a 10185 14438 1 +a 10186 14437 1 +a 10187 14436 1 +a 10188 14435 1 +a 10189 14434 1 +a 10190 14433 1 +a 10191 14432 1 +a 10192 14431 1 +a 10193 14430 1 +a 10194 14429 1 +a 10195 14428 1 +a 10196 14427 1 +a 10197 14426 1 +a 10198 14425 1 +a 10199 14424 1 +a 10200 14423 1 +a 10201 14422 1 +a 10202 14421 1 +a 10203 14420 1 +a 10204 14419 1 +a 10205 14418 1 +a 10206 14417 1 +a 10207 14416 1 +a 10208 14415 1 +a 10209 14414 1 +a 10210 14413 1 +a 10211 14412 1 +a 10212 14411 1 +a 10213 14410 1 +a 10214 14409 1 +a 10215 14408 1 +a 10216 14407 1 +a 10217 14406 1 +a 10218 14405 1 +a 10219 14404 1 +a 10220 14403 1 +a 10221 14402 1 +a 10222 14401 1 +a 10223 14400 1 +a 10224 14399 1 +a 10225 14398 1 +a 10226 14397 1 +a 10227 14396 1 +a 10228 14395 1 +a 10229 14394 1 +a 10230 14393 1 +a 10231 14392 1 +a 10232 14391 1 +a 10233 14390 1 +a 10234 14389 1 +a 10235 14388 1 +a 10236 14387 1 +a 10237 14386 1 +a 10238 14385 1 +a 10239 14384 1 +a 10240 14383 1 +a 10241 14382 1 +a 10242 14381 1 +a 10243 14380 1 +a 10244 14379 1 +a 10245 14378 1 +a 10246 14377 1 +a 10247 14376 1 +a 10248 14375 1 +a 10249 14374 1 +a 10250 14373 1 +a 10251 14372 1 +a 10252 14371 1 +a 10253 14370 1 +a 10254 14369 1 +a 10255 14368 1 +a 10256 14367 1 +a 10257 14366 1 +a 10258 14365 1 +a 10259 14364 1 +a 10260 14363 1 +a 10261 14362 1 +a 10262 14361 1 +a 10263 14360 1 +a 10264 14359 1 +a 10265 14358 1 +a 10266 14357 1 +a 10267 14356 1 +a 10268 14355 1 +a 10269 14354 1 +a 10270 14353 1 +a 10271 14352 1 +a 10272 14351 1 +a 10273 14350 1 +a 10274 14349 1 +a 10275 14348 1 +a 10276 14347 1 +a 10277 14346 1 +a 10278 14345 1 +a 10279 14344 1 +a 10280 14343 1 +a 10281 14342 1 +a 10282 14341 1 +a 10283 14340 1 +a 10284 14339 1 +a 10285 14338 1 +a 10286 14337 1 +a 10287 14336 1 +a 10288 14335 1 +a 10289 14334 1 +a 10290 14333 1 +a 10291 14332 1 +a 10292 14331 1 +a 10293 14330 1 +a 10294 14329 1 +a 10295 14328 1 +a 10296 14327 1 +a 10297 14326 1 +a 10298 14325 1 +a 10299 14324 1 +a 10300 14323 1 +a 10301 14322 1 +a 10302 14321 1 +a 10303 14320 1 +a 10304 14319 1 +a 10305 14318 1 +a 10306 14317 1 +a 10307 14316 1 +a 10308 14315 1 +a 10309 14314 1 +a 10310 14313 1 +a 10311 14312 1 +a 10312 14311 1 +a 10313 14310 1 +a 10314 14309 1 +a 10315 14308 1 +a 10316 14307 1 +a 10317 14306 1 +a 10318 14305 1 +a 10319 14304 1 +a 10320 14303 1 +a 10321 14302 1 +a 10322 14301 1 +a 10323 14300 1 +a 10324 14299 1 +a 10325 14298 1 +a 10326 14297 1 +a 10327 14296 1 +a 10328 14295 1 +a 10329 14294 1 +a 10330 14293 1 +a 10331 14292 1 +a 10332 14291 1 +a 10333 14290 1 +a 10334 14289 1 +a 10335 14288 1 +a 10336 14287 1 +a 10337 14286 1 +a 10338 14285 1 +a 10339 14284 1 +a 10340 14283 1 +a 10341 14282 1 +a 10342 14281 1 +a 10343 14280 1 +a 10344 14279 1 +a 10345 14278 1 +a 10346 14277 1 +a 10347 14276 1 +a 10348 14275 1 +a 10349 14274 1 +a 10350 14273 1 +a 10351 14272 1 +a 10352 14271 1 +a 10353 14270 1 +a 10354 14269 1 +a 10355 14268 1 +a 10356 14267 1 +a 10357 14266 1 +a 10358 14265 1 +a 10359 14264 1 +a 10360 14263 1 +a 10361 14262 1 +a 10362 14261 1 +a 10363 14260 1 +a 10364 14259 1 +a 10365 14258 1 +a 10366 14257 1 +a 10367 14256 1 +a 10368 14255 1 +a 10369 14254 1 +a 10370 14253 1 +a 10371 14252 1 +a 10372 14251 1 +a 10373 14250 1 +a 10374 14249 1 +a 10375 14248 1 +a 10376 14247 1 +a 10377 14246 1 +a 10378 14245 1 +a 10379 14244 1 +a 10380 14243 1 +a 10381 14242 1 +a 10382 14241 1 +a 10383 14240 1 +a 10384 14239 1 +a 10385 14238 1 +a 10386 14237 1 +a 10387 14236 1 +a 10388 14235 1 +a 10389 14234 1 +a 10390 14233 1 +a 10391 14232 1 +a 10392 14231 1 +a 10393 14230 1 +a 10394 14229 1 +a 10395 14228 1 +a 10396 14227 1 +a 10397 14226 1 +a 10398 14225 1 +a 10399 14224 1 +a 10400 14223 1 +a 10401 14222 1 +a 10402 14221 1 +a 10403 14220 1 +a 10404 14219 1 +a 10405 14218 1 +a 10406 14217 1 +a 10407 14216 1 +a 10408 14215 1 +a 10409 14214 1 +a 10410 14213 1 +a 10411 14212 1 +a 10412 14211 1 +a 10413 14210 1 +a 10414 14209 1 +a 10415 14208 1 +a 10416 14207 1 +a 10417 14206 1 +a 10418 14205 1 +a 10419 14204 1 +a 10420 14203 1 +a 10421 14202 1 +a 10422 14201 1 +a 10423 14200 1 +a 10424 14199 1 +a 10425 14198 1 +a 10426 14197 1 +a 10427 14196 1 +a 10428 14195 1 +a 10429 14194 1 +a 10430 14193 1 +a 10431 14192 1 +a 10432 14191 1 +a 10433 14190 1 +a 10434 14189 1 +a 10435 14188 1 +a 10436 14187 1 +a 10437 14186 1 +a 10438 14185 1 +a 10439 14184 1 +a 10440 14183 1 +a 10441 14182 1 +a 10442 14181 1 +a 10443 14180 1 +a 10444 14179 1 +a 10445 14178 1 +a 10446 14177 1 +a 10447 14176 1 +a 10448 14175 1 +a 10449 14174 1 +a 10450 14173 1 +a 10451 14172 1 +a 10452 14171 1 +a 10453 14170 1 +a 10454 14169 1 +a 10455 14168 1 +a 10456 14167 1 +a 10457 14166 1 +a 10458 14165 1 +a 10459 14164 1 +a 10460 14163 1 +a 10461 14162 1 +a 10462 14161 1 +a 10463 14160 1 +a 10464 14159 1 +a 10465 14158 1 +a 10466 14157 1 +a 10467 14156 1 +a 10468 14155 1 +a 10469 14154 1 +a 10470 14153 1 +a 10471 14152 1 +a 10472 14151 1 +a 10473 14150 1 +a 10474 14149 1 +a 10475 14148 1 +a 10476 14147 1 +a 10477 14146 1 +a 10478 14145 1 +a 10479 14144 1 +a 10480 14143 1 +a 10481 14142 1 +a 10482 14141 1 +a 10483 14140 1 +a 10484 14139 1 +a 10485 14138 1 +a 10486 14137 1 +a 10487 14136 1 +a 10488 14135 1 +a 10489 14134 1 +a 10490 14133 1 +a 10491 14132 1 +a 10492 14131 1 +a 10493 14130 1 +a 10494 14129 1 +a 10495 14128 1 +a 10496 14127 1 +a 10497 14126 1 +a 10498 14125 1 +a 10499 14124 1 +a 10500 14123 1 +a 10501 14122 1 +a 10502 14121 1 +a 10503 14120 1 +a 10504 14119 1 +a 10505 14118 1 +a 10506 14117 1 +a 10507 14116 1 +a 10508 14115 1 +a 10509 14114 1 +a 10510 14113 1 +a 10511 14112 1 +a 10512 14111 1 +a 10513 14110 1 +a 10514 14109 1 +a 10515 14108 1 +a 10516 14107 1 +a 10517 14106 1 +a 10518 14105 1 +a 10519 14104 1 +a 10520 14103 1 +a 10521 14102 1 +a 10522 14101 1 +a 10523 14100 1 +a 10524 14099 1 +a 10525 14098 1 +a 10526 14097 1 +a 10527 14096 1 +a 10528 14095 1 +a 10529 14094 1 +a 10530 14093 1 +a 10531 14092 1 +a 10532 14091 1 +a 10533 14090 1 +a 10534 14089 1 +a 10535 14088 1 +a 10536 14087 1 +a 10537 14086 1 +a 10538 14085 1 +a 10539 14084 1 +a 10540 14083 1 +a 10541 14082 1 +a 10542 14081 1 +a 10543 14080 1 +a 10544 14079 1 +a 10545 14078 1 +a 10546 14077 1 +a 10547 14076 1 +a 10548 14075 1 +a 10549 14074 1 +a 10550 14073 1 +a 10551 14072 1 +a 10552 14071 1 +a 10553 14070 1 +a 10554 14069 1 +a 10555 14068 1 +a 10556 14067 1 +a 10557 14066 1 +a 10558 14065 1 +a 10559 14064 1 +a 10560 14063 1 +a 10561 14062 1 +a 10562 14061 1 +a 10563 14060 1 +a 10564 14059 1 +a 10565 14058 1 +a 10566 14057 1 +a 10567 14056 1 +a 10568 14055 1 +a 10569 14054 1 +a 10570 14053 1 +a 10571 14052 1 +a 10572 14051 1 +a 10573 14050 1 +a 10574 14049 1 +a 10575 14048 1 +a 10576 14047 1 +a 10577 14046 1 +a 10578 14045 1 +a 10579 14044 1 +a 10580 14043 1 +a 10581 14042 1 +a 10582 14041 1 +a 10583 14040 1 +a 10584 14039 1 +a 10585 14038 1 +a 10586 14037 1 +a 10587 14036 1 +a 10588 14035 1 +a 10589 14034 1 +a 10590 14033 1 +a 10591 14032 1 +a 10592 14031 1 +a 10593 14030 1 +a 10594 14029 1 +a 10595 14028 1 +a 10596 14027 1 +a 10597 14026 1 +a 10598 14025 1 +a 10599 14024 1 +a 10600 14023 1 +a 10601 14022 1 +a 10602 14021 1 +a 10603 14020 1 +a 10604 14019 1 +a 10605 14018 1 +a 10606 14017 1 +a 10607 14016 1 +a 10608 14015 1 +a 10609 14014 1 +a 10610 14013 1 +a 10611 14012 1 +a 10612 14011 1 +a 10613 14010 1 +a 10614 14009 1 +a 10615 14008 1 +a 10616 14007 1 +a 10617 14006 1 +a 10618 14005 1 +a 10619 14004 1 +a 10620 14003 1 +a 10621 14002 1 +a 10622 14001 1 +a 10623 14000 1 +a 10624 13999 1 +a 10625 13998 1 +a 10626 13997 1 +a 10627 13996 1 +a 10628 13995 1 +a 10629 13994 1 +a 10630 13993 1 +a 10631 13992 1 +a 10632 13991 1 +a 10633 13990 1 +a 10634 13989 1 +a 10635 13988 1 +a 10636 13987 1 +a 10637 13986 1 +a 10638 13985 1 +a 10639 13984 1 +a 10640 13983 1 +a 10641 13982 1 +a 10642 13981 1 +a 10643 13980 1 +a 10644 13979 1 +a 10645 13978 1 +a 10646 13977 1 +a 10647 13976 1 +a 10648 13975 1 +a 10649 13974 1 +a 10650 13973 1 +a 10651 13972 1 +a 10652 13971 1 +a 10653 13970 1 +a 10654 13969 1 +a 10655 13968 1 +a 10656 13967 1 +a 10657 13966 1 +a 10658 13965 1 +a 10659 13964 1 +a 10660 13963 1 +a 10661 13962 1 +a 10662 13961 1 +a 10663 13960 1 +a 10664 13959 1 +a 10665 13958 1 +a 10666 13957 1 +a 10667 13956 1 +a 10668 13955 1 +a 10669 13954 1 +a 10670 13953 1 +a 10671 13952 1 +a 10672 13951 1 +a 10673 13950 1 +a 10674 13949 1 +a 10675 13948 1 +a 10676 13947 1 +a 10677 13946 1 +a 10678 13945 1 +a 10679 13944 1 +a 10680 13943 1 +a 10681 13942 1 +a 10682 13941 1 +a 10683 13940 1 +a 10684 13939 1 +a 10685 13938 1 +a 10686 13937 1 +a 10687 13936 1 +a 10688 13935 1 +a 10689 13934 1 +a 10690 13933 1 +a 10691 13932 1 +a 10692 13931 1 +a 10693 13930 1 +a 10694 13929 1 +a 10695 13928 1 +a 10696 13927 1 +a 10697 13926 1 +a 10698 13925 1 +a 10699 13924 1 +a 10700 13923 1 +a 10701 13922 1 +a 10702 13921 1 +a 10703 13920 1 +a 10704 13919 1 +a 10705 13918 1 +a 10706 13917 1 +a 10707 13916 1 +a 10708 13915 1 +a 10709 13914 1 +a 10710 13913 1 +a 10711 13912 1 +a 10712 13911 1 +a 10713 13910 1 +a 10714 13909 1 +a 10715 13908 1 +a 10716 13907 1 +a 10717 13906 1 +a 10718 13905 1 +a 10719 13904 1 +a 10720 13903 1 +a 10721 13902 1 +a 10722 13901 1 +a 10723 13900 1 +a 10724 13899 1 +a 10725 13898 1 +a 10726 13897 1 +a 10727 13896 1 +a 10728 13895 1 +a 10729 13894 1 +a 10730 13893 1 +a 10731 13892 1 +a 10732 13891 1 +a 10733 13890 1 +a 10734 13889 1 +a 10735 13888 1 +a 10736 13887 1 +a 10737 13886 1 +a 10738 13885 1 +a 10739 13884 1 +a 10740 13883 1 +a 10741 13882 1 +a 10742 13881 1 +a 10743 13880 1 +a 10744 13879 1 +a 10745 13878 1 +a 10746 13877 1 +a 10747 13876 1 +a 10748 13875 1 +a 10749 13874 1 +a 10750 13873 1 +a 10751 13872 1 +a 10752 13871 1 +a 10753 13870 1 +a 10754 13869 1 +a 10755 13868 1 +a 10756 13867 1 +a 10757 13866 1 +a 10758 13865 1 +a 10759 13864 1 +a 10760 13863 1 +a 10761 13862 1 +a 10762 13861 1 +a 10763 13860 1 +a 10764 13859 1 +a 10765 13858 1 +a 10766 13857 1 +a 10767 13856 1 +a 10768 13855 1 +a 10769 13854 1 +a 10770 13853 1 +a 10771 13852 1 +a 10772 13851 1 +a 10773 13850 1 +a 10774 13849 1 +a 10775 13848 1 +a 10776 13847 1 +a 10777 13846 1 +a 10778 13845 1 +a 10779 13844 1 +a 10780 13843 1 +a 10781 13842 1 +a 10782 13841 1 +a 10783 13840 1 +a 10784 13839 1 +a 10785 13838 1 +a 10786 13837 1 +a 10787 13836 1 +a 10788 13835 1 +a 10789 13834 1 +a 10790 13833 1 +a 10791 13832 1 +a 10792 13831 1 +a 10793 13830 1 +a 10794 13829 1 +a 10795 13828 1 +a 10796 13827 1 +a 10797 13826 1 +a 10798 13825 1 +a 10799 13824 1 +a 10800 13823 1 +a 10801 13822 1 +a 10802 13821 1 +a 10803 13820 1 +a 10804 13819 1 +a 10805 13818 1 +a 10806 13817 1 +a 10807 13816 1 +a 10808 13815 1 +a 10809 13814 1 +a 10810 13813 1 +a 10811 13812 1 +a 10812 13811 1 +a 10813 13810 1 +a 10814 13809 1 +a 10815 13808 1 +a 10816 13807 1 +a 10817 13806 1 +a 10818 13805 1 +a 10819 13804 1 +a 10820 13803 1 +a 10821 13802 1 +a 10822 13801 1 +a 10823 13800 1 +a 10824 13799 1 +a 10825 13798 1 +a 10826 13797 1 +a 10827 13796 1 +a 10828 13795 1 +a 10829 13794 1 +a 10830 13793 1 +a 10831 13792 1 +a 10832 13791 1 +a 10833 13790 1 +a 10834 13789 1 +a 10835 13788 1 +a 10836 13787 1 +a 10837 13786 1 +a 10838 13785 1 +a 10839 13784 1 +a 10840 13783 1 +a 10841 13782 1 +a 10842 13781 1 +a 10843 13780 1 +a 10844 13779 1 +a 10845 13778 1 +a 10846 13777 1 +a 10847 13776 1 +a 10848 13775 1 +a 10849 13774 1 +a 10850 13773 1 +a 10851 13772 1 +a 10852 13771 1 +a 10853 13770 1 +a 10854 13769 1 +a 10855 13768 1 +a 10856 13767 1 +a 10857 13766 1 +a 10858 13765 1 +a 10859 13764 1 +a 10860 13763 1 +a 10861 13762 1 +a 10862 13761 1 +a 10863 13760 1 +a 10864 13759 1 +a 10865 13758 1 +a 10866 13757 1 +a 10867 13756 1 +a 10868 13755 1 +a 10869 13754 1 +a 10870 13753 1 +a 10871 13752 1 +a 10872 13751 1 +a 10873 13750 1 +a 10874 13749 1 +a 10875 13748 1 +a 10876 13747 1 +a 10877 13746 1 +a 10878 13745 1 +a 10879 13744 1 +a 10880 13743 1 +a 10881 13742 1 +a 10882 13741 1 +a 10883 13740 1 +a 10884 13739 1 +a 10885 13738 1 +a 10886 13737 1 +a 10887 13736 1 +a 10888 13735 1 +a 10889 13734 1 +a 10890 13733 1 +a 10891 13732 1 +a 10892 13731 1 +a 10893 13730 1 +a 10894 13729 1 +a 10895 13728 1 +a 10896 13727 1 +a 10897 13726 1 +a 10898 13725 1 +a 10899 13724 1 +a 10900 13723 1 +a 10901 13722 1 +a 10902 13721 1 +a 10903 13720 1 +a 10904 13719 1 +a 10905 13718 1 +a 10906 13717 1 +a 10907 13716 1 +a 10908 13715 1 +a 10909 13714 1 +a 10910 13713 1 +a 10911 13712 1 +a 10912 13711 1 +a 10913 13710 1 +a 10914 13709 1 +a 10915 13708 1 +a 10916 13707 1 +a 10917 13706 1 +a 10918 13705 1 +a 10919 13704 1 +a 10920 13703 1 +a 10921 13702 1 +a 10922 13701 1 +a 10923 13700 1 +a 10924 13699 1 +a 10925 13698 1 +a 10926 13697 1 +a 10927 13696 1 +a 10928 13695 1 +a 10929 13694 1 +a 10930 13693 1 +a 10931 13692 1 +a 10932 13691 1 +a 10933 13690 1 +a 10934 13689 1 +a 10935 13688 1 +a 10936 13687 1 +a 10937 13686 1 +a 10938 13685 1 +a 10939 13684 1 +a 10940 13683 1 +a 10941 13682 1 +a 10942 13681 1 +a 10943 13680 1 +a 10944 13679 1 +a 10945 13678 1 +a 10946 13677 1 +a 10947 13676 1 +a 10948 13675 1 +a 10949 13674 1 +a 10950 13673 1 +a 10951 13672 1 +a 10952 13671 1 +a 10953 13670 1 +a 10954 13669 1 +a 10955 13668 1 +a 10956 13667 1 +a 10957 13666 1 +a 10958 13665 1 +a 10959 13664 1 +a 10960 13663 1 +a 10961 13662 1 +a 10962 13661 1 +a 10963 13660 1 +a 10964 13659 1 +a 10965 13658 1 +a 10966 13657 1 +a 10967 13656 1 +a 10968 13655 1 +a 10969 13654 1 +a 10970 13653 1 +a 10971 13652 1 +a 10972 13651 1 +a 10973 13650 1 +a 10974 13649 1 +a 10975 13648 1 +a 10976 13647 1 +a 10977 13646 1 +a 10978 13645 1 +a 10979 13644 1 +a 10980 13643 1 +a 10981 13642 1 +a 10982 13641 1 +a 10983 13640 1 +a 10984 13639 1 +a 10985 13638 1 +a 10986 13637 1 +a 10987 13636 1 +a 10988 13635 1 +a 10989 13634 1 +a 10990 13633 1 +a 10991 13632 1 +a 10992 13631 1 +a 10993 13630 1 +a 10994 13629 1 +a 10995 13628 1 +a 10996 13627 1 +a 10997 13626 1 +a 10998 13625 1 +a 10999 13624 1 +a 11000 13623 1 +a 11001 13622 1 +a 11002 13621 1 +a 11003 13620 1 +a 11004 13619 1 +a 11005 13618 1 +a 11006 13617 1 +a 11007 13616 1 +a 11008 13615 1 +a 11009 13614 1 +a 11010 13613 1 +a 11011 13612 1 +a 11012 13611 1 +a 11013 13610 1 +a 11014 13609 1 +a 11015 13608 1 +a 11016 13607 1 +a 11017 13606 1 +a 11018 13605 1 +a 11019 13604 1 +a 11020 13603 1 +a 11021 13602 1 +a 11022 13601 1 +a 11023 13600 1 +a 11024 13599 1 +a 11025 13598 1 +a 11026 13597 1 +a 11027 13596 1 +a 11028 13595 1 +a 11029 13594 1 +a 11030 13593 1 +a 11031 13592 1 +a 11032 13591 1 +a 11033 13590 1 +a 11034 13589 1 +a 11035 13588 1 +a 11036 13587 1 +a 11037 13586 1 +a 11038 13585 1 +a 11039 13584 1 +a 11040 13583 1 +a 11041 13582 1 +a 11042 13581 1 +a 11043 13580 1 +a 11044 13579 1 +a 11045 13578 1 +a 11046 13577 1 +a 11047 13576 1 +a 11048 13575 1 +a 11049 13574 1 +a 11050 13573 1 +a 11051 13572 1 +a 11052 13571 1 +a 11053 13570 1 +a 11054 13569 1 +a 11055 13568 1 +a 11056 13567 1 +a 11057 13566 1 +a 11058 13565 1 +a 11059 13564 1 +a 11060 13563 1 +a 11061 13562 1 +a 11062 13561 1 +a 11063 13560 1 +a 11064 13559 1 +a 11065 13558 1 +a 11066 13557 1 +a 11067 13556 1 +a 11068 13555 1 +a 11069 13554 1 +a 11070 13553 1 +a 11071 13552 1 +a 11072 13551 1 +a 11073 13550 1 +a 11074 13549 1 +a 11075 13548 1 +a 11076 13547 1 +a 11077 13546 1 +a 11078 13545 1 +a 11079 13544 1 +a 11080 13543 1 +a 11081 13542 1 +a 11082 13541 1 +a 11083 13540 1 +a 11084 13539 1 +a 11085 13538 1 +a 11086 13537 1 +a 11087 13536 1 +a 11088 13535 1 +a 11089 13534 1 +a 11090 13533 1 +a 11091 13532 1 +a 11092 13531 1 +a 11093 13530 1 +a 11094 13529 1 +a 11095 13528 1 +a 11096 13527 1 +a 11097 13526 1 +a 11098 13525 1 +a 11099 13524 1 +a 11100 13523 1 +a 11101 13522 1 +a 11102 13521 1 +a 11103 13520 1 +a 11104 13519 1 +a 11105 13518 1 +a 11106 13517 1 +a 11107 13516 1 +a 11108 13515 1 +a 11109 13514 1 +a 11110 13513 1 +a 11111 13512 1 +a 11112 13511 1 +a 11113 13510 1 +a 11114 13509 1 +a 11115 13508 1 +a 11116 13507 1 +a 11117 13506 1 +a 11118 13505 1 +a 11119 13504 1 +a 11120 13503 1 +a 11121 13502 1 +a 11122 13501 1 +a 11123 13500 1 +a 11124 13499 1 +a 11125 13498 1 +a 11126 13497 1 +a 11127 13496 1 +a 11128 13495 1 +a 11129 13494 1 +a 11130 13493 1 +a 11131 13492 1 +a 11132 13491 1 +a 11133 13490 1 +a 11134 13489 1 +a 11135 13488 1 +a 11136 13487 1 +a 11137 13486 1 +a 11138 13485 1 +a 11139 13484 1 +a 11140 13483 1 +a 11141 13482 1 +a 11142 13481 1 +a 11143 13480 1 +a 11144 13479 1 +a 11145 13478 1 +a 11146 13477 1 +a 11147 13476 1 +a 11148 13475 1 +a 11149 13474 1 +a 11150 13473 1 +a 11151 13472 1 +a 11152 13471 1 +a 11153 13470 1 +a 11154 13469 1 +a 11155 13468 1 +a 11156 13467 1 +a 11157 13466 1 +a 11158 13465 1 +a 11159 13464 1 +a 11160 13463 1 +a 11161 13462 1 +a 11162 13461 1 +a 11163 13460 1 +a 11164 13459 1 +a 11165 13458 1 +a 11166 13457 1 +a 11167 13456 1 +a 11168 13455 1 +a 11169 13454 1 +a 11170 13453 1 +a 11171 13452 1 +a 11172 13451 1 +a 11173 13450 1 +a 11174 13449 1 +a 11175 13448 1 +a 11176 13447 1 +a 11177 13446 1 +a 11178 13445 1 +a 11179 13444 1 +a 11180 13443 1 +a 11181 13442 1 +a 11182 13441 1 +a 11183 13440 1 +a 11184 13439 1 +a 11185 13438 1 +a 11186 13437 1 +a 11187 13436 1 +a 11188 13435 1 +a 11189 13434 1 +a 11190 13433 1 +a 11191 13432 1 +a 11192 13431 1 +a 11193 13430 1 +a 11194 13429 1 +a 11195 13428 1 +a 11196 13427 1 +a 11197 13426 1 +a 11198 13425 1 +a 11199 13424 1 +a 11200 13423 1 +a 11201 13422 1 +a 11202 13421 1 +a 11203 13420 1 +a 11204 13419 1 +a 11205 13418 1 +a 11206 13417 1 +a 11207 13416 1 +a 11208 13415 1 +a 11209 13414 1 +a 11210 13413 1 +a 11211 13412 1 +a 11212 13411 1 +a 11213 13410 1 +a 11214 13409 1 +a 11215 13408 1 +a 11216 13407 1 +a 11217 13406 1 +a 11218 13405 1 +a 11219 13404 1 +a 11220 13403 1 +a 11221 13402 1 +a 11222 13401 1 +a 11223 13400 1 +a 11224 13399 1 +a 11225 13398 1 +a 11226 13397 1 +a 11227 13396 1 +a 11228 13395 1 +a 11229 13394 1 +a 11230 13393 1 +a 11231 13392 1 +a 11232 13391 1 +a 11233 13390 1 +a 11234 13389 1 +a 11235 13388 1 +a 11236 13387 1 +a 11237 13386 1 +a 11238 13385 1 +a 11239 13384 1 +a 11240 13383 1 +a 11241 13382 1 +a 11242 13381 1 +a 11243 13380 1 +a 11244 13379 1 +a 11245 13378 1 +a 11246 13377 1 +a 11247 13376 1 +a 11248 13375 1 +a 11249 13374 1 +a 11250 13373 1 +a 11251 13372 1 +a 11252 13371 1 +a 11253 13370 1 +a 11254 13369 1 +a 11255 13368 1 +a 11256 13367 1 +a 11257 13366 1 +a 11258 13365 1 +a 11259 13364 1 +a 11260 13363 1 +a 11261 13362 1 +a 11262 13361 1 +a 11263 13360 1 +a 11264 13359 1 +a 11265 13358 1 +a 11266 13357 1 +a 11267 13356 1 +a 11268 13355 1 +a 11269 13354 1 +a 11270 13353 1 +a 11271 13352 1 +a 11272 13351 1 +a 11273 13350 1 +a 11274 13349 1 +a 11275 13348 1 +a 11276 13347 1 +a 11277 13346 1 +a 11278 13345 1 +a 11279 13344 1 +a 11280 13343 1 +a 11281 13342 1 +a 11282 13341 1 +a 11283 13340 1 +a 11284 13339 1 +a 11285 13338 1 +a 11286 13337 1 +a 11287 13336 1 +a 11288 13335 1 +a 11289 13334 1 +a 11290 13333 1 +a 11291 13332 1 +a 11292 13331 1 +a 11293 13330 1 +a 11294 13329 1 +a 11295 13328 1 +a 11296 13327 1 +a 11297 13326 1 +a 11298 13325 1 +a 11299 13324 1 +a 11300 13323 1 +a 11301 13322 1 +a 11302 13321 1 +a 11303 13320 1 +a 11304 13319 1 +a 11305 13318 1 +a 11306 13317 1 +a 11307 13316 1 +a 11308 13315 1 +a 11309 13314 1 +a 11310 13313 1 +a 11311 13312 1 +a 11312 13311 1 +a 11313 13310 1 +a 11314 13309 1 +a 11315 13308 1 +a 11316 13307 1 +a 11317 13306 1 +a 11318 13305 1 +a 11319 13304 1 +a 11320 13303 1 +a 11321 13302 1 +a 11322 13301 1 +a 11323 13300 1 +a 11324 13299 1 +a 11325 13298 1 +a 11326 13297 1 +a 11327 13296 1 +a 11328 13295 1 +a 11329 13294 1 +a 11330 13293 1 +a 11331 13292 1 +a 11332 13291 1 +a 11333 13290 1 +a 11334 13289 1 +a 11335 13288 1 +a 11336 13287 1 +a 11337 13286 1 +a 11338 13285 1 +a 11339 13284 1 +a 11340 13283 1 +a 11341 13282 1 +a 11342 13281 1 +a 11343 13280 1 +a 11344 13279 1 +a 11345 13278 1 +a 11346 13277 1 +a 11347 13276 1 +a 11348 13275 1 +a 11349 13274 1 +a 11350 13273 1 +a 11351 13272 1 +a 11352 13271 1 +a 11353 13270 1 +a 11354 13269 1 +a 11355 13268 1 +a 11356 13267 1 +a 11357 13266 1 +a 11358 13265 1 +a 11359 13264 1 +a 11360 13263 1 +a 11361 13262 1 +a 11362 13261 1 +a 11363 13260 1 +a 11364 13259 1 +a 11365 13258 1 +a 11366 13257 1 +a 11367 13256 1 +a 11368 13255 1 +a 11369 13254 1 +a 11370 13253 1 +a 11371 13252 1 +a 11372 13251 1 +a 11373 13250 1 +a 11374 13249 1 +a 11375 13248 1 +a 11376 13247 1 +a 11377 13246 1 +a 11378 13245 1 +a 11379 13244 1 +a 11380 13243 1 +a 11381 13242 1 +a 11382 13241 1 +a 11383 13240 1 +a 11384 13239 1 +a 11385 13238 1 +a 11386 13237 1 +a 11387 13236 1 +a 11388 13235 1 +a 11389 13234 1 +a 11390 13233 1 +a 11391 13232 1 +a 11392 13231 1 +a 11393 13230 1 +a 11394 13229 1 +a 11395 13228 1 +a 11396 13227 1 +a 11397 13226 1 +a 11398 13225 1 +a 11399 13224 1 +a 11400 13223 1 +a 11401 13222 1 +a 11402 13221 1 +a 11403 13220 1 +a 11404 13219 1 +a 11405 13218 1 +a 11406 13217 1 +a 11407 13216 1 +a 11408 13215 1 +a 11409 13214 1 +a 11410 13213 1 +a 11411 13212 1 +a 11412 13211 1 +a 11413 13210 1 +a 11414 13209 1 +a 11415 13208 1 +a 11416 13207 1 +a 11417 13206 1 +a 11418 13205 1 +a 11419 13204 1 +a 11420 13203 1 +a 11421 13202 1 +a 11422 13201 1 +a 11423 13200 1 +a 11424 13199 1 +a 11425 13198 1 +a 11426 13197 1 +a 11427 13196 1 +a 11428 13195 1 +a 11429 13194 1 +a 11430 13193 1 +a 11431 13192 1 +a 11432 13191 1 +a 11433 13190 1 +a 11434 13189 1 +a 11435 13188 1 +a 11436 13187 1 +a 11437 13186 1 +a 11438 13185 1 +a 11439 13184 1 +a 11440 13183 1 +a 11441 13182 1 +a 11442 13181 1 +a 11443 13180 1 +a 11444 13179 1 +a 11445 13178 1 +a 11446 13177 1 +a 11447 13176 1 +a 11448 13175 1 +a 11449 13174 1 +a 11450 13173 1 +a 11451 13172 1 +a 11452 13171 1 +a 11453 13170 1 +a 11454 13169 1 +a 11455 13168 1 +a 11456 13167 1 +a 11457 13166 1 +a 11458 13165 1 +a 11459 13164 1 +a 11460 13163 1 +a 11461 13162 1 +a 11462 13161 1 +a 11463 13160 1 +a 11464 13159 1 +a 11465 13158 1 +a 11466 13157 1 +a 11467 13156 1 +a 11468 13155 1 +a 11469 13154 1 +a 11470 13153 1 +a 11471 13152 1 +a 11472 13151 1 +a 11473 13150 1 +a 11474 13149 1 +a 11475 13148 1 +a 11476 13147 1 +a 11477 13146 1 +a 11478 13145 1 +a 11479 13144 1 +a 11480 13143 1 +a 11481 13142 1 +a 11482 13141 1 +a 11483 13140 1 +a 11484 13139 1 +a 11485 13138 1 +a 11486 13137 1 +a 11487 13136 1 +a 11488 13135 1 +a 11489 13134 1 +a 11490 13133 1 +a 11491 13132 1 +a 11492 13131 1 +a 11493 13130 1 +a 11494 13129 1 +a 11495 13128 1 +a 11496 13127 1 +a 11497 13126 1 +a 11498 13125 1 +a 11499 13124 1 +a 11500 13123 1 +a 11501 13122 1 +a 11502 13121 1 +a 11503 13120 1 +a 11504 13119 1 +a 11505 13118 1 +a 11506 13117 1 +a 11507 13116 1 +a 11508 13115 1 +a 11509 13114 1 +a 11510 13113 1 +a 11511 13112 1 +a 11512 13111 1 +a 11513 13110 1 +a 11514 13109 1 +a 11515 13108 1 +a 11516 13107 1 +a 11517 13106 1 +a 11518 13105 1 +a 11519 13104 1 +a 11520 13103 1 +a 11521 13102 1 +a 11522 13101 1 +a 11523 13100 1 +a 11524 13099 1 +a 11525 13098 1 +a 11526 13097 1 +a 11527 13096 1 +a 11528 13095 1 +a 11529 13094 1 +a 11530 13093 1 +a 11531 13092 1 +a 11532 13091 1 +a 11533 13090 1 +a 11534 13089 1 +a 11535 13088 1 +a 11536 13087 1 +a 11537 13086 1 +a 11538 13085 1 +a 11539 13084 1 +a 11540 13083 1 +a 11541 13082 1 +a 11542 13081 1 +a 11543 13080 1 +a 11544 13079 1 +a 11545 13078 1 +a 11546 13077 1 +a 11547 13076 1 +a 11548 13075 1 +a 11549 13074 1 +a 11550 13073 1 +a 11551 13072 1 +a 11552 13071 1 +a 11553 13070 1 +a 11554 13069 1 +a 11555 13068 1 +a 11556 13067 1 +a 11557 13066 1 +a 11558 13065 1 +a 11559 13064 1 +a 11560 13063 1 +a 11561 13062 1 +a 11562 13061 1 +a 11563 13060 1 +a 11564 13059 1 +a 11565 13058 1 +a 11566 13057 1 +a 11567 13056 1 +a 11568 13055 1 +a 11569 13054 1 +a 11570 13053 1 +a 11571 13052 1 +a 11572 13051 1 +a 11573 13050 1 +a 11574 13049 1 +a 11575 13048 1 +a 11576 13047 1 +a 11577 13046 1 +a 11578 13045 1 +a 11579 13044 1 +a 11580 13043 1 +a 11581 13042 1 +a 11582 13041 1 +a 11583 13040 1 +a 11584 13039 1 +a 11585 13038 1 +a 11586 13037 1 +a 11587 13036 1 +a 11588 13035 1 +a 11589 13034 1 +a 11590 13033 1 +a 11591 13032 1 +a 11592 13031 1 +a 11593 13030 1 +a 11594 13029 1 +a 11595 13028 1 +a 11596 13027 1 +a 11597 13026 1 +a 11598 13025 1 +a 11599 13024 1 +a 11600 13023 1 +a 11601 13022 1 +a 11602 13021 1 +a 11603 13020 1 +a 11604 13019 1 +a 11605 13018 1 +a 11606 13017 1 +a 11607 13016 1 +a 11608 13015 1 +a 11609 13014 1 +a 11610 13013 1 +a 11611 13012 1 +a 11612 13011 1 +a 11613 13010 1 +a 11614 13009 1 +a 11615 13008 1 +a 11616 13007 1 +a 11617 13006 1 +a 11618 13005 1 +a 11619 13004 1 +a 11620 13003 1 +a 11621 13002 1 +a 11622 13001 1 +a 11623 13000 1 +a 11624 12999 1 +a 11625 12998 1 +a 11626 12997 1 +a 11627 12996 1 +a 11628 12995 1 +a 11629 12994 1 +a 11630 12993 1 +a 11631 12992 1 +a 11632 12991 1 +a 11633 12990 1 +a 11634 12989 1 +a 11635 12988 1 +a 11636 12987 1 +a 11637 12986 1 +a 11638 12985 1 +a 11639 12984 1 +a 11640 12983 1 +a 11641 12982 1 +a 11642 12981 1 +a 11643 12980 1 +a 11644 12979 1 +a 11645 12978 1 +a 11646 12977 1 +a 11647 12976 1 +a 11648 12975 1 +a 11649 12974 1 +a 11650 12973 1 +a 11651 12972 1 +a 11652 12971 1 +a 11653 12970 1 +a 11654 12969 1 +a 11655 12968 1 +a 11656 12967 1 +a 11657 12966 1 +a 11658 12965 1 +a 11659 12964 1 +a 11660 12963 1 +a 11661 12962 1 +a 11662 12961 1 +a 11663 12960 1 +a 11664 12959 1 +a 11665 12958 1 +a 11666 12957 1 +a 11667 12956 1 +a 11668 12955 1 +a 11669 12954 1 +a 11670 12953 1 +a 11671 12952 1 +a 11672 12951 1 +a 11673 12950 1 +a 11674 12949 1 +a 11675 12948 1 +a 11676 12947 1 +a 11677 12946 1 +a 11678 12945 1 +a 11679 12944 1 +a 11680 12943 1 +a 11681 12942 1 +a 11682 12941 1 +a 11683 12940 1 +a 11684 12939 1 +a 11685 12938 1 +a 11686 12937 1 +a 11687 12936 1 +a 11688 12935 1 +a 11689 12934 1 +a 11690 12933 1 +a 11691 12932 1 +a 11692 12931 1 +a 11693 12930 1 +a 11694 12929 1 +a 11695 12928 1 +a 11696 12927 1 +a 11697 12926 1 +a 11698 12925 1 +a 11699 12924 1 +a 11700 12923 1 +a 11701 12922 1 +a 11702 12921 1 +a 11703 12920 1 +a 11704 12919 1 +a 11705 12918 1 +a 11706 12917 1 +a 11707 12916 1 +a 11708 12915 1 +a 11709 12914 1 +a 11710 12913 1 +a 11711 12912 1 +a 11712 12911 1 +a 11713 12910 1 +a 11714 12909 1 +a 11715 12908 1 +a 11716 12907 1 +a 11717 12906 1 +a 11718 12905 1 +a 11719 12904 1 +a 11720 12903 1 +a 11721 12902 1 +a 11722 12901 1 +a 11723 12900 1 +a 11724 12899 1 +a 11725 12898 1 +a 11726 12897 1 +a 11727 12896 1 +a 11728 12895 1 +a 11729 12894 1 +a 11730 12893 1 +a 11731 12892 1 +a 11732 12891 1 +a 11733 12890 1 +a 11734 12889 1 +a 11735 12888 1 +a 11736 12887 1 +a 11737 12886 1 +a 11738 12885 1 +a 11739 12884 1 +a 11740 12883 1 +a 11741 12882 1 +a 11742 12881 1 +a 11743 12880 1 +a 11744 12879 1 +a 11745 12878 1 +a 11746 12877 1 +a 11747 12876 1 +a 11748 12875 1 +a 11749 12874 1 +a 11750 12873 1 +a 11751 12872 1 +a 11752 12871 1 +a 11753 12870 1 +a 11754 12869 1 +a 11755 12868 1 +a 11756 12867 1 +a 11757 12866 1 +a 11758 12865 1 +a 11759 12864 1 +a 11760 12863 1 +a 11761 12862 1 +a 11762 12861 1 +a 11763 12860 1 +a 11764 12859 1 +a 11765 12858 1 +a 11766 12857 1 +a 11767 12856 1 +a 11768 12855 1 +a 11769 12854 1 +a 11770 12853 1 +a 11771 12852 1 +a 11772 12851 1 +a 11773 12850 1 +a 11774 12849 1 +a 11775 12848 1 +a 11776 12847 1 +a 11777 12846 1 +a 11778 12845 1 +a 11779 12844 1 +a 11780 12843 1 +a 11781 12842 1 +a 11782 12841 1 +a 11783 12840 1 +a 11784 12839 1 +a 11785 12838 1 +a 11786 12837 1 +a 11787 12836 1 +a 11788 12835 1 +a 11789 12834 1 +a 11790 12833 1 +a 11791 12832 1 +a 11792 12831 1 +a 11793 12830 1 +a 11794 12829 1 +a 11795 12828 1 +a 11796 12827 1 +a 11797 12826 1 +a 11798 12825 1 +a 11799 12824 1 +a 11800 12823 1 +a 11801 12822 1 +a 11802 12821 1 +a 11803 12820 1 +a 11804 12819 1 +a 11805 12818 1 +a 11806 12817 1 +a 11807 12816 1 +a 11808 12815 1 +a 11809 12814 1 +a 11810 12813 1 +a 11811 12812 1 +a 11812 12811 1 +a 11813 12810 1 +a 11814 12809 1 +a 11815 12808 1 +a 11816 12807 1 +a 11817 12806 1 +a 11818 12805 1 +a 11819 12804 1 +a 11820 12803 1 +a 11821 12802 1 +a 11822 12801 1 +a 11823 12800 1 +a 11824 12799 1 +a 11825 12798 1 +a 11826 12797 1 +a 11827 12796 1 +a 11828 12795 1 +a 11829 12794 1 +a 11830 12793 1 +a 11831 12792 1 +a 11832 12791 1 +a 11833 12790 1 +a 11834 12789 1 +a 11835 12788 1 +a 11836 12787 1 +a 11837 12786 1 +a 11838 12785 1 +a 11839 12784 1 +a 11840 12783 1 +a 11841 12782 1 +a 11842 12781 1 +a 11843 12780 1 +a 11844 12779 1 +a 11845 12778 1 +a 11846 12777 1 +a 11847 12776 1 +a 11848 12775 1 +a 11849 12774 1 +a 11850 12773 1 +a 11851 12772 1 +a 11852 12771 1 +a 11853 12770 1 +a 11854 12769 1 +a 11855 12768 1 +a 11856 12767 1 +a 11857 12766 1 +a 11858 12765 1 +a 11859 12764 1 +a 11860 12763 1 +a 11861 12762 1 +a 11862 12761 1 +a 11863 12760 1 +a 11864 12759 1 +a 11865 12758 1 +a 11866 12757 1 +a 11867 12756 1 +a 11868 12755 1 +a 11869 12754 1 +a 11870 12753 1 +a 11871 12752 1 +a 11872 12751 1 +a 11873 12750 1 +a 11874 12749 1 +a 11875 12748 1 +a 11876 12747 1 +a 11877 12746 1 +a 11878 12745 1 +a 11879 12744 1 +a 11880 12743 1 +a 11881 12742 1 +a 11882 12741 1 +a 11883 12740 1 +a 11884 12739 1 +a 11885 12738 1 +a 11886 12737 1 +a 11887 12736 1 +a 11888 12735 1 +a 11889 12734 1 +a 11890 12733 1 +a 11891 12732 1 +a 11892 12731 1 +a 11893 12730 1 +a 11894 12729 1 +a 11895 12728 1 +a 11896 12727 1 +a 11897 12726 1 +a 11898 12725 1 +a 11899 12724 1 +a 11900 12723 1 +a 11901 12722 1 +a 11902 12721 1 +a 11903 12720 1 +a 11904 12719 1 +a 11905 12718 1 +a 11906 12717 1 +a 11907 12716 1 +a 11908 12715 1 +a 11909 12714 1 +a 11910 12713 1 +a 11911 12712 1 +a 11912 12711 1 +a 11913 12710 1 +a 11914 12709 1 +a 11915 12708 1 +a 11916 12707 1 +a 11917 12706 1 +a 11918 12705 1 +a 11919 12704 1 +a 11920 12703 1 +a 11921 12702 1 +a 11922 12701 1 +a 11923 12700 1 +a 11924 12699 1 +a 11925 12698 1 +a 11926 12697 1 +a 11927 12696 1 +a 11928 12695 1 +a 11929 12694 1 +a 11930 12693 1 +a 11931 12692 1 +a 11932 12691 1 +a 11933 12690 1 +a 11934 12689 1 +a 11935 12688 1 +a 11936 12687 1 +a 11937 12686 1 +a 11938 12685 1 +a 11939 12684 1 +a 11940 12683 1 +a 11941 12682 1 +a 11942 12681 1 +a 11943 12680 1 +a 11944 12679 1 +a 11945 12678 1 +a 11946 12677 1 +a 11947 12676 1 +a 11948 12675 1 +a 11949 12674 1 +a 11950 12673 1 +a 11951 12672 1 +a 11952 12671 1 +a 11953 12670 1 +a 11954 12669 1 +a 11955 12668 1 +a 11956 12667 1 +a 11957 12666 1 +a 11958 12665 1 +a 11959 12664 1 +a 11960 12663 1 +a 11961 12662 1 +a 11962 12661 1 +a 11963 12660 1 +a 11964 12659 1 +a 11965 12658 1 +a 11966 12657 1 +a 11967 12656 1 +a 11968 12655 1 +a 11969 12654 1 +a 11970 12653 1 +a 11971 12652 1 +a 11972 12651 1 +a 11973 12650 1 +a 11974 12649 1 +a 11975 12648 1 +a 11976 12647 1 +a 11977 12646 1 +a 11978 12645 1 +a 11979 12644 1 +a 11980 12643 1 +a 11981 12642 1 +a 11982 12641 1 +a 11983 12640 1 +a 11984 12639 1 +a 11985 12638 1 +a 11986 12637 1 +a 11987 12636 1 +a 11988 12635 1 +a 11989 12634 1 +a 11990 12633 1 +a 11991 12632 1 +a 11992 12631 1 +a 11993 12630 1 +a 11994 12629 1 +a 11995 12628 1 +a 11996 12627 1 +a 11997 12626 1 +a 11998 12625 1 +a 11999 12624 1 +a 12000 12623 1 +a 12001 12622 1 +a 12002 12621 1 +a 12003 12620 1 +a 12004 12619 1 +a 12005 12618 1 +a 12006 12617 1 +a 12007 12616 1 +a 12008 12615 1 +a 12009 12614 1 +a 12010 12613 1 +a 12011 12612 1 +a 12012 12611 1 +a 12013 12610 1 +a 12014 12609 1 +a 12015 12608 1 +a 12016 12607 1 +a 12017 12606 1 +a 12018 12605 1 +a 12019 12604 1 +a 12020 12603 1 +a 12021 12602 1 +a 12022 12601 1 +a 12023 12600 1 +a 12024 12599 1 +a 12025 12598 1 +a 12026 12597 1 +a 12027 12596 1 +a 12028 12595 1 +a 12029 12594 1 +a 12030 12593 1 +a 12031 12592 1 +a 12032 12591 1 +a 12033 12590 1 +a 12034 12589 1 +a 12035 12588 1 +a 12036 12587 1 +a 12037 12586 1 +a 12038 12585 1 +a 12039 12584 1 +a 12040 12583 1 +a 12041 12582 1 +a 12042 12581 1 +a 12043 12580 1 +a 12044 12579 1 +a 12045 12578 1 +a 12046 12577 1 +a 12047 12576 1 +a 12048 12575 1 +a 12049 12574 1 +a 12050 12573 1 +a 12051 12572 1 +a 12052 12571 1 +a 12053 12570 1 +a 12054 12569 1 +a 12055 12568 1 +a 12056 12567 1 +a 12057 12566 1 +a 12058 12565 1 +a 12059 12564 1 +a 12060 12563 1 +a 12061 12562 1 +a 12062 12561 1 +a 12063 12560 1 +a 12064 12559 1 +a 12065 12558 1 +a 12066 12557 1 +a 12067 12556 1 +a 12068 12555 1 +a 12069 12554 1 +a 12070 12553 1 +a 12071 12552 1 +a 12072 12551 1 +a 12073 12550 1 +a 12074 12549 1 +a 12075 12548 1 +a 12076 12547 1 +a 12077 12546 1 +a 12078 12545 1 +a 12079 12544 1 +a 12080 12543 1 +a 12081 12542 1 +a 12082 12541 1 +a 12083 12540 1 +a 12084 12539 1 +a 12085 12538 1 +a 12086 12537 1 +a 12087 12536 1 +a 12088 12535 1 +a 12089 12534 1 +a 12090 12533 1 +a 12091 12532 1 +a 12092 12531 1 +a 12093 12530 1 +a 12094 12529 1 +a 12095 12528 1 +a 12096 12527 1 +a 12097 12526 1 +a 12098 12525 1 +a 12099 12524 1 +a 12100 12523 1 +a 12101 12522 1 +a 12102 12521 1 +a 12103 12520 1 +a 12104 12519 1 +a 12105 12518 1 +a 12106 12517 1 +a 12107 12516 1 +a 12108 12515 1 +a 12109 12514 1 +a 12110 12513 1 +a 12111 12512 1 +a 12112 12511 1 +a 12113 12510 1 +a 12114 12509 1 +a 12115 12508 1 +a 12116 12507 1 +a 12117 12506 1 +a 12118 12505 1 +a 12119 12504 1 +a 12120 12503 1 +a 12121 12502 1 +a 12122 12501 1 +a 12123 12500 1 +a 12124 12499 1 +a 12125 12498 1 +a 12126 12497 1 +a 12127 12496 1 +a 12128 12495 1 +a 12129 12494 1 +a 12130 12493 1 +a 12131 12492 1 +a 12132 12491 1 +a 12133 12490 1 +a 12134 12489 1 +a 12135 12488 1 +a 12136 12487 1 +a 12137 12486 1 +a 12138 12485 1 +a 12139 12484 1 +a 12140 12483 1 +a 12141 12482 1 +a 12142 12481 1 +a 12143 12480 1 +a 12144 12479 1 +a 12145 12478 1 +a 12146 12477 1 +a 12147 12476 1 +a 12148 12475 1 +a 12149 12474 1 +a 12150 12473 1 +a 12151 12472 1 +a 12152 12471 1 +a 12153 12470 1 +a 12154 12469 1 +a 12155 12468 1 +a 12156 12467 1 +a 12157 12466 1 +a 12158 12465 1 +a 12159 12464 1 +a 12160 12463 1 +a 12161 12462 1 +a 12162 12461 1 +a 12163 12460 1 +a 12164 12459 1 +a 12165 12458 1 +a 12166 12457 1 +a 12167 12456 1 +a 12168 12455 1 +a 12169 12454 1 +a 12170 12453 1 +a 12171 12452 1 +a 12172 12451 1 +a 12173 12450 1 +a 12174 12449 1 +a 12175 12448 1 +a 12176 12447 1 +a 12177 12446 1 +a 12178 12445 1 +a 12179 12444 1 +a 12180 12443 1 +a 12181 12442 1 +a 12182 12441 1 +a 12183 12440 1 +a 12184 12439 1 +a 12185 12438 1 +a 12186 12437 1 +a 12187 12436 1 +a 12188 12435 1 +a 12189 12434 1 +a 12190 12433 1 +a 12191 12432 1 +a 12192 12431 1 +a 12193 12430 1 +a 12194 12429 1 +a 12195 12428 1 +a 12196 12427 1 +a 12197 12426 1 +a 12198 12425 1 +a 12199 12424 1 +a 12200 12423 1 +a 12201 12422 1 +a 12202 12421 1 +a 12203 12420 1 +a 12204 12419 1 +a 12205 12418 1 +a 12206 12417 1 +a 12207 12416 1 +a 12208 12415 1 +a 12209 12414 1 +a 12210 12413 1 +a 12211 12412 1 +a 12212 12411 1 +a 12213 12410 1 +a 12214 12409 1 +a 12215 12408 1 +a 12216 12407 1 +a 12217 12406 1 +a 12218 12405 1 +a 12219 12404 1 +a 12220 12403 1 +a 12221 12402 1 +a 12222 12401 1 +a 12223 12400 1 +a 12224 12399 1 +a 12225 12398 1 +a 12226 12397 1 +a 12227 12396 1 +a 12228 12395 1 +a 12229 12394 1 +a 12230 12393 1 +a 12231 12392 1 +a 12232 12391 1 +a 12233 12390 1 +a 12234 12389 1 +a 12235 12388 1 +a 12236 12387 1 +a 12237 12386 1 +a 12238 12385 1 +a 12239 12384 1 +a 12240 12383 1 +a 12241 12382 1 +a 12242 12381 1 +a 12243 12380 1 +a 12244 12379 1 +a 12245 12378 1 +a 12246 12377 1 +a 12247 12376 1 +a 12248 12375 1 +a 12249 12374 1 +a 12250 12373 1 +a 12251 12372 1 +a 12252 12371 1 +a 12253 12370 1 +a 12254 12369 1 +a 12255 12368 1 +a 12256 12367 1 +a 12257 12366 1 +a 12258 12365 1 +a 12259 12364 1 +a 12260 12363 1 +a 12261 12362 1 +a 12262 12361 1 +a 12263 12360 1 +a 12264 12359 1 +a 12265 12358 1 +a 12266 12357 1 +a 12267 12356 1 +a 12268 12355 1 +a 12269 12354 1 +a 12270 12353 1 +a 12271 12352 1 +a 12272 12351 1 +a 12273 12350 1 +a 12274 12349 1 +a 12275 12348 1 +a 12276 12347 1 +a 12277 12346 1 +a 12278 12345 1 +a 12279 12344 1 +a 12280 12343 1 +a 12281 12342 1 +a 12282 12341 1 +a 12283 12340 1 +a 12284 12339 1 +a 12285 12338 1 +a 12286 12337 1 +a 12287 12336 1 +a 12288 12335 1 +a 12289 12334 1 +a 12290 12333 1 +a 12291 12332 1 +a 12292 12331 1 +a 12293 12330 1 +a 12294 12329 1 +a 12295 12328 1 +a 12296 12327 1 +a 12297 12326 1 +a 12298 12325 1 +a 12299 12324 1 +a 12300 12323 1 +a 12301 12322 1 +a 12302 12321 1 +a 12303 12320 1 +a 12304 12319 1 +a 12305 12318 1 +a 12306 12317 1 +a 12307 12316 1 +a 12308 12315 1 +a 12309 12314 1 +a 12310 12313 1 +a 1 3 1000000 +a 1 8209 1000000 +a 8208 2 1000000 +a 16414 2 1000000 diff --git a/examples/simple/bellman_ford.c b/examples/simple/bellman_ford.c new file mode 100644 index 0000000..efd6f72 --- /dev/null +++ b/examples/simple/bellman_ford.c @@ -0,0 +1,92 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + igraph_vector_t weights; + igraph_real_t weights_data_0[] = { 0, 2, 1, 0, 5, 2, 1, 1, 0, 2, 2, 8, 1, 1, 3, 1, 1, 4, 2, 1 }; + igraph_real_t weights_data_1[] = { 6, 7, 8, -4, -2, -3, 9, 2, 7 }; + igraph_real_t weights_data_2[] = { 6, 7, 2, -4, -2, -3, 9, 2, 7 }; + igraph_matrix_t res; + + /* Graph with only positive weights */ + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 4, 1, 5, + 2, 3, 2, 6, 3, 2, 3, 6, + 4, 5, 4, 7, 5, 6, 5, 8, 5, 9, + 7, 5, 7, 8, 8, 9, + 5, 2, + 2, 1, + -1); + + igraph_vector_view(&weights, weights_data_0, + sizeof(weights_data_0) / sizeof(weights_data_0[0])); + + igraph_matrix_init(&res, 0, 0); + igraph_distances_bellman_ford(&g, &res, igraph_vss_all(), igraph_vss_all(), + &weights, IGRAPH_OUT); + igraph_matrix_print(&res); + + igraph_matrix_destroy(&res); + igraph_destroy(&g); + + printf("\n"); + + /***************************************/ + + /* Graph with negative weights */ + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 0, 3, 1, 3, 1, 4, 2, 1, 3, 2, 3, 4, 4, 0, 4, 2, -1); + + igraph_vector_view(&weights, weights_data_1, + sizeof(weights_data_1) / sizeof(weights_data_1[0])); + + igraph_matrix_init(&res, 0, 0); + igraph_distances_bellman_ford(&g, &res, igraph_vss_all(), + igraph_vss_all(), &weights, IGRAPH_OUT); + igraph_matrix_print(&res); + + /***************************************/ + + /* Same graph with negative loop */ + igraph_set_error_handler(igraph_error_handler_ignore); + igraph_vector_view(&weights, weights_data_2, + sizeof(weights_data_2) / sizeof(weights_data_2[0])); + if (igraph_distances_bellman_ford(&g, &res, igraph_vss_all(), + igraph_vss_all(), + &weights, IGRAPH_OUT) != IGRAPH_ENEGLOOP) { + return 1; + } + + igraph_matrix_destroy(&res); + igraph_destroy(&g); + + if (!IGRAPH_FINALLY_STACK_EMPTY) { + return 1; + } + + return 0; +} diff --git a/examples/simple/bellman_ford.out b/examples/simple/bellman_ford.out new file mode 100644 index 0000000..8711a3b --- /dev/null +++ b/examples/simple/bellman_ford.out @@ -0,0 +1,16 @@ + 0 0 0 1 5 2 1 13 3 5 +Inf 0 0 1 5 2 1 13 3 5 +Inf 1 0 1 6 3 1 14 4 6 +Inf 1 0 0 6 3 1 14 4 6 +Inf 5 4 5 0 2 3 8 3 5 +Inf 3 2 3 8 0 1 16 1 3 +Inf Inf Inf Inf Inf Inf 0 Inf Inf Inf +Inf 4 3 4 9 1 2 0 1 4 +Inf Inf Inf Inf Inf Inf Inf Inf 0 4 +Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 + + 0 2 4 7 -2 +-2 0 2 5 -4 +-4 -2 0 3 -6 +-7 -5 -3 0 -9 + 2 4 6 9 0 diff --git a/examples/simple/blas.c b/examples/simple/blas.c new file mode 100644 index 0000000..8f895ef --- /dev/null +++ b/examples/simple/blas.c @@ -0,0 +1,42 @@ + +#include + +int main(void) { + igraph_matrix_t m; + igraph_vector_t x, y, z; + igraph_real_t xz, xx; + + igraph_vector_init_real(&x, 3, 1.0, 2.0, 3.0); + igraph_vector_init_real(&y, 4, 4.0, 5.0, 6.0, 7.0); + igraph_vector_init_real(&z, 3, -1.0, 0.0, 0.5); + + igraph_matrix_init(&m, 4, 3); + MATRIX(m, 0, 0) = 1; + MATRIX(m, 0, 1) = 2; + MATRIX(m, 0, 2) = 3; + MATRIX(m, 1, 0) = 2; + MATRIX(m, 1, 1) = 3; + MATRIX(m, 1, 2) = 4; + MATRIX(m, 2, 0) = 3; + MATRIX(m, 2, 1) = 4; + MATRIX(m, 2, 2) = 5; + MATRIX(m, 3, 0) = 4; + MATRIX(m, 3, 1) = 5; + MATRIX(m, 3, 2) = 6; + + /* Compute 2 m.x + 3 y and store it in y. */ + igraph_blas_dgemv(/* transpose= */ 0, /* alpha= */ 2, &m, &x, /* beta= */ 3, &y); + igraph_vector_print(&y); + + /* Compute the squared norm of x, as well as the dor product of x and z. */ + igraph_blas_ddot(&x, &x, &xx); + igraph_blas_ddot(&x, &z, &xz); + printf("x.x = %g, x.z = %g\n", xx, xz); + + igraph_matrix_destroy(&m); + igraph_vector_destroy(&z); + igraph_vector_destroy(&y); + igraph_vector_destroy(&x); + + return 0; +} diff --git a/examples/simple/blas.out b/examples/simple/blas.out new file mode 100644 index 0000000..345590b --- /dev/null +++ b/examples/simple/blas.out @@ -0,0 +1,2 @@ +40 55 70 85 +x.x = 14, x.z = 0.5 diff --git a/examples/simple/blas_dgemm.c b/examples/simple/blas_dgemm.c new file mode 100644 index 0000000..93585d3 --- /dev/null +++ b/examples/simple/blas_dgemm.c @@ -0,0 +1,27 @@ +#include + +int main(void) { + igraph_matrix_t a, b, c; + + igraph_matrix_init(&a, 2, 2); + MATRIX(a, 0, 0) = 1; + MATRIX(a, 0, 1) = 2; + MATRIX(a, 1, 0) = 3; + MATRIX(a, 1, 1) = 4; + + igraph_matrix_init(&b, 2, 2); + MATRIX(b, 0, 0) = 5; + MATRIX(b, 0, 1) = 6; + MATRIX(b, 1, 0) = 7; + MATRIX(b, 1, 1) = 8; + + igraph_matrix_init(&c, 2, 2); + + igraph_blas_dgemm(1, 1, 0.5, &a, &b, 0, &c); + igraph_matrix_printf(&c, "%g"); + + igraph_matrix_destroy(&a); + igraph_matrix_destroy(&b); + igraph_matrix_destroy(&c); + return 0; +} diff --git a/examples/simple/blas_dgemm.out b/examples/simple/blas_dgemm.out new file mode 100644 index 0000000..718f4a7 --- /dev/null +++ b/examples/simple/blas_dgemm.out @@ -0,0 +1,2 @@ +11.5 15.5 +17 23 diff --git a/examples/simple/cattributes.c b/examples/simple/cattributes.c new file mode 100644 index 0000000..4d84554 --- /dev/null +++ b/examples/simple/cattributes.c @@ -0,0 +1,133 @@ +/* -*- 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 +#include +#include + +/* Prints graph, vertex and edge attributes stored in a graph. */ +void print_attributes(const igraph_t *g) { + igraph_vector_int_t gtypes, vtypes, etypes; + igraph_strvector_t gnames, vnames, enames; + igraph_integer_t i, j; + + igraph_vector_int_init(>ypes, 0); + igraph_vector_int_init(&vtypes, 0); + igraph_vector_int_init(&etypes, 0); + igraph_strvector_init(&gnames, 0); + igraph_strvector_init(&vnames, 0); + igraph_strvector_init(&enames, 0); + + igraph_cattribute_list(g, + &gnames, >ypes, + &vnames, &vtypes, + &enames, &etypes); + + /* graph attributes */ + for (i = 0; i < igraph_strvector_size(&gnames); i++) { + printf("%s=", igraph_strvector_get(&gnames, i)); + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_real_printf(GAN(g, igraph_strvector_get(&gnames, i))); + putchar(' '); + } else { + printf("\"%s\" ", GAS(g, igraph_strvector_get(&gnames, i))); + } + } + printf("\n"); + + /* vertex attributes */ + for (i = 0; i < igraph_vcount(g); i++) { + printf("Vertex %" IGRAPH_PRId ": ", i); + for (j = 0; j < igraph_strvector_size(&vnames); j++) { + printf("%s=", igraph_strvector_get(&vnames, j)); + if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_real_printf(VAN(g, igraph_strvector_get(&vnames, j), i)); + putchar(' '); + } else { + printf("\"%s\" ", VAS(g, igraph_strvector_get(&vnames, j), i)); + } + } + printf("\n"); + } + + /* edge attributes */ + for (i = 0; i < igraph_ecount(g); i++) { + printf("Edge %" IGRAPH_PRId " (%" IGRAPH_PRId "-%" IGRAPH_PRId "): ", i, IGRAPH_FROM(g, i), IGRAPH_TO(g, i)); + for (j = 0; j < igraph_strvector_size(&enames); j++) { + printf("%s=", igraph_strvector_get(&enames, j)); + if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_real_printf(EAN(g, igraph_strvector_get(&enames, j), i)); + putchar(' '); + } else { + printf("\"%s\" ", EAS(g, igraph_strvector_get(&enames, j), i)); + } + } + printf("\n"); + } + + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&gnames); + igraph_vector_int_destroy(&etypes); + igraph_vector_int_destroy(&vtypes); + igraph_vector_int_destroy(>ypes); +} + +int main(void) { + igraph_t graph; + igraph_vector_t y; + + /* Turn on attribute handling. */ + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_small(&graph, 3, IGRAPH_DIRECTED, 0,1, 1,2, -1); + + /* Set graph attributes. */ + /* numeric */ + SETGAN(&graph, "id", 10); + /* string */ + SETGAS(&graph, "name", "toy"); + /* boolean */ + SETGAB(&graph, "is_regular", false); + + /* Set edge string attribute. */ + SETEAS(&graph, "color", 1, "RED"); + + /* Set vertex attributes as vector. */ + igraph_vector_init(&y, igraph_vcount(&graph)); + igraph_vector_fill(&y, 1.23); + SETVANV(&graph, "y", &y); + igraph_vector_destroy(&y); + + /* Set single vertex numeric attribute. */ + SETVAN(&graph, "y", 0, -1); + + /* Delete graph attribute. */ + DELGA(&graph, "is_regular"); + + /* Print the final result. */ + print_attributes(&graph); + + /* Delete all remaining attributes. */ + DELALL(&graph); + + /* Destroy the graph. */ + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/cattributes.out b/examples/simple/cattributes.out new file mode 100644 index 0000000..623aa3a --- /dev/null +++ b/examples/simple/cattributes.out @@ -0,0 +1,6 @@ +id=10 name="toy" +Vertex 0: y=-1 +Vertex 1: y=1.23 +Vertex 2: y=1.23 +Edge 0 (0-1): color="" +Edge 1 (1-2): color="RED" diff --git a/examples/simple/cattributes2.c b/examples/simple/cattributes2.c new file mode 100644 index 0000000..99d8a72 --- /dev/null +++ b/examples/simple/cattributes2.c @@ -0,0 +1,68 @@ +/* -*- mode: C -*- */ +/* IGraph library. + Copyright (C) 2007-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, see . +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_vector_t y; + igraph_warning_handler_t* oldwarnhandler; + + /* Turn on attribute handling. */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* Create a graph, add some attributes and save it as a GraphML file. */ + igraph_famous(&g, "Petersen"); + SETGAS(&g, "name", "Petersen's graph"); + SETGAN(&g, "vertices", igraph_vcount(&g)); + SETGAN(&g, "edges", igraph_ecount(&g)); + SETGAB(&g, "famous", true); + + igraph_vector_init_range(&y, 1, igraph_vcount(&g) + 1); + SETVANV(&g, "id", &y); + igraph_vector_destroy(&y); + + SETVAS(&g, "name", 0, "foo"); + SETVAS(&g, "name", 1, "foobar"); + + SETVAB(&g, "is_first", 0, true); + + igraph_vector_init_range(&y, 1, igraph_ecount(&g) + 1); + SETEANV(&g, "id", &y); + igraph_vector_destroy(&y); + + SETEAS(&g, "name", 0, "FOO"); + SETEAS(&g, "name", 1, "FOOBAR"); + + SETEAB(&g, "is_first", 0, true); + + /* Turn off the warning handler temporarily because the GML writer will + * print warnings about boolean attributes being converted to numbers, and + * we don't care about these. */ + oldwarnhandler = igraph_set_warning_handler(igraph_warning_handler_ignore); + igraph_write_graph_gml(&g, stdout, IGRAPH_WRITE_GML_DEFAULT_SW, 0, ""); + igraph_set_warning_handler(oldwarnhandler); + + /* Back to business. */ + igraph_write_graph_graphml(&g, stdout, /*prefixattr=*/ true); + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/cattributes2.out b/examples/simple/cattributes2.out new file mode 100644 index 0000000..f736f51 --- /dev/null +++ b/examples/simple/cattributes2.out @@ -0,0 +1,337 @@ +Version 1 +graph +[ + directed 0 + name "Petersen's graph" + vertices 10 + edges 15 + famous 1 + node + [ + id 1 + name "foo" + isfirst 1 + ] + node + [ + id 2 + name "foobar" + isfirst 0 + ] + node + [ + id 3 + name "" + isfirst 0 + ] + node + [ + id 4 + name "" + isfirst 0 + ] + node + [ + id 5 + name "" + isfirst 0 + ] + node + [ + id 6 + name "" + isfirst 0 + ] + node + [ + id 7 + name "" + isfirst 0 + ] + node + [ + id 8 + name "" + isfirst 0 + ] + node + [ + id 9 + name "" + isfirst 0 + ] + node + [ + id 10 + name "" + isfirst 0 + ] + edge + [ + source 2 + target 1 + id 1 + name "FOO" + isfirst 1 + ] + edge + [ + source 5 + target 1 + id 2 + name "FOOBAR" + isfirst 0 + ] + edge + [ + source 6 + target 1 + id 3 + name "" + isfirst 0 + ] + edge + [ + source 3 + target 2 + id 4 + name "" + isfirst 0 + ] + edge + [ + source 7 + target 2 + id 5 + name "" + isfirst 0 + ] + edge + [ + source 4 + target 3 + id 6 + name "" + isfirst 0 + ] + edge + [ + source 8 + target 3 + id 7 + name "" + isfirst 0 + ] + edge + [ + source 5 + target 4 + id 8 + name "" + isfirst 0 + ] + edge + [ + source 9 + target 4 + id 9 + name "" + isfirst 0 + ] + edge + [ + source 10 + target 5 + id 10 + name "" + isfirst 0 + ] + edge + [ + source 8 + target 6 + id 11 + name "" + isfirst 0 + ] + edge + [ + source 9 + target 6 + id 12 + name "" + isfirst 0 + ] + edge + [ + source 9 + target 7 + id 13 + name "" + isfirst 0 + ] + edge + [ + source 10 + target 7 + id 14 + name "" + isfirst 0 + ] + edge + [ + source 10 + target 8 + id 15 + name "" + isfirst 0 + ] +] + + + + + + + + + + + + + + + Petersen's graph + 10 + 15 + true + + 1 + foo + true + + + 2 + foobar + false + + + 3 + + false + + + 4 + + false + + + 5 + + false + + + 6 + + false + + + 7 + + false + + + 8 + + false + + + 9 + + false + + + 10 + + false + + + 1 + FOO + true + + + 2 + FOOBAR + false + + + 3 + + false + + + 4 + + false + + + 5 + + false + + + 6 + + false + + + 7 + + false + + + 8 + + false + + + 9 + + false + + + 10 + + false + + + 11 + + false + + + 12 + + false + + + 13 + + false + + + 14 + + false + + + 15 + + false + + + diff --git a/examples/simple/cattributes3.c b/examples/simple/cattributes3.c new file mode 100644 index 0000000..ed0f804 --- /dev/null +++ b/examples/simple/cattributes3.c @@ -0,0 +1,97 @@ +/* -*- 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_error_t mf(const igraph_vector_t *input, igraph_real_t *output) { + *output = 0.0; + return IGRAPH_SUCCESS; +} + +static void simplify_write_destroy(igraph_t *g, igraph_attribute_combination_t *comb) { + igraph_simplify(g, /*remove_multiple=*/ true, /*remove_loops=*/ true, comb); + igraph_write_graph_graphml(g, stdout, /*prefixattr=*/ true); + igraph_attribute_combination_destroy(comb); + igraph_destroy(g); +} + +static void weight_test(igraph_t *g, igraph_attribute_combination_type_t weight_attr) { + igraph_t g2; + igraph_attribute_combination_t comb; + + igraph_copy(&g2, g); + igraph_attribute_combination(&comb, + "weight", weight_attr, + "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); +} + +int main(void) { + + igraph_t g, g2; + igraph_vector_t weight; + igraph_attribute_combination_t comb; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_small(&g, 4, IGRAPH_DIRECTED, + 0, 1, 0, 1, 0, 1, + 1, 2, 2, 3, + -1); + + igraph_vector_init_range(&weight, 1, igraph_ecount(&g) + 1); + SETEANV(&g, "weight", &weight); + igraph_vector_destroy(&weight); + + weight_test(&g, IGRAPH_ATTRIBUTE_COMBINE_SUM); + weight_test(&g, IGRAPH_ATTRIBUTE_COMBINE_PROD); + weight_test(&g, IGRAPH_ATTRIBUTE_COMBINE_MIN); + weight_test(&g, IGRAPH_ATTRIBUTE_COMBINE_MAX); + weight_test(&g, IGRAPH_ATTRIBUTE_COMBINE_FIRST); + weight_test(&g, IGRAPH_ATTRIBUTE_COMBINE_LAST); + weight_test(&g, IGRAPH_ATTRIBUTE_COMBINE_MEAN); + + /* ****************************************************** */ + + igraph_copy(&g2, &g); + igraph_attribute_combination(&comb, + "weight", IGRAPH_ATTRIBUTE_COMBINE_FUNCTION, mf, + "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); + + /* ****************************************************** */ + + igraph_copy(&g2, &g); + igraph_attribute_combination(&comb, + "", IGRAPH_ATTRIBUTE_COMBINE_MEAN, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); + + /* ****************************************************** */ + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/cattributes3.out b/examples/simple/cattributes3.out new file mode 100644 index 0000000..de970eb --- /dev/null +++ b/examples/simple/cattributes3.out @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + 6 + + + 4 + + + 5 + + + + + + + + + + + + + + + + + + 6 + + + 4 + + + 5 + + + + + + + + + + + + + + + + + + 1 + + + 4 + + + 5 + + + + + + + + + + + + + + + + + + 3 + + + 4 + + + 5 + + + + + + + + + + + + + + + + + + 1 + + + 4 + + + 5 + + + + + + + + + + + + + + + + + + 3 + + + 4 + + + 5 + + + + + + + + + + + + + + + + + + 2 + + + 4 + + + 5 + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + 2 + + + 4 + + + 5 + + + diff --git a/examples/simple/cattributes4.c b/examples/simple/cattributes4.c new file mode 100644 index 0000000..e6a21f9 --- /dev/null +++ b/examples/simple/cattributes4.c @@ -0,0 +1,82 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + +static void simplify_write_destroy(igraph_t *g, igraph_attribute_combination_t *comb) { + igraph_simplify(g, /*remove_multiple=*/ true, /*remove_loops=*/ true, comb); + igraph_write_graph_graphml(g, stdout, /*prefixattr=*/ true); + igraph_attribute_combination_destroy(comb); + igraph_destroy(g); +} + +int main(void) { + + igraph_t g, g2; + igraph_attribute_combination_t comb; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_small(&g, 4, IGRAPH_DIRECTED, + 0, 1, 0, 1, 0, 1, + 1, 2, 2, 3, + -1); + + SETEAS(&g, "color", 0, "green"); + SETEAS(&g, "color", 1, "red"); + SETEAS(&g, "color", 2, "blue"); + SETEAS(&g, "color", 3, "white"); + SETEAS(&g, "color", 4, "black"); + + /* ****************************************************** */ + + igraph_copy(&g2, &g); + igraph_attribute_combination(&comb, + "weight", IGRAPH_ATTRIBUTE_COMBINE_SUM, + "color", IGRAPH_ATTRIBUTE_COMBINE_FIRST, + "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); + + /* ****************************************************** */ + + igraph_copy(&g2, &g); + igraph_attribute_combination(&comb, + "", IGRAPH_ATTRIBUTE_COMBINE_LAST, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); + + /* ****************************************************** */ + + igraph_copy(&g2, &g); + igraph_attribute_combination(&comb, + "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, + "color", IGRAPH_ATTRIBUTE_COMBINE_CONCAT, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); + + /* ****************************************************** */ + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/cattributes4.out b/examples/simple/cattributes4.out new file mode 100644 index 0000000..bc3b76b --- /dev/null +++ b/examples/simple/cattributes4.out @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + green + + + white + + + black + + + + + + + + + + + + + + + + + + blue + + + white + + + black + + + + + + + + + + + + + + + + + + greenredblue + + + green + + + green + + + diff --git a/examples/simple/celegansneural.gml b/examples/simple/celegansneural.gml new file mode 100644 index 0000000..98697f4 --- /dev/null +++ b/examples/simple/celegansneural.gml @@ -0,0 +1,15644 @@ +Creator "Mark Newman on Thu Aug 31 12:59:09 2006" +graph +[ + directed 1 + node + [ + id 0 + label "1" + ] + node + [ + id 1 + label "51" + ] + node + [ + id 2 + label "72" + ] + node + [ + id 3 + label "77" + ] + node + [ + id 4 + label "78" + ] + node + [ + id 5 + label "2" + ] + node + [ + id 6 + label "90" + ] + node + [ + id 7 + label "92" + ] + node + [ + id 8 + label "158" + ] + node + [ + id 9 + label "159" + ] + node + [ + id 10 + label "113" + ] + node + [ + id 11 + label "69" + ] + node + [ + id 12 + label "71" + ] + node + [ + id 13 + label "89" + ] + node + [ + id 14 + label "91" + ] + node + [ + id 15 + label "3" + ] + node + [ + id 16 + label "47" + ] + node + [ + id 17 + label "9" + ] + node + [ + id 18 + label "17" + ] + node + [ + id 19 + label "21" + ] + node + [ + id 20 + label "93" + ] + node + [ + id 21 + label "94" + ] + node + [ + id 22 + label "23" + ] + node + [ + id 23 + label "121" + ] + node + [ + id 24 + label "125" + ] + node + [ + id 25 + label "131" + ] + node + [ + id 26 + label "31" + ] + node + [ + id 27 + label "4" + ] + node + [ + id 28 + label "60" + ] + node + [ + id 29 + label "10" + ] + node + [ + id 30 + label "16" + ] + node + [ + id 31 + label "18" + ] + node + [ + id 32 + label "22" + ] + node + [ + id 33 + label "24" + ] + node + [ + id 34 + label "97" + ] + node + [ + id 35 + label "122" + ] + node + [ + id 36 + label "126" + ] + node + [ + id 37 + label "132" + ] + node + [ + id 38 + label "32" + ] + node + [ + id 39 + label "303" + ] + node + [ + id 40 + label "5" + ] + node + [ + id 41 + label "7" + ] + node + [ + id 42 + label "222" + ] + node + [ + id 43 + label "101" + ] + node + [ + id 44 + label "305" + ] + node + [ + id 45 + label "6" + ] + node + [ + id 46 + label "102" + ] + node + [ + id 47 + label "99" + ] + node + [ + id 48 + label "100" + ] + node + [ + id 49 + label "27" + ] + node + [ + id 50 + label "8" + ] + node + [ + id 51 + label "26" + ] + node + [ + id 52 + label "44" + ] + node + [ + id 53 + label "37" + ] + node + [ + id 54 + label "11" + ] + node + [ + id 55 + label "19" + ] + node + [ + id 56 + label "29" + ] + node + [ + id 57 + label "12" + ] + node + [ + id 58 + label "41" + ] + node + [ + id 59 + label "118" + ] + node + [ + id 60 + label "25" + ] + node + [ + id 61 + label "30" + ] + node + [ + id 62 + label "13" + ] + node + [ + id 63 + label "143" + ] + node + [ + id 64 + label "28" + ] + node + [ + id 65 + label "43" + ] + node + [ + id 66 + label "14" + ] + node + [ + id 67 + label "144" + ] + node + [ + id 68 + label "20" + ] + node + [ + id 69 + label "34" + ] + node + [ + id 70 + label "15" + ] + node + [ + id 71 + label "40" + ] + node + [ + id 72 + label "128" + ] + node + [ + id 73 + label "139" + ] + node + [ + id 74 + label "140" + ] + node + [ + id 75 + label "108" + ] + node + [ + id 76 + label "35" + ] + node + [ + id 77 + label "107" + ] + node + [ + id 78 + label "133" + ] + node + [ + id 79 + label "134" + ] + node + [ + id 80 + label "105" + ] + node + [ + id 81 + label "106" + ] + node + [ + id 82 + label "36" + ] + node + [ + id 83 + label "33" + ] + node + [ + id 84 + label "73" + ] + node + [ + id 85 + label "136" + ] + node + [ + id 86 + label "74" + ] + node + [ + id 87 + label "161" + ] + node + [ + id 88 + label "129" + ] + node + [ + id 89 + label "135" + ] + node + [ + id 90 + label "120" + ] + node + [ + id 91 + label "38" + ] + node + [ + id 92 + label "39" + ] + node + [ + id 93 + label "160" + ] + node + [ + id 94 + label "130" + ] + node + [ + id 95 + label "174" + ] + node + [ + id 96 + label "42" + ] + node + [ + id 97 + label "189" + ] + node + [ + id 98 + label "82" + ] + node + [ + id 99 + label "70" + ] + node + [ + id 100 + label "45" + ] + node + [ + id 101 + label "141" + ] + node + [ + id 102 + label "55" + ] + node + [ + id 103 + label "119" + ] + node + [ + id 104 + label "137" + ] + node + [ + id 105 + label "46" + ] + node + [ + id 106 + label "142" + ] + node + [ + id 107 + label "114" + ] + node + [ + id 108 + label "56" + ] + node + [ + id 109 + label "62" + ] + node + [ + id 110 + label "86" + ] + node + [ + id 111 + label "193" + ] + node + [ + id 112 + label "138" + ] + node + [ + id 113 + label "109" + ] + node + [ + id 114 + label "52" + ] + node + [ + id 115 + label "58" + ] + node + [ + id 116 + label "61" + ] + node + [ + id 117 + label "75" + ] + node + [ + id 118 + label "76" + ] + node + [ + id 119 + label "81" + ] + node + [ + id 120 + label "85" + ] + node + [ + id 121 + label "48" + ] + node + [ + id 122 + label "110" + ] + node + [ + id 123 + label "80" + ] + node + [ + id 124 + label "88" + ] + node + [ + id 125 + label "216" + ] + node + [ + id 126 + label "49" + ] + node + [ + id 127 + label "54" + ] + node + [ + id 128 + label "50" + ] + node + [ + id 129 + label "154" + ] + node + [ + id 130 + label "96" + ] + node + [ + id 131 + label "127" + ] + node + [ + id 132 + label "95" + ] + node + [ + id 133 + label "166" + ] + node + [ + id 134 + label "53" + ] + node + [ + id 135 + label "57" + ] + node + [ + id 136 + label "63" + ] + node + [ + id 137 + label "198" + ] + node + [ + id 138 + label "87" + ] + node + [ + id 139 + label "84" + ] + node + [ + id 140 + label "59" + ] + node + [ + id 141 + label "67" + ] + node + [ + id 142 + label "178" + ] + node + [ + id 143 + label "64" + ] + node + [ + id 144 + label "65" + ] + node + [ + id 145 + label "220" + ] + node + [ + id 146 + label "66" + ] + node + [ + id 147 + label "68" + ] + node + [ + id 148 + label "221" + ] + node + [ + id 149 + label "111" + ] + node + [ + id 150 + label "112" + ] + node + [ + id 151 + label "146" + ] + node + [ + id 152 + label "225" + ] + node + [ + id 153 + label "186" + ] + node + [ + id 154 + label "226" + ] + node + [ + id 155 + label "227" + ] + node + [ + id 156 + label "228" + ] + node + [ + id 157 + label "229" + ] + node + [ + id 158 + label "230" + ] + node + [ + id 159 + label "150" + ] + node + [ + id 160 + label "234" + ] + node + [ + id 161 + label "235" + ] + node + [ + id 162 + label "236" + ] + node + [ + id 163 + label "237" + ] + node + [ + id 164 + label "238" + ] + node + [ + id 165 + label "239" + ] + node + [ + id 166 + label "187" + ] + node + [ + id 167 + label "188" + ] + node + [ + id 168 + label "240" + ] + node + [ + id 169 + label "242" + ] + node + [ + id 170 + label "203" + ] + node + [ + id 171 + label "179" + ] + node + [ + id 172 + label "217" + ] + node + [ + id 173 + label "162" + ] + node + [ + id 174 + label "164" + ] + node + [ + id 175 + label "249" + ] + node + [ + id 176 + label "250" + ] + node + [ + id 177 + label "195" + ] + node + [ + id 178 + label "251" + ] + node + [ + id 179 + label "252" + ] + node + [ + id 180 + label "253" + ] + node + [ + id 181 + label "254" + ] + node + [ + id 182 + label "255" + ] + node + [ + id 183 + label "197" + ] + node + [ + id 184 + label "204" + ] + node + [ + id 185 + label "180" + ] + node + [ + id 186 + label "163" + ] + node + [ + id 187 + label "117" + ] + node + [ + id 188 + label "256" + ] + node + [ + id 189 + label "276" + ] + node + [ + id 190 + label "306" + ] + node + [ + id 191 + label "177" + ] + node + [ + id 192 + label "241" + ] + node + [ + id 193 + label "200" + ] + node + [ + id 194 + label "215" + ] + node + [ + id 195 + label "199" + ] + node + [ + id 196 + label "165" + ] + node + [ + id 197 + label "169" + ] + node + [ + id 198 + label "79" + ] + node + [ + id 199 + label "148" + ] + node + [ + id 200 + label "168" + ] + node + [ + id 201 + label "145" + ] + node + [ + id 202 + label "147" + ] + node + [ + id 203 + label "192" + ] + node + [ + id 204 + label "219" + ] + node + [ + id 205 + label "157" + ] + node + [ + id 206 + label "172" + ] + node + [ + id 207 + label "218" + ] + node + [ + id 208 + label "83" + ] + node + [ + id 209 + label "98" + ] + node + [ + id 210 + label "124" + ] + node + [ + id 211 + label "103" + ] + node + [ + id 212 + label "104" + ] + node + [ + id 213 + label "115" + ] + node + [ + id 214 + label "123" + ] + node + [ + id 215 + label "156" + ] + node + [ + id 216 + label "170" + ] + node + [ + id 217 + label "183" + ] + node + [ + id 218 + label "116" + ] + node + [ + id 219 + label "153" + ] + node + [ + id 220 + label "224" + ] + node + [ + id 221 + label "196" + ] + node + [ + id 222 + label "214" + ] + node + [ + id 223 + label "213" + ] + node + [ + id 224 + label "155" + ] + node + [ + id 225 + label "173" + ] + node + [ + id 226 + label "149" + ] + node + [ + id 227 + label "275" + ] + node + [ + id 228 + label "205" + ] + node + [ + id 229 + label "206" + ] + node + [ + id 230 + label "151" + ] + node + [ + id 231 + label "277" + ] + node + [ + id 232 + label "152" + ] + node + [ + id 233 + label "245" + ] + node + [ + id 234 + label "278" + ] + node + [ + id 235 + label "201" + ] + node + [ + id 236 + label "202" + ] + node + [ + id 237 + label "167" + ] + node + [ + id 238 + label "269" + ] + node + [ + id 239 + label "184" + ] + node + [ + id 240 + label "171" + ] + node + [ + id 241 + label "185" + ] + node + [ + id 242 + label "175" + ] + node + [ + id 243 + label "176" + ] + node + [ + id 244 + label "272" + ] + node + [ + id 245 + label "270" + ] + node + [ + id 246 + label "258" + ] + node + [ + id 247 + label "181" + ] + node + [ + id 248 + label "182" + ] + node + [ + id 249 + label "274" + ] + node + [ + id 250 + label "190" + ] + node + [ + id 251 + label "191" + ] + node + [ + id 252 + label "194" + ] + node + [ + id 253 + label "244" + ] + node + [ + id 254 + label "260" + ] + node + [ + id 255 + label "207" + ] + node + [ + id 256 + label "223" + ] + node + [ + id 257 + label "208" + ] + node + [ + id 258 + label "209" + ] + node + [ + id 259 + label "210" + ] + node + [ + id 260 + label "211" + ] + node + [ + id 261 + label "212" + ] + node + [ + id 262 + label "261" + ] + node + [ + id 263 + label "262" + ] + node + [ + id 264 + label "263" + ] + node + [ + id 265 + label "264" + ] + node + [ + id 266 + label "265" + ] + node + [ + id 267 + label "266" + ] + node + [ + id 268 + label "282" + ] + node + [ + id 269 + label "279" + ] + node + [ + id 270 + label "231" + ] + node + [ + id 271 + label "280" + ] + node + [ + id 272 + label "232" + ] + node + [ + id 273 + label "281" + ] + node + [ + id 274 + label "233" + ] + node + [ + id 275 + label "246" + ] + node + [ + id 276 + label "247" + ] + node + [ + id 277 + label "248" + ] + node + [ + id 278 + label "243" + ] + node + [ + id 279 + label "257" + ] + node + [ + id 280 + label "259" + ] + node + [ + id 281 + label "267" + ] + node + [ + id 282 + label "268" + ] + node + [ + id 283 + label "271" + ] + node + [ + id 284 + label "273" + ] + node + [ + id 285 + label "291" + ] + node + [ + id 286 + label "292" + ] + node + [ + id 287 + label "293" + ] + node + [ + id 288 + label "294" + ] + node + [ + id 289 + label "295" + ] + node + [ + id 290 + label "296" + ] + node + [ + id 291 + label "297" + ] + node + [ + id 292 + label "298" + ] + node + [ + id 293 + label "299" + ] + node + [ + id 294 + label "300" + ] + node + [ + id 295 + label "301" + ] + node + [ + id 296 + label "302" + ] + edge + [ + source 0 + target 1 + value 1 + ] + edge + [ + source 0 + target 2 + value 2 + ] + edge + [ + source 0 + target 3 + value 1 + ] + edge + [ + source 0 + target 4 + value 2 + ] + edge + [ + source 0 + target 5 + value 1 + ] + edge + [ + source 0 + target 6 + value 6 + ] + edge + [ + source 0 + target 7 + value 6 + ] + edge + [ + source 0 + target 8 + value 1 + ] + edge + [ + source 0 + target 9 + value 4 + ] + edge + [ + source 1 + target 10 + value 1 + ] + edge + [ + source 1 + target 115 + value 1 + ] + edge + [ + source 1 + target 12 + value 2 + ] + edge + [ + source 1 + target 84 + value 3 + ] + edge + [ + source 1 + target 129 + value 1 + ] + edge + [ + source 1 + target 7 + value 2 + ] + edge + [ + source 1 + target 130 + value 12 + ] + edge + [ + source 1 + target 131 + value 1 + ] + edge + [ + source 1 + target 72 + value 2 + ] + edge + [ + source 1 + target 74 + value 1 + ] + edge + [ + source 2 + target 67 + value 1 + ] + edge + [ + source 2 + target 151 + value 1 + ] + edge + [ + source 2 + target 152 + value 4 + ] + edge + [ + source 2 + target 153 + value 9 + ] + edge + [ + source 2 + target 154 + value 3 + ] + edge + [ + source 2 + target 155 + value 2 + ] + edge + [ + source 2 + target 156 + value 2 + ] + edge + [ + source 2 + target 157 + value 4 + ] + edge + [ + source 2 + target 158 + value 2 + ] + edge + [ + source 2 + target 12 + value 3 + ] + edge + [ + source 2 + target 86 + value 2 + ] + edge + [ + source 2 + target 118 + value 2 + ] + edge + [ + source 2 + target 4 + value 4 + ] + edge + [ + source 2 + target 159 + value 4 + ] + edge + [ + source 2 + target 160 + value 4 + ] + edge + [ + source 2 + target 161 + value 9 + ] + edge + [ + source 2 + target 162 + value 12 + ] + edge + [ + source 2 + target 163 + value 9 + ] + edge + [ + source 2 + target 164 + value 2 + ] + edge + [ + source 2 + target 165 + value 7 + ] + edge + [ + source 2 + target 166 + value 14 + ] + edge + [ + source 2 + target 167 + value 5 + ] + edge + [ + source 2 + target 168 + value 2 + ] + edge + [ + source 2 + target 169 + value 2 + ] + edge + [ + source 2 + target 184 + value 5 + ] + edge + [ + source 2 + target 185 + value 2 + ] + edge + [ + source 2 + target 125 + value 28 + ] + edge + [ + source 2 + target 8 + value 1 + ] + edge + [ + source 2 + target 173 + value 1 + ] + edge + [ + source 2 + target 186 + value 2 + ] + edge + [ + source 2 + target 175 + value 10 + ] + edge + [ + source 2 + target 176 + value 14 + ] + edge + [ + source 2 + target 177 + value 2 + ] + edge + [ + source 2 + target 178 + value 3 + ] + edge + [ + source 2 + target 179 + value 4 + ] + edge + [ + source 2 + target 180 + value 4 + ] + edge + [ + source 2 + target 181 + value 6 + ] + edge + [ + source 2 + target 182 + value 4 + ] + edge + [ + source 2 + target 183 + value 2 + ] + edge + [ + source 3 + target 151 + value 5 + ] + edge + [ + source 3 + target 154 + value 2 + ] + edge + [ + source 3 + target 155 + value 1 + ] + edge + [ + source 3 + target 12 + value 44 + ] + edge + [ + source 3 + target 117 + value 1 + ] + edge + [ + source 3 + target 159 + value 12 + ] + edge + [ + source 3 + target 160 + value 6 + ] + edge + [ + source 3 + target 161 + value 4 + ] + edge + [ + source 3 + target 162 + value 1 + ] + edge + [ + source 3 + target 168 + value 2 + ] + edge + [ + source 3 + target 195 + value 1 + ] + edge + [ + source 3 + target 125 + value 1 + ] + edge + [ + source 3 + target 173 + value 8 + ] + edge + [ + source 3 + target 186 + value 17 + ] + edge + [ + source 3 + target 196 + value 6 + ] + edge + [ + source 3 + target 178 + value 2 + ] + edge + [ + source 3 + target 179 + value 5 + ] + edge + [ + source 3 + target 180 + value 1 + ] + edge + [ + source 3 + target 182 + value 1 + ] + edge + [ + source 3 + target 197 + value 1 + ] + edge + [ + source 3 + target 189 + value 1 + ] + edge + [ + source 4 + target 151 + value 5 + ] + edge + [ + source 4 + target 154 + value 2 + ] + edge + [ + source 4 + target 155 + value 1 + ] + edge + [ + source 4 + target 2 + value 44 + ] + edge + [ + source 4 + target 118 + value 1 + ] + edge + [ + source 4 + target 159 + value 12 + ] + edge + [ + source 4 + target 160 + value 6 + ] + edge + [ + source 4 + target 161 + value 4 + ] + edge + [ + source 4 + target 162 + value 1 + ] + edge + [ + source 4 + target 168 + value 2 + ] + edge + [ + source 4 + target 195 + value 1 + ] + edge + [ + source 4 + target 172 + value 1 + ] + edge + [ + source 4 + target 173 + value 8 + ] + edge + [ + source 4 + target 174 + value 17 + ] + edge + [ + source 4 + target 196 + value 6 + ] + edge + [ + source 4 + target 178 + value 2 + ] + edge + [ + source 4 + target 179 + value 5 + ] + edge + [ + source 4 + target 180 + value 1 + ] + edge + [ + source 4 + target 182 + value 1 + ] + edge + [ + source 4 + target 197 + value 1 + ] + edge + [ + source 4 + target 189 + value 1 + ] + edge + [ + source 5 + target 10 + value 1 + ] + edge + [ + source 5 + target 11 + value 1 + ] + edge + [ + source 5 + target 12 + value 3 + ] + edge + [ + source 5 + target 3 + value 4 + ] + edge + [ + source 5 + target 13 + value 4 + ] + edge + [ + source 5 + target 14 + value 4 + ] + edge + [ + source 5 + target 8 + value 7 + ] + edge + [ + source 6 + target 27 + value 1 + ] + edge + [ + source 6 + target 50 + value 1 + ] + edge + [ + source 6 + target 34 + value 2 + ] + edge + [ + source 6 + target 23 + value 12 + ] + edge + [ + source 6 + target 35 + value 8 + ] + edge + [ + source 6 + target 47 + value 5 + ] + edge + [ + source 6 + target 48 + value 7 + ] + edge + [ + source 6 + target 43 + value 4 + ] + edge + [ + source 6 + target 46 + value 9 + ] + edge + [ + source 6 + target 72 + value 1 + ] + edge + [ + source 6 + target 88 + value 1 + ] + edge + [ + source 6 + target 94 + value 1 + ] + edge + [ + source 6 + target 25 + value 1 + ] + edge + [ + source 6 + target 73 + value 6 + ] + edge + [ + source 6 + target 74 + value 10 + ] + edge + [ + source 6 + target 77 + value 13 + ] + edge + [ + source 6 + target 75 + value 7 + ] + edge + [ + source 7 + target 1 + value 1 + ] + edge + [ + source 7 + target 2 + value 2 + ] + edge + [ + source 7 + target 3 + value 4 + ] + edge + [ + source 7 + target 6 + value 2 + ] + edge + [ + source 7 + target 59 + value 1 + ] + edge + [ + source 7 + target 73 + value 1 + ] + edge + [ + source 8 + target 102 + value 2 + ] + edge + [ + source 8 + target 108 + value 1 + ] + edge + [ + source 8 + target 235 + value 1 + ] + edge + [ + source 8 + target 3 + value 1 + ] + edge + [ + source 8 + target 4 + value 2 + ] + edge + [ + source 8 + target 213 + value 1 + ] + edge + [ + source 8 + target 5 + value 2 + ] + edge + [ + source 8 + target 193 + value 1 + ] + edge + [ + source 8 + target 14 + value 1 + ] + edge + [ + source 8 + target 103 + value 2 + ] + edge + [ + source 8 + target 214 + value 1 + ] + edge + [ + source 8 + target 36 + value 3 + ] + edge + [ + source 9 + target 108 + value 2 + ] + edge + [ + source 9 + target 236 + value 1 + ] + edge + [ + source 9 + target 4 + value 2 + ] + edge + [ + source 9 + target 213 + value 4 + ] + edge + [ + source 9 + target 218 + value 1 + ] + edge + [ + source 9 + target 5 + value 1 + ] + edge + [ + source 9 + target 7 + value 1 + ] + edge + [ + source 9 + target 103 + value 1 + ] + edge + [ + source 9 + target 24 + value 4 + ] + edge + [ + source 10 + target 102 + value 13 + ] + edge + [ + source 10 + target 208 + value 3 + ] + edge + [ + source 10 + target 138 + value 1 + ] + edge + [ + source 10 + target 124 + value 1 + ] + edge + [ + source 10 + target 142 + value 1 + ] + edge + [ + source 10 + target 13 + value 5 + ] + edge + [ + source 10 + target 14 + value 5 + ] + edge + [ + source 11 + target 12 + value 2 + ] + edge + [ + source 11 + target 118 + value 1 + ] + edge + [ + source 11 + target 3 + value 4 + ] + edge + [ + source 11 + target 13 + value 6 + ] + edge + [ + source 11 + target 14 + value 9 + ] + edge + [ + source 12 + target 16 + value 1 + ] + edge + [ + source 12 + target 151 + value 1 + ] + edge + [ + source 12 + target 152 + value 4 + ] + edge + [ + source 12 + target 153 + value 9 + ] + edge + [ + source 12 + target 154 + value 3 + ] + edge + [ + source 12 + target 155 + value 2 + ] + edge + [ + source 12 + target 156 + value 2 + ] + edge + [ + source 12 + target 157 + value 4 + ] + edge + [ + source 12 + target 158 + value 2 + ] + edge + [ + source 12 + target 2 + value 3 + ] + edge + [ + source 12 + target 84 + value 2 + ] + edge + [ + source 12 + target 117 + value 2 + ] + edge + [ + source 12 + target 3 + value 4 + ] + edge + [ + source 12 + target 159 + value 4 + ] + edge + [ + source 12 + target 160 + value 4 + ] + edge + [ + source 12 + target 161 + value 9 + ] + edge + [ + source 12 + target 162 + value 12 + ] + edge + [ + source 12 + target 163 + value 9 + ] + edge + [ + source 12 + target 164 + value 2 + ] + edge + [ + source 12 + target 165 + value 7 + ] + edge + [ + source 12 + target 166 + value 14 + ] + edge + [ + source 12 + target 167 + value 5 + ] + edge + [ + source 12 + target 168 + value 1 + ] + edge + [ + source 12 + target 168 + value 2 + ] + edge + [ + source 12 + target 169 + value 2 + ] + edge + [ + source 12 + target 170 + value 5 + ] + edge + [ + source 12 + target 171 + value 2 + ] + edge + [ + source 12 + target 172 + value 28 + ] + edge + [ + source 12 + target 173 + value 1 + ] + edge + [ + source 12 + target 174 + value 2 + ] + edge + [ + source 12 + target 175 + value 10 + ] + edge + [ + source 12 + target 176 + value 14 + ] + edge + [ + source 12 + target 177 + value 2 + ] + edge + [ + source 12 + target 178 + value 3 + ] + edge + [ + source 12 + target 179 + value 4 + ] + edge + [ + source 12 + target 180 + value 4 + ] + edge + [ + source 12 + target 181 + value 6 + ] + edge + [ + source 12 + target 182 + value 4 + ] + edge + [ + source 12 + target 183 + value 2 + ] + edge + [ + source 13 + target 6 + value 1 + ] + edge + [ + source 13 + target 34 + value 2 + ] + edge + [ + source 13 + target 209 + value 3 + ] + edge + [ + source 13 + target 23 + value 10 + ] + edge + [ + source 13 + target 35 + value 6 + ] + edge + [ + source 13 + target 47 + value 6 + ] + edge + [ + source 13 + target 48 + value 5 + ] + edge + [ + source 13 + target 43 + value 7 + ] + edge + [ + source 13 + target 46 + value 10 + ] + edge + [ + source 13 + target 88 + value 1 + ] + edge + [ + source 13 + target 73 + value 9 + ] + edge + [ + source 13 + target 74 + value 8 + ] + edge + [ + source 13 + target 77 + value 3 + ] + edge + [ + source 13 + target 75 + value 12 + ] + edge + [ + source 14 + target 114 + value 1 + ] + edge + [ + source 14 + target 11 + value 1 + ] + edge + [ + source 14 + target 12 + value 1 + ] + edge + [ + source 14 + target 86 + value 1 + ] + edge + [ + source 14 + target 118 + value 1 + ] + edge + [ + source 14 + target 3 + value 2 + ] + edge + [ + source 14 + target 4 + value 5 + ] + edge + [ + source 14 + target 13 + value 3 + ] + edge + [ + source 14 + target 75 + value 2 + ] + edge + [ + source 15 + target 16 + value 1 + ] + edge + [ + source 15 + target 4 + value 3 + ] + edge + [ + source 15 + target 17 + value 2 + ] + edge + [ + source 15 + target 18 + value 4 + ] + edge + [ + source 15 + target 19 + value 5 + ] + edge + [ + source 15 + target 20 + value 6 + ] + edge + [ + source 15 + target 21 + value 4 + ] + edge + [ + source 15 + target 22 + value 1 + ] + edge + [ + source 15 + target 23 + value 3 + ] + edge + [ + source 15 + target 24 + value 1 + ] + edge + [ + source 15 + target 25 + value 2 + ] + edge + [ + source 15 + target 26 + value 2 + ] + edge + [ + source 16 + target 113 + value 5 + ] + edge + [ + source 16 + target 1 + value 6 + ] + edge + [ + source 16 + target 114 + value 1 + ] + edge + [ + source 16 + target 92 + value 1 + ] + edge + [ + source 16 + target 115 + value 3 + ] + edge + [ + source 16 + target 116 + value 2 + ] + edge + [ + source 16 + target 12 + value 2 + ] + edge + [ + source 16 + target 2 + value 4 + ] + edge + [ + source 16 + target 84 + value 1 + ] + edge + [ + source 16 + target 117 + value 1 + ] + edge + [ + source 16 + target 118 + value 4 + ] + edge + [ + source 16 + target 119 + value 1 + ] + edge + [ + source 16 + target 98 + value 2 + ] + edge + [ + source 16 + target 120 + value 2 + ] + edge + [ + source 16 + target 19 + value 1 + ] + edge + [ + source 16 + target 22 + value 1 + ] + edge + [ + source 16 + target 73 + value 2 + ] + edge + [ + source 17 + target 41 + value 1 + ] + edge + [ + source 17 + target 22 + value 7 + ] + edge + [ + source 17 + target 23 + value 11 + ] + edge + [ + source 17 + target 53 + value 1 + ] + edge + [ + source 17 + target 44 + value 10 + ] + edge + [ + source 18 + target 4 + value 19 + ] + edge + [ + source 18 + target 71 + value 2 + ] + edge + [ + source 18 + target 15 + value 2 + ] + edge + [ + source 18 + target 17 + value 1 + ] + edge + [ + source 18 + target 14 + value 7 + ] + edge + [ + source 18 + target 23 + value 7 + ] + edge + [ + source 18 + target 35 + value 1 + ] + edge + [ + source 18 + target 47 + value 2 + ] + edge + [ + source 18 + target 43 + value 1 + ] + edge + [ + source 18 + target 51 + value 2 + ] + edge + [ + source 18 + target 72 + value 1 + ] + edge + [ + source 18 + target 73 + value 3 + ] + edge + [ + source 18 + target 74 + value 4 + ] + edge + [ + source 18 + target 75 + value 4 + ] + edge + [ + source 18 + target 76 + value 1 + ] + edge + [ + source 19 + target 17 + value 2 + ] + edge + [ + source 19 + target 70 + value 1 + ] + edge + [ + source 19 + target 20 + value 1 + ] + edge + [ + source 19 + target 59 + value 2 + ] + edge + [ + source 19 + target 22 + value 2 + ] + edge + [ + source 19 + target 46 + value 4 + ] + edge + [ + source 19 + target 80 + value 3 + ] + edge + [ + source 20 + target 12 + value 5 + ] + edge + [ + source 20 + target 2 + value 6 + ] + edge + [ + source 20 + target 132 + value 1 + ] + edge + [ + source 20 + target 130 + value 2 + ] + edge + [ + source 20 + target 209 + value 1 + ] + edge + [ + source 20 + target 210 + value 1 + ] + edge + [ + source 20 + target 89 + value 2 + ] + edge + [ + source 20 + target 73 + value 3 + ] + edge + [ + source 20 + target 74 + value 4 + ] + edge + [ + source 20 + target 75 + value 1 + ] + edge + [ + source 21 + target 106 + value 1 + ] + edge + [ + source 21 + target 12 + value 5 + ] + edge + [ + source 21 + target 2 + value 3 + ] + edge + [ + source 21 + target 85 + value 1 + ] + edge + [ + source 21 + target 73 + value 2 + ] + edge + [ + source 21 + target 74 + value 3 + ] + edge + [ + source 21 + target 77 + value 2 + ] + edge + [ + source 21 + target 75 + value 1 + ] + edge + [ + source 22 + target 55 + value 2 + ] + edge + [ + source 23 + target 46 + value 7 + ] + edge + [ + source 23 + target 44 + value 8 + ] + edge + [ + source 24 + target 48 + value 1 + ] + edge + [ + source 24 + target 44 + value 8 + ] + edge + [ + source 25 + target 190 + value 1 + ] + edge + [ + source 26 + target 22 + value 3 + ] + edge + [ + source 26 + target 51 + value 1 + ] + edge + [ + source 26 + target 49 + value 1 + ] + edge + [ + source 26 + target 64 + value 2 + ] + edge + [ + source 26 + target 44 + value 9 + ] + edge + [ + source 27 + target 28 + value 1 + ] + edge + [ + source 27 + target 3 + value 4 + ] + edge + [ + source 27 + target 29 + value 2 + ] + edge + [ + source 27 + target 30 + value 1 + ] + edge + [ + source 27 + target 31 + value 5 + ] + edge + [ + source 27 + target 32 + value 3 + ] + edge + [ + source 27 + target 20 + value 2 + ] + edge + [ + source 27 + target 21 + value 4 + ] + edge + [ + source 27 + target 33 + value 1 + ] + edge + [ + source 27 + target 34 + value 1 + ] + edge + [ + source 27 + target 35 + value 2 + ] + edge + [ + source 27 + target 36 + value 2 + ] + edge + [ + source 27 + target 37 + value 2 + ] + edge + [ + source 27 + target 38 + value 1 + ] + edge + [ + source 27 + target 39 + value 1 + ] + edge + [ + source 28 + target 122 + value 10 + ] + edge + [ + source 28 + target 114 + value 2 + ] + edge + [ + source 29 + target 50 + value 1 + ] + edge + [ + source 29 + target 30 + value 1 + ] + edge + [ + source 29 + target 33 + value 8 + ] + edge + [ + source 29 + target 35 + value 11 + ] + edge + [ + source 29 + target 44 + value 10 + ] + edge + [ + source 30 + target 29 + value 5 + ] + edge + [ + source 30 + target 32 + value 2 + ] + edge + [ + source 30 + target 6 + value 2 + ] + edge + [ + source 30 + target 59 + value 4 + ] + edge + [ + source 30 + target 33 + value 14 + ] + edge + [ + source 30 + target 51 + value 3 + ] + edge + [ + source 30 + target 49 + value 2 + ] + edge + [ + source 30 + target 64 + value 3 + ] + edge + [ + source 30 + target 38 + value 3 + ] + edge + [ + source 30 + target 52 + value 1 + ] + edge + [ + source 31 + target 3 + value 14 + ] + edge + [ + source 31 + target 4 + value 1 + ] + edge + [ + source 31 + target 58 + value 2 + ] + edge + [ + source 31 + target 27 + value 6 + ] + edge + [ + source 31 + target 45 + value 3 + ] + edge + [ + source 31 + target 29 + value 1 + ] + edge + [ + source 31 + target 66 + value 1 + ] + edge + [ + source 31 + target 7 + value 10 + ] + edge + [ + source 31 + target 35 + value 10 + ] + edge + [ + source 31 + target 47 + value 2 + ] + edge + [ + source 31 + target 48 + value 1 + ] + edge + [ + source 31 + target 46 + value 3 + ] + edge + [ + source 31 + target 49 + value 2 + ] + edge + [ + source 31 + target 73 + value 1 + ] + edge + [ + source 31 + target 74 + value 1 + ] + edge + [ + source 31 + target 77 + value 3 + ] + edge + [ + source 31 + target 75 + value 3 + ] + edge + [ + source 32 + target 29 + value 1 + ] + edge + [ + source 32 + target 30 + value 1 + ] + edge + [ + source 32 + target 21 + value 1 + ] + edge + [ + source 32 + target 59 + value 1 + ] + edge + [ + source 32 + target 33 + value 1 + ] + edge + [ + source 32 + target 43 + value 3 + ] + edge + [ + source 32 + target 49 + value 1 + ] + edge + [ + source 32 + target 81 + value 3 + ] + edge + [ + source 33 + target 68 + value 2 + ] + edge + [ + source 34 + target 13 + value 1 + ] + edge + [ + source 34 + target 6 + value 1 + ] + edge + [ + source 34 + target 47 + value 2 + ] + edge + [ + source 34 + target 43 + value 1 + ] + edge + [ + source 34 + target 72 + value 3 + ] + edge + [ + source 34 + target 37 + value 3 + ] + edge + [ + source 34 + target 112 + value 1 + ] + edge + [ + source 34 + target 74 + value 1 + ] + edge + [ + source 34 + target 44 + value 4 + ] + edge + [ + source 35 + target 43 + value 10 + ] + edge + [ + source 35 + target 44 + value 10 + ] + edge + [ + source 36 + target 49 + value 1 + ] + edge + [ + source 36 + target 93 + value 1 + ] + edge + [ + source 36 + target 44 + value 7 + ] + edge + [ + source 37 + target 190 + value 1 + ] + edge + [ + source 38 + target 50 + value 1 + ] + edge + [ + source 38 + target 33 + value 4 + ] + edge + [ + source 38 + target 43 + value 1 + ] + edge + [ + source 38 + target 49 + value 2 + ] + edge + [ + source 38 + target 64 + value 2 + ] + edge + [ + source 38 + target 44 + value 9 + ] + edge + [ + source 40 + target 41 + value 1 + ] + edge + [ + source 40 + target 42 + value 1 + ] + edge + [ + source 40 + target 22 + value 3 + ] + edge + [ + source 40 + target 43 + value 6 + ] + edge + [ + source 40 + target 44 + value 4 + ] + edge + [ + source 41 + target 4 + value 2 + ] + edge + [ + source 41 + target 40 + value 1 + ] + edge + [ + source 41 + target 23 + value 3 + ] + edge + [ + source 41 + target 47 + value 1 + ] + edge + [ + source 41 + target 48 + value 3 + ] + edge + [ + source 41 + target 43 + value 4 + ] + edge + [ + source 41 + target 46 + value 2 + ] + edge + [ + source 41 + target 49 + value 1 + ] + edge + [ + source 41 + target 44 + value 10 + ] + edge + [ + source 42 + target 84 + value 4 + ] + edge + [ + source 42 + target 86 + value 3 + ] + edge + [ + source 42 + target 119 + value 2 + ] + edge + [ + source 42 + target 98 + value 2 + ] + edge + [ + source 42 + target 218 + value 1 + ] + edge + [ + source 42 + target 167 + value 1 + ] + edge + [ + source 42 + target 232 + value 1 + ] + edge + [ + source 42 + target 168 + value 1 + ] + edge + [ + source 42 + target 137 + value 1 + ] + edge + [ + source 42 + target 40 + value 1 + ] + edge + [ + source 42 + target 45 + value 1 + ] + edge + [ + source 42 + target 17 + value 1 + ] + edge + [ + source 42 + target 29 + value 1 + ] + edge + [ + source 42 + target 185 + value 1 + ] + edge + [ + source 42 + target 172 + value 1 + ] + edge + [ + source 42 + target 22 + value 4 + ] + edge + [ + source 42 + target 33 + value 3 + ] + edge + [ + source 42 + target 173 + value 1 + ] + edge + [ + source 43 + target 6 + value 1 + ] + edge + [ + source 43 + target 23 + value 1 + ] + edge + [ + source 43 + target 35 + value 6 + ] + edge + [ + source 43 + target 44 + value 7 + ] + edge + [ + source 45 + target 33 + value 7 + ] + edge + [ + source 45 + target 46 + value 8 + ] + edge + [ + source 45 + target 44 + value 8 + ] + edge + [ + source 46 + target 23 + value 10 + ] + edge + [ + source 46 + target 79 + value 1 + ] + edge + [ + source 46 + target 44 + value 9 + ] + edge + [ + source 47 + target 31 + value 1 + ] + edge + [ + source 47 + target 13 + value 5 + ] + edge + [ + source 47 + target 6 + value 3 + ] + edge + [ + source 47 + target 35 + value 1 + ] + edge + [ + source 47 + target 48 + value 2 + ] + edge + [ + source 47 + target 214 + value 1 + ] + edge + [ + source 47 + target 44 + value 11 + ] + edge + [ + source 48 + target 213 + value 1 + ] + edge + [ + source 48 + target 13 + value 5 + ] + edge + [ + source 48 + target 6 + value 7 + ] + edge + [ + source 48 + target 23 + value 1 + ] + edge + [ + source 48 + target 47 + value 1 + ] + edge + [ + source 48 + target 44 + value 4 + ] + edge + [ + source 49 + target 44 + value 14 + ] + edge + [ + source 50 + target 3 + value 1 + ] + edge + [ + source 50 + target 45 + value 1 + ] + edge + [ + source 50 + target 23 + value 3 + ] + edge + [ + source 50 + target 35 + value 4 + ] + edge + [ + source 50 + target 47 + value 4 + ] + edge + [ + source 50 + target 48 + value 1 + ] + edge + [ + source 50 + target 43 + value 1 + ] + edge + [ + source 50 + target 46 + value 4 + ] + edge + [ + source 50 + target 51 + value 2 + ] + edge + [ + source 50 + target 52 + value 1 + ] + edge + [ + source 50 + target 44 + value 12 + ] + edge + [ + source 51 + target 44 + value 11 + ] + edge + [ + source 52 + target 99 + value 4 + ] + edge + [ + source 52 + target 84 + value 1 + ] + edge + [ + source 52 + target 86 + value 1 + ] + edge + [ + source 52 + target 4 + value 1 + ] + edge + [ + source 52 + target 32 + value 1 + ] + edge + [ + source 52 + target 6 + value 4 + ] + edge + [ + source 52 + target 21 + value 1 + ] + edge + [ + source 52 + target 9 + value 2 + ] + edge + [ + source 52 + target 33 + value 3 + ] + edge + [ + source 52 + target 48 + value 1 + ] + edge + [ + source 52 + target 37 + value 1 + ] + edge + [ + source 53 + target 86 + value 1 + ] + edge + [ + source 53 + target 4 + value 5 + ] + edge + [ + source 53 + target 17 + value 1 + ] + edge + [ + source 53 + target 13 + value 1 + ] + edge + [ + source 53 + target 14 + value 2 + ] + edge + [ + source 53 + target 59 + value 1 + ] + edge + [ + source 53 + target 90 + value 1 + ] + edge + [ + source 53 + target 23 + value 4 + ] + edge + [ + source 53 + target 46 + value 2 + ] + edge + [ + source 53 + target 78 + value 1 + ] + edge + [ + source 53 + target 75 + value 4 + ] + edge + [ + source 54 + target 11 + value 1 + ] + edge + [ + source 54 + target 40 + value 7 + ] + edge + [ + source 54 + target 55 + value 2 + ] + edge + [ + source 54 + target 14 + value 1 + ] + edge + [ + source 54 + target 22 + value 9 + ] + edge + [ + source 54 + target 51 + value 4 + ] + edge + [ + source 54 + target 49 + value 4 + ] + edge + [ + source 54 + target 56 + value 3 + ] + edge + [ + source 55 + target 21 + value 1 + ] + edge + [ + source 55 + target 35 + value 4 + ] + edge + [ + source 55 + target 78 + value 3 + ] + edge + [ + source 56 + target 40 + value 2 + ] + edge + [ + source 56 + target 22 + value 3 + ] + edge + [ + source 56 + target 35 + value 1 + ] + edge + [ + source 56 + target 51 + value 1 + ] + edge + [ + source 56 + target 44 + value 6 + ] + edge + [ + source 57 + target 58 + value 1 + ] + edge + [ + source 57 + target 45 + value 7 + ] + edge + [ + source 57 + target 21 + value 1 + ] + edge + [ + source 57 + target 59 + value 1 + ] + edge + [ + source 57 + target 33 + value 12 + ] + edge + [ + source 57 + target 60 + value 1 + ] + edge + [ + source 57 + target 51 + value 3 + ] + edge + [ + source 57 + target 49 + value 2 + ] + edge + [ + source 57 + target 61 + value 3 + ] + edge + [ + source 58 + target 3 + value 5 + ] + edge + [ + source 58 + target 95 + value 1 + ] + edge + [ + source 58 + target 45 + value 5 + ] + edge + [ + source 58 + target 50 + value 1 + ] + edge + [ + source 58 + target 31 + value 6 + ] + edge + [ + source 58 + target 68 + value 5 + ] + edge + [ + source 58 + target 7 + value 1 + ] + edge + [ + source 58 + target 20 + value 4 + ] + edge + [ + source 58 + target 21 + value 2 + ] + edge + [ + source 58 + target 23 + value 1 + ] + edge + [ + source 58 + target 46 + value 2 + ] + edge + [ + source 58 + target 87 + value 1 + ] + edge + [ + source 58 + target 24 + value 4 + ] + edge + [ + source 58 + target 88 + value 1 + ] + edge + [ + source 58 + target 85 + value 1 + ] + edge + [ + source 58 + target 61 + value 1 + ] + edge + [ + source 58 + target 69 + value 2 + ] + edge + [ + source 58 + target 82 + value 1 + ] + edge + [ + source 59 + target 105 + value 1 + ] + edge + [ + source 59 + target 102 + value 5 + ] + edge + [ + source 59 + target 108 + value 4 + ] + edge + [ + source 59 + target 99 + value 1 + ] + edge + [ + source 59 + target 5 + value 1 + ] + edge + [ + source 59 + target 71 + value 1 + ] + edge + [ + source 59 + target 58 + value 1 + ] + edge + [ + source 59 + target 15 + value 1 + ] + edge + [ + source 59 + target 27 + value 2 + ] + edge + [ + source 59 + target 66 + value 1 + ] + edge + [ + source 59 + target 55 + value 3 + ] + edge + [ + source 59 + target 68 + value 3 + ] + edge + [ + source 59 + target 19 + value 3 + ] + edge + [ + source 59 + target 32 + value 7 + ] + edge + [ + source 59 + target 13 + value 12 + ] + edge + [ + source 59 + target 6 + value 9 + ] + edge + [ + source 59 + target 14 + value 5 + ] + edge + [ + source 59 + target 7 + value 4 + ] + edge + [ + source 59 + target 22 + value 5 + ] + edge + [ + source 59 + target 33 + value 5 + ] + edge + [ + source 59 + target 49 + value 1 + ] + edge + [ + source 59 + target 64 + value 1 + ] + edge + [ + source 59 + target 91 + value 1 + ] + edge + [ + source 59 + target 39 + value 1 + ] + edge + [ + source 60 + target 14 + value 1 + ] + edge + [ + source 60 + target 7 + value 1 + ] + edge + [ + source 60 + target 44 + value 10 + ] + edge + [ + source 61 + target 33 + value 3 + ] + edge + [ + source 61 + target 46 + value 1 + ] + edge + [ + source 61 + target 60 + value 1 + ] + edge + [ + source 61 + target 49 + value 1 + ] + edge + [ + source 61 + target 82 + value 1 + ] + edge + [ + source 61 + target 44 + value 6 + ] + edge + [ + source 62 + target 63 + value 2 + ] + edge + [ + source 62 + target 3 + value 1 + ] + edge + [ + source 62 + target 41 + value 1 + ] + edge + [ + source 62 + target 55 + value 6 + ] + edge + [ + source 62 + target 19 + value 8 + ] + edge + [ + source 62 + target 20 + value 1 + ] + edge + [ + source 62 + target 59 + value 9 + ] + edge + [ + source 62 + target 47 + value 3 + ] + edge + [ + source 62 + target 48 + value 1 + ] + edge + [ + source 62 + target 49 + value 2 + ] + edge + [ + source 62 + target 64 + value 2 + ] + edge + [ + source 62 + target 65 + value 2 + ] + edge + [ + source 63 + target 101 + value 1 + ] + edge + [ + source 63 + target 12 + value 2 + ] + edge + [ + source 63 + target 2 + value 3 + ] + edge + [ + source 63 + target 3 + value 1 + ] + edge + [ + source 63 + target 225 + value 1 + ] + edge + [ + source 63 + target 71 + value 1 + ] + edge + [ + source 63 + target 129 + value 2 + ] + edge + [ + source 63 + target 41 + value 1 + ] + edge + [ + source 63 + target 62 + value 2 + ] + edge + [ + source 63 + target 18 + value 2 + ] + edge + [ + source 63 + target 13 + value 1 + ] + edge + [ + source 63 + target 215 + value 1 + ] + edge + [ + source 63 + target 8 + value 5 + ] + edge + [ + source 63 + target 9 + value 6 + ] + edge + [ + source 63 + target 59 + value 2 + ] + edge + [ + source 63 + target 34 + value 1 + ] + edge + [ + source 63 + target 209 + value 1 + ] + edge + [ + source 63 + target 47 + value 2 + ] + edge + [ + source 63 + target 93 + value 3 + ] + edge + [ + source 63 + target 24 + value 1 + ] + edge + [ + source 63 + target 94 + value 1 + ] + edge + [ + source 63 + target 81 + value 1 + ] + edge + [ + source 63 + target 85 + value 1 + ] + edge + [ + source 63 + target 83 + value 1 + ] + edge + [ + source 64 + target 74 + value 1 + ] + edge + [ + source 64 + target 44 + value 5 + ] + edge + [ + source 65 + target 11 + value 7 + ] + edge + [ + source 65 + target 84 + value 2 + ] + edge + [ + source 65 + target 3 + value 3 + ] + edge + [ + source 65 + target 98 + value 2 + ] + edge + [ + source 65 + target 13 + value 8 + ] + edge + [ + source 65 + target 20 + value 1 + ] + edge + [ + source 65 + target 8 + value 2 + ] + edge + [ + source 66 + target 67 + value 2 + ] + edge + [ + source 66 + target 50 + value 1 + ] + edge + [ + source 66 + target 31 + value 2 + ] + edge + [ + source 66 + target 68 + value 1 + ] + edge + [ + source 66 + target 32 + value 7 + ] + edge + [ + source 66 + target 59 + value 5 + ] + edge + [ + source 66 + target 47 + value 1 + ] + edge + [ + source 66 + target 51 + value 2 + ] + edge + [ + source 66 + target 64 + value 1 + ] + edge + [ + source 66 + target 38 + value 1 + ] + edge + [ + source 66 + target 69 + value 1 + ] + edge + [ + source 67 + target 106 + value 1 + ] + edge + [ + source 67 + target 63 + value 2 + ] + edge + [ + source 67 + target 12 + value 5 + ] + edge + [ + source 67 + target 2 + value 1 + ] + edge + [ + source 67 + target 118 + value 1 + ] + edge + [ + source 67 + target 4 + value 1 + ] + edge + [ + source 67 + target 98 + value 1 + ] + edge + [ + source 67 + target 218 + value 1 + ] + edge + [ + source 67 + target 58 + value 1 + ] + edge + [ + source 67 + target 129 + value 1 + ] + edge + [ + source 67 + target 224 + value 2 + ] + edge + [ + source 67 + target 31 + value 2 + ] + edge + [ + source 67 + target 8 + value 7 + ] + edge + [ + source 67 + target 9 + value 4 + ] + edge + [ + source 67 + target 59 + value 1 + ] + edge + [ + source 67 + target 48 + value 2 + ] + edge + [ + source 67 + target 87 + value 2 + ] + edge + [ + source 67 + target 212 + value 1 + ] + edge + [ + source 68 + target 20 + value 1 + ] + edge + [ + source 68 + target 21 + value 1 + ] + edge + [ + source 68 + target 23 + value 3 + ] + edge + [ + source 68 + target 36 + value 1 + ] + edge + [ + source 68 + target 79 + value 2 + ] + edge + [ + source 69 + target 86 + value 1 + ] + edge + [ + source 69 + target 58 + value 1 + ] + edge + [ + source 69 + target 50 + value 3 + ] + edge + [ + source 69 + target 66 + value 1 + ] + edge + [ + source 69 + target 21 + value 1 + ] + edge + [ + source 69 + target 47 + value 1 + ] + edge + [ + source 69 + target 48 + value 1 + ] + edge + [ + source 69 + target 87 + value 1 + ] + edge + [ + source 69 + target 88 + value 1 + ] + edge + [ + source 69 + target 37 + value 1 + ] + edge + [ + source 69 + target 89 + value 1 + ] + edge + [ + source 69 + target 52 + value 7 + ] + edge + [ + source 70 + target 5 + value 1 + ] + edge + [ + source 70 + target 17 + value 6 + ] + edge + [ + source 70 + target 19 + value 1 + ] + edge + [ + source 70 + target 13 + value 1 + ] + edge + [ + source 70 + target 59 + value 2 + ] + edge + [ + source 70 + target 22 + value 11 + ] + edge + [ + source 70 + target 51 + value 2 + ] + edge + [ + source 70 + target 49 + value 5 + ] + edge + [ + source 70 + target 64 + value 1 + ] + edge + [ + source 70 + target 26 + value 4 + ] + edge + [ + source 71 + target 4 + value 5 + ] + edge + [ + source 71 + target 40 + value 4 + ] + edge + [ + source 71 + target 41 + value 1 + ] + edge + [ + source 71 + target 18 + value 2 + ] + edge + [ + source 71 + target 55 + value 6 + ] + edge + [ + source 71 + target 14 + value 2 + ] + edge + [ + source 71 + target 20 + value 1 + ] + edge + [ + source 71 + target 21 + value 3 + ] + edge + [ + source 71 + target 22 + value 2 + ] + edge + [ + source 71 + target 90 + value 1 + ] + edge + [ + source 71 + target 43 + value 3 + ] + edge + [ + source 71 + target 93 + value 3 + ] + edge + [ + source 71 + target 36 + value 4 + ] + edge + [ + source 71 + target 94 + value 1 + ] + edge + [ + source 71 + target 89 + value 1 + ] + edge + [ + source 71 + target 56 + value 2 + ] + edge + [ + source 71 + target 83 + value 2 + ] + edge + [ + source 71 + target 76 + value 2 + ] + edge + [ + source 72 + target 114 + value 1 + ] + edge + [ + source 72 + target 2 + value 3 + ] + edge + [ + source 72 + target 18 + value 1 + ] + edge + [ + source 72 + target 132 + value 4 + ] + edge + [ + source 72 + target 130 + value 5 + ] + edge + [ + source 72 + target 214 + value 1 + ] + edge + [ + source 72 + target 93 + value 1 + ] + edge + [ + source 73 + target 13 + value 1 + ] + edge + [ + source 73 + target 6 + value 1 + ] + edge + [ + source 73 + target 75 + value 1 + ] + edge + [ + source 73 + target 44 + value 3 + ] + edge + [ + source 74 + target 13 + value 2 + ] + edge + [ + source 74 + target 6 + value 1 + ] + edge + [ + source 74 + target 44 + value 3 + ] + edge + [ + source 75 + target 13 + value 8 + ] + edge + [ + source 75 + target 6 + value 4 + ] + edge + [ + source 75 + target 209 + value 1 + ] + edge + [ + source 75 + target 23 + value 2 + ] + edge + [ + source 75 + target 73 + value 2 + ] + edge + [ + source 75 + target 44 + value 3 + ] + edge + [ + source 76 + target 4 + value 3 + ] + edge + [ + source 76 + target 14 + value 1 + ] + edge + [ + source 76 + target 35 + value 4 + ] + edge + [ + source 76 + target 43 + value 5 + ] + edge + [ + source 76 + target 73 + value 1 + ] + edge + [ + source 76 + target 74 + value 2 + ] + edge + [ + source 77 + target 42 + value 1 + ] + edge + [ + source 77 + target 13 + value 3 + ] + edge + [ + source 77 + target 6 + value 7 + ] + edge + [ + source 77 + target 34 + value 1 + ] + edge + [ + source 77 + target 35 + value 1 + ] + edge + [ + source 77 + target 74 + value 4 + ] + edge + [ + source 77 + target 44 + value 2 + ] + edge + [ + source 78 + target 190 + value 1 + ] + edge + [ + source 79 + target 190 + value 1 + ] + edge + [ + source 80 + target 190 + value 1 + ] + edge + [ + source 81 + target 190 + value 1 + ] + edge + [ + source 82 + target 3 + value 2 + ] + edge + [ + source 82 + target 4 + value 2 + ] + edge + [ + source 82 + target 7 + value 1 + ] + edge + [ + source 82 + target 23 + value 2 + ] + edge + [ + source 82 + target 46 + value 4 + ] + edge + [ + source 82 + target 73 + value 4 + ] + edge + [ + source 82 + target 74 + value 1 + ] + edge + [ + source 83 + target 84 + value 1 + ] + edge + [ + source 83 + target 71 + value 1 + ] + edge + [ + source 83 + target 41 + value 1 + ] + edge + [ + source 83 + target 21 + value 1 + ] + edge + [ + source 83 + target 35 + value 1 + ] + edge + [ + source 83 + target 25 + value 1 + ] + edge + [ + source 83 + target 85 + value 1 + ] + edge + [ + source 83 + target 65 + value 4 + ] + edge + [ + source 84 + target 151 + value 1 + ] + edge + [ + source 84 + target 152 + value 2 + ] + edge + [ + source 84 + target 155 + value 2 + ] + edge + [ + source 84 + target 156 + value 2 + ] + edge + [ + source 84 + target 157 + value 1 + ] + edge + [ + source 84 + target 158 + value 2 + ] + edge + [ + source 84 + target 2 + value 25 + ] + edge + [ + source 84 + target 86 + value 2 + ] + edge + [ + source 84 + target 118 + value 3 + ] + edge + [ + source 84 + target 4 + value 3 + ] + edge + [ + source 84 + target 187 + value 1 + ] + edge + [ + source 84 + target 163 + value 1 + ] + edge + [ + source 84 + target 142 + value 1 + ] + edge + [ + source 84 + target 175 + value 1 + ] + edge + [ + source 84 + target 180 + value 1 + ] + edge + [ + source 84 + target 188 + value 1 + ] + edge + [ + source 84 + target 189 + value 1 + ] + edge + [ + source 84 + target 190 + value 6 + ] + edge + [ + source 85 + target 12 + value 1 + ] + edge + [ + source 85 + target 60 + value 3 + ] + edge + [ + source 85 + target 211 + value 2 + ] + edge + [ + source 85 + target 44 + value 9 + ] + edge + [ + source 86 + target 151 + value 1 + ] + edge + [ + source 86 + target 152 + value 2 + ] + edge + [ + source 86 + target 155 + value 2 + ] + edge + [ + source 86 + target 156 + value 2 + ] + edge + [ + source 86 + target 157 + value 1 + ] + edge + [ + source 86 + target 158 + value 2 + ] + edge + [ + source 86 + target 12 + value 25 + ] + edge + [ + source 86 + target 84 + value 2 + ] + edge + [ + source 86 + target 117 + value 3 + ] + edge + [ + source 86 + target 3 + value 3 + ] + edge + [ + source 86 + target 187 + value 1 + ] + edge + [ + source 86 + target 163 + value 1 + ] + edge + [ + source 86 + target 191 + value 1 + ] + edge + [ + source 86 + target 175 + value 1 + ] + edge + [ + source 86 + target 180 + value 1 + ] + edge + [ + source 86 + target 188 + value 1 + ] + edge + [ + source 86 + target 189 + value 1 + ] + edge + [ + source 86 + target 190 + value 6 + ] + edge + [ + source 87 + target 106 + value 1 + ] + edge + [ + source 87 + target 236 + value 1 + ] + edge + [ + source 87 + target 109 + value 1 + ] + edge + [ + source 87 + target 2 + value 1 + ] + edge + [ + source 87 + target 86 + value 1 + ] + edge + [ + source 87 + target 117 + value 1 + ] + edge + [ + source 87 + target 4 + value 3 + ] + edge + [ + source 87 + target 119 + value 1 + ] + edge + [ + source 87 + target 103 + value 1 + ] + edge + [ + source 87 + target 47 + value 3 + ] + edge + [ + source 87 + target 48 + value 3 + ] + edge + [ + source 87 + target 43 + value 1 + ] + edge + [ + source 87 + target 46 + value 4 + ] + edge + [ + source 87 + target 52 + value 1 + ] + edge + [ + source 87 + target 44 + value 3 + ] + edge + [ + source 88 + target 190 + value 1 + ] + edge + [ + source 89 + target 2 + value 1 + ] + edge + [ + source 89 + target 60 + value 4 + ] + edge + [ + source 89 + target 212 + value 2 + ] + edge + [ + source 89 + target 44 + value 9 + ] + edge + [ + source 90 + target 3 + value 8 + ] + edge + [ + source 90 + target 4 + value 6 + ] + edge + [ + source 90 + target 213 + value 1 + ] + edge + [ + source 90 + target 218 + value 3 + ] + edge + [ + source 90 + target 187 + value 2 + ] + edge + [ + source 90 + target 71 + value 1 + ] + edge + [ + source 90 + target 58 + value 2 + ] + edge + [ + source 90 + target 15 + value 2 + ] + edge + [ + source 90 + target 27 + value 1 + ] + edge + [ + source 90 + target 31 + value 1 + ] + edge + [ + source 90 + target 14 + value 3 + ] + edge + [ + source 90 + target 7 + value 6 + ] + edge + [ + source 90 + target 132 + value 2 + ] + edge + [ + source 90 + target 130 + value 4 + ] + edge + [ + source 90 + target 23 + value 1 + ] + edge + [ + source 90 + target 47 + value 2 + ] + edge + [ + source 90 + target 48 + value 5 + ] + edge + [ + source 90 + target 73 + value 1 + ] + edge + [ + source 90 + target 74 + value 1 + ] + edge + [ + source 90 + target 77 + value 1 + ] + edge + [ + source 90 + target 75 + value 1 + ] + edge + [ + source 90 + target 91 + value 1 + ] + edge + [ + source 91 + target 12 + value 1 + ] + edge + [ + source 91 + target 3 + value 6 + ] + edge + [ + source 91 + target 29 + value 1 + ] + edge + [ + source 91 + target 6 + value 1 + ] + edge + [ + source 91 + target 7 + value 1 + ] + edge + [ + source 91 + target 23 + value 1 + ] + edge + [ + source 91 + target 35 + value 4 + ] + edge + [ + source 91 + target 43 + value 3 + ] + edge + [ + source 91 + target 78 + value 1 + ] + edge + [ + source 91 + target 79 + value 1 + ] + edge + [ + source 91 + target 77 + value 3 + ] + edge + [ + source 92 + target 3 + value 2 + ] + edge + [ + source 92 + target 4 + value 2 + ] + edge + [ + source 92 + target 48 + value 1 + ] + edge + [ + source 92 + target 39 + value 1 + ] + edge + [ + source 92 + target 39 + value 2 + ] + edge + [ + source 93 + target 101 + value 1 + ] + edge + [ + source 93 + target 114 + value 1 + ] + edge + [ + source 93 + target 216 + value 1 + ] + edge + [ + source 93 + target 235 + value 1 + ] + edge + [ + source 93 + target 141 + value 1 + ] + edge + [ + source 93 + target 12 + value 1 + ] + edge + [ + source 93 + target 86 + value 2 + ] + edge + [ + source 93 + target 3 + value 2 + ] + edge + [ + source 93 + target 71 + value 1 + ] + edge + [ + source 93 + target 47 + value 1 + ] + edge + [ + source 93 + target 48 + value 3 + ] + edge + [ + source 93 + target 43 + value 4 + ] + edge + [ + source 93 + target 36 + value 1 + ] + edge + [ + source 93 + target 25 + value 1 + ] + edge + [ + source 93 + target 78 + value 2 + ] + edge + [ + source 93 + target 104 + value 2 + ] + edge + [ + source 93 + target 65 + value 1 + ] + edge + [ + source 93 + target 44 + value 5 + ] + edge + [ + source 94 + target 190 + value 1 + ] + edge + [ + source 95 + target 63 + value 1 + ] + edge + [ + source 95 + target 67 + value 1 + ] + edge + [ + source 95 + target 240 + value 1 + ] + edge + [ + source 95 + target 12 + value 5 + ] + edge + [ + source 95 + target 198 + value 1 + ] + edge + [ + source 95 + target 119 + value 2 + ] + edge + [ + source 95 + target 142 + value 7 + ] + edge + [ + source 95 + target 125 + value 2 + ] + edge + [ + source 95 + target 172 + value 2 + ] + edge + [ + source 95 + target 207 + value 4 + ] + edge + [ + source 95 + target 204 + value 1 + ] + edge + [ + source 95 + target 72 + value 1 + ] + edge + [ + source 95 + target 241 + value 1 + ] + edge + [ + source 95 + target 61 + value 1 + ] + edge + [ + source 96 + target 97 + value 1 + ] + edge + [ + source 96 + target 60 + value 1 + ] + edge + [ + source 96 + target 44 + value 1 + ] + edge + [ + source 97 + target 166 + value 2 + ] + edge + [ + source 97 + target 227 + value 1 + ] + edge + [ + source 97 + target 221 + value 1 + ] + edge + [ + source 97 + target 44 + value 29 + ] + edge + [ + source 98 + target 2 + value 2 + ] + edge + [ + source 98 + target 84 + value 4 + ] + edge + [ + source 98 + target 86 + value 1 + ] + edge + [ + source 98 + target 118 + value 4 + ] + edge + [ + source 98 + target 3 + value 4 + ] + edge + [ + source 98 + target 202 + value 1 + ] + edge + [ + source 98 + target 198 + value 1 + ] + edge + [ + source 98 + target 119 + value 1 + ] + edge + [ + source 98 + target 206 + value 1 + ] + edge + [ + source 98 + target 191 + value 1 + ] + edge + [ + source 98 + target 125 + value 2 + ] + edge + [ + source 98 + target 172 + value 4 + ] + edge + [ + source 98 + target 207 + value 1 + ] + edge + [ + source 98 + target 145 + value 1 + ] + edge + [ + source 98 + target 186 + value 1 + ] + edge + [ + source 99 + target 107 + value 1 + ] + edge + [ + source 99 + target 2 + value 1 + ] + edge + [ + source 99 + target 4 + value 3 + ] + edge + [ + source 99 + target 6 + value 10 + ] + edge + [ + source 99 + target 7 + value 12 + ] + edge + [ + source 99 + target 52 + value 1 + ] + edge + [ + source 100 + target 101 + value 1 + ] + edge + [ + source 100 + target 102 + value 11 + ] + edge + [ + source 100 + target 11 + value 3 + ] + edge + [ + source 100 + target 19 + value 1 + ] + edge + [ + source 100 + target 13 + value 12 + ] + edge + [ + source 100 + target 103 + value 2 + ] + edge + [ + source 100 + target 104 + value 2 + ] + edge + [ + source 101 + target 1 + value 1 + ] + edge + [ + source 101 + target 114 + value 2 + ] + edge + [ + source 101 + target 2 + value 1 + ] + edge + [ + source 101 + target 84 + value 4 + ] + edge + [ + source 101 + target 86 + value 5 + ] + edge + [ + source 101 + target 3 + value 1 + ] + edge + [ + source 101 + target 98 + value 4 + ] + edge + [ + source 101 + target 224 + value 1 + ] + edge + [ + source 101 + target 132 + value 3 + ] + edge + [ + source 101 + target 22 + value 1 + ] + edge + [ + source 101 + target 22 + value 1 + ] + edge + [ + source 101 + target 77 + value 2 + ] + edge + [ + source 102 + target 100 + value 2 + ] + edge + [ + source 102 + target 113 + value 2 + ] + edge + [ + source 102 + target 1 + value 4 + ] + edge + [ + source 102 + target 114 + value 7 + ] + edge + [ + source 102 + target 135 + value 1 + ] + edge + [ + source 102 + target 136 + value 1 + ] + edge + [ + source 102 + target 4 + value 4 + ] + edge + [ + source 102 + target 137 + value 1 + ] + edge + [ + source 102 + target 13 + value 7 + ] + edge + [ + source 102 + target 59 + value 1 + ] + edge + [ + source 102 + target 132 + value 3 + ] + edge + [ + source 102 + target 89 + value 4 + ] + edge + [ + source 102 + target 104 + value 7 + ] + edge + [ + source 103 + target 102 + value 6 + ] + edge + [ + source 103 + target 108 + value 5 + ] + edge + [ + source 103 + target 201 + value 1 + ] + edge + [ + source 103 + target 11 + value 1 + ] + edge + [ + source 103 + target 5 + value 1 + ] + edge + [ + source 103 + target 137 + value 3 + ] + edge + [ + source 103 + target 191 + value 1 + ] + edge + [ + source 103 + target 13 + value 6 + ] + edge + [ + source 103 + target 6 + value 1 + ] + edge + [ + source 103 + target 65 + value 5 + ] + edge + [ + source 103 + target 52 + value 2 + ] + edge + [ + source 104 + target 222 + value 1 + ] + edge + [ + source 104 + target 64 + value 5 + ] + edge + [ + source 104 + target 131 + value 3 + ] + edge + [ + source 104 + target 44 + value 4 + ] + edge + [ + source 105 + target 106 + value 1 + ] + edge + [ + source 105 + target 107 + value 1 + ] + edge + [ + source 105 + target 108 + value 8 + ] + edge + [ + source 105 + target 109 + value 1 + ] + edge + [ + source 105 + target 99 + value 3 + ] + edge + [ + source 105 + target 110 + value 2 + ] + edge + [ + source 105 + target 111 + value 1 + ] + edge + [ + source 105 + target 6 + value 12 + ] + edge + [ + source 105 + target 9 + value 2 + ] + edge + [ + source 105 + target 103 + value 3 + ] + edge + [ + source 105 + target 89 + value 1 + ] + edge + [ + source 105 + target 112 + value 2 + ] + edge + [ + source 105 + target 52 + value 1 + ] + edge + [ + source 106 + target 84 + value 3 + ] + edge + [ + source 106 + target 86 + value 4 + ] + edge + [ + source 106 + target 3 + value 1 + ] + edge + [ + source 106 + target 119 + value 3 + ] + edge + [ + source 106 + target 130 + value 4 + ] + edge + [ + source 106 + target 33 + value 1 + ] + edge + [ + source 106 + target 209 + value 1 + ] + edge + [ + source 106 + target 75 + value 2 + ] + edge + [ + source 107 + target 105 + value 1 + ] + edge + [ + source 107 + target 108 + value 6 + ] + edge + [ + source 107 + target 139 + value 1 + ] + edge + [ + source 107 + target 191 + value 1 + ] + edge + [ + source 107 + target 6 + value 4 + ] + edge + [ + source 107 + target 7 + value 4 + ] + edge + [ + source 108 + target 122 + value 1 + ] + edge + [ + source 108 + target 1 + value 9 + ] + edge + [ + source 108 + target 114 + value 1 + ] + edge + [ + source 108 + target 3 + value 4 + ] + edge + [ + source 108 + target 4 + value 1 + ] + edge + [ + source 108 + target 137 + value 2 + ] + edge + [ + source 108 + target 6 + value 7 + ] + edge + [ + source 108 + target 130 + value 4 + ] + edge + [ + source 108 + target 85 + value 5 + ] + edge + [ + source 108 + target 112 + value 3 + ] + edge + [ + source 109 + target 106 + value 1 + ] + edge + [ + source 109 + target 105 + value 2 + ] + edge + [ + source 109 + target 122 + value 9 + ] + edge + [ + source 109 + target 114 + value 3 + ] + edge + [ + source 109 + target 2 + value 5 + ] + edge + [ + source 109 + target 86 + value 3 + ] + edge + [ + source 109 + target 117 + value 5 + ] + edge + [ + source 109 + target 118 + value 1 + ] + edge + [ + source 109 + target 4 + value 2 + ] + edge + [ + source 109 + target 142 + value 1 + ] + edge + [ + source 109 + target 111 + value 1 + ] + edge + [ + source 109 + target 6 + value 2 + ] + edge + [ + source 109 + target 87 + value 2 + ] + edge + [ + source 110 + target 105 + value 4 + ] + edge + [ + source 110 + target 108 + value 4 + ] + edge + [ + source 110 + target 28 + value 1 + ] + edge + [ + source 110 + target 86 + value 2 + ] + edge + [ + source 110 + target 6 + value 1 + ] + edge + [ + source 110 + target 103 + value 2 + ] + edge + [ + source 110 + target 112 + value 1 + ] + edge + [ + source 111 + target 105 + value 1 + ] + edge + [ + source 111 + target 201 + value 1 + ] + edge + [ + source 111 + target 12 + value 1 + ] + edge + [ + source 111 + target 2 + value 2 + ] + edge + [ + source 111 + target 84 + value 3 + ] + edge + [ + source 111 + target 86 + value 7 + ] + edge + [ + source 111 + target 3 + value 1 + ] + edge + [ + source 111 + target 198 + value 3 + ] + edge + [ + source 111 + target 187 + value 4 + ] + edge + [ + source 111 + target 233 + value 1 + ] + edge + [ + source 111 + target 193 + value 2 + ] + edge + [ + source 111 + target 125 + value 5 + ] + edge + [ + source 111 + target 172 + value 7 + ] + edge + [ + source 111 + target 145 + value 1 + ] + edge + [ + source 111 + target 6 + value 2 + ] + edge + [ + source 111 + target 9 + value 1 + ] + edge + [ + source 111 + target 130 + value 1 + ] + edge + [ + source 111 + target 190 + value 3 + ] + edge + [ + source 112 + target 223 + value 3 + ] + edge + [ + source 112 + target 64 + value 3 + ] + edge + [ + source 112 + target 72 + value 4 + ] + edge + [ + source 112 + target 44 + value 4 + ] + edge + [ + source 113 + target 101 + value 1 + ] + edge + [ + source 113 + target 1 + value 11 + ] + edge + [ + source 113 + target 149 + value 2 + ] + edge + [ + source 113 + target 102 + value 2 + ] + edge + [ + source 113 + target 115 + value 2 + ] + edge + [ + source 113 + target 140 + value 1 + ] + edge + [ + source 113 + target 141 + value 3 + ] + edge + [ + source 113 + target 124 + value 1 + ] + edge + [ + source 113 + target 191 + value 1 + ] + edge + [ + source 113 + target 215 + value 1 + ] + edge + [ + source 114 + target 2 + value 1 + ] + edge + [ + source 114 + target 86 + value 3 + ] + edge + [ + source 114 + target 3 + value 1 + ] + edge + [ + source 114 + target 13 + value 1 + ] + edge + [ + source 114 + target 14 + value 4 + ] + edge + [ + source 114 + target 132 + value 14 + ] + edge + [ + source 114 + target 130 + value 1 + ] + edge + [ + source 114 + target 131 + value 1 + ] + edge + [ + source 114 + target 73 + value 3 + ] + edge + [ + source 114 + target 133 + value 1 + ] + edge + [ + source 115 + target 126 + value 3 + ] + edge + [ + source 115 + target 128 + value 1 + ] + edge + [ + source 115 + target 113 + value 4 + ] + edge + [ + source 115 + target 122 + value 3 + ] + edge + [ + source 115 + target 1 + value 1 + ] + edge + [ + source 115 + target 114 + value 7 + ] + edge + [ + source 115 + target 10 + value 4 + ] + edge + [ + source 115 + target 107 + value 12 + ] + edge + [ + source 115 + target 139 + value 1 + ] + edge + [ + source 115 + target 138 + value 1 + ] + edge + [ + source 115 + target 124 + value 2 + ] + edge + [ + source 116 + target 100 + value 3 + ] + edge + [ + source 116 + target 113 + value 7 + ] + edge + [ + source 116 + target 1 + value 5 + ] + edge + [ + source 116 + target 141 + value 1 + ] + edge + [ + source 116 + target 12 + value 2 + ] + edge + [ + source 116 + target 84 + value 6 + ] + edge + [ + source 116 + target 117 + value 2 + ] + edge + [ + source 116 + target 118 + value 4 + ] + edge + [ + source 116 + target 13 + value 4 + ] + edge + [ + source 116 + target 132 + value 1 + ] + edge + [ + source 116 + target 22 + value 1 + ] + edge + [ + source 116 + target 103 + value 1 + ] + edge + [ + source 117 + target 151 + value 1 + ] + edge + [ + source 117 + target 152 + value 2 + ] + edge + [ + source 117 + target 153 + value 2 + ] + edge + [ + source 117 + target 156 + value 1 + ] + edge + [ + source 117 + target 157 + value 1 + ] + edge + [ + source 117 + target 12 + value 70 + ] + edge + [ + source 117 + target 84 + value 1 + ] + edge + [ + source 117 + target 118 + value 2 + ] + edge + [ + source 117 + target 159 + value 3 + ] + edge + [ + source 117 + target 160 + value 2 + ] + edge + [ + source 117 + target 161 + value 6 + ] + edge + [ + source 117 + target 162 + value 4 + ] + edge + [ + source 117 + target 163 + value 3 + ] + edge + [ + source 117 + target 166 + value 1 + ] + edge + [ + source 117 + target 167 + value 1 + ] + edge + [ + source 117 + target 192 + value 1 + ] + edge + [ + source 117 + target 193 + value 1 + ] + edge + [ + source 117 + target 170 + value 3 + ] + edge + [ + source 117 + target 194 + value 1 + ] + edge + [ + source 117 + target 125 + value 1 + ] + edge + [ + source 117 + target 173 + value 3 + ] + edge + [ + source 117 + target 186 + value 6 + ] + edge + [ + source 117 + target 176 + value 1 + ] + edge + [ + source 117 + target 178 + value 1 + ] + edge + [ + source 117 + target 179 + value 3 + ] + edge + [ + source 117 + target 181 + value 1 + ] + edge + [ + source 117 + target 182 + value 2 + ] + edge + [ + source 118 + target 151 + value 1 + ] + edge + [ + source 118 + target 152 + value 2 + ] + edge + [ + source 118 + target 153 + value 2 + ] + edge + [ + source 118 + target 156 + value 1 + ] + edge + [ + source 118 + target 157 + value 1 + ] + edge + [ + source 118 + target 2 + value 70 + ] + edge + [ + source 118 + target 86 + value 1 + ] + edge + [ + source 118 + target 117 + value 2 + ] + edge + [ + source 118 + target 159 + value 3 + ] + edge + [ + source 118 + target 160 + value 2 + ] + edge + [ + source 118 + target 161 + value 6 + ] + edge + [ + source 118 + target 162 + value 4 + ] + edge + [ + source 118 + target 163 + value 3 + ] + edge + [ + source 118 + target 166 + value 1 + ] + edge + [ + source 118 + target 167 + value 1 + ] + edge + [ + source 118 + target 192 + value 1 + ] + edge + [ + source 118 + target 193 + value 1 + ] + edge + [ + source 118 + target 184 + value 3 + ] + edge + [ + source 118 + target 194 + value 1 + ] + edge + [ + source 118 + target 172 + value 1 + ] + edge + [ + source 118 + target 173 + value 3 + ] + edge + [ + source 118 + target 174 + value 6 + ] + edge + [ + source 118 + target 176 + value 1 + ] + edge + [ + source 118 + target 178 + value 1 + ] + edge + [ + source 118 + target 179 + value 3 + ] + edge + [ + source 118 + target 181 + value 1 + ] + edge + [ + source 118 + target 182 + value 2 + ] + edge + [ + source 119 + target 12 + value 2 + ] + edge + [ + source 119 + target 84 + value 1 + ] + edge + [ + source 119 + target 86 + value 5 + ] + edge + [ + source 119 + target 117 + value 3 + ] + edge + [ + source 119 + target 4 + value 4 + ] + edge + [ + source 119 + target 199 + value 1 + ] + edge + [ + source 119 + target 123 + value 1 + ] + edge + [ + source 119 + target 142 + value 1 + ] + edge + [ + source 119 + target 125 + value 1 + ] + edge + [ + source 119 + target 172 + value 3 + ] + edge + [ + source 119 + target 204 + value 2 + ] + edge + [ + source 119 + target 148 + value 1 + ] + edge + [ + source 119 + target 205 + value 1 + ] + edge + [ + source 119 + target 90 + value 2 + ] + edge + [ + source 119 + target 174 + value 1 + ] + edge + [ + source 120 + target 100 + value 8 + ] + edge + [ + source 120 + target 114 + value 1 + ] + edge + [ + source 120 + target 102 + value 7 + ] + edge + [ + source 120 + target 84 + value 1 + ] + edge + [ + source 120 + target 13 + value 3 + ] + edge + [ + source 120 + target 85 + value 1 + ] + edge + [ + source 121 + target 122 + value 10 + ] + edge + [ + source 121 + target 114 + value 11 + ] + edge + [ + source 121 + target 115 + value 1 + ] + edge + [ + source 121 + target 109 + value 3 + ] + edge + [ + source 121 + target 2 + value 2 + ] + edge + [ + source 121 + target 84 + value 1 + ] + edge + [ + source 121 + target 86 + value 2 + ] + edge + [ + source 121 + target 117 + value 5 + ] + edge + [ + source 121 + target 118 + value 2 + ] + edge + [ + source 121 + target 123 + value 1 + ] + edge + [ + source 121 + target 98 + value 2 + ] + edge + [ + source 121 + target 124 + value 3 + ] + edge + [ + source 121 + target 31 + value 1 + ] + edge + [ + source 121 + target 125 + value 2 + ] + edge + [ + source 121 + target 20 + value 1 + ] + edge + [ + source 121 + target 21 + value 1 + ] + edge + [ + source 122 + target 106 + value 1 + ] + edge + [ + source 122 + target 121 + value 1 + ] + edge + [ + source 122 + target 114 + value 12 + ] + edge + [ + source 122 + target 108 + value 1 + ] + edge + [ + source 122 + target 115 + value 1 + ] + edge + [ + source 122 + target 139 + value 1 + ] + edge + [ + source 122 + target 124 + value 1 + ] + edge + [ + source 122 + target 205 + value 2 + ] + edge + [ + source 123 + target 121 + value 2 + ] + edge + [ + source 123 + target 201 + value 1 + ] + edge + [ + source 123 + target 84 + value 1 + ] + edge + [ + source 123 + target 118 + value 1 + ] + edge + [ + source 123 + target 202 + value 5 + ] + edge + [ + source 123 + target 198 + value 2 + ] + edge + [ + source 123 + target 119 + value 1 + ] + edge + [ + source 123 + target 98 + value 2 + ] + edge + [ + source 123 + target 203 + value 4 + ] + edge + [ + source 123 + target 145 + value 2 + ] + edge + [ + source 123 + target 8 + value 1 + ] + edge + [ + source 123 + target 103 + value 3 + ] + edge + [ + source 123 + target 89 + value 1 + ] + edge + [ + source 123 + target 104 + value 1 + ] + edge + [ + source 123 + target 200 + value 1 + ] + edge + [ + source 124 + target 121 + value 1 + ] + edge + [ + source 124 + target 122 + value 2 + ] + edge + [ + source 124 + target 1 + value 1 + ] + edge + [ + source 124 + target 114 + value 3 + ] + edge + [ + source 124 + target 10 + value 4 + ] + edge + [ + source 124 + target 107 + value 9 + ] + edge + [ + source 124 + target 138 + value 2 + ] + edge + [ + source 125 + target 151 + value 1 + ] + edge + [ + source 125 + target 154 + value 2 + ] + edge + [ + source 125 + target 12 + value 7 + ] + edge + [ + source 125 + target 2 + value 9 + ] + edge + [ + source 125 + target 84 + value 4 + ] + edge + [ + source 125 + target 86 + value 16 + ] + edge + [ + source 125 + target 117 + value 3 + ] + edge + [ + source 125 + target 118 + value 6 + ] + edge + [ + source 125 + target 3 + value 2 + ] + edge + [ + source 125 + target 4 + value 1 + ] + edge + [ + source 125 + target 119 + value 3 + ] + edge + [ + source 125 + target 187 + value 1 + ] + edge + [ + source 125 + target 160 + value 1 + ] + edge + [ + source 125 + target 232 + value 4 + ] + edge + [ + source 125 + target 168 + value 5 + ] + edge + [ + source 125 + target 192 + value 7 + ] + edge + [ + source 125 + target 169 + value 3 + ] + edge + [ + source 125 + target 253 + value 4 + ] + edge + [ + source 125 + target 137 + value 4 + ] + edge + [ + source 125 + target 184 + value 1 + ] + edge + [ + source 125 + target 171 + value 1 + ] + edge + [ + source 125 + target 172 + value 3 + ] + edge + [ + source 125 + target 42 + value 1 + ] + edge + [ + source 125 + target 256 + value 1 + ] + edge + [ + source 125 + target 96 + value 5 + ] + edge + [ + source 125 + target 90 + value 2 + ] + edge + [ + source 125 + target 78 + value 2 + ] + edge + [ + source 125 + target 254 + value 1 + ] + edge + [ + source 125 + target 262 + value 1 + ] + edge + [ + source 125 + target 263 + value 4 + ] + edge + [ + source 125 + target 264 + value 1 + ] + edge + [ + source 125 + target 265 + value 5 + ] + edge + [ + source 125 + target 266 + value 3 + ] + edge + [ + source 125 + target 267 + value 2 + ] + edge + [ + source 126 + target 127 + value 1 + ] + edge + [ + source 126 + target 10 + value 7 + ] + edge + [ + source 127 + target 126 + value 5 + ] + edge + [ + source 127 + target 113 + value 2 + ] + edge + [ + source 127 + target 1 + value 2 + ] + edge + [ + source 127 + target 115 + value 3 + ] + edge + [ + source 127 + target 5 + value 3 + ] + edge + [ + source 127 + target 14 + value 1 + ] + edge + [ + source 127 + target 96 + value 1 + ] + edge + [ + source 127 + target 39 + value 1 + ] + edge + [ + source 128 + target 107 + value 11 + ] + edge + [ + source 128 + target 115 + value 1 + ] + edge + [ + source 129 + target 63 + value 1 + ] + edge + [ + source 129 + target 67 + value 2 + ] + edge + [ + source 129 + target 1 + value 1 + ] + edge + [ + source 129 + target 114 + value 2 + ] + edge + [ + source 129 + target 12 + value 13 + ] + edge + [ + source 129 + target 2 + value 18 + ] + edge + [ + source 129 + target 84 + value 4 + ] + edge + [ + source 129 + target 86 + value 5 + ] + edge + [ + source 129 + target 117 + value 6 + ] + edge + [ + source 129 + target 118 + value 14 + ] + edge + [ + source 129 + target 137 + value 1 + ] + edge + [ + source 129 + target 224 + value 1 + ] + edge + [ + source 130 + target 106 + value 1 + ] + edge + [ + source 130 + target 1 + value 4 + ] + edge + [ + source 130 + target 12 + value 2 + ] + edge + [ + source 130 + target 84 + value 2 + ] + edge + [ + source 130 + target 86 + value 5 + ] + edge + [ + source 130 + target 119 + value 1 + ] + edge + [ + source 130 + target 213 + value 1 + ] + edge + [ + source 130 + target 7 + value 1 + ] + edge + [ + source 130 + target 90 + value 1 + ] + edge + [ + source 130 + target 47 + value 2 + ] + edge + [ + source 130 + target 48 + value 1 + ] + edge + [ + source 130 + target 210 + value 2 + ] + edge + [ + source 130 + target 211 + value 3 + ] + edge + [ + source 130 + target 212 + value 3 + ] + edge + [ + source 130 + target 73 + value 2 + ] + edge + [ + source 130 + target 74 + value 3 + ] + edge + [ + source 130 + target 44 + value 4 + ] + edge + [ + source 131 + target 1 + value 1 + ] + edge + [ + source 131 + target 12 + value 5 + ] + edge + [ + source 131 + target 132 + value 3 + ] + edge + [ + source 131 + target 130 + value 5 + ] + edge + [ + source 131 + target 93 + value 1 + ] + edge + [ + source 132 + target 12 + value 1 + ] + edge + [ + source 132 + target 84 + value 2 + ] + edge + [ + source 132 + target 86 + value 3 + ] + edge + [ + source 132 + target 14 + value 1 + ] + edge + [ + source 132 + target 90 + value 1 + ] + edge + [ + source 132 + target 47 + value 1 + ] + edge + [ + source 132 + target 48 + value 3 + ] + edge + [ + source 132 + target 210 + value 1 + ] + edge + [ + source 132 + target 72 + value 1 + ] + edge + [ + source 132 + target 211 + value 3 + ] + edge + [ + source 132 + target 212 + value 2 + ] + edge + [ + source 132 + target 74 + value 5 + ] + edge + [ + source 132 + target 77 + value 1 + ] + edge + [ + source 132 + target 44 + value 4 + ] + edge + [ + source 133 + target 114 + value 1 + ] + edge + [ + source 133 + target 219 + value 1 + ] + edge + [ + source 133 + target 132 + value 1 + ] + edge + [ + source 133 + target 214 + value 2 + ] + edge + [ + source 133 + target 131 + value 9 + ] + edge + [ + source 133 + target 72 + value 4 + ] + edge + [ + source 133 + target 173 + value 1 + ] + edge + [ + source 133 + target 196 + value 3 + ] + edge + [ + source 133 + target 178 + value 1 + ] + edge + [ + source 133 + target 179 + value 1 + ] + edge + [ + source 133 + target 200 + value 2 + ] + edge + [ + source 133 + target 197 + value 1 + ] + edge + [ + source 133 + target 44 + value 5 + ] + edge + [ + source 134 + target 128 + value 5 + ] + edge + [ + source 134 + target 135 + value 2 + ] + edge + [ + source 134 + target 28 + value 1 + ] + edge + [ + source 134 + target 99 + value 1 + ] + edge + [ + source 134 + target 0 + value 3 + ] + edge + [ + source 134 + target 14 + value 1 + ] + edge + [ + source 134 + target 7 + value 2 + ] + edge + [ + source 134 + target 39 + value 1 + ] + edge + [ + source 135 + target 105 + value 1 + ] + edge + [ + source 135 + target 113 + value 4 + ] + edge + [ + source 135 + target 1 + value 7 + ] + edge + [ + source 135 + target 114 + value 3 + ] + edge + [ + source 135 + target 10 + value 12 + ] + edge + [ + source 135 + target 107 + value 6 + ] + edge + [ + source 135 + target 138 + value 4 + ] + edge + [ + source 135 + target 124 + value 2 + ] + edge + [ + source 135 + target 6 + value 1 + ] + edge + [ + source 136 + target 1 + value 1 + ] + edge + [ + source 136 + target 10 + value 2 + ] + edge + [ + source 136 + target 102 + value 1 + ] + edge + [ + source 136 + target 135 + value 1 + ] + edge + [ + source 136 + target 115 + value 1 + ] + edge + [ + source 136 + target 141 + value 2 + ] + edge + [ + source 136 + target 138 + value 2 + ] + edge + [ + source 136 + target 124 + value 2 + ] + edge + [ + source 137 + target 102 + value 3 + ] + edge + [ + source 137 + target 201 + value 3 + ] + edge + [ + source 137 + target 11 + value 1 + ] + edge + [ + source 137 + target 99 + value 1 + ] + edge + [ + source 137 + target 12 + value 3 + ] + edge + [ + source 137 + target 84 + value 1 + ] + edge + [ + source 137 + target 3 + value 8 + ] + edge + [ + source 137 + target 4 + value 6 + ] + edge + [ + source 137 + target 232 + value 1 + ] + edge + [ + source 137 + target 168 + value 3 + ] + edge + [ + source 137 + target 192 + value 1 + ] + edge + [ + source 137 + target 169 + value 1 + ] + edge + [ + source 137 + target 253 + value 2 + ] + edge + [ + source 137 + target 185 + value 2 + ] + edge + [ + source 137 + target 125 + value 5 + ] + edge + [ + source 137 + target 42 + value 2 + ] + edge + [ + source 137 + target 13 + value 1 + ] + edge + [ + source 137 + target 6 + value 2 + ] + edge + [ + source 137 + target 130 + value 1 + ] + edge + [ + source 137 + target 103 + value 1 + ] + edge + [ + source 137 + target 72 + value 1 + ] + edge + [ + source 137 + target 211 + value 1 + ] + edge + [ + source 137 + target 212 + value 1 + ] + edge + [ + source 137 + target 89 + value 3 + ] + edge + [ + source 137 + target 85 + value 2 + ] + edge + [ + source 137 + target 104 + value 3 + ] + edge + [ + source 137 + target 112 + value 2 + ] + edge + [ + source 137 + target 177 + value 1 + ] + edge + [ + source 137 + target 133 + value 1 + ] + edge + [ + source 137 + target 254 + value 2 + ] + edge + [ + source 138 + target 113 + value 2 + ] + edge + [ + source 138 + target 122 + value 4 + ] + edge + [ + source 138 + target 1 + value 1 + ] + edge + [ + source 138 + target 114 + value 1 + ] + edge + [ + source 138 + target 10 + value 13 + ] + edge + [ + source 138 + target 12 + value 1 + ] + edge + [ + source 138 + target 13 + value 2 + ] + edge + [ + source 139 + target 105 + value 3 + ] + edge + [ + source 139 + target 128 + value 9 + ] + edge + [ + source 139 + target 107 + value 2 + ] + edge + [ + source 139 + target 108 + value 7 + ] + edge + [ + source 139 + target 135 + value 1 + ] + edge + [ + source 139 + target 115 + value 2 + ] + edge + [ + source 139 + target 110 + value 2 + ] + edge + [ + source 139 + target 205 + value 2 + ] + edge + [ + source 139 + target 103 + value 1 + ] + edge + [ + source 140 + target 113 + value 9 + ] + edge + [ + source 140 + target 1 + value 3 + ] + edge + [ + source 140 + target 127 + value 1 + ] + edge + [ + source 140 + target 141 + value 1 + ] + edge + [ + source 141 + target 113 + value 11 + ] + edge + [ + source 141 + target 1 + value 3 + ] + edge + [ + source 141 + target 149 + value 2 + ] + edge + [ + source 141 + target 144 + value 1 + ] + edge + [ + source 142 + target 1 + value 1 + ] + edge + [ + source 142 + target 114 + value 1 + ] + edge + [ + source 142 + target 102 + value 1 + ] + edge + [ + source 142 + target 108 + value 1 + ] + edge + [ + source 142 + target 157 + value 1 + ] + edge + [ + source 142 + target 116 + value 2 + ] + edge + [ + source 142 + target 117 + value 3 + ] + edge + [ + source 142 + target 202 + value 2 + ] + edge + [ + source 142 + target 119 + value 1 + ] + edge + [ + source 142 + target 120 + value 1 + ] + edge + [ + source 142 + target 95 + value 2 + ] + edge + [ + source 142 + target 163 + value 2 + ] + edge + [ + source 142 + target 164 + value 1 + ] + edge + [ + source 142 + target 169 + value 1 + ] + edge + [ + source 142 + target 191 + value 1 + ] + edge + [ + source 142 + target 204 + value 2 + ] + edge + [ + source 142 + target 148 + value 1 + ] + edge + [ + source 142 + target 205 + value 3 + ] + edge + [ + source 142 + target 130 + value 1 + ] + edge + [ + source 142 + target 87 + value 1 + ] + edge + [ + source 142 + target 173 + value 1 + ] + edge + [ + source 142 + target 186 + value 3 + ] + edge + [ + source 142 + target 181 + value 1 + ] + edge + [ + source 142 + target 182 + value 1 + ] + edge + [ + source 142 + target 238 + value 4 + ] + edge + [ + source 142 + target 245 + value 2 + ] + edge + [ + source 142 + target 244 + value 5 + ] + edge + [ + source 142 + target 44 + value 3 + ] + edge + [ + source 142 + target 44 + value 21 + ] + edge + [ + source 143 + target 122 + value 3 + ] + edge + [ + source 143 + target 135 + value 2 + ] + edge + [ + source 143 + target 109 + value 1 + ] + edge + [ + source 143 + target 138 + value 1 + ] + edge + [ + source 143 + target 124 + value 2 + ] + edge + [ + source 144 + target 141 + value 4 + ] + edge + [ + source 144 + target 145 + value 13 + ] + edge + [ + source 145 + target 113 + value 6 + ] + edge + [ + source 145 + target 144 + value 1 + ] + edge + [ + source 145 + target 141 + value 4 + ] + edge + [ + source 145 + target 187 + value 1 + ] + edge + [ + source 145 + target 219 + value 1 + ] + edge + [ + source 145 + target 195 + value 1 + ] + edge + [ + source 145 + target 193 + value 1 + ] + edge + [ + source 145 + target 191 + value 3 + ] + edge + [ + source 145 + target 93 + value 1 + ] + edge + [ + source 145 + target 200 + value 1 + ] + edge + [ + source 145 + target 39 + value 1 + ] + edge + [ + source 146 + target 147 + value 4 + ] + edge + [ + source 146 + target 142 + value 1 + ] + edge + [ + source 146 + target 148 + value 13 + ] + edge + [ + source 147 + target 122 + value 11 + ] + edge + [ + source 147 + target 114 + value 1 + ] + edge + [ + source 147 + target 150 + value 1 + ] + edge + [ + source 147 + target 139 + value 1 + ] + edge + [ + source 148 + target 122 + value 7 + ] + edge + [ + source 148 + target 147 + value 4 + ] + edge + [ + source 148 + target 199 + value 1 + ] + edge + [ + source 148 + target 187 + value 1 + ] + edge + [ + source 148 + target 219 + value 1 + ] + edge + [ + source 148 + target 195 + value 1 + ] + edge + [ + source 148 + target 193 + value 1 + ] + edge + [ + source 148 + target 142 + value 1 + ] + edge + [ + source 148 + target 205 + value 1 + ] + edge + [ + source 148 + target 200 + value 1 + ] + edge + [ + source 148 + target 39 + value 1 + ] + edge + [ + source 149 + target 113 + value 5 + ] + edge + [ + source 149 + target 216 + value 1 + ] + edge + [ + source 149 + target 140 + value 2 + ] + edge + [ + source 149 + target 141 + value 2 + ] + edge + [ + source 149 + target 86 + value 2 + ] + edge + [ + source 149 + target 117 + value 1 + ] + edge + [ + source 149 + target 118 + value 1 + ] + edge + [ + source 149 + target 4 + value 1 + ] + edge + [ + source 149 + target 202 + value 4 + ] + edge + [ + source 149 + target 199 + value 1 + ] + edge + [ + source 149 + target 198 + value 1 + ] + edge + [ + source 149 + target 123 + value 2 + ] + edge + [ + source 149 + target 119 + value 1 + ] + edge + [ + source 149 + target 145 + value 1 + ] + edge + [ + source 149 + target 215 + value 1 + ] + edge + [ + source 149 + target 104 + value 1 + ] + edge + [ + source 149 + target 39 + value 1 + ] + edge + [ + source 149 + target 39 + value 1 + ] + edge + [ + source 150 + target 122 + value 4 + ] + edge + [ + source 150 + target 28 + value 2 + ] + edge + [ + source 150 + target 146 + value 2 + ] + edge + [ + source 150 + target 147 + value 2 + ] + edge + [ + source 150 + target 118 + value 1 + ] + edge + [ + source 150 + target 202 + value 3 + ] + edge + [ + source 150 + target 199 + value 2 + ] + edge + [ + source 150 + target 98 + value 1 + ] + edge + [ + source 150 + target 191 + value 1 + ] + edge + [ + source 150 + target 142 + value 2 + ] + edge + [ + source 150 + target 205 + value 1 + ] + edge + [ + source 150 + target 87 + value 2 + ] + edge + [ + source 150 + target 39 + value 1 + ] + edge + [ + source 151 + target 159 + value 1 + ] + edge + [ + source 151 + target 200 + value 1 + ] + edge + [ + source 151 + target 44 + value 1 + ] + edge + [ + source 152 + target 165 + value 1 + ] + edge + [ + source 152 + target 249 + value 16 + ] + edge + [ + source 152 + target 268 + value 2 + ] + edge + [ + source 152 + target 44 + value 15 + ] + edge + [ + source 153 + target 161 + value 1 + ] + edge + [ + source 153 + target 166 + value 1 + ] + edge + [ + source 153 + target 167 + value 1 + ] + edge + [ + source 153 + target 249 + value 2 + ] + edge + [ + source 153 + target 227 + value 16 + ] + edge + [ + source 153 + target 44 + value 15 + ] + edge + [ + source 154 + target 160 + value 1 + ] + edge + [ + source 154 + target 197 + value 1 + ] + edge + [ + source 154 + target 44 + value 15 + ] + edge + [ + source 155 + target 161 + value 1 + ] + edge + [ + source 155 + target 197 + value 2 + ] + edge + [ + source 155 + target 189 + value 16 + ] + edge + [ + source 155 + target 44 + value 15 + ] + edge + [ + source 156 + target 189 + value 2 + ] + edge + [ + source 156 + target 231 + value 16 + ] + edge + [ + source 156 + target 44 + value 15 + ] + edge + [ + source 157 + target 162 + value 1 + ] + edge + [ + source 157 + target 231 + value 2 + ] + edge + [ + source 157 + target 234 + value 16 + ] + edge + [ + source 157 + target 44 + value 15 + ] + edge + [ + source 158 + target 163 + value 1 + ] + edge + [ + source 158 + target 234 + value 2 + ] + edge + [ + source 158 + target 269 + value 16 + ] + edge + [ + source 158 + target 44 + value 15 + ] + edge + [ + source 159 + target 200 + value 1 + ] + edge + [ + source 159 + target 197 + value 1 + ] + edge + [ + source 159 + target 44 + value 10 + ] + edge + [ + source 160 + target 219 + value 1 + ] + edge + [ + source 160 + target 200 + value 1 + ] + edge + [ + source 160 + target 197 + value 23 + ] + edge + [ + source 160 + target 189 + value 6 + ] + edge + [ + source 160 + target 44 + value 29 + ] + edge + [ + source 161 + target 162 + value 1 + ] + edge + [ + source 161 + target 233 + value 1 + ] + edge + [ + source 161 + target 189 + value 23 + ] + edge + [ + source 161 + target 231 + value 6 + ] + edge + [ + source 161 + target 44 + value 29 + ] + edge + [ + source 162 + target 163 + value 1 + ] + edge + [ + source 162 + target 275 + value 1 + ] + edge + [ + source 162 + target 231 + value 23 + ] + edge + [ + source 162 + target 234 + value 6 + ] + edge + [ + source 162 + target 269 + value 1 + ] + edge + [ + source 162 + target 44 + value 29 + ] + edge + [ + source 163 + target 164 + value 1 + ] + edge + [ + source 163 + target 276 + value 1 + ] + edge + [ + source 163 + target 269 + value 23 + ] + edge + [ + source 163 + target 271 + value 1 + ] + edge + [ + source 163 + target 44 + value 29 + ] + edge + [ + source 164 + target 165 + value 1 + ] + edge + [ + source 164 + target 276 + value 1 + ] + edge + [ + source 164 + target 273 + value 23 + ] + edge + [ + source 164 + target 268 + value 1 + ] + edge + [ + source 164 + target 44 + value 29 + ] + edge + [ + source 165 + target 166 + value 1 + ] + edge + [ + source 165 + target 97 + value 1 + ] + edge + [ + source 165 + target 249 + value 23 + ] + edge + [ + source 165 + target 227 + value 1 + ] + edge + [ + source 165 + target 44 + value 29 + ] + edge + [ + source 166 + target 167 + value 1 + ] + edge + [ + source 166 + target 97 + value 1 + ] + edge + [ + source 166 + target 227 + value 23 + ] + edge + [ + source 166 + target 221 + value 1 + ] + edge + [ + source 166 + target 44 + value 29 + ] + edge + [ + source 167 + target 97 + value 1 + ] + edge + [ + source 167 + target 227 + value 23 + ] + edge + [ + source 167 + target 221 + value 1 + ] + edge + [ + source 167 + target 44 + value 29 + ] + edge + [ + source 168 + target 156 + value 1 + ] + edge + [ + source 168 + target 233 + value 4 + ] + edge + [ + source 168 + target 275 + value 9 + ] + edge + [ + source 168 + target 189 + value 1 + ] + edge + [ + source 168 + target 231 + value 9 + ] + edge + [ + source 168 + target 234 + value 22 + ] + edge + [ + source 168 + target 269 + value 7 + ] + edge + [ + source 168 + target 44 + value 22 + ] + edge + [ + source 169 + target 270 + value 1 + ] + edge + [ + source 169 + target 272 + value 1 + ] + edge + [ + source 169 + target 276 + value 4 + ] + edge + [ + source 169 + target 277 + value 9 + ] + edge + [ + source 169 + target 234 + value 1 + ] + edge + [ + source 169 + target 269 + value 9 + ] + edge + [ + source 169 + target 271 + value 22 + ] + edge + [ + source 169 + target 273 + value 7 + ] + edge + [ + source 169 + target 44 + value 22 + ] + edge + [ + source 170 + target 12 + value 5 + ] + edge + [ + source 170 + target 2 + value 4 + ] + edge + [ + source 170 + target 117 + value 4 + ] + edge + [ + source 170 + target 118 + value 2 + ] + edge + [ + source 170 + target 119 + value 1 + ] + edge + [ + source 170 + target 255 + value 1 + ] + edge + [ + source 170 + target 207 + value 1 + ] + edge + [ + source 170 + target 256 + value 1 + ] + edge + [ + source 171 + target 213 + value 22 + ] + edge + [ + source 171 + target 137 + value 61 + ] + edge + [ + source 171 + target 185 + value 1 + ] + edge + [ + source 171 + target 125 + value 2 + ] + edge + [ + source 171 + target 217 + value 1 + ] + edge + [ + source 171 + target 42 + value 2 + ] + edge + [ + source 171 + target 246 + value 1 + ] + edge + [ + source 171 + target 190 + value 4 + ] + edge + [ + source 172 + target 201 + value 1 + ] + edge + [ + source 172 + target 151 + value 1 + ] + edge + [ + source 172 + target 154 + value 2 + ] + edge + [ + source 172 + target 12 + value 8 + ] + edge + [ + source 172 + target 2 + value 10 + ] + edge + [ + source 172 + target 84 + value 9 + ] + edge + [ + source 172 + target 86 + value 6 + ] + edge + [ + source 172 + target 117 + value 6 + ] + edge + [ + source 172 + target 118 + value 1 + ] + edge + [ + source 172 + target 3 + value 3 + ] + edge + [ + source 172 + target 4 + value 1 + ] + edge + [ + source 172 + target 187 + value 1 + ] + edge + [ + source 172 + target 160 + value 1 + ] + edge + [ + source 172 + target 232 + value 4 + ] + edge + [ + source 172 + target 168 + value 5 + ] + edge + [ + source 172 + target 192 + value 7 + ] + edge + [ + source 172 + target 169 + value 3 + ] + edge + [ + source 172 + target 253 + value 4 + ] + edge + [ + source 172 + target 137 + value 4 + ] + edge + [ + source 172 + target 129 + value 1 + ] + edge + [ + source 172 + target 170 + value 1 + ] + edge + [ + source 172 + target 185 + value 1 + ] + edge + [ + source 172 + target 125 + value 1 + ] + edge + [ + source 172 + target 42 + value 1 + ] + edge + [ + source 172 + target 220 + value 1 + ] + edge + [ + source 172 + target 96 + value 4 + ] + edge + [ + source 172 + target 79 + value 1 + ] + edge + [ + source 172 + target 254 + value 1 + ] + edge + [ + source 172 + target 262 + value 1 + ] + edge + [ + source 172 + target 263 + value 4 + ] + edge + [ + source 172 + target 264 + value 1 + ] + edge + [ + source 172 + target 265 + value 5 + ] + edge + [ + source 172 + target 266 + value 3 + ] + edge + [ + source 172 + target 267 + value 2 + ] + edge + [ + source 173 + target 178 + value 1 + ] + edge + [ + source 174 + target 190 + value 1 + ] + edge + [ + source 175 + target 277 + value 8 + ] + edge + [ + source 175 + target 176 + value 1 + ] + edge + [ + source 175 + target 44 + value 10 + ] + edge + [ + source 176 + target 97 + value 8 + ] + edge + [ + source 176 + target 177 + value 1 + ] + edge + [ + source 176 + target 249 + value 3 + ] + edge + [ + source 176 + target 227 + value 2 + ] + edge + [ + source 176 + target 44 + value 10 + ] + edge + [ + source 177 + target 153 + value 2 + ] + edge + [ + source 177 + target 166 + value 3 + ] + edge + [ + source 177 + target 167 + value 5 + ] + edge + [ + source 177 + target 253 + value 4 + ] + edge + [ + source 177 + target 97 + value 2 + ] + edge + [ + source 177 + target 170 + value 2 + ] + edge + [ + source 177 + target 125 + value 2 + ] + edge + [ + source 177 + target 172 + value 3 + ] + edge + [ + source 177 + target 176 + value 1 + ] + edge + [ + source 177 + target 221 + value 2 + ] + edge + [ + source 177 + target 183 + value 11 + ] + edge + [ + source 177 + target 44 + value 14 + ] + edge + [ + source 178 + target 219 + value 8 + ] + edge + [ + source 178 + target 179 + value 1 + ] + edge + [ + source 178 + target 200 + value 1 + ] + edge + [ + source 178 + target 197 + value 3 + ] + edge + [ + source 178 + target 44 + value 10 + ] + edge + [ + source 179 + target 219 + value 18 + ] + edge + [ + source 179 + target 233 + value 11 + ] + edge + [ + source 179 + target 180 + value 1 + ] + edge + [ + source 179 + target 197 + value 3 + ] + edge + [ + source 179 + target 189 + value 2 + ] + edge + [ + source 179 + target 44 + value 31 + ] + edge + [ + source 180 + target 233 + value 8 + ] + edge + [ + source 180 + target 181 + value 1 + ] + edge + [ + source 180 + target 231 + value 1 + ] + edge + [ + source 180 + target 44 + value 10 + ] + edge + [ + source 181 + target 275 + value 8 + ] + edge + [ + source 181 + target 182 + value 1 + ] + edge + [ + source 181 + target 231 + value 3 + ] + edge + [ + source 181 + target 234 + value 2 + ] + edge + [ + source 181 + target 44 + value 10 + ] + edge + [ + source 182 + target 275 + value 8 + ] + edge + [ + source 182 + target 188 + value 1 + ] + edge + [ + source 182 + target 44 + value 10 + ] + edge + [ + source 183 + target 177 + value 1 + ] + edge + [ + source 183 + target 44 + value 8 + ] + edge + [ + source 184 + target 12 + value 3 + ] + edge + [ + source 184 + target 2 + value 7 + ] + edge + [ + source 184 + target 117 + value 1 + ] + edge + [ + source 184 + target 118 + value 3 + ] + edge + [ + source 184 + target 98 + value 1 + ] + edge + [ + source 184 + target 194 + value 1 + ] + edge + [ + source 184 + target 172 + value 3 + ] + edge + [ + source 184 + target 42 + value 1 + ] + edge + [ + source 184 + target 256 + value 1 + ] + edge + [ + source 185 + target 218 + value 22 + ] + edge + [ + source 185 + target 137 + value 61 + ] + edge + [ + source 185 + target 171 + value 1 + ] + edge + [ + source 185 + target 172 + value 2 + ] + edge + [ + source 185 + target 217 + value 1 + ] + edge + [ + source 185 + target 42 + value 2 + ] + edge + [ + source 185 + target 246 + value 1 + ] + edge + [ + source 185 + target 190 + value 4 + ] + edge + [ + source 186 + target 190 + value 1 + ] + edge + [ + source 187 + target 151 + value 1 + ] + edge + [ + source 187 + target 4 + value 1 + ] + edge + [ + source 187 + target 199 + value 1 + ] + edge + [ + source 187 + target 160 + value 1 + ] + edge + [ + source 187 + target 219 + value 1 + ] + edge + [ + source 187 + target 97 + value 2 + ] + edge + [ + source 187 + target 111 + value 1 + ] + edge + [ + source 187 + target 220 + value 1 + ] + edge + [ + source 187 + target 173 + value 5 + ] + edge + [ + source 187 + target 174 + value 7 + ] + edge + [ + source 187 + target 221 + value 3 + ] + edge + [ + source 187 + target 44 + value 6 + ] + edge + [ + source 188 + target 276 + value 8 + ] + edge + [ + source 188 + target 279 + value 1 + ] + edge + [ + source 188 + target 269 + value 3 + ] + edge + [ + source 188 + target 271 + value 2 + ] + edge + [ + source 188 + target 44 + value 10 + ] + edge + [ + source 189 + target 155 + value 2 + ] + edge + [ + source 189 + target 237 + value 2 + ] + edge + [ + source 189 + target 44 + value 25 + ] + edge + [ + source 191 + target 113 + value 2 + ] + edge + [ + source 191 + target 102 + value 2 + ] + edge + [ + source 191 + target 108 + value 1 + ] + edge + [ + source 191 + target 157 + value 1 + ] + edge + [ + source 191 + target 116 + value 1 + ] + edge + [ + source 191 + target 109 + value 2 + ] + edge + [ + source 191 + target 146 + value 1 + ] + edge + [ + source 191 + target 141 + value 1 + ] + edge + [ + source 191 + target 117 + value 3 + ] + edge + [ + source 191 + target 202 + value 6 + ] + edge + [ + source 191 + target 119 + value 2 + ] + edge + [ + source 191 + target 120 + value 2 + ] + edge + [ + source 191 + target 110 + value 2 + ] + edge + [ + source 191 + target 163 + value 1 + ] + edge + [ + source 191 + target 142 + value 3 + ] + edge + [ + source 191 + target 215 + value 3 + ] + edge + [ + source 191 + target 132 + value 2 + ] + edge + [ + source 191 + target 173 + value 1 + ] + edge + [ + source 191 + target 186 + value 3 + ] + edge + [ + source 191 + target 182 + value 1 + ] + edge + [ + source 191 + target 238 + value 2 + ] + edge + [ + source 191 + target 244 + value 10 + ] + edge + [ + source 191 + target 44 + value 3 + ] + edge + [ + source 191 + target 44 + value 22 + ] + edge + [ + source 192 + target 157 + value 1 + ] + edge + [ + source 192 + target 158 + value 1 + ] + edge + [ + source 192 + target 275 + value 4 + ] + edge + [ + source 192 + target 276 + value 9 + ] + edge + [ + source 192 + target 231 + value 1 + ] + edge + [ + source 192 + target 234 + value 9 + ] + edge + [ + source 192 + target 269 + value 22 + ] + edge + [ + source 192 + target 271 + value 7 + ] + edge + [ + source 192 + target 44 + value 22 + ] + edge + [ + source 193 + target 1 + value 1 + ] + edge + [ + source 193 + target 114 + value 3 + ] + edge + [ + source 193 + target 12 + value 5 + ] + edge + [ + source 193 + target 2 + value 7 + ] + edge + [ + source 193 + target 84 + value 1 + ] + edge + [ + source 193 + target 213 + value 2 + ] + edge + [ + source 193 + target 218 + value 1 + ] + edge + [ + source 193 + target 195 + value 1 + ] + edge + [ + source 193 + target 14 + value 1 + ] + edge + [ + source 193 + target 8 + value 5 + ] + edge + [ + source 193 + target 9 + value 5 + ] + edge + [ + source 193 + target 214 + value 2 + ] + edge + [ + source 193 + target 210 + value 4 + ] + edge + [ + source 194 + target 12 + value 7 + ] + edge + [ + source 194 + target 2 + value 7 + ] + edge + [ + source 194 + target 118 + value 23 + ] + edge + [ + source 194 + target 226 + value 1 + ] + edge + [ + source 194 + target 170 + value 1 + ] + edge + [ + source 194 + target 204 + value 1 + ] + edge + [ + source 195 + target 1 + value 2 + ] + edge + [ + source 195 + target 114 + value 5 + ] + edge + [ + source 195 + target 213 + value 7 + ] + edge + [ + source 195 + target 218 + value 7 + ] + edge + [ + source 195 + target 187 + value 2 + ] + edge + [ + source 195 + target 166 + value 2 + ] + edge + [ + source 195 + target 97 + value 1 + ] + edge + [ + source 195 + target 97 + value 3 + ] + edge + [ + source 195 + target 193 + value 5 + ] + edge + [ + source 195 + target 250 + value 1 + ] + edge + [ + source 195 + target 111 + value 2 + ] + edge + [ + source 195 + target 8 + value 2 + ] + edge + [ + source 195 + target 9 + value 3 + ] + edge + [ + source 195 + target 59 + value 1 + ] + edge + [ + source 195 + target 64 + value 1 + ] + edge + [ + source 195 + target 214 + value 2 + ] + edge + [ + source 195 + target 210 + value 4 + ] + edge + [ + source 195 + target 85 + value 1 + ] + edge + [ + source 195 + target 44 + value 5 + ] + edge + [ + source 195 + target 190 + value 5 + ] + edge + [ + source 196 + target 219 + value 8 + ] + edge + [ + source 196 + target 178 + value 1 + ] + edge + [ + source 196 + target 133 + value 1 + ] + edge + [ + source 196 + target 200 + value 3 + ] + edge + [ + source 196 + target 44 + value 10 + ] + edge + [ + source 197 + target 155 + value 2 + ] + edge + [ + source 197 + target 237 + value 2 + ] + edge + [ + source 197 + target 44 + value 8 + ] + edge + [ + source 198 + target 105 + value 3 + ] + edge + [ + source 198 + target 86 + value 2 + ] + edge + [ + source 198 + target 117 + value 1 + ] + edge + [ + source 198 + target 199 + value 5 + ] + edge + [ + source 198 + target 123 + value 2 + ] + edge + [ + source 198 + target 119 + value 4 + ] + edge + [ + source 198 + target 98 + value 1 + ] + edge + [ + source 198 + target 110 + value 1 + ] + edge + [ + source 198 + target 111 + value 1 + ] + edge + [ + source 198 + target 148 + value 2 + ] + edge + [ + source 198 + target 130 + value 1 + ] + edge + [ + source 198 + target 103 + value 3 + ] + edge + [ + source 198 + target 85 + value 2 + ] + edge + [ + source 198 + target 112 + value 2 + ] + edge + [ + source 198 + target 200 + value 1 + ] + edge + [ + source 199 + target 84 + value 1 + ] + edge + [ + source 199 + target 86 + value 8 + ] + edge + [ + source 199 + target 202 + value 4 + ] + edge + [ + source 199 + target 226 + value 2 + ] + edge + [ + source 199 + target 123 + value 5 + ] + edge + [ + source 199 + target 119 + value 4 + ] + edge + [ + source 199 + target 187 + value 1 + ] + edge + [ + source 199 + target 142 + value 2 + ] + edge + [ + source 199 + target 185 + value 1 + ] + edge + [ + source 199 + target 145 + value 1 + ] + edge + [ + source 199 + target 227 + value 1 + ] + edge + [ + source 199 + target 44 + value 2 + ] + edge + [ + source 200 + target 219 + value 1 + ] + edge + [ + source 200 + target 196 + value 2 + ] + edge + [ + source 200 + target 44 + value 8 + ] + edge + [ + source 201 + target 12 + value 5 + ] + edge + [ + source 201 + target 2 + value 3 + ] + edge + [ + source 201 + target 84 + value 4 + ] + edge + [ + source 201 + target 86 + value 5 + ] + edge + [ + source 201 + target 117 + value 1 + ] + edge + [ + source 201 + target 118 + value 1 + ] + edge + [ + source 201 + target 119 + value 1 + ] + edge + [ + source 201 + target 0 + value 2 + ] + edge + [ + source 201 + target 5 + value 2 + ] + edge + [ + source 201 + target 137 + value 1 + ] + edge + [ + source 201 + target 125 + value 1 + ] + edge + [ + source 201 + target 172 + value 2 + ] + edge + [ + source 201 + target 203 + value 1 + ] + edge + [ + source 201 + target 13 + value 3 + ] + edge + [ + source 201 + target 6 + value 2 + ] + edge + [ + source 201 + target 65 + value 1 + ] + edge + [ + source 202 + target 84 + value 5 + ] + edge + [ + source 202 + target 199 + value 5 + ] + edge + [ + source 202 + target 226 + value 2 + ] + edge + [ + source 202 + target 123 + value 6 + ] + edge + [ + source 202 + target 98 + value 4 + ] + edge + [ + source 202 + target 187 + value 1 + ] + edge + [ + source 202 + target 191 + value 1 + ] + edge + [ + source 202 + target 171 + value 1 + ] + edge + [ + source 202 + target 145 + value 2 + ] + edge + [ + source 202 + target 133 + value 1 + ] + edge + [ + source 202 + target 227 + value 1 + ] + edge + [ + source 202 + target 44 + value 2 + ] + edge + [ + source 203 + target 101 + value 1 + ] + edge + [ + source 203 + target 201 + value 1 + ] + edge + [ + source 203 + target 12 + value 2 + ] + edge + [ + source 203 + target 2 + value 2 + ] + edge + [ + source 203 + target 84 + value 5 + ] + edge + [ + source 203 + target 86 + value 6 + ] + edge + [ + source 203 + target 118 + value 2 + ] + edge + [ + source 203 + target 4 + value 1 + ] + edge + [ + source 203 + target 123 + value 2 + ] + edge + [ + source 203 + target 187 + value 4 + ] + edge + [ + source 203 + target 233 + value 1 + ] + edge + [ + source 203 + target 193 + value 2 + ] + edge + [ + source 203 + target 172 + value 4 + ] + edge + [ + source 203 + target 148 + value 1 + ] + edge + [ + source 203 + target 8 + value 2 + ] + edge + [ + source 203 + target 190 + value 3 + ] + edge + [ + source 204 + target 2 + value 6 + ] + edge + [ + source 204 + target 86 + value 3 + ] + edge + [ + source 204 + target 118 + value 6 + ] + edge + [ + source 204 + target 4 + value 3 + ] + edge + [ + source 204 + target 199 + value 1 + ] + edge + [ + source 204 + target 226 + value 1 + ] + edge + [ + source 204 + target 119 + value 2 + ] + edge + [ + source 204 + target 98 + value 7 + ] + edge + [ + source 204 + target 187 + value 4 + ] + edge + [ + source 204 + target 225 + value 2 + ] + edge + [ + source 204 + target 95 + value 3 + ] + edge + [ + source 204 + target 219 + value 2 + ] + edge + [ + source 204 + target 233 + value 1 + ] + edge + [ + source 204 + target 194 + value 3 + ] + edge + [ + source 204 + target 172 + value 2 + ] + edge + [ + source 204 + target 207 + value 5 + ] + edge + [ + source 204 + target 252 + value 2 + ] + edge + [ + source 204 + target 220 + value 3 + ] + edge + [ + source 204 + target 205 + value 1 + ] + edge + [ + source 204 + target 238 + value 2 + ] + edge + [ + source 204 + target 245 + value 1 + ] + edge + [ + source 204 + target 221 + value 1 + ] + edge + [ + source 204 + target 189 + value 2 + ] + edge + [ + source 204 + target 231 + value 4 + ] + edge + [ + source 204 + target 44 + value 7 + ] + edge + [ + source 204 + target 190 + value 2 + ] + edge + [ + source 205 + target 109 + value 2 + ] + edge + [ + source 205 + target 84 + value 1 + ] + edge + [ + source 205 + target 86 + value 16 + ] + edge + [ + source 205 + target 202 + value 1 + ] + edge + [ + source 205 + target 198 + value 1 + ] + edge + [ + source 205 + target 119 + value 1 + ] + edge + [ + source 205 + target 98 + value 2 + ] + edge + [ + source 205 + target 142 + value 1 + ] + edge + [ + source 205 + target 125 + value 1 + ] + edge + [ + source 205 + target 172 + value 1 + ] + edge + [ + source 205 + target 111 + value 4 + ] + edge + [ + source 205 + target 130 + value 4 + ] + edge + [ + source 205 + target 33 + value 1 + ] + edge + [ + source 206 + target 67 + value 3 + ] + edge + [ + source 206 + target 84 + value 6 + ] + edge + [ + source 206 + target 86 + value 5 + ] + edge + [ + source 206 + target 98 + value 1 + ] + edge + [ + source 206 + target 225 + value 3 + ] + edge + [ + source 206 + target 95 + value 4 + ] + edge + [ + source 206 + target 159 + value 1 + ] + edge + [ + source 206 + target 159 + value 1 + ] + edge + [ + source 206 + target 125 + value 4 + ] + edge + [ + source 206 + target 172 + value 6 + ] + edge + [ + source 206 + target 42 + value 3 + ] + edge + [ + source 206 + target 96 + value 1 + ] + edge + [ + source 206 + target 78 + value 1 + ] + edge + [ + source 207 + target 2 + value 6 + ] + edge + [ + source 207 + target 86 + value 3 + ] + edge + [ + source 207 + target 118 + value 6 + ] + edge + [ + source 207 + target 4 + value 3 + ] + edge + [ + source 207 + target 199 + value 1 + ] + edge + [ + source 207 + target 226 + value 1 + ] + edge + [ + source 207 + target 119 + value 3 + ] + edge + [ + source 207 + target 98 + value 7 + ] + edge + [ + source 207 + target 187 + value 4 + ] + edge + [ + source 207 + target 95 + value 3 + ] + edge + [ + source 207 + target 219 + value 2 + ] + edge + [ + source 207 + target 233 + value 1 + ] + edge + [ + source 207 + target 194 + value 3 + ] + edge + [ + source 207 + target 172 + value 2 + ] + edge + [ + source 207 + target 204 + value 4 + ] + edge + [ + source 207 + target 252 + value 2 + ] + edge + [ + source 207 + target 256 + value 3 + ] + edge + [ + source 207 + target 205 + value 1 + ] + edge + [ + source 207 + target 238 + value 2 + ] + edge + [ + source 207 + target 245 + value 1 + ] + edge + [ + source 207 + target 221 + value 1 + ] + edge + [ + source 207 + target 189 + value 2 + ] + edge + [ + source 207 + target 231 + value 4 + ] + edge + [ + source 207 + target 44 + value 7 + ] + edge + [ + source 207 + target 190 + value 2 + ] + edge + [ + source 208 + target 101 + value 1 + ] + edge + [ + source 208 + target 126 + value 5 + ] + edge + [ + source 208 + target 10 + value 1 + ] + edge + [ + source 208 + target 102 + value 10 + ] + edge + [ + source 208 + target 135 + value 4 + ] + edge + [ + source 208 + target 140 + value 1 + ] + edge + [ + source 208 + target 120 + value 1 + ] + edge + [ + source 209 + target 13 + value 2 + ] + edge + [ + source 209 + target 6 + value 2 + ] + edge + [ + source 209 + target 23 + value 1 + ] + edge + [ + source 209 + target 48 + value 1 + ] + edge + [ + source 209 + target 46 + value 1 + ] + edge + [ + source 209 + target 64 + value 1 + ] + edge + [ + source 209 + target 131 + value 2 + ] + edge + [ + source 209 + target 25 + value 2 + ] + edge + [ + source 209 + target 73 + value 2 + ] + edge + [ + source 209 + target 75 + value 2 + ] + edge + [ + source 209 + target 44 + value 5 + ] + edge + [ + source 210 + target 114 + value 1 + ] + edge + [ + source 210 + target 213 + value 1 + ] + edge + [ + source 210 + target 218 + value 3 + ] + edge + [ + source 210 + target 48 + value 2 + ] + edge + [ + source 210 + target 44 + value 4 + ] + edge + [ + source 211 + target 12 + value 15 + ] + edge + [ + source 211 + target 31 + value 1 + ] + edge + [ + source 211 + target 132 + value 2 + ] + edge + [ + source 211 + target 130 + value 9 + ] + edge + [ + source 211 + target 210 + value 2 + ] + edge + [ + source 211 + target 74 + value 6 + ] + edge + [ + source 212 + target 2 + value 11 + ] + edge + [ + source 212 + target 132 + value 4 + ] + edge + [ + source 212 + target 130 + value 1 + ] + edge + [ + source 212 + target 73 + value 6 + ] + edge + [ + source 213 + target 84 + value 1 + ] + edge + [ + source 213 + target 3 + value 2 + ] + edge + [ + source 213 + target 4 + value 1 + ] + edge + [ + source 213 + target 206 + value 1 + ] + edge + [ + source 213 + target 137 + value 1 + ] + edge + [ + source 213 + target 185 + value 4 + ] + edge + [ + source 213 + target 217 + value 1 + ] + edge + [ + source 213 + target 148 + value 1 + ] + edge + [ + source 213 + target 132 + value 2 + ] + edge + [ + source 213 + target 130 + value 1 + ] + edge + [ + source 213 + target 72 + value 1 + ] + edge + [ + source 213 + target 37 + value 1 + ] + edge + [ + source 213 + target 190 + value 5 + ] + edge + [ + source 214 + target 1 + value 1 + ] + edge + [ + source 214 + target 213 + value 1 + ] + edge + [ + source 214 + target 218 + value 4 + ] + edge + [ + source 214 + target 47 + value 5 + ] + edge + [ + source 214 + target 48 + value 1 + ] + edge + [ + source 214 + target 87 + value 1 + ] + edge + [ + source 214 + target 44 + value 5 + ] + edge + [ + source 215 + target 216 + value 2 + ] + edge + [ + source 215 + target 84 + value 10 + ] + edge + [ + source 215 + target 86 + value 1 + ] + edge + [ + source 215 + target 123 + value 1 + ] + edge + [ + source 215 + target 98 + value 2 + ] + edge + [ + source 215 + target 203 + value 3 + ] + edge + [ + source 215 + target 132 + value 2 + ] + edge + [ + source 215 + target 130 + value 1 + ] + edge + [ + source 216 + target 118 + value 1 + ] + edge + [ + source 216 + target 3 + value 1 + ] + edge + [ + source 216 + target 225 + value 5 + ] + edge + [ + source 216 + target 71 + value 3 + ] + edge + [ + source 216 + target 15 + value 1 + ] + edge + [ + source 216 + target 125 + value 4 + ] + edge + [ + source 216 + target 172 + value 2 + ] + edge + [ + source 216 + target 35 + value 1 + ] + edge + [ + source 216 + target 93 + value 1 + ] + edge + [ + source 216 + target 239 + value 1 + ] + edge + [ + source 217 + target 213 + value 11 + ] + edge + [ + source 217 + target 206 + value 1 + ] + edge + [ + source 217 + target 137 + value 3 + ] + edge + [ + source 217 + target 171 + value 10 + ] + edge + [ + source 217 + target 125 + value 2 + ] + edge + [ + source 217 + target 42 + value 2 + ] + edge + [ + source 217 + target 190 + value 1 + ] + edge + [ + source 218 + target 137 + value 1 + ] + edge + [ + source 218 + target 171 + value 4 + ] + edge + [ + source 218 + target 217 + value 1 + ] + edge + [ + source 218 + target 203 + value 1 + ] + edge + [ + source 218 + target 145 + value 1 + ] + edge + [ + source 218 + target 132 + value 3 + ] + edge + [ + source 218 + target 130 + value 3 + ] + edge + [ + source 218 + target 131 + value 1 + ] + edge + [ + source 218 + target 85 + value 1 + ] + edge + [ + source 218 + target 73 + value 1 + ] + edge + [ + source 218 + target 74 + value 2 + ] + edge + [ + source 218 + target 75 + value 1 + ] + edge + [ + source 218 + target 190 + value 5 + ] + edge + [ + source 219 + target 159 + value 2 + ] + edge + [ + source 219 + target 200 + value 1 + ] + edge + [ + source 219 + target 197 + value 1 + ] + edge + [ + source 219 + target 44 + value 29 + ] + edge + [ + source 220 + target 2 + value 1 + ] + edge + [ + source 220 + target 118 + value 1 + ] + edge + [ + source 220 + target 119 + value 1 + ] + edge + [ + source 220 + target 172 + value 1 + ] + edge + [ + source 220 + target 252 + value 3 + ] + edge + [ + source 220 + target 256 + value 1 + ] + edge + [ + source 220 + target 177 + value 1 + ] + edge + [ + source 221 + target 177 + value 1 + ] + edge + [ + source 221 + target 44 + value 8 + ] + edge + [ + source 222 + target 72 + value 3 + ] + edge + [ + source 222 + target 112 + value 5 + ] + edge + [ + source 223 + target 131 + value 5 + ] + edge + [ + source 223 + target 104 + value 6 + ] + edge + [ + source 224 + target 67 + value 2 + ] + edge + [ + source 224 + target 114 + value 1 + ] + edge + [ + source 224 + target 12 + value 10 + ] + edge + [ + source 224 + target 2 + value 4 + ] + edge + [ + source 224 + target 84 + value 4 + ] + edge + [ + source 224 + target 86 + value 2 + ] + edge + [ + source 224 + target 117 + value 10 + ] + edge + [ + source 224 + target 118 + value 2 + ] + edge + [ + source 224 + target 3 + value 3 + ] + edge + [ + source 224 + target 4 + value 2 + ] + edge + [ + source 224 + target 137 + value 1 + ] + edge + [ + source 224 + target 129 + value 1 + ] + edge + [ + source 224 + target 125 + value 2 + ] + edge + [ + source 224 + target 133 + value 1 + ] + edge + [ + source 224 + target 190 + value 3 + ] + edge + [ + source 225 + target 63 + value 4 + ] + edge + [ + source 225 + target 2 + value 2 + ] + edge + [ + source 225 + target 198 + value 1 + ] + edge + [ + source 225 + target 98 + value 1 + ] + edge + [ + source 225 + target 191 + value 4 + ] + edge + [ + source 225 + target 125 + value 2 + ] + edge + [ + source 225 + target 207 + value 1 + ] + edge + [ + source 225 + target 204 + value 2 + ] + edge + [ + source 225 + target 131 + value 2 + ] + edge + [ + source 225 + target 56 + value 1 + ] + edge + [ + source 225 + target 39 + value 1 + ] + edge + [ + source 226 + target 12 + value 3 + ] + edge + [ + source 226 + target 2 + value 1 + ] + edge + [ + source 226 + target 84 + value 4 + ] + edge + [ + source 226 + target 117 + value 1 + ] + edge + [ + source 226 + target 118 + value 1 + ] + edge + [ + source 226 + target 4 + value 1 + ] + edge + [ + source 226 + target 202 + value 1 + ] + edge + [ + source 226 + target 98 + value 1 + ] + edge + [ + source 226 + target 166 + value 1 + ] + edge + [ + source 226 + target 166 + value 1 + ] + edge + [ + source 226 + target 195 + value 1 + ] + edge + [ + source 226 + target 228 + value 2 + ] + edge + [ + source 226 + target 229 + value 1 + ] + edge + [ + source 226 + target 125 + value 1 + ] + edge + [ + source 226 + target 172 + value 1 + ] + edge + [ + source 226 + target 207 + value 2 + ] + edge + [ + source 226 + target 204 + value 2 + ] + edge + [ + source 226 + target 203 + value 1 + ] + edge + [ + source 226 + target 111 + value 1 + ] + edge + [ + source 226 + target 145 + value 1 + ] + edge + [ + source 226 + target 148 + value 1 + ] + edge + [ + source 226 + target 176 + value 1 + ] + edge + [ + source 226 + target 176 + value 1 + ] + edge + [ + source 226 + target 190 + value 1 + ] + edge + [ + source 226 + target 190 + value 1 + ] + edge + [ + source 227 + target 44 + value 8 + ] + edge + [ + source 228 + target 118 + value 1 + ] + edge + [ + source 228 + target 202 + value 3 + ] + edge + [ + source 228 + target 226 + value 5 + ] + edge + [ + source 228 + target 198 + value 1 + ] + edge + [ + source 228 + target 123 + value 1 + ] + edge + [ + source 228 + target 166 + value 1 + ] + edge + [ + source 228 + target 137 + value 2 + ] + edge + [ + source 228 + target 229 + value 5 + ] + edge + [ + source 228 + target 255 + value 5 + ] + edge + [ + source 228 + target 257 + value 4 + ] + edge + [ + source 228 + target 145 + value 2 + ] + edge + [ + source 229 + target 226 + value 3 + ] + edge + [ + source 229 + target 123 + value 1 + ] + edge + [ + source 229 + target 137 + value 2 + ] + edge + [ + source 229 + target 228 + value 6 + ] + edge + [ + source 229 + target 255 + value 1 + ] + edge + [ + source 229 + target 257 + value 5 + ] + edge + [ + source 229 + target 145 + value 2 + ] + edge + [ + source 230 + target 151 + value 1 + ] + edge + [ + source 230 + target 154 + value 1 + ] + edge + [ + source 230 + target 219 + value 4 + ] + edge + [ + source 230 + target 200 + value 1 + ] + edge + [ + source 230 + target 197 + value 9 + ] + edge + [ + source 230 + target 189 + value 22 + ] + edge + [ + source 230 + target 231 + value 7 + ] + edge + [ + source 230 + target 44 + value 22 + ] + edge + [ + source 231 + target 44 + value 25 + ] + edge + [ + source 232 + target 155 + value 1 + ] + edge + [ + source 232 + target 219 + value 1 + ] + edge + [ + source 232 + target 233 + value 4 + ] + edge + [ + source 232 + target 197 + value 1 + ] + edge + [ + source 232 + target 189 + value 9 + ] + edge + [ + source 232 + target 231 + value 22 + ] + edge + [ + source 232 + target 234 + value 7 + ] + edge + [ + source 232 + target 44 + value 22 + ] + edge + [ + source 233 + target 161 + value 2 + ] + edge + [ + source 233 + target 189 + value 1 + ] + edge + [ + source 233 + target 231 + value 1 + ] + edge + [ + source 233 + target 44 + value 29 + ] + edge + [ + source 234 + target 44 + value 25 + ] + edge + [ + source 235 + target 211 + value 3 + ] + edge + [ + source 235 + target 85 + value 2 + ] + edge + [ + source 235 + target 77 + value 1 + ] + edge + [ + source 236 + target 36 + value 1 + ] + edge + [ + source 236 + target 212 + value 3 + ] + edge + [ + source 236 + target 89 + value 1 + ] + edge + [ + source 236 + target 74 + value 1 + ] + edge + [ + source 236 + target 77 + value 1 + ] + edge + [ + source 237 + target 219 + value 19 + ] + edge + [ + source 237 + target 233 + value 1 + ] + edge + [ + source 237 + target 178 + value 1 + ] + edge + [ + source 237 + target 238 + value 1 + ] + edge + [ + source 237 + target 197 + value 6 + ] + edge + [ + source 237 + target 189 + value 4 + ] + edge + [ + source 237 + target 44 + value 23 + ] + edge + [ + source 238 + target 233 + value 4 + ] + edge + [ + source 238 + target 142 + value 1 + ] + edge + [ + source 238 + target 200 + value 1 + ] + edge + [ + source 238 + target 231 + value 2 + ] + edge + [ + source 238 + target 44 + value 2 + ] + edge + [ + source 238 + target 44 + value 8 + ] + edge + [ + source 239 + target 114 + value 1 + ] + edge + [ + source 239 + target 216 + value 1 + ] + edge + [ + source 239 + target 12 + value 2 + ] + edge + [ + source 239 + target 2 + value 3 + ] + edge + [ + source 239 + target 3 + value 1 + ] + edge + [ + source 239 + target 129 + value 1 + ] + edge + [ + source 239 + target 21 + value 1 + ] + edge + [ + source 239 + target 90 + value 2 + ] + edge + [ + source 239 + target 214 + value 1 + ] + edge + [ + source 240 + target 95 + value 5 + ] + edge + [ + source 240 + target 58 + value 1 + ] + edge + [ + source 240 + target 27 + value 1 + ] + edge + [ + source 240 + target 172 + value 2 + ] + edge + [ + source 240 + target 23 + value 1 + ] + edge + [ + source 240 + target 88 + value 1 + ] + edge + [ + source 241 + target 16 + value 1 + ] + edge + [ + source 241 + target 1 + value 2 + ] + edge + [ + source 241 + target 12 + value 2 + ] + edge + [ + source 241 + target 84 + value 5 + ] + edge + [ + source 241 + target 86 + value 4 + ] + edge + [ + source 241 + target 137 + value 3 + ] + edge + [ + source 241 + target 24 + value 2 + ] + edge + [ + source 241 + target 36 + value 1 + ] + edge + [ + source 242 + target 190 + value 1 + ] + edge + [ + source 243 + target 190 + value 1 + ] + edge + [ + source 244 + target 202 + value 1 + ] + edge + [ + source 244 + target 199 + value 1 + ] + edge + [ + source 244 + target 195 + value 1 + ] + edge + [ + source 244 + target 193 + value 2 + ] + edge + [ + source 244 + target 245 + value 1 + ] + edge + [ + source 244 + target 283 + value 1 + ] + edge + [ + source 244 + target 44 + value 10 + ] + edge + [ + source 245 + target 187 + value 1 + ] + edge + [ + source 245 + target 219 + value 2 + ] + edge + [ + source 245 + target 233 + value 4 + ] + edge + [ + source 245 + target 275 + value 4 + ] + edge + [ + source 245 + target 276 + value 10 + ] + edge + [ + source 245 + target 142 + value 1 + ] + edge + [ + source 245 + target 148 + value 3 + ] + edge + [ + source 245 + target 252 + value 1 + ] + edge + [ + source 245 + target 282 + value 2 + ] + edge + [ + source 245 + target 238 + value 1 + ] + edge + [ + source 245 + target 283 + value 1 + ] + edge + [ + source 245 + target 200 + value 1 + ] + edge + [ + source 245 + target 197 + value 1 + ] + edge + [ + source 245 + target 189 + value 1 + ] + edge + [ + source 245 + target 231 + value 2 + ] + edge + [ + source 245 + target 234 + value 4 + ] + edge + [ + source 245 + target 269 + value 3 + ] + edge + [ + source 245 + target 271 + value 5 + ] + edge + [ + source 245 + target 44 + value 8 + ] + edge + [ + source 246 + target 277 + value 8 + ] + edge + [ + source 246 + target 175 + value 1 + ] + edge + [ + source 246 + target 273 + value 3 + ] + edge + [ + source 246 + target 268 + value 2 + ] + edge + [ + source 246 + target 44 + value 30 + ] + edge + [ + source 247 + target 12 + value 27 + ] + edge + [ + source 247 + target 137 + value 3 + ] + edge + [ + source 247 + target 172 + value 28 + ] + edge + [ + source 247 + target 248 + value 1 + ] + edge + [ + source 247 + target 190 + value 1 + ] + edge + [ + source 248 + target 2 + value 27 + ] + edge + [ + source 248 + target 137 + value 3 + ] + edge + [ + source 248 + target 125 + value 28 + ] + edge + [ + source 248 + target 247 + value 1 + ] + edge + [ + source 248 + target 190 + value 1 + ] + edge + [ + source 249 + target 44 + value 8 + ] + edge + [ + source 250 + target 167 + value 1 + ] + edge + [ + source 250 + target 97 + value 1 + ] + edge + [ + source 250 + target 204 + value 1 + ] + edge + [ + source 250 + target 44 + value 1 + ] + edge + [ + source 251 + target 97 + value 1 + ] + edge + [ + source 251 + target 44 + value 1 + ] + edge + [ + source 252 + target 190 + value 1 + ] + edge + [ + source 253 + target 152 + value 1 + ] + edge + [ + source 253 + target 153 + value 1 + ] + edge + [ + source 253 + target 97 + value 4 + ] + edge + [ + source 253 + target 249 + value 22 + ] + edge + [ + source 253 + target 227 + value 7 + ] + edge + [ + source 253 + target 273 + value 1 + ] + edge + [ + source 253 + target 268 + value 9 + ] + edge + [ + source 253 + target 44 + value 22 + ] + edge + [ + source 254 + target 97 + value 15 + ] + edge + [ + source 254 + target 177 + value 1 + ] + edge + [ + source 254 + target 44 + value 18 + ] + edge + [ + source 255 + target 12 + value 9 + ] + edge + [ + source 255 + target 2 + value 6 + ] + edge + [ + source 255 + target 117 + value 1 + ] + edge + [ + source 255 + target 257 + value 1 + ] + edge + [ + source 255 + target 125 + value 13 + ] + edge + [ + source 255 + target 177 + value 2 + ] + edge + [ + source 256 + target 12 + value 1 + ] + edge + [ + source 256 + target 117 + value 1 + ] + edge + [ + source 256 + target 98 + value 1 + ] + edge + [ + source 256 + target 125 + value 1 + ] + edge + [ + source 256 + target 252 + value 3 + ] + edge + [ + source 256 + target 220 + value 1 + ] + edge + [ + source 256 + target 177 + value 1 + ] + edge + [ + source 257 + target 12 + value 7 + ] + edge + [ + source 257 + target 2 + value 7 + ] + edge + [ + source 257 + target 117 + value 1 + ] + edge + [ + source 257 + target 118 + value 1 + ] + edge + [ + source 257 + target 255 + value 1 + ] + edge + [ + source 257 + target 125 + value 6 + ] + edge + [ + source 257 + target 172 + value 3 + ] + edge + [ + source 257 + target 177 + value 1 + ] + edge + [ + source 258 + target 12 + value 1 + ] + edge + [ + source 258 + target 137 + value 5 + ] + edge + [ + source 258 + target 170 + value 1 + ] + edge + [ + source 258 + target 125 + value 2 + ] + edge + [ + source 258 + target 177 + value 1 + ] + edge + [ + source 259 + target 123 + value 1 + ] + edge + [ + source 259 + target 167 + value 6 + ] + edge + [ + source 259 + target 137 + value 7 + ] + edge + [ + source 259 + target 184 + value 1 + ] + edge + [ + source 259 + target 258 + value 1 + ] + edge + [ + source 259 + target 172 + value 8 + ] + edge + [ + source 259 + target 177 + value 1 + ] + edge + [ + source 260 + target 191 + value 2 + ] + edge + [ + source 261 + target 12 + value 5 + ] + edge + [ + source 261 + target 117 + value 4 + ] + edge + [ + source 261 + target 137 + value 5 + ] + edge + [ + source 261 + target 142 + value 2 + ] + edge + [ + source 261 + target 185 + value 5 + ] + edge + [ + source 261 + target 172 + value 1 + ] + edge + [ + source 262 + target 233 + value 15 + ] + edge + [ + source 262 + target 44 + value 18 + ] + edge + [ + source 263 + target 233 + value 6 + ] + edge + [ + source 263 + target 275 + value 15 + ] + edge + [ + source 263 + target 44 + value 18 + ] + edge + [ + source 264 + target 275 + value 15 + ] + edge + [ + source 264 + target 44 + value 18 + ] + edge + [ + source 265 + target 275 + value 6 + ] + edge + [ + source 265 + target 276 + value 15 + ] + edge + [ + source 265 + target 44 + value 18 + ] + edge + [ + source 266 + target 276 + value 15 + ] + edge + [ + source 266 + target 44 + value 18 + ] + edge + [ + source 267 + target 276 + value 6 + ] + edge + [ + source 267 + target 277 + value 15 + ] + edge + [ + source 267 + target 44 + value 18 + ] + edge + [ + source 268 + target 44 + value 25 + ] + edge + [ + source 269 + target 44 + value 25 + ] + edge + [ + source 270 + target 269 + value 2 + ] + edge + [ + source 270 + target 271 + value 16 + ] + edge + [ + source 270 + target 44 + value 15 + ] + edge + [ + source 271 + target 44 + value 25 + ] + edge + [ + source 272 + target 164 + value 1 + ] + edge + [ + source 272 + target 271 + value 2 + ] + edge + [ + source 272 + target 273 + value 16 + ] + edge + [ + source 272 + target 44 + value 15 + ] + edge + [ + source 273 + target 44 + value 25 + ] + edge + [ + source 274 + target 273 + value 2 + ] + edge + [ + source 274 + target 268 + value 16 + ] + edge + [ + source 274 + target 44 + value 15 + ] + edge + [ + source 275 + target 162 + value 2 + ] + edge + [ + source 275 + target 234 + value 1 + ] + edge + [ + source 275 + target 269 + value 1 + ] + edge + [ + source 275 + target 44 + value 29 + ] + edge + [ + source 276 + target 164 + value 2 + ] + edge + [ + source 276 + target 271 + value 1 + ] + edge + [ + source 276 + target 273 + value 1 + ] + edge + [ + source 276 + target 44 + value 29 + ] + edge + [ + source 277 + target 165 + value 2 + ] + edge + [ + source 277 + target 249 + value 1 + ] + edge + [ + source 277 + target 268 + value 1 + ] + edge + [ + source 277 + target 44 + value 29 + ] + edge + [ + source 278 + target 274 + value 1 + ] + edge + [ + source 278 + target 277 + value 4 + ] + edge + [ + source 278 + target 97 + value 9 + ] + edge + [ + source 278 + target 269 + value 1 + ] + edge + [ + source 278 + target 271 + value 9 + ] + edge + [ + source 278 + target 273 + value 22 + ] + edge + [ + source 278 + target 268 + value 7 + ] + edge + [ + source 278 + target 44 + value 22 + ] + edge + [ + source 279 + target 276 + value 8 + ] + edge + [ + source 279 + target 246 + value 1 + ] + edge + [ + source 279 + target 44 + value 10 + ] + edge + [ + source 280 + target 277 + value 6 + ] + edge + [ + source 280 + target 97 + value 15 + ] + edge + [ + source 280 + target 44 + value 18 + ] + edge + [ + source 281 + target 277 + value 15 + ] + edge + [ + source 281 + target 44 + value 18 + ] + edge + [ + source 282 + target 219 + value 1 + ] + edge + [ + source 282 + target 142 + value 1 + ] + edge + [ + source 282 + target 238 + value 1 + ] + edge + [ + source 282 + target 200 + value 1 + ] + edge + [ + source 282 + target 44 + value 2 + ] + edge + [ + source 282 + target 44 + value 2 + ] + edge + [ + source 283 + target 202 + value 1 + ] + edge + [ + source 283 + target 199 + value 1 + ] + edge + [ + source 283 + target 276 + value 2 + ] + edge + [ + source 283 + target 195 + value 2 + ] + edge + [ + source 283 + target 193 + value 2 + ] + edge + [ + source 283 + target 244 + value 2 + ] + edge + [ + source 283 + target 44 + value 8 + ] + edge + [ + source 284 + target 187 + value 1 + ] + edge + [ + source 284 + target 233 + value 1 + ] + edge + [ + source 284 + target 275 + value 1 + ] + edge + [ + source 284 + target 276 + value 1 + ] + edge + [ + source 284 + target 277 + value 1 + ] + edge + [ + source 284 + target 142 + value 1 + ] + edge + [ + source 284 + target 145 + value 1 + ] + edge + [ + source 284 + target 244 + value 1 + ] + edge + [ + source 284 + target 249 + value 1 + ] + edge + [ + source 284 + target 227 + value 1 + ] + edge + [ + source 284 + target 221 + value 1 + ] + edge + [ + source 284 + target 183 + value 1 + ] + edge + [ + source 284 + target 271 + value 1 + ] + edge + [ + source 284 + target 273 + value 1 + ] + edge + [ + source 284 + target 268 + value 1 + ] + edge + [ + source 284 + target 44 + value 1 + ] + edge + [ + source 284 + target 44 + value 10 + ] + edge + [ + source 285 + target 44 + value 1 + ] + edge + [ + source 286 + target 44 + value 1 + ] + edge + [ + source 287 + target 44 + value 1 + ] + edge + [ + source 288 + target 44 + value 1 + ] + edge + [ + source 289 + target 44 + value 1 + ] + edge + [ + source 290 + target 44 + value 1 + ] + edge + [ + source 291 + target 44 + value 1 + ] + edge + [ + source 292 + target 44 + value 1 + ] + edge + [ + source 293 + target 44 + value 1 + ] + edge + [ + source 294 + target 44 + value 1 + ] + edge + [ + source 295 + target 190 + value 1 + ] + edge + [ + source 296 + target 190 + value 1 + ] +] diff --git a/examples/simple/centralization.c b/examples/simple/centralization.c new file mode 100644 index 0000000..d745174 --- /dev/null +++ b/examples/simple/centralization.c @@ -0,0 +1,78 @@ +/* -*- 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 + +int main(void) { + + igraph_t graph; + igraph_real_t cent; + + /* Create an undirected star graph, which is the most centralized graph + * with several common centrality scores. */ + printf("undirected star graph:\n"); + igraph_star(&graph, 10, IGRAPH_STAR_UNDIRECTED, /*center=*/ 0); + + igraph_centralization_degree(&graph, /*res=*/ NULL, + /*mode=*/ IGRAPH_ALL, IGRAPH_NO_LOOPS, + ¢, /*theoretical_max=*/ NULL, + /*normalized=*/ true); + printf("degree centralization: %g\n", cent); + + igraph_centralization_betweenness(&graph, /*res=*/ NULL, + IGRAPH_UNDIRECTED, ¢, + /*theoretical_max=*/ NULL, + /*normalized=*/ true); + printf("betweenness centralization: %g\n", cent); + + + igraph_centralization_closeness(&graph, /*res=*/ NULL, + IGRAPH_ALL, ¢, + /*theoretical_max=*/ NULL, + /*normalized=*/ true); + printf("closeness centralization: %g\n", cent); + + igraph_destroy(&graph); + + /* With eigenvector centrality, the most centralized structure is + * a graph containing a single edge. */ + printf("\ngraph with single edge:\n"); + igraph_small(&graph, /*n=*/ 10, /*directed=*/ 0, + 0,1, -1); + + igraph_centralization_eigenvector_centrality( + &graph, + /*vector=*/ NULL, + /*value=*/ NULL, + IGRAPH_DIRECTED, + /*scale=*/ true, + /*options=*/ NULL, + ¢, + /*theoretical_max=*/ NULL, + /*normalized=*/ true); + printf("eigenvector centralization: %g\n", cent); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/centralization.out b/examples/simple/centralization.out new file mode 100644 index 0000000..757de7b --- /dev/null +++ b/examples/simple/centralization.out @@ -0,0 +1,7 @@ +undirected star graph: +degree centralization: 1 +betweenness centralization: 1 +closeness centralization: 1 + +graph with single edge: +eigenvector centralization: 1 diff --git a/examples/simple/cohesive_blocks.c b/examples/simple/cohesive_blocks.c new file mode 100644 index 0000000..3a2b14d --- /dev/null +++ b/examples/simple/cohesive_blocks.c @@ -0,0 +1,63 @@ +/* -*- 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 + +int main(void) { + igraph_t g; + igraph_vector_int_list_t blocks; + igraph_vector_int_t cohesion; + igraph_vector_int_t parent; + igraph_t block_tree; + igraph_integer_t i; + + igraph_famous(&g, "zachary"); + igraph_vector_int_list_init(&blocks, 0); + igraph_vector_int_init(&cohesion, 0); + igraph_vector_int_init(&parent, 0); + + igraph_cohesive_blocks(&g, &blocks, &cohesion, &parent, + &block_tree); + + printf("Blocks:\n"); + for (i = 0; i < igraph_vector_int_list_size(&blocks); i++) { + igraph_vector_int_t *sg = igraph_vector_int_list_get_ptr(&blocks, i); + printf(" "); + igraph_vector_int_print(sg); + } + printf("Cohesion:\n "); + igraph_vector_int_print(&cohesion); + printf("Parents:\n "); + igraph_vector_int_print(&parent); + printf("Block graph:\n"); + igraph_write_graph_edgelist(&block_tree, stdout); + + igraph_vector_int_list_destroy(&blocks); + igraph_vector_int_destroy(&cohesion); + igraph_vector_int_destroy(&parent); + igraph_destroy(&block_tree); + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/cohesive_blocks.out b/examples/simple/cohesive_blocks.out new file mode 100644 index 0000000..9c25498 --- /dev/null +++ b/examples/simple/cohesive_blocks.out @@ -0,0 +1,21 @@ +Blocks: + 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 + 0 1 2 3 7 8 9 12 13 14 15 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 + 0 4 5 6 10 16 + 0 1 2 3 7 + 0 1 2 8 30 32 33 + 0 4 5 6 10 + 0 1 2 3 13 + 2 23 24 25 27 28 29 31 32 33 +Cohesion: + 1 2 2 4 3 3 4 3 +Parents: + -1 0 0 1 1 2 1 1 +Block graph: +0 1 +0 2 +1 3 +1 4 +1 6 +1 7 +2 5 diff --git a/examples/simple/creation.c b/examples/simple/creation.c new file mode 100644 index 0000000..6fe736e --- /dev/null +++ b/examples/simple/creation.c @@ -0,0 +1,30 @@ + +#include +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t edges; + + /* Create a directed graph with no vertices or edges. */ + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + + /* Add 5 vertices. Vertex IDs will range from 0 to 4, inclusive. */ + igraph_add_vertices(&graph, 5, NULL); + + /* Add 5 edges, specified as 5 consecutive pairs of vertex IDs + * stored in an integer vector. */ + igraph_vector_int_init_int(&edges, 10, + 0,1, 0,2, 3,1, 2,1, 0,4); + igraph_add_edges(&graph, &edges, NULL); + + igraph_vector_int_destroy(&edges); + + /* Now the graph has 5 vertices and 5 edges. */ + assert(igraph_vcount(&graph) == 5); + assert(igraph_ecount(&graph) == 5); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/distances.c b/examples/simple/distances.c new file mode 100644 index 0000000..8702d9f --- /dev/null +++ b/examples/simple/distances.c @@ -0,0 +1,70 @@ +/* + IGraph library. + Copyright (C) 2008-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, see . +*/ + + +#include + +int main(void) { + + igraph_t graph; + igraph_vector_t weights; + igraph_real_t weights_data[] = { 0, 2, 1, 0, 5, 2, 1, 1, 0, 2, 2, 8, 1, 1, 3, 1, 1, 4, 2, 1 }; + igraph_matrix_t res; + igraph_real_t cutoff; + + igraph_small(&graph, 10, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 4, 1, 5, + 2, 3, 2, 6, 3, 2, 3, 6, + 4, 5, 4, 7, 5, 6, 5, 8, 5, 9, + 7, 5, 7, 8, 8, 9, + 5, 2, + 2, 1, + -1); + + igraph_matrix_init(&res, 0, 0); + + printf("Unweighted distances:\n\n"); + + igraph_distances(&graph, &res, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT); + igraph_matrix_print(&res); + + cutoff = 3; /* distances longer than this will be returned as infinity */ + printf("\nUnweighted distances with a cutoff of %g:\n\n", cutoff); + igraph_distances_cutoff(&graph, &res, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT, cutoff); + igraph_matrix_print(&res); + + printf("\nWeighted distances:\n\n"); + + igraph_vector_view(&weights, weights_data, + sizeof(weights_data) / sizeof(weights_data[0])); + + igraph_distances_dijkstra(&graph, &res, igraph_vss_all(), igraph_vss_all(), + &weights, IGRAPH_OUT); + igraph_matrix_print(&res); + + cutoff = 8; /* distances longer than this will be returned as infinity */ + printf("\nWeighted distances with a cutoff of %g:\n\n", cutoff); + igraph_distances_dijkstra_cutoff(&graph, &res, igraph_vss_all(), igraph_vss_all(), + &weights, IGRAPH_OUT, cutoff); + igraph_matrix_print(&res); + + igraph_matrix_destroy(&res); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/distances.out b/examples/simple/distances.out new file mode 100644 index 0000000..a35c1bb --- /dev/null +++ b/examples/simple/distances.out @@ -0,0 +1,51 @@ +Unweighted distances: + + 0 1 1 1 2 2 2 3 3 3 +Inf 0 1 2 1 1 2 2 2 2 +Inf 1 0 1 2 2 1 3 3 3 +Inf 2 1 0 3 3 1 4 4 4 +Inf 3 2 3 0 1 2 1 2 2 +Inf 2 1 2 3 0 1 4 1 1 +Inf Inf Inf Inf Inf Inf 0 Inf Inf Inf +Inf 3 2 3 4 1 2 0 1 2 +Inf Inf Inf Inf Inf Inf Inf Inf 0 1 +Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 + +Unweighted distances with a cutoff of 3: + + 0 1 1 1 2 2 2 3 3 3 +Inf 0 1 2 1 1 2 2 2 2 +Inf 1 0 1 2 2 1 3 3 3 +Inf 2 1 0 3 3 1 Inf Inf Inf +Inf 3 2 3 0 1 2 1 2 2 +Inf 2 1 2 3 0 1 Inf 1 1 +Inf Inf Inf Inf Inf Inf 0 Inf Inf Inf +Inf 3 2 3 Inf 1 2 0 1 2 +Inf Inf Inf Inf Inf Inf Inf Inf 0 1 +Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 + +Weighted distances: + + 0 0 0 1 5 2 1 13 3 5 +Inf 0 0 1 5 2 1 13 3 5 +Inf 1 0 1 6 3 1 14 4 6 +Inf 1 0 0 6 3 1 14 4 6 +Inf 5 4 5 0 2 3 8 3 5 +Inf 3 2 3 8 0 1 16 1 3 +Inf Inf Inf Inf Inf Inf 0 Inf Inf Inf +Inf 4 3 4 9 1 2 0 1 4 +Inf Inf Inf Inf Inf Inf Inf Inf 0 4 +Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 + +Weighted distances with a cutoff of 8: + + 0 0 0 1 5 2 1 Inf 3 5 +Inf 0 0 1 5 2 1 Inf 3 5 +Inf 1 0 1 6 3 1 Inf 4 6 +Inf 1 0 0 6 3 1 Inf 4 6 +Inf 5 4 5 0 2 3 8 3 5 +Inf 3 2 3 8 0 1 Inf 1 3 +Inf Inf Inf Inf Inf Inf 0 Inf Inf Inf +Inf 4 3 4 Inf 1 2 0 1 4 +Inf Inf Inf Inf Inf Inf Inf Inf 0 4 +Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 diff --git a/examples/simple/dominator_tree.c b/examples/simple/dominator_tree.c new file mode 100644 index 0000000..922b08f --- /dev/null +++ b/examples/simple/dominator_tree.c @@ -0,0 +1,58 @@ +/* -*- 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 +#include + +int main(void) { + igraph_t g, domtree; + igraph_vector_int_t dom; + igraph_vector_int_t leftout; + + igraph_vector_int_init(&dom, 0); + igraph_vector_int_init(&leftout, 0); + + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 9, + 1, 0, 1, 2, + 2, 3, 2, 7, + 3, 1, + 4, 1, 4, 3, + 5, 2, 5, 3, 5, 4, 5, 8, + 6, 5, 6, 9, + 8, 7, + -1); + + igraph_dominator_tree(&g, /*root=*/ 9, &dom, &domtree, + &leftout, /*mode=*/ IGRAPH_IN); + igraph_vector_int_print(&dom); + igraph_vector_int_print(&leftout); + igraph_write_graph_edgelist(&domtree, stdout); + + igraph_vector_int_destroy(&dom); + igraph_vector_int_destroy(&leftout); + igraph_destroy(&domtree); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/dominator_tree.out b/examples/simple/dominator_tree.out new file mode 100644 index 0000000..7580ef9 --- /dev/null +++ b/examples/simple/dominator_tree.out @@ -0,0 +1,9 @@ +9 0 3 1 1 1 9 -2 -2 -1 +7 8 +0 9 +1 0 +2 3 +3 1 +4 1 +5 1 +6 9 diff --git a/examples/simple/dot.c b/examples/simple/dot.c new file mode 100644 index 0000000..adbb770 --- /dev/null +++ b/examples/simple/dot.c @@ -0,0 +1,52 @@ +/* -*- 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 +#include + +int main(void) { + + igraph_t g; + FILE *ifile; + + ifile = fopen("karate.gml", "r"); + if (ifile == 0) { + return 10; + } + + igraph_read_graph_gml(&g, ifile); + fclose(ifile); + + if (igraph_is_directed(&g)) { + printf("directed\n"); + } else { + printf("undirected\n"); + } + + igraph_write_graph_edgelist(&g, stdout); + printf("-----------------\n"); + igraph_write_graph_dot(&g, stdout); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/dot.out b/examples/simple/dot.out new file mode 100644 index 0000000..a959aeb --- /dev/null +++ b/examples/simple/dot.out @@ -0,0 +1,196 @@ +undirected +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 8 +2 9 +2 13 +2 27 +2 28 +2 32 +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 29 +23 32 +23 33 +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 +----------------- +/* Created by igraph @VERSION@ */ +graph { + 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; + + 1 -- 0; + 2 -- 0; + 2 -- 1; + 3 -- 0; + 3 -- 1; + 3 -- 2; + 4 -- 0; + 5 -- 0; + 6 -- 0; + 6 -- 4; + 6 -- 5; + 7 -- 0; + 7 -- 1; + 7 -- 2; + 7 -- 3; + 8 -- 0; + 8 -- 2; + 9 -- 2; + 10 -- 0; + 10 -- 4; + 10 -- 5; + 11 -- 0; + 12 -- 0; + 12 -- 3; + 13 -- 0; + 13 -- 1; + 13 -- 2; + 13 -- 3; + 16 -- 5; + 16 -- 6; + 17 -- 0; + 17 -- 1; + 19 -- 0; + 19 -- 1; + 21 -- 0; + 21 -- 1; + 25 -- 23; + 25 -- 24; + 27 -- 2; + 27 -- 23; + 27 -- 24; + 28 -- 2; + 29 -- 23; + 29 -- 26; + 30 -- 1; + 30 -- 8; + 31 -- 0; + 31 -- 24; + 31 -- 25; + 31 -- 28; + 32 -- 2; + 32 -- 8; + 32 -- 14; + 32 -- 15; + 32 -- 18; + 32 -- 20; + 32 -- 22; + 32 -- 23; + 32 -- 29; + 32 -- 30; + 32 -- 31; + 33 -- 8; + 33 -- 9; + 33 -- 13; + 33 -- 14; + 33 -- 15; + 33 -- 18; + 33 -- 19; + 33 -- 20; + 33 -- 22; + 33 -- 23; + 33 -- 26; + 33 -- 27; + 33 -- 28; + 33 -- 29; + 33 -- 30; + 33 -- 31; + 33 -- 32; +} diff --git a/examples/simple/dqueue.c b/examples/simple/dqueue.c new file mode 100644 index 0000000..330094f --- /dev/null +++ b/examples/simple/dqueue.c @@ -0,0 +1,117 @@ +/* -*- 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 + +int main(void) { + + igraph_dqueue_t q; + + /* igraph_dqueue_init, igraph_dqueue_destroy, igraph_dqueue_empty */ + igraph_dqueue_init(&q, 5); + if (!igraph_dqueue_empty(&q)) { + return 1; + } + igraph_dqueue_destroy(&q); + + /* igraph_dqueue_push, igraph_dqueue_pop */ + igraph_dqueue_init(&q, 4); + igraph_dqueue_push(&q, 1); + igraph_dqueue_push(&q, 2); + igraph_dqueue_push(&q, 3); + igraph_dqueue_push(&q, 4); + if (igraph_dqueue_pop(&q) != 1) { + return 2; + } + if (igraph_dqueue_pop(&q) != 2) { + return 3; + } + if (igraph_dqueue_pop(&q) != 3) { + return 4; + } + if (igraph_dqueue_pop(&q) != 4) { + return 5; + } + igraph_dqueue_destroy(&q); + + /* igraph_dqueue_clear, igraph_dqueue_size */ + igraph_dqueue_init(&q, 0); + if (igraph_dqueue_size(&q) != 0) { + return 6; + } + igraph_dqueue_clear(&q); + if (igraph_dqueue_size(&q) != 0) { + return 7; + } + for (int i = 0; i < 10; i++) { + igraph_dqueue_push(&q, i); + } + igraph_dqueue_clear(&q); + if (igraph_dqueue_size(&q) != 0) { + return 8; + } + igraph_dqueue_destroy(&q); + + /* TODO: igraph_dqueue_full */ + + /* igraph_dqueue_head, igraph_dqueue_back, igraph_dqueue_pop_back */ + igraph_dqueue_init(&q, 0); + for (int i = 0; i < 10; i++) { + igraph_dqueue_push(&q, i); + } + for (int i = 0; i < 10; i++) { + if (igraph_dqueue_head(&q) != 0) { + return 9; + } + if (igraph_dqueue_back(&q) != 9 - i) { + return 10; + } + if (igraph_dqueue_pop_back(&q) != 9 - i) { + return 11; + } + } + igraph_dqueue_destroy(&q); + + /* print */ + igraph_dqueue_init(&q, 4); + igraph_dqueue_push(&q, 1); + igraph_dqueue_push(&q, 2); + igraph_dqueue_push(&q, 3); + igraph_dqueue_push(&q, 4); + igraph_dqueue_pop(&q); + igraph_dqueue_pop(&q); + igraph_dqueue_push(&q, 5); + igraph_dqueue_push(&q, 6); + igraph_dqueue_print(&q); + + igraph_dqueue_clear(&q); + igraph_dqueue_print(&q); + + igraph_dqueue_destroy(&q); + + if (IGRAPH_FINALLY_STACK_SIZE() != 0) { + return 12; + } + + return 0; +} diff --git a/examples/simple/dqueue.out b/examples/simple/dqueue.out new file mode 100644 index 0000000..02defa8 --- /dev/null +++ b/examples/simple/dqueue.out @@ -0,0 +1,2 @@ +3 4 5 6 + diff --git a/examples/simple/edgelist1.dl b/examples/simple/edgelist1.dl new file mode 100644 index 0000000..2024ab8 --- /dev/null +++ b/examples/simple/edgelist1.dl @@ -0,0 +1,10 @@ +DL n=5 +format = edgelist1 +labels: +george, sally, jim, billy, jane +data: +1 2 +1 3 +2 3 +3 1 +4 3 diff --git a/examples/simple/edgelist2.dl b/examples/simple/edgelist2.dl new file mode 100644 index 0000000..5b38e18 --- /dev/null +++ b/examples/simple/edgelist2.dl @@ -0,0 +1,9 @@ +DL n=5 +format = edgelist1 +labels embedded: +data: +george sally +george jim +sally jim +billy george +jane jim diff --git a/examples/simple/edgelist3.dl b/examples/simple/edgelist3.dl new file mode 100644 index 0000000..a7625f3 --- /dev/null +++ b/examples/simple/edgelist3.dl @@ -0,0 +1,11 @@ +DL n=5 +format = edgelist1 +labels: +george, sally, jim, billy, jane +labels embedded: +data: +george sally +george jim +sally jim +billy george +jane jim diff --git a/examples/simple/edgelist4.dl b/examples/simple/edgelist4.dl new file mode 100644 index 0000000..cb2ce4d --- /dev/null +++ b/examples/simple/edgelist4.dl @@ -0,0 +1,10 @@ +DL n=5 +format = edgelist1 +labels: +george, sally, jim, billy, jane +data: +1 2 +1 3 -1 +2 3 +3 1 -1 +4 3 diff --git a/examples/simple/edgelist5.dl b/examples/simple/edgelist5.dl new file mode 100644 index 0000000..37173a5 --- /dev/null +++ b/examples/simple/edgelist5.dl @@ -0,0 +1,9 @@ +DL n=5 +format = edgelist1 +labels embedded: +data: +george sally 0.1 +george jim 0.5 +sally jim +billy george 1 +jane jim diff --git a/examples/simple/edgelist6.dl b/examples/simple/edgelist6.dl new file mode 100644 index 0000000..df7db54 --- /dev/null +++ b/examples/simple/edgelist6.dl @@ -0,0 +1,11 @@ +DL n=5 +format = edgelist1 +labels: +george, sally, jim, billy, jane +labels embedded: +data: +george sally +george jim 2 +sally jim +billy george 1 +jane jim 1e-5 diff --git a/examples/simple/edgelist7.dl b/examples/simple/edgelist7.dl new file mode 100644 index 0000000..edbe5e2 --- /dev/null +++ b/examples/simple/edgelist7.dl @@ -0,0 +1,6 @@ +DL n=4 +format = edgelist1 +data: +1 2 +2 3 +2 4 diff --git a/examples/simple/eigenvector_centrality.c b/examples/simple/eigenvector_centrality.c new file mode 100644 index 0000000..cce5c6d --- /dev/null +++ b/examples/simple/eigenvector_centrality.c @@ -0,0 +1,56 @@ +/* -*- 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.h" + +#include + +int main(void) { + + igraph_t graph; + igraph_vector_t vector, weights; + igraph_real_t value; + + /* Create a star graph, with vertex 0 at the center, and associated edge weights. */ + igraph_star(&graph, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_vector_init_range(&weights, 1, igraph_ecount(&graph)+1); + + /* Initialize the vector where the result will be stored. */ + igraph_vector_init(&vector, 0); + + /* Compute eigenvector centrality. */ + igraph_eigenvector_centrality(&graph, &vector, &value, IGRAPH_UNDIRECTED, + /*scale=*/ true, &weights, /*options=*/ NULL); + + /* Print results. */ + printf("eigenvalue: %g\n", value); + printf("eigenvector:\n"); + igraph_vector_print(&vector); + + /* Free allocated data structures. */ + igraph_vector_destroy(&vector); + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/eigenvector_centrality.out b/examples/simple/eigenvector_centrality.out new file mode 100644 index 0000000..9ccd727 --- /dev/null +++ b/examples/simple/eigenvector_centrality.out @@ -0,0 +1,3 @@ +eigenvalue: 16.8819 +eigenvector: +1 0.0592349 0.11847 0.177705 0.23694 0.296174 0.355409 0.414644 0.473879 0.533114 diff --git a/examples/simple/even_tarjan.c b/examples/simple/even_tarjan.c new file mode 100644 index 0000000..7d8fcf3 --- /dev/null +++ b/examples/simple/even_tarjan.c @@ -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 + +*/ + +#include +#include + +int main(void) { + + igraph_t g, gbar; + igraph_integer_t k1, k2 = INT_MAX; + igraph_real_t tmpk; + igraph_integer_t i, j, n; + igraph_maxflow_stats_t stats; + + /* --------------------------------------------------- */ + + igraph_famous(&g, "meredith"); + igraph_even_tarjan_reduction(&g, &gbar, /*capacity=*/ NULL); + + igraph_vertex_connectivity(&g, &k1, /* checks= */ false); + + n = igraph_vcount(&g); + for (i = 0; i < n; i++) { + for (j = i + 1; j < n; j++) { + igraph_bool_t conn; + igraph_are_adjacent(&g, i, j, &conn); + if (conn) { + continue; + } + igraph_maxflow_value(&gbar, &tmpk, + /* source= */ i + n, + /* target= */ j, + /* capacity= */ 0, + &stats); + if (tmpk < k2) { + k2 = tmpk; + } + } + } + + igraph_destroy(&gbar); + igraph_destroy(&g); + + if (k1 != k2) { + printf("k1 = %" IGRAPH_PRId " while k2 = %" IGRAPH_PRId "\n", k1, k2); + return 1; + } + + return 0; +} diff --git a/examples/simple/flow.c b/examples/simple/flow.c new file mode 100644 index 0000000..0e00a34 --- /dev/null +++ b/examples/simple/flow.c @@ -0,0 +1,106 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + igraph_real_t flow; + igraph_vector_t capacity; + igraph_integer_t source, target; + FILE *infile; + igraph_maxflow_stats_t stats; + + igraph_vector_init(&capacity, 0); + + /***************/ + infile = fopen("ak-4102.max", "r"); + igraph_read_graph_dimacs_flow( + &g, infile, 0, 0, &source, &target, &capacity, IGRAPH_DIRECTED); + fclose(infile); + + igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); + + if (flow != 8207) { + return 1; + } + igraph_destroy(&g); + /***************/ + + /* /\***************\/ */ + /* infile=fopen("ak-8198.max", "r"); */ + /* igraph_read_graph_dimacs_flow(&g, infile, 0, 0, &source, &target, &capacity, */ + /* IGRAPH_DIRECTED); */ + /* fclose(infile); */ + + /* t=timer(); */ + /* igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); */ + /* t=timer()-t; */ + /* printf("8198: %g (time %.10f)\n", flow, t); */ + /* igraph_destroy(&g); */ + /* /\***************\/ */ + + /* /\***************\/ */ + /* infile=fopen("ak-16390.max", "r"); */ + /* igraph_read_graph_dimacs_flow(&g, infile, 0, 0, &source, &target, &capacity, */ + /* IGRAPH_DIRECTED); */ + /* fclose(infile); */ + + /* t=timer(); */ + /* igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); */ + /* t=timer()-t; */ + /* printf("16390: %g (time %.10f)\n", flow, t); */ + /* igraph_destroy(&g); */ + /* /\***************\/ */ + + /* /\***************\/ */ + /* infile=fopen("ak-32774.max", "r"); */ + /* igraph_read_graph_dimacs_flow(&g, infile, 0, 0, &source, &target, &capacity, */ + /* IGRAPH_DIRECTED); */ + /* fclose(infile); */ + + /* t=timer(); */ + /* igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); */ + /* t=timer()-t; */ + /* printf("32774: %g (time %.10f)\n", flow, t); */ + /* igraph_destroy(&g); */ + /* /\***************\/ */ + + /* /\***************\/ */ + /* infile=fopen("ak-65542.max", "r"); */ + /* igraph_read_graph_dimacs_flow(&g, infile, 0, 0, &source, &target, &capacity, */ + /* IGRAPH_DIRECTED); */ + /* fclose(infile); */ + + /* t=timer(); */ + /* igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); */ + /* t=timer()-t; */ + /* printf("65542: %g (time %.10f)\n", flow, t); */ + /* igraph_destroy(&g); */ + /* /\***************\/ */ + + igraph_vector_destroy(&capacity); + + return 0; +} diff --git a/examples/simple/flow2.c b/examples/simple/flow2.c new file mode 100644 index 0000000..e72b8ab --- /dev/null +++ b/examples/simple/flow2.c @@ -0,0 +1,73 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + igraph_real_t flow_value; + igraph_vector_int_t cut; + igraph_vector_t capacity; + igraph_vector_int_t partition, partition2; + igraph_vector_t flow; + igraph_integer_t i; + igraph_maxflow_stats_t stats; + + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 0, 5, 5, 4, 4, 3, 3, 0, -1); + igraph_vector_init_int_end(&capacity, -1, 3, 1, 2, 10, 1, 3, 2, -1); + igraph_vector_int_init(&cut, 0); + igraph_vector_int_init(&partition, 0); + igraph_vector_int_init(&partition2, 0); + igraph_vector_init(&flow, 0); + + igraph_maxflow(&g, &flow_value, &flow, &cut, &partition, &partition2, + /*source=*/ 0, /*target=*/ 2, &capacity, &stats); + + igraph_integer_t nc = igraph_vector_int_size(&cut); + printf("flow value: %g\n", (double) flow_value); + printf("flow: "); + igraph_vector_print(&flow); + printf("first partition: "); + igraph_vector_int_print(&partition); + printf("second partition: "); + igraph_vector_int_print(&partition2); + printf("edges in the cut: "); + for (i = 0; i < nc; i++) { + igraph_integer_t edge = VECTOR(cut)[i]; + igraph_integer_t from = IGRAPH_FROM(&g, edge); + igraph_integer_t to = IGRAPH_TO(&g, edge); + printf("%" IGRAPH_PRId "-%" IGRAPH_PRId " (%g), ", from, to, VECTOR(capacity)[edge]); + } + printf("\n"); + + igraph_vector_int_destroy(&cut); + igraph_vector_int_destroy(&partition2); + igraph_vector_int_destroy(&partition); + igraph_vector_destroy(&capacity); + igraph_vector_destroy(&flow); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/flow2.out b/examples/simple/flow2.out new file mode 100644 index 0000000..c741455 --- /dev/null +++ b/examples/simple/flow2.out @@ -0,0 +1,5 @@ +flow value: 1 +flow: 1 1 0 0 0 0 0 +first partition: 0 1 3 4 5 +second partition: 2 +edges in the cut: 1-2 (1), diff --git a/examples/simple/foreign.c b/examples/simple/foreign.c new file mode 100644 index 0000000..3e6132b --- /dev/null +++ b/examples/simple/foreign.c @@ -0,0 +1,51 @@ +/* -*- 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 +#include + +int main(void) { + igraph_t g; + FILE *ifile; + + /* Turn on attribute handling. */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* Read a Pajek file. */ + ifile = fopen("links.net", "r"); + if (ifile == 0) { + return 10; + } + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + /* Write it in edgelist format. */ + printf("The graph:\n"); + printf("Vertices: %" IGRAPH_PRId "\n", igraph_vcount(&g)); + printf("Edges: %" IGRAPH_PRId "\n", igraph_ecount(&g)); + printf("Directed: %i\n", igraph_is_directed(&g) ? 1 : 0); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/foreign.out b/examples/simple/foreign.out new file mode 100644 index 0000000..a1244f9 --- /dev/null +++ b/examples/simple/foreign.out @@ -0,0 +1,11 @@ +The graph: +Vertices: 4 +Edges: 7 +Directed: 1 +0 0 +0 1 +0 2 +1 0 +2 2 +2 3 +3 1 diff --git a/examples/simple/fullmatrix1.dl b/examples/simple/fullmatrix1.dl new file mode 100644 index 0000000..a77922c --- /dev/null +++ b/examples/simple/fullmatrix1.dl @@ -0,0 +1,7 @@ +DL N = 5 +Data: +0 1 1 1 1 +1 0 1 0 0 +1 1 0 0 1 +1 0 0 0 0 +1 0 1 0 0 diff --git a/examples/simple/fullmatrix2.dl b/examples/simple/fullmatrix2.dl new file mode 100644 index 0000000..2794732 --- /dev/null +++ b/examples/simple/fullmatrix2.dl @@ -0,0 +1,10 @@ +dl n=5 +format = fullmatrix +labels: +barry,david,lin,pat,russ +data: +0 1 1 1 0 +1 0 0 0 1 +1 0 0 1 0 +1 0 1 0 1 +0 1 0 1 0 diff --git a/examples/simple/fullmatrix3.dl b/examples/simple/fullmatrix3.dl new file mode 100644 index 0000000..7a966bf --- /dev/null +++ b/examples/simple/fullmatrix3.dl @@ -0,0 +1,12 @@ +dl n=5 +format = fullmatrix +labels: +barry,david +lin,pat +russ +data: +0 1 1 1 0 +1 0 0 0 1 +1 0 0 1 0 +1 0 1 0 1 +0 1 0 1 0 diff --git a/examples/simple/fullmatrix4.dl b/examples/simple/fullmatrix4.dl new file mode 100644 index 0000000..15d21df --- /dev/null +++ b/examples/simple/fullmatrix4.dl @@ -0,0 +1,10 @@ +dl n=5 +format = fullmatrix +labels embedded +data: +larry david lin pat russ +Larry 0 1 1 1 0 +david 1 0 0 0 1 +Lin 1 0 0 1 0 +Pat 1 0 1 0 1 +Russ 0 1 0 1 0 diff --git a/examples/simple/gml.c b/examples/simple/gml.c new file mode 100644 index 0000000..b9947ae --- /dev/null +++ b/examples/simple/gml.c @@ -0,0 +1,52 @@ +/* -*- 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 +#include + +int main(void) { + igraph_t graph; + FILE *ifile; + + ifile = fopen("karate.gml", "r"); + if (ifile == 0) { + return 1; + } + + igraph_read_graph_gml(&graph, ifile); + fclose(ifile); + + printf("The graph is %s.\n", igraph_is_directed(&graph) ? "directed" : "undirected"); + + /* Output as edge list */ + printf("\n-----------------\n"); + igraph_write_graph_edgelist(&graph, stdout); + + /* Output as GML */ + printf("\n-----------------\n"); + igraph_write_graph_gml(&graph, stdout,IGRAPH_WRITE_GML_DEFAULT_SW, 0, ""); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/gml.out b/examples/simple/gml.out new file mode 100644 index 0000000..fe1dfb1 --- /dev/null +++ b/examples/simple/gml.out @@ -0,0 +1,614 @@ +The graph is undirected. + +----------------- +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 8 +2 9 +2 13 +2 27 +2 28 +2 32 +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 29 +23 32 +23 33 +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 + +----------------- +Version 1 +graph +[ + directed 0 + node + [ + id 0 + ] + node + [ + id 1 + ] + node + [ + id 2 + ] + node + [ + id 3 + ] + node + [ + id 4 + ] + node + [ + id 5 + ] + node + [ + id 6 + ] + node + [ + id 7 + ] + node + [ + id 8 + ] + node + [ + id 9 + ] + node + [ + id 10 + ] + node + [ + id 11 + ] + node + [ + id 12 + ] + node + [ + id 13 + ] + node + [ + id 14 + ] + node + [ + id 15 + ] + node + [ + id 16 + ] + node + [ + id 17 + ] + node + [ + id 18 + ] + node + [ + id 19 + ] + node + [ + id 20 + ] + node + [ + id 21 + ] + node + [ + id 22 + ] + node + [ + id 23 + ] + node + [ + id 24 + ] + node + [ + id 25 + ] + node + [ + id 26 + ] + node + [ + id 27 + ] + node + [ + id 28 + ] + node + [ + id 29 + ] + node + [ + id 30 + ] + node + [ + id 31 + ] + node + [ + id 32 + ] + node + [ + id 33 + ] + edge + [ + source 1 + target 0 + ] + edge + [ + source 2 + target 0 + ] + edge + [ + source 2 + target 1 + ] + edge + [ + source 3 + target 0 + ] + edge + [ + source 3 + target 1 + ] + edge + [ + source 3 + target 2 + ] + edge + [ + source 4 + target 0 + ] + edge + [ + source 5 + target 0 + ] + edge + [ + source 6 + target 0 + ] + edge + [ + source 6 + target 4 + ] + edge + [ + source 6 + target 5 + ] + edge + [ + source 7 + target 0 + ] + edge + [ + source 7 + target 1 + ] + edge + [ + source 7 + target 2 + ] + edge + [ + source 7 + target 3 + ] + edge + [ + source 8 + target 0 + ] + edge + [ + source 8 + target 2 + ] + edge + [ + source 9 + target 2 + ] + edge + [ + source 10 + target 0 + ] + edge + [ + source 10 + target 4 + ] + edge + [ + source 10 + target 5 + ] + edge + [ + source 11 + target 0 + ] + edge + [ + source 12 + target 0 + ] + edge + [ + source 12 + target 3 + ] + edge + [ + source 13 + target 0 + ] + edge + [ + source 13 + target 1 + ] + edge + [ + source 13 + target 2 + ] + edge + [ + source 13 + target 3 + ] + edge + [ + source 16 + target 5 + ] + edge + [ + source 16 + target 6 + ] + edge + [ + source 17 + target 0 + ] + edge + [ + source 17 + target 1 + ] + edge + [ + source 19 + target 0 + ] + edge + [ + source 19 + target 1 + ] + edge + [ + source 21 + target 0 + ] + edge + [ + source 21 + target 1 + ] + edge + [ + source 25 + target 23 + ] + edge + [ + source 25 + target 24 + ] + edge + [ + source 27 + target 2 + ] + edge + [ + source 27 + target 23 + ] + edge + [ + source 27 + target 24 + ] + edge + [ + source 28 + target 2 + ] + edge + [ + source 29 + target 23 + ] + edge + [ + source 29 + target 26 + ] + edge + [ + source 30 + target 1 + ] + edge + [ + source 30 + target 8 + ] + edge + [ + source 31 + target 0 + ] + edge + [ + source 31 + target 24 + ] + edge + [ + source 31 + target 25 + ] + edge + [ + source 31 + target 28 + ] + edge + [ + source 32 + target 2 + ] + edge + [ + source 32 + target 8 + ] + edge + [ + source 32 + target 14 + ] + edge + [ + source 32 + target 15 + ] + edge + [ + source 32 + target 18 + ] + edge + [ + source 32 + target 20 + ] + edge + [ + source 32 + target 22 + ] + edge + [ + source 32 + target 23 + ] + edge + [ + source 32 + target 29 + ] + edge + [ + source 32 + target 30 + ] + edge + [ + source 32 + target 31 + ] + edge + [ + source 33 + target 8 + ] + edge + [ + source 33 + target 9 + ] + edge + [ + source 33 + target 13 + ] + edge + [ + source 33 + target 14 + ] + edge + [ + source 33 + target 15 + ] + edge + [ + source 33 + target 18 + ] + edge + [ + source 33 + target 19 + ] + edge + [ + source 33 + target 20 + ] + edge + [ + source 33 + target 22 + ] + edge + [ + source 33 + target 23 + ] + edge + [ + source 33 + target 26 + ] + edge + [ + source 33 + target 27 + ] + edge + [ + source 33 + target 28 + ] + edge + [ + source 33 + target 29 + ] + edge + [ + source 33 + target 30 + ] + edge + [ + source 33 + target 31 + ] + edge + [ + source 33 + target 32 + ] +] diff --git a/examples/simple/graphml.c b/examples/simple/graphml.c new file mode 100644 index 0000000..ae2b855 --- /dev/null +++ b/examples/simple/graphml.c @@ -0,0 +1,83 @@ +/* + IGraph library. + Copyright (C) 2006-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 + +*/ + +#include +#include +#include /* unlink */ + +int main(void) { + igraph_t graph; + const char *infilename = "test.graphml"; + const char *outfilename = "test2.graphml"; + + /* Set up attribute handling, so graph attributes can be imported + * from the GraphML file. */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* Problems in the GraphML file may cause igraph to print warnings. + * If this is not desired, set a silent warning handler: */ + igraph_set_warning_handler(&igraph_warning_handler_ignore); + + /* Read the contents of a GraphML file. */ + + /* GraphML */ + FILE *infile = fopen("test.graphml", "r"); + if (! infile) { + fprintf(stderr, "Could not open input file '%s'.", infilename); + exit(1); + } + + /* GraphML support is an optional feature in igraph. If igraph was compiled + * without GraphML support, igraph_read_graph_graphml() returns IGRAPH_UNIMPLEMENTED. + * We temporarily disable the default error handler so we can test for this condition. */ + igraph_error_handler_t *oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); + igraph_error_t ret = igraph_read_graph_graphml(&graph, infile, 0); + if (ret == IGRAPH_UNIMPLEMENTED) { + fprintf(stderr, "igraph was compiled without GraphML support."); + exit(77); + } + if (ret != IGRAPH_SUCCESS) { + fprintf(stderr, "Unexpected error while reading GraphML."); + exit(1); + } + igraph_set_error_handler(oldhandler); + + + fclose(infile); + + /* Write it back into another file. */ + + FILE *outfile = fopen(outfilename, "w"); + if (outfile) { + igraph_write_graph_graphml(&graph, outfile, true); + fclose(outfile); + + /* Clean up after ourselves */ + unlink(outfilename); + } else { + fprintf(stderr, "Could not write output file '%s'.", outfilename); + } + + /* Destroy the graph */ + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_adjacency.c b/examples/simple/igraph_adjacency.c new file mode 100644 index 0000000..fdad463 --- /dev/null +++ b/examples/simple/igraph_adjacency.c @@ -0,0 +1,29 @@ +/* -*- 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 + +int main(void) { + + return 0; +} diff --git a/examples/simple/igraph_all_st_mincuts.c b/examples/simple/igraph_all_st_mincuts.c new file mode 100644 index 0000000..34c53ef --- /dev/null +++ b/examples/simple/igraph_all_st_mincuts.c @@ -0,0 +1,66 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t partitions; + igraph_vector_int_list_t cuts; + igraph_real_t value; + + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, + -1); + + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 0, /*target=*/ 4, + /*capacity=*/ 0); + + igraph_integer_t i, e, m, n = igraph_vector_int_list_size(&partitions); + printf("Found %" IGRAPH_PRId " cuts, value: %g\n", n, value); + for (i = 0; i < n; i++) { + igraph_vector_int_t *vec = igraph_vector_int_list_get_ptr(&partitions, i); + igraph_vector_int_t *vec2 = igraph_vector_int_list_get_ptr(&cuts, i); + printf("Partition %" IGRAPH_PRId ": ", i); + igraph_vector_int_print(vec); + if (vec2) { + printf("Cut %" IGRAPH_PRId ":\n", i); + m = igraph_vector_int_size(vec2); + for (e = 0; e < m; e++) { + igraph_integer_t from = IGRAPH_FROM(&g, VECTOR(*vec2)[e]), to = IGRAPH_TO(&g, VECTOR(*vec2)[e]); + printf(" %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", from, to); + } + } + } + + igraph_vector_int_list_destroy(&partitions); + igraph_vector_int_list_destroy(&cuts); + printf("\n"); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_assortativity_degree.c b/examples/simple/igraph_assortativity_degree.c new file mode 100644 index 0000000..2cc138c --- /dev/null +++ b/examples/simple/igraph_assortativity_degree.c @@ -0,0 +1,34 @@ +#include +#include + +int main(void){ + igraph_t g; + igraph_integer_t vcount = 1000; + igraph_real_t pf = 0.2; + + /* Seed random number generator to ensure reproducibility. */ + igraph_rng_seed(igraph_rng_default(), 42); + + printf("Forest fire model network with %" IGRAPH_PRId " vertices and %g forward burning probability.\n\n", + vcount, pf); + + for (int i = 0; i < 5; i++) { + igraph_real_t assortativity; + + /* Generate graph from the forest fire model. */ + igraph_forest_fire_game(&g, vcount, pf, 1.0, 1, IGRAPH_UNDIRECTED); + + /* Compute assortativity. */ + igraph_assortativity_degree(&g, &assortativity, /* ignore edge directions */ IGRAPH_UNDIRECTED); + printf("Assortativity before rewiring = %g\n", assortativity); + + /* Randomize the graph while preserving the degrees. */ + igraph_rewire(&g, 20 * igraph_ecount(&g), IGRAPH_REWIRING_SIMPLE); + + /* Re-compute assortativity. Did it change? */ + igraph_assortativity_degree(&g, &assortativity, /* ignore edge directions */ IGRAPH_UNDIRECTED); + printf("Assortativity after rewiring = %g\n\n", assortativity); + + igraph_destroy(&g); + } +} diff --git a/examples/simple/igraph_assortativity_degree.out b/examples/simple/igraph_assortativity_degree.out new file mode 100644 index 0000000..21f38a0 --- /dev/null +++ b/examples/simple/igraph_assortativity_degree.out @@ -0,0 +1,17 @@ +Forest fire model network with 1000 vertices and 0.2 forward burning probability. + +Assortativity before rewiring = 0.164775 +Assortativity after rewiring = -0.0126025 + +Assortativity before rewiring = 0.151338 +Assortativity after rewiring = -0.00941098 + +Assortativity before rewiring = 0.125732 +Assortativity after rewiring = -0.012306 + +Assortativity before rewiring = 0.154392 +Assortativity after rewiring = -0.00993259 + +Assortativity before rewiring = 0.154686 +Assortativity after rewiring = -0.00258259 + diff --git a/examples/simple/igraph_assortativity_nominal.c b/examples/simple/igraph_assortativity_nominal.c new file mode 100644 index 0000000..3c98edd --- /dev/null +++ b/examples/simple/igraph_assortativity_nominal.c @@ -0,0 +1,43 @@ +#include +#include + +int main(void) { + igraph_integer_t nodes = 120, types = 4; + + igraph_matrix_t pref_matrix; + igraph_matrix_init(&pref_matrix, types, types); + + igraph_rng_seed(igraph_rng_default(), 42); + printf("Randomly generated graph with %" IGRAPH_PRId " nodes and %" IGRAPH_PRId " vertex types\n\n", nodes, types); + + /* Generate preference matrix giving connection probabilities for different vertex types */ + for (igraph_integer_t i = 0; i < types; i++) { + for (igraph_integer_t j = 0; j < types; j++) { + MATRIX(pref_matrix, i, j) = (i == j ? 0.1: 0.01); + } + } + + igraph_vector_int_t node_type_vec; + igraph_vector_int_init(&node_type_vec, nodes); + + for (int i = 0; i < 5; i++) { + igraph_real_t assortativity; + igraph_t g; + + /* Generate undirected graph with 1000 nodes and 50 vertex types */ + igraph_preference_game(&g, nodes, types, /* type_dist= */ NULL, /* fixed_sizes= */ 1, &pref_matrix, &node_type_vec, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_assortativity_nominal(&g, &node_type_vec, &assortativity, IGRAPH_UNDIRECTED, 1); + printf("Assortativity before rewiring = %g\n", assortativity); + + /* Rewire graph */ + igraph_rewire(&g, 10 * igraph_ecount(&g), IGRAPH_REWIRING_SIMPLE); + + igraph_assortativity_nominal(&g, &node_type_vec, &assortativity, IGRAPH_UNDIRECTED, 1); + printf("Assortativity after rewiring = %g\n\n", assortativity); + + igraph_destroy(&g); + } + igraph_vector_int_destroy(&node_type_vec); + igraph_matrix_destroy(&pref_matrix); +} diff --git a/examples/simple/igraph_assortativity_nominal.out b/examples/simple/igraph_assortativity_nominal.out new file mode 100644 index 0000000..c561674 --- /dev/null +++ b/examples/simple/igraph_assortativity_nominal.out @@ -0,0 +1,17 @@ +Randomly generated graph with 120 nodes and 4 vertex types + +Assortativity before rewiring = 0.690908 +Assortativity after rewiring = 0.0451254 + +Assortativity before rewiring = 0.729653 +Assortativity after rewiring = -0.0153034 + +Assortativity before rewiring = 0.688232 +Assortativity after rewiring = -0.0430041 + +Assortativity before rewiring = 0.722913 +Assortativity after rewiring = -0.0310209 + +Assortativity before rewiring = 0.686532 +Assortativity after rewiring = 0.00735045 + diff --git a/examples/simple/igraph_atlas.c b/examples/simple/igraph_atlas.c new file mode 100644 index 0000000..13f36a1 --- /dev/null +++ b/examples/simple/igraph_atlas.c @@ -0,0 +1,46 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + + igraph_atlas(&g, 45); + igraph_write_graph_edgelist(&g, stdout); + printf("\n"); + igraph_destroy(&g); + + igraph_atlas(&g, 0); + igraph_write_graph_edgelist(&g, stdout); + printf("\n"); + igraph_destroy(&g); + + igraph_atlas(&g, 1252); + igraph_write_graph_edgelist(&g, stdout); + printf("\n"); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_atlas.out b/examples/simple/igraph_atlas.out new file mode 100644 index 0000000..38233da --- /dev/null +++ b/examples/simple/igraph_atlas.out @@ -0,0 +1,31 @@ +0 4 +1 2 +1 3 +1 4 +2 3 +2 4 +3 4 + + +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 + diff --git a/examples/simple/igraph_attribute_combination.c b/examples/simple/igraph_attribute_combination.c new file mode 100644 index 0000000..c79e83d --- /dev/null +++ b/examples/simple/igraph_attribute_combination.c @@ -0,0 +1,29 @@ +#include + +int main(void) { + igraph_t graph; + + igraph_attribute_combination_t comb; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_small(&graph, 2, IGRAPH_DIRECTED, + 0,1, 0,1, + -1); + + SETEAB(&graph, "type", 0, true); + SETEAB(&graph, "type", 1, false); + + igraph_attribute_combination(&comb, + "weight", IGRAPH_ATTRIBUTE_COMBINE_SUM, + "type", IGRAPH_ATTRIBUTE_COMBINE_FIRST, + "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, + IGRAPH_NO_MORE_ATTRIBUTES); + igraph_simplify(&graph, /*remove_multiple=*/ true, /*remove_loops=*/ true, &comb); + igraph_write_graph_graphml(&graph, stdout, /*prefixattr=*/ true); + + igraph_destroy(&graph); + igraph_attribute_combination_destroy(&comb); + + return 0; +} diff --git a/examples/simple/igraph_attribute_combination.out b/examples/simple/igraph_attribute_combination.out new file mode 100644 index 0000000..cc6f8dd --- /dev/null +++ b/examples/simple/igraph_attribute_combination.out @@ -0,0 +1,17 @@ + + + + + + + + + + + true + + + diff --git a/examples/simple/igraph_average_path_length.c b/examples/simple/igraph_average_path_length.c new file mode 100644 index 0000000..4a7dd71 --- /dev/null +++ b/examples/simple/igraph_average_path_length.c @@ -0,0 +1,21 @@ + +#include + +int main(void) { + igraph_t graph; + igraph_real_t result; + + /* Create a random preferential attachment graph. */ + igraph_barabasi_game(&graph, 30, /*power=*/ 1, 30, 0, 0, /*A=*/ 1, + IGRAPH_DIRECTED, IGRAPH_BARABASI_BAG, + /*start_from=*/ 0); + + /* Compute the average shortest path length. */ + igraph_average_path_length(&graph, &result, NULL, IGRAPH_UNDIRECTED, 1); + printf("Average length of all-pairs shortest paths: %g\n", result); + + /* Destroy no-longer-needed objects. */ + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_avg_nearest_neighbor_degree.c b/examples/simple/igraph_avg_nearest_neighbor_degree.c new file mode 100644 index 0000000..8baf3aa --- /dev/null +++ b/examples/simple/igraph_avg_nearest_neighbor_degree.c @@ -0,0 +1,63 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + igraph_t graph; + igraph_vector_t knn, knnk; + igraph_vector_t weights; + + igraph_famous(&graph, "Zachary"); + + igraph_vector_init(&knn, 0); + igraph_vector_init(&knnk, 0); + + igraph_avg_nearest_neighbor_degree(&graph, igraph_vss_all(), + IGRAPH_ALL, IGRAPH_ALL, + &knn, &knnk, /*weights=*/ NULL); + + printf("knn: "); + igraph_vector_print(&knn); + printf("knn(k): "); + igraph_vector_print(&knnk); + + igraph_vector_init_range(&weights, 0, igraph_ecount(&graph)); + + igraph_avg_nearest_neighbor_degree(&graph, igraph_vss_all(), + IGRAPH_ALL, IGRAPH_ALL, + &knn, &knnk, &weights); + igraph_vector_destroy(&weights); + + printf("knn: "); + igraph_vector_print(&knn); + printf("knn(k): "); + igraph_vector_print(&knnk); + + igraph_vector_destroy(&knn); + igraph_vector_destroy(&knnk); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_avg_nearest_neighbor_degree.out b/examples/simple/igraph_avg_nearest_neighbor_degree.out new file mode 100644 index 0000000..5fb4f85 --- /dev/null +++ b/examples/simple/igraph_avg_nearest_neighbor_degree.out @@ -0,0 +1,4 @@ +knn: 4.3125 5.77778 6.6 7.66667 7.66667 6.25 6.25 10.25 11.8 13.5 7.66667 16 11 11.6 14.5 14.5 4 12.5 14.5 14 14.5 12.5 14.5 8 4.33333 4.66667 10.5 8.75 11 9 10.75 9 5.08333 3.82353 +knn(k): 16 12.4091 8.22222 8.54167 10.4667 8.33333 NaN NaN 5.77778 6.6 NaN 5.08333 NaN NaN NaN 4.3125 3.82353 +knn: 3.45833 4.28205 5.4346 5.55634 4 3.42373 3.52991 8.64198 11.1104 14.2192 4.73171 16 8.32558 11.6143 14.5269 14.5258 4 11.625 14.5248 14.8953 14.5234 11.7222 14.5225 8.05085 4.34921 4.67935 10.5489 8.81395 11.2892 9.30741 12.0664 8.31319 5.35303 4.05771 +knn(k): 16 12.4884 7.07042 8.37055 9.71906 7.53953 NaN NaN 4.28205 5.4346 NaN 5.35303 NaN NaN NaN 3.45833 4.05771 diff --git a/examples/simple/igraph_barabasi_game.c b/examples/simple/igraph_barabasi_game.c new file mode 100644 index 0000000..3e9ae92 --- /dev/null +++ b/examples/simple/igraph_barabasi_game.c @@ -0,0 +1,94 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + igraph_vector_int_t v; + igraph_vector_int_t v2, v3; + + igraph_barabasi_game(&g, 10, /*power=*/ 1, 2, 0, 0, /*A=*/ 1, 1, + IGRAPH_BARABASI_BAG, /*start_from=*/ 0); + if (igraph_ecount(&g) != 18) { + return 1; + } + if (igraph_vcount(&g) != 10) { + return 2; + } + if (!igraph_is_directed(&g)) { + return 3; + } + + igraph_vector_int_init(&v, 0); + igraph_get_edgelist(&g, &v, 0); + for (igraph_integer_t i = 0; i < igraph_ecount(&g); i++) { + if (VECTOR(v)[2 * i] <= VECTOR(v)[2 * i + 1]) { + return 4; + } + } + igraph_vector_int_destroy(&v); + igraph_destroy(&g); + + /* out-degree sequence */ + igraph_vector_int_init_int(&v3, 10, 0, 1, 3, 3, 4, 5, 6, 7, 8, 9); + + igraph_barabasi_game(&g, 10, /*power=*/ 1, 0, &v3, 0, /*A=*/ 1, 1, + IGRAPH_BARABASI_BAG, /*start_from=*/ 0); + if (igraph_ecount(&g) != igraph_vector_int_sum(&v3)) { + return 5; + } + igraph_vector_int_init(&v2, 0); + igraph_degree(&g, &v2, igraph_vss_all(), IGRAPH_OUT, 1); + for (igraph_integer_t i = 0; i < igraph_vcount(&g); i++) { + if (VECTOR(v3)[i] != VECTOR(v2)[i]) { + igraph_vector_int_print(&v3); + printf("\n"); + igraph_vector_int_print(&v2); + return 6; + } + } + igraph_vector_int_destroy(&v3); + igraph_vector_int_destroy(&v2); + igraph_destroy(&g); + + /* outpref, we cannot really test this quantitatively, + would need to set random seed */ + igraph_barabasi_game(&g, 10, /*power=*/ 1, 2, 0, 1, /*A=*/ 1, 1, + IGRAPH_BARABASI_BAG, /*start_from=*/ 0); + igraph_vector_int_init(&v, 0); + igraph_get_edgelist(&g, &v, 0); + for (igraph_integer_t i = 0; i < igraph_ecount(&g); i++) { + if (VECTOR(v)[2 * i] <= VECTOR(v)[2 * i + 1]) { + return 7; + } + } + if (!igraph_is_directed(&g)) { + return 8; + } + igraph_vector_int_destroy(&v); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_barabasi_game2.c b/examples/simple/igraph_barabasi_game2.c new file mode 100644 index 0000000..119f5ac --- /dev/null +++ b/examples/simple/igraph_barabasi_game2.c @@ -0,0 +1,109 @@ +/* -*- 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 +#include + +int main(void) { + + igraph_t g; + igraph_bool_t simple; + + igraph_barabasi_game(/* graph= */ &g, + /* n= */ 100, + /* power= */ 1.0, + /* m= */ 2, + /* outseq= */ 0, + /* outpref= */ 0, + /* A= */ 1.0, + /* directed= */ IGRAPH_DIRECTED, + /* algo= */ IGRAPH_BARABASI_PSUMTREE, + /* start_from= */ 0); + + if (igraph_ecount(&g) != 197) { + return 1; + } + if (igraph_vcount(&g) != 100) { + return 2; + } + igraph_is_simple(&g, &simple); + if (!simple) { + return 3; + } + + igraph_destroy(&g); + + /* ============================== */ + + igraph_barabasi_game(/* graph= */ &g, + /* n= */ 100, + /* power= */ 1.0, + /* m= */ 2, + /* outseq= */ 0, + /* outpref= */ 0, + /* A= */ 1.0, + /* directed= */ IGRAPH_DIRECTED, + /* algo= */ IGRAPH_BARABASI_PSUMTREE_MULTIPLE, + /* start_from= */ 0); + + if (igraph_ecount(&g) != 198) { + return 4; + } + if (igraph_vcount(&g) != 100) { + return 5; + } + igraph_is_simple(&g, &simple); + if (simple) { + return 6; + } + + igraph_destroy(&g); + + /* ============================== */ + + igraph_barabasi_game(/* graph= */ &g, + /* n= */ 100, + /* power= */ 1.0, + /* m= */ 2, + /* outseq= */ 0, + /* outpref= */ 0, + /* A= */ 1.0, + /* directed= */ IGRAPH_DIRECTED, + /* algo= */ IGRAPH_BARABASI_BAG, + /* start_from= */ 0); + + if (igraph_ecount(&g) != 198) { + return 7; + } + if (igraph_vcount(&g) != 100) { + return 8; + } + igraph_is_simple(&g, &simple); + if (simple) { + return 9; + } + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_bfs.c b/examples/simple/igraph_bfs.c new file mode 100644 index 0000000..7d0c95a --- /dev/null +++ b/examples/simple/igraph_bfs.c @@ -0,0 +1,70 @@ +/* -*- 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 + +*/ + +#include + +int main(void) { + + igraph_t graph, ring; + igraph_vector_int_t order, rank, father, pred, succ, dist; + + /* Create a disjoint union of two rings */ + igraph_ring(&ring, 10, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + igraph_disjoint_union(&graph, &ring, &ring); + igraph_destroy(&ring); + + /* Initialize the vectors where the result will be stored. Any of these + * can be omitted and replaced with a null pointer when calling + * igraph_bfs() */ + igraph_vector_int_init(&order, 0); + igraph_vector_int_init(&rank, 0); + igraph_vector_int_init(&father, 0); + igraph_vector_int_init(&pred, 0); + igraph_vector_int_init(&succ, 0); + igraph_vector_int_init(&dist, 0); + + /* Now call the BFS function */ + igraph_bfs(&graph, /*root=*/0, /*roots=*/ NULL, /*neimode=*/ IGRAPH_OUT, + /*unreachable=*/ 1, /*restricted=*/ NULL, + &order, &rank, &father, &pred, &succ, &dist, + /*callback=*/ NULL, /*extra=*/ NULL); + + /* Print the results */ + igraph_vector_int_print(&order); + igraph_vector_int_print(&rank); + igraph_vector_int_print(&father); + igraph_vector_int_print(&pred); + igraph_vector_int_print(&succ); + igraph_vector_int_print(&dist); + + /* Cleam up after ourselves */ + igraph_vector_int_destroy(&order); + igraph_vector_int_destroy(&rank); + igraph_vector_int_destroy(&father); + igraph_vector_int_destroy(&pred); + igraph_vector_int_destroy(&succ); + igraph_vector_int_destroy(&dist); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_bfs.out b/examples/simple/igraph_bfs.out new file mode 100644 index 0000000..e15657e --- /dev/null +++ b/examples/simple/igraph_bfs.out @@ -0,0 +1,6 @@ +0 1 9 2 8 3 7 4 6 5 10 11 19 12 18 13 17 14 16 15 +0 1 3 5 7 9 8 6 4 2 10 11 13 15 17 19 18 16 14 12 +-1 0 1 2 3 4 7 8 9 0 -1 10 11 12 13 14 17 18 19 10 +-1 0 9 8 7 6 4 3 2 1 -1 10 19 18 17 16 14 13 12 11 +1 9 8 7 6 -1 5 4 3 2 11 19 18 17 16 -1 15 14 13 12 +0 1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1 diff --git a/examples/simple/igraph_bfs_callback.c b/examples/simple/igraph_bfs_callback.c new file mode 100644 index 0000000..e5ca6c8 --- /dev/null +++ b/examples/simple/igraph_bfs_callback.c @@ -0,0 +1,62 @@ +/* -*- 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 + +*/ + +#include + +igraph_error_t bfs_callback(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) { + IGRAPH_UNUSED(graph); + IGRAPH_UNUSED(pred); + IGRAPH_UNUSED(succ); + IGRAPH_UNUSED(rank); + IGRAPH_UNUSED(dist); + printf(" %" IGRAPH_PRId "", vid); + return IGRAPH_SUCCESS; +} + +int main(void) { + igraph_t graph, ring; + + /* Create a disjoint union of two rings */ + igraph_ring(&ring, 10, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + igraph_disjoint_union(&graph, &ring, &ring); + igraph_destroy(&ring); + + /* Now call the BFS function */ + printf("("); + igraph_bfs(&graph, /*root=*/0, /*roots=*/ 0, /*neimode=*/ IGRAPH_OUT, + /*unreachable=*/ 1, /*restricted=*/ 0, + /*order=*/ 0, /*rank=*/ 0, /*father=*/ 0, /*pred=*/ 0, + /*succ=*/ 0, /*dist=*/ 0, + /*callback=*/ bfs_callback, /*extra=*/ 0); + printf(" )\n"); + + /* Cleam up after ourselves */ + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_bfs_callback.out b/examples/simple/igraph_bfs_callback.out new file mode 100644 index 0000000..ce158cf --- /dev/null +++ b/examples/simple/igraph_bfs_callback.out @@ -0,0 +1 @@ +( 0 1 9 2 8 3 7 4 6 5 10 11 19 12 18 13 17 14 16 15 ) diff --git a/examples/simple/igraph_bfs_simple.c b/examples/simple/igraph_bfs_simple.c new file mode 100644 index 0000000..1c3722f --- /dev/null +++ b/examples/simple/igraph_bfs_simple.c @@ -0,0 +1,49 @@ +/* -*- 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 + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_vector_int_t vids, layers, parents; + + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, 0, 0); + + igraph_vector_int_init(&vids, 0); + igraph_vector_int_init(&layers, 0); + igraph_vector_int_init(&parents, 0); + + igraph_bfs_simple(&g, 0, IGRAPH_ALL, &vids, &layers, &parents); + + igraph_vector_int_print(&vids); + igraph_vector_int_print(&layers); + igraph_vector_int_print(&parents); + + igraph_destroy(&g); + + igraph_vector_int_destroy(&vids); + igraph_vector_int_destroy(&layers); + igraph_vector_int_destroy(&parents); + + return 0; +} diff --git a/examples/simple/igraph_bfs_simple.out b/examples/simple/igraph_bfs_simple.out new file mode 100644 index 0000000..7c6d1f1 --- /dev/null +++ b/examples/simple/igraph_bfs_simple.out @@ -0,0 +1,3 @@ +0 1 2 3 4 5 6 7 8 9 +0 1 2 3 4 5 6 7 8 9 10 +-1 0 1 2 3 4 5 6 7 8 diff --git a/examples/simple/igraph_biconnected_components.c b/examples/simple/igraph_biconnected_components.c new file mode 100644 index 0000000..cfcabe0 --- /dev/null +++ b/examples/simple/igraph_biconnected_components.c @@ -0,0 +1,68 @@ +/* + IGraph library. + Copyright (C) 2006-2024 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 + +/* Prints a vector of edge IDs as u--v vertex pairs. */ +void print_edge_vector(const igraph_t *graph, const igraph_vector_int_t *edges) { + const igraph_integer_t n = igraph_vector_int_size(edges); + for (igraph_integer_t i=0; i < n; i++) { + igraph_integer_t edge = VECTOR(*edges)[i]; + printf("%" IGRAPH_PRId "--%" IGRAPH_PRId " ", IGRAPH_FROM(graph, edge), IGRAPH_TO(graph, edge)); + } + printf("\n"); +} + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t component_vertices, component_edges; + igraph_integer_t no; + + /* Create an example graph. */ + igraph_small(&graph, 7, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,3, 3,0, + 2,4, 4,5, 5,2, + 0,6, + 0,7, + -1); + + /* The data structures that the result will be written to must be initialized first. */ + igraph_vector_int_list_init(&component_vertices, 0); + igraph_vector_int_list_init(&component_edges, 0); + + igraph_biconnected_components(&graph, &no, NULL, &component_edges, &component_vertices, NULL); + + printf("Number of components: %" IGRAPH_PRId "\n", no); + for (igraph_integer_t i=0; i < no; i++) { + printf("\n"); + printf("Component %" IGRAPH_PRId ":\n", i); + printf("Vertices: "); + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&component_vertices, i)); + printf("Edges: "); + print_edge_vector(&graph, igraph_vector_int_list_get_ptr(&component_edges, i)); + } + + /* Destroy data structures after we no longer need them. */ + + igraph_vector_int_list_destroy(&component_edges); + igraph_vector_int_list_destroy(&component_vertices); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_biconnected_components.out b/examples/simple/igraph_biconnected_components.out new file mode 100644 index 0000000..7ebc7ba --- /dev/null +++ b/examples/simple/igraph_biconnected_components.out @@ -0,0 +1,17 @@ +Number of components: 4 + +Component 0: +Vertices: 5 4 2 +Edges: 5--2 5--4 4--2 + +Component 1: +Vertices: 3 2 1 0 +Edges: 3--0 3--2 2--1 1--0 + +Component 2: +Vertices: 6 0 +Edges: 6--0 + +Component 3: +Vertices: 7 0 +Edges: 7--0 diff --git a/examples/simple/igraph_bipartite_create.c b/examples/simple/igraph_bipartite_create.c new file mode 100644 index 0000000..67e279d --- /dev/null +++ b/examples/simple/igraph_bipartite_create.c @@ -0,0 +1,46 @@ +/* -*- 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 + +int main(void) { + + igraph_integer_t edges2[] = {0, 1, 1, 2, 3, 4, 5, 6, 6, 5, 1, 4, 1, 6, 0, 3 }; + igraph_t g; + igraph_vector_bool_t types; + igraph_vector_int_t edges; + igraph_integer_t i; + + igraph_vector_int_view(&edges, edges2, sizeof(edges2) / sizeof(edges2[0])); + igraph_vector_bool_init(&types, igraph_vector_int_max(&edges) + 1); + for (i = 0; i < igraph_vector_bool_size(&types); i++) { + VECTOR(types)[i] = i % 2; + } + igraph_create_bipartite(&g, &types, &edges, /*directed=*/ 1); + igraph_write_graph_edgelist(&g, stdout); + igraph_vector_bool_destroy(&types); + igraph_destroy(&g); + + + return 0; +} diff --git a/examples/simple/igraph_bipartite_create.out b/examples/simple/igraph_bipartite_create.out new file mode 100644 index 0000000..ef269aa --- /dev/null +++ b/examples/simple/igraph_bipartite_create.out @@ -0,0 +1,8 @@ +0 1 +0 3 +1 2 +1 4 +1 6 +3 4 +5 6 +6 5 diff --git a/examples/simple/igraph_bipartite_projection.c b/examples/simple/igraph_bipartite_projection.c new file mode 100644 index 0000000..b315456 --- /dev/null +++ b/examples/simple/igraph_bipartite_projection.c @@ -0,0 +1,51 @@ +/* + 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, see . +*/ + +#include +#include + +int main(void) { + igraph_t g, p1, p2; + igraph_vector_bool_t types; + igraph_vector_int_t mult1, mult2; + + igraph_vector_int_init(&mult1, 0); + igraph_vector_int_init(&mult2, 0); + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 0,3, 3,2, 2,4, 4,5, -1); + igraph_vector_bool_init_int(&types, 6, 0, 1, 0, 1, 1, 0); + + igraph_bipartite_projection(&g, &types, &p1, &p2, &mult1, &mult2, /*probe1=*/ 1); + + igraph_write_graph_edgelist(&p1, stdout); + printf("\n"); + igraph_write_graph_edgelist(&p2, stdout); + printf("\n"); + + igraph_vector_int_print(&mult1); + igraph_vector_int_print(&mult2); + + igraph_vector_int_destroy(&mult1); + igraph_vector_int_destroy(&mult2); + igraph_destroy(&p1); + igraph_destroy(&p2); + igraph_destroy(&g); + igraph_vector_bool_destroy(&types); + + return 0; +} diff --git a/examples/simple/igraph_cliques.c b/examples/simple/igraph_cliques.c new file mode 100644 index 0000000..4b5439b --- /dev/null +++ b/examples/simple/igraph_cliques.c @@ -0,0 +1,145 @@ +/* -*- 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 +#include + +int compare_vectors(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2) { + igraph_integer_t s1, s2, i; + + s1 = igraph_vector_int_size(v1); + s2 = igraph_vector_int_size(v2); + if (s1 < s2) { + return -1; + } + if (s1 > s2) { + return 1; + } + for (i = 0; i < s1; ++i) { + if (VECTOR(*v1)[i] < VECTOR(*v2)[i]) { + return -1; + } + if (VECTOR(*v1)[i] > VECTOR(*v2)[i]) { + return 1; + } + } + return 0; +} + +/* Takes a pointer vector of vectors. Sorts each vector, then sorts the pointer vector */ +void canonicalize_list(igraph_vector_int_list_t *list) { + igraph_integer_t len = igraph_vector_int_list_size(list); + for (igraph_integer_t i = 0; i < len; ++i) { + igraph_vector_int_sort(igraph_vector_int_list_get_ptr(list, i)); + } + igraph_vector_int_list_sort(list, &compare_vectors); +} + +struct userdata { + igraph_integer_t i; + igraph_vector_int_list_t *list; +}; + +igraph_error_t handler(const igraph_vector_int_t *clique, void *arg) { + struct userdata *ud; + igraph_bool_t cont; + + ud = (struct userdata *) arg; + cont = 1; /* true */ + + if (compare_vectors(clique, igraph_vector_int_list_get_ptr(ud->list, ud->i)) != 0) { + printf("igraph_cliques() and igraph_cliques_callback() give different results.\n"); + cont = 0; /* false */ + } + + ud->i += 1; + + return cont ? IGRAPH_SUCCESS : IGRAPH_STOP; +} + +void test_callback(const igraph_t *graph) { + igraph_vector_int_list_t list; + struct userdata ud; + + igraph_vector_int_list_init(&list, 0); + igraph_cliques(graph, &list, 0, 0); + + ud.i = 0; + ud.list = &list; + + igraph_cliques_callback(graph, 0, 0, &handler, (void *) &ud); + + igraph_vector_int_list_destroy(&list); +} + + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t result; + igraph_es_t es; + igraph_integer_t omega; + igraph_integer_t i, j, n; + const int params[] = {4, -1, 2, 2, 0, 0, -1, -1}; + + igraph_set_warning_handler(igraph_warning_handler_ignore); + + igraph_vector_int_list_init(&result, 0); + igraph_full(&g, 6, 0, 0); + igraph_es_pairs_small(&es, 0, 0, 1, 0, 2, 3, 5, -1); + igraph_delete_edges(&g, es); + igraph_es_destroy(&es); + + for (j = 0; j < sizeof(params) / (2 * sizeof(params[0])); j++) { + if (params[2 * j + 1] != 0) { + igraph_cliques(&g, &result, params[2 * j], params[2 * j + 1]); + } else { + igraph_largest_cliques(&g, &result); + } + n = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " cliques found\n", n); + canonicalize_list(&result); + for (i = 0; i < n; i++) { + igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(&result, i); + igraph_vector_int_print(v); + } + } + + igraph_clique_number(&g, &omega); + printf("omega=%" IGRAPH_PRId "\n", omega); + + test_callback(&g); + + igraph_destroy(&g); + + igraph_kary_tree(&g, 5, 2, IGRAPH_TREE_OUT); + igraph_cliques(&g, &result, 5, 5); + if (igraph_vector_int_list_size(&result) != 0) { + return 1; + } + + igraph_destroy(&g); + igraph_vector_int_list_destroy(&result); + + return 0; +} diff --git a/examples/simple/igraph_cliques.out b/examples/simple/igraph_cliques.out new file mode 100644 index 0000000..351d033 --- /dev/null +++ b/examples/simple/igraph_cliques.out @@ -0,0 +1,50 @@ +2 cliques found +1 2 3 4 +1 2 4 5 +12 cliques found +0 3 +0 4 +0 5 +1 2 +1 3 +1 4 +1 5 +2 3 +2 4 +2 5 +3 4 +4 5 +2 cliques found +1 2 3 4 +1 2 4 5 +29 cliques found +0 +1 +2 +3 +4 +5 +0 3 +0 4 +0 5 +1 2 +1 3 +1 4 +1 5 +2 3 +2 4 +2 5 +3 4 +4 5 +0 3 4 +0 4 5 +1 2 3 +1 2 4 +1 2 5 +1 3 4 +1 4 5 +2 3 4 +2 4 5 +1 2 3 4 +1 2 4 5 +omega=4 diff --git a/examples/simple/igraph_cocitation.c b/examples/simple/igraph_cocitation.c new file mode 100644 index 0000000..f8dfa02 --- /dev/null +++ b/examples/simple/igraph_cocitation.c @@ -0,0 +1,48 @@ +/* -*- 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, see . +*/ + +#include +#include + +int main(void) { + igraph_t graph; + igraph_matrix_t matrix; + + /* Create a small test graph. */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, + 0, 1, 2, 1, 2, 0, 3, 0, + -1); + + /* As usual with igraph functions, the data structure in which the result + will be returned must be initialized in advance. */ + igraph_matrix_init(&matrix, 0, 0); + igraph_bibcoupling(&graph, &matrix, igraph_vss_all()); + printf("Bibliographic coupling matrix:\n"); + igraph_matrix_print(&matrix); + + igraph_cocitation(&graph, &matrix, igraph_vss_all()); + printf("\nCocitation matrix:\n"); + igraph_matrix_print(&matrix); + + /* Destroy data structures when we are done with them. */ + igraph_matrix_destroy(&matrix); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_cocitation.out b/examples/simple/igraph_cocitation.out new file mode 100644 index 0000000..8d83c5d --- /dev/null +++ b/examples/simple/igraph_cocitation.out @@ -0,0 +1,11 @@ +Bibliographic coupling matrix: +0 0 1 0 +0 0 0 0 +1 0 0 1 +0 0 1 0 + +Cocitation matrix: +0 1 0 0 +1 0 0 0 +0 0 0 0 +0 0 0 0 diff --git a/examples/simple/igraph_coloring.c b/examples/simple/igraph_coloring.c new file mode 100644 index 0000000..1ad2251 --- /dev/null +++ b/examples/simple/igraph_coloring.c @@ -0,0 +1,39 @@ + +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t colors; + + /* Setting a seed makes the result of erdos_renyi_game_gnm deterministic. */ + igraph_rng_seed(igraph_rng_default(), 42); + + /* IGRAPH_UNDIRECTED and IGRAPH_NO_LOOPS are both equivalent to 0/FALSE, but + communicate intent better in this context. */ + igraph_erdos_renyi_game_gnm(&graph, 1000, 10000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + /* As with all igraph functions, the vector in which the result is returned must + be initialized in advance. */ + igraph_vector_int_init(&colors, 0); + igraph_vertex_coloring_greedy(&graph, &colors, IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS); + + /* Verify that the colouring is valid, i.e. no two adjacent vertices have the same colour. */ + { + igraph_integer_t i; + /* Store the edge count to avoid the overhead from igraph_ecount in the for loop. */ + igraph_integer_t no_of_edges = igraph_ecount(&graph); + for (i = 0; i < no_of_edges; ++i) { + if ( VECTOR(colors)[ IGRAPH_FROM(&graph, i) ] == VECTOR(colors)[ IGRAPH_TO(&graph, i) ] ) { + printf("Inconsistent coloring! Vertices %" IGRAPH_PRId " and %" IGRAPH_PRId " are adjacent but have the same color.\n", + IGRAPH_FROM(&graph, i), IGRAPH_TO(&graph, i)); + abort(); + } + } + } + + /* Destroy data structure when we are done. */ + igraph_vector_int_destroy(&colors); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_community_edge_betweenness.c b/examples/simple/igraph_community_edge_betweenness.c new file mode 100644 index 0000000..8a95e49 --- /dev/null +++ b/examples/simple/igraph_community_edge_betweenness.c @@ -0,0 +1,57 @@ +/* + 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, see . +*/ + +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t edges; + igraph_vector_t eb, weights; + igraph_matrix_int_t merges; + igraph_vector_int_t bridges; + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,1, 0,1, 0,1, -1); + igraph_vector_init_int(&weights, 3, 1, 2, 3); + igraph_vector_init(&eb, 0); + igraph_vector_int_init(&edges, 0); + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_int_init(&bridges, 0); + igraph_community_edge_betweenness(&graph, &edges, &eb, &merges, + &bridges, /*modularity*/ NULL, + /*membership*/ NULL, + IGRAPH_UNDIRECTED, + &weights); + printf("edges:\n"); + igraph_vector_int_print(&edges); + printf("edge betweenness:\n"); + igraph_vector_print(&eb); + printf("merges:\n"); + igraph_matrix_int_print(&merges); + printf("bridges:\n"); + igraph_vector_int_print(&bridges); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&eb); + igraph_vector_destroy(&weights); + igraph_matrix_int_destroy(&merges); + igraph_vector_int_destroy(&bridges); + + return 0; +} diff --git a/examples/simple/igraph_community_edge_betweenness.out b/examples/simple/igraph_community_edge_betweenness.out new file mode 100644 index 0000000..d4ac617 --- /dev/null +++ b/examples/simple/igraph_community_edge_betweenness.out @@ -0,0 +1,8 @@ +edges: +0 1 2 +edge betweenness: +1 1 1 +merges: +0 1 +bridges: +2 diff --git a/examples/simple/igraph_community_fastgreedy.c b/examples/simple/igraph_community_fastgreedy.c new file mode 100644 index 0000000..559e120 --- /dev/null +++ b/examples/simple/igraph_community_fastgreedy.c @@ -0,0 +1,39 @@ +/* + 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, see . +*/ + +#include + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_matrix_int_t merges; + + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_init_int(&weights, 8, 10, 10, 1, 1, 1, 1, 1, 1); + + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,3, 2,4, 2,5, 3,4, 3,5, 4,5, -1); + igraph_community_fastgreedy(&g, &weights, &merges, + /*modularity*/ NULL, + /*membership=*/ NULL); + igraph_matrix_int_print(&merges); + igraph_destroy(&g); + igraph_vector_destroy(&weights); + igraph_matrix_int_destroy(&merges); + return 0; +} diff --git a/examples/simple/igraph_community_fastgreedy.out b/examples/simple/igraph_community_fastgreedy.out new file mode 100644 index 0000000..ba79f77 --- /dev/null +++ b/examples/simple/igraph_community_fastgreedy.out @@ -0,0 +1,5 @@ +1 0 +2 6 +3 4 +8 5 +9 7 diff --git a/examples/simple/igraph_community_label_propagation.c b/examples/simple/igraph_community_label_propagation.c new file mode 100644 index 0000000..990818a --- /dev/null +++ b/examples/simple/igraph_community_label_propagation.c @@ -0,0 +1,58 @@ +/* -*- 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 +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t membership; + igraph_real_t modularity; + + igraph_famous(&graph, "Zachary"); /* We use Zachary's karate club network. */ + + /* Label propagation is a stochastic method; the result will depend on the random seed. */ + igraph_rng_seed(igraph_rng_default(), 123); + + /* All igraph functions that returns their result in an igraph_vector_t must be given + an already initialized vector. */ + igraph_vector_int_init(&membership, 0); + igraph_community_label_propagation( + &graph, &membership, /* mode = */ IGRAPH_ALL, + /* weights= */ NULL, /* initial= */ NULL, /* fixed= */ NULL + ); + + /* Also calculate the modularity of the partition */ + igraph_modularity( + &graph, &membership, /* weights= */ NULL, /* resolution = */ 1, + /* directed= */ 0, &modularity); + + printf("%" IGRAPH_PRId " communities found; modularity score is %g.\n", + igraph_vector_int_max(&membership) + 1, modularity); + + printf("Communities membership: "); + igraph_vector_int_print(&membership); + + /* Destroy data structures at the end. */ + igraph_vector_int_destroy(&membership); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_community_label_propagation.out b/examples/simple/igraph_community_label_propagation.out new file mode 100644 index 0000000..24dd8f6 --- /dev/null +++ b/examples/simple/igraph_community_label_propagation.out @@ -0,0 +1,2 @@ +3 communities found; modularity score is 0.394395. +Communities membership: 0 0 0 0 1 1 1 0 0 0 1 0 0 0 2 2 1 0 2 0 2 0 2 2 2 2 2 2 2 2 0 2 2 2 diff --git a/examples/simple/igraph_community_leading_eigenvector.c b/examples/simple/igraph_community_leading_eigenvector.c new file mode 100644 index 0000000..72d1edf --- /dev/null +++ b/examples/simple/igraph_community_leading_eigenvector.c @@ -0,0 +1,90 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + igraph_matrix_int_t merges; + igraph_vector_int_t membership; + igraph_vector_t x; + igraph_arpack_options_t options; + + /* Zachary Karate club */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_int_init(&membership, 0); + igraph_vector_init(&x, 0); + igraph_arpack_options_init(&options); + + igraph_community_leading_eigenvector(&g, /*weights=*/ 0, &merges, + &membership, 1, + &options, /*modularity=*/ 0, + /*start=*/ 0, /*eigenvalues=*/ 0, + /*eigenvectors=*/ 0, /*history=*/ 0, + /*callback=*/ 0, + /*callback_extra=*/ 0); + + igraph_matrix_int_print(&merges); + igraph_vector_int_print(&membership); + + printf("\n"); + + /* Make all the steps */ + igraph_community_leading_eigenvector(&g, /*weights=*/ 0, &merges, + &membership, igraph_vcount(&g), + &options, /*modularity=*/ 0, + /*start=*/ 0, /*eigenvalues=*/ 0, + /*eigenvectors=*/ 0, /*history=*/ 0, + /*callback=*/ 0, + /*callback_extra=*/ 0); + + igraph_matrix_int_print(&merges); + igraph_vector_int_print(&membership); + + igraph_vector_destroy(&x); + igraph_vector_int_destroy(&membership); + igraph_matrix_int_destroy(&merges); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_community_leading_eigenvector.out b/examples/simple/igraph_community_leading_eigenvector.out new file mode 100644 index 0000000..195196c --- /dev/null +++ b/examples/simple/igraph_community_leading_eigenvector.out @@ -0,0 +1,7 @@ +0 1 +0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 + +1 3 +0 2 +5 4 +0 2 2 2 0 0 0 2 1 1 0 0 2 2 1 1 0 2 1 2 1 2 1 3 3 3 1 3 3 1 1 3 1 1 diff --git a/examples/simple/igraph_community_leiden.c b/examples/simple/igraph_community_leiden.c new file mode 100644 index 0000000..f0ab8de --- /dev/null +++ b/examples/simple/igraph_community_leiden.c @@ -0,0 +1,82 @@ +/* -*- 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 + +int main(void) { + igraph_t graph; + igraph_vector_int_t membership; + igraph_vector_int_t degree; + igraph_vector_t weights; + igraph_integer_t nb_clusters, i; + igraph_real_t quality; + + /* Set default seed to get reproducible results */ + igraph_rng_seed(igraph_rng_default(), 0); + + /* Simple unweighted graph */ + igraph_small(&graph, 10, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 5, 6, 5, 7, 5, 8, 5, 9, 6, 7, 6, 8, 6, 9, 7, 8, 7, 9, 8, 9, + 0, 5, -1); + + /* Perform Leiden algorithm using CPM for 1 iteration */ + igraph_vector_int_init(&membership, igraph_vcount(&graph)); + igraph_community_leiden(&graph, NULL, NULL, 0.05, 0.01, 0, 1, &membership, &nb_clusters, &quality); + + printf("Leiden found %" IGRAPH_PRId " clusters using CPM (resolution parameter 0.05), quality is %.4f.\n", nb_clusters, quality); + printf("Membership: "); + igraph_vector_int_print(&membership); + printf("\n"); + + /* Start from existing membership for 10 iterations to improve it further */ + igraph_community_leiden(&graph, NULL, NULL, 0.05, 0.01, 1, 10, &membership, &nb_clusters, &quality); + + printf("Iterated Leiden, using CPM (resolution parameter 0.05), quality is %.4f.\n", quality); + printf("Membership: "); + igraph_vector_int_print(&membership); + printf("\n"); + + /* Initialize degree vector to use for optimizing modularity */ + igraph_vector_int_init(°ree, igraph_vcount(&graph)); + igraph_degree(&graph, °ree, igraph_vss_all(), IGRAPH_ALL, 1); + igraph_vector_init(&weights, igraph_vector_int_size(°ree)); + for (i = 0; i < igraph_vector_int_size(°ree); i++) { + VECTOR(weights)[i] = VECTOR(degree)[i]; + } + + /* Perform Leiden algorithm using modularity until stable iteration */ + igraph_community_leiden(&graph, NULL, &weights, 1.0 / (2 * igraph_ecount(&graph)), 0.01, 0, -1, &membership, &nb_clusters, &quality); + + printf("Leiden found %" IGRAPH_PRId " clusters using modularity, quality is %.4f.\n", nb_clusters, quality); + printf("Membership: "); + igraph_vector_int_print(&membership); + printf("\n"); + + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(°ree); + igraph_vector_int_destroy(&membership); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_community_leiden.out b/examples/simple/igraph_community_leiden.out new file mode 100644 index 0000000..ec6463b --- /dev/null +++ b/examples/simple/igraph_community_leiden.out @@ -0,0 +1,9 @@ +Leiden found 2 clusters using CPM (resolution parameter 0.05), quality is 0.8929. +Membership: 0 0 0 0 0 1 1 1 1 1 + +Iterated Leiden, using CPM (resolution parameter 0.05), quality is 0.8929. +Membership: 0 0 0 0 0 1 1 1 1 1 + +Leiden found 2 clusters using modularity, quality is 0.4524. +Membership: 0 0 0 0 0 1 1 1 1 1 + diff --git a/examples/simple/igraph_community_multilevel.c b/examples/simple/igraph_community_multilevel.c new file mode 100644 index 0000000..1dee730 --- /dev/null +++ b/examples/simple/igraph_community_multilevel.c @@ -0,0 +1,112 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sts=4 sw=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 + +void show_results(igraph_t *g, igraph_vector_int_t *membership, igraph_matrix_int_t *memberships, igraph_vector_t *modularity, FILE* f) { + igraph_integer_t i, j, no_of_nodes = igraph_vcount(g); + + j = igraph_vector_which_max(modularity); + for (i = 0; i < igraph_vector_int_size(membership); i++) { + if (VECTOR(*membership)[i] != MATRIX(*memberships, j, i)) { + fprintf(f, "WARNING: best membership vector element %" IGRAPH_PRId " does not match the best one in the membership matrix\n", i); + } + } + + fprintf(f, "Modularities:\n"); + igraph_vector_print(modularity); + + for (i = 0; i < igraph_matrix_int_nrow(memberships); i++) { + for (j = 0; j < no_of_nodes; j++) { + fprintf(f, "%" IGRAPH_PRId " ", MATRIX(*memberships, i, j)); + } + fprintf(f, "\n"); + } + + fprintf(f, "\n"); +} + +int main(void) { + igraph_t g; + igraph_vector_t modularity; + igraph_vector_int_t edges; + igraph_vector_int_t membership; + igraph_matrix_int_t memberships; + igraph_integer_t i, j, k; + + igraph_vector_init(&modularity, 0); + igraph_vector_int_init(&membership, 0); + igraph_matrix_int_init(&memberships, 0, 0); + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Unweighted test graph from the paper of Blondel et al */ + igraph_small(&g, 16, IGRAPH_UNDIRECTED, + 0, 2, 0, 3, 0, 4, 0, 5, + 1, 2, 1, 4, 1, 7, + 2, 4, 2, 5, 2, 6, + 3, 7, + 4, 10, + 5, 7, 5, 11, + 6, 7, 6, 11, + 8, 9, 8, 10, 8, 11, 8, 14, 8, 15, + 9, 12, 9, 14, + 10, 11, 10, 12, 10, 13, 10, 14, + 11, 13, + -1); + igraph_community_multilevel(&g, 0, 1, &membership, &memberships, &modularity); + show_results(&g, &membership, &memberships, &modularity, stdout); + + /* Higher resolution */ + igraph_community_multilevel(&g, 0, 1.5, &membership, &memberships, &modularity); + show_results(&g, &membership, &memberships, &modularity, stdout); + + igraph_destroy(&g); + + /* Ring of 30 cliques */ + igraph_vector_int_init(&edges, 0); + for (i = 0; i < 30; i++) { + for (j = 0; j < 5; j++) { + for (k = j + 1; k < 5; k++) { + igraph_vector_int_push_back(&edges, i * 5 + j); + igraph_vector_int_push_back(&edges, i * 5 + k); + } + } + } + for (i = 0; i < 30; i++) { + igraph_vector_int_push_back(&edges, i * 5 % 150); + igraph_vector_int_push_back(&edges, (i * 5 + 6) % 150); + } + igraph_create(&g, &edges, 150, 0); + igraph_community_multilevel(&g, 0, 1, &membership, &memberships, &modularity); + show_results(&g, &membership, &memberships, &modularity, stdout); + igraph_destroy(&g); + + igraph_vector_destroy(&modularity); + igraph_vector_int_destroy(&membership); + igraph_vector_int_destroy(&edges); + igraph_matrix_int_destroy(&memberships); + + return 0; +} diff --git a/examples/simple/igraph_community_multilevel.out b/examples/simple/igraph_community_multilevel.out new file mode 100644 index 0000000..637af12 --- /dev/null +++ b/examples/simple/igraph_community_multilevel.out @@ -0,0 +1,15 @@ +Modularities: +0.346301 0.392219 +0 0 0 1 0 0 1 1 2 2 2 3 2 3 2 2 +0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 + +Modularities: +0.179847 0.205357 +0 1 1 0 1 2 3 2 4 4 4 3 4 3 4 4 +0 1 1 0 1 0 2 0 3 3 3 2 3 2 3 3 + +Modularities: +0.875758 0.887879 +0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 8 8 8 8 8 9 9 9 9 9 10 10 10 10 10 11 11 11 11 11 12 12 12 12 12 13 13 13 13 13 14 14 14 14 14 15 15 15 15 15 16 16 16 16 16 17 17 17 17 17 18 18 18 18 18 19 19 19 19 19 20 20 20 20 20 21 21 21 21 21 22 22 22 22 22 23 23 23 23 23 24 24 24 24 24 25 25 25 25 25 26 26 26 26 26 27 27 27 27 27 28 28 28 28 28 29 29 29 29 29 +0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 10 10 10 10 10 10 10 10 10 10 11 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 12 12 12 13 13 13 13 13 13 13 13 13 13 14 14 14 14 14 14 14 14 14 14 0 0 0 0 0 + diff --git a/examples/simple/igraph_community_optimal_modularity.c b/examples/simple/igraph_community_optimal_modularity.c new file mode 100644 index 0000000..74542ff --- /dev/null +++ b/examples/simple/igraph_community_optimal_modularity.c @@ -0,0 +1,110 @@ +/* -*- 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 + +void prepare_weights_vector(igraph_vector_t* weights, const igraph_t* graph) { + igraph_integer_t i, n = igraph_ecount(graph); + igraph_vector_resize(weights, n); + for (i = 0; i < n; i++) { + VECTOR(*weights)[i] = i % 5; + } +} + +int main(void) { + igraph_t graph; + + igraph_vector_int_t v; + igraph_integer_t edges[] = { 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 + }; + + igraph_vector_int_t membership; + igraph_vector_t weights; + igraph_real_t modularity; + igraph_bool_t simple; + igraph_error_t retval; + + igraph_vector_int_view(&v, edges, sizeof(edges) / sizeof(edges[0])); + igraph_create(&graph, &v, 0, IGRAPH_UNDIRECTED); + + igraph_vector_init(&weights, 0); + + igraph_is_simple(&graph, &simple); + if (!simple) { + return 1; + } + + igraph_vector_int_init(&membership, 0); + + igraph_set_error_handler(&igraph_error_handler_printignore); + + /* Zachary karate club, unweighted */ + retval = igraph_community_optimal_modularity(&graph, &modularity, + &membership, 0); + if (retval == IGRAPH_UNIMPLEMENTED) { + return 77; + } + if (fabs(modularity - 0.4197896) > 0.0000001) { + return 2; + } + /* Zachary karate club, weighted */ + prepare_weights_vector(&weights, &graph); + igraph_community_optimal_modularity(&graph, &modularity, + &membership, &weights); + if (fabs(modularity - 0.5115767) > 0.0000001) { + return 4; + } + igraph_destroy(&graph); + + /* simple graph with loop edges, unweighted */ + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 0, 0, 0, 2, 2, -1); + igraph_community_optimal_modularity(&graph, &modularity, + &membership, 0); + if (fabs(modularity - 0.28125) > 0.00001) { + return 3; + } + /* simple graph with loop edges, weighted */ + prepare_weights_vector(&weights, &graph); + igraph_community_optimal_modularity(&graph, &modularity, + &membership, &weights); + if (fabs(modularity - 0.36686) > 0.00001) { + return 5; + } + igraph_destroy(&graph); + + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&weights); + + return 0; +} diff --git a/examples/simple/igraph_complementer.c b/examples/simple/igraph_complementer.c new file mode 100644 index 0000000..2aa79b8 --- /dev/null +++ b/examples/simple/igraph_complementer.c @@ -0,0 +1,109 @@ +/* -*- 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 + +int main(void) { + + igraph_t g1, g2; + + /* complementer of the empty graph */ + igraph_empty(&g1, 5, IGRAPH_DIRECTED); + igraph_complementer(&g2, &g1, IGRAPH_LOOPS); + igraph_write_graph_edgelist(&g2, stdout); + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("---\n"); + + /* the same without loops */ + igraph_empty(&g1, 5, IGRAPH_DIRECTED); + igraph_complementer(&g2, &g1, IGRAPH_NO_LOOPS); + igraph_write_graph_edgelist(&g2, stdout); + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("---\n"); + + /* complementer of the full graph */ + igraph_full(&g1, 5, IGRAPH_DIRECTED, IGRAPH_LOOPS); + igraph_complementer(&g2, &g1, IGRAPH_LOOPS); + if (igraph_ecount(&g2) != 0) { + return 1; + } + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("---\n"); + + /* complementer of the full graph, results loops only */ + igraph_full(&g1, 5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_complementer(&g2, &g1, IGRAPH_LOOPS); + igraph_write_graph_edgelist(&g2, stdout); + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("---\n"); + + /************** + * undirected * + *************/ + + /* complementer of the empty graph */ + igraph_empty(&g1, 5, IGRAPH_UNDIRECTED); + igraph_complementer(&g2, &g1, IGRAPH_LOOPS); + igraph_write_graph_edgelist(&g2, stdout); + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("---\n"); + + /* the same without loops */ + igraph_empty(&g1, 5, IGRAPH_UNDIRECTED); + igraph_complementer(&g2, &g1, IGRAPH_NO_LOOPS); + igraph_write_graph_edgelist(&g2, stdout); + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("---\n"); + + /* complementer of the full graph */ + igraph_full(&g1, 5, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + igraph_complementer(&g2, &g1, IGRAPH_LOOPS); + if (igraph_ecount(&g2) != 0) { + return 1; + } + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("---\n"); + + /* complementer of the full graph, results loops only */ + igraph_full(&g1, 5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_complementer(&g2, &g1, IGRAPH_LOOPS); + igraph_write_graph_edgelist(&g2, stdout); + igraph_destroy(&g1); + igraph_destroy(&g2); + + return 0; +} diff --git a/examples/simple/igraph_complementer.out b/examples/simple/igraph_complementer.out new file mode 100644 index 0000000..25f2fdb --- /dev/null +++ b/examples/simple/igraph_complementer.out @@ -0,0 +1,87 @@ +0 0 +0 1 +0 2 +0 3 +0 4 +1 0 +1 1 +1 2 +1 3 +1 4 +2 0 +2 1 +2 2 +2 3 +2 4 +3 0 +3 1 +3 2 +3 3 +3 4 +4 0 +4 1 +4 2 +4 3 +4 4 +--- +0 1 +0 2 +0 3 +0 4 +1 0 +1 2 +1 3 +1 4 +2 0 +2 1 +2 3 +2 4 +3 0 +3 1 +3 2 +3 4 +4 0 +4 1 +4 2 +4 3 +--- +--- +0 0 +1 1 +2 2 +3 3 +4 4 +--- +0 0 +0 1 +0 2 +0 3 +0 4 +1 1 +1 2 +1 3 +1 4 +2 2 +2 3 +2 4 +3 3 +3 4 +4 4 +--- +0 1 +0 2 +0 3 +0 4 +1 2 +1 3 +1 4 +2 3 +2 4 +3 4 +--- +--- +0 0 +1 1 +2 2 +3 3 +4 4 diff --git a/examples/simple/igraph_compose.c b/examples/simple/igraph_compose.c new file mode 100644 index 0000000..f2ed3a3 --- /dev/null +++ b/examples/simple/igraph_compose.c @@ -0,0 +1,117 @@ +/* -*- 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 + +int main(void) { + + igraph_t g1, g2, res; + igraph_vector_int_t v; + igraph_vector_int_t map1, map2; + + igraph_vector_int_init(&map1, 0); + igraph_vector_int_init(&map2, 0); + + /* composition with the empty graph */ + igraph_empty(&g1, 5, IGRAPH_DIRECTED); + igraph_full(&g2, 5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_compose(&res, &g1, &g2, &map1, &map2); + if (igraph_ecount(&res) != 0) { + return 1; + } + if (igraph_vector_int_size(&map1) != 0 || igraph_vector_int_size(&map2) != 0) { + return 11; + } + igraph_destroy(&res); + igraph_compose(&res, &g2, &g1, &map1, &map2); + if (igraph_ecount(&res) != 0) { + return 2; + } + if (igraph_vector_int_size(&map1) != 0 || igraph_vector_int_size(&map2) != 0) { + return 12; + } + igraph_destroy(&res); + igraph_destroy(&g1); + igraph_destroy(&g2); + + /* same but undirected */ + igraph_empty(&g1, 5, IGRAPH_UNDIRECTED); + igraph_full(&g2, 5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_compose(&res, &g1, &g2, &map1, &map2); + if (igraph_ecount(&res) != 0) { + return 1; + } + if (igraph_vector_int_size(&map1) != 0 || igraph_vector_int_size(&map2) != 0) { + return 11; + } + igraph_destroy(&res); + igraph_compose(&res, &g2, &g1, &map1, &map2); + if (igraph_ecount(&res) != 0) { + return 2; + } + if (igraph_vector_int_size(&map1) != 0 || igraph_vector_int_size(&map2) != 0) { + return 12; + } + igraph_destroy(&res); + igraph_destroy(&g1); + igraph_destroy(&g2); + + /* proper directed graph */ + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 5, 6, -1); + igraph_create(&g1, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + igraph_vector_int_init_int_end(&v, -1, 0, 1, 2, 4, 5, 6, -1); + igraph_create(&g2, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + igraph_compose(&res, &g1, &g2, &map1, &map2); + igraph_write_graph_edgelist(&res, stdout); + igraph_vector_int_print(&map1); + igraph_vector_int_print(&map2); + igraph_destroy(&res); + igraph_destroy(&g1); + igraph_destroy(&g2); + + /* undirected graph */ + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 5, 6, -1); + igraph_create(&g1, &v, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&v); + + igraph_vector_int_init_int_end(&v, -1, 0, 1, 0, 4, 5, 6, -1); + igraph_create(&g2, &v, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&v); + + igraph_compose(&res, &g1, &g2, &map1, &map2); + igraph_write_graph_edgelist(&res, stdout); + igraph_vector_int_print(&map1); + igraph_vector_int_print(&map2); + igraph_destroy(&res); + igraph_destroy(&g1); + igraph_destroy(&g2); + + igraph_vector_int_destroy(&map2); + igraph_vector_int_destroy(&map1); + + return 0; +} diff --git a/examples/simple/igraph_compose.out b/examples/simple/igraph_compose.out new file mode 100644 index 0000000..0f37aa3 --- /dev/null +++ b/examples/simple/igraph_compose.out @@ -0,0 +1,11 @@ +1 4 +1 +1 +0 0 +0 2 +1 1 +1 4 +5 5 +6 6 +0 0 0 1 2 2 +0 1 0 0 2 2 diff --git a/examples/simple/igraph_contract_vertices.c b/examples/simple/igraph_contract_vertices.c new file mode 100644 index 0000000..99794e2 --- /dev/null +++ b/examples/simple/igraph_contract_vertices.c @@ -0,0 +1,81 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +/* Create the condensation of a directed graph. + * See https://en.wikipedia.org/wiki/Strongly_connected_component#Definitions + * This example demonstrates how to write a basic igraph function, complete + * with error handling. */ +igraph_error_t condensation(const igraph_t *graph, igraph_t *cond) { + igraph_vector_int_t membership; + + /* Data structures such as vector must be initialized in igraph before use. */ + IGRAPH_CHECK(igraph_vector_int_init(&membership, 0)); + + /* Adding the initialized vector to the "finally" stack ensures that it will + * be automatically destroyed if an error occurs. */ + IGRAPH_FINALLY(igraph_vector_int_destroy, &membership); + + /* Functions that return an error code can be wrapped in IGRAPH_CHECK to pass that error + * up to the caller. */ + IGRAPH_CHECK(igraph_connected_components(graph, &membership, /* csize */ NULL, /* no */ NULL, IGRAPH_STRONG)); + + /* To compute the condensation, we simply contract strongly connected components. + * Since igraph_contract_vertices() modifies graphs in-place, we make a copy first. */ + IGRAPH_CHECK(igraph_copy(cond, graph)); + + /* Since we are not done creating the condensation yet, we add 'cond' to the + * "finally" stack, so that it will be destroyed if an error occurs. */ + IGRAPH_FINALLY(igraph_destroy, cond); + + /* Contract strongly connected components. */ + IGRAPH_CHECK(igraph_contract_vertices(cond, &membership, NULL)); + + /* igraph_contract_vertices() preserves all edges, some of which become + * parallel edges or self-loops after the contraction. We simplify these. */ + IGRAPH_CHECK(igraph_simplify(cond, /* remove_multiple */ true, /* remove_loops */ true, NULL)); + + /* Data structures that are no longer needed must be explicitly destroyed. + * If they were added to the "finally" stack, they must be removed explicitly, + * in the opposite order to how they were added. IGRAPH_FINALLY_CLEAN removes + * the indicated number of entries from the "finally" stack. We remove + * 'membership' because it was destroyed, and 'cond' because the responsibility + * to destroy it is now with the caller. */ + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; /* return with no error */ +} + +int main(void) { + igraph_t graph, cond; + + /* Create a random directed graph with mean degree 2 and compute its condensation. */ + igraph_erdos_renyi_game_gnm(&graph, 100, 200, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + condensation(&graph, &cond); + + printf("Number of vertices in the condensation: %" IGRAPH_PRId "\n", igraph_vcount(&cond)); + igraph_write_graph_edgelist(&cond, stdout); + + /* Destroy data structures that are no longer needed. */ + igraph_destroy(&graph); + igraph_destroy(&cond); + + return 0; +} diff --git a/examples/simple/igraph_copy.c b/examples/simple/igraph_copy.c new file mode 100644 index 0000000..56e0055 --- /dev/null +++ b/examples/simple/igraph_copy.c @@ -0,0 +1,56 @@ +/* -*- 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 + +int main(void) { + + igraph_t g1, g2; + igraph_vector_int_t v1, v2; + + igraph_vector_int_init(&v1, 8); + VECTOR(v1)[0] = 0; + VECTOR(v1)[1] = 1; + VECTOR(v1)[2] = 1; + VECTOR(v1)[3] = 2; + VECTOR(v1)[4] = 2; + VECTOR(v1)[5] = 3; + VECTOR(v1)[6] = 2; + VECTOR(v1)[7] = 2; + + igraph_create(&g1, &v1, 0, 0); + igraph_copy(&g2, &g1); + + igraph_vector_int_init(&v2, 0); + igraph_get_edgelist(&g2, &v2, 0); + if (!igraph_vector_int_all_e(&v1, &v2)) { + return 1; + } + + igraph_vector_int_destroy(&v1); + igraph_vector_int_destroy(&v2); + igraph_destroy(&g1); + igraph_destroy(&g2); + + return 0; +} diff --git a/examples/simple/igraph_create.c b/examples/simple/igraph_create.c new file mode 100644 index 0000000..10f30ce --- /dev/null +++ b/examples/simple/igraph_create.c @@ -0,0 +1,70 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + igraph_vector_int_t v1, v2; + + /* simple use */ + igraph_vector_int_init(&v1, 8); + VECTOR(v1)[0] = 0; + VECTOR(v1)[1] = 1; + VECTOR(v1)[2] = 1; + VECTOR(v1)[3] = 2; + VECTOR(v1)[4] = 2; + VECTOR(v1)[5] = 3; + VECTOR(v1)[6] = 2; + VECTOR(v1)[7] = 2; + igraph_create(&g, &v1, 0, 0); + if (igraph_vcount(&g) != 4) { + return 1; + } + igraph_vector_int_init(&v2, 0); + igraph_get_edgelist(&g, &v2, 0); + igraph_vector_int_sort(&v1); + igraph_vector_int_sort(&v2); + if (!igraph_vector_int_all_e(&v1, &v2)) { + return 2; + } + igraph_destroy(&g); + + /* higher number of vertices */ + igraph_create(&g, &v1, 10, 0); + if (igraph_vcount(&g) != 10) { + return 1; + } + igraph_get_edgelist(&g, &v2, 0); + igraph_vector_int_sort(&v1); + igraph_vector_int_sort(&v2); + if (!igraph_vector_int_all_e(&v1, &v2)) { + return 3; + } + igraph_destroy(&g); + igraph_vector_int_destroy(&v1); + igraph_vector_int_destroy(&v2); + + return 0; +} diff --git a/examples/simple/igraph_decompose.c b/examples/simple/igraph_decompose.c new file mode 100644 index 0000000..0912680 --- /dev/null +++ b/examples/simple/igraph_decompose.c @@ -0,0 +1,50 @@ + +#include +#include + +int main(void) { + + igraph_t ring, g, *component; + igraph_graph_list_t complist; + igraph_integer_t i; + igraph_integer_t edges[] = { 0, 1, 1, 2, 2, 0, + 3, 4, 4, 5, 5, 6, + 8, 9, 9, 10 + }; + igraph_vector_int_t v; + + igraph_graph_list_init(&complist, 0); + + /* A ring, a single component */ + igraph_ring(&ring, 10, IGRAPH_UNDIRECTED, 0, 1); + + igraph_decompose(&ring, &complist, IGRAPH_WEAK, -1, 0); + component = igraph_graph_list_get_ptr(&complist, 0); + igraph_write_graph_edgelist(component, stdout); + igraph_destroy(&ring); + igraph_graph_list_clear(&complist); + + /* Random graph with a giant component */ + igraph_erdos_renyi_game_gnp(&g, 100, 4.0 / 100, IGRAPH_UNDIRECTED, 0); + igraph_decompose(&g, &complist, IGRAPH_WEAK, -1, 20); + if (igraph_graph_list_size(&complist) != 1) { + return 1; + } + igraph_destroy(&g); + igraph_graph_list_clear(&complist); + + /* A toy graph, three components maximum, with at least 2 vertices each */ + igraph_create(&g, + igraph_vector_int_view(&v, edges, sizeof(edges) / sizeof(edges[0])), + 0, IGRAPH_DIRECTED); + igraph_decompose(&g, &complist, IGRAPH_WEAK, 3, 2); + for (i = 0; i < igraph_graph_list_size(&complist); i++) { + component = igraph_graph_list_get_ptr(&complist, i); + igraph_write_graph_edgelist(component, stdout); + } + igraph_destroy(&g); + + igraph_graph_list_destroy(&complist); + + return 0; +} diff --git a/examples/simple/igraph_decompose.out b/examples/simple/igraph_decompose.out new file mode 100644 index 0000000..370b3f7 --- /dev/null +++ b/examples/simple/igraph_decompose.out @@ -0,0 +1,18 @@ +0 1 +0 9 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +0 1 +1 2 +2 0 +0 1 +1 2 +2 3 +0 1 +1 2 diff --git a/examples/simple/igraph_degree.c b/examples/simple/igraph_degree.c new file mode 100644 index 0000000..6d165b7 --- /dev/null +++ b/examples/simple/igraph_degree.c @@ -0,0 +1,152 @@ +/* -*- 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_bool_t handshaking_lemma(igraph_t *g, igraph_vector_int_t *v) { + /* Consistency check of the handshaking lemma: + * If d is the sum of all vertex degrees, then d = 2|E|. */ + return igraph_vector_int_sum(v) == 2 * igraph_ecount(g); +} + +int main(void) { + + igraph_t g; + igraph_vector_int_t v; + igraph_vector_int_t seq; + igraph_integer_t mdeg; + + /* Create graph */ + igraph_vector_int_init(&v, 8); + igraph_small(&g, 4, IGRAPH_DIRECTED, 0,1, 1,2, 2,3, 2,2, -1); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_OUT, IGRAPH_NO_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_IN, IGRAPH_NO_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + igraph_vector_int_print(&v); + + if (!handshaking_lemma(&g, &v)) { + exit(3); + } + + igraph_destroy(&g); + + igraph_small(&g, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, 2,2, -1); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_OUT, IGRAPH_NO_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_IN, IGRAPH_NO_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS); + igraph_vector_int_print(&v); + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + igraph_vector_int_print(&v); + + if (!handshaking_lemma(&g, &v)) { + exit(4); + } + + /* Degree of the same vertex multiple times */ + + igraph_vector_int_init(&seq, 3); + VECTOR(seq)[0] = 2; + VECTOR(seq)[1] = 0; + VECTOR(seq)[2] = 2; + igraph_degree(&g, &v, igraph_vss_vector(&seq), IGRAPH_ALL, IGRAPH_LOOPS); + igraph_vector_int_print(&v); + + igraph_destroy(&g); + igraph_vector_int_destroy(&seq); + + /* Maximum degree */ + + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, /* mutual */ false, /* circular */ false); + igraph_maxdegree(&g, &mdeg, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + if (mdeg != 2) { + exit(5); + } + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + if (! handshaking_lemma(&g, &v)) { + exit(6); + } + igraph_destroy(&g); + + igraph_full(&g, 10, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_maxdegree(&g, &mdeg, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + if (mdeg != 9) { + exit(7); + } + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + if (! handshaking_lemma(&g, &v)) { + exit(8); + } + igraph_destroy(&g); + + igraph_star(&g, 10, IGRAPH_STAR_OUT, /* center */ 0); + igraph_maxdegree(&g, &mdeg, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + if (mdeg != 9) { + exit(9); + } + igraph_maxdegree(&g, &mdeg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); + if (mdeg != 1) { + exit(10); + } + igraph_maxdegree(&g, &mdeg, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + if (mdeg != 9) { + exit(11); + } + + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + if (! handshaking_lemma(&g, &v)) { + exit(12); + } + igraph_destroy(&g); + + igraph_vector_int_destroy(&v); + + return 0; +} diff --git a/examples/simple/igraph_degree.out b/examples/simple/igraph_degree.out new file mode 100644 index 0000000..2073f83 --- /dev/null +++ b/examples/simple/igraph_degree.out @@ -0,0 +1,13 @@ +1 1 1 0 +1 1 2 0 +0 1 1 1 +0 1 2 1 +1 2 2 1 +1 2 4 1 +1 2 2 1 +1 2 4 1 +1 2 2 1 +1 2 4 1 +1 2 2 1 +1 2 4 1 +4 1 4 diff --git a/examples/simple/igraph_degree_sequence_game.c b/examples/simple/igraph_degree_sequence_game.c new file mode 100644 index 0000000..a8b0afe --- /dev/null +++ b/examples/simple/igraph_degree_sequence_game.c @@ -0,0 +1,94 @@ + +#include + +int main(void) { + igraph_t g; + igraph_vector_int_t outdeg, indeg; + igraph_vector_int_t vec; + igraph_bool_t is_simple; + + /* Set random seed for reproducibility */ + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_int_init_int(&outdeg, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3); + igraph_vector_int_init_int(&indeg, 10, 4, 4, 2, 2, 4, 4, 2, 2, 3, 3); + igraph_vector_int_init(&vec, 0); + + /* checking the configuration model, undirected graphs */ + igraph_degree_sequence_game(&g, &outdeg, 0, IGRAPH_DEGSEQ_CONFIGURATION); + if (igraph_is_directed(&g) || igraph_vcount(&g) != 10) { + return 1; + } + if (igraph_degree(&g, &vec, igraph_vss_all(), IGRAPH_OUT, 1)) { + return 2; + } + igraph_vector_int_print(&vec); + igraph_destroy(&g); + + /* checking the Viger-Latapy method, undirected graphs */ + igraph_degree_sequence_game(&g, &outdeg, 0, IGRAPH_DEGSEQ_VL); + if (igraph_is_directed(&g) || igraph_vcount(&g) != 10) { + return 3; + } + if (igraph_is_simple(&g, &is_simple) || !is_simple) { + return 4; + } + if (igraph_degree(&g, &vec, igraph_vss_all(), IGRAPH_OUT, 0)) { + return 5; + } + igraph_vector_int_print(&vec); + igraph_destroy(&g); + + /* checking the configuration model, directed graphs */ + igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_CONFIGURATION); + if (!igraph_is_directed(&g) || igraph_vcount(&g) != 10) { + return 6; + } + if (igraph_degree(&g, &vec, igraph_vss_all(), IGRAPH_OUT, 1)) { + return 7; + } + igraph_vector_int_print(&vec); + if (igraph_degree(&g, &vec, igraph_vss_all(), IGRAPH_IN, 1)) { + return 8; + } + igraph_vector_int_print(&vec); + igraph_destroy(&g); + + /* checking the fast heuristic method, undirected graphs */ + igraph_degree_sequence_game(&g, &outdeg, 0, IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE); + if (igraph_is_directed(&g) || igraph_vcount(&g) != 10) { + return 9; + } + if (igraph_is_simple(&g, &is_simple) || !is_simple) { + return 10; + } + if (igraph_degree(&g, &vec, igraph_vss_all(), IGRAPH_OUT, 1)) { + return 11; + } + igraph_vector_int_print(&vec); + igraph_destroy(&g); + + /* checking the fast heuristic method, directed graphs */ + igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE); + if (!igraph_is_directed(&g) || igraph_vcount(&g) != 10) { + return 12; + } + if (igraph_is_simple(&g, &is_simple) || !is_simple) { + return 13; + } + if (igraph_degree(&g, &vec, igraph_vss_all(), IGRAPH_OUT, 1)) { + return 14; + } + igraph_vector_int_print(&vec); + if (igraph_degree(&g, &vec, igraph_vss_all(), IGRAPH_IN, 1)) { + return 15; + } + igraph_vector_int_print(&vec); + igraph_destroy(&g); + + igraph_vector_int_destroy(&vec); + igraph_vector_int_destroy(&outdeg); + igraph_vector_int_destroy(&indeg); + + return 0; +} diff --git a/examples/simple/igraph_degree_sequence_game.out b/examples/simple/igraph_degree_sequence_game.out new file mode 100644 index 0000000..d2285c1 --- /dev/null +++ b/examples/simple/igraph_degree_sequence_game.out @@ -0,0 +1,7 @@ +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +4 4 2 2 4 4 2 2 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +4 4 2 2 4 4 2 2 3 3 diff --git a/examples/simple/igraph_delete_edges.c b/examples/simple/igraph_delete_edges.c new file mode 100644 index 0000000..daff2e8 --- /dev/null +++ b/examples/simple/igraph_delete_edges.c @@ -0,0 +1,43 @@ +/* -*- 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 + +int main(void) { + + igraph_t g; + igraph_es_t es; + + igraph_small(&g, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,3, -1); + + igraph_es_pairs_small(&es, IGRAPH_DIRECTED, 3, 2, -1); + igraph_delete_edges(&g, es); + if (igraph_ecount(&g) != 3) { + return 1; + } + + igraph_es_destroy(&es); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_delete_vertices.c b/examples/simple/igraph_delete_vertices.c new file mode 100644 index 0000000..f6d48c0 --- /dev/null +++ b/examples/simple/igraph_delete_vertices.c @@ -0,0 +1,51 @@ +/* -*- 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 + +int main(void) { + igraph_t g; + + /* without edges */ + igraph_small(&g, 15, IGRAPH_UNDIRECTED, -1); + + igraph_delete_vertices(&g, igraph_vss_1(2)); + if (igraph_vcount(&g) != 14) { + return 2; + } + igraph_destroy(&g); + + /* with edges */ + igraph_small(&g, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, 2,2, -1); + igraph_delete_vertices(&g, igraph_vss_1(2)); + if (igraph_vcount(&g) != 3) { + return 3; + } + if (igraph_ecount(&g) != 1) { + return 4; + } + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_deterministic_optimal_imitation.c b/examples/simple/igraph_deterministic_optimal_imitation.c new file mode 100644 index 0000000..b0c1b2e --- /dev/null +++ b/examples/simple/igraph_deterministic_optimal_imitation.c @@ -0,0 +1,172 @@ +/* -*- mode: C -*- */ +/* + Test suite for deterministic optimal imitation. + 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 +#include + +/* test parameters structure */ +typedef struct { + igraph_t *graph; + igraph_integer_t vertex; + igraph_optimal_t optimality; + igraph_vector_t *quantities; + igraph_vector_int_t *strategies; + igraph_neimode_t mode; + igraph_error_t retval; +} strategy_test_t; + +/* Updating the strategy of an isolated vertex. In this case, the strategies + * vector should not change at all. + */ +igraph_error_t isolated_vertex_test(void) { + igraph_t g; + igraph_vector_t quant; + igraph_vector_int_t strat, v; + igraph_integer_t i; + igraph_error_t ret; + + /* graph with one isolated vertex */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + igraph_add_vertices(&g, 1, 0); /* new vertex 3 is isolated */ + /* quantities vector: all vertices have the same fitness */ + igraph_vector_init_real(&quant, 4, 0.25, 0.25, 0.25, 0.25); + /* strategies vector: 0 means aggressive strategy; 1 means passive */ + igraph_vector_int_init_int(&strat, 4, 1, 0, 1, 0); + /* make a copy of the original strategies vector for comparison later on */ + igraph_vector_int_init_copy(&v, &strat); + /* Now update strategy of vertex 3. Since this vertex is isolated, no */ + /* strategy update would take place. The resulting strategies vector */ + /* would be the same as it was originally. */ + ret = igraph_deterministic_optimal_imitation(/*graph*/ &g, + /*vertex*/ 3, + /*optimality*/ IGRAPH_MAXIMUM, + /*quantities*/ &quant, + /*strategies*/ &strat, + /*mode*/ IGRAPH_ALL); + if (ret) { + printf("Isolated vertex test failed.\n"); + return IGRAPH_FAILURE; + } + for (i = 0; i < igraph_vector_int_size(&strat); i++) { + if (VECTOR(strat)[i] != VECTOR(v)[i]) { + printf("Isolated vertex test failed.\n"); + return IGRAPH_FAILURE; + } + } + /* clean up */ + igraph_destroy(&g); + igraph_vector_destroy(&quant); + igraph_vector_int_destroy(&strat); + igraph_vector_int_destroy(&v); + + return IGRAPH_SUCCESS; +} + +/* A game on the Petersen graph. This graph has 10 vertices and 15 edges. + * The Petersen graph is initialized with a default quantities vector and a + * default strategies vector. For each vertex v in the graph, we update the + * strategy of v via deterministic optimal imitation. The resulting updated + * strategies vector is compared with the known result vector. A mismatch would + * raise an error code. If the updated strategies vector matches the known + * result vector, we reset the strategies vector to its default state and + * repeat the game with another vertex. + */ +igraph_error_t petersen_game_test(void) { + igraph_t g; + igraph_vector_t quant; + igraph_vector_int_t known_max_v, known_min_v, strat, stratcopy; + igraph_integer_t i, nvert; + + /* the Petersen graph */ + igraph_small(&g, /*n=*/ 0, IGRAPH_UNDIRECTED, + 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, -1); + nvert = igraph_vcount(&g); + /* Strategies vector, one strategy for each vertex. Thus vec[i] is the */ + /* strategy of vertex i. The strategy space is: {0, 1, 2, 3}. */ + igraph_vector_int_init_int(&strat, nvert, 1, 1, 2, 2, 0, 0, 0, 1, 2, 3); + /* Quantities vector, one quantity per vertex. Thus vec[i] is the */ + /* quantity for vertex i. */ + igraph_vector_init_real(&quant, nvert, + 0.3, 1.1, 0.5, 1.0, 0.9, + 0.8, 0.4, 0.1, 0.7, 0.7); + /* Known strategies that would be adopted. Thus vec[i] means that in */ + /* game i where we revise the strategy of vertex i, the strategy */ + /* vec[i] would be adopted by i. */ + /*maximum deterministic imitation*/ + igraph_vector_int_init_int(&known_max_v, nvert, 1, 1, 1, 2, 2, 0, 1, 0, 2, 0); + /*minimum deterministic imitation*/ + igraph_vector_int_init_int(&known_min_v, nvert, 1, 1, 1, 2, 1, 1, 0, 1, 0, 1); + /* play game and compare resulting updated strategies */ + for (i = 0; i < nvert; i++) { + /* maximum deterministic imitation */ + igraph_vector_int_init_copy(&stratcopy, &strat); + igraph_deterministic_optimal_imitation(/*graph*/ &g, + /*vertex*/ i, + /*optimality*/ IGRAPH_MAXIMUM, + /*quantities*/ &quant, + /*strategies*/ &stratcopy, + /*neighbours*/ IGRAPH_ALL); + if (VECTOR(stratcopy)[i] != VECTOR(known_max_v)[i]) { + printf("Maximum deterministic imitation failed for vertex %" IGRAPH_PRId ".\n", i); + return IGRAPH_FAILURE; + } + igraph_vector_int_destroy(&stratcopy); + /* minimum deterministic imitation */ + igraph_vector_int_init_copy(&stratcopy, &strat); + igraph_deterministic_optimal_imitation(/*graph*/ &g, + /*vertex*/ i, + /*optimality*/ IGRAPH_MINIMUM, + /*quantities*/ &quant, + /*strategies*/ &stratcopy, + /*neighbours*/ IGRAPH_ALL); + if (VECTOR(stratcopy)[i] != VECTOR(known_min_v)[i]) { + printf("Minimum deterministic imitation failed for vertex %" IGRAPH_PRId ".\n", i); + return IGRAPH_FAILURE; + } + igraph_vector_int_destroy(&stratcopy); + } + /* clean up */ + igraph_destroy(&g); + igraph_vector_int_destroy(&known_max_v); + igraph_vector_int_destroy(&known_min_v); + igraph_vector_destroy(&quant); + igraph_vector_int_destroy(&strat); + + return IGRAPH_SUCCESS; +} + +int main(void) { + igraph_error_t ret; + + igraph_rng_seed(igraph_rng_default(), 648); + + ret = isolated_vertex_test(); + if (ret) { + return ret; + } + ret = petersen_game_test(); + if (ret) { + return ret; + } + + return IGRAPH_SUCCESS; +} diff --git a/examples/simple/igraph_diameter.c b/examples/simple/igraph_diameter.c new file mode 100644 index 0000000..c30ab13 --- /dev/null +++ b/examples/simple/igraph_diameter.c @@ -0,0 +1,45 @@ +/* -*- mode: C -*- */ + +#include + +void print_vector_int(igraph_vector_int_t *v) { + igraph_integer_t i, n = igraph_vector_int_size(v); + for (i = 0; i < n; i++) { + printf(" %" IGRAPH_PRId, VECTOR(*v)[i]); + } + printf("\n"); +} + +int main(void) { + + igraph_t g; + igraph_real_t result; + igraph_integer_t from, to; + igraph_vector_int_t path, path_edge; + + igraph_barabasi_game(&g, 30, /*power=*/ 1, 30, 0, 0, /*A=*/ 1, + IGRAPH_DIRECTED, IGRAPH_BARABASI_BAG, + /*start_from=*/ 0); + igraph_diameter(&g, &result, 0, 0, 0, 0, IGRAPH_UNDIRECTED, 1); + + /* printf("Diameter: %" IGRAPH_PRId "\n", (igraph_integer_t) result); */ + + igraph_destroy(&g); + + igraph_ring(&g, 10, IGRAPH_DIRECTED, 0, 0); + igraph_vector_int_init(&path, 0); + igraph_vector_int_init(&path_edge, 0); + igraph_diameter(&g, &result, &from, &to, &path, &path_edge, IGRAPH_DIRECTED, 1); + printf( + "diameter: %" IGRAPH_PRId ", from %" IGRAPH_PRId " to %" IGRAPH_PRId "\n", + (igraph_integer_t) result, from, to + ); + print_vector_int(&path); + print_vector_int(&path_edge); + + igraph_vector_int_destroy(&path); + igraph_vector_int_destroy(&path_edge); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_diameter.out b/examples/simple/igraph_diameter.out new file mode 100644 index 0000000..d5a8cfe --- /dev/null +++ b/examples/simple/igraph_diameter.out @@ -0,0 +1,3 @@ +diameter: 9, from 0 to 9 + 0 1 2 3 4 5 6 7 8 9 + 0 1 2 3 4 5 6 7 8 diff --git a/examples/simple/igraph_difference.c b/examples/simple/igraph_difference.c new file mode 100644 index 0000000..22a4f5d --- /dev/null +++ b/examples/simple/igraph_difference.c @@ -0,0 +1,138 @@ +/* -*- 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 + +int main(void) { + + igraph_t orig, sub, diff; + igraph_vector_int_t v; + + /* Subtract from itself */ + printf("subtract itself\n"); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, -1); + igraph_create(&orig, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + igraph_difference(&diff, &orig, &orig); + igraph_write_graph_edgelist(&diff, stdout); + if (igraph_ecount(&diff) != 0 || + igraph_vcount(&diff) != igraph_vcount(&orig)) { + return 1; + } + + igraph_destroy(&orig); + igraph_destroy(&diff); + + /* Same for undirected graph */ + printf("subtract itself, undirected\n"); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, -1); + igraph_create(&orig, &v, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&v); + + igraph_vector_int_init_int_end(&v, -1, 1, 0, 1, 2, 2, 1, 4, 5, -1); + igraph_create(&sub, &v, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&v); + + igraph_difference(&diff, &orig, &sub); + igraph_write_graph_edgelist(&diff, stdout); + if (igraph_ecount(&diff) != 0 || + igraph_vcount(&diff) != igraph_vcount(&orig)) { + return 2; + } + + igraph_destroy(&orig); + igraph_destroy(&sub); + igraph_destroy(&diff); + + /* Subtract the empty graph */ + printf("subtract empty\n"); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, -1); + igraph_create(&orig, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + igraph_empty(&sub, 3, IGRAPH_DIRECTED); + igraph_difference(&diff, &orig, &sub); + igraph_write_graph_edgelist(&diff, stdout); + if (igraph_ecount(&diff) != igraph_ecount(&orig) || + igraph_vcount(&diff) != igraph_vcount(&orig)) { + return 3; + } + + igraph_destroy(&orig); + igraph_destroy(&sub); + igraph_destroy(&diff); + + /* A `real' example */ + printf("real example\n"); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, 8, 9, -1); + igraph_create(&orig, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + igraph_vector_int_init_int_end(&v, -1, 0, 1, 5, 4, 2, 1, 6, 7, -1); + igraph_create(&sub, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + igraph_difference(&diff, &orig, &sub); + igraph_write_graph_edgelist(&diff, stdout); + + igraph_destroy(&diff); + igraph_destroy(&orig); + igraph_destroy(&sub); + + /* undirected version */ + printf("real example, undirected\n"); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, 8, 9, 8, 10, 8, 13, 8, 11, 8, 12, -1); + igraph_create(&orig, &v, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&v); + + igraph_vector_int_init_int_end(&v, -1, 0, 1, 5, 4, 2, 1, 6, 7, 8, 10, 8, 13, -1); + igraph_create(&sub, &v, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&v); + + igraph_difference(&diff, &orig, &sub); + igraph_write_graph_edgelist(&diff, stdout); + + igraph_destroy(&diff); + igraph_destroy(&orig); + igraph_destroy(&sub); + + /* undirected version with loop edge, tests Github issue #597 */ + printf("Github issue #597, undirected\n"); + igraph_vector_int_init_int_end(&v, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 0, -1); + igraph_create(&orig, &v, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&v); + + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, -1); + igraph_create(&sub, &v, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&v); + + igraph_difference(&diff, &orig, &sub); + igraph_write_graph_edgelist(&diff, stdout); + + igraph_destroy(&diff); + igraph_destroy(&orig); + igraph_destroy(&sub); + + return 0; +} diff --git a/examples/simple/igraph_difference.out b/examples/simple/igraph_difference.out new file mode 100644 index 0000000..6bc55e8 --- /dev/null +++ b/examples/simple/igraph_difference.out @@ -0,0 +1,24 @@ +subtract itself +subtract itself, undirected +subtract empty +0 1 +1 2 +2 1 +4 5 +real example +1 2 +4 5 +8 9 +real example, undirected +1 2 +8 9 +8 11 +8 12 +Github issue #597, undirected +0 0 +0 9 +4 5 +5 6 +6 7 +7 8 +8 9 diff --git a/examples/simple/igraph_disjoint_union.c b/examples/simple/igraph_disjoint_union.c new file mode 100644 index 0000000..ab737e3 --- /dev/null +++ b/examples/simple/igraph_disjoint_union.c @@ -0,0 +1,73 @@ +/* -*- 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, see . +*/ + +#include +#include + +int main(void) { + igraph_t left, right, uni; + igraph_vector_ptr_t glist; + igraph_integer_t i, n; + + igraph_small(&left, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,3, -1); + igraph_small(&right, 5, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,4, -1); + + igraph_disjoint_union(&uni, &left, &right); + igraph_write_graph_edgelist(&uni, stdout); + printf("\n"); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&uni); + + /* Empty graph list; the result is the directed null graph. */ + igraph_vector_ptr_init(&glist, 0); + igraph_disjoint_union_many(&uni, &glist); + if (!igraph_is_directed(&uni) || igraph_vcount(&uni) != 0) { + return 1; + } + igraph_vector_ptr_destroy(&glist); + igraph_destroy(&uni); + + /* Non-empty graph list. */ + igraph_vector_ptr_init(&glist, 10); + n = igraph_vector_ptr_size(&glist); + for (i = 0; i < n; i++) { + VECTOR(glist)[i] = calloc(1, sizeof(igraph_t)); + igraph_small(VECTOR(glist)[i], 2, IGRAPH_DIRECTED, 0,1, 1,0, -1); + } + if (!igraph_is_directed(&uni)) { + return 2; + } + + igraph_disjoint_union_many(&uni, &glist); + igraph_write_graph_edgelist(&uni, stdout); + printf("\n"); + + /* Destroy and free the graph list. */ + n = igraph_vector_ptr_size(&glist); + for (i = 0; i < n; i++) { + igraph_destroy(VECTOR(glist)[i]); + free(VECTOR(glist)[i]); + } + igraph_vector_ptr_destroy(&glist); + igraph_destroy(&uni); + + return 0; +} diff --git a/examples/simple/igraph_disjoint_union.out b/examples/simple/igraph_disjoint_union.out new file mode 100644 index 0000000..2c5fb4b --- /dev/null +++ b/examples/simple/igraph_disjoint_union.out @@ -0,0 +1,30 @@ +0 1 +1 2 +2 2 +2 3 +4 5 +5 6 +6 6 +6 8 + +0 1 +1 0 +2 3 +3 2 +4 5 +5 4 +6 7 +7 6 +8 9 +9 8 +10 11 +11 10 +12 13 +13 12 +14 15 +15 14 +16 17 +17 16 +18 19 +19 18 + diff --git a/examples/simple/igraph_eccentricity.c b/examples/simple/igraph_eccentricity.c new file mode 100644 index 0000000..cba7212 --- /dev/null +++ b/examples/simple/igraph_eccentricity.c @@ -0,0 +1,29 @@ + +#include + +int main(void) { + + igraph_t g; + igraph_vector_t ecc; + + igraph_vector_init(&ecc, 0); + + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + igraph_vector_print(&ecc); + igraph_destroy(&g); + + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_ALL); + igraph_vector_print(&ecc); + igraph_destroy(&g); + + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + igraph_vector_print(&ecc); + igraph_destroy(&g); + + igraph_vector_destroy(&ecc); + + return 0; +} diff --git a/examples/simple/igraph_eccentricity.out b/examples/simple/igraph_eccentricity.out new file mode 100644 index 0000000..a92f5e0 --- /dev/null +++ b/examples/simple/igraph_eccentricity.out @@ -0,0 +1,3 @@ +1 2 2 2 2 2 2 2 2 2 +1 2 2 2 2 2 2 2 2 2 +1 0 0 0 0 0 0 0 0 0 diff --git a/examples/simple/igraph_erdos_renyi_game_gnm.c b/examples/simple/igraph_erdos_renyi_game_gnm.c new file mode 100644 index 0000000..fc59f52 --- /dev/null +++ b/examples/simple/igraph_erdos_renyi_game_gnm.c @@ -0,0 +1,33 @@ + +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t component_sizes; + + igraph_rng_seed(igraph_rng_default(), 42); /* make program deterministic */ + + /* Sample a graph from the Erdős-Rényi G(n,m) model */ + + igraph_erdos_renyi_game_gnm( + &graph, /* n= */ 100, /* m= */ 100, + IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS + ); + + /* Compute the fraction of vertices contained within the largest connected component */ + + igraph_vector_int_init(&component_sizes, 0); + igraph_connected_components(&graph, NULL, &component_sizes, NULL, IGRAPH_STRONG); + + printf( + "Fraction of vertices in giant component: %g\n", + ((double) igraph_vector_int_max(&component_sizes)) / igraph_vcount(&graph) + ); + + /* Clean up data structures when no longer needed */ + + igraph_vector_int_destroy(&component_sizes); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_erdos_renyi_game_gnp.c b/examples/simple/igraph_erdos_renyi_game_gnp.c new file mode 100644 index 0000000..a5cc4fe --- /dev/null +++ b/examples/simple/igraph_erdos_renyi_game_gnp.c @@ -0,0 +1,33 @@ + +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t component_sizes; + + igraph_rng_seed(igraph_rng_default(), 42); /* make program deterministic */ + + /* Sample a graph from the Erdős-Rényi G(n,p) model */ + + igraph_erdos_renyi_game_gnp( + &graph, /* n= */ 100, /* p= */ 0.01, + IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS + ); + + /* Compute the fraction of vertices contained within the largest connected component */ + + igraph_vector_int_init(&component_sizes, 0); + igraph_connected_components(&graph, NULL, &component_sizes, NULL, IGRAPH_STRONG); + + printf( + "Fraction of vertices in giant component: %g\n", + ((double) igraph_vector_int_max(&component_sizes)) / igraph_vcount(&graph) + ); + + /* Clean up data structures when no longer needed */ + + igraph_vector_int_destroy(&component_sizes); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_es_pairs.c b/examples/simple/igraph_es_pairs.c new file mode 100644 index 0000000..1e9d6e1 --- /dev/null +++ b/examples/simple/igraph_es_pairs.c @@ -0,0 +1,80 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_integer_t i; + igraph_integer_t size; + + /* DIRECTED */ + + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + + for (i = 0; i < 100; i++) { + igraph_es_t es; + igraph_eit_t it; + igraph_es_pairs_small(&es, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 5, 0, 2, 0, 3, 0, 4, 0, 7, 0, 9, -1); + igraph_eit_create(&g, es, &it); + igraph_es_size(&g, &es, &size); + IGRAPH_EIT_RESET(it); + while (!IGRAPH_EIT_END(it)) { + (void) IGRAPH_EIT_GET(it); + IGRAPH_EIT_NEXT(it); + size--; + } + if (size != 0) { + return 1; + } + igraph_eit_destroy(&it); + igraph_es_destroy(&es); + } + + igraph_destroy(&g); + + /* UNDIRECTED */ + + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + + for (i = 0; i < 100; i++) { + igraph_es_t es; + igraph_eit_t it; + igraph_es_pairs_small(&es, IGRAPH_DIRECTED, + 0, 1, 2, 0, 5, 0, 0, 2, 3, 0, 0, 4, 7, 0, 0, 9, -1); + igraph_eit_create(&g, es, &it); + IGRAPH_EIT_RESET(it); + while (!IGRAPH_EIT_END(it)) { + (void) IGRAPH_EIT_GET(it); + IGRAPH_EIT_NEXT(it); + } + igraph_eit_destroy(&it); + igraph_es_destroy(&es); + } + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_feedback_arc_set.c b/examples/simple/igraph_feedback_arc_set.c new file mode 100644 index 0000000..2f038b1 --- /dev/null +++ b/examples/simple/igraph_feedback_arc_set.c @@ -0,0 +1,94 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + 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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_vector_int_t result; + igraph_bool_t dag; + + igraph_vector_int_init(&result, 0); + + /***********************************************************************/ + /* Approximation with Eades' method */ + /***********************************************************************/ + + /* Simple unweighted graph */ + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 2, 3, 2, 4, 0, 4, 4, 3, 5, 0, 6, 5, -1); + igraph_feedback_arc_set(&g, &result, 0, IGRAPH_FAS_APPROX_EADES); + igraph_vector_int_print(&result); + igraph_delete_edges(&g, igraph_ess_vector(&result)); + igraph_is_dag(&g, &dag); + if (!dag) { + return 1; + } + igraph_destroy(&g); + + /* Simple weighted graph */ + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 2, 3, 2, 4, 0, 4, 4, 3, 5, 0, 6, 5, -1); + igraph_vector_init_int_end(&weights, -1, 1, 1, 3, 1, 1, 1, 1, 1, 1, -1); + igraph_feedback_arc_set(&g, &result, &weights, IGRAPH_FAS_APPROX_EADES); + igraph_vector_int_print(&result); + igraph_delete_edges(&g, igraph_ess_vector(&result)); + igraph_is_dag(&g, &dag); + if (!dag) { + return 2; + } + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* Simple unweighted graph with loops */ + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 2, 3, 2, 4, 0, 4, 4, 3, 5, 0, 6, 5, 1, 1, 4, 4, -1); + igraph_feedback_arc_set(&g, &result, 0, IGRAPH_FAS_APPROX_EADES); + igraph_vector_int_print(&result); + igraph_delete_edges(&g, igraph_ess_vector(&result)); + igraph_is_dag(&g, &dag); + if (!dag) { + return 3; + } + igraph_destroy(&g); + + /* Null graph */ + igraph_empty(&g, 0, IGRAPH_DIRECTED); + igraph_feedback_arc_set(&g, &result, NULL, IGRAPH_FAS_APPROX_EADES); + if (igraph_vector_int_size(&result) != 0) { + return 4; + } + igraph_destroy(&g); + + /* Singleton graph */ + igraph_empty(&g, 1, IGRAPH_DIRECTED); + igraph_feedback_arc_set(&g, &result, NULL, IGRAPH_FAS_APPROX_EADES); + if (igraph_vector_int_size(&result) != 0) { + return 5; + } + igraph_destroy(&g); + + igraph_vector_int_destroy(&result); + + return 0; +} diff --git a/examples/simple/igraph_feedback_arc_set.out b/examples/simple/igraph_feedback_arc_set.out new file mode 100644 index 0000000..aeb5a03 --- /dev/null +++ b/examples/simple/igraph_feedback_arc_set.out @@ -0,0 +1,3 @@ +2 +1 +2 9 10 diff --git a/examples/simple/igraph_feedback_arc_set_ip.c b/examples/simple/igraph_feedback_arc_set_ip.c new file mode 100644 index 0000000..2ad84c4 --- /dev/null +++ b/examples/simple/igraph_feedback_arc_set_ip.c @@ -0,0 +1,125 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + 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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_vector_int_t result; + igraph_bool_t dag; + igraph_error_t retval; + + igraph_vector_int_init(&result, 0); + + igraph_set_error_handler(&igraph_error_handler_printignore); + + /***********************************************************************/ + /* Exact solution with integer programming */ + /***********************************************************************/ + + /* Simple unweighted graph */ + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 2, 3, 2, 4, 0, 4, 4, 3, 5, 0, 6, 5, -1); + retval = igraph_feedback_arc_set(&g, &result, 0, IGRAPH_FAS_EXACT_IP); + if (retval == IGRAPH_UNIMPLEMENTED) { + return 77; + } + igraph_vector_int_print(&result); + igraph_delete_edges(&g, igraph_ess_vector(&result)); + igraph_is_dag(&g, &dag); + if (!dag) { + return 1; + } + igraph_destroy(&g); + + /* Simple weighted graph */ + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 2, 3, 2, 4, 0, 4, 4, 3, 5, 0, 6, 5, -1); + igraph_vector_init_int_end(&weights, -1, 1, 1, 3, 1, 1, 1, 1, 1, 1, -1); + igraph_feedback_arc_set(&g, &result, &weights, IGRAPH_FAS_EXACT_IP); + igraph_vector_int_print(&result); + igraph_delete_edges(&g, igraph_ess_vector(&result)); + igraph_is_dag(&g, &dag); + if (!dag) { + return 2; + } + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* Simple unweighted graph with loops */ + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 2, 3, 2, 4, 0, 4, 4, 3, 5, 0, 6, 5, 1, 1, 4, 4, -1); + igraph_feedback_arc_set(&g, &result, 0, IGRAPH_FAS_EXACT_IP); + igraph_vector_int_print(&result); + igraph_delete_edges(&g, igraph_ess_vector(&result)); + igraph_is_dag(&g, &dag); + if (!dag) { + return 3; + } + igraph_destroy(&g); + + /* Disjoint union of two almost identical graphs */ + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 0, 2, 3, 2, 4, 0, 4, 4, 3, 5, 0, 6, 5, 1, 1, 4, 4, + 7, 8, 8, 9, 9, 7, 9, 10, 9, 11, 7, 11, 11, 10, 12, 7, 13, 12, + -1); + igraph_feedback_arc_set(&g, &result, 0, IGRAPH_FAS_EXACT_IP); + igraph_vector_int_print(&result); + igraph_delete_edges(&g, igraph_ess_vector(&result)); + igraph_is_dag(&g, &dag); + if (!dag) { + return 4; + } + igraph_destroy(&g); + + /* Graph with lots of isolated vertices */ + igraph_small(&g, 10000, IGRAPH_DIRECTED, 0, 1, -1); + igraph_feedback_arc_set(&g, &result, 0, IGRAPH_FAS_EXACT_IP); + igraph_vector_int_print(&result); + igraph_delete_edges(&g, igraph_ess_vector(&result)); + igraph_is_dag(&g, &dag); + if (!dag) { + return 5; + } + igraph_destroy(&g); + + /* Null graph */ + igraph_empty(&g, 0, IGRAPH_DIRECTED); + igraph_feedback_arc_set(&g, &result, NULL, IGRAPH_FAS_EXACT_IP); + if (igraph_vector_int_size(&result) != 0) { + return 6; + } + igraph_destroy(&g); + + /* Singleton graph */ + igraph_empty(&g, 1, IGRAPH_DIRECTED); + igraph_feedback_arc_set(&g, &result, NULL, IGRAPH_FAS_EXACT_IP); + if (igraph_vector_int_size(&result) != 0) { + return 7; + } + igraph_destroy(&g); + + igraph_vector_int_destroy(&result); + + return 0; +} diff --git a/examples/simple/igraph_feedback_arc_set_ip.out b/examples/simple/igraph_feedback_arc_set_ip.out new file mode 100644 index 0000000..eda4c6e --- /dev/null +++ b/examples/simple/igraph_feedback_arc_set_ip.out @@ -0,0 +1,5 @@ +1 +1 +1 9 10 +1 9 10 12 + diff --git a/examples/simple/igraph_fisher_yates_shuffle.c b/examples/simple/igraph_fisher_yates_shuffle.c new file mode 100644 index 0000000..e552d45 --- /dev/null +++ b/examples/simple/igraph_fisher_yates_shuffle.c @@ -0,0 +1,104 @@ +/* -*- mode: C -*- */ +/* + Test suite for the Fisher-Yates shuffle. + 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 + +#define R_INTEGER(a,b) (igraph_rng_get_integer(igraph_rng_default(), (a), (b))) +#define R_UNIF(a,b) (igraph_rng_get_unif(igraph_rng_default(), (a), (b))) + +int main(void) { + igraph_real_t d; + igraph_vector_t u, v; + igraph_integer_t i, k, n; + + /******************************** + * Example usage + ********************************/ + + /* Sequences with one element. Such sequences are trivially permuted. + * The result of any Fisher-Yates shuffle on a sequence with one element + * must be the original sequence itself. + */ + n = 1; + igraph_vector_init(&v, n); + igraph_rng_seed(igraph_rng_default(), 42); /* make tests deterministic */ + k = R_INTEGER(-1000, 1000); + VECTOR(v)[0] = k; + igraph_vector_shuffle(&v); + if (VECTOR(v)[0] != k) { + return 1; + } + d = R_UNIF(-1000.0, 1000.0); + + VECTOR(v)[0] = d; + igraph_vector_shuffle(&v); + if (VECTOR(v)[0] != d) { + return 2; + } + igraph_vector_destroy(&v); + + /* Sequences with multiple elements. A Fisher-Yates shuffle of a sequence S + * is a random permutation \pi(S) of S. Thus \pi(S) must have the same + * length and elements as the original sequence S. A major difference between + * S and its random permutation \pi(S) is that the order in which elements + * appear in \pi(S) is probably different from how elements are ordered in S. + * If S has length n = 1, then both \pi(S) and S are equivalent sequences in + * that \pi(S) is merely S and no permutation has taken place. If S has + * length n > 1, then there are n! possible permutations of S. Assume that + * each such permutation is equally likely to appear as a result of the + * Fisher-Yates shuffle. As n increases, the probability that S is different + * from \pi(S) also increases. We have a probability of 1 / n! that S and + * \pi(S) are equivalent sequences. + */ + n = 100; + igraph_vector_init(&u, n); + igraph_vector_init(&v, n); + + for (i = 0; i < n; i++) { + k = R_INTEGER(-1000, 1000); + VECTOR(u)[i] = k; + VECTOR(v)[i] = k; + } + + igraph_vector_shuffle(&v); + /* must have same length */ + if (igraph_vector_size(&v) != n) { + return 3; + } + if (igraph_vector_size(&u) != igraph_vector_size(&v)) { + return 4; + } + /* must have same elements */ + igraph_vector_sort(&u); + igraph_vector_sort(&v); + if (!igraph_vector_all_e(&u, &v)) { + return 5; + } + igraph_vector_destroy(&u); + igraph_vector_destroy(&v); + + /* empty sequence */ + igraph_vector_init(&v, 0); + igraph_vector_shuffle(&v); + igraph_vector_destroy(&v); + + return 0; +} diff --git a/examples/simple/igraph_full.c b/examples/simple/igraph_full.c new file mode 100644 index 0000000..cf4c9ac --- /dev/null +++ b/examples/simple/igraph_full.c @@ -0,0 +1,59 @@ +/* -*- 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, see . +*/ + +#include +#include + +int main(void) { + igraph_t graph; + igraph_integer_t n_vertices = 10; + + /* Create an undirected complete graph. */ + /* Use IGRAPH_UNDIRECTED and IGRAPH_NO_LOOPS instead of 1/TRUE and 0/FALSE for better readability. */ + igraph_full(&graph, n_vertices, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + printf("The undirected complete graph on %" IGRAPH_PRId " vertices has %" IGRAPH_PRId " edges.\n", + igraph_vcount(&graph), igraph_ecount(&graph)); + + /* Remember to destroy the object at the end. */ + igraph_destroy(&graph); + + /* Create a directed complete graph. */ + igraph_full(&graph, n_vertices, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + printf("The directed complete graph on %" IGRAPH_PRId " vertices has %" IGRAPH_PRId " edges.\n", + igraph_vcount(&graph), igraph_ecount(&graph)); + + igraph_destroy(&graph); + + /* Create an undirected complete graph with self-loops. */ + igraph_full(&graph, n_vertices, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + printf("The undirected complete graph on %" IGRAPH_PRId " vertices with self-loops has %" IGRAPH_PRId " edges.\n", + igraph_vcount(&graph), igraph_ecount(&graph)); + + igraph_destroy(&graph); + + /* Create a directed graph with self-loops. */ + igraph_full(&graph, n_vertices, IGRAPH_DIRECTED, IGRAPH_LOOPS); + printf("The directed complete graph on %" IGRAPH_PRId " vertices with self-loops has %" IGRAPH_PRId " edges.\n", + igraph_vcount(&graph), igraph_ecount(&graph)); + + igraph_destroy(&graph); + + return 0; + +} diff --git a/examples/simple/igraph_full.out b/examples/simple/igraph_full.out new file mode 100644 index 0000000..aa00c75 --- /dev/null +++ b/examples/simple/igraph_full.out @@ -0,0 +1,4 @@ +The undirected complete graph on 10 vertices has 45 edges. +The directed complete graph on 10 vertices has 90 edges. +The undirected complete graph on 10 vertices with self-loops has 55 edges. +The directed complete graph on 10 vertices with self-loops has 100 edges. diff --git a/examples/simple/igraph_get_all_shortest_paths_dijkstra.c b/examples/simple/igraph_get_all_shortest_paths_dijkstra.c new file mode 100644 index 0000000..44bd6b7 --- /dev/null +++ b/examples/simple/igraph_get_all_shortest_paths_dijkstra.c @@ -0,0 +1,84 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +void print_and_destroy_items(igraph_vector_int_list_t* vec) { + igraph_integer_t i; + + /* Sort the paths in a deterministic manner to avoid problems with + * different qsort() implementations on different platforms */ + igraph_vector_int_list_sort(vec, igraph_vector_int_colex_cmp); + for (i = 0; i < igraph_vector_int_list_size(vec); i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(vec, i)); + } + + igraph_vector_int_list_destroy(vec); +} + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t vertices, edges; + + igraph_real_t weights[] = { 0, 2, 1, 0, 5, 2, 1, 1, 0, 2, 2, 8, 1, 1, 3, 1, 1, 4, 2, 1 }; + + igraph_vector_t weights_vec; + igraph_vector_int_t nrgeo; + igraph_vs_t vs; + + igraph_vector_int_list_init(&vertices, 0); + igraph_vector_int_list_init(&edges, 0); + igraph_vs_vector_small(&vs, 1, 3, 4, 5, 2, 1, -1); + igraph_vector_int_init(&nrgeo, 0); + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 4, 1, 5, + 2, 3, 2, 6, 3, 2, 3, 6, + 4, 5, 4, 7, 5, 6, 5, 8, 5, 9, + 7, 5, 7, 8, 8, 9, + 5, 2, + 2, 1, + -1); + + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + igraph_get_all_shortest_paths_dijkstra( + &g, + /*vertices=*/ &vertices, /*edges=*/ &edges, /*nrgeo=*/ &nrgeo, + /*from=*/ 0, /*to=*/ vs, + /*weights=*/ &weights_vec, /*mode=*/ IGRAPH_OUT); + + printf("Vertices:\n"); + print_and_destroy_items(&vertices); + printf("\nEdges:\n"); + print_and_destroy_items(&edges); + printf("\nNumber of geodesics:\n"); + igraph_vector_int_print(&nrgeo); + + igraph_vector_int_destroy(&nrgeo); + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_get_all_shortest_paths_dijkstra.out b/examples/simple/igraph_get_all_shortest_paths_dijkstra.out new file mode 100644 index 0000000..0237c78 --- /dev/null +++ b/examples/simple/igraph_get_all_shortest_paths_dijkstra.out @@ -0,0 +1,18 @@ +Vertices: +0 1 +0 1 2 +0 3 +0 1 2 3 +0 1 4 +0 1 5 + +Edges: +0 +2 +0 3 +0 4 +0 5 +0 3 6 + +Number of geodesics: +1 1 1 2 1 1 1 0 1 1 diff --git a/examples/simple/igraph_get_eid.c b/examples/simple/igraph_get_eid.c new file mode 100644 index 0000000..f8d032c --- /dev/null +++ b/examples/simple/igraph_get_eid.c @@ -0,0 +1,66 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + igraph_t g; + igraph_integer_t eid; + igraph_vector_int_t hist; + igraph_integer_t i; + + /* DIRECTED */ + + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + + igraph_vector_int_init(&hist, 9); + + for (i = 1; i < 10; i++) { + igraph_get_eid(&g, &eid, 0, i, IGRAPH_DIRECTED, /*error=*/ true); + VECTOR(hist)[ eid ] = 1; + } + + igraph_vector_int_print(&hist); + + igraph_vector_int_destroy(&hist); + igraph_destroy(&g); + + /* UNDIRECTED */ + + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + + igraph_vector_int_init(&hist, 9); + + for (i = 1; i < 10; i++) { + igraph_get_eid(&g, &eid, 0, i, IGRAPH_UNDIRECTED, /*error=*/ true); + VECTOR(hist)[ eid ] += 1; + igraph_get_eid(&g, &eid, i, 0, IGRAPH_DIRECTED, /*error=*/ true); + VECTOR(hist)[ eid ] += 1; + } + igraph_vector_int_print(&hist); + + igraph_vector_int_destroy(&hist); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_get_eid.out b/examples/simple/igraph_get_eid.out new file mode 100644 index 0000000..8d4cb57 --- /dev/null +++ b/examples/simple/igraph_get_eid.out @@ -0,0 +1,2 @@ +1 1 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 2 diff --git a/examples/simple/igraph_get_eids.c b/examples/simple/igraph_get_eids.c new file mode 100644 index 0000000..8447600 --- /dev/null +++ b/examples/simple/igraph_get_eids.c @@ -0,0 +1,106 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-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 + 02110-1301 USA + +*/ + +#include +#include + +void print_vector_int(igraph_vector_int_t *v, FILE *f) { + igraph_integer_t i; + for (i = 0; i < igraph_vector_int_size(v); i++) { + fprintf(f, " %" IGRAPH_PRId, VECTOR(*v)[i]); + } + fprintf(f, "\n"); +} + +int main(void) { + igraph_t g; + igraph_integer_t nodes = 100; + igraph_integer_t edges = 1000; + igraph_real_t p = 3.0 / nodes; + igraph_integer_t runs = 10; + igraph_integer_t r, e, ecount; + igraph_vector_int_t eids, pairs, path; + + igraph_rng_seed(igraph_rng_default(), 42); /* make tests deterministic */ + + igraph_vector_int_init(&pairs, edges * 2); + igraph_vector_int_init(&path, 0); + igraph_vector_int_init(&eids, 0); + + for (r = 0; r < runs; r++) { + igraph_vector_int_resize(&pairs, edges * 2); + igraph_vector_int_clear(&path); + igraph_vector_int_clear(&eids); + + igraph_erdos_renyi_game_gnp(&g, nodes, p, /*directed=*/ 0, /*loops=*/ 0); + ecount = igraph_ecount(&g); + for (e = 0; e < edges; e++) { + igraph_integer_t edge = RNG_INTEGER(0, ecount - 1); + VECTOR(pairs)[2 * e] = IGRAPH_FROM(&g, edge); + VECTOR(pairs)[2 * e + 1] = IGRAPH_TO(&g, edge); + } + igraph_get_eids(&g, &eids, &pairs, /* directed= */ 0, /*error=*/ 1); + for (e = 0; e < edges; e++) { + igraph_integer_t edge = VECTOR(eids)[e]; + igraph_integer_t from1 = VECTOR(pairs)[2 * e]; + igraph_integer_t to1 = VECTOR(pairs)[2 * e + 1]; + igraph_integer_t from2 = IGRAPH_FROM(&g, edge); + igraph_integer_t to2 = IGRAPH_TO(&g, edge); + igraph_integer_t min1 = from1 < to1 ? from1 : to1; + igraph_integer_t max1 = from1 < to1 ? to1 : from1; + igraph_integer_t min2 = from2 < to2 ? from2 : to2; + igraph_integer_t max2 = from2 < to2 ? to2 : from2; + if (min1 != min2 || max1 != max2) { + return 11; + } + } + + igraph_diameter(&g, /*res=*/ 0, /*from=*/ 0, /*to=*/ 0, &path, NULL, + IGRAPH_UNDIRECTED, /*unconn=*/ 1); + igraph_vector_int_update(&pairs, &path); + igraph_expand_path_to_pairs(&pairs); + igraph_get_eids(&g, &eids, &pairs, 0, /*error=*/ 1); + for (e = 0; e < igraph_vector_int_size(&path) - 1; e++) { + igraph_integer_t edge = VECTOR(eids)[e]; + igraph_integer_t from1 = VECTOR(path)[e]; + igraph_integer_t to1 = VECTOR(path)[e + 1]; + igraph_integer_t from2 = IGRAPH_FROM(&g, edge); + igraph_integer_t to2 = IGRAPH_TO(&g, edge); + igraph_integer_t min1 = from1 < to1 ? from1 : to1; + igraph_integer_t max1 = from1 < to1 ? to1 : from1; + igraph_integer_t min2 = from2 < to2 ? from2 : to2; + igraph_integer_t max2 = from2 < to2 ? to2 : from2; + if (min1 != min2 || max1 != max2) { + return 12; + } + } + + igraph_destroy(&g); + } + + igraph_vector_int_destroy(&path); + igraph_vector_int_destroy(&pairs); + igraph_vector_int_destroy(&eids); + + return 0; +} diff --git a/examples/simple/igraph_get_laplacian.c b/examples/simple/igraph_get_laplacian.c new file mode 100644 index 0000000..fce52b7 --- /dev/null +++ b/examples/simple/igraph_get_laplacian.c @@ -0,0 +1,37 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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, see . +*/ + +#include + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_matrix_t m; + + igraph_matrix_init(&m, 1, 1); + igraph_vector_init_int(&weights, 5, 1, 2, 3, 4, 5); + + igraph_ring(&g, 5, IGRAPH_DIRECTED, 0, 1); + igraph_get_laplacian(&g, &m, IGRAPH_OUT, IGRAPH_LAPLACIAN_SYMMETRIC, &weights); + igraph_matrix_print(&m); + + igraph_vector_destroy(&weights); + igraph_matrix_destroy(&m); + igraph_destroy(&g); +} diff --git a/examples/simple/igraph_get_laplacian.out b/examples/simple/igraph_get_laplacian.out new file mode 100644 index 0000000..6288c7d --- /dev/null +++ b/examples/simple/igraph_get_laplacian.out @@ -0,0 +1,5 @@ + 1 -0.707107 0 0 0 + 0 1 -0.816497 0 0 + 0 0 1 -0.866025 0 + 0 0 0 1 -0.894427 +-2.23607 0 0 0 1 diff --git a/examples/simple/igraph_get_laplacian_sparse.c b/examples/simple/igraph_get_laplacian_sparse.c new file mode 100644 index 0000000..29a1032 --- /dev/null +++ b/examples/simple/igraph_get_laplacian_sparse.c @@ -0,0 +1,131 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int test_laplacian(const igraph_vector_t *w, igraph_bool_t dir, igraph_laplacian_normalization_t normalization) { + igraph_t g; + igraph_matrix_t m; + igraph_sparsemat_t sm; + igraph_vector_int_t vec; + igraph_vector_t *weights = 0; + igraph_neimode_t mode = IGRAPH_OUT; + + igraph_sparsemat_init(&sm, 0, 0, 0); + + if (w) { + weights = (igraph_vector_t*) calloc(1, sizeof(igraph_vector_t)); + igraph_vector_init_copy(weights, w); + } + + /* Base graph, no loop or multiple edges */ + igraph_ring(&g, 5, dir, 0, 1); + igraph_get_laplacian_sparse(&g, &sm, mode, normalization, weights); + igraph_matrix_init(&m, 0, 0); + igraph_sparsemat_as_matrix(&m, &sm); + igraph_matrix_print(&m); + igraph_matrix_destroy(&m); + printf("===\n"); + + /* Add some loop edges */ + igraph_vector_int_init_int(&vec, 4, 1, 1, 2, 2); + igraph_add_edges(&g, &vec, 0); + igraph_vector_int_destroy(&vec); + if (weights) { + igraph_vector_push_back(weights, 2); + igraph_vector_push_back(weights, 2); + } + + igraph_get_laplacian_sparse(&g, &sm, mode, normalization, weights); + igraph_matrix_init(&m, 0, 0); + igraph_sparsemat_as_matrix(&m, &sm); + igraph_matrix_print(&m); + igraph_matrix_destroy(&m); + printf("===\n"); + + /* Duplicate some edges */ + igraph_vector_int_init_int(&vec, 4, 1, 2, 3, 4); + igraph_add_edges(&g, &vec, 0); + igraph_vector_int_destroy(&vec); + if (weights) { + igraph_vector_push_back(weights, 3); + igraph_vector_push_back(weights, 3); + } + + igraph_get_laplacian_sparse(&g, &sm, mode, normalization, weights); + igraph_matrix_init(&m, 0, 0); + igraph_sparsemat_as_matrix(&m, &sm); + igraph_matrix_print(&m); + igraph_matrix_destroy(&m); + printf("===\n"); + + /* Add an isolated vertex */ + igraph_add_vertices(&g, 1, NULL); + + igraph_get_laplacian_sparse(&g, &sm, mode, normalization, weights); + igraph_matrix_init(&m, 0, 0); + igraph_sparsemat_as_matrix(&m, &sm); + igraph_matrix_print(&m); + igraph_matrix_destroy(&m); + + igraph_destroy(&g); + + if (weights) { + igraph_vector_destroy(weights); + free(weights); + } + + igraph_sparsemat_destroy(&sm); + + return 0; +} + +int main(void) { + int res; + int i; + igraph_vector_t weights; + + igraph_vector_init_int(&weights, 5, 1, 2, 3, 4, 5); + + for (i = 0; i < 8; i++) { + igraph_bool_t is_normalized = i / 4; + igraph_vector_t* v = ((i & 2) / 2 ? &weights : 0); + igraph_bool_t dir = (i % 2 ? IGRAPH_DIRECTED : IGRAPH_UNDIRECTED); + + printf("=== %sormalized, %sweighted, %sdirected\n", + (is_normalized ? "N" : "Unn"), + (v != 0 ? "" : "un"), + (dir == IGRAPH_DIRECTED ? "" : "un") + ); + + res = test_laplacian(v, dir, is_normalized ? IGRAPH_LAPLACIAN_SYMMETRIC : IGRAPH_LAPLACIAN_UNNORMALIZED); + + if (res) { + return i + 1; + } + } + + igraph_vector_destroy(&weights); + + return 0; +} diff --git a/examples/simple/igraph_get_laplacian_sparse.out b/examples/simple/igraph_get_laplacian_sparse.out new file mode 100644 index 0000000..331560b --- /dev/null +++ b/examples/simple/igraph_get_laplacian_sparse.out @@ -0,0 +1,200 @@ +=== Unnormalized, unweighted, undirected + 2 -1 0 0 -1 +-1 2 -1 0 0 + 0 -1 2 -1 0 + 0 0 -1 2 -1 +-1 0 0 -1 2 +=== + 2 -1 0 0 -1 +-1 2 -1 0 0 + 0 -1 2 -1 0 + 0 0 -1 2 -1 +-1 0 0 -1 2 +=== + 2 -1 0 0 -1 +-1 3 -2 0 0 + 0 -2 3 -1 0 + 0 0 -1 3 -2 +-1 0 0 -2 3 +=== + 2 -1 0 0 -1 0 +-1 3 -2 0 0 0 + 0 -2 3 -1 0 0 + 0 0 -1 3 -2 0 +-1 0 0 -2 3 0 + 0 0 0 0 0 0 +=== Unnormalized, unweighted, directed + 1 -1 0 0 0 + 0 1 -1 0 0 + 0 0 1 -1 0 + 0 0 0 1 -1 +-1 0 0 0 1 +=== + 1 -1 0 0 0 + 0 1 -1 0 0 + 0 0 1 -1 0 + 0 0 0 1 -1 +-1 0 0 0 1 +=== + 1 -1 0 0 0 + 0 2 -2 0 0 + 0 0 1 -1 0 + 0 0 0 2 -2 +-1 0 0 0 1 +=== + 1 -1 0 0 0 0 + 0 2 -2 0 0 0 + 0 0 1 -1 0 0 + 0 0 0 2 -2 0 +-1 0 0 0 1 0 + 0 0 0 0 0 0 +=== Unnormalized, weighted, undirected + 6 -1 0 0 -5 +-1 3 -2 0 0 + 0 -2 5 -3 0 + 0 0 -3 7 -4 +-5 0 0 -4 9 +=== + 6 -1 0 0 -5 +-1 3 -2 0 0 + 0 -2 5 -3 0 + 0 0 -3 7 -4 +-5 0 0 -4 9 +=== + 6 -1 0 0 -5 +-1 6 -5 0 0 + 0 -5 8 -3 0 + 0 0 -3 10 -7 +-5 0 0 -7 12 +=== + 6 -1 0 0 -5 0 +-1 6 -5 0 0 0 + 0 -5 8 -3 0 0 + 0 0 -3 10 -7 0 +-5 0 0 -7 12 0 + 0 0 0 0 0 0 +=== Unnormalized, weighted, directed + 1 -1 0 0 0 + 0 2 -2 0 0 + 0 0 3 -3 0 + 0 0 0 4 -4 +-5 0 0 0 5 +=== + 1 -1 0 0 0 + 0 2 -2 0 0 + 0 0 3 -3 0 + 0 0 0 4 -4 +-5 0 0 0 5 +=== + 1 -1 0 0 0 + 0 5 -5 0 0 + 0 0 3 -3 0 + 0 0 0 7 -7 +-5 0 0 0 5 +=== + 1 -1 0 0 0 0 + 0 5 -5 0 0 0 + 0 0 3 -3 0 0 + 0 0 0 7 -7 0 +-5 0 0 0 5 0 + 0 0 0 0 0 0 +=== Normalized, unweighted, undirected + 1 -0.5 0 0 -0.5 +-0.5 1 -0.5 0 0 + 0 -0.5 1 -0.5 0 + 0 0 -0.5 1 -0.5 +-0.5 0 0 -0.5 1 +=== + 1 -0.353553 0 0 -0.5 +-0.353553 0.5 -0.25 0 0 + 0 -0.25 0.5 -0.353553 0 + 0 0 -0.353553 1 -0.5 + -0.5 0 0 -0.5 1 +=== + 1 -0.316228 0 0 -0.408248 +-0.316228 0.6 -0.4 0 0 + 0 -0.4 0.6 -0.258199 0 + 0 0 -0.258199 1 -0.666667 +-0.408248 0 0 -0.666667 1 +=== + 1 -0.316228 0 0 -0.408248 0 +-0.316228 0.6 -0.4 0 0 0 + 0 -0.4 0.6 -0.258199 0 0 + 0 0 -0.258199 1 -0.666667 0 +-0.408248 0 0 -0.666667 1 0 + 0 0 0 0 0 0 +=== Normalized, unweighted, directed + 1 -1 0 0 0 + 0 1 -1 0 0 + 0 0 1 -1 0 + 0 0 0 1 -1 +-1 0 0 0 1 +=== + 1 -0.707107 0 0 0 + 0 0.5 -0.5 0 0 + 0 0 0.5 -0.707107 0 + 0 0 0 1 -1 +-1 0 0 0 1 +=== + 1 -0.57735 0 0 0 + 0 0.666667 -0.816497 0 0 + 0 0 0.5 -0.5 0 + 0 0 0 1 -1.41421 +-1 0 0 0 1 +=== + 1 -0.57735 0 0 0 0 + 0 0.666667 -0.816497 0 0 0 + 0 0 0.5 -0.5 0 0 + 0 0 0 1 -1.41421 0 +-1 0 0 0 1 0 + 0 0 0 0 0 0 +=== Normalized, weighted, undirected + 1 -0.235702 0 0 -0.680414 +-0.235702 1 -0.516398 0 0 + 0 -0.516398 1 -0.507093 0 + 0 0 -0.507093 1 -0.503953 +-0.680414 0 0 -0.503953 1 +=== + 1 -0.154303 0 0 -0.680414 +-0.154303 0.428571 -0.251976 0 0 + 0 -0.251976 0.555556 -0.377964 0 + 0 0 -0.377964 1 -0.503953 +-0.680414 0 0 -0.503953 1 +=== + 1 -0.129099 0 0 -0.589256 +-0.129099 0.6 -0.456435 0 0 + 0 -0.456435 0.666667 -0.273861 0 + 0 0 -0.273861 1 -0.63901 +-0.589256 0 0 -0.63901 1 +=== + 1 -0.129099 0 0 -0.589256 0 +-0.129099 0.6 -0.456435 0 0 0 + 0 -0.456435 0.666667 -0.273861 0 0 + 0 0 -0.273861 1 -0.63901 0 +-0.589256 0 0 -0.63901 1 0 + 0 0 0 0 0 0 +=== Normalized, weighted, directed + 1 -0.707107 0 0 0 + 0 1 -0.816497 0 0 + 0 0 1 -0.866025 0 + 0 0 0 1 -0.894427 +-2.23607 0 0 0 1 +=== + 1 -0.5 0 0 0 + 0 0.5 -0.447214 0 0 + 0 0 0.6 -0.67082 0 + 0 0 0 1 -0.894427 +-2.23607 0 0 0 1 +=== + 1 -0.377964 0 0 0 + 0 0.714286 -0.845154 0 0 + 0 0 0.6 -0.507093 0 + 0 0 0 1 -1.18322 +-2.23607 0 0 0 1 +=== + 1 -0.377964 0 0 0 0 + 0 0.714286 -0.845154 0 0 0 + 0 0 0.6 -0.507093 0 0 + 0 0 0 1 -1.18322 0 +-2.23607 0 0 0 1 0 + 0 0 0 0 0 0 diff --git a/examples/simple/igraph_get_shortest_paths.c b/examples/simple/igraph_get_shortest_paths.c new file mode 100644 index 0000000..611ee94 --- /dev/null +++ b/examples/simple/igraph_get_shortest_paths.c @@ -0,0 +1,114 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include + +int check_evecs(const igraph_t *graph, const igraph_vector_int_list_t *vecs, + const igraph_vector_int_list_t *evecs, int error_code) { + + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t i, n = igraph_vector_int_list_size(vecs); + if (igraph_vector_int_list_size(evecs) != n) { + exit(error_code + 1); + } + + for (i = 0; i < n; i++) { + igraph_vector_int_t *vvec = igraph_vector_int_list_get_ptr(vecs, i); + igraph_vector_int_t *evec = igraph_vector_int_list_get_ptr(evecs, i); + igraph_integer_t j, n2 = igraph_vector_int_size(evec); + if (igraph_vector_int_size(vvec) == 0 && n2 == 0) { + continue; + } + if (igraph_vector_int_size(vvec) != n2 + 1) { + exit(error_code + 2); + } + for (j = 0; j < n2; j++) { + igraph_integer_t edge = VECTOR(*evec)[j]; + igraph_integer_t from = VECTOR(*vvec)[j]; + igraph_integer_t to = VECTOR(*vvec)[j + 1]; + if (directed) { + if (from != IGRAPH_FROM(graph, edge) || + to != IGRAPH_TO (graph, edge)) { + exit(error_code); + } + } else { + igraph_integer_t from2 = IGRAPH_FROM(graph, edge); + igraph_integer_t to2 = IGRAPH_TO(graph, edge); + igraph_integer_t min1 = from < to ? from : to; + igraph_integer_t max1 = from < to ? to : from; + igraph_integer_t min2 = from2 < to2 ? from2 : to2; + igraph_integer_t max2 = from2 < to2 ? to2 : from2; + if (min1 != min2 || max1 != max2) { + exit(error_code + 3); + } + } + } + } + + return 0; +} + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t vecs, evecs; + igraph_vector_int_t parents, inbound; + igraph_integer_t i; + igraph_vs_t vs; + + igraph_ring(&g, 10, IGRAPH_DIRECTED, 0, 1); + + igraph_vector_int_list_init(&vecs, 0); + igraph_vector_int_list_init(&evecs, 0); + igraph_vector_int_init(&parents, 0); + igraph_vector_int_init(&inbound, 0); + + igraph_vs_vector_small(&vs, 1, 3, 5, 2, 1, -1); + + igraph_get_shortest_paths(&g, &vecs, &evecs, 0, vs, IGRAPH_OUT, &parents, &inbound); + + check_evecs(&g, &vecs, &evecs, 10); + + for (i = 0; i < igraph_vector_int_list_size(&vecs); i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&vecs, i)); + } + + igraph_vector_int_print(&parents); + igraph_vector_int_print(&inbound); + + igraph_vector_int_list_destroy(&vecs); + igraph_vector_int_list_destroy(&evecs); + igraph_vector_int_destroy(&parents); + igraph_vector_int_destroy(&inbound); + + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + if (!IGRAPH_FINALLY_STACK_EMPTY) { + return 1; + } + + return 0; +} diff --git a/examples/simple/igraph_get_shortest_paths.out b/examples/simple/igraph_get_shortest_paths.out new file mode 100644 index 0000000..b172b8e --- /dev/null +++ b/examples/simple/igraph_get_shortest_paths.out @@ -0,0 +1,7 @@ +0 1 +0 1 2 3 +0 1 2 3 4 5 +0 1 2 +0 1 +-1 0 1 2 3 4 -2 -2 -2 -2 +-1 0 1 2 3 4 -1 -1 -1 -1 diff --git a/examples/simple/igraph_get_shortest_paths_dijkstra.c b/examples/simple/igraph_get_shortest_paths_dijkstra.c new file mode 100644 index 0000000..faec308 --- /dev/null +++ b/examples/simple/igraph_get_shortest_paths_dijkstra.c @@ -0,0 +1,83 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t vecs, evecs; + igraph_vector_int_t parents, inbound; + igraph_integer_t i; + igraph_real_t weights[] = { 0, 2, 1, 0, 5, 2, 1, 1, 0, 2, 2, 8, 1, 1, 3, 1, 1, 4, 2, 1 }; + igraph_vector_t weights_vec; + igraph_vs_t vs; + + igraph_vector_int_list_init(&vecs, 0); + igraph_vector_int_list_init(&evecs, 0); + igraph_vector_int_init(&parents, 0); + igraph_vector_int_init(&inbound, 0); + + igraph_vs_vector_small(&vs, 0, 1, 3, 5, 2, 1, -1); + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 4, 1, 5, + 2, 3, 2, 6, 3, 2, 3, 6, + 4, 5, 4, 7, 5, 6, 5, 8, 5, 9, + 7, 5, 7, 8, 8, 9, + 5, 2, + 2, 1, + -1); + + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + igraph_get_shortest_paths_dijkstra(&g, /*vertices=*/ &vecs, + /*edges=*/ &evecs, /*from=*/ 0, /*to=*/ vs, + &weights_vec, IGRAPH_OUT, + &parents, + /*inbound_edges=*/ &inbound); + printf("Vertices:\n"); + for (i = 0; i < igraph_vector_int_list_size(&vecs); i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&vecs, i)); + } + + printf("\nEdges:\n"); + for (i = 0; i < igraph_vector_int_list_size(&evecs); i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&evecs, i)); + } + + printf("\nParents:\n"); + igraph_vector_int_print(&parents); + + printf("\nInbound:\n"); + igraph_vector_int_print(&inbound); + + igraph_vector_int_list_destroy(&vecs); + igraph_vector_int_list_destroy(&evecs); + igraph_vector_int_destroy(&parents); + igraph_vector_int_destroy(&inbound); + + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_get_shortest_paths_dijkstra.out b/examples/simple/igraph_get_shortest_paths_dijkstra.out new file mode 100644 index 0000000..09c8a60 --- /dev/null +++ b/examples/simple/igraph_get_shortest_paths_dijkstra.out @@ -0,0 +1,21 @@ +Vertices: +0 +0 1 +0 3 +0 1 5 +0 1 2 +0 1 + +Edges: + +0 +2 +0 5 +0 3 +0 + +Parents: +-1 0 1 0 1 1 2 -2 5 5 + +Inbound: +-1 0 3 2 4 5 7 -1 13 14 diff --git a/examples/simple/igraph_girth.c b/examples/simple/igraph_girth.c new file mode 100644 index 0000000..371878f --- /dev/null +++ b/examples/simple/igraph_girth.c @@ -0,0 +1,58 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + igraph_t g; + igraph_real_t girth; + igraph_vector_int_t v; + igraph_vector_int_t circle; + igraph_integer_t chord[] = { 0, 50 }; + + igraph_ring(&g, 100, IGRAPH_UNDIRECTED, 0, 1); + igraph_vector_int_view(&v, chord, sizeof(chord) / sizeof(chord[0])); + igraph_add_edges(&g, &v, 0); + igraph_girth(&g, &girth, 0); + if (girth != 51) { + return 1; + } + + igraph_destroy(&g); + + /* Special case: null graph */ + igraph_ring(&g, 0, IGRAPH_UNDIRECTED, 0, 1); + igraph_vector_int_init(&circle, 1); + VECTOR(circle)[0] = 2; + igraph_girth(&g, &girth, &circle); + if (girth != IGRAPH_INFINITY) { + return 2; + } + if (igraph_vector_int_size(&circle) != 0) { + return 3; + } + igraph_vector_int_destroy(&circle); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_grg_game.c b/examples/simple/igraph_grg_game.c new file mode 100644 index 0000000..8c57c30 --- /dev/null +++ b/examples/simple/igraph_grg_game.c @@ -0,0 +1,50 @@ + +#include +#include + +int main(void) { + igraph_t graph; + igraph_vector_t x, y; + igraph_vector_t weights; + igraph_eit_t eit; + igraph_real_t avg_dist; + + /* Set random seed for reproducible results */ + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Create a random geometric graph and retrieve vertex coordinates */ + + igraph_vector_init(&x, 0); + igraph_vector_init(&y, 0); + + igraph_grg_game(&graph, 200, 0.1, /* torus */ 0, &x, &y); + + /* Compute edge weights as geometric distance */ + + igraph_vector_init(&weights, igraph_ecount(&graph)); + igraph_eit_create(&graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit); + for (; ! IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + igraph_integer_t e = IGRAPH_EIT_GET(eit); + igraph_integer_t u = IGRAPH_FROM(&graph, e); + igraph_integer_t v = IGRAPH_TO(&graph, e); + + VECTOR(weights)[e] = hypot(VECTOR(x)[u] - VECTOR(x)[v], VECTOR(y)[u] - VECTOR(y)[v]); + } + igraph_eit_destroy(&eit); + + /* Compute average path length */ + + igraph_average_path_length_dijkstra(&graph, &avg_dist, NULL, &weights, IGRAPH_UNDIRECTED, /* unconn */ 1); + + printf("Average distance in the geometric graph: %g.\n", avg_dist); + + /* Destroy data structures when no longer needed */ + + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + igraph_vector_destroy(&x); + igraph_vector_destroy(&y); + + return 0; +} diff --git a/examples/simple/igraph_grg_game.out b/examples/simple/igraph_grg_game.out new file mode 100644 index 0000000..dd1be69 --- /dev/null +++ b/examples/simple/igraph_grg_game.out @@ -0,0 +1 @@ +Average distance in the geometric graph: 0.645025. diff --git a/examples/simple/igraph_has_multiple.c b/examples/simple/igraph_has_multiple.c new file mode 100644 index 0000000..c4239a1 --- /dev/null +++ b/examples/simple/igraph_has_multiple.c @@ -0,0 +1,85 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t graph; + igraph_bool_t res; + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 1, 0, 1, 1, 0, 3, 4, 11, 10, -1); + igraph_has_multiple(&graph, &res); + if (!res) { + return 1; + } + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0, 0, 1, 2, 1, 1, 2, 2, 2, 1, 2, 3, 2, 4, + 2, 5, 2, 6, 2, 2, 3, 2, 0, 0, 6, 2, 2, 2, 0, 0, -1); + igraph_has_multiple(&graph, &res); + if (!res) { + return 2; + } + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 1, 1, 0, 3, 4, 11, 10, -1); + igraph_has_multiple(&graph, &res); + if (res) { + return 3; + } + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0, 0, 1, 2, 1, 1, 2, 2, 2, 3, 2, 4, 2, 5, 2, 6, 2, 2, -1); + igraph_has_multiple(&graph, &res); + if (!res) { + return 4; + } + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0, 0, 1, 2, 1, 1, 2, 2, 2, 3, 2, 4, 2, 5, 2, 6, -1); + igraph_has_multiple(&graph, &res); + if (res) { + return 5; + } + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0, 1, 0, 1, 1, 2, -1); + igraph_has_multiple(&graph, &res); + if (!res) { + return 6; + } + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0, 0, 0, 0, -1); + igraph_has_multiple(&graph, &res); + if (!res) { + return 7; + } + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_independent_sets.c b/examples/simple/igraph_independent_sets.c new file mode 100644 index 0000000..bb310b7 --- /dev/null +++ b/examples/simple/igraph_independent_sets.c @@ -0,0 +1,69 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t result; + igraph_integer_t i, j, n; + igraph_integer_t alpha; + const int params[] = {4, -1, 2, 2, 0, 0, -1, -1}; + + igraph_set_warning_handler(igraph_warning_handler_ignore); + igraph_vector_int_list_init(&result, 0); + + igraph_kary_tree(&g, 5, 2, IGRAPH_TREE_OUT); + for (j = 0; j < sizeof(params) / (2 * sizeof(params[0])); j++) { + if (params[2 * j + 1] != 0) { + igraph_independent_vertex_sets(&g, &result, params[2 * j], params[2 * j + 1]); + } else { + igraph_largest_independent_vertex_sets(&g, &result); + } + n = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " independent sets found\n", n); + for (i = 0; i < n; i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&result, i)); + } + } + igraph_destroy(&g); + + igraph_kary_tree(&g, 10, 2, IGRAPH_TREE_OUT); + igraph_maximal_independent_vertex_sets(&g, &result); + n = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " maximal independent sets found\n", n); + for (i = 0; i < n; i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&result, i)); + } + + igraph_independence_number(&g, &alpha); + printf("alpha=%" IGRAPH_PRId "\n", alpha); + + igraph_destroy(&g); + + igraph_vector_int_list_destroy(&result); + + return 0; +} diff --git a/examples/simple/igraph_independent_sets.out b/examples/simple/igraph_independent_sets.out new file mode 100644 index 0000000..d380bcd --- /dev/null +++ b/examples/simple/igraph_independent_sets.out @@ -0,0 +1,36 @@ +0 independent sets found +6 independent sets found +0 3 +0 4 +1 2 +2 3 +2 4 +3 4 +2 independent sets found +0 3 4 +2 3 4 +13 independent sets found +0 +1 +2 +3 +4 +0 3 +0 4 +1 2 +2 3 +2 4 +3 4 +0 3 4 +2 3 4 +9 maximal independent sets found +0 3 4 5 6 +0 3 5 6 9 +0 4 5 6 7 8 +0 5 6 7 8 9 +1 2 7 8 9 +1 5 6 7 8 9 +2 3 4 +2 3 9 +2 4 7 8 +alpha=6 diff --git a/examples/simple/igraph_intersection.c b/examples/simple/igraph_intersection.c new file mode 100644 index 0000000..4642268 --- /dev/null +++ b/examples/simple/igraph_intersection.c @@ -0,0 +1,124 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +void print_vector(igraph_vector_t *v) { + igraph_integer_t i, l = igraph_vector_size(v); + for (i = 0; i < l; i++) { + printf(" %" IGRAPH_PRId "", (igraph_integer_t) VECTOR(*v)[i]); + } + printf("\n"); +} + +int main(void) { + + igraph_t left, right, isec; + igraph_vector_int_t v; + igraph_vector_ptr_t glist; + igraph_t g1, g2, g3; + igraph_vector_int_t edge_map1, edge_map2; + + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, -1); + igraph_create(&left, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + igraph_vector_int_init_int_end(&v, -1, 1, 0, 5, 4, 1, 2, 3, 2, -1); + igraph_create(&right, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + igraph_vector_int_init(&edge_map1, 0); + igraph_vector_int_init(&edge_map2, 0); + + igraph_intersection(&isec, &left, &right, &edge_map1, &edge_map2); + igraph_vector_int_init(&v, 0); + igraph_get_edgelist(&isec, &v, 0); + printf("---\n"); + igraph_vector_int_print(&v); + igraph_vector_int_print(&edge_map1); + igraph_vector_int_print(&edge_map2); + printf("---\n"); + igraph_vector_int_destroy(&v); + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&isec); + igraph_vector_int_destroy(&edge_map1); + igraph_vector_int_destroy(&edge_map2); + + /* empty graph list */ + igraph_vector_ptr_init(&glist, 0); + igraph_intersection_many(&isec, &glist, 0); + if (igraph_vcount(&isec) != 0 || !igraph_is_directed(&isec)) { + return 1; + } + igraph_destroy(&isec); + igraph_vector_ptr_destroy(&glist); + + /* graph list with an empty graph */ + igraph_vector_ptr_init(&glist, 3); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, -1); + igraph_create(&g1, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, -1); + igraph_create(&g2, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + igraph_empty(&g3, 10, IGRAPH_DIRECTED); + + VECTOR(glist)[0] = &g1; + VECTOR(glist)[1] = &g2; + VECTOR(glist)[2] = &g3; + igraph_intersection_many(&isec, &glist, 0); + if (igraph_ecount(&isec) != 0 || igraph_vcount(&isec) != 10) { + return 2; + } + igraph_destroy(&g1); + igraph_destroy(&g2); + igraph_destroy(&g3); + igraph_destroy(&isec); + igraph_vector_ptr_destroy(&glist); + + /* "proper" graph list */ + igraph_vector_ptr_init(&glist, 3); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, -1); + igraph_create(&g1, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, 3, 2, 4, 5, 6, 5, -1); + igraph_create(&g2, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + igraph_vector_int_init_int_end(&v, -1, 2, 3, 1, 0, 1, 2, 3, 2, 4, 5, 6, 5, 2, 3, -1); + igraph_create(&g3, &v, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&v); + + VECTOR(glist)[0] = &g1; + VECTOR(glist)[1] = &g2; + VECTOR(glist)[2] = &g3; + igraph_intersection_many(&isec, &glist, 0); + igraph_write_graph_edgelist(&isec, stdout); + igraph_destroy(&g1); + igraph_destroy(&g2); + igraph_destroy(&g3); + igraph_destroy(&isec); + igraph_vector_ptr_destroy(&glist); + + return 0; +} diff --git a/examples/simple/igraph_intersection.out b/examples/simple/igraph_intersection.out new file mode 100644 index 0000000..751d9db --- /dev/null +++ b/examples/simple/igraph_intersection.out @@ -0,0 +1,7 @@ +--- +1 2 +1 +2 +--- +1 2 +2 3 diff --git a/examples/simple/igraph_is_biconnected.c b/examples/simple/igraph_is_biconnected.c new file mode 100644 index 0000000..fead2ce --- /dev/null +++ b/examples/simple/igraph_is_biconnected.c @@ -0,0 +1,33 @@ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +int main(void) { + + igraph_t g; + igraph_bool_t result; + + igraph_small(&g, 7, 0, 0, 1, 1, 2, 2, 3, 3, 0, 2, 4, 4, 5, 2, 5, -1); + igraph_is_biconnected(&g, &result); + printf("Graph is%sbiconnected.\n", result ? " " : " not "); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_is_biconnected.out b/examples/simple/igraph_is_biconnected.out new file mode 100644 index 0000000..020696b --- /dev/null +++ b/examples/simple/igraph_is_biconnected.out @@ -0,0 +1 @@ +Graph is not biconnected. diff --git a/examples/simple/igraph_is_directed.c b/examples/simple/igraph_is_directed.c new file mode 100644 index 0000000..1c0dc3d --- /dev/null +++ b/examples/simple/igraph_is_directed.c @@ -0,0 +1,43 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + + igraph_empty(&g, 0, 0); + if (igraph_is_directed(&g)) { + return 1; + } + igraph_destroy(&g); + + igraph_empty(&g, 0, 1); + if (!igraph_is_directed(&g)) { + return 2; + } + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_is_loop.c b/examples/simple/igraph_is_loop.c new file mode 100644 index 0000000..96f3350 --- /dev/null +++ b/examples/simple/igraph_is_loop.c @@ -0,0 +1,55 @@ +/* + IGraph library. + Copyright (C) 2007-2024 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 + +void analyze_loops(const igraph_t *graph) { + igraph_vector_bool_t is_loop; + igraph_bool_t has_loop; + igraph_integer_t loop_count; + + igraph_has_loop(graph, &has_loop); + printf("Has loops? %s\n", has_loop ? "Yes" : "No"); + + igraph_count_loops(graph, &loop_count); + printf("How many? %" IGRAPH_PRId "\n", loop_count); + + igraph_vector_bool_init(&is_loop, 0); + igraph_is_loop(graph, &is_loop, igraph_ess_all(IGRAPH_EDGEORDER_ID)); + printf("Loop positions: "); igraph_vector_bool_print(&is_loop); + igraph_vector_bool_destroy(&is_loop); + + printf("\n"); +} + +int main(void) { + + igraph_t graph; + + igraph_small(&graph, 0, IGRAPH_DIRECTED, + 0,1, 1,2, 2,1, 0,1, 1,0, 3,4, 11,10, -1); + analyze_loops(&graph); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,0, 1,1, 2,2, 2,3, 2,4, 2,5, 2,6, 2,2, 0,0, -1); + analyze_loops(&graph); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_is_loop.out b/examples/simple/igraph_is_loop.out new file mode 100644 index 0000000..cb38344 --- /dev/null +++ b/examples/simple/igraph_is_loop.out @@ -0,0 +1,8 @@ +Has loops? No +How many? 0 +Loop positions: 0 0 0 0 0 0 0 + +Has loops? Yes +How many? 5 +Loop positions: 1 1 1 0 0 0 0 1 1 + diff --git a/examples/simple/igraph_is_minimal_separator.c b/examples/simple/igraph_is_minimal_separator.c new file mode 100644 index 0000000..8949620 --- /dev/null +++ b/examples/simple/igraph_is_minimal_separator.c @@ -0,0 +1,74 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include + +#define FAIL(msg, error) do { printf(msg "\n") ; return error; } while (0) + +int main(void) { + + igraph_t graph; + igraph_vector_int_t sep; + igraph_bool_t result; + + /* Simple star graph, remove the center */ + igraph_star(&graph, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_is_minimal_separator(&graph, igraph_vss_1(0), &result); + if (!result) { + FAIL("Center of star graph failed.", 1); + } + + /* Same graph, but another vertex */ + igraph_is_minimal_separator(&graph, igraph_vss_1(6), &result); + if (result) { + FAIL("Non-center of star graph failed.", 2); + } + igraph_destroy(&graph); + + /* Karate club */ + igraph_famous(&graph, "zachary"); + igraph_vector_int_init(&sep, 0); + igraph_vector_int_push_back(&sep, 32); + igraph_vector_int_push_back(&sep, 33); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&sep), &result); + if (!result) { + FAIL("Karate network (32,33) failed", 3); + } + + igraph_vector_int_resize(&sep, 5); + VECTOR(sep)[0] = 8; + VECTOR(sep)[1] = 9; + VECTOR(sep)[2] = 19; + VECTOR(sep)[3] = 30; + VECTOR(sep)[4] = 31; + igraph_is_minimal_separator(&graph, igraph_vss_vector(&sep), &result); + if (result) { + FAIL("Karate network (8,9,19,30,31) failed", 4); + } + + igraph_destroy(&graph); + igraph_vector_int_destroy(&sep); + + return 0; +} diff --git a/examples/simple/igraph_is_multiple.c b/examples/simple/igraph_is_multiple.c new file mode 100644 index 0000000..1f05fcc --- /dev/null +++ b/examples/simple/igraph_is_multiple.c @@ -0,0 +1,56 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-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 + 02110-1301 USA + +*/ + +#include + +void print_vector(igraph_vector_bool_t *v, FILE *f) { + igraph_integer_t i; + for (i = 0; i < igraph_vector_bool_size(v); i++) { + fprintf(f, " %i", VECTOR(*v)[i] ? 1 : 0); + } + fprintf(f, "\n"); +} + +int main(void) { + + igraph_t graph; + igraph_vector_bool_t v; + + igraph_vector_bool_init(&v, 0); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 1, 0, 1, 1, 0, 3, 4, 11, 10, -1); + igraph_is_multiple(&graph, &v, igraph_ess_all(IGRAPH_EDGEORDER_ID)); + print_vector(&v, stdout); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0, 0, 1, 2, 1, 1, 2, 2, 2, 1, 2, 3, 2, 4, + 2, 5, 2, 6, 2, 2, 3, 2, 0, 0, 6, 2, 2, 2, 0, 0, -1); + igraph_is_multiple(&graph, &v, igraph_ess_all(IGRAPH_EDGEORDER_ID)); + print_vector(&v, stdout); + igraph_destroy(&graph); + + igraph_vector_bool_destroy(&v); + + return 0; +} diff --git a/examples/simple/igraph_is_multiple.out b/examples/simple/igraph_is_multiple.out new file mode 100644 index 0000000..14ea4e2 --- /dev/null +++ b/examples/simple/igraph_is_multiple.out @@ -0,0 +1,2 @@ + 0 0 0 1 0 0 0 + 0 0 0 0 1 0 0 0 0 1 1 1 1 1 1 diff --git a/examples/simple/igraph_is_separator.c b/examples/simple/igraph_is_separator.c new file mode 100644 index 0000000..bb67f3c --- /dev/null +++ b/examples/simple/igraph_is_separator.c @@ -0,0 +1,86 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include + +#define FAIL(msg, error) do { printf(msg "\n") ; return error; } while (0) + +int main(void) { + + igraph_t graph; + igraph_vector_int_t sep; + igraph_bool_t result; + + /* Simple star graph, remove the center */ + igraph_star(&graph, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_is_separator(&graph, igraph_vss_1(0), &result); + if (!result) { + FAIL("Center of star graph failed.", 1); + } + + /* Same graph, but another vertex */ + igraph_is_separator(&graph, igraph_vss_1(6), &result); + if (result) { + FAIL("Non-center of star graph failed.", 2); + } + + /* Same graph, all vertices but the center */ + igraph_is_separator(&graph, igraph_vss_range(1, 10), &result); + if (result) { + FAIL("All non-central vertices of star graph failed.", 5); + } + + /* Same graph, all vertices */ + igraph_is_separator(&graph, igraph_vss_range(0, 10), &result); + if (result) { + FAIL("All vertices of star graph failed.", 6); + } + igraph_destroy(&graph); + + /* Karate club */ + igraph_famous(&graph, "zachary"); + igraph_vector_int_init(&sep, 0); + igraph_vector_int_push_back(&sep, 32); + igraph_vector_int_push_back(&sep, 33); + igraph_is_separator(&graph, igraph_vss_vector(&sep), &result); + if (!result) { + FAIL("Karate network (32,33) failed", 3); + } + + igraph_vector_int_resize(&sep, 5); + VECTOR(sep)[0] = 8; + VECTOR(sep)[1] = 9; + VECTOR(sep)[2] = 19; + VECTOR(sep)[3] = 30; + VECTOR(sep)[4] = 31; + igraph_is_separator(&graph, igraph_vss_vector(&sep), &result); + if (result) { + FAIL("Karate network (8,9,19,30,31) failed", 4); + } + + igraph_destroy(&graph); + igraph_vector_int_destroy(&sep); + + return 0; +} diff --git a/examples/simple/igraph_isomorphic_vf2.c b/examples/simple/igraph_isomorphic_vf2.c new file mode 100644 index 0000000..9f4bdc1 --- /dev/null +++ b/examples/simple/igraph_isomorphic_vf2.c @@ -0,0 +1,95 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 + 02110-1301 USA + +*/ + +#include +#include +#include + +int main(void) { + + igraph_t ring1, ring2; + igraph_vector_int_t color1, color2; + igraph_vector_int_t perm; + igraph_bool_t iso; + igraph_integer_t count; + igraph_integer_t i; + + igraph_rng_seed(igraph_rng_default(), 12345); + + igraph_ring(&ring1, 100, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/1); + igraph_vector_int_init_range(&perm, 0, igraph_vcount(&ring1)); + igraph_vector_int_shuffle(&perm); + igraph_permute_vertices(&ring1, &ring2, &perm); + + /* Everything has the same colors */ + igraph_vector_int_init(&color1, igraph_vcount(&ring1)); + igraph_vector_int_init(&color2, igraph_vcount(&ring2)); + igraph_isomorphic_vf2(&ring1, &ring2, &color1, &color2, 0, 0, &iso, 0, 0, 0, 0, 0); + if (!iso) { + fprintf(stderr, "Single color failed.\n"); + return 1; + } + + /* Two colors, just counting */ + for (i = 0; i < igraph_vector_int_size(&color1); i += 2) { + VECTOR(color1)[i] = VECTOR(color2)[VECTOR(perm)[i]] = 1; + } + igraph_count_isomorphisms_vf2(&ring1, &ring2, &color1, &color2, 0, 0, &count, 0, 0, 0); + if (count != 100) { + fprintf(stderr, "Count with two colors failed, expected 100, got %" IGRAPH_PRId ".\n", count); + return 2; + } + + igraph_destroy(&ring1); + igraph_destroy(&ring2); + igraph_vector_int_destroy(&color2); + igraph_vector_int_destroy(&perm); + + /* Two colors, count subisomorphisms */ + igraph_ring(&ring1, 100, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/0); + igraph_ring(&ring2, 80, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/0); + + igraph_vector_int_init(&color2, igraph_vcount(&ring2)); + for (i = 0; i < igraph_vector_int_size(&color1); i += 2) { + VECTOR(color1)[i] = 0; + VECTOR(color1)[i + 1] = 1; + } + for (i = 0; i < igraph_vector_int_size(&color2); i += 2) { + VECTOR(color2)[i] = 0; + VECTOR(color2)[i + 1] = 1; + } + igraph_count_subisomorphisms_vf2(&ring1, &ring2, &color1, &color2, 0, 0, + &count, 0, 0, 0); + if (count != 21) { + fprintf(stderr, "Count with two colors failed, expected 21, got %" IGRAPH_PRId ".\n", count); + return 3; + } + + igraph_vector_int_destroy(&color1); + igraph_vector_int_destroy(&color2); + + igraph_destroy(&ring1); + igraph_destroy(&ring2); + + return 0; +} diff --git a/examples/simple/igraph_join.c b/examples/simple/igraph_join.c new file mode 100644 index 0000000..6155146 --- /dev/null +++ b/examples/simple/igraph_join.c @@ -0,0 +1,50 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +int main(void) { + igraph_t left, right, joined; + + igraph_small(&left, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, -1); + igraph_small(&right, 5, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,4, -1); + + igraph_join(&joined, &left, &right); + igraph_write_graph_edgelist(&joined, stdout); + printf("\n"); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&joined); + + + igraph_small(&left, 2, IGRAPH_DIRECTED, 0,1, -1); + igraph_small(&right, 3, IGRAPH_DIRECTED, 0,1, 2,1, -1); + + igraph_join(&joined, &left, &right); + igraph_write_graph_edgelist(&joined, stdout); + printf("\n"); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&joined); + + return 0; +} diff --git a/examples/simple/igraph_join.out b/examples/simple/igraph_join.out new file mode 100644 index 0000000..65f662e --- /dev/null +++ b/examples/simple/igraph_join.out @@ -0,0 +1,44 @@ +0 1 +0 4 +0 5 +0 6 +0 7 +0 8 +1 2 +1 4 +1 5 +1 6 +1 7 +1 8 +2 2 +2 4 +2 5 +2 6 +2 7 +2 8 +3 4 +3 5 +3 6 +3 7 +3 8 +4 5 +5 6 +6 6 +6 8 + +0 1 +0 2 +0 3 +0 4 +1 2 +1 3 +1 4 +2 0 +2 1 +2 3 +3 0 +3 1 +4 0 +4 1 +4 3 + diff --git a/examples/simple/igraph_kary_tree.c b/examples/simple/igraph_kary_tree.c new file mode 100644 index 0000000..2293e6b --- /dev/null +++ b/examples/simple/igraph_kary_tree.c @@ -0,0 +1,43 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + igraph_t graph; + igraph_bool_t res; + + /* Create a directed binary tree on 15 nodes, + with edges pointing towards the root. */ + igraph_kary_tree(&graph, 15, 2, IGRAPH_TREE_IN); + + igraph_is_tree(&graph, &res, NULL, IGRAPH_IN); + printf("Is it an in-tree? %s\n", res ? "Yes" : "No"); + + igraph_is_tree(&graph, &res, NULL, IGRAPH_OUT); + printf("Is it an out-tree? %s\n", res ? "Yes" : "No"); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_kary_tree.out b/examples/simple/igraph_kary_tree.out new file mode 100644 index 0000000..d1fca17 --- /dev/null +++ b/examples/simple/igraph_kary_tree.out @@ -0,0 +1,2 @@ +Is it an in-tree? Yes +Is it an out-tree? No diff --git a/examples/simple/igraph_lapack_dgeev.c b/examples/simple/igraph_lapack_dgeev.c new file mode 100644 index 0000000..b51e094 --- /dev/null +++ b/examples/simple/igraph_lapack_dgeev.c @@ -0,0 +1,63 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + + igraph_matrix_t A; + igraph_matrix_t vectors_left, vectors_right; + igraph_vector_t values_real, values_imag; + igraph_vector_complex_t values; + int info = 1; + + igraph_matrix_init(&A, 2, 2); + igraph_matrix_init(&vectors_left, 0, 0); + igraph_matrix_init(&vectors_right, 0, 0); + igraph_vector_init(&values_real, 0); + igraph_vector_init(&values_imag, 0); + MATRIX(A, 0, 0) = 1.0; + MATRIX(A, 0, 1) = 1.0; + MATRIX(A, 1, 0) = -1.0; + MATRIX(A, 1, 1) = 1.0; + + igraph_lapack_dgeev(&A, &values_real, &values_imag, + &vectors_left, &vectors_right, &info); + igraph_vector_complex_create(&values, &values_real, &values_imag); + printf("eigenvalues:\n"); + igraph_vector_complex_print(&values); + printf("left eigenvectors:\n"); + igraph_matrix_print(&vectors_left); + printf("right eigenvectors:\n"); + igraph_matrix_print(&vectors_right); + + igraph_vector_destroy(&values_imag); + igraph_vector_destroy(&values_real); + igraph_vector_complex_destroy(&values); + igraph_matrix_destroy(&vectors_right); + igraph_matrix_destroy(&vectors_left); + igraph_matrix_destroy(&A); + + return 0; +} diff --git a/examples/simple/igraph_lapack_dgeev.out b/examples/simple/igraph_lapack_dgeev.out new file mode 100644 index 0000000..4187d18 --- /dev/null +++ b/examples/simple/igraph_lapack_dgeev.out @@ -0,0 +1,8 @@ +eigenvalues: +1+1i 1-1i +left eigenvectors: +0.707107 0 + 0 0.707107 +right eigenvectors: +0.707107 0 + 0 0.707107 diff --git a/examples/simple/igraph_lapack_dgeevx.c b/examples/simple/igraph_lapack_dgeevx.c new file mode 100644 index 0000000..6d712cb --- /dev/null +++ b/examples/simple/igraph_lapack_dgeevx.c @@ -0,0 +1,95 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include + +void matrix_to_complex_vectors(igraph_vector_complex_t *c1, igraph_vector_complex_t *c2, igraph_matrix_t *m) { + IGRAPH_REAL(VECTOR(*c1)[0]) = MATRIX(*m, 0, 0); + IGRAPH_REAL(VECTOR(*c1)[1]) = MATRIX(*m, 1, 0); + IGRAPH_IMAG(VECTOR(*c1)[0]) = MATRIX(*m, 0, 1); + IGRAPH_IMAG(VECTOR(*c1)[1]) = MATRIX(*m, 1, 1); + + IGRAPH_REAL(VECTOR(*c2)[0]) = MATRIX(*m, 0, 0); + IGRAPH_REAL(VECTOR(*c2)[1]) = MATRIX(*m, 1, 0); + IGRAPH_IMAG(VECTOR(*c2)[0]) = -MATRIX(*m, 0, 1); + IGRAPH_IMAG(VECTOR(*c2)[1]) = -MATRIX(*m, 1, 1); +} + +int main(void) { + + igraph_matrix_t A; + igraph_matrix_t vectors_left, vectors_right; + igraph_vector_t values_real, values_imag; + igraph_vector_complex_t values; + igraph_vector_complex_t eigenvector1; + igraph_vector_complex_t eigenvector2; + int info = 1; + igraph_real_t abnrm; + + igraph_matrix_init(&A, 2, 2); + igraph_matrix_init(&vectors_left, 0, 0); + igraph_matrix_init(&vectors_right, 0, 0); + igraph_vector_init(&values_real, 0); + igraph_vector_init(&values_imag, 0); + igraph_vector_complex_init(&eigenvector1, 2); + igraph_vector_complex_init(&eigenvector2, 2); + MATRIX(A, 0, 0) = 1.0; + MATRIX(A, 0, 1) = 1.0; + MATRIX(A, 1, 0) = -1.0; + MATRIX(A, 1, 1) = 1.0; + + igraph_lapack_dgeevx(IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH, + &A, &values_real, &values_imag, + &vectors_left, &vectors_right, NULL, NULL, + /*scale=*/ NULL, &abnrm, /*rconde=*/ NULL, + /*rcondv=*/ NULL, &info); + + igraph_vector_complex_create(&values, &values_real, &values_imag); + printf("eigenvalues:\n"); + igraph_vector_complex_print(&values); + + printf("\nleft eigenvectors:\n"); + /*matrix_to_complex_vectors only works because we have two complex + conjugate eigenvalues */ + matrix_to_complex_vectors(&eigenvector1, &eigenvector2, &vectors_left); + igraph_vector_complex_print(&eigenvector1); + igraph_vector_complex_print(&eigenvector2); + + printf("\nright eigenvectors:\n"); + matrix_to_complex_vectors(&eigenvector1, &eigenvector2, &vectors_right); + igraph_vector_complex_print(&eigenvector1); + igraph_vector_complex_print(&eigenvector2); + printf("\nOne-norm of the balanced matrix:\n%g\n", abnrm); + + igraph_vector_destroy(&values_imag); + igraph_vector_destroy(&values_real); + igraph_vector_complex_destroy(&values); + igraph_vector_complex_destroy(&eigenvector1); + igraph_vector_complex_destroy(&eigenvector2); + igraph_matrix_destroy(&vectors_right); + igraph_matrix_destroy(&vectors_left); + igraph_matrix_destroy(&A); + + return 0; +} diff --git a/examples/simple/igraph_lapack_dgeevx.out b/examples/simple/igraph_lapack_dgeevx.out new file mode 100644 index 0000000..dc45f4b --- /dev/null +++ b/examples/simple/igraph_lapack_dgeevx.out @@ -0,0 +1,13 @@ +eigenvalues: +1+1i 1-1i + +left eigenvectors: +0.707107+0i 0+0.707107i +0.707107-0i 0-0.707107i + +right eigenvectors: +0.707107+0i 0+0.707107i +0.707107-0i 0-0.707107i + +One-norm of the balanced matrix: +2 diff --git a/examples/simple/igraph_lapack_dgesv.c b/examples/simple/igraph_lapack_dgesv.c new file mode 100644 index 0000000..e2f89cf --- /dev/null +++ b/examples/simple/igraph_lapack_dgesv.c @@ -0,0 +1,151 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include + +#define DIM 10 + +void igraph_print_warning(const char *reason, const char *file, + int line) { + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + printf("Warning: %s\n", reason); +} + +int main(void) { + + igraph_matrix_t A, B, RHS; + int info; + int i, j; + + /* Identity matrix, you have to start somewhere */ + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&B, DIM, 1); + for (i = 0; i < DIM; i++) { + MATRIX(A, i, i) = 1.0; + MATRIX(B, i, 0) = i + 1; + } + + igraph_matrix_init_copy(&RHS, &B); + igraph_lapack_dgesv(&A, /*ipiv=*/ 0, &RHS, &info); + + if (info != 0) { + return 1; + } + if (!igraph_matrix_all_e(&B, &RHS)) { + return 2; + } + + igraph_matrix_destroy(&A); + igraph_matrix_destroy(&B); + igraph_matrix_destroy(&RHS); + + /* Diagonal matrix */ + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&RHS, DIM, 1); + for (i = 0; i < DIM; i++) { + MATRIX(A, i, i) = i + 1; + MATRIX(RHS, i, 0) = i + 1; + } + + igraph_lapack_dgesv(&A, /*ipiv=*/ 0, &RHS, &info); + + if (info != 0) { + return 3; + } + for (i = 0; i < DIM; i++) { + if (MATRIX(RHS, i, 0) != 1.0) { + return 4; + } + } + + igraph_matrix_destroy(&A); + igraph_matrix_destroy(&RHS); + + /* A general matrix */ + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&B, DIM, 1); + igraph_matrix_init(&RHS, DIM, 1); + for (i = 0; i < DIM; i++) { + int j; + MATRIX(B, i, 0) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + for (j = 0; j < DIM; j++) { + MATRIX(A, i, j) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0, /*a=*/ &A, + /*x-*/ &MATRIX(B, 0, 0), /*beta=*/ 0, + /*y=*/ &MATRIX(RHS, 0, 0)); + + igraph_lapack_dgesv(&A, /*ipiv=*/ 0, &RHS, &info); + if (info != 0) { + return 5; + } + for (i = 0; i < DIM; i++) { + if (fabs(MATRIX(B, i, 0) - MATRIX(RHS, i, 0)) > 1e-11) { + return 6; + } + } + + igraph_matrix_destroy(&A); + igraph_matrix_destroy(&B); + igraph_matrix_destroy(&RHS); + + /* A singular matrix */ + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&B, DIM, 1); + igraph_matrix_init(&RHS, DIM, 1); + for (i = 0; i < DIM; i++) { + MATRIX(B, i, 0) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + for (j = 0; j < DIM; j++) { + MATRIX(A, i, j) = i == j ? 1 : 0; + } + } + for (i = 0; i < DIM; i++) { + MATRIX(A, DIM - 1, i) = MATRIX(A, 0, i); + } + + igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0, /*a=*/ &A, + /*x-*/ &MATRIX(B, 0, 0), /*beta=*/ 0, + /*y=*/ &MATRIX(RHS, 0, 0)); + + igraph_set_warning_handler(igraph_print_warning); + igraph_lapack_dgesv(&A, /*ipiv=*/ 0, &RHS, &info); + if (info != 10) { + printf("LAPACK returned info = %d, should have been 10", info); + return 7; + } + + igraph_matrix_destroy(&A); + igraph_matrix_destroy(&B); + igraph_matrix_destroy(&RHS); + + return 0; +} diff --git a/examples/simple/igraph_lapack_dgesv.out b/examples/simple/igraph_lapack_dgesv.out new file mode 100644 index 0000000..2443131 --- /dev/null +++ b/examples/simple/igraph_lapack_dgesv.out @@ -0,0 +1 @@ +Warning: LU: factor is exactly singular. diff --git a/examples/simple/igraph_lapack_dsyevr.c b/examples/simple/igraph_lapack_dsyevr.c new file mode 100644 index 0000000..04a60bf --- /dev/null +++ b/examples/simple/igraph_lapack_dsyevr.c @@ -0,0 +1,69 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_matrix_t A; + igraph_matrix_t vectors; + igraph_vector_t values; + + igraph_matrix_init(&A, 2, 2); + igraph_matrix_init(&vectors, 0, 0); + igraph_vector_init(&values, 0); + + MATRIX(A, 0, 0) = 2.0; + MATRIX(A, 0, 1) = -1.0; + MATRIX(A, 1, 0) = -1.0; + MATRIX(A, 1, 1) = 3.0; + + printf("Take a subset:\n"); + + igraph_lapack_dsyevr(&A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, + /*vestimate=*/ 0, /*il=*/ 1, /*iu=*/ 1, + /*abstol=*/ 1e-10, &values, &vectors, + /*support=*/ 0); + printf("eigenvalues:\n"); + igraph_vector_print(&values); + printf("eigenvectors:\n"); + igraph_matrix_print(&vectors); + + printf("\nTake a subset based on an interval:\n"); + + igraph_lapack_dsyevr(&A, IGRAPH_LAPACK_DSYEV_INTERVAL, /*vl*/ 3, /*vu*/ 4, + /*vestimate=*/ 1, /*il=*/ 0, /*iu=*/ 0, + /*abstol=*/ 1e-10, &values, &vectors, + /*support=*/ 0); + + printf("eigenvalues:\n"); + igraph_vector_print(&values); + printf("eigenvectors:\n"); + igraph_matrix_print(&vectors); + + igraph_vector_destroy(&values); + igraph_matrix_destroy(&vectors); + igraph_matrix_destroy(&A); + + return 0; +} diff --git a/examples/simple/igraph_lapack_dsyevr.out b/examples/simple/igraph_lapack_dsyevr.out new file mode 100644 index 0000000..dbe6bbb --- /dev/null +++ b/examples/simple/igraph_lapack_dsyevr.out @@ -0,0 +1,13 @@ +Take a subset: +eigenvalues: +1.38197 +eigenvectors: +0.850651 +0.525731 + +Take a subset based on an interval: +eigenvalues: +3.61803 +eigenvectors: +-0.525731 + 0.850651 diff --git a/examples/simple/igraph_layout_reingold_tilford.c b/examples/simple/igraph_layout_reingold_tilford.c new file mode 100644 index 0000000..9f87221 --- /dev/null +++ b/examples/simple/igraph_layout_reingold_tilford.c @@ -0,0 +1,47 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + + igraph_t g; + FILE *f; + igraph_matrix_t coords; + /* igraph_integer_t i, n; */ + + f = fopen("igraph_layout_reingold_tilford.in", "r"); + igraph_read_graph_edgelist(&g, f, 0, IGRAPH_DIRECTED); + igraph_matrix_init(&coords, 0, 0); + igraph_layout_reingold_tilford(&g, &coords, IGRAPH_IN, 0, 0); + + /*n=igraph_vcount(&g); + for (i=0; i + 334 Harvard st, Cambridge MA, 02139 USA + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g, g2; + igraph_bool_t iso; + + // Franklin graph + igraph_lcf(&g, 12, 5, -5, 6, 0); + igraph_famous(&g2, "franklin"); + + igraph_isomorphic_vf2(&g, &g2, + /*vertex.color1=*/ 0, /*vertex.color2=*/ 0, + /*edge.color1=*/ 0, /*edge.color2=*/ 0, + &iso, 0, 0, 0, 0, 0); + if (!iso) { + printf("Failure: Franklin\n"); + return 1; + } + + igraph_destroy(&g); + igraph_destroy(&g2); + + // [3, -2]^4, n=8 + igraph_lcf(&g, 8, 3, -2, 4, 0); + + if (igraph_ecount(&g) != 16) { + printf("Failure: [3, -2]^4, n=8\n"); + return 1; + } + + igraph_destroy(&g); + + // [2, -2]^2, n=2 + igraph_lcf(&g, 2, 2, -2, 2, 0); + + if (igraph_ecount(&g) != 1) { + printf("Failure: [2, -2]^2, n=2\n"); + return 1; + } + + igraph_destroy(&g); + + // [2]^2, n=2 + igraph_lcf(&g, 2, 2, 2, 0); + + if (igraph_ecount(&g) != 1) { + printf("Failure: [2]^2, n=2\n"); + return 1; + } + + igraph_destroy(&g); + + // Regression test for bug #996 + igraph_lcf(&g, 0, 0); + if (igraph_vcount(&g) != 0 || igraph_ecount(&g) != 0) { + printf("Failure: regression test for #996\n"); + return 1; + } + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_list_triangles.c b/examples/simple/igraph_list_triangles.c new file mode 100644 index 0000000..41d20df --- /dev/null +++ b/examples/simple/igraph_list_triangles.c @@ -0,0 +1,37 @@ +/* + 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, see . +*/ + +#include + +int main(void) { + igraph_t g; + igraph_vector_int_t v; + igraph_matrix_int_t result; + + igraph_full(&g, 5, 0, IGRAPH_NO_LOOPS); + + printf("Triangles in a full graph of 5 vertices:\n"); + igraph_vector_int_init(&v, 0); + igraph_list_triangles(&g, &v); + igraph_matrix_int_view_from_vector(&result, &v, /* nrow = */ 3); + igraph_matrix_int_print(&result); + igraph_vector_int_destroy(&v); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_list_triangles.out b/examples/simple/igraph_list_triangles.out new file mode 100644 index 0000000..446331c --- /dev/null +++ b/examples/simple/igraph_list_triangles.out @@ -0,0 +1,4 @@ +Triangles in a full graph of 5 vertices: +0 0 0 0 0 0 1 1 1 2 +1 1 1 2 2 3 2 2 3 3 +4 2 3 4 3 4 4 3 4 4 diff --git a/examples/simple/igraph_maximal_cliques.c b/examples/simple/igraph_maximal_cliques.c new file mode 100644 index 0000000..c3427d9 --- /dev/null +++ b/examples/simple/igraph_maximal_cliques.c @@ -0,0 +1,53 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t cliques; + igraph_integer_t no; + + igraph_small(&g, 9, IGRAPH_UNDIRECTED, + 0,1, 0,2, 1,2, 2,3, 3,4, 3, 5, 4,5, 5,6, 6,7, -1); + igraph_vector_int_list_init(&cliques, 0); + igraph_maximal_cliques(&g, &cliques, /*min_size=*/ 0, + /*max_size=*/ 0 /*no limit*/); + igraph_maximal_cliques_count(&g, &no, /*min_size=*/ 0, + /*max_size=*/ 0 /*no limit*/); + + if (no != igraph_vector_int_list_size(&cliques)) { + return 1; + } + + for (igraph_integer_t i = 0; i < igraph_vector_int_list_size(&cliques); i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&cliques, i); + igraph_vector_int_print(v); + } + + igraph_vector_int_list_destroy(&cliques); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_maximum_bipartite_matching.c b/examples/simple/igraph_maximum_bipartite_matching.c new file mode 100644 index 0000000..dcf233a --- /dev/null +++ b/examples/simple/igraph_maximum_bipartite_matching.c @@ -0,0 +1,77 @@ +/* -*- 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 +#include + +int main(void) { + /* Test graph from the LEDA tutorial: + * http://www.leda-tutorial.org/en/unofficial/ch05s03s05.html + */ + igraph_t graph; + igraph_vector_bool_t types; + igraph_vector_int_t matching; + igraph_integer_t matching_size; + igraph_real_t matching_weight; + igraph_bool_t is_matching; + int i; + + igraph_small(&graph, 0, 0, + 0, 8, 0, 12, 0, 14, + 1, 9, 1, 10, 1, 13, + 2, 8, 2, 9, + 3, 10, 3, 11, 3, 13, + 4, 9, 4, 14, + 5, 14, + 6, 9, 6, 14, + 7, 8, 7, 12, 7, 14 + , -1); + igraph_vector_bool_init(&types, 15); + for (i = 0; i < 15; i++) { + VECTOR(types)[i] = (i >= 8); + } + igraph_vector_int_init(&matching, 0); + + igraph_maximum_bipartite_matching(&graph, &types, &matching_size, + &matching_weight, &matching, 0, 0); + if (matching_size != 6) { + printf("matching_size is %" IGRAPH_PRId ", expected: 6\n", matching_size); + return 1; + } + if (matching_weight != 6) { + printf("matching_weight is %" IGRAPH_PRId ", expected: 6\n", (igraph_integer_t) matching_weight); + return 2; + } + igraph_is_maximal_matching(&graph, &types, &matching, &is_matching); + if (!is_matching) { + printf("not a matching: "); + igraph_vector_int_print(&matching); + return 3; + } + + igraph_vector_int_destroy(&matching); + igraph_vector_bool_destroy(&types); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_mincut.c b/examples/simple/igraph_mincut.c new file mode 100644 index 0000000..2e541e2 --- /dev/null +++ b/examples/simple/igraph_mincut.c @@ -0,0 +1,112 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-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 + 02110-1301 USA + +*/ + +#include + +int print_mincut(const igraph_t *graph, igraph_real_t value, + const igraph_vector_int_t *partition, + const igraph_vector_int_t *partition2, + const igraph_vector_int_t *cut, + const igraph_vector_t *capacity) { + + igraph_integer_t i, nc = igraph_vector_int_size(cut); + igraph_bool_t directed = igraph_is_directed(graph); + + printf("mincut value: %g\n", (double) value); + printf("first partition: "); + igraph_vector_int_print(partition); + printf("second partition: "); + igraph_vector_int_print(partition2); + printf("edges in the cut: "); + for (i = 0; i < nc; i++) { + igraph_integer_t edge = VECTOR(*cut)[i]; + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO (graph, edge); + if (!directed && from > to) { + igraph_integer_t tmp = from; + from = to; + to = tmp; + } + printf("%" IGRAPH_PRId "-%" IGRAPH_PRId " (%g), ", from, to, VECTOR(*capacity)[edge]); + } + printf("\n"); + + return 0; +} + +int main(void) { + + igraph_t g; + igraph_vector_int_t partition, partition2, cut; + igraph_vector_t weights; + igraph_real_t value; + + igraph_vector_int_init(&partition, 0); + igraph_vector_int_init(&partition2, 0); + igraph_vector_int_init(&cut, 0); + + /* -------------------------------------------- */ + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 4, 1, 2, 1, 4, 1, 5, 2, 3, 2, 6, 3, 6, 3, 7, 4, 5, 5, 6, 6, 7, + -1); + igraph_vector_init_int_end(&weights, -1, 2, 3, 3, 2, 2, 4, 2, 2, 2, 3, 1, 3, -1); + + igraph_mincut(&g, &value, &partition, &partition2, &cut, &weights); + print_mincut(&g, value, &partition, &partition2, &cut, &weights); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* -------------------------------------------- */ + + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 0, 5, 5, 4, 4, 3, 3, 0, -1); + igraph_vector_init_int_end(&weights, -1, 3, 1, 2, 10, 1, 3, 2, -1); + + igraph_mincut(&g, &value, &partition, &partition2, &cut, &weights); + print_mincut(&g, value, &partition, &partition2, &cut, &weights); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* -------------------------------------------- */ + + igraph_small(&g, 5, IGRAPH_DIRECTED, + 4, 3, 3, 2, 2, 1, 1, 0, + -1); + igraph_vector_init_int_end(&weights, -1, 1, 1, 1, 1, -1); + igraph_mincut(&g, &value, &partition, &partition2, &cut, &weights); + print_mincut(&g, value, &partition, &partition2, &cut, &weights); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* -------------------------------------------- */ + + igraph_vector_int_destroy(&cut); + igraph_vector_int_destroy(&partition2); + igraph_vector_int_destroy(&partition); + + return 0; +} diff --git a/examples/simple/igraph_mincut.out b/examples/simple/igraph_mincut.out new file mode 100644 index 0000000..4308766 --- /dev/null +++ b/examples/simple/igraph_mincut.out @@ -0,0 +1,12 @@ +mincut value: 4 +first partition: 2 3 6 7 +second partition: 0 1 4 5 +edges in the cut: 1-2 (3), 5-6 (1), +mincut value: 1 +first partition: 1 +second partition: 0 2 3 4 5 +edges in the cut: 1-2 (1), +mincut value: 0 +first partition: 0 +second partition: 1 2 3 4 +edges in the cut: diff --git a/examples/simple/igraph_minimal_separators.c b/examples/simple/igraph_minimal_separators.c new file mode 100644 index 0000000..877ca21 --- /dev/null +++ b/examples/simple/igraph_minimal_separators.c @@ -0,0 +1,53 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t separators; + igraph_integer_t i, n; + + igraph_famous(&graph, "zachary"); + igraph_vector_int_list_init(&separators, 0); + igraph_all_minimal_st_separators(&graph, &separators); + + n = igraph_vector_int_list_size(&separators); + for (i = 0; i < n; i++) { + igraph_bool_t res; + igraph_vector_int_t *sep = igraph_vector_int_list_get_ptr(&separators, i); + + igraph_is_separator(&graph, igraph_vss_vector(sep), &res); + if (!res) { + printf("Vertex set %" IGRAPH_PRId " is not a separator!\n", i); + igraph_vector_int_print(sep); + return 1; + } + } + + igraph_destroy(&graph); + igraph_vector_int_list_destroy(&separators); + + return 0; +} diff --git a/examples/simple/igraph_minimum_size_separators.c b/examples/simple/igraph_minimum_size_separators.c new file mode 100644 index 0000000..5f3cd5c --- /dev/null +++ b/examples/simple/igraph_minimum_size_separators.c @@ -0,0 +1,45 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + igraph_t g; + igraph_vector_int_list_t sep; + + igraph_small(&g, 7, IGRAPH_UNDIRECTED, + 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, + -1); + igraph_vector_int_list_init(&sep, 0); + igraph_minimum_size_separators(&g, &sep); + + for (igraph_integer_t i = 0; i < igraph_vector_int_list_size(&sep); i++) { + igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(&sep, i); + igraph_vector_int_print(v); + } + + igraph_vector_int_list_destroy(&sep); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_minimum_spanning_tree.c b/examples/simple/igraph_minimum_spanning_tree.c new file mode 100644 index 0000000..a0509ce --- /dev/null +++ b/examples/simple/igraph_minimum_spanning_tree.c @@ -0,0 +1,68 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t graph, tree; + igraph_vector_t eb; + igraph_vector_int_t edges; + + /* Create the Frucht graph */ + igraph_famous(&graph, "Frucht"); + + /* Compute the edge betweenness. */ + igraph_vector_init(&eb, igraph_ecount(&graph)); + igraph_edge_betweenness(&graph, &eb, IGRAPH_UNDIRECTED, /*weights=*/ NULL); + + /* Compute and output a minimum weight spanning tree using edge betweenness + * values as weights. */ + igraph_minimum_spanning_tree_prim(&graph, &tree, &eb); + printf("Minimum spanning tree:\n"); + igraph_write_graph_edgelist(&tree, stdout); + + /* A maximum spanning tree can be computed by first negating the weights. */ + igraph_vector_scale(&eb, -1); + + /* Compute and output the edges that belong to the maximum weight spanning tree. */ + igraph_vector_int_init(&edges, 0); + igraph_minimum_spanning_tree(&graph, &edges, &eb); + printf("\nMaximum spanning tree edges:\n"); + igraph_vector_int_print(&edges); + + igraph_real_t total_tree_weight = 0; + igraph_integer_t n = igraph_vector_int_size(&edges); + for (igraph_integer_t i=0; i < n; i++) { + total_tree_weight += -VECTOR(eb)[ VECTOR(edges)[i] ]; + } + printf("\nTotal maximum spanning tree weight: %g\n", total_tree_weight); + + /* Clean up */ + igraph_vector_int_destroy(&edges); + igraph_destroy(&tree); + igraph_destroy(&graph); + igraph_vector_destroy(&eb); + + return 0; +} diff --git a/examples/simple/igraph_minimum_spanning_tree.out b/examples/simple/igraph_minimum_spanning_tree.out new file mode 100644 index 0000000..488d35f --- /dev/null +++ b/examples/simple/igraph_minimum_spanning_tree.out @@ -0,0 +1,17 @@ +Minimum spanning tree: +0 1 +0 11 +1 3 +2 10 +3 4 +3 6 +5 10 +6 7 +7 8 +8 9 +10 11 + +Maximum spanning tree edges: +0 1 4 13 15 11 2 10 9 7 17 + +Total maximum spanning tree weight: 102.5 diff --git a/examples/simple/igraph_motifs_randesu.c b/examples/simple/igraph_motifs_randesu.c new file mode 100644 index 0000000..f030b7f --- /dev/null +++ b/examples/simple/igraph_motifs_randesu.c @@ -0,0 +1,54 @@ + +#include + +/* This is a callback function suitable for use with igraph_motifs_randesu_callback(). + * It prints each motif it is calld with. */ +igraph_error_t print_motif(const igraph_t *graph, igraph_vector_int_t *vids, + igraph_integer_t isoclass, void* extra) { + printf("Found isoclass %2" IGRAPH_PRId ": ", isoclass); + igraph_vector_int_print(vids); + return IGRAPH_SUCCESS; /* Return 'IGRAPH_SUCCESS': do not interrupt the search. */ +} + +int main(void) { + + igraph_t graph; + igraph_vector_t hist; + igraph_real_t zeros[] = { 0.0, 0.0, 0.0, 0.0, 0.0 }; + igraph_vector_t cut_prob; + + /* Compute the 4-motif distritbuion in Zachary's karate club network. */ + + igraph_famous(&graph, "Zachary"); + igraph_vector_init(&hist, 0); + + igraph_motifs_randesu(&graph, &hist, 4, igraph_vector_view(&cut_prob, zeros, 4)); + + /* Compute the total number of motifs (connected 4-vertex subgraphs) + * so that we can print the normalized distribution. */ + igraph_real_t sum = 0.0; + igraph_integer_t n = igraph_vector_size(&hist); + for (igraph_integer_t i=0; i < n; i++) { + if (!isnan(VECTOR(hist)[i])) { + sum += VECTOR(hist)[i]; + } + } + printf("4-motif distribution:\n"); + for (igraph_integer_t i=0; i < n; i++) { + /* Print NaN values in a platform-independent manner: */ + igraph_real_printf(VECTOR(hist)[i] / sum); + printf(" "); + } + printf("\n\n"); + + igraph_vector_destroy(&hist); + igraph_destroy(&graph); + + /* Identify the vertices of each three-motif in a small Kautz graph. */ + + igraph_kautz(&graph, 2, 1); + igraph_motifs_randesu_callback(&graph, 3, igraph_vector_view(&cut_prob, zeros, 3), &print_motif, NULL); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_motifs_randesu.out b/examples/simple/igraph_motifs_randesu.out new file mode 100644 index 0000000..bff9d2b --- /dev/null +++ b/examples/simple/igraph_motifs_randesu.out @@ -0,0 +1,17 @@ +4-motif distribution: +NaN NaN NaN NaN 0.464664 NaN 0.288193 0.191282 0.0152349 0.0359712 0.0046551 + +Found isoclass 5: 0 4 2 +Found isoclass 11: 0 4 3 +Found isoclass 9: 0 4 1 +Found isoclass 9: 0 3 2 +Found isoclass 5: 0 3 5 +Found isoclass 9: 0 2 1 +Found isoclass 5: 0 2 5 +Found isoclass 11: 1 5 2 +Found isoclass 9: 1 5 4 +Found isoclass 5: 1 5 3 +Found isoclass 5: 1 4 2 +Found isoclass 5: 1 4 3 +Found isoclass 9: 2 5 3 +Found isoclass 9: 3 5 4 diff --git a/examples/simple/igraph_neighbors.c b/examples/simple/igraph_neighbors.c new file mode 100644 index 0000000..4eb6581 --- /dev/null +++ b/examples/simple/igraph_neighbors.c @@ -0,0 +1,49 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_vector_int_t v; + + igraph_vector_int_init(&v, 0); + igraph_small(&g, 4, IGRAPH_DIRECTED, 0,1, 1,2, 2,3, 2,2, -1); + + igraph_neighbors(&g, &v, 2, IGRAPH_OUT); + igraph_vector_int_sort(&v); + igraph_vector_int_print(&v); + + igraph_neighbors(&g, &v, 2, IGRAPH_IN); + igraph_vector_int_sort(&v); + igraph_vector_int_print(&v); + + igraph_neighbors(&g, &v, 2, IGRAPH_ALL); + igraph_vector_int_sort(&v); + igraph_vector_int_print(&v); + + igraph_vector_int_destroy(&v); + igraph_destroy(&g); + return 0; +} diff --git a/examples/simple/igraph_neighbors.out b/examples/simple/igraph_neighbors.out new file mode 100644 index 0000000..cba5b77 --- /dev/null +++ b/examples/simple/igraph_neighbors.out @@ -0,0 +1,3 @@ +2 3 +1 2 +1 2 2 3 diff --git a/examples/simple/igraph_pagerank.c b/examples/simple/igraph_pagerank.c new file mode 100644 index 0000000..eb5976a --- /dev/null +++ b/examples/simple/igraph_pagerank.c @@ -0,0 +1,36 @@ + +#include +#include + +int main(void) { + igraph_t graph; + igraph_vector_t pagerank; + igraph_real_t value; + + /* Create a directed graph */ + igraph_kautz(&graph, 2, 3); + + /* Initialize the vector where the results will be stored */ + igraph_vector_init(&pagerank, 0); + + igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, + &pagerank, &value, + igraph_vss_all(), IGRAPH_DIRECTED, + /* damping */ 0.85, /* weights */ NULL, + NULL /* not needed with PRPACK method */); + + /* Check that the eigenvalue is 1, as expected. */ + if (fabs(value - 1.0) > 32*DBL_EPSILON) { + fprintf(stderr, "PageRank failed to converge.\n"); + return 1; + } + + /* Output the result */ + igraph_vector_print(&pagerank); + + /* Destroy data structure when no longer needed */ + igraph_vector_destroy(&pagerank); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_power_law_fit.c b/examples/simple/igraph_power_law_fit.c new file mode 100644 index 0000000..48f86e4 --- /dev/null +++ b/examples/simple/igraph_power_law_fit.c @@ -0,0 +1,64 @@ +/* -*- 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 + +*/ + +#include + +int main(void) { + igraph_t g; + igraph_vector_t degree; + igraph_plfit_result_t model; + + /* Seed random number generator to ensure reproducibility. */ + igraph_rng_seed(igraph_rng_default(), 42); + + /* Generate a BA network; degree distribution is supposed to be a power-law + * if the graph is large enough */ + igraph_barabasi_game( + &g, 10000, /*power=*/ 1, /*m=*/ 2, + /* outseq= */ 0, /* outpref= */ 0, /*A=*/ 1, + IGRAPH_UNDIRECTED, IGRAPH_BARABASI_BAG, + /*start_from=*/ 0 + ); + + /* Get the vertex degrees. We use igraph_strength() because it stores its + * result in an igraph_vector_t */ + igraph_vector_init(°ree, 0); + igraph_strength(&g, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS, 0); + + /* Fit a power-law to the degrees */ + igraph_power_law_fit( + °ree, &model, /* xmin = */ -1, + /* force_continuous = */ 0 + ); + + /* If you also need a p-value: */ + /* igraph_plfit_result_calculate_p_value(&model, &p, 0.001); */ + + printf("alpha = %.5f\n", model.alpha); + printf("xmin = %.5f\n", model.xmin); + printf("log-likelihood = %.5f\n", model.L); + + igraph_vector_destroy(°ree); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_power_law_fit.out b/examples/simple/igraph_power_law_fit.out new file mode 100644 index 0000000..d24ecd2 --- /dev/null +++ b/examples/simple/igraph_power_law_fit.out @@ -0,0 +1,3 @@ +alpha = 3.04393 +xmin = 7.00000 +log-likelihood = -3103.87560 diff --git a/examples/simple/igraph_radius.c b/examples/simple/igraph_radius.c new file mode 100644 index 0000000..fd960a4 --- /dev/null +++ b/examples/simple/igraph_radius.c @@ -0,0 +1,54 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sts=4 sw=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 + +int main(void) { + + igraph_t g; + igraph_real_t radius; + + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_radius(&g, &radius, IGRAPH_OUT); + if (radius != 1) { + return 1; + } + igraph_destroy(&g); + + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_radius(&g, &radius, IGRAPH_ALL); + if (radius != 1) { + return 2; + } + igraph_destroy(&g); + + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_radius(&g, &radius, IGRAPH_OUT); + if (radius != 0) { + return 3; + } + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_random_sample.c b/examples/simple/igraph_random_sample.c new file mode 100644 index 0000000..fedb7bd --- /dev/null +++ b/examples/simple/igraph_random_sample.c @@ -0,0 +1,30 @@ +/* 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, see . +*/ + +#include + +int main(void) { + igraph_vector_int_t V; + + igraph_vector_int_init(&V, 0); + + igraph_random_sample(&V, 0, 100, 5); + + igraph_vector_int_print(&V); + + igraph_vector_int_destroy(&V); +} diff --git a/examples/simple/igraph_read_graph_dl.c b/examples/simple/igraph_read_graph_dl.c new file mode 100644 index 0000000..e20c3ac --- /dev/null +++ b/examples/simple/igraph_read_graph_dl.c @@ -0,0 +1,59 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 + 02110-1301 USA + +*/ + +#include +#include +#include + +int main(void) { + + const char *files[] = { "fullmatrix1.dl", "fullmatrix2.dl", + "fullmatrix3.dl", "fullmatrix4.dl", + "edgelist1.dl", "edgelist2.dl", "edgelist3.dl", + "edgelist4.dl", "edgelist5.dl", "edgelist6.dl", + "edgelist7.dl", "nodelist1.dl", "nodelist2.dl" }; + igraph_t graph; + FILE *infile; + + /* Turn on attribute handling. */ + igraph_set_attribute_table(&igraph_cattribute_table); + + for (size_t i = 0; i < sizeof(files) / sizeof(files[0]); i++) { + printf("Doing %s\n", files[i]); + infile = fopen(files[i], "r"); + if (!infile) { + printf("Cannot open file: %s\n", files[i]); + abort(); + } + igraph_read_graph_dl(&graph, infile, IGRAPH_DIRECTED); + fclose(infile); + igraph_write_graph_edgelist(&graph, stdout); + igraph_destroy(&graph); + } + + if (IGRAPH_FINALLY_STACK_SIZE() != 0) { + return 1; + } + + return 0; +} diff --git a/examples/simple/igraph_read_graph_dl.out b/examples/simple/igraph_read_graph_dl.out new file mode 100644 index 0000000..b62cc2f --- /dev/null +++ b/examples/simple/igraph_read_graph_dl.out @@ -0,0 +1,104 @@ +Doing fullmatrix1.dl +0 1 +0 2 +0 3 +0 4 +1 0 +1 2 +2 0 +2 1 +2 4 +3 0 +4 0 +4 2 +Doing fullmatrix2.dl +0 1 +0 2 +0 3 +1 0 +1 4 +2 0 +2 3 +3 0 +3 2 +3 4 +4 1 +4 3 +Doing fullmatrix3.dl +0 1 +0 2 +0 3 +1 0 +1 4 +2 0 +2 3 +3 0 +3 2 +3 4 +4 1 +4 3 +Doing fullmatrix4.dl +0 1 +0 2 +0 3 +1 0 +1 4 +2 0 +2 3 +3 0 +3 2 +3 4 +4 1 +4 3 +Doing edgelist1.dl +0 1 +0 2 +1 2 +2 0 +3 2 +Doing edgelist2.dl +0 1 +0 2 +1 2 +3 0 +4 2 +Doing edgelist3.dl +0 1 +0 2 +1 2 +3 0 +4 2 +Doing edgelist4.dl +0 1 +0 2 +1 2 +2 0 +3 2 +Doing edgelist5.dl +0 1 +0 2 +1 2 +3 0 +4 2 +Doing edgelist6.dl +0 1 +0 2 +1 2 +3 0 +4 2 +Doing edgelist7.dl +0 1 +1 2 +1 3 +Doing nodelist1.dl +0 1 +0 2 +1 2 +2 0 +3 2 +Doing nodelist2.dl +0 1 +0 2 +1 2 +3 0 +4 2 diff --git a/examples/simple/igraph_read_graph_graphdb.c b/examples/simple/igraph_read_graph_graphdb.c new file mode 100644 index 0000000..3df6be3 --- /dev/null +++ b/examples/simple/igraph_read_graph_graphdb.c @@ -0,0 +1,41 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + FILE *input; + + input = fopen("iso_b03_m1000.A00", "rb"); + if (!input) { + return 1; + } + igraph_read_graph_graphdb(&g, input, IGRAPH_DIRECTED); + fclose(input); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_read_graph_graphdb.out b/examples/simple/igraph_read_graph_graphdb.out new file mode 100644 index 0000000..ec9aa8e --- /dev/null +++ b/examples/simple/igraph_read_graph_graphdb.out @@ -0,0 +1,1500 @@ +0 723 +1 37 +1 951 +3 310 +3 439 +4 64 +4 873 +6 875 +7 617 +8 0 +8 429 +9 711 +10 181 +10 184 +11 257 +11 262 +11 365 +12 400 +12 781 +13 61 +13 482 +13 963 +15 68 +15 567 +16 649 +17 93 +17 355 +18 32 +18 503 +19 53 +19 360 +19 646 +20 71 +20 220 +21 900 +21 909 +22 552 +22 778 +25 623 +25 731 +27 232 +28 293 +28 378 +29 179 +30 542 +30 713 +31 535 +32 312 +33 246 +33 828 +33 868 +34 230 +35 382 +35 519 +36 321 +37 767 +38 216 +38 658 +39 400 +39 889 +40 352 +40 941 +41 60 +41 540 +42 680 +44 280 +44 734 +45 520 +46 302 +46 940 +46 959 +47 928 +49 149 +50 29 +50 216 +50 658 +52 758 +53 455 +53 514 +54 500 +55 51 +55 506 +56 647 +57 352 +57 821 +58 945 +58 953 +59 320 +59 393 +60 157 +62 200 +62 669 +64 886 +65 69 +65 638 +66 390 +66 537 +67 981 +69 42 +70 7 +70 759 +70 991 +71 244 +72 137 +72 450 +72 932 +73 980 +74 819 +75 508 +75 973 +76 295 +76 573 +76 838 +78 778 +79 290 +79 627 +80 981 +81 712 +82 907 +83 17 +83 109 +83 125 +85 97 +86 239 +86 390 +86 850 +87 185 +87 260 +87 652 +88 24 +88 522 +88 614 +89 682 +90 302 +92 164 +95 465 +95 601 +96 23 +96 822 +97 895 +98 195 +98 241 +98 899 +99 367 +99 392 +99 749 +101 811 +102 696 +103 423 +104 55 +104 147 +104 879 +105 68 +105 161 +105 931 +106 318 +106 379 +107 20 +107 816 +108 240 +108 718 +108 883 +109 355 +110 130 +110 337 +111 89 +111 499 +112 91 +112 236 +112 835 +113 217 +113 668 +113 675 +114 199 +114 453 +114 830 +115 607 +116 854 +117 651 +117 710 +118 100 +118 511 +119 347 +119 812 +120 490 +121 521 +121 632 +121 982 +122 16 +122 130 +124 251 +124 644 +124 986 +126 26 +126 727 +126 918 +127 437 +128 194 +128 327 +129 595 +129 639 +131 488 +131 708 +132 60 +132 538 +133 5 +133 203 +134 179 +134 258 +134 913 +135 271 +135 842 +136 696 +136 858 +136 937 +137 273 +138 930 +139 432 +139 654 +139 959 +140 329 +141 627 +141 923 +144 715 +144 939 +145 513 +145 967 +147 2 +147 51 +148 704 +148 746 +150 944 +151 358 +151 611 +152 460 +154 77 +155 190 +155 953 +156 441 +156 962 +156 997 +157 833 +158 508 +158 973 +159 4 +159 423 +162 495 +162 995 +163 173 +164 146 +165 142 +165 187 +165 928 +166 676 +166 804 +167 177 +167 377 +168 175 +168 410 +170 917 +171 473 +172 898 +173 880 +174 177 +175 579 +175 779 +176 243 +176 500 +178 24 +178 84 +180 201 +180 341 +180 813 +181 876 +182 106 +183 202 +185 417 +185 894 +187 47 +188 263 +188 275 +188 786 +189 460 +189 683 +190 58 +190 576 +191 116 +191 324 +192 424 +192 464 +192 670 +193 614 +193 697 +194 829 +195 370 +196 132 +196 952 +197 49 +197 511 +198 506 +198 633 +200 158 +200 775 +201 433 +201 780 +202 394 +202 632 +203 267 +203 855 +204 214 +204 805 +205 990 +206 577 +206 806 +207 705 +208 804 +209 700 +210 764 +211 210 +212 560 +212 575 +212 899 +213 817 +214 110 +215 62 +215 556 +216 664 +217 471 +219 381 +219 645 +219 653 +221 103 +221 409 +221 873 +222 123 +222 420 +223 838 +224 371 +225 479 +225 840 +226 513 +226 761 +226 967 +228 128 +228 346 +228 489 +229 218 +229 459 +230 218 +230 459 +231 135 +231 845 +232 943 +233 391 +233 446 +234 275 +234 786 +235 384 +235 993 +236 635 +236 882 +237 163 +237 824 +238 138 +238 808 +240 259 +241 350 +241 543 +242 238 +242 272 +243 54 +243 319 +244 220 +244 761 +245 628 +245 872 +246 341 +247 273 +248 263 +248 691 +249 752 +250 161 +251 227 +252 565 +253 97 +253 251 +253 414 +254 225 +254 789 +255 407 +255 451 +256 183 +257 457 +259 590 +260 672 +260 894 +261 623 +262 390 +263 527 +264 684 +265 821 +266 507 +266 815 +267 5 +267 307 +268 111 +268 856 +269 616 +270 261 +270 693 +270 742 +271 845 +272 808 +272 935 +274 45 +274 998 +276 16 +276 130 +277 224 +278 572 +279 608 +279 630 +281 207 +282 440 +283 34 +283 117 +285 674 +286 952 +287 73 +287 358 +288 737 +289 933 +290 174 +291 758 +292 283 +293 434 +293 655 +294 38 +296 300 +297 322 +297 892 +298 154 +299 515 +299 864 +300 960 +301 222 +303 127 +303 288 +303 978 +304 314 +304 557 +305 936 +306 9 +306 94 +307 301 +307 420 +309 997 +310 856 +311 634 +312 921 +313 865 +314 450 +315 186 +315 249 +316 199 +317 659 +318 28 +318 434 +319 578 +319 849 +321 205 +322 142 +322 187 +323 618 +323 774 +324 211 +325 699 +325 753 +325 758 +326 399 +327 489 +328 170 +328 597 +329 621 +330 887 +331 64 +331 167 +332 240 +332 883 +333 122 +333 155 +333 649 +334 329 +335 561 +335 608 +335 985 +336 825 +337 276 +338 51 +338 506 +338 891 +339 446 +339 536 +340 107 +340 630 +342 48 +342 61 +343 84 +343 406 +343 533 +344 696 +345 936 +346 920 +348 502 +348 985 +350 575 +351 559 +351 799 +352 265 +353 677 +353 948 +354 718 +356 653 +356 655 +357 661 +359 138 +359 242 +360 455 +362 82 +362 486 +363 285 +363 345 +363 925 +364 289 +364 686 +364 898 +366 504 +366 866 +366 885 +368 92 +369 211 +370 628 +370 897 +371 868 +372 6 +372 993 +373 357 +373 799 +374 68 +374 161 +375 213 +375 636 +376 977 +377 620 +378 653 +379 277 +380 24 +380 84 +380 533 +382 186 +382 320 +383 341 +383 813 +384 372 +384 875 +385 626 +386 127 +386 288 +386 621 +387 103 +387 409 +388 481 +388 870 +389 153 +389 388 +391 339 +393 151 +393 287 +394 481 +395 280 +396 208 +396 890 +397 408 +398 0 +398 846 +399 264 +399 974 +401 74 +402 898 +403 229 +404 160 +404 412 +405 404 +405 915 +406 321 +407 869 +407 948 +411 518 +411 541 +411 994 +412 818 +413 63 +414 85 +414 227 +415 143 +415 580 +415 764 +416 96 +416 385 +417 347 +417 812 +418 77 +418 298 +418 619 +419 266 +420 5 +421 143 +422 514 +424 633 +426 171 +426 785 +427 40 +427 52 +427 265 +430 512 +431 474 +431 881 +432 90 +434 182 +435 249 +435 487 +436 129 +436 751 +437 334 +437 621 +438 444 +439 466 +439 983 +440 120 +440 146 +441 295 +441 309 +442 419 +442 507 +442 867 +443 848 +444 381 +444 572 +445 324 +445 369 +445 954 +447 397 +447 798 +448 182 +448 224 +448 379 +449 170 +450 273 +452 629 +452 656 +452 933 +453 488 +453 708 +454 80 +454 472 +454 765 +455 730 +456 56 +457 89 +457 499 +458 22 +458 78 +459 839 +461 360 +461 730 +462 137 +462 247 +463 101 +463 368 +464 528 +465 747 +466 391 +466 531 +467 304 +467 693 +468 181 +468 184 +468 826 +469 6 +469 498 +470 523 +470 878 +471 675 +471 995 +472 776 +472 919 +473 740 +474 347 +474 666 +475 397 +475 798 +476 401 +476 518 +477 532 +477 981 +478 172 +478 402 +480 754 +481 153 +482 342 +482 610 +483 152 +483 612 +483 741 +484 18 +484 724 +484 796 +485 892 +485 938 +486 323 +486 907 +487 315 +490 282 +491 61 +491 902 +492 421 +492 550 +493 330 +493 701 +494 827 +495 996 +496 750 +496 904 +497 745 +497 950 +500 629 +501 493 +501 871 +502 239 +503 724 +503 905 +504 308 +504 430 +505 346 +505 489 +507 725 +508 775 +509 331 +509 377 +509 886 +510 91 +510 150 +511 149 +512 308 +512 549 +514 646 +515 94 +515 801 +516 465 +516 601 +517 271 +517 361 +518 906 +519 320 +521 256 +522 193 +523 37 +524 769 +524 809 +524 969 +525 969 +526 23 +526 416 +527 275 +527 691 +528 424 +529 100 +529 428 +529 494 +530 389 +531 233 +531 983 +532 67 +532 485 +533 984 +534 21 +534 460 +534 683 +536 462 +537 262 +537 365 +538 157 +539 27 +540 145 +540 662 +541 866 +542 714 +543 195 +544 220 +544 967 +545 35 +545 186 +545 487 +546 635 +546 732 +547 387 +547 681 +547 900 +548 141 +548 988 +549 359 +549 930 +550 39 +551 208 +551 890 +552 823 +552 939 +553 269 +554 362 +554 478 +555 464 +555 667 +556 928 +557 693 +558 565 +560 349 +560 525 +561 279 +562 298 +562 974 +563 559 +563 799 +564 517 +565 859 +566 648 +567 759 +568 539 +568 660 +569 566 +569 602 +569 659 +570 120 +570 146 +571 7 +571 398 +572 877 +573 223 +574 9 +575 525 +576 848 +578 654 +579 410 +579 908 +580 421 +581 741 +582 739 +582 943 +583 305 +584 539 +584 660 +584 726 +585 206 +586 610 +587 204 +587 687 +588 376 +588 480 +588 859 +589 282 +589 425 +589 690 +590 354 +591 78 +591 330 +592 947 +593 578 +593 849 +593 926 +594 1 +594 523 +595 422 +595 646 +597 449 +597 687 +598 210 +598 369 +599 194 +599 367 +600 231 +600 305 +601 688 +602 317 +603 618 +604 596 +604 927 +605 168 +605 694 +605 745 +606 43 +606 344 +607 698 +607 737 +608 348 +609 336 +610 48 +611 59 +611 519 +612 581 +612 862 +613 173 +613 237 +613 609 +614 178 +615 577 +615 851 +616 306 +616 574 +617 863 +619 562 +619 684 +620 177 +620 290 +622 590 +622 732 +624 902 +624 963 +624 988 +625 82 +625 172 +625 554 +626 596 +627 174 +628 965 +629 976 +630 782 +631 559 +631 837 +632 256 +633 670 +634 827 +635 622 +637 692 +638 42 +639 422 +640 313 +640 832 +640 982 +641 280 +641 505 +642 456 +642 637 +642 647 +643 522 +644 227 +644 698 +645 356 +648 45 +648 966 +649 953 +650 73 +650 250 +651 34 +651 218 +652 443 +652 672 +655 378 +656 289 +656 976 +657 302 +657 395 +659 840 +660 643 +661 910 +662 513 +663 469 +663 875 +663 884 +664 26 +664 294 +665 133 +665 603 +666 119 +666 881 +667 435 +667 752 +668 413 +668 802 +669 47 +669 556 +670 891 +671 470 +671 637 +673 255 +673 677 +673 948 +674 209 +674 345 +675 413 +676 14 +676 831 +678 392 +678 749 +679 311 +679 964 +681 409 +682 268 +682 310 +685 376 +685 480 +685 795 +686 402 +687 695 +688 837 +689 535 +689 847 +690 490 +690 716 +691 75 +692 456 +692 946 +694 410 +694 497 +695 328 +697 568 +697 643 +699 140 +699 334 +700 123 +700 285 +701 591 +702 91 +702 150 +702 835 +703 101 +704 615 +704 999 +705 433 +705 780 +706 400 +706 550 +706 781 +707 10 +707 869 +707 888 +708 797 +709 30 +709 43 +710 292 +710 729 +711 94 +711 299 +712 277 +712 371 +713 43 +714 743 +715 821 +715 922 +716 403 +716 425 +717 553 +718 259 +719 26 +719 294 +719 727 +720 154 +720 510 +721 902 +721 988 +722 340 +722 782 +722 816 +723 429 +723 832 +724 968 +725 647 +725 815 +726 27 +726 793 +727 284 +728 164 +728 570 +728 958 +729 581 +729 862 +730 235 +731 261 +731 742 +732 354 +733 169 +733 281 +733 803 +734 395 +735 171 +735 596 +735 785 +736 118 +736 149 +736 844 +737 978 +738 528 +738 555 +739 196 +739 538 +740 426 +740 746 +742 557 +743 357 +744 542 +744 709 +744 792 +745 160 +747 592 +748 760 +748 784 +748 990 +749 599 +750 586 +751 153 +751 530 +752 738 +753 140 +753 291 +754 677 +754 795 +755 636 +755 678 +755 756 +756 375 +756 392 +757 179 +757 258 +759 617 +760 36 +760 205 +761 544 +762 246 +762 383 +763 566 +763 602 +763 966 +765 23 +766 14 +766 717 +766 971 +769 361 +769 564 +770 63 +770 463 +770 703 +771 214 +771 337 +772 269 +772 574 +772 971 +773 548 +773 721 +774 603 +774 855 +775 215 +776 80 +776 477 +777 317 +777 479 +778 701 +779 197 +780 281 +781 431 +782 561 +783 25 +783 258 +783 913 +784 85 +784 895 +785 604 +787 143 +787 598 +787 764 +788 199 +788 638 +788 957 +789 2 +789 479 +790 606 +790 713 +791 381 +791 438 +791 645 +792 714 +792 910 +793 232 +793 286 +794 223 +794 872 +795 353 +796 968 +797 234 +798 326 +800 473 +800 746 +801 274 +802 63 +802 703 +803 207 +803 871 +804 831 +805 771 +805 807 +807 587 +807 695 +808 125 +809 349 +809 361 +810 131 +810 786 +810 797 +811 368 +811 958 +813 433 +814 152 +814 189 +814 741 +815 56 +816 71 +817 74 +817 906 +818 93 +818 405 +819 213 +819 636 +820 296 +820 975 +822 385 +822 927 +823 144 +823 922 +824 296 +824 975 +825 768 +826 102 +827 934 +828 81 +828 762 +829 327 +829 367 +830 316 +830 488 +831 396 +832 865 +833 41 +833 662 +834 29 +834 658 +834 757 +835 882 +836 31 +836 585 +836 806 +837 95 +839 403 +839 425 +840 777 +841 313 +841 521 +842 583 +842 600 +843 428 +843 494 +843 634 +844 100 +844 428 +845 564 +846 8 +847 332 +847 857 +848 672 +849 54 +850 66 +850 502 +851 148 +851 800 +852 516 +852 747 +852 947 +853 336 +853 768 +854 284 +854 954 +855 665 +856 942 +857 535 +858 102 +860 419 +860 750 +861 408 +861 475 +861 592 +862 292 +863 571 +863 846 +864 801 +864 998 +865 429 +866 994 +867 496 +867 860 +868 81 +869 451 +870 183 +870 394 +871 169 +872 965 +873 423 +874 31 +874 689 +874 806 +876 826 +876 858 +877 438 +877 929 +878 594 +879 2 +879 254 +880 609 +880 825 +881 12 +882 546 +883 857 +884 498 +885 430 +885 541 +886 159 +887 169 +887 501 +888 184 +888 451 +889 492 +889 580 +890 278 +891 198 +892 67 +893 768 +893 956 +893 979 +894 812 +896 257 +896 365 +896 499 +897 543 +899 350 +901 14 +901 166 +901 717 +903 247 +903 446 +903 536 +904 48 +904 586 +905 32 +906 401 +907 618 +908 49 +908 779 +909 683 +909 912 +910 743 +911 123 +911 209 +911 301 +912 681 +912 900 +913 623 +914 162 +914 498 +915 93 +915 355 +916 245 +916 794 +916 838 +917 968 +918 116 +918 284 +919 526 +919 765 +920 44 +920 641 +921 300 +921 820 +922 57 +923 79 +923 773 +924 351 +924 631 +924 688 +925 583 +925 936 +926 432 +926 654 +927 626 +929 278 +929 551 +930 308 +931 15 +931 991 +932 314 +932 467 +933 686 +934 311 +934 964 +935 109 +935 125 +937 344 +937 790 +938 142 +938 297 +939 458 +940 657 +940 734 +941 52 +941 291 +942 3 +943 286 +944 77 +944 720 +945 443 +945 576 +946 671 +946 878 +947 408 +949 217 +949 495 +950 160 +950 412 +951 309 +951 767 +952 582 +954 191 +955 650 +956 252 +956 558 +957 65 +957 316 +958 92 +959 90 +960 312 +960 905 +961 264 +961 326 +961 447 +962 295 +962 573 +963 491 +964 680 +965 897 +966 520 +969 349 +970 69 +970 679 +970 680 +971 553 +972 436 +972 530 +972 639 +973 248 +974 684 +975 163 +976 176 +977 252 +977 859 +978 115 +979 558 +979 853 +980 358 +980 955 +982 841 +983 942 +984 36 +984 406 +985 239 +986 115 +986 698 +987 449 +987 796 +987 917 +989 250 +989 374 +989 955 +990 895 +991 567 +992 373 +992 563 +992 661 +993 461 +994 476 +995 949 +996 884 +996 914 +997 767 +998 520 +999 577 +999 585 diff --git a/examples/simple/igraph_read_graph_lgl-1.lgl b/examples/simple/igraph_read_graph_lgl-1.lgl new file mode 100644 index 0000000..d85f4dc --- /dev/null +++ b/examples/simple/igraph_read_graph_lgl-1.lgl @@ -0,0 +1,7 @@ +# foo +bar +foobar 5 +# foobar +bat +tab +# tab diff --git a/examples/simple/igraph_read_graph_lgl-2.lgl b/examples/simple/igraph_read_graph_lgl-2.lgl new file mode 100644 index 0000000..9834c88 --- /dev/null +++ b/examples/simple/igraph_read_graph_lgl-2.lgl @@ -0,0 +1,7 @@ +# foo +bar 1 +foobar 2 +# foobar +bat 10 +tab +# tab diff --git a/examples/simple/igraph_read_graph_lgl-3.lgl b/examples/simple/igraph_read_graph_lgl-3.lgl new file mode 100644 index 0000000..f293023 --- /dev/null +++ b/examples/simple/igraph_read_graph_lgl-3.lgl @@ -0,0 +1,4 @@ +# +1 +# 1 +2 diff --git a/examples/simple/igraph_read_graph_lgl.c b/examples/simple/igraph_read_graph_lgl.c new file mode 100644 index 0000000..b3b8abf --- /dev/null +++ b/examples/simple/igraph_read_graph_lgl.c @@ -0,0 +1,83 @@ +/* -*- mode: C -*- */ +/* + IGraph R package. + Copyright (C) 2005-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + FILE *input; + + /* Turn on attribute handling. */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* Without names and weights */ + input = fopen("igraph_read_graph_lgl-1.lgl", "r"); + if (!input) { + return 1; + } + igraph_read_graph_lgl(&g, input, 0, IGRAPH_ADD_WEIGHTS_NO, 1); + fclose(input); + if (!igraph_is_directed(&g)) { + return 2; + } + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + /* With names and weights */ + input = fopen("igraph_read_graph_lgl-2.lgl", "r"); + if (!input) { + return 3; + } + igraph_read_graph_lgl(&g, input, 0, IGRAPH_ADD_WEIGHTS_NO, 1); + fclose(input); + if (!igraph_is_directed(&g)) { + return 4; + } + igraph_write_graph_ncol(&g, stdout, 0, 0); + igraph_destroy(&g); + + /* Same graph, but forcing undirected mode */ + input = fopen("igraph_read_graph_lgl-2.lgl", "r"); + igraph_read_graph_lgl(&g, input, 0, IGRAPH_ADD_WEIGHTS_NO, 0); + fclose(input); + if (igraph_is_directed(&g)) { + return 5; + } + igraph_write_graph_ncol(&g, stdout, 0, 0); + igraph_destroy(&g); + + /* Erroneous LGL file (empty vertex name) */ + input = fopen("igraph_read_graph_lgl-3.lgl", "r"); + if (!input) { + return 6; + } + igraph_set_error_handler(igraph_error_handler_ignore); + if (igraph_read_graph_lgl(&g, input, 0, IGRAPH_ADD_WEIGHTS_NO, 1) != + IGRAPH_PARSEERROR) { + return 7; + } + fclose(input); + + return 0; +} diff --git a/examples/simple/igraph_read_graph_lgl.out b/examples/simple/igraph_read_graph_lgl.out new file mode 100644 index 0000000..036899b --- /dev/null +++ b/examples/simple/igraph_read_graph_lgl.out @@ -0,0 +1,12 @@ +0 1 +0 2 +2 3 +2 4 +0 1 +0 2 +2 3 +2 4 +0 1 +0 2 +2 3 +2 4 diff --git a/examples/simple/igraph_realize_degree_sequence.c b/examples/simple/igraph_realize_degree_sequence.c new file mode 100644 index 0000000..9b7b6b9 --- /dev/null +++ b/examples/simple/igraph_realize_degree_sequence.c @@ -0,0 +1,38 @@ +#include +#include + +int main(void){ + igraph_t g1, g2, g3; + igraph_integer_t nodes = 500, A = 0, power = 1, m = 1; + igraph_real_t assortativity; + + igraph_rng_seed(igraph_rng_default(), 42); + printf("Demonstration of difference in assortativities of graphs with the same degree sequence but different linkages:\n\nInitial graph based on the Barabasi-Albert model with %" IGRAPH_PRId " nodes.\n", nodes); + + /* Graph 1 generated by a randomized graph generator */ + igraph_barabasi_game(&g1, nodes, power, m, NULL, /* outpref */ 0, A, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE, /* start from */ NULL); + + igraph_vector_int_t degree; + igraph_vector_int_init(°ree, nodes); + igraph_degree(&g1, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS); + + /* Measuring assortativity of the first graph */ + igraph_assortativity_degree(&g1, &assortativity, IGRAPH_UNDIRECTED); + printf("Assortativity of initial graph = %g\n\n", assortativity); + igraph_destroy(&g1); + + /* Graph 2 (with the same degree sequence) generated by selecting vertices with the smallest degree first */ + igraph_realize_degree_sequence(&g2, °ree, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + igraph_assortativity_degree(&g2, &assortativity, IGRAPH_UNDIRECTED); + printf("Assortativity after choosing vertices with the smallest degrees first = %g\n\n", assortativity); + igraph_destroy(&g2); + + /* Graph 3 (with the same degree sequence) generated by selecting vertices with the largest degree first */ + igraph_realize_degree_sequence(&g3, °ree, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + igraph_assortativity_degree(&g3, &assortativity, IGRAPH_UNDIRECTED); + printf("Assortativity after choosing vertices with the largest degrees first = %g\n", assortativity); + igraph_destroy(&g3); + igraph_vector_int_destroy(°ree); + + return 0; +} diff --git a/examples/simple/igraph_realize_degree_sequence.out b/examples/simple/igraph_realize_degree_sequence.out new file mode 100644 index 0000000..d0c743b --- /dev/null +++ b/examples/simple/igraph_realize_degree_sequence.out @@ -0,0 +1,8 @@ +Demonstration of difference in assortativities of graphs with the same degree sequence but different linkages: + +Initial graph based on the Barabasi-Albert model with 500 nodes. +Assortativity of initial graph = -0.171365 + +Assortativity after choosing vertices with the smallest degrees first = -0.262756 + +Assortativity after choosing vertices with the largest degrees first = 0.366441 diff --git a/examples/simple/igraph_reciprocity.c b/examples/simple/igraph_reciprocity.c new file mode 100644 index 0000000..ca18dc2 --- /dev/null +++ b/examples/simple/igraph_reciprocity.c @@ -0,0 +1,65 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + + igraph_t g; + igraph_real_t res; + + /* Trivial cases */ + + igraph_ring(&g, 100, IGRAPH_UNDIRECTED, 0, 0); + igraph_reciprocity(&g, &res, 0, IGRAPH_RECIPROCITY_DEFAULT); + igraph_destroy(&g); + + if (res != 1) { + return 1; + } + + /* Small test graph */ + + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 3, 1, 0, 2, 3, 3, 2, -1); + + igraph_reciprocity(&g, &res, 0, IGRAPH_RECIPROCITY_RATIO); + igraph_destroy(&g); + + if (res != 0.5) { + fprintf(stderr, "%f != %f\n", res, 0.5); + return 2; + } + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 1, -1); + igraph_reciprocity(&g, &res, 0, IGRAPH_RECIPROCITY_DEFAULT); + igraph_destroy(&g); + + if (fabs(res - 2.0 / 3.0) > 1e-15) { + fprintf(stderr, "%f != %f\n", res, 2.0 / 3.0); + return 3; + } + + return 0; +} diff --git a/examples/simple/igraph_regular_tree.c b/examples/simple/igraph_regular_tree.c new file mode 100644 index 0000000..0afa173 --- /dev/null +++ b/examples/simple/igraph_regular_tree.c @@ -0,0 +1,27 @@ + +#include + +int main(void) { + igraph_t tree; + igraph_vector_t eccentricity; + igraph_bool_t is_tree; + + /* Create a Bethe lattice with 5 levels, i.e. height 4. */ + igraph_regular_tree(&tree, 4, 3, IGRAPH_TREE_UNDIRECTED); + + /* Bethe lattices are trees. */ + igraph_is_tree(&tree, &is_tree, NULL, IGRAPH_ALL); + printf("Is it a tree? %s\n", is_tree ? "Yes." : "No."); + + /* Compute and print eccentricities. The root is the most central. */ + igraph_vector_init(&eccentricity, 0); + igraph_eccentricity(&tree, &eccentricity, igraph_vss_all(), IGRAPH_ALL); + printf("Vertex eccentricities:\n"); + igraph_vector_print(&eccentricity); + igraph_vector_destroy(&eccentricity); + + /* Clean up. */ + igraph_destroy(&tree); + + return 0; +} diff --git a/examples/simple/igraph_regular_tree.out b/examples/simple/igraph_regular_tree.out new file mode 100644 index 0000000..187d910 --- /dev/null +++ b/examples/simple/igraph_regular_tree.out @@ -0,0 +1,3 @@ +Is it a tree? Yes. +Vertex eccentricities: +4 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 diff --git a/examples/simple/igraph_ring.c b/examples/simple/igraph_ring.c new file mode 100644 index 0000000..b68e438 --- /dev/null +++ b/examples/simple/igraph_ring.c @@ -0,0 +1,46 @@ +/* + 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 +#include + +int main(void) { + igraph_t graph; + + /* Create a directed path graph on 10 vertices. */ + igraph_ring(&graph, 10, IGRAPH_DIRECTED, /* mutual= */ 0, /* circular= */ 0); + + /* Output the edge list of the graph. */ + printf("10-path graph:\n"); + igraph_write_graph_edgelist(&graph, stdout); + + /* Destroy the graph. */ + igraph_destroy(&graph); + + /* Create a 4-cycle graph. */ + igraph_ring(&graph, 4, IGRAPH_UNDIRECTED, /* mutual= */ 0, /* circular= */ 1); + + /* Output the edge list of the graph. */ + printf("\n4-cycle graph:\n"); + igraph_write_graph_edgelist(&graph, stdout); + + /* Destroy the graph. */ + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_ring.out b/examples/simple/igraph_ring.out new file mode 100644 index 0000000..3cd8a54 --- /dev/null +++ b/examples/simple/igraph_ring.out @@ -0,0 +1,16 @@ +10-path graph: +0 1 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 + +4-cycle graph: +0 1 +0 3 +1 2 +2 3 diff --git a/examples/simple/igraph_roulette_wheel_imitation.c b/examples/simple/igraph_roulette_wheel_imitation.c new file mode 100644 index 0000000..6ceb149 --- /dev/null +++ b/examples/simple/igraph_roulette_wheel_imitation.c @@ -0,0 +1,213 @@ +/* -*- mode: C -*- */ +/* + Test suite for stochastic imitation via roulette wheel selection. + 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 +#include + +#define R_INTEGER(a,b) (igraph_rng_get_integer(igraph_rng_default(), (a), (b))) + +/* test parameters structure */ +typedef struct { + igraph_t *graph; + igraph_integer_t vertex; + igraph_bool_t islocal; + igraph_vector_t *quantities; + igraph_vector_int_t *strategies; + igraph_vector_int_t *known_strats; + igraph_neimode_t mode; + igraph_error_t retval; +} strategy_test_t; + +/* A game on a graph with 5 vertices and 7 edges. Use roulette wheel selection + * to update strategies. This example also illustrates how a choice of + * perspective (whether local or global) could affect the range of + * possible strategies a vertex could adopt. + */ +igraph_error_t roulette_test(void) { + igraph_t g; + igraph_bool_t success; + igraph_vector_t quant; + igraph_vector_int_t *known, strat, stratcopy; + igraph_vector_int_t known0, known1, known2, known3, known4, known5; + igraph_integer_t i, k, n, nvert; + igraph_error_t ret; + strategy_test_t *test; + + /* the game network */ + igraph_small(&g, /*nvert=*/ 0, IGRAPH_UNDIRECTED, + 0, 3, 0, 4, 1, 2, 1, 4, 1, 5, 2, 3, 2, 4, 3, 4, -1); + nvert = igraph_vcount(&g); + /* strategies vector; the strategy space is {0, 1, 2, 3} */ + /* V[i] is strategy of vertex i */ + igraph_vector_int_init_int(&strat, nvert, 1, 0, 1, 2, 0, 3); + /* quantities vector; V[i] is quantity of vertex i */ + igraph_vector_init_real(&quant, nvert, 0.56, 0.13, 0.26, 0.73, 0.67, 0.82); + /* possible strategies each vertex can adopt */ + igraph_vector_int_init_int(&known0, /*n=*/ 3, 0, 1, 2); /* local */ + igraph_vector_int_init_int(&known1, /*n=*/ 3, 0, 1, 3); /* local */ + igraph_vector_int_init_int(&known2, /*n=*/ 3, 0, 1, 2); /* local */ + igraph_vector_int_init_int(&known3, /*n=*/ 3, 0, 1, 2); /* local */ + igraph_vector_int_init_int(&known4, /*n=*/ 3, 0, 1, 2); /* local */ + igraph_vector_int_init_int(&known5, /*n=*/ 4, 0, 1, 2, 3); /* global */ + + /* test parameters */ + /*graph--vert--islocal--quantities--strategies--known_strats--mode-retval*/ + strategy_test_t game0 = {&g, 0, 1, &quant, NULL, &known0, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t game1 = {&g, 1, 1, &quant, NULL, &known1, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t game2 = {&g, 2, 1, &quant, NULL, &known2, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t game3 = {&g, 3, 1, &quant, NULL, &known3, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t game4 = {&g, 4, 1, &quant, NULL, &known4, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t game5 = {&g, 5, 0, &quant, NULL, &known5, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t *all_checks[] = {/* 1 */ &game0, + /* 2 */ &game1, + /* 3 */ &game2, + /* 4 */ &game3, + /* 5 */ &game4, + /* 6 */ &game5 + }; + + /* play game */ + n = 6; + i = 0; + while (i < n) { + test = all_checks[i]; + igraph_vector_int_init_copy(&stratcopy, &strat); + ret = igraph_roulette_wheel_imitation(test->graph, test->vertex, + test->islocal, test->quantities, + &stratcopy, test->mode); + if (ret != test->retval) { + printf("Test no. %" IGRAPH_PRId " failed.\n", i + 1); + return IGRAPH_FAILURE; + } + /* If the revised strategy s matches one of the candidate strategies, */ + /* then success. If s doesn't match any of the possible strategies, then */ + /* failure. Default to failure. */ + success = 0; + known = test->known_strats; + for (k = 0; k < igraph_vector_int_size(known); k++) { + if (VECTOR(*known)[k] == VECTOR(stratcopy)[test->vertex]) { + success = 1; + break; + } + } + if (!success) { + printf("Roulette wheel imitation failed for vertex %" IGRAPH_PRId ".\n", test->vertex); + return IGRAPH_FAILURE; + } + igraph_vector_int_destroy(&stratcopy); + i++; + } + /* game finished; pack up */ + igraph_destroy(&g); + igraph_vector_int_destroy(&known0); + igraph_vector_int_destroy(&known1); + igraph_vector_int_destroy(&known2); + igraph_vector_int_destroy(&known3); + igraph_vector_int_destroy(&known4); + igraph_vector_int_destroy(&known5); + igraph_vector_destroy(&quant); + igraph_vector_int_destroy(&strat); + + return IGRAPH_SUCCESS; +} + +/* It is possible for a vertex to retain its current strategy. This can + * happen both in the local and global perspectives. + */ +igraph_error_t retain_strategy_test(void) { + igraph_t g; + igraph_integer_t max, min, v; + igraph_vector_t quant; + igraph_vector_int_t strat, stratcp; + igraph_integer_t i, ntry, nvert; + + /* the game network */ + igraph_small(&g, /*nvert=*/ 0, IGRAPH_UNDIRECTED, + 0, 3, 0, 4, 1, 2, 1, 4, 1, 5, 2, 3, 2, 4, 3, 4, -1); + nvert = igraph_vcount(&g); + /* strategies vector; the strategy space is {0, 1, 2, 3} */ + /* V[i] is strategy of vertex i */ + igraph_vector_int_init_int(&strat, nvert, 1, 0, 1, 2, 0, 3); + /* quantities vector; V[i] is quantity of vertex i */ + igraph_vector_init_real(&quant, nvert, 0.56, 0.13, 0.26, 0.73, 0.67, 0.82); + + /* random vertex */ + min = 0; + max = 5; + igraph_rng_seed(igraph_rng_default(), 42); /* make tests deterministic */ + v = R_INTEGER(min, max); /* min <= v <= max */ + /* Ensure that it is possible for v to retain its current strategy. We */ + /* will try to do this at most ntry times. As there are at most 6 vertices */ + /* to choose from, it shouldn't take long before we encounter a strategy */ + /* revision round where v retains its current strategy. */ + /* With local perspective. */ + i = 0; + ntry = 100; + igraph_vector_int_init(&stratcp, 0); + do { + i++; + if (i > ntry) { + return IGRAPH_FAILURE; /* ideally this should never happen */ + } + igraph_vector_int_destroy(&stratcp); + igraph_vector_int_init_copy(&stratcp, &strat); + igraph_roulette_wheel_imitation(&g, v, /*is local?*/ 1, &quant, &stratcp, + IGRAPH_ALL); + } while (VECTOR(stratcp)[v] != VECTOR(strat)[v]); + /* If we get to this point, we know that there was an update round */ + /* i <= ntry as a result of which v retains its current strategy. */ + /* Now try again, but this time with the global perspective. */ + i = 0; + do { + i++; + if (i > ntry) { + return IGRAPH_FAILURE; /* ideally this should never happen */ + } + igraph_vector_int_destroy(&stratcp); + igraph_vector_int_init_copy(&stratcp, &strat); + igraph_roulette_wheel_imitation(&g, v, /*is local?*/ 0, &quant, &stratcp, + IGRAPH_ALL); + } while (VECTOR(stratcp)[v] != VECTOR(strat)[v]); + /* nothing further to do, but housekeeping */ + igraph_destroy(&g); + igraph_vector_destroy(&quant); + igraph_vector_int_destroy(&strat); + igraph_vector_int_destroy(&stratcp); + + return IGRAPH_SUCCESS; +} + +int main(void) { + igraph_error_t ret; + + igraph_rng_seed(igraph_rng_default(), 3241); + + ret = roulette_test(); + if (ret) { + return IGRAPH_FAILURE; + } + ret = retain_strategy_test(); + if (ret) { + return IGRAPH_FAILURE; + } + + return IGRAPH_SUCCESS; +} diff --git a/examples/simple/igraph_similarity.c b/examples/simple/igraph_similarity.c new file mode 100644 index 0000000..b543040 --- /dev/null +++ b/examples/simple/igraph_similarity.c @@ -0,0 +1,85 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_matrix_t m; + igraph_vector_int_t pairs; + igraph_vector_t res; + igraph_integer_t i, j, n; + + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0, 1, 2, 1, 2, 0, 3, 0, + -1); + + igraph_matrix_init(&m, 0, 0); + igraph_vector_init(&res, 0); + igraph_vector_int_init(&pairs, 0); + + n = igraph_vcount(&g); + for (i = 0; i < n; i++) { + for (j = n - 1; j >= 0; j--) { + igraph_vector_int_push_back(&pairs, i); + igraph_vector_int_push_back(&pairs, j); + } + } + + printf("Jaccard similarity:\n"); + igraph_similarity_jaccard(&g, &m, igraph_vss_range(1, 3), IGRAPH_ALL, 0); + igraph_matrix_printf(&m, "%.2f"); + + printf("\nJaccard similarity, pairs:\n"); + igraph_similarity_jaccard_pairs(&g, &res, &pairs, IGRAPH_ALL, 0); + igraph_vector_print(&res); + + printf("\nJaccard similarity with edge selector:\n"); + igraph_similarity_jaccard_es(&g, &res, igraph_ess_all(IGRAPH_EDGEORDER_FROM), IGRAPH_IN, 0); + igraph_vector_print(&res); + + printf("\nDice similarity:\n"); + igraph_similarity_dice(&g, &m, igraph_vss_range(1, 3), IGRAPH_ALL, 0); + igraph_matrix_printf(&m, "%.2f"); + + printf("\nDice similarity, pairs:\n"); + igraph_similarity_dice_pairs(&g, &res, &pairs, IGRAPH_ALL, 0); + igraph_vector_print(&res); + + printf("\nDice similarity with edge selector:\n"); + igraph_similarity_dice_es(&g, &res, igraph_ess_all(IGRAPH_EDGEORDER_FROM), IGRAPH_IN, 0); + igraph_vector_print(&res); + + printf("\nWeighted inverse log similarity:\n"); + igraph_similarity_inverse_log_weighted(&g, &m, igraph_vss_all(), IGRAPH_ALL); + igraph_matrix_printf(&m, "%.2f"); + + igraph_matrix_destroy(&m); + igraph_destroy(&g); + igraph_vector_destroy(&res); + igraph_vector_int_destroy(&pairs); + + return 0; +} diff --git a/examples/simple/igraph_similarity.out b/examples/simple/igraph_similarity.out new file mode 100644 index 0000000..37f5069 --- /dev/null +++ b/examples/simple/igraph_similarity.out @@ -0,0 +1,25 @@ +Jaccard similarity: +1.00 0.33 +0.33 1.00 + +Jaccard similarity, pairs: +0 0.25 0.25 1 0.5 0.333333 1 0.25 0.5 1 0.333333 0.25 1 0.5 0.5 0 + +Jaccard similarity with edge selector: +0.333333 0 0 0 + +Dice similarity: +1.00 0.50 +0.50 1.00 + +Dice similarity, pairs: +0 0.4 0.4 1 0.666667 0.5 1 0.4 0.666667 1 0.5 0.4 1 0.666667 0.666667 0 + +Dice similarity with edge selector: +0.5 0 0 0 + +Weighted inverse log similarity: +0.00 1.44 1.44 0.00 +1.44 0.00 0.91 0.91 +1.44 0.91 0.00 0.91 +0.00 0.91 0.91 0.00 diff --git a/examples/simple/igraph_simplify.c b/examples/simple/igraph_simplify.c new file mode 100644 index 0000000..67cb0a4 --- /dev/null +++ b/examples/simple/igraph_simplify.c @@ -0,0 +1,92 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + + /* Multiple edges */ + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, -1); + igraph_simplify(&g, true, true, /*edge_comb=*/ NULL); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, -1); + igraph_simplify(&g, true, true, /*edge_comb=*/ NULL); + if (igraph_ecount(&g) != 1) { + return 1; + } + igraph_destroy(&g); + + /* Loop edges*/ + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 0, 1, 1, 2, 2, 1, 2, -1); + igraph_simplify(&g, true, true, /*edge_comb=*/ NULL); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0, 0, 1, 1, 2, 2, 1, 2, -1); + igraph_simplify(&g, true, true, /*edge_comb=*/ NULL); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + /* Loop & multiple edges */ + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, -1); + igraph_simplify(&g, /* remove_multiple */ true, /* remove_loops */ false, /*edge_comb=*/ NULL); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, -1); + igraph_simplify(&g, /* remove_multiple */ true, /* remove_loops */ false, /*edge_comb=*/ NULL); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_DIRECTED, 2, 2, 2, 2, 2, 2, 3, 2, -1); + igraph_simplify(&g, /* remove_multiple */ false, /* remove_loops */ true, /*edge_comb=*/ NULL); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 3, 3, 3, 3, 3, 4, -1); + igraph_simplify(&g, /* remove_multiple */ false, /* remove_loops */ true, /*edge_comb=*/ NULL); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_DIRECTED, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, -1); + igraph_simplify(&g, true, true, /*edge_comb=*/ NULL); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 3, 3, 2, 3, 2, 3, 2, -1); + igraph_simplify(&g, true, true, /*edge_comb=*/ NULL); + if (igraph_ecount(&g) != 1) { + return 2; + } + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_simplify.out b/examples/simple/igraph_simplify.out new file mode 100644 index 0000000..3ad16e2 --- /dev/null +++ b/examples/simple/igraph_simplify.out @@ -0,0 +1,10 @@ +0 1 +1 2 +1 2 +0 0 +1 2 +1 1 +2 3 +3 2 +3 4 +3 2 diff --git a/examples/simple/igraph_small.c b/examples/simple/igraph_small.c new file mode 100644 index 0000000..55a850a --- /dev/null +++ b/examples/simple/igraph_small.c @@ -0,0 +1,35 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 6, 1, -1); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_small.out b/examples/simple/igraph_small.out new file mode 100644 index 0000000..4ed7c99 --- /dev/null +++ b/examples/simple/igraph_small.out @@ -0,0 +1,5 @@ +0 1 +1 2 +2 3 +3 4 +6 1 diff --git a/examples/simple/igraph_sparsemat.c b/examples/simple/igraph_sparsemat.c new file mode 100644 index 0000000..7f15ec1 --- /dev/null +++ b/examples/simple/igraph_sparsemat.c @@ -0,0 +1,173 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_sparsemat_t A, B, C, D; + igraph_t G, H; + igraph_vector_t vect; + igraph_integer_t i; + + /* Create, compress, destroy */ + igraph_sparsemat_init(&A, 100, 20, 50); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&B); + igraph_sparsemat_destroy(&A); + + /* Convert a ring graph to a matrix, print it, compress, print again */ +#define VC 10 + igraph_sparsemat_init(&A, 1, 1, 0); + igraph_ring(&G, VC, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + igraph_get_adjacency_sparse(&G, &A, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_destroy(&G); + + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_print(&A, stdout); + igraph_sparsemat_print(&B, stdout); + + /* Basic query, nrow, ncol, type, is_triplet, is_cc */ + if (igraph_sparsemat_nrow(&A) != VC || + igraph_sparsemat_ncol(&A) != VC || + igraph_sparsemat_nrow(&B) != VC || + igraph_sparsemat_ncol(&B) != VC) { + return 1; + } + if (!igraph_sparsemat_is_triplet(&A)) { + return 2; + } + if (!igraph_sparsemat_is_cc(&B)) { + return 3; + } + if (igraph_sparsemat_type(&A) != IGRAPH_SPARSEMAT_TRIPLET) { + return 4; + } + if (igraph_sparsemat_type(&B) != IGRAPH_SPARSEMAT_CC) { + return 5; + } + + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); +#undef VC + + printf("------------------------\n"); + + /* Create unit matrices */ + igraph_sparsemat_init_eye(&A, /*n=*/ 5, /*nzmax=*/ 5, /*value=*/ 1.0, /*compress=*/ 0); + igraph_sparsemat_init_eye(&B, /*n=*/ 5, /*nzmax=*/ 5, /*value=*/ 1.0, /*compress=*/ 1); + igraph_sparsemat_print(&A, stdout); + igraph_sparsemat_print(&B, stdout); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + + printf("------------------------\n"); + + /* Create diagonal matrices */ + igraph_vector_init(&vect, 5); + for (i = 0; i < 5; i++) { + VECTOR(vect)[i] = i; + } + igraph_sparsemat_init_diag(&A, /*nzmax=*/ 5, /*values=*/ &vect, /*compress=*/ 0); + igraph_sparsemat_init_diag(&B, /*nzmax=*/ 5, /*values=*/ &vect, /*compress=*/ 1); + igraph_vector_destroy(&vect); + igraph_sparsemat_print(&A, stdout); + igraph_sparsemat_print(&B, stdout); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + + printf("------------------------\n"); + + /* Transpose matrices */ + igraph_sparsemat_init(&A, 1, 1, 0); + igraph_kary_tree(&G, 10, /*children=*/ 2, IGRAPH_TREE_OUT); + igraph_get_adjacency_sparse(&G, &A, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_destroy(&G); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_print(&B, stdout); + igraph_sparsemat_transpose(&B, &C); + igraph_sparsemat_print(&C, stdout); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + igraph_sparsemat_destroy(&C); + + printf("------------------------\n"); + + /* Add duplicate elements */ + igraph_sparsemat_init(&A, 10, 10, /*nzmax=*/ 20); + for (i = 1; i < 10; i++) { + igraph_sparsemat_entry(&A, 0, i, 1.0); + } + for (i = 1; i < 10; i++) { + igraph_sparsemat_entry(&A, 0, i, 1.0); + } + igraph_sparsemat_print(&A, stdout); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_print(&B, stdout); + igraph_sparsemat_dupl(&B); + igraph_sparsemat_print(&B, stdout); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + + printf("------------------------\n"); + + /* Drop zero elements */ + igraph_sparsemat_init(&A, 10, 10, /*nzmax=*/ 20); + igraph_sparsemat_entry(&A, 7, 3, 0.0); + for (i = 1; i < 10; i++) { + igraph_sparsemat_entry(&A, 0, i, 1.0); + igraph_sparsemat_entry(&A, 0, i, 0.0); + } + igraph_sparsemat_entry(&A, 0, 0, 0.0); + igraph_sparsemat_print(&A, stdout); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_print(&B, stdout); + igraph_sparsemat_dropzeros(&B); + igraph_sparsemat_print(&B, stdout); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + + printf("------------------------\n"); + + /* Add two matrices */ + + igraph_sparsemat_init(&A, 1, 1, 0); + igraph_sparsemat_init(&B, 1, 1, 0); + igraph_star(&G, 10, IGRAPH_STAR_OUT, /*center=*/ 0); + igraph_ring(&H, 10, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + igraph_get_adjacency_sparse(&G, &A, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_get_adjacency_sparse(&H, &B, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_destroy(&G); + igraph_destroy(&H); + igraph_sparsemat_compress(&A, &C); + igraph_sparsemat_compress(&B, &D); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + igraph_sparsemat_add(&C, &D, /*alpha=*/ 1.0, /*beta=*/ 2.0, &A); + igraph_sparsemat_destroy(&C); + igraph_sparsemat_destroy(&D); + igraph_sparsemat_print(&A, stdout); + igraph_sparsemat_destroy(&A); + + return 0; +} diff --git a/examples/simple/igraph_sparsemat.out b/examples/simple/igraph_sparsemat.out new file mode 100644 index 0000000..33b5e6d --- /dev/null +++ b/examples/simple/igraph_sparsemat.out @@ -0,0 +1,295 @@ +1 0 : 1 +0 1 : 1 +2 1 : 1 +1 2 : 1 +3 2 : 1 +2 3 : 1 +4 3 : 1 +3 4 : 1 +5 4 : 1 +4 5 : 1 +6 5 : 1 +5 6 : 1 +7 6 : 1 +6 7 : 1 +8 7 : 1 +7 8 : 1 +9 8 : 1 +8 9 : 1 +9 0 : 1 +0 9 : 1 +col 0: locations 0 to 1 +1 : 1 +9 : 1 +col 1: locations 2 to 3 +0 : 1 +2 : 1 +col 2: locations 4 to 5 +1 : 1 +3 : 1 +col 3: locations 6 to 7 +2 : 1 +4 : 1 +col 4: locations 8 to 9 +3 : 1 +5 : 1 +col 5: locations 10 to 11 +4 : 1 +6 : 1 +col 6: locations 12 to 13 +5 : 1 +7 : 1 +col 7: locations 14 to 15 +6 : 1 +8 : 1 +col 8: locations 16 to 17 +7 : 1 +9 : 1 +col 9: locations 18 to 19 +8 : 1 +0 : 1 +------------------------ +0 0 : 1 +1 1 : 1 +2 2 : 1 +3 3 : 1 +4 4 : 1 +col 0: locations 0 to 0 +0 : 1 +col 1: locations 1 to 1 +1 : 1 +col 2: locations 2 to 2 +2 : 1 +col 3: locations 3 to 3 +3 : 1 +col 4: locations 4 to 4 +4 : 1 +------------------------ +0 0 : 0 +1 1 : 1 +2 2 : 2 +3 3 : 3 +4 4 : 4 +col 0: locations 0 to 0 +0 : 0 +col 1: locations 1 to 1 +1 : 1 +col 2: locations 2 to 2 +2 : 2 +col 3: locations 3 to 3 +3 : 3 +col 4: locations 4 to 4 +4 : 4 +------------------------ +col 0: locations 0 to -1 +col 1: locations 0 to 0 +0 : 1 +col 2: locations 1 to 1 +0 : 1 +col 3: locations 2 to 2 +1 : 1 +col 4: locations 3 to 3 +1 : 1 +col 5: locations 4 to 4 +2 : 1 +col 6: locations 5 to 5 +2 : 1 +col 7: locations 6 to 6 +3 : 1 +col 8: locations 7 to 7 +3 : 1 +col 9: locations 8 to 8 +4 : 1 +col 0: locations 0 to 1 +1 : 1 +2 : 1 +col 1: locations 2 to 3 +3 : 1 +4 : 1 +col 2: locations 4 to 5 +5 : 1 +6 : 1 +col 3: locations 6 to 7 +7 : 1 +8 : 1 +col 4: locations 8 to 8 +9 : 1 +col 5: locations 9 to 8 +col 6: locations 9 to 8 +col 7: locations 9 to 8 +col 8: locations 9 to 8 +col 9: locations 9 to 8 +------------------------ +0 1 : 1 +0 2 : 1 +0 3 : 1 +0 4 : 1 +0 5 : 1 +0 6 : 1 +0 7 : 1 +0 8 : 1 +0 9 : 1 +0 1 : 1 +0 2 : 1 +0 3 : 1 +0 4 : 1 +0 5 : 1 +0 6 : 1 +0 7 : 1 +0 8 : 1 +0 9 : 1 +col 0: locations 0 to -1 +col 1: locations 0 to 1 +0 : 1 +0 : 1 +col 2: locations 2 to 3 +0 : 1 +0 : 1 +col 3: locations 4 to 5 +0 : 1 +0 : 1 +col 4: locations 6 to 7 +0 : 1 +0 : 1 +col 5: locations 8 to 9 +0 : 1 +0 : 1 +col 6: locations 10 to 11 +0 : 1 +0 : 1 +col 7: locations 12 to 13 +0 : 1 +0 : 1 +col 8: locations 14 to 15 +0 : 1 +0 : 1 +col 9: locations 16 to 17 +0 : 1 +0 : 1 +col 0: locations 0 to -1 +col 1: locations 0 to 0 +0 : 2 +col 2: locations 1 to 1 +0 : 2 +col 3: locations 2 to 2 +0 : 2 +col 4: locations 3 to 3 +0 : 2 +col 5: locations 4 to 4 +0 : 2 +col 6: locations 5 to 5 +0 : 2 +col 7: locations 6 to 6 +0 : 2 +col 8: locations 7 to 7 +0 : 2 +col 9: locations 8 to 8 +0 : 2 +------------------------ +7 3 : 0 +0 1 : 1 +0 1 : 0 +0 2 : 1 +0 2 : 0 +0 3 : 1 +0 3 : 0 +0 4 : 1 +0 4 : 0 +0 5 : 1 +0 5 : 0 +0 6 : 1 +0 6 : 0 +0 7 : 1 +0 7 : 0 +0 8 : 1 +0 8 : 0 +0 9 : 1 +0 9 : 0 +0 0 : 0 +col 0: locations 0 to 0 +0 : 0 +col 1: locations 1 to 2 +0 : 1 +0 : 0 +col 2: locations 3 to 4 +0 : 1 +0 : 0 +col 3: locations 5 to 7 +7 : 0 +0 : 1 +0 : 0 +col 4: locations 8 to 9 +0 : 1 +0 : 0 +col 5: locations 10 to 11 +0 : 1 +0 : 0 +col 6: locations 12 to 13 +0 : 1 +0 : 0 +col 7: locations 14 to 15 +0 : 1 +0 : 0 +col 8: locations 16 to 17 +0 : 1 +0 : 0 +col 9: locations 18 to 19 +0 : 1 +0 : 0 +col 0: locations 0 to -1 +col 1: locations 0 to 0 +0 : 1 +col 2: locations 1 to 1 +0 : 1 +col 3: locations 2 to 2 +0 : 1 +col 4: locations 3 to 3 +0 : 1 +col 5: locations 4 to 4 +0 : 1 +col 6: locations 5 to 5 +0 : 1 +col 7: locations 6 to 6 +0 : 1 +col 8: locations 7 to 7 +0 : 1 +col 9: locations 8 to 8 +0 : 1 +------------------------ +col 0: locations 0 to 1 +1 : 2 +9 : 2 +col 1: locations 2 to 3 +0 : 3 +2 : 2 +col 2: locations 4 to 6 +0 : 1 +1 : 2 +3 : 2 +col 3: locations 7 to 9 +0 : 1 +2 : 2 +4 : 2 +col 4: locations 10 to 12 +0 : 1 +3 : 2 +5 : 2 +col 5: locations 13 to 15 +0 : 1 +4 : 2 +6 : 2 +col 6: locations 16 to 18 +0 : 1 +5 : 2 +7 : 2 +col 7: locations 19 to 21 +0 : 1 +6 : 2 +8 : 2 +col 8: locations 22 to 24 +0 : 1 +7 : 2 +9 : 2 +col 9: locations 25 to 26 +0 : 3 +8 : 2 diff --git a/examples/simple/igraph_sparsemat3.c b/examples/simple/igraph_sparsemat3.c new file mode 100644 index 0000000..fcf1f8d --- /dev/null +++ b/examples/simple/igraph_sparsemat3.c @@ -0,0 +1,312 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 + 02110-1301 USA + +*/ + +#include + +void permute(const igraph_matrix_t *M, + const igraph_vector_int_t *p, + const igraph_vector_int_t *q, + igraph_matrix_t *res) { + + igraph_integer_t nrow = igraph_vector_int_size(p); + igraph_integer_t ncol = igraph_vector_int_size(q); + igraph_integer_t i, j; + + igraph_matrix_resize(res, nrow, ncol); + + for (i = 0; i < nrow; i++) { + for (j = 0; j < ncol; j++) { + igraph_integer_t ii = VECTOR(*p)[i]; + igraph_integer_t jj = VECTOR(*q)[j]; + MATRIX(*res, i, j) = MATRIX(*M, ii, jj); + } + } +} + +void permute_rows(const igraph_matrix_t *M, + const igraph_vector_int_t *p, + igraph_matrix_t *res) { + + igraph_integer_t nrow = igraph_vector_int_size(p); + igraph_integer_t ncol = igraph_matrix_ncol(M); + igraph_integer_t i, j; + + igraph_matrix_resize(res, nrow, ncol); + + for (i = 0; i < nrow; i++) { + for (j = 0; j < ncol; j++) { + igraph_integer_t ii = VECTOR(*p)[i]; + MATRIX(*res, i, j) = MATRIX(*M, ii, j); + } + } +} + +void permute_cols(const igraph_matrix_t *M, + const igraph_vector_int_t *q, + igraph_matrix_t *res) { + + igraph_integer_t nrow = igraph_matrix_nrow(M); + igraph_integer_t ncol = igraph_vector_int_size(q); + igraph_integer_t i, j; + + igraph_matrix_resize(res, nrow, ncol); + + for (i = 0; i < nrow; i++) { + for (j = 0; j < ncol; j++) { + igraph_integer_t jj = VECTOR(*q)[j]; + MATRIX(*res, i, j) = MATRIX(*M, i, jj); + } + } +} + +void random_permutation(igraph_vector_int_t *vec) { + /* We just do size(vec) * 2 swaps */ + igraph_integer_t one, two, i, n = igraph_vector_int_size(vec); + igraph_integer_t tmp; + for (i = 0; i < 2 * n; i++) { + one = RNG_INTEGER(0, n - 1); + two = RNG_INTEGER(0, n - 1); + tmp = VECTOR(*vec)[one]; + VECTOR(*vec)[one] = VECTOR(*vec)[two]; + VECTOR(*vec)[two] = tmp; + } +} + +igraph_bool_t check_same(const igraph_sparsemat_t *A, + const igraph_matrix_t *M) { + igraph_matrix_t A_dense; + igraph_bool_t result; + + igraph_matrix_init(&A_dense, 1, 1); + igraph_sparsemat_as_matrix(&A_dense, A); + result = igraph_matrix_all_e(&A_dense, M); + igraph_matrix_destroy(&A_dense); + + return result; +} + +int main(void) { + + igraph_sparsemat_t A, B; + igraph_matrix_t M, N; + igraph_vector_int_t p, q; + igraph_integer_t i; + + RNG_BEGIN(); + + /* Permutation of a matrix */ + +#define NROW 10 +#define NCOL 5 +#define EDGES NROW*NCOL/3 + igraph_matrix_init(&M, NROW, NCOL); + igraph_sparsemat_init(&A, NROW, NCOL, EDGES); + for (i = 0; i < EDGES; i++) { + igraph_integer_t r = RNG_INTEGER(0, NROW - 1); + igraph_integer_t c = RNG_INTEGER(0, NCOL - 1); + igraph_real_t value = RNG_INTEGER(1, 5); + MATRIX(M, r, c) = MATRIX(M, r, c) + value; + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_vector_int_init_range(&p, 0, NROW); + igraph_vector_int_init_range(&q, 0, NCOL); + + /* Identity */ + + igraph_matrix_init(&N, 0, 0); + permute(&M, &p, &q, &N); + + igraph_sparsemat_permute(&B, &p, &q, &A); + igraph_sparsemat_dupl(&A); + + if (! check_same(&A, &N)) { + return 1; + } + + /* Random permutation */ + random_permutation(&p); + random_permutation(&q); + + permute(&M, &p, &q, &N); + + igraph_sparsemat_destroy(&A); + igraph_sparsemat_permute(&B, &p, &q, &A); + igraph_sparsemat_dupl(&A); + + if (! check_same(&A, &N)) { + return 2; + } + + igraph_vector_int_destroy(&p); + igraph_vector_int_destroy(&q); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + igraph_matrix_destroy(&M); + igraph_matrix_destroy(&N); + +#undef NROW +#undef NCOL +#undef EDGES + + /* Indexing */ + +#define NROW 10 +#define NCOL 5 +#define EDGES NROW*NCOL/3 +#define I_NROW 6 +#define I_NCOL 3 + igraph_matrix_init(&M, NROW, NCOL); + igraph_sparsemat_init(&A, NROW, NCOL, EDGES); + for (i = 0; i < EDGES; i++) { + igraph_integer_t r = RNG_INTEGER(0, NROW - 1); + igraph_integer_t c = RNG_INTEGER(0, NCOL - 1); + igraph_real_t value = RNG_INTEGER(1, 5); + MATRIX(M, r, c) = MATRIX(M, r, c) + value; + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_vector_int_init(&p, I_NROW); + igraph_vector_int_init(&q, I_NCOL); + + for (i = 0; i < I_NROW; i++) { + VECTOR(p)[i] = RNG_INTEGER(0, I_NROW - 1); + } + for (i = 0; i < I_NCOL; i++) { + VECTOR(p)[i] = RNG_INTEGER(0, I_NCOL - 1); + } + + igraph_matrix_init(&N, 0, 0); + permute(&M, &p, &q, &N); + + igraph_sparsemat_index(&B, &p, &q, &A, 0); + + if (! check_same(&A, &N)) { + return 3; + } + + igraph_sparsemat_destroy(&A); + + /* Getting single elements with index() */ + + igraph_vector_int_resize(&p, 1); + igraph_vector_int_resize(&q, 1); + + for (i = 0; i < 100; i++) { + igraph_real_t value; + VECTOR(p)[0] = RNG_INTEGER(0, NROW - 1); + VECTOR(q)[0] = RNG_INTEGER(0, NCOL - 1); + igraph_sparsemat_index(&B, &p, &q, /*res=*/ 0, &value); + if (value != MATRIX(M, VECTOR(p)[0], VECTOR(q)[0])) { + return 4; + } + } + + /* Getting single elements with get() */ + + igraph_vector_int_resize(&p, 1); + igraph_vector_int_resize(&q, 1); + + for (i = 0; i < 100; i++) { + igraph_integer_t row = RNG_INTEGER(0, NROW - 1); + igraph_integer_t col = RNG_INTEGER(0, NCOL - 1); + if (igraph_sparsemat_get(&B, row, col) != MATRIX(M, row, col)) { + return 4; + } + } + + /* Getting submatrices with index() */ + + for (i = 0; i < 100; i++) { + igraph_real_t value; + VECTOR(p)[0] = RNG_INTEGER(0, NROW - 1); + VECTOR(q)[0] = RNG_INTEGER(0, NCOL - 1); + igraph_sparsemat_index(&B, &p, &q, /*res=*/ &A, &value); + igraph_sparsemat_destroy(&A); + if (value != MATRIX(M, VECTOR(p)[0], VECTOR(q)[0])) { + return 4; + } + } + + igraph_vector_int_destroy(&p); + igraph_vector_int_destroy(&q); + igraph_sparsemat_destroy(&B); + igraph_matrix_destroy(&M); + igraph_matrix_destroy(&N); + + /* Indexing only the rows or the columns */ + + igraph_matrix_init(&M, NROW, NCOL); + igraph_sparsemat_init(&A, NROW, NCOL, EDGES); + for (i = 0; i < EDGES; i++) { + igraph_integer_t r = RNG_INTEGER(0, NROW - 1); + igraph_integer_t c = RNG_INTEGER(0, NCOL - 1); + igraph_real_t value = RNG_INTEGER(1, 5); + MATRIX(M, r, c) = MATRIX(M, r, c) + value; + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_vector_int_init(&p, I_NROW); + igraph_vector_int_init(&q, I_NCOL); + + for (i = 0; i < I_NROW; i++) { + VECTOR(p)[i] = RNG_INTEGER(0, I_NROW - 1); + } + for (i = 0; i < I_NCOL; i++) { + VECTOR(p)[i] = RNG_INTEGER(0, I_NCOL - 1); + } + + igraph_matrix_init(&N, 0, 0); + permute_rows(&M, &p, &N); + + igraph_sparsemat_index(&B, &p, 0, &A, 0); + + if (! check_same(&A, &N)) { + return 5; + } + + permute_cols(&M, &q, &N); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_index(&B, 0, &q, &A, 0); + + if (! check_same(&A, &N)) { + return 6; + } + + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + igraph_vector_int_destroy(&p); + igraph_vector_int_destroy(&q); + igraph_matrix_destroy(&M); + igraph_matrix_destroy(&N); + + RNG_END(); + + return 0; +} diff --git a/examples/simple/igraph_sparsemat3.out b/examples/simple/igraph_sparsemat3.out new file mode 100644 index 0000000..e69de29 diff --git a/examples/simple/igraph_sparsemat4.c b/examples/simple/igraph_sparsemat4.c new file mode 100644 index 0000000..38db991 --- /dev/null +++ b/examples/simple/igraph_sparsemat4.c @@ -0,0 +1,311 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 + 02110-1301 USA + +*/ + +#include + +igraph_bool_t check_solution(const igraph_sparsemat_t *A, + const igraph_vector_t *x, + const igraph_vector_t *b) { + + igraph_vector_t res; + igraph_real_t min, max; + igraph_bool_t success; + igraph_sparsemat_iterator_t it; + + igraph_vector_init_copy(&res, b); + + igraph_sparsemat_iterator_init(&it, (igraph_sparsemat_t*) A); + while (!igraph_sparsemat_iterator_end(&it)) { + igraph_integer_t row = igraph_sparsemat_iterator_row(&it); + igraph_integer_t col = igraph_sparsemat_iterator_col(&it); + igraph_real_t value = igraph_sparsemat_iterator_get(&it); + VECTOR(res)[row] -= VECTOR(*x)[col] * value; + igraph_sparsemat_iterator_next(&it); + } + + igraph_vector_minmax(&res, &min, &max); + igraph_vector_destroy(&res); + + success = fabs(min) < 1e-12 && fabs(max) < 1e-12; + + if (!success) { + printf("Incorrect solution.\n\n"); + printf("A =\n"); igraph_sparsemat_print(A, stdout); printf("\n"); + printf("x =\n"); igraph_vector_print(x); printf("\n"); + printf("b =\n"); igraph_vector_print(b); printf("\n"); + printf("difference between A*x and b =\n"); + igraph_vector_print(&res); printf("\n\n"); + } + + return success; +} + +int main(void) { + + igraph_sparsemat_t A, B, C; + igraph_vector_t b, x; + igraph_integer_t i; + + RNG_BEGIN(); + + /* lsolve */ + +#define DIM 10 +#define EDGES (DIM*DIM/6) + igraph_sparsemat_init(&A, DIM, DIM, EDGES + DIM); + for (i = 0; i < DIM; i++) { + igraph_sparsemat_entry(&A, i, i, RNG_INTEGER(1, 3)); + } + for (i = 0; i < EDGES; i++) { + igraph_integer_t r = RNG_INTEGER(0, DIM - 1); + igraph_integer_t c = RNG_INTEGER(0, r); + igraph_real_t value = RNG_INTEGER(1, 5); + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_dupl(&B); + + igraph_vector_init(&b, DIM); + for (i = 0; i < DIM; i++) { + VECTOR(b)[i] = RNG_INTEGER(1, 10); + } + + igraph_vector_init(&x, DIM); + igraph_sparsemat_lsolve(&B, &b, &x); + + if (! check_solution(&B, &x, &b)) { + return 1; + } + + igraph_vector_destroy(&b); + igraph_vector_destroy(&x); + igraph_sparsemat_destroy(&B); + +#undef DIM +#undef EDGES + + /* ltsolve */ + +#define DIM 10 +#define EDGES (DIM*DIM/6) + igraph_sparsemat_init(&A, DIM, DIM, EDGES + DIM); + for (i = 0; i < DIM; i++) { + igraph_sparsemat_entry(&A, i, i, RNG_INTEGER(1, 3)); + } + for (i = 0; i < EDGES; i++) { + igraph_integer_t r = RNG_INTEGER(0, DIM - 1); + igraph_integer_t c = RNG_INTEGER(0, r); + igraph_real_t value = RNG_INTEGER(1, 5); + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_dupl(&B); + + igraph_vector_init(&b, DIM); + for (i = 0; i < DIM; i++) { + VECTOR(b)[i] = RNG_INTEGER(1, 10); + } + + igraph_vector_init(&x, DIM); + igraph_sparsemat_ltsolve(&B, &b, &x); + + igraph_sparsemat_transpose(&B, &A); + if (! check_solution(&A, &x, &b)) { + return 2; + } + + igraph_vector_destroy(&b); + igraph_vector_destroy(&x); + igraph_sparsemat_destroy(&B); + igraph_sparsemat_destroy(&A); + +#undef DIM +#undef EDGES + + /* usolve */ + +#define DIM 10 +#define EDGES (DIM*DIM/6) + igraph_sparsemat_init(&A, DIM, DIM, EDGES + DIM); + for (i = 0; i < DIM; i++) { + igraph_sparsemat_entry(&A, i, i, RNG_INTEGER(1, 3)); + } + for (i = 0; i < EDGES; i++) { + igraph_integer_t r = RNG_INTEGER(0, DIM - 1); + igraph_integer_t c = RNG_INTEGER(0, r); + igraph_real_t value = RNG_INTEGER(1, 5); + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_dupl(&B); + igraph_sparsemat_transpose(&B, &A); + + igraph_vector_init(&b, DIM); + for (i = 0; i < DIM; i++) { + VECTOR(b)[i] = RNG_INTEGER(1, 10); + } + + igraph_vector_init(&x, DIM); + igraph_sparsemat_usolve(&A, &b, &x); + + if (! check_solution(&A, &x, &b)) { + return 3; + } + + igraph_vector_destroy(&b); + igraph_vector_destroy(&x); + igraph_sparsemat_destroy(&B); + igraph_sparsemat_destroy(&A); + +#undef DIM +#undef EDGES + + /* utsolve */ + +#define DIM 10 +#define EDGES (DIM*DIM/6) + igraph_sparsemat_init(&A, DIM, DIM, EDGES + DIM); + for (i = 0; i < DIM; i++) { + igraph_sparsemat_entry(&A, i, i, RNG_INTEGER(1, 3)); + } + for (i = 0; i < EDGES; i++) { + igraph_integer_t r = RNG_INTEGER(0, DIM - 1); + igraph_integer_t c = RNG_INTEGER(0, r); + igraph_real_t value = RNG_INTEGER(1, 5); + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_dupl(&B); + igraph_sparsemat_transpose(&B, &A); + igraph_sparsemat_destroy(&B); + + igraph_vector_init(&b, DIM); + for (i = 0; i < DIM; i++) { + VECTOR(b)[i] = RNG_INTEGER(1, 10); + } + + igraph_vector_init(&x, DIM); + igraph_sparsemat_utsolve(&A, &b, &x); + + igraph_sparsemat_transpose(&A, &B); + if (! check_solution(&B, &x, &b)) { + return 4; + } + + igraph_vector_destroy(&b); + igraph_vector_destroy(&x); + igraph_sparsemat_destroy(&B); + igraph_sparsemat_destroy(&A); + +#undef DIM +#undef EDGES + + /* cholsol */ + /* We need a positive definite matrix, so we create a full-rank + matrix first and then calculate A'A, which will be positive + definite. */ + +#define DIM 10 +#define EDGES (DIM*DIM/6) + igraph_sparsemat_init(&A, DIM, DIM, EDGES + DIM); + for (i = 0; i < DIM; i++) { + igraph_sparsemat_entry(&A, i, i, RNG_INTEGER(1, 3)); + } + for (i = 0; i < EDGES; i++) { + igraph_integer_t from = RNG_INTEGER(0, DIM - 1); + igraph_integer_t to = RNG_INTEGER(0, DIM - 1); + igraph_real_t value = RNG_INTEGER(1, 5); + igraph_sparsemat_entry(&A, from, to, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_dupl(&B); + igraph_sparsemat_transpose(&B, &A); + igraph_sparsemat_multiply(&A, &B, &C); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&B); + + igraph_vector_init(&b, DIM); + for (i = 0; i < DIM; i++) { + VECTOR(b)[i] = RNG_INTEGER(1, 10); + } + + igraph_vector_init(&x, DIM); + igraph_sparsemat_cholsol(&C, &b, &x, /*order=*/ 0); + + if (! check_solution(&C, &x, &b)) { + return 5; + } + + igraph_vector_destroy(&b); + igraph_vector_destroy(&x); + igraph_sparsemat_destroy(&C); + +#undef DIM +#undef EDGES + + /* lusol */ + +#define DIM 10 +#define EDGES (DIM*DIM/4) + igraph_sparsemat_init(&A, DIM, DIM, EDGES + DIM); + for (i = 0; i < DIM; i++) { + igraph_sparsemat_entry(&A, i, i, RNG_INTEGER(1, 3)); + } + for (i = 0; i < EDGES; i++) { + igraph_integer_t from = RNG_INTEGER(0, DIM - 1); + igraph_integer_t to = RNG_INTEGER(0, DIM - 1); + igraph_real_t value = RNG_INTEGER(1, 5); + igraph_sparsemat_entry(&A, from, to, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_dupl(&B); + + igraph_vector_init(&b, DIM); + for (i = 0; i < DIM; i++) { + VECTOR(b)[i] = RNG_INTEGER(1, 10); + } + + igraph_vector_init(&x, DIM); + igraph_sparsemat_lusol(&B, &b, &x, /*order=*/ 0, /*tol=*/ 1e-10); + + if (! check_solution(&B, &x, &b)) { + return 6; + } + + igraph_vector_destroy(&b); + igraph_vector_destroy(&x); + igraph_sparsemat_destroy(&B); + +#undef DIM +#undef EDGES + + RNG_END(); + + return 0; +} diff --git a/examples/simple/igraph_sparsemat4.out b/examples/simple/igraph_sparsemat4.out new file mode 100644 index 0000000..e69de29 diff --git a/examples/simple/igraph_sparsemat6.c b/examples/simple/igraph_sparsemat6.c new file mode 100644 index 0000000..9abdb33 --- /dev/null +++ b/examples/simple/igraph_sparsemat6.c @@ -0,0 +1,65 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + igraph_matrix_t mat, mat2, mat3; + igraph_sparsemat_t spmat, spmat2; + + igraph_rng_seed(igraph_rng_default(), 42); + +#define NROW 10 +#define NCOL 7 +#define NUM_NONZEROS 15 + + igraph_matrix_init(&mat, NROW, NCOL); + for (igraph_integer_t i = 0; i < NUM_NONZEROS; i++) { + igraph_integer_t r = igraph_rng_get_integer(igraph_rng_default(), 0, NROW - 1); + igraph_integer_t c = igraph_rng_get_integer(igraph_rng_default(), 0, NCOL - 1); + igraph_real_t val = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + MATRIX(mat, r, c) = val; + } + + igraph_matrix_as_sparsemat(&spmat, &mat, /*tol=*/ 1e-14); + igraph_matrix_init(&mat2, 0, 0); + igraph_sparsemat_as_matrix(&mat2, &spmat); + if (!igraph_matrix_all_e(&mat, &mat2)) { + return 1; + } + + igraph_sparsemat_compress(&spmat, &spmat2); + igraph_matrix_init(&mat3, 0, 0); + igraph_sparsemat_as_matrix(&mat3, &spmat2); + if (!igraph_matrix_all_e(&mat, &mat3)) { + return 2; + } + + igraph_matrix_destroy(&mat); + igraph_matrix_destroy(&mat2); + igraph_matrix_destroy(&mat3); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat2); + + return 0; +} diff --git a/examples/simple/igraph_sparsemat7.c b/examples/simple/igraph_sparsemat7.c new file mode 100644 index 0000000..c7adc26 --- /dev/null +++ b/examples/simple/igraph_sparsemat7.c @@ -0,0 +1,76 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +#define DIM1 10 +#define DIM2 5 + +#define RANDINT(a) (igraph_rng_get_integer(igraph_rng_default(), 0, (a))) + +int main(void) { + igraph_matrix_t mat; + igraph_sparsemat_t spmat, spmat2; + igraph_real_t m1, m2; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_sparsemat_init(&spmat, DIM1, DIM2, 20); + igraph_sparsemat_entry(&spmat, 1, 2, -1.0); + igraph_sparsemat_entry(&spmat, 3, 2, 10.0); + for (int i = 0; i < 10; i++) { + igraph_sparsemat_entry(&spmat, RANDINT(DIM1 - 1), RANDINT(DIM2 - 1), 1.0); + } + igraph_sparsemat_entry(&spmat, 1, 2, -1.0); + igraph_sparsemat_entry(&spmat, 3, 2, 10.0); + + igraph_sparsemat_compress(&spmat, &spmat2); + igraph_matrix_init(&mat, 0, 0); + igraph_sparsemat_as_matrix(&mat, &spmat2); + m1 = igraph_sparsemat_min(&spmat2); + m2 = igraph_matrix_min(&mat); + if (m1 != m2) { + printf("%f %f\n", m1, m2); + return 1; + } + m1 = igraph_sparsemat_max(&spmat2); + m2 = igraph_matrix_max(&mat); + if (m1 != m2) { + printf("%f %f\n", m1, m2); + return 2; + } + + igraph_sparsemat_minmax(&spmat2, &m1, &m2); + if (m1 != igraph_matrix_min(&mat)) { + return 3; + } + if (m2 != igraph_matrix_max(&mat)) { + return 4; + } + + igraph_matrix_destroy(&mat); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat2); + + return 0; +} diff --git a/examples/simple/igraph_sparsemat8.c b/examples/simple/igraph_sparsemat8.c new file mode 100644 index 0000000..35cdef8 --- /dev/null +++ b/examples/simple/igraph_sparsemat8.c @@ -0,0 +1,209 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +#define DIM1 10 +#define DIM2 5 + +#define INT(a) (igraph_rng_get_integer(igraph_rng_default(), 0, (a))) + +int main(void) { + igraph_matrix_t mat, mat2; + igraph_sparsemat_t spmat, spmat2; + igraph_integer_t i, j, nz1, nz2; + igraph_vector_t sums1, sums2; + + + igraph_rng_seed(igraph_rng_default(), 42); + + /* COPY */ + + igraph_sparsemat_init(&spmat, DIM1, DIM2, 20); + for (i = 0; i < 10; i++) { + igraph_sparsemat_entry(&spmat, INT(DIM1 - 1), INT(DIM2 - 1), 1.0); + } + igraph_sparsemat_init_copy(&spmat2, &spmat); + + igraph_matrix_init(&mat, 0, 0); + igraph_sparsemat_as_matrix(&mat, &spmat); + igraph_matrix_init(&mat2, 0, 0); + igraph_sparsemat_as_matrix(&mat2, &spmat2); + if (!igraph_matrix_all_e(&mat, &mat2)) { + return 1; + } + + igraph_matrix_destroy(&mat2); + igraph_sparsemat_destroy(&spmat2); + + igraph_sparsemat_compress(&spmat, &spmat2); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_init_copy(&spmat, &spmat2); + + igraph_matrix_init(&mat2, 0, 0); + igraph_sparsemat_as_matrix(&mat2, &spmat); + if (!igraph_matrix_all_e(&mat, &mat2)) { + return 2; + } + + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat2); + igraph_matrix_destroy(&mat); + igraph_matrix_destroy(&mat2); + + /* COLSUMS, ROWSUMS */ + + igraph_sparsemat_init(&spmat, DIM1, DIM2, 20); + for (i = 0; i < 10; i++) { + igraph_sparsemat_entry(&spmat, INT(DIM1 - 1), INT(DIM2 - 1), 1.0); + } + igraph_sparsemat_compress(&spmat, &spmat2); + + igraph_matrix_init(&mat, 0, 0); + igraph_sparsemat_as_matrix(&mat, &spmat); + igraph_vector_init(&sums1, 0); + igraph_vector_init(&sums2, 0); + igraph_sparsemat_colsums(&spmat, &sums1); + igraph_matrix_colsum(&mat, &sums2); + if (!igraph_vector_all_e(&sums1, &sums2)) { + return 3; + } + igraph_sparsemat_colsums(&spmat2, &sums1); + if (!igraph_vector_all_e(&sums1, &sums2)) { + return 4; + } + + igraph_sparsemat_rowsums(&spmat, &sums1); + igraph_matrix_rowsum(&mat, &sums2); + if (!igraph_vector_all_e(&sums1, &sums2)) { + return 5; + } + igraph_sparsemat_rowsums(&spmat2, &sums1); + if (!igraph_vector_all_e(&sums1, &sums2)) { + return 6; + } + + igraph_matrix_destroy(&mat); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat2); + igraph_vector_destroy(&sums1); + igraph_vector_destroy(&sums2); + + /* COUNT_NONZERO, COUNT_NONZEROTOL */ + + igraph_sparsemat_init(&spmat, DIM1, DIM2, 20); + igraph_sparsemat_entry(&spmat, 1, 2, 1.0); + igraph_sparsemat_entry(&spmat, 1, 2, 1.0); + igraph_sparsemat_entry(&spmat, 1, 3, 1e-12); + for (i = 0; i < 10; i++) { + igraph_sparsemat_entry(&spmat, INT(DIM1 - 1), INT(DIM2 - 1), 1.0); + } + igraph_sparsemat_compress(&spmat, &spmat2); + + igraph_matrix_init(&mat, 0, 0); + igraph_sparsemat_as_matrix(&mat, &spmat2); + + nz1 = igraph_sparsemat_count_nonzero(&spmat2); + for (nz2 = 0, i = 0; i < igraph_matrix_nrow(&mat); i++) { + for (j = 0; j < igraph_matrix_ncol(&mat); j++) { + if (MATRIX(mat, i, j) != 0) { + nz2++; + } + } + } + if (nz1 != nz2) { + printf("%" IGRAPH_PRId " %" IGRAPH_PRId "\n", nz1, nz2); + return 7; + } + + nz1 = igraph_sparsemat_count_nonzerotol(&spmat2, 1e-10); + for (nz2 = 0, i = 0; i < igraph_matrix_nrow(&mat); i++) { + for (j = 0; j < igraph_matrix_ncol(&mat); j++) { + if (fabs(MATRIX(mat, i, j)) >= 1e-10) { + nz2++; + } + } + } + if (nz1 != nz2) { + printf("%" IGRAPH_PRId " %" IGRAPH_PRId "\n", nz1, nz2); + return 8; + } + + igraph_matrix_destroy(&mat); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat2); + + /* SCALE */ + + igraph_sparsemat_init(&spmat, DIM1, DIM2, 20); + for (i = 0; i < 10; i++) { + igraph_sparsemat_entry(&spmat, INT(DIM1 - 1), INT(DIM2 - 1), 1.0); + } + igraph_sparsemat_compress(&spmat, &spmat2); + + igraph_sparsemat_scale(&spmat, 2.0); + igraph_sparsemat_scale(&spmat2, 2.0); + igraph_matrix_init(&mat, 0, 0); + igraph_sparsemat_as_matrix(&mat, &spmat); + igraph_matrix_init(&mat2, 0, 0); + igraph_sparsemat_as_matrix(&mat2, &spmat2); + igraph_matrix_scale(&mat, 1.0 / 2.0); + igraph_matrix_scale(&mat2, 1.0 / 2.0); + if (!igraph_matrix_all_e(&mat, &mat2)) { + return 9; + } + + igraph_matrix_destroy(&mat); + igraph_matrix_destroy(&mat2); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat2); + + /* ADDROWS, ADDCOLS */ + + igraph_sparsemat_init(&spmat, DIM1, DIM2, 20); + for (i = 0; i < 10; i++) { + igraph_sparsemat_entry(&spmat, INT(DIM1 - 1), INT(DIM2 - 1), 1.0); + } + igraph_sparsemat_compress(&spmat, &spmat2); + + igraph_sparsemat_add_rows(&spmat, 3); + igraph_sparsemat_add_cols(&spmat, 2); + + igraph_sparsemat_add_rows(&spmat2, 3); + igraph_sparsemat_add_cols(&spmat2, 2); + + igraph_matrix_init(&mat, 0, 0); + igraph_sparsemat_as_matrix(&mat, &spmat); + igraph_matrix_init(&mat2, 0, 0); + igraph_sparsemat_as_matrix(&mat2, &spmat2); + if (!igraph_matrix_all_e(&mat, &mat2)) { + return 10; + } + + igraph_matrix_destroy(&mat); + igraph_matrix_destroy(&mat2); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat2); + + return 0; +} diff --git a/examples/simple/igraph_star.c b/examples/simple/igraph_star.c new file mode 100644 index 0000000..1795422 --- /dev/null +++ b/examples/simple/igraph_star.c @@ -0,0 +1,35 @@ +/* + 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 +#include + +int main(void) { + igraph_t graph; + + /* Create an undirected 6-star, with the 0th node as the centre. */ + igraph_star(&graph, 7, IGRAPH_STAR_UNDIRECTED, 0); + + /* Output the edge list of the graph. */ + igraph_write_graph_edgelist(&graph, stdout); + + /* Destroy the graph when we are done using it. */ + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_star.out b/examples/simple/igraph_star.out new file mode 100644 index 0000000..d3fc616 --- /dev/null +++ b/examples/simple/igraph_star.out @@ -0,0 +1,6 @@ +0 1 +0 2 +0 3 +0 4 +0 5 +0 6 diff --git a/examples/simple/igraph_stochastic_imitation.c b/examples/simple/igraph_stochastic_imitation.c new file mode 100644 index 0000000..3f7d694 --- /dev/null +++ b/examples/simple/igraph_stochastic_imitation.c @@ -0,0 +1,182 @@ +/* -*- mode: C -*- */ +/* + Test suite for stochastic imitation via uniform selection. + 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 +#include + +/* test parameters structure */ +typedef struct { + igraph_t *graph; + igraph_integer_t vertex; + igraph_imitate_algorithm_t algo; + igraph_vector_t *quantities; + igraph_vector_int_t *strategies; + igraph_vector_int_t *known_strats; + igraph_neimode_t mode; + igraph_integer_t retval; +} strategy_test_t; + +/* Updating the strategy of an isolated vertex. In this case, the strategies + * vector should not change at all. + */ +igraph_error_t isolated_vertex_test(void) { + igraph_t g; + igraph_vector_t quant; + igraph_vector_int_t strat, v; + igraph_integer_t i; + igraph_error_t ret; + + /* graph with one isolated vertex */ + igraph_small(&g, /*n vertices*/ 0, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + igraph_add_vertices(&g, 1, 0); /* new vertex 3 is isolated */ + /* quantities vector: all vertices have the same fitness */ + igraph_vector_init_real(&quant, 4, 0.25, 0.25, 0.25, 0.25); + /* strategies vector: 0 means aggressive strategy; 1 means passive */ + igraph_vector_int_init_int(&strat, 4, 1, 0, 1, 0); + /* make a copy of the original strategies vector for comparison later on */ + igraph_vector_int_init_copy(&v, &strat); + /* Now update strategy of vertex 3. Since this vertex is isolated, no */ + /* strategy update would take place. The resulting strategies vector */ + /* would be the same as it was originally. */ + ret = igraph_stochastic_imitation(/*graph*/ &g, + /*vertex*/ 3, + /*algorithm*/ IGRAPH_IMITATE_BLIND, + /*quantities*/ &quant, + /*strategies*/ &strat, + /*mode*/ IGRAPH_ALL); + if (ret) { + printf("Isolated vertex test failed.\n"); + return IGRAPH_FAILURE; + } + for (i = 0; i < igraph_vector_int_size(&strat); i++) { + if (VECTOR(strat)[i] != VECTOR(v)[i]) { + printf("Isolated vertex test failed.\n"); + return IGRAPH_FAILURE; + } + } + /* clean up */ + igraph_destroy(&g); + igraph_vector_destroy(&quant); + igraph_vector_int_destroy(&strat); + igraph_vector_int_destroy(&v); + + return IGRAPH_SUCCESS; +} + +/* A game on the Petersen graph. This graph has 10 vertices and 15 edges. The + * Petersen graph is initialized with a default quantities vector and a + * default strategies vector. Some vertices are chosen for strategy revision, + * each one via a different stochastic imitation rule. + */ +igraph_error_t petersen_game_test(void) { + igraph_t g; + igraph_bool_t success; + igraph_vector_t quant; + igraph_vector_int_t strat, stratcopy, *knownstrats; + igraph_vector_int_t known0, known2, known4; + igraph_integer_t i, k, n; + igraph_error_t ret; + int nvert; + strategy_test_t *test; + + /* the Petersen graph */ + igraph_small(&g, /*n vertices*/ 0, IGRAPH_UNDIRECTED, + 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, -1); + nvert = igraph_vcount(&g); + /* Strategies vector, one strategy for each vertex. Thus vec[i] is the */ + /* strategy of vertex i. The strategy space is: {0, 1, 2, 3}. */ + /* Each strategy should be an integer. */ + igraph_vector_int_init_int(&strat, nvert, 1, 1, 2, 2, 0, 0, 0, 1, 2, 3); + /* Quantities vector, one quantity per vertex. Thus vec[i] is the */ + /* quantity for vertex i. */ + igraph_vector_init_real(&quant, nvert, + 0.3, 1.1, 0.5, 1.0, 0.9, + 0.8, 0.4, 0.1, 0.7, 0.7); + /* parameter settings and known results */ + igraph_vector_int_init_int(&known0, 2, 0, 1); + igraph_vector_int_init_int(&known2, 2, 1, 2); + igraph_vector_int_init_int(&known4, 2, 0, 2); + /*graph--vertex--algo--quantities--strategies--known_strats--mode--retval*/ + strategy_test_t blind0 = {&g, 0, IGRAPH_IMITATE_BLIND, &quant, NULL, &known0, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t augmented4 = {&g, 4, IGRAPH_IMITATE_AUGMENTED, &quant, NULL, &known4, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t contracted2 = {&g, 2, IGRAPH_IMITATE_CONTRACTED, &quant, NULL, &known2, IGRAPH_ALL, IGRAPH_SUCCESS}; + strategy_test_t *all_checks[] = {/* 1 */ &blind0, + /* 2 */ &augmented4, + /* 3 */ &contracted2 + }; + /* run the tests */ + n = 3; + i = 0; + while (i < n) { + test = all_checks[i]; + igraph_vector_int_init_copy(&stratcopy, &strat); + ret = igraph_stochastic_imitation(test->graph, test->vertex, test->algo, + test->quantities, &stratcopy, + test->mode); + if (ret) { + printf("Stochastic imitation failed for vertex %" IGRAPH_PRId ".\n", test->vertex); + return IGRAPH_FAILURE; + } + /* If the updated strategy for the vertex matches one of the known */ + /* strategies, then success. Default to failure. */ + success = 0; + knownstrats = test->known_strats; + for (k = 0; k < igraph_vector_int_size(knownstrats); k++) { + if (VECTOR(*knownstrats)[k] == VECTOR(stratcopy)[test->vertex]) { + success = 1; + break; + } + } + if (!success) { + printf("Stochastic imitation failed for vertex %" IGRAPH_PRId ".\n", test->vertex); + return IGRAPH_FAILURE; + } + igraph_vector_int_destroy(&stratcopy); + i++; + } + /* clean up */ + igraph_destroy(&g); + igraph_vector_int_destroy(&known0); + igraph_vector_int_destroy(&known2); + igraph_vector_int_destroy(&known4); + igraph_vector_destroy(&quant); + igraph_vector_int_destroy(&strat); + + return IGRAPH_SUCCESS; +} + +int main(void) { + igraph_error_t ret; + + igraph_rng_seed(igraph_rng_default(), 547612); + + ret = isolated_vertex_test(); + if (ret) { + return ret; + } + ret = petersen_game_test(); + if (ret) { + return ret; + } + + return IGRAPH_SUCCESS; +} diff --git a/examples/simple/igraph_strvector.c b/examples/simple/igraph_strvector.c new file mode 100644 index 0000000..4a63ff9 --- /dev/null +++ b/examples/simple/igraph_strvector.c @@ -0,0 +1,86 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +void strvector_print(const igraph_strvector_t sv) { + igraph_integer_t i, s = igraph_strvector_size(&sv); + for (i = 0; i < s; i++) { + printf("---%s---\n", igraph_strvector_get(&sv, i)); + } + printf("\n"); +} + +int main(void) { + + igraph_strvector_t sv1, sv2; + + printf("Initializing and setting elements:\n"); + igraph_strvector_init(&sv1, 5); + + igraph_strvector_set(&sv1, 0, "zero"); + igraph_strvector_set(&sv1, 1, "one"); + igraph_strvector_set(&sv1, 2, "two"); + igraph_strvector_set(&sv1, 3, "three"); + igraph_strvector_set(&sv1, 4, "four"); + strvector_print(sv1); + + printf("strvector size after first removing one element, and then the rest:\n"); + igraph_strvector_remove(&sv1, 4); + igraph_strvector_remove_section(&sv1, 0, 4); + printf("%" IGRAPH_PRId "\n\n", igraph_strvector_size(&sv1)); + + printf("Resize to three elements, and set them:\n"); + igraph_strvector_resize(&sv1, 3); + igraph_strvector_set(&sv1, 0, "zero"); + igraph_strvector_set(&sv1, 1, "one"); + igraph_strvector_set(&sv1, 2, "two"); + strvector_print(sv1); + + printf("Then copy the first element over the third element:\n"); + igraph_strvector_set(&sv1, 2, igraph_strvector_get(&sv1, 0)); + strvector_print(sv1); + + printf("Make a copy of the strvector and set the last element of the copy:\n"); + igraph_strvector_init_copy(&sv2, &sv1); + igraph_strvector_set(&sv2, 2, "copy two"); + strvector_print(sv2); + + printf("Append the copy to the strvector:\n"); + igraph_strvector_append(&sv1, &sv2); + strvector_print(sv1); + + printf("Add two strings at the end:\n"); + igraph_strvector_push_back(&sv1, "zeroth"); + igraph_strvector_push_back(&sv1, "first"); + strvector_print(sv1); + igraph_strvector_destroy(&sv1); + + printf("strvector size after clearing it:\n"); + igraph_strvector_clear(&sv2); + printf("%" IGRAPH_PRId "\n", igraph_strvector_size(&sv2)); + + igraph_strvector_destroy(&sv2); + + return 0; +} diff --git a/examples/simple/igraph_strvector.out b/examples/simple/igraph_strvector.out new file mode 100644 index 0000000..2cdbe3b --- /dev/null +++ b/examples/simple/igraph_strvector.out @@ -0,0 +1,45 @@ +Initializing and setting elements: +---zero--- +---one--- +---two--- +---three--- +---four--- + +strvector size after first removing one element, and then the rest: +0 + +Resize to three elements, and set them: +---zero--- +---one--- +---two--- + +Then copy the first element over the third element: +---zero--- +---one--- +---zero--- + +Make a copy of the strvector and set the last element of the copy: +---zero--- +---one--- +---copy two--- + +Append the copy to the strvector: +---zero--- +---one--- +---zero--- +---zero--- +---one--- +---copy two--- + +Add two strings at the end: +---zero--- +---one--- +---zero--- +---zero--- +---one--- +---copy two--- +---zeroth--- +---first--- + +strvector size after clearing it: +0 diff --git a/examples/simple/igraph_subisomorphic_lad.c b/examples/simple/igraph_subisomorphic_lad.c new file mode 100644 index 0000000..c603fbf --- /dev/null +++ b/examples/simple/igraph_subisomorphic_lad.c @@ -0,0 +1,125 @@ +/* -*- 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 + +void print_maps(igraph_vector_int_t *map, igraph_vector_int_list_t *maps) { + igraph_integer_t n, i; + igraph_vector_int_print(map); + n = igraph_vector_int_list_size(maps); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(maps, i); + igraph_vector_int_print(v); + } + igraph_vector_int_list_clear(maps); +} + +int main(void) { + igraph_t target, pattern; + igraph_bool_t iso; + igraph_vector_int_t map; + igraph_vector_int_list_t maps; + igraph_integer_t i; + int domainsvec[] = { 0, 2, 8, -1, + 4, 5, 6, 7, -1, + 1, 3, 5, 6, 7, 8, -1, + 0, 2, 8, -1, + 1, 3, 7, 8, -1, -2 + }; + igraph_vector_int_list_t domains; + igraph_vector_int_t v; + + igraph_small(&target, 9, IGRAPH_UNDIRECTED, + 0, 1, 0, 4, 0, 6, + 1, 4, 1, 2, + 2, 3, + 3, 4, 3, 5, 3, 7, 3, 8, + 4, 5, 4, 6, + 5, 6, 5, 8, + 7, 8, + -1); + + igraph_small(&pattern, 5, IGRAPH_UNDIRECTED, + 0, 1, 0, 4, + 1, 4, 1, 2, + 2, 3, + 3, 4, + -1); + + igraph_vector_int_init(&map, 0); + igraph_vector_int_list_init(&maps, 0); + + igraph_subisomorphic_lad(&pattern, &target, /*domains=*/ NULL, &iso, &map, + &maps, /*induced=*/ false, /*time_limit=*/ 0); + + if (!iso) { + return 1; + } + print_maps(&map, &maps); + + printf("---------\n"); + + igraph_subisomorphic_lad(&pattern, &target, /*domains=*/ NULL, &iso, &map, + &maps, /*induced=*/ true, /*time_limit=*/ 0); + + if (!iso) { + return 2; + } + print_maps(&map, &maps); + + printf("---------\n"); + + igraph_vector_int_list_init(&domains, 0); + i = 0; + igraph_vector_int_init(&v, 0); + while (1) { + if (domainsvec[i] == -2) { + break; + } else if (domainsvec[i] == -1) { + igraph_vector_int_list_push_back_copy(&domains, &v); + igraph_vector_int_clear(&v); + } else { + igraph_vector_int_push_back(&v, domainsvec[i]); + } + i++; + } + igraph_vector_int_destroy(&v); + + igraph_subisomorphic_lad(&pattern, &target, &domains, &iso, &map, &maps, + /*induced=*/ false, /*time_limit=*/ 0); + + if (!iso) { + return 3; + } + print_maps(&map, &maps); + + igraph_vector_int_list_destroy(&domains); + igraph_vector_int_destroy(&map); + igraph_vector_int_list_destroy(&maps); + + igraph_destroy(&pattern); + igraph_destroy(&target); + + + return 0; +} diff --git a/examples/simple/igraph_subisomorphic_lad.out b/examples/simple/igraph_subisomorphic_lad.out new file mode 100644 index 0000000..215a48f --- /dev/null +++ b/examples/simple/igraph_subisomorphic_lad.out @@ -0,0 +1,30 @@ +1 0 6 5 4 +1 0 6 5 4 +0 1 2 3 4 +5 3 2 1 4 +7 3 4 5 8 +4 3 7 8 5 +8 3 4 6 5 +0 4 3 5 6 +0 4 3 2 1 +3 4 0 6 5 +6 4 3 8 5 +5 4 1 2 3 +5 4 1 0 6 +1 4 5 6 0 +8 5 6 4 3 +4 5 8 7 3 +3 5 6 0 4 +6 5 8 3 4 +0 6 5 3 4 +5 6 0 1 4 +7 8 5 4 3 +--------- +0 1 2 3 4 +0 1 2 3 4 +5 3 2 1 4 +5 4 1 2 3 +0 4 3 2 1 +--------- +0 4 3 2 1 +0 4 3 2 1 diff --git a/examples/simple/igraph_symmetric_tree.c b/examples/simple/igraph_symmetric_tree.c new file mode 100644 index 0000000..c817913 --- /dev/null +++ b/examples/simple/igraph_symmetric_tree.c @@ -0,0 +1,27 @@ + +#include + +int main(void) { + + igraph_t graph; + igraph_bool_t res; + igraph_vector_int_t v; + igraph_vector_int_init_int(&v, 3, 3, 4, 5); + + /* Create a directed symmetric tree with 2 levels - + 3 children in first and 4 children in second level, + 5 children in third level + with edges pointing towards the root. */ + igraph_symmetric_tree(&graph, &v, IGRAPH_TREE_IN); + + igraph_is_tree(&graph, &res, NULL, IGRAPH_IN); + printf("Is it an in-tree? %s\n", res ? "Yes" : "No"); + + igraph_is_tree(&graph, &res, NULL, IGRAPH_OUT); + printf("Is it an out-tree? %s\n", res ? "Yes" : "No"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&v); + + return 0; +} diff --git a/examples/simple/igraph_to_undirected.c b/examples/simple/igraph_to_undirected.c new file mode 100644 index 0000000..9468679 --- /dev/null +++ b/examples/simple/igraph_to_undirected.c @@ -0,0 +1,52 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_vector_int_t v; + igraph_t g; + + igraph_vector_int_init_int(&v, 2, 5, 5); + igraph_square_lattice(&g, &v, 1, IGRAPH_DIRECTED, 1 /*mutual*/, 0 /*periodic*/); + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_COLLAPSE, + /*edge_comb=*/ 0); + igraph_write_graph_edgelist(&g, stdout); + + igraph_destroy(&g); + igraph_vector_int_destroy(&v); + + printf("---\n"); + + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 3, + 5, 6, 6, 5, 6, 7, 6, 7, 7, 6, 7, 8, 7, 8, 8, 7, 8, 7, 8, 8, 9, 9, 9, 9, + -1); + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_MUTUAL, + /*edge_comb=*/ 0); + igraph_write_graph_edgelist(&g, stdout); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_to_undirected.out b/examples/simple/igraph_to_undirected.out new file mode 100644 index 0000000..4829e26 --- /dev/null +++ b/examples/simple/igraph_to_undirected.out @@ -0,0 +1,48 @@ +0 1 +0 5 +1 2 +1 6 +2 3 +2 7 +3 4 +3 8 +4 9 +5 6 +5 10 +6 7 +6 11 +7 8 +7 12 +8 9 +8 13 +9 14 +10 11 +10 15 +11 12 +11 16 +12 13 +12 17 +13 14 +13 18 +14 19 +15 16 +15 20 +16 17 +16 21 +17 18 +17 22 +18 19 +18 23 +19 24 +20 21 +21 22 +22 23 +23 24 +--- +5 6 +6 7 +7 8 +7 8 +8 8 +9 9 +9 9 diff --git a/examples/simple/igraph_topological_sorting.c b/examples/simple/igraph_topological_sorting.c new file mode 100644 index 0000000..1e40268 --- /dev/null +++ b/examples/simple/igraph_topological_sorting.c @@ -0,0 +1,49 @@ +/* -*- 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, see . +*/ + +#include +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t res; + + /* Test graph taken from http://en.wikipedia.org/wiki/Topological_sorting + * @ 05.03.2006 */ + igraph_small(&graph, 8, IGRAPH_DIRECTED, + 0, 3, 0, 4, 1, 3, 2, 4, 2, 7, 3, 5, 3, 6, 3, 7, 4, 6, + -1); + + igraph_vector_int_init(&res, 0); + + /* Sort the vertices in "increasing" order. */ + igraph_topological_sorting(&graph, &res, IGRAPH_OUT); + igraph_vector_int_print(&res); + printf("\n"); + + /* Sort the vertices in "decreasing" order. */ + igraph_topological_sorting(&graph, &res, IGRAPH_IN); + igraph_vector_int_print(&res); + + /* Destroy data structures when done using them. */ + igraph_destroy(&graph); + igraph_vector_int_destroy(&res); + + return 0; +} diff --git a/examples/simple/igraph_topological_sorting.out b/examples/simple/igraph_topological_sorting.out new file mode 100644 index 0000000..7ff0e5f --- /dev/null +++ b/examples/simple/igraph_topological_sorting.out @@ -0,0 +1,3 @@ +0 1 2 3 4 5 7 6 + +5 6 7 4 3 2 0 1 diff --git a/examples/simple/igraph_transitivity.c b/examples/simple/igraph_transitivity.c new file mode 100644 index 0000000..c02d8f5 --- /dev/null +++ b/examples/simple/igraph_transitivity.c @@ -0,0 +1,94 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_real_t res; + + /* Trivial cases */ + + igraph_ring(&g, 100, IGRAPH_UNDIRECTED, 0, 0); + igraph_transitivity_undirected(&g, &res, IGRAPH_TRANSITIVITY_NAN); + igraph_destroy(&g); + + if (res != 0) { + return 1; + } + + igraph_full(&g, 20, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_transitivity_undirected(&g, &res, IGRAPH_TRANSITIVITY_NAN); + igraph_destroy(&g); + + if (res != 1) { + return 2; + } + + /* Degenerate cases */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 2, 3, 4, 5, -1); + igraph_transitivity_undirected(&g, &res, IGRAPH_TRANSITIVITY_NAN); + /* res should be NaN here, any comparison must return false */ + if (res == 0 || res > 0 || res < 0) { + return 4; + } + igraph_transitivity_undirected(&g, &res, IGRAPH_TRANSITIVITY_ZERO); + /* res should be zero here */ + if (res) { + return 5; + } + igraph_destroy(&g); + + /* Zachary Karate club */ + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + + igraph_transitivity_undirected(&g, &res, IGRAPH_TRANSITIVITY_NAN); + igraph_destroy(&g); + + if (res != 0.2556818181818181767717) { + fprintf(stderr, "%f != %f\n", res, 0.2556818181818181767717); + return 3; + } + + return 0; +} diff --git a/examples/simple/igraph_union.c b/examples/simple/igraph_union.c new file mode 100644 index 0000000..e1cef08 --- /dev/null +++ b/examples/simple/igraph_union.c @@ -0,0 +1,51 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2023 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 +#include + +int main(void) { + igraph_t left, right, uni; + igraph_vector_int_t edge_map1, edge_map2; + + igraph_vector_int_init(&edge_map1, 0); + igraph_vector_int_init(&edge_map2, 0); + + igraph_small(&left, 4, IGRAPH_DIRECTED, + 0,1, 1,2, 2,2, 2,3, 2,3, 3,2, + -1); + + igraph_small(&right, 6, IGRAPH_DIRECTED, + 0,1, 1,2, 2,2, 2,3, 5,2, + -1); + + igraph_union(&uni, &left, &right, &edge_map1, &edge_map2); + igraph_write_graph_edgelist(&uni, stdout); + igraph_vector_int_print(&edge_map1); + igraph_vector_int_print(&edge_map2); + + igraph_destroy(&uni); + igraph_destroy(&left); + igraph_destroy(&right); + + igraph_vector_int_destroy(&edge_map2); + igraph_vector_int_destroy(&edge_map1); + + return 0; +} diff --git a/examples/simple/igraph_union.out b/examples/simple/igraph_union.out new file mode 100644 index 0000000..d1259ca --- /dev/null +++ b/examples/simple/igraph_union.out @@ -0,0 +1,9 @@ +0 1 +1 2 +2 2 +2 3 +2 3 +3 2 +5 2 +0 1 2 3 4 5 +0 1 2 3 6 diff --git a/examples/simple/igraph_vector_int_list_sort.c b/examples/simple/igraph_vector_int_list_sort.c new file mode 100644 index 0000000..e13c1a0 --- /dev/null +++ b/examples/simple/igraph_vector_int_list_sort.c @@ -0,0 +1,41 @@ + +#include +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t cliques; + igraph_integer_t i, n; + + /* Set a random seed to make the program deterministic */ + igraph_rng_seed(igraph_rng_default(), 31415); + + /* Create a random graph with a given number of vertices and edges */ + igraph_erdos_renyi_game_gnm(&graph, 15, 80, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + /* Find all maximal cliques in the graph */ + igraph_vector_int_list_init(&cliques, 0); + igraph_maximal_cliques(&graph, &cliques, -1, -1); + + /* Print the cliques in lexicographical order */ + printf("Maximal cliques in lexicographical order:\n"); + igraph_vector_int_list_sort(&cliques, igraph_vector_int_lex_cmp); + n = igraph_vector_int_list_size(&cliques); + for (i=0; i < n; ++i) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&cliques, i)); + } + + /* Print the cliques in colexicographical order */ + printf("\nMaximal cliques in colexicographical order:\n"); + igraph_vector_int_list_sort(&cliques, igraph_vector_int_colex_cmp); + n = igraph_vector_int_list_size(&cliques); + for (i=0; i < n; ++i) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&cliques, i)); + } + + /* Destroy data structures when we no longer need them */ + igraph_vector_int_list_destroy(&cliques); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_version.c b/examples/simple/igraph_version.c new file mode 100644 index 0000000..b7308bf --- /dev/null +++ b/examples/simple/igraph_version.c @@ -0,0 +1,41 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include + +int main(void) { + + char tmp[100]; + const char *string; + int major, minor, subminor; + + igraph_version(&string, &major, &minor, &subminor); + snprintf(tmp, sizeof(tmp), "%i.%i.%i", major, minor, subminor); + + if (strncmp(string, tmp, strlen(tmp))) { + return 1; + } + + return 0; +} diff --git a/examples/simple/igraph_vs_nonadj.c b/examples/simple/igraph_vs_nonadj.c new file mode 100644 index 0000000..e6ab4d9 --- /dev/null +++ b/examples/simple/igraph_vs_nonadj.c @@ -0,0 +1,65 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_vs_t vs; + igraph_vit_t vit; + igraph_integer_t size; + + /* empty graph, all vertices */ + igraph_empty(&g, 10, IGRAPH_DIRECTED); + igraph_vs_nonadj(&vs, 0, IGRAPH_ALL); + igraph_vs_size(&g, &vs, &size); + printf("%" IGRAPH_PRId " ", size); + igraph_vit_create(&g, vs, &vit); + while (!IGRAPH_VIT_END(vit)) { + printf("%" IGRAPH_PRId " ", IGRAPH_VIT_GET(vit)); + IGRAPH_VIT_NEXT(vit); + } + printf("\n"); + + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + /* full graph, no vertices */ + igraph_full(&g, 10, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + igraph_vs_nonadj(&vs, 0, IGRAPH_ALL); + igraph_vit_create(&g, vs, &vit); + while (!IGRAPH_VIT_END(vit)) { + printf("%" IGRAPH_PRId " ", IGRAPH_VIT_GET(vit)); + IGRAPH_VIT_NEXT(vit); + } + printf("\n"); + + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + + return 0; +} diff --git a/examples/simple/igraph_vs_nonadj.out b/examples/simple/igraph_vs_nonadj.out new file mode 100644 index 0000000..bc74074 --- /dev/null +++ b/examples/simple/igraph_vs_nonadj.out @@ -0,0 +1,2 @@ +10 0 1 2 3 4 5 6 7 8 9 + diff --git a/examples/simple/igraph_vs_range.c b/examples/simple/igraph_vs_range.c new file mode 100644 index 0000000..1a2bbd2 --- /dev/null +++ b/examples/simple/igraph_vs_range.c @@ -0,0 +1,50 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_vs_t vs; + igraph_vit_t vit; + igraph_t g; + igraph_integer_t size; + + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, 0, 1); + igraph_vs_range(&vs, 0, 10); + igraph_vit_create(&g, vs, &vit); + igraph_vs_size(&g, &vs, &size); + printf("%" IGRAPH_PRId "", size); + + while (!IGRAPH_VIT_END(vit)) { + printf(" %" IGRAPH_PRId "", IGRAPH_VIT_GET(vit)); + IGRAPH_VIT_NEXT(vit); + } + printf("\n"); + + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_vs_range.out b/examples/simple/igraph_vs_range.out new file mode 100644 index 0000000..1b4b2e8 --- /dev/null +++ b/examples/simple/igraph_vs_range.out @@ -0,0 +1 @@ +10 0 1 2 3 4 5 6 7 8 9 diff --git a/examples/simple/igraph_vs_vector.c b/examples/simple/igraph_vs_vector.c new file mode 100644 index 0000000..ec15f25 --- /dev/null +++ b/examples/simple/igraph_vs_vector.c @@ -0,0 +1,85 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_vector_int_t v = IGRAPH_VECTOR_NULL; + igraph_integer_t edges[] = { 0, 1, 1, 2, 2, 2, 2, 3, 2, 4, 3, 4 }; + igraph_vector_int_t v2; + igraph_integer_t i; + igraph_vit_t vit; + igraph_vs_t vs; + igraph_integer_t size; + + igraph_vector_int_view(&v, edges, sizeof(edges) / sizeof(igraph_integer_t)); + igraph_create(&g, &v, 0, IGRAPH_DIRECTED); + + /* Create iterator based on a vector (view) */ + igraph_vector_int_init(&v2, 6); + VECTOR(v2)[0] = 0; + VECTOR(v2)[1] = 2; + VECTOR(v2)[2] = 4; + VECTOR(v2)[3] = 0; + VECTOR(v2)[4] = 2; + VECTOR(v2)[5] = 4; + + igraph_vit_create(&g, igraph_vss_vector(&v2), &vit); + + i = 0; + while (!IGRAPH_VIT_END(vit)) { + if (IGRAPH_VIT_GET(vit) != VECTOR(v2)[i]) { + return 1; + } + IGRAPH_VIT_NEXT(vit); + i++; + } + if (i != igraph_vector_int_size(&v2)) { + return 2; + } + + igraph_vit_destroy(&vit); + igraph_vector_int_destroy(&v2); + + /* Create small vector iterator */ + + igraph_vs_vector_small(&vs, 0, 2, 4, 0, 2, 4, 2, -1); + igraph_vit_create(&g, vs, &vit); + igraph_vs_size(&g, &vs, &size); + printf("%" IGRAPH_PRId " ", size); + for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + printf("%" IGRAPH_PRId " ", IGRAPH_VIT_GET(vit)); + } + printf("\n"); + + igraph_vit_destroy(&vit); + igraph_vs_destroy(&vs); + + /* Clean up */ + + igraph_destroy(&g); + + return 0; +} diff --git a/examples/simple/igraph_vs_vector.out b/examples/simple/igraph_vs_vector.out new file mode 100644 index 0000000..cb9f83b --- /dev/null +++ b/examples/simple/igraph_vs_vector.out @@ -0,0 +1 @@ +7 0 2 4 0 2 4 2 diff --git a/examples/simple/igraph_weighted_adjacency.c b/examples/simple/igraph_weighted_adjacency.c new file mode 100644 index 0000000..6b92ca3 --- /dev/null +++ b/examples/simple/igraph_weighted_adjacency.c @@ -0,0 +1,53 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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, see . +*/ + +#include +#include + +int main(void) { + igraph_matrix_t mat; + igraph_t g; + int m[4][4] = { { 0, 1, 2, 0 }, { 2, 0, 0, 1 }, { 0, 0, 1, 0 }, { 0, 1, 0, 0 } }; + igraph_vector_t weights; + igraph_vector_int_t el; + igraph_integer_t i, j, n; + igraph_vector_int_init(&el, 0); + igraph_vector_init(&weights, 0); + + igraph_matrix_init(&mat, 4, 4); + for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) { + MATRIX(mat, i, j) = m[i][j]; + } + + igraph_weighted_adjacency(&g, &mat, IGRAPH_ADJ_DIRECTED, &weights, IGRAPH_LOOPS_ONCE); + + igraph_get_edgelist(&g, &el, 0); + n = igraph_ecount(&g); + + for (i = 0, j = 0; i < n; i++, j += 2) { + printf("%" IGRAPH_PRId " --> %" IGRAPH_PRId ": %g\n", + VECTOR(el)[j], VECTOR(el)[j + 1], VECTOR(weights)[i]); + } + + igraph_matrix_destroy(&mat); + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&el); + igraph_destroy(&g); + +} diff --git a/examples/simple/igraph_weighted_adjacency.out b/examples/simple/igraph_weighted_adjacency.out new file mode 100644 index 0000000..2e7be85 --- /dev/null +++ b/examples/simple/igraph_weighted_adjacency.out @@ -0,0 +1,6 @@ +0 --> 1: 1 +0 --> 2: 2 +1 --> 0: 2 +1 --> 3: 1 +2 --> 2: 1 +3 --> 1: 1 diff --git a/examples/simple/igraph_write_graph_lgl.c b/examples/simple/igraph_write_graph_lgl.c new file mode 100644 index 0000000..ff2c381 --- /dev/null +++ b/examples/simple/igraph_write_graph_lgl.c @@ -0,0 +1,47 @@ +#include + +int main(void) { + + igraph_t graph; + igraph_strvector_t names; + igraph_vector_t weights; + igraph_integer_t i; + igraph_integer_t vcount, ecount; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_small(&graph, 7, IGRAPH_UNDIRECTED, + 0,1, 1,3, 1,2, 2,0, 4,2, 3,4, + -1); + vcount = igraph_vcount(&graph); + ecount = igraph_ecount(&graph); + + + printf("Output without isolates:\n"); + igraph_write_graph_lgl(&graph, stdout, /*names*/ NULL, /*weights*/ NULL, /*isolates*/ 0); + + + printf("\nOutput with isolates:\n"); + igraph_write_graph_lgl(&graph, stdout, /*names*/ NULL, /*weights*/ NULL, /*isolates*/ 1); + + + printf("\nOutput vertex and edge labels:\n"); + igraph_strvector_init(&names, vcount); + for (i = 0; i < vcount; i++) { + char str[2] = " "; /* initialize to ensure presence of null terminator */ + str[0] = 'A' + i; + igraph_strvector_set(&names, i, str); + } + SETVASV(&graph, "names", &names); + + igraph_vector_init_range(&weights, 1, ecount + 1); + SETEANV(&graph, "weights", &weights); + + igraph_write_graph_lgl(&graph, stdout, "names", "weights", /*isolates*/ 0); + + igraph_strvector_destroy(&names); + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/igraph_write_graph_lgl.out b/examples/simple/igraph_write_graph_lgl.out new file mode 100644 index 0000000..2370f19 --- /dev/null +++ b/examples/simple/igraph_write_graph_lgl.out @@ -0,0 +1,37 @@ +Output without isolates: +# 0 +1 +2 +# 1 +2 +3 +# 2 +4 +# 3 +4 + +Output with isolates: +# 0 +1 +2 +# 1 +2 +3 +# 2 +4 +# 3 +4 +# 5 +# 6 + +Output vertex and edge labels: +# A +B 1 +C 4 +# B +C 3 +D 2 +# C +E 5 +# D +E 6 diff --git a/examples/simple/igraph_write_graph_pajek.c b/examples/simple/igraph_write_graph_pajek.c new file mode 100644 index 0000000..f89db88 --- /dev/null +++ b/examples/simple/igraph_write_graph_pajek.c @@ -0,0 +1,75 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +int main(void) { + + igraph_t g; + igraph_strvector_t names; + + igraph_set_attribute_table(&igraph_cattribute_table); + + /* save a simple ring graph */ + igraph_ring(&g, 10, IGRAPH_DIRECTED, false /* mutual */, true /* circular */); + igraph_write_graph_pajek(&g, stdout); + + /* add some vertex attributes */ + igraph_strvector_init(&names, 0); + igraph_strvector_push_back(&names, "A"); + igraph_strvector_push_back(&names, "B"); + igraph_strvector_push_back(&names, "C"); + igraph_strvector_push_back(&names, "D"); + igraph_strvector_push_back(&names, "E"); + igraph_strvector_push_back(&names, "F"); + igraph_strvector_push_back(&names, "G"); + igraph_strvector_push_back(&names, "H"); + igraph_strvector_push_back(&names, "I"); + igraph_strvector_push_back(&names, "J"); + SETVASV(&g, "id", &names); + igraph_strvector_destroy(&names); + + /* save the graph with vertex names */ + igraph_write_graph_pajek(&g, stdout); + + igraph_strvector_init(&names, 0); + igraph_strvector_push_back(&names, "square"); + igraph_strvector_push_back(&names, "square"); + igraph_strvector_push_back(&names, "square"); + igraph_strvector_push_back(&names, "square"); + igraph_strvector_push_back(&names, "escaping spaces"); + igraph_strvector_push_back(&names, "square"); + igraph_strvector_push_back(&names, "square"); + igraph_strvector_push_back(&names, "escaping\nnewline"); + igraph_strvector_push_back(&names, "square"); + igraph_strvector_push_back(&names, "encoding \"quotes\""); + SETVASV(&g, "shape", &names); + igraph_strvector_destroy(&names); + + /* save the graph with escaped shapes */ + igraph_write_graph_pajek(&g, stdout); + + /* destroy the graph */ + igraph_destroy(&g); + return 0; +} diff --git a/examples/simple/igraph_write_graph_pajek.out b/examples/simple/igraph_write_graph_pajek.out new file mode 100644 index 0000000..c10d7ab --- /dev/null +++ b/examples/simple/igraph_write_graph_pajek.out @@ -0,0 +1,56 @@ +*Vertices 10 +*Arcs +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +10 1 +*Vertices 10 +1 "A" +2 "B" +3 "C" +4 "D" +5 "E" +6 "F" +7 "G" +8 "H" +9 "I" +10 "J" +*Arcs +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +10 1 +*Vertices 10 +1 "A" "square" +2 "B" "square" +3 "C" "square" +4 "D" "square" +5 "E" "escaping spaces" +6 "F" "square" +7 "G" "square" +8 "H" "escaping\nnewline" +9 "I" "square" +10 "J" "encoding "quotes"" +*Arcs +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +10 1 diff --git a/examples/simple/iso_b03_m1000.A00 b/examples/simple/iso_b03_m1000.A00 new file mode 100644 index 0000000000000000000000000000000000000000..196b400b36190f54c01dc3d66bed33a73e0b5cfa GIT binary patch literal 5002 zcmX|_3A~R*7st;zPh3>CvhT9*a>;(l7Dbn+xLLE8WXZlHTlOtN;mW@6dr@RxBSc<` z*IJ>CNU!&M=6c_IKQqtFoZtDKGiS~-&&>Z?q;kSc^2?@3#H5^VQj==ZiJr<^NIM}F zS)nR>X>E|r5{YVZ?E-G1J{d()MK0>Dpj}g*GnG-`n#-HWbIVN#Vpsbf11i^O=huGNSw}0U?T0B7r92FGq;k|2U#=h-q%n+E2lo)p?_@Id zMC}V_D_ucdS+XNv45vwEJx@D@MN&eSM>}9h4D*`G9C8F*w|obNy=ox;z~?e@AMTNi zwbHWit%!->s|@AYRX@~Ug4acnY({SpaeYpvNLQpaWvZk?pTO=BIcUq!P}@u2_%B2G(r$!UtVYsa z-^BAnA&T{pZI>-@ZPA_4Ww@$?@D($8jEE&M-qK4*RuJ{c_%vR&kyR%1Eg$t7>5KJM zGBfZ^1$PGTKH&K-Wexl_>~V0vlWQSErE!SIerck`LA*`r4D*5D`&=Cqa-<}7V)Q9Z zkLDp=Mm#D?HR_&Hlh`iSV>+8&d)vZHs=&}4ABM?h(51sV+}FM#qO&E&ebgtYM{6H+ z9@p1spOXA&ou^gZ3efrro_st%=lOtg9Q&m#&W18pn(_|ws4B&fOsAY?CXo!(;WiE{ zk#vw5+6k`yGM~BE2HyjJF6axyRqb9_iAdg;W_q3RvvTnN^+>4?dKJ1ObvvU;Gk#xx zbT;cwJx|J0kCs9qE)kd(nu~edl(e+VNgVY#x#zL5j&L=>gLI6==sG;TO_@t{FHy>p zyRFH?*+_qs46uHzyFhP3wm+<116UGm5G-BM8-Xkh&*c=~7yj0g)XAg_`d#I9P#s}R zrW@=Ei2BG1MTAA1+1En&|t*lqb!K3sy!;x3sW6jBAw0-63l9?($(pv!c zN+fq^X9KxbC(tSo(8cIKuLD5LWKAM#Me9q-`H)qECAS`jtD+W1V==9i(gIv@ogZfC z8Sb92?WIh?)9hHpiZu)|Ka9TTyh&$E4*moyzRi9$_ih3n`cdSkPiECPL7da(ZePjTQ&S}w*@Vts+Fr*$M~k28a^NL>%sV6B*`s@U1a=ON*vfG_I#7iqmN+}NJIfoa-C%AO`WxZDCs$z{ zBDat=V5aON^pERleDA}aX?*?45~q3kx4wyHJmUkkGLfmN-E=E{n~c+(SlY7@-AFy!T!Y9 zNPUXB6z^*kPBj%_O)kHo|G2$Fd!AjQP7!^KhyMndpfjo6FOx}{oc7aplJ;_$jAtsp z!8-#huWB0@r%PMA%rm`wYuP~!h?Y{<&@r%8dK9~?*s3BU7+;_T;H#%!z){KVF?Q2F zq`qz&!#XN6H~B?7Aiaf^Z*3IPSX~K+@1yR)Nwi~#+Kbv({cMsT(-@r!I#% z3(12l>70;8I}wr5a8?Tp9PH#Pbn+8vjS5Hmk?aSZNfNR1RMsmuD)Bg_Z8NwIJ{W*fv&xw9+V&}q-Niq!|+rWB&=PpLh$zF7xVaM^D>2K$V*-%|# z!{8hz=cqgAXW$Eh_4mN!xo8>vCqZ}C^VG-m6Sxu?+rYDIc<-9)le9A-EyBCk#%0uZ)Y*8aWSq%D&d8qmU+F4( z$E6$Cot)cDu3|xDHTqR}c1GIR=Age=HrjQ1Mdfu+Tg|@}Z-9uWl!B>}#2~F@Ex@jj zU!*niwTxG{SFrl5R;AxEyd}@ta*%5zFIj+xtb)I5Bcff=IdmJ5<3+$WrucVgH*-El zNgX0vi*7|&CQ_eu8Hp2)LG0kFd*0UN!g z8zbxVHtkuOmv+f;a`7+CDSYvnJ`CRs-U?~TOlF~*TH-=v{M1a&>DM6xe7}A|%LRXv zRyP}#C!@0oFKWS<$zGtI*m@snD_?WTbFJu4NvdLQIDkmO|5P4ej(ylWvv;mM#q;3-O+t}<3Onh9zO zd8bksUm_y!Q8|rlru+x|h_JSeHFZ=9wH4j~7@r_y^cR`-8FG+-O)=ZUmkieoM_KhiT=e zbw>tc!QbpXVcjyZ#;N779i-=x@1(KVi<6_e1N13wHxWBZRphYPH;g6NYj|=RY`pvn zJ5}@ok@Wp}H#V}+UPUPlOO{7>k|x}@c>h8^O^{dlw))*@kbMECpMF^%_8~Rju{RG2FRgVU?Avy=iMDg75F`S^Z zM{gk`4dp)AI_i5;Wxa;CV=1kX6lP!d8}(%S6Fc2mrCd@s+$36tcqipyN{|&arE}Qp z;QbrYkDh-`RASLgfp;%yQ*0*ED@X5nxr4QjBsZLo*-y3(j(F0UQz<6}zPR94^7LN{ z+6PUAF8D@xYw{mTYx7g<6KLecihJxQhn1Rx-W=ph zxhT=+(Ft^ld6Uhvi~f!@7rCR{XM8NREF@pOb@$PExD@Y%N^TI7!1`C9wD?VOnN5W0 zDs%47{#6{S{`K@bi^*rfEB`Gfl0PMDRGV>s$9obz_Bi_oOfebawZzLmf{ATTESC2EoP3wh%h zYlcpF9fjX1w6^X**MDDOw?e*{=Mb1vz~J$1PAqE4BJ=UWL~XsM=l2GczmWOeZXVCo z@I44;g&wp@SQBQ_UbnGY++5&VNk1PqlBBa(558g%-|g(t)UclSr59NL{YCjbj5Dj2 av8ga027ivS0qvgAWTZZ`6h0HHYySnxHsUA% literal 0 HcmV?d00001 diff --git a/examples/simple/karate.gml b/examples/simple/karate.gml new file mode 100644 index 0000000..ecafe9c --- /dev/null +++ b/examples/simple/karate.gml @@ -0,0 +1,530 @@ +Creator "Mark Newman on Fri Jul 21 12:39:27 2006" +graph +[ + node + [ + id 1 + ] + node + [ + id 2 + ] + node + [ + id 3 + ] + node + [ + id 4 + ] + node + [ + id 5 + ] + node + [ + id 6 + ] + node + [ + id 7 + ] + node + [ + id 8 + ] + node + [ + id 9 + ] + node + [ + id 10 + ] + node + [ + id 11 + ] + node + [ + id 12 + ] + node + [ + id 13 + ] + node + [ + id 14 + ] + node + [ + id 15 + ] + node + [ + id 16 + ] + node + [ + id 17 + ] + node + [ + id 18 + ] + node + [ + id 19 + ] + node + [ + id 20 + ] + node + [ + id 21 + ] + node + [ + id 22 + ] + node + [ + id 23 + ] + node + [ + id 24 + ] + node + [ + id 25 + ] + node + [ + id 26 + ] + node + [ + id 27 + ] + node + [ + id 28 + ] + node + [ + id 29 + ] + node + [ + id 30 + ] + node + [ + id 31 + ] + node + [ + id 32 + ] + node + [ + id 33 + ] + node + [ + id 34 + ] + edge + [ + source 2 + target 1 + ] + edge + [ + source 3 + target 1 + ] + edge + [ + source 3 + target 2 + ] + edge + [ + source 4 + target 1 + ] + edge + [ + source 4 + target 2 + ] + edge + [ + source 4 + target 3 + ] + edge + [ + source 5 + target 1 + ] + edge + [ + source 6 + target 1 + ] + edge + [ + source 7 + target 1 + ] + edge + [ + source 7 + target 5 + ] + edge + [ + source 7 + target 6 + ] + edge + [ + source 8 + target 1 + ] + edge + [ + source 8 + target 2 + ] + edge + [ + source 8 + target 3 + ] + edge + [ + source 8 + target 4 + ] + edge + [ + source 9 + target 1 + ] + edge + [ + source 9 + target 3 + ] + edge + [ + source 10 + target 3 + ] + edge + [ + source 11 + target 1 + ] + edge + [ + source 11 + target 5 + ] + edge + [ + source 11 + target 6 + ] + edge + [ + source 12 + target 1 + ] + edge + [ + source 13 + target 1 + ] + edge + [ + source 13 + target 4 + ] + edge + [ + source 14 + target 1 + ] + edge + [ + source 14 + target 2 + ] + edge + [ + source 14 + target 3 + ] + edge + [ + source 14 + target 4 + ] + edge + [ + source 17 + target 6 + ] + edge + [ + source 17 + target 7 + ] + edge + [ + source 18 + target 1 + ] + edge + [ + source 18 + target 2 + ] + edge + [ + source 20 + target 1 + ] + edge + [ + source 20 + target 2 + ] + edge + [ + source 22 + target 1 + ] + edge + [ + source 22 + target 2 + ] + edge + [ + source 26 + target 24 + ] + edge + [ + source 26 + target 25 + ] + edge + [ + source 28 + target 3 + ] + edge + [ + source 28 + target 24 + ] + edge + [ + source 28 + target 25 + ] + edge + [ + source 29 + target 3 + ] + edge + [ + source 30 + target 24 + ] + edge + [ + source 30 + target 27 + ] + edge + [ + source 31 + target 2 + ] + edge + [ + source 31 + target 9 + ] + edge + [ + source 32 + target 1 + ] + edge + [ + source 32 + target 25 + ] + edge + [ + source 32 + target 26 + ] + edge + [ + source 32 + target 29 + ] + edge + [ + source 33 + target 3 + ] + edge + [ + source 33 + target 9 + ] + edge + [ + source 33 + target 15 + ] + edge + [ + source 33 + target 16 + ] + edge + [ + source 33 + target 19 + ] + edge + [ + source 33 + target 21 + ] + edge + [ + source 33 + target 23 + ] + edge + [ + source 33 + target 24 + ] + edge + [ + source 33 + target 30 + ] + edge + [ + source 33 + target 31 + ] + edge + [ + source 33 + target 32 + ] + edge + [ + source 34 + target 9 + ] + edge + [ + source 34 + target 10 + ] + edge + [ + source 34 + target 14 + ] + edge + [ + source 34 + target 15 + ] + edge + [ + source 34 + target 16 + ] + edge + [ + source 34 + target 19 + ] + edge + [ + source 34 + target 20 + ] + edge + [ + source 34 + target 21 + ] + edge + [ + source 34 + target 23 + ] + edge + [ + source 34 + target 24 + ] + edge + [ + source 34 + target 27 + ] + edge + [ + source 34 + target 28 + ] + edge + [ + source 34 + target 29 + ] + edge + [ + source 34 + target 30 + ] + edge + [ + source 34 + target 31 + ] + edge + [ + source 34 + target 32 + ] + edge + [ + source 34 + target 33 + ] +] diff --git a/examples/simple/links.net b/examples/simple/links.net new file mode 100644 index 0000000..175b90b --- /dev/null +++ b/examples/simple/links.net @@ -0,0 +1,16 @@ +% Example Pajek file + +*Network TRALALA +*vertices 4 + 1 "1" 0.0938 0.0896 ellipse x_fact 1 y_fact 1 + 2 "2" 0.8188 0.2458 ellipse x_fact 1 y_fact 1 + 3 "3" 0.3688 0.7792 ellipse x_fact 1 + 4 "4" 0.9583 0.8563 ellipse x_fact 1 +*arcs +1 1 1 h2 0 w 3 c Blue s 3 a1 -130 k1 0.6 a2 -130 k2 0.6 ap 0.5 l "Bezier loop" lc BlueViolet fos 20 lr 58 lp 0.3 la 360 +2 1 1 h2 0 a1 120 k1 1.3 a2 -120 k2 0.3 ap 25 l "Bezier arc" lphi 270 la 180 lr 19 lp 0.5 +1 2 1 h2 0 a1 40 k1 2.8 a2 30 k2 0.8 ap 25 l "Bezier arc" lphi 90 la 0 lp 0.65 +4 2 -1 h2 0 w 1 k1 -2 k2 250 ap 25 l "Circular arc" c Red lc OrangeRed +3 4 1 p Dashed h2 0 w 2 c OliveGreen ap 25 l "Straight arc" lc PineGreen +1 3 1 p Dashed h2 0 w 5 k1 -1 k2 -20 ap 25 l "Oval arc" c Brown lc Black +3 3 -1 h1 6 w 1 h2 12 k1 -2 k2 -15 ap 0.5 l "Circular loop" c Red lc OrangeRed lphi 270 la 180 diff --git a/examples/simple/nodelist1.dl b/examples/simple/nodelist1.dl new file mode 100644 index 0000000..56d73ca --- /dev/null +++ b/examples/simple/nodelist1.dl @@ -0,0 +1,9 @@ +DL n=5 +format = nodelist1 +labels: +george, sally, jim, billy, jane +data: +1 2 3 +2 3 +3 1 +4 3 diff --git a/examples/simple/nodelist2.dl b/examples/simple/nodelist2.dl new file mode 100644 index 0000000..08f2a54 --- /dev/null +++ b/examples/simple/nodelist2.dl @@ -0,0 +1,8 @@ +DL n=5 +format = nodelist1 +labels embedded: +data: +george sally jim +sally jim +billy george +jane jim diff --git a/examples/simple/random_seed.c b/examples/simple/random_seed.c new file mode 100644 index 0000000..a6ceaf0 --- /dev/null +++ b/examples/simple/random_seed.c @@ -0,0 +1,36 @@ + +#include + +int main(void) { + + igraph_t g1, g2; + igraph_bool_t iso; + + /* Seed the default random number generator and create a random graph. */ + + igraph_rng_seed(igraph_rng_default(), 1122); + + igraph_erdos_renyi_game_gnp(&g1, 100, 3.0 / 100, /*directed=*/ 0, /*loops=*/ 0); + + /* Seed the generator with the same seed again, + * and create a graph with the same method. */ + + igraph_rng_seed(igraph_rng_default(), 1122); + + igraph_erdos_renyi_game_gnp(&g2, 100, 3.0 / 100, /*directed=*/ 0, /*loops=*/ 0); + + /* The two graphs will be identical. */ + + igraph_is_same_graph(&g1, &g2, &iso); + + if (!iso) { + return 1; + } + + /* Destroy no longer needed data structures. */ + + igraph_destroy(&g2); + igraph_destroy(&g1); + + return 0; +} diff --git a/examples/simple/safelocale.c b/examples/simple/safelocale.c new file mode 100644 index 0000000..f6e0d0e --- /dev/null +++ b/examples/simple/safelocale.c @@ -0,0 +1,49 @@ + +#include + +#include +#include +#include + +int main(void) { + const char *filename = "weighted.gml"; + igraph_t graph; + igraph_safelocale_t loc; + + /* Attempt to set a locale that uses a decimal comma. Locale names + * differ between platforms, and not all locales are available, + * so the locale change may not be successful. */ + const char *locname = setlocale(LC_ALL, "de_DE"); + struct lconv *lc = localeconv(); + if (strcmp(lc->decimal_point, ",")) { + /* If decimal point is not a comma, presumably because the requested + * locale was not available, report locale information. */ + fprintf(stderr, "setlocale() returned '%s', decimal point is '%s'\n", + locname ? locname : "NULL", + lc->decimal_point); + } + + FILE *file = fopen(filename, "r"); + if (! file) { + fprintf(stderr, "Cannot open %s file.\n", filename); + exit(1); + } + + /* An attribute table is needed to read graph attributes. */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* At this point, the current locale may use decimal commas. + * We temporarily set a C locale using enter_safelocale() to + * allow the GML reader and writer to work correctly.*/ + igraph_enter_safelocale(&loc); + if (igraph_read_graph_gml(&graph, file) != IGRAPH_SUCCESS) { + fprintf(stderr, "Reading %s failed.\n", filename); + abort(); + } + igraph_write_graph_gml(&graph, stdout, IGRAPH_WRITE_GML_DEFAULT_SW, NULL, ""); + igraph_exit_safelocale(&loc); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/simple/safelocale.out b/examples/simple/safelocale.out new file mode 100644 index 0000000..cfdb381 --- /dev/null +++ b/examples/simple/safelocale.out @@ -0,0 +1,21 @@ +Version 1 +graph +[ + directed 0 + node + [ + id 1 + value 1.23 + ] + node + [ + id 2 + value 4.87e-05 + ] + edge + [ + source 2 + target 1 + weight -377.9 + ] +] diff --git a/examples/simple/test.graphml b/examples/simple/test.graphml new file mode 100644 index 0000000..a6ab576 --- /dev/null +++ b/examples/simple/test.graphml @@ -0,0 +1,55 @@ + + + + + yellow + + + + + + 1 + + + 2006-11-12 + + + + green + incorrect + true + + + + blue + 0 + + + red "with entities" + + + + false + + + turquoise + fAlSe + + + 1.0 + + + 1.0 + + + 2.0 + + + + + + 1.1 + + + diff --git a/examples/simple/walktrap.c b/examples/simple/walktrap.c new file mode 100644 index 0000000..3e0ebe7 --- /dev/null +++ b/examples/simple/walktrap.c @@ -0,0 +1,59 @@ +/* -*- 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 + +int main(void) { + igraph_t g; + igraph_matrix_int_t merges; + igraph_vector_t modularity; + igraph_integer_t no_of_nodes; + igraph_integer_t i; + + igraph_small(&g, 5, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 5, 6, 5, 7, 5, 8, 5, 9, 6, 7, 6, 8, 6, 9, 7, 8, 7, 9, 8, 9, 0, 5, 4, 9, -1); + igraph_vector_init(&modularity, 0); + igraph_matrix_int_init(&merges, 0, 0); + + igraph_community_walktrap(&g, + NULL /* no weights */, + 4 /* steps */, + &merges, &modularity, + /* membership=*/ NULL); + + no_of_nodes = igraph_vcount(&g); + printf("Merges:\n"); + for (i = 0; i < igraph_matrix_int_nrow(&merges); i++) { + printf("%2.1" IGRAPH_PRId " + %2." IGRAPH_PRId " -> %2." IGRAPH_PRId " (modularity %4.2f)\n", + MATRIX(merges, i, 0), MATRIX(merges, i, 1), + no_of_nodes + i, VECTOR(modularity)[i]); + } + + igraph_destroy(&g); + + igraph_matrix_int_destroy(&merges); + igraph_vector_destroy(&modularity); + + return 0; +} diff --git a/examples/simple/weighted.gml b/examples/simple/weighted.gml new file mode 100644 index 0000000..79d3638 --- /dev/null +++ b/examples/simple/weighted.gml @@ -0,0 +1,17 @@ +# This file is used to test the handling of decimal points +# under different locales. +graph [ + node [ + id 1 + value 1.23 + ] + node [ + id 2 + value 4.87e-5 + ] + edge [ + source 1 + target 2 + weight -3.779e+2 + ] +] diff --git a/examples/tutorial/tutorial1.c b/examples/tutorial/tutorial1.c new file mode 100644 index 0000000..b5d1b8c --- /dev/null +++ b/examples/tutorial/tutorial1.c @@ -0,0 +1,29 @@ +#include + +int main(void) { + igraph_integer_t num_vertices = 1000; + igraph_integer_t num_edges = 1000; + igraph_real_t diameter; + igraph_t graph; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_erdos_renyi_game_gnm( + &graph, num_vertices, num_edges, + IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS + ); + + igraph_diameter( + &graph, &diameter, + /* from = */ NULL, /* to = */ NULL, + /* vertex_path = */ NULL, /* edge_path = */ NULL, + IGRAPH_UNDIRECTED, /* unconn= */ true + ); + printf("Diameter of a random graph with average degree %g: %g\n", + 2.0 * igraph_ecount(&graph) / igraph_vcount(&graph), + (double) diameter); + + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/tutorial/tutorial2.c b/examples/tutorial/tutorial2.c new file mode 100644 index 0000000..121ce16 --- /dev/null +++ b/examples/tutorial/tutorial2.c @@ -0,0 +1,37 @@ +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t dimvector; + igraph_vector_int_t edges; + igraph_vector_bool_t periodic; + igraph_real_t avg_path_len; + + igraph_vector_int_init(&dimvector, 2); + VECTOR(dimvector)[0]=30; + VECTOR(dimvector)[1]=30; + + igraph_vector_bool_init(&periodic, 2); + igraph_vector_bool_fill(&periodic, true); + igraph_square_lattice(&graph, &dimvector, 0, IGRAPH_UNDIRECTED, /* mutual= */ false, &periodic); + + igraph_average_path_length(&graph, &avg_path_len, NULL, IGRAPH_UNDIRECTED, /* unconn= */ true); + printf("Average path length (lattice): %g\n", (double) avg_path_len); + + igraph_rng_seed(igraph_rng_default(), 42); /* seed RNG before first use */ + igraph_vector_int_init(&edges, 20); + for (igraph_integer_t i=0; i < igraph_vector_int_size(&edges); i++) { + VECTOR(edges)[i] = RNG_INTEGER(0, igraph_vcount(&graph) - 1); + } + + igraph_add_edges(&graph, &edges, NULL); + igraph_average_path_length(&graph, &avg_path_len, NULL, IGRAPH_UNDIRECTED, /* unconn= */ true); + printf("Average path length (randomized lattice): %g\n", (double) avg_path_len); + + igraph_vector_bool_destroy(&periodic); + igraph_vector_int_destroy(&dimvector); + igraph_vector_int_destroy(&edges); + igraph_destroy(&graph); + + return 0; +} diff --git a/examples/tutorial/tutorial3.c b/examples/tutorial/tutorial3.c new file mode 100644 index 0000000..81f3a2f --- /dev/null +++ b/examples/tutorial/tutorial3.c @@ -0,0 +1,48 @@ +#include + +int main(void) { + igraph_t graph; + igraph_vector_int_t v; + igraph_vector_int_t result; + igraph_vector_t result_real; + igraph_integer_t edges[] = { 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 }; + + igraph_vector_int_view(&v, edges, sizeof(edges) / sizeof(edges[0])); + igraph_create(&graph, &v, 0, IGRAPH_UNDIRECTED); + + igraph_vector_int_init(&result, 0); + igraph_vector_init(&result_real, 0); + + igraph_degree(&graph, &result, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + printf("Maximum degree is %10" IGRAPH_PRId ", vertex %2" IGRAPH_PRId ".\n", + igraph_vector_int_max(&result), + igraph_vector_int_which_max(&result)); + + igraph_closeness(&graph, &result_real, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, + /* weights= */ NULL, /* normalized= */ false); + printf("Maximum closeness is %10g, vertex %2" IGRAPH_PRId ".\n", + (double) igraph_vector_max(&result_real), + igraph_vector_which_max(&result_real)); + + igraph_betweenness(&graph, &result_real, igraph_vss_all(), + IGRAPH_UNDIRECTED, /* weights= */ NULL); + printf("Maximum betweenness is %10g, vertex %2" IGRAPH_PRId ".\n", + (double) igraph_vector_max(&result_real), + igraph_vector_which_max(&result_real)); + + igraph_vector_int_destroy(&result); + igraph_vector_destroy(&result_real); + igraph_destroy(&graph); + + return 0; +} diff --git a/igraph.pc.in b/igraph.pc.in new file mode 100644 index 0000000..f869537 --- /dev/null +++ b/igraph.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@PKGCONFIG_LIBDIR@ +includedir=@PKGCONFIG_INCLUDEDIR@ + +Name: libigraph +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ +URL: @PROJECT_HOMEPAGE_URL@ +Libs: -L${libdir} -ligraph +Libs.private: @PKGCONFIG_LIBS_PRIVATE@ +Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@ +Cflags: -I${includedir}/igraph diff --git a/include/igraph.h b/include/igraph.h new file mode 100644 index 0000000..8426ff7 --- /dev/null +++ b/include/igraph.h @@ -0,0 +1,101 @@ +/* -*- 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_H +#define IGRAPH_H + +#include "igraph_version.h" +#include "igraph_memory.h" +#include "igraph_error.h" +#include "igraph_random.h" +#include "igraph_progress.h" +#include "igraph_statusbar.h" + +#include "igraph_types.h" +#include "igraph_complex.h" +#include "igraph_vector.h" +#include "igraph_matrix.h" +#include "igraph_array.h" +#include "igraph_bitset.h" +#include "igraph_dqueue.h" +#include "igraph_stack.h" +#include "igraph_heap.h" +#include "igraph_psumtree.h" +#include "igraph_strvector.h" +#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" +#include "igraph_sparsemat.h" +#include "igraph_qsort.h" + +#include "igraph_constants.h" +#include "igraph_datatype.h" +#include "igraph_graph_list.h" +#include "igraph_iterators.h" +#include "igraph_interface.h" +#include "igraph_constructors.h" +#include "igraph_games.h" +#include "igraph_microscopic_update.h" +#include "igraph_centrality.h" +#include "igraph_paths.h" +#include "igraph_components.h" +#include "igraph_structural.h" +#include "igraph_transitivity.h" +#include "igraph_neighborhood.h" +#include "igraph_topology.h" +#include "igraph_bipartite.h" +#include "igraph_cliques.h" +#include "igraph_layout.h" +#include "igraph_visitor.h" +#include "igraph_community.h" +#include "igraph_conversion.h" +#include "igraph_foreign.h" +#include "igraph_motifs.h" +#include "igraph_operators.h" +#include "igraph_flow.h" +#include "igraph_nongraph.h" +#include "igraph_cocitation.h" +#include "igraph_adjlist.h" +#include "igraph_attributes.h" +#include "igraph_blas.h" +#include "igraph_lapack.h" +#include "igraph_arpack.h" +#include "igraph_mixing.h" +#include "igraph_separators.h" +#include "igraph_cohesive_blocks.h" +#include "igraph_eigen.h" +#include "igraph_hrg.h" +#include "igraph_threading.h" +#include "igraph_interrupt.h" +#include "igraph_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" +#include "igraph_cycles.h" +#include "igraph_reachability.h" + +#endif diff --git a/include/igraph_adjlist.h b/include/igraph_adjlist.h new file mode 100644 index 0000000..49a584c --- /dev/null +++ b/include/igraph_adjlist.h @@ -0,0 +1,224 @@ +/* -*- 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_error.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; + +typedef struct igraph_inclist_t { + igraph_integer_t length; + igraph_vector_int_t *incs; +} igraph_inclist_t; + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_init_complementer(const igraph_t *graph, + igraph_adjlist_t *al, + igraph_neimode_t mode, + igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_init_from_inclist( + const igraph_t *graph, igraph_adjlist_t *al, const igraph_inclist_t *il); +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 igraph_error_t igraph_adjlist_simplify(igraph_adjlist_t *al); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_print(const igraph_adjlist_t *al); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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 igraph_error_t 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[(igraph_integer_t)(no)]) + +IGRAPH_EXPORT igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, + igraph_neimode_t mode, igraph_bool_t duplicate); + +IGRAPH_EXPORT igraph_error_t igraph_inclist_init(const igraph_t *graph, + igraph_inclist_t *il, + igraph_neimode_t mode, + igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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_EXPORT igraph_error_t igraph_inclist_print(const igraph_inclist_t *il); +IGRAPH_EXPORT igraph_error_t 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[(igraph_integer_t)(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_lazy_adjlist_t; + +IGRAPH_EXPORT igraph_error_t 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_FUNCATTR_PURE igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlist_t *al); + +/** + * \define igraph_lazy_adjlist_has + * \brief Are adjacenct vertices already stored in a lazy adjacency list? + * + * \param al The lazy adjacency list. + * \param no The vertex ID to query. + * \return True if the adjacent vertices of this vertex are already computed + * and stored, false otherwise. + * + * Time complexity: O(1). + */ +#define igraph_lazy_adjlist_has(al,no) ((al)->adjs[(igraph_integer_t)(no)] != NULL) + +/** + * \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, or \c NULL upon error. Errors can only + * occur the first time this function is called for a given vertex. + * It is safe to modify this vector, + * modification does not affect the original graph. + * + * \sa \ref igraph_lazy_adjlist_has() to check if this function has + * already been called for a vertex. + * + * 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) \ + (igraph_lazy_adjlist_has(al,no) ? ((al)->adjs[(igraph_integer_t)(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_loops_t loops; +} igraph_lazy_inclist_t; + +IGRAPH_EXPORT igraph_error_t 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_FUNCATTR_PURE igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclist_t *il); + +/** + * \define igraph_lazy_inclist_has + * \brief Are incident edges already stored in a lazy inclist? + * + * \param il The lazy incidence list. + * \param no The vertex ID to query. + * \return True if the incident edges of this vertex are already computed + * and stored, false otherwise. + * + * Time complexity: O(1). + */ +#define igraph_lazy_inclist_has(il,no) ((il)->incs[(igraph_integer_t)(no)] != NULL) + +/** + * \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 il The lazy incidence list object. + * \param no The vertex ID to query. + * \return Pointer to a vector, or \c NULL upon error. Errors can only + * occur the first time this function is called for a given vertex. + * It is safe to modify this vector, + * modification does not affect the original graph. + * + * \sa \ref igraph_lazy_inclist_has() to check if this function has + * already been called for a vertex. + * + * 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(il,no) \ + (igraph_lazy_inclist_has(il,no) ? ((il)->incs[(igraph_integer_t)(no)]) \ + : (igraph_i_lazy_inclist_get_real(il,no))) +IGRAPH_EXPORT igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *il, igraph_integer_t no); + +__END_DECLS + +#endif diff --git a/include/igraph_arpack.h b/include/igraph_arpack.h new file mode 100644 index 0000000..8999819 --- /dev/null +++ b/include/igraph_arpack.h @@ -0,0 +1,337 @@ +/* -*- 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_ARPACK_H +#define IGRAPH_ARPACK_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_matrix.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/** + * \section about_arpack ARPACK interface in igraph + * + * + * ARPACK is a library for solving large scale eigenvalue problems. + * The package is designed to compute a few eigenvalues and corresponding + * eigenvectors of a general \c n by \c n matrix \c A. It is + * most appropriate for large sparse or structured matrices \c A where + * structured means that a matrix-vector product w <- Av requires + * order \c n rather than the usual order n^2 floating point + * operations. Please see + * http://www.caam.rice.edu/software/ARPACK/ for details. + * + * + * + * The eigenvalue calculation in ARPACK (in the simplest + * case) involves the calculation of the \c Av product where \c A + * is the matrix we work with and \c v is an arbitrary vector. A + * user-defined function of type \ref igraph_arpack_function_t + * is expected to perform this product. If the product can be done + * efficiently, e.g. if the matrix is sparse, then ARPACK is usually + * able to calculate the eigenvalues very quickly. + * + * + * In igraph, eigenvalue/eigenvector calculations usually + * involve the following steps: + * \olist + * \oli Initialization of an \ref igraph_arpack_options_t data + * structure using \ref igraph_arpack_options_init. + * \oli Setting some options in the initialized \ref + * igraph_arpack_options_t object. + * \oli Defining a function of type \ref igraph_arpack_function_t. + * The input of this function is a vector, and the output + * should be the output matrix multiplied by the input vector. + * \oli Calling \ref igraph_arpack_rssolve() (is the matrix is + * symmetric), or \ref igraph_arpack_rnsolve(). + * \endolist + * The \ref igraph_arpack_options_t object can be used multiple + * times. + * + * + * + * If we have many eigenvalue problems to solve, then it might worth + * to create an \ref igraph_arpack_storage_t object, and initialize it + * via \ref igraph_arpack_storage_init(). This structure contains all + * memory needed for ARPACK (with the given upper limit regerding to + * the size of the eigenvalue problem). Then many problems can be + * solved using the same \ref igraph_arpack_storage_t object, without + * always reallocating the required memory. + * The \ref igraph_arpack_storage_t object needs to be destroyed by + * calling \ref igraph_arpack_storage_destroy() on it, when it is not + * needed any more. + * + * + * + * igraph does not contain all + * ARPACK routines, only the ones dealing with symmetric and + * non-symmetric eigenvalue problems using double precision real + * numbers. + * + * + */ + +/** + * \struct igraph_arpack_options_t + * \brief Options for ARPACK. + * + * This data structure contains the options of the ARPACK eigenvalue + * solver routines. It must be initialized by calling \ref + * igraph_arpack_options_init() on it. Then it can be used for + * multiple ARPACK calls, as the ARPACK solvers do not modify it. + * + * Input options: + * + * \member bmat Character. Whether to solve a standard ('I') ot a + * generalized problem ('B'). + * \member n Dimension of the eigenproblem. + * \member which Specifies which eigenvalues/vectors to + * compute. Possible values for symmetric matrices: + * \clist \cli LA + * Compute \c nev largest (algebraic) eigenvalues. + * \cli SA + * Compute \c nev smallest (algebraic) eigenvalues. + * \cli LM + * Compute \c nev largest (in magnitude) eigenvalues. + * \cli SM + * Compute \c nev smallest (in magnitude) eigenvalues. + * \cli BE + * Compute \c nev eigenvalues, half from each end of + * the spectrum. When \c nev is odd, compute one + * more from the high en than from the low + * end. \endclist + * Possible values for non-symmetric matrices: + * \clist \cli LM + * Compute \c nev largest (in magnitude) eigenvalues. + * \cli SM + * Compute \c nev smallest (in magnitude) eigenvalues. + * \cli LR + * Compute \c nev eigenvalues of largest real part. + * \cli SR + * Compute \c nev eigenvalues of smallest real part. + * \cli LI + * Compute \c nev eigenvalues of largest imaginary part. + * \cli SI + * Compute \c nev eigenvalues of smallest imaginary + * part. \endclist + * \member nev The number of eigenvalues to be computed. + * \member tol Stopping criterion: the relative accuracy + * of the Ritz value is considered acceptable if its error is less + * than \c tol times its estimated value. If this is set to zero + * then machine precision is used. + * \member ncv Number of Lanczos vectors to be generated. Setting this + * to zero means that \ref igraph_arpack_rssolve and \ref igraph_arpack_rnsolve + * will determine a suitable value for \c ncv automatically. + * \member ldv Numberic scalar. It should be set to + * zero in the current igraph implementation. + * \member ishift Either zero or one. If zero then the shifts are + * provided by the user via reverse communication. If one then exact + * shifts with respect to the reduced tridiagonal matrix \c T. + * Please always set this to one. + * \member mxiter Maximum number of Arnoldi update iterations allowed. + * \member nb Blocksize to be used in the recurrence. Please always + * leave this on the default value, one. + * \member mode The type of the eigenproblem to be solved. + * Possible values if the input matrix is symmetric: + * \olist + * \oli A*x=lambda*x, A is symmetric. + * \oli A*x=lambda*M*x, A is + * symmetric, M is symmetric positive definite. + * \oli K*x=lambda*M*x, K is + * symmetric, M is symmetric positive semi-definite. + * \oli K*x=lambda*KG*x, K is + * symmetric positive semi-definite, KG is symmetric + * indefinite. + * \oli A*x=lambda*M*x, A is + * symmetric, M is symmetric positive + * semi-definite. (Cayley transformed mode.) \endolist + * Please note that only \c mode ==1 was tested and other values + * might not work properly. + * Possible values if the input matrix is not symmetric: + * \olist + * \oli A*x=lambda*x. + * \oli A*x=lambda*M*x, M is + * symmetric positive definite. + * \oli A*x=lambda*M*x, M is + * symmetric semi-definite. + * \oli A*x=lambda*M*x, M is + * symmetric semi-definite. \endolist + * Please note that only \c mode == 1 was tested and other values + * might not work properly. + * \member start Whether to use the supplied starting vector (1), or + * use a random starting vector (0). The starting vector must be + * supplied in the first column of the \c vectors argument of the + * \ref igraph_arpack_rssolve() of \ref igraph_arpack_rnsolve() call. + * + * Output options: + * + * \member info Error flag of ARPACK. Possible values: + * \clist \cli 0 + * Normal exit. + * \cli 1 + * Maximum number of iterations taken. + * \cli 3 + * No shifts could be applied during a cycle of the + * Implicitly restarted Arnoldi iteration. One possibility + * is to increase the size of \c ncv relative to \c + * nev. \endclist + * ARPACK can return other error flags as well, but these are + * converted to igraph errors, see \ref igraph_error_type_t. + * \member ierr Error flag of the second ARPACK call (one eigenvalue + * computation usually involves two calls to ARPACK). This is + * always zero, as other error codes are converted to igraph errors. + * \member noiter Number of Arnoldi iterations taken. + * \member nconv Number of converged Ritz values. This + * represents the number of Ritz values that satisfy the + * convergence critetion. + * \member numop Total number of matrix-vector multiplications. + * \member numopb Not used currently. + * \member numreo Total number of steps of re-orthogonalization. + * + * Internal options: + * \member lworkl Do not modify this option. + * \member sigma The shift for the shift-invert mode. + * \member sigmai The imaginary part of the shift, for the + * non-symmetric or complex shift-invert mode. + * \member iparam Do not modify this option. + * \member ipntr Do not modify this option. + * + */ + +typedef struct igraph_arpack_options_t { + /* INPUT */ + char bmat[1]; /* I-standard problem, G-generalized */ + int n; /* Dimension of the eigenproblem */ + char which[2]; /* LA, SA, LM, SM, BE */ + int nev; /* Number of eigenvalues to be computed */ + igraph_real_t tol; /* Stopping criterion */ + int ncv; /* Number of columns in V */ + int ldv; /* Leading dimension of V */ + int ishift; /* 0-reverse comm., 1-exact with tridiagonal */ + int mxiter; /* Maximum number of update iterations to take */ + int nb; /* Block size on the recurrence, only 1 works */ + int mode; /* The kind of problem to be solved (1-5) + 1: A*x=l*x, A symmetric + 2: A*x=l*M*x, A symm. M pos. def. + 3: K*x = l*M*x, K symm., M pos. semidef. + 4: K*x = l*KG*x, K s. pos. semidef. KG s. indef. + 5: A*x = l*M*x, A symm., M symm. pos. semidef. */ + int start; /* 0: random, 1: use the supplied vector */ + int lworkl; /* Size of temporary storage, default is fine */ + igraph_real_t sigma; /* The shift for modes 3,4,5 */ + igraph_real_t sigmai; /* The imaginary part of shift for rnsolve */ + /* OUTPUT */ + int info; /* What happened, see docs */ + int ierr; /* What happened in the dseupd call */ + int noiter; /* The number of iterations taken */ + int nconv; + int numop; /* Number of OP*x operations */ + int numopb; /* Number of B*x operations if BMAT='G' */ + int numreo; /* Number of steps of re-orthogonalizations */ + /* INTERNAL */ + int iparam[11]; + int ipntr[14]; +} igraph_arpack_options_t; + +/** + * \struct igraph_arpack_storage_t + * \brief Storage for ARPACK. + * + * Public members, do not modify them directly, these are considered + * to be read-only. + * \member maxn Maximum rank of matrix. + * \member maxncv Maximum NCV. + * \member maxldv Maximum LDV. + * + * These members are considered to be private: + * \member workl Working memory. + * \member workd Working memory. + * \member d Memory for eigenvalues. + * \member resid Memory for residuals. + * \member ax Working memory. + * \member select Working memory. + * \member di Memory for eigenvalues, non-symmetric case only. + * \member workev Working memory, non-symmetric case only. + */ + +typedef struct igraph_arpack_storage_t { + int maxn, maxncv, maxldv; + igraph_real_t *v; + igraph_real_t *workl; + igraph_real_t *workd; + igraph_real_t *d; + igraph_real_t *resid; + igraph_real_t *ax; + int *select; + /* The following two are only used for non-symmetric problems: */ + igraph_real_t *di; + igraph_real_t *workev; +} igraph_arpack_storage_t; + +IGRAPH_EXPORT void igraph_arpack_options_init(igraph_arpack_options_t *o); +IGRAPH_EXPORT igraph_arpack_options_t* igraph_arpack_options_get_default(void); + +IGRAPH_EXPORT igraph_error_t igraph_arpack_storage_init(igraph_arpack_storage_t *s, igraph_integer_t maxn, + igraph_integer_t maxncv, igraph_integer_t maxldv, igraph_bool_t symm); +IGRAPH_EXPORT void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); + +/** + * \typedef igraph_arpack_function_t + * \brief Type of the ARPACK callback function. + * + * \param to Pointer to an \c igraph_real_t, the result of the + * matrix-vector product is expected to be stored here. + * \param from Pointer to an \c igraph_real_t, the input matrix should + * be multiplied by the vector stored here. + * \param n The length of the vector (which is the same as the order + * of the input matrix). + * \param extra Extra argument to the matrix-vector calculation + * function. This is coming from the \ref igraph_arpack_rssolve() + * or \ref igraph_arpack_rnsolve() function. + * \return Error code. If not \c IGRAPH_SUCCESS, then the ARPACK solver considers + * this as an error, stops and calls the igraph error handler. + */ + +typedef igraph_error_t igraph_arpack_function_t(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, + igraph_integer_t nev); + +__END_DECLS + +#endif diff --git a/include/igraph_array.h b/include/igraph_array.h new file mode 100644 index 0000000..1f098d0 --- /dev/null +++ b/include/igraph_array.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_ARRAY_H +#define IGRAPH_ARRAY_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* 3D array */ +/* -------------------------------------------------- */ + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "igraph_array_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#include "igraph_pmt.h" +#include "igraph_array_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "igraph_array_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "igraph_array_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_BOOL + +__END_DECLS + +#endif diff --git a/include/igraph_array_pmt.h b/include/igraph_array_pmt.h new file mode 100644 index 0000000..9add5e8 --- /dev/null +++ b/include/igraph_array_pmt.h @@ -0,0 +1,54 @@ +/* -*- 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; + igraph_integer_t 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 igraph_error_t FUNCTION(igraph_array3, init)( + TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, + igraph_integer_t n3); +IGRAPH_EXPORT void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_array3, n)( + const TYPE(igraph_array3) *a, igraph_integer_t idx); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_array3, resize)( + TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, + igraph_integer_t n3); +IGRAPH_EXPORT void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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 igraph_error_t FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to, + const TYPE(igraph_array3) *from); diff --git a/include/igraph_attributes.h b/include/igraph_attributes.h new file mode 100644 index 0000000..9773ce7 --- /dev/null +++ b/include/igraph_attributes.h @@ -0,0 +1,860 @@ +/* -*- 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_config.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_datatype.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_strvector.h" +#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" +#include "igraph_iterators.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Attributes */ +/* -------------------------------------------------- */ + +/** + * \section about_attributes + * + * Attributes are numbers, boolean values or strings associated with + * the vertices or edges of a graph, or with the graph itself. E.g. you may + * label vertices with symbolic names or attach numeric weights to the edges + * of a graph. In addition to these three basic types, a custom object + * type is supported as well. + * + * 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 R all R types are allowed. There is also 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. This is normally done on program startup, and is kept untouched + * for the program's lifetime. It must be done before any graph object + * is created, as graphs created with a given attribute handler + * cannot be manipulated while a different attribute handler is + * active. + */ + +/** + * \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. E.g. in the 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_UNSPECIFIED Currently used internally + * as a "null value" or "placeholder value" in some algorithms. + * Attribute records with this type must not be passed to igraph + * functions. + * \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_OBJECT Custom attribute type, to be + * used for special data types by client applications. The R and + * Python interfaces use this for attributes that hold R or Python + * objects. Usually ignored by igraph functions. + */ +typedef enum { IGRAPH_ATTRIBUTE_UNSPECIFIED = 0, + IGRAPH_ATTRIBUTE_DEFAULT IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_ATTRIBUTE_UNSPECIFIED, + IGRAPH_ATTRIBUTE_NUMERIC = 1, + IGRAPH_ATTRIBUTE_BOOLEAN = 2, + IGRAPH_ATTRIBUTE_STRING = 3, + IGRAPH_ATTRIBUTE_OBJECT = 127 + } 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 igraph_error_t igraph_attribute_combination_init(igraph_attribute_combination_t *comb); +IGRAPH_EXPORT igraph_error_t igraph_attribute_combination(igraph_attribute_combination_t *comb, ...); +IGRAPH_EXPORT void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, + const char *name); +IGRAPH_EXPORT igraph_error_t 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, which is guaranteed to be set to a null pointer + * before this function is called. 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. It is supposed + * to do any cleanup operations that are need to dispose of the \c attr + * member of the \c igraph_t object properly. The caller will set the + * \c attr member to a null pointer after this function returns. + * \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 supposed to populate the \c attr member of the target + * \c igraph_t object. The \c attr member of the target is guaranteed to be + * set to a null pointer before this function is called. 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 Called when a new graph is created based on an + * existing one such that there is a mapping from the vertices of the new + * graph back to the vertices of the old graph (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. Note that the old and the new graph + * may be the same. If the two graph instances are \em not the same, implementors + * may safely assume that the new graph has no vertex attributes yet (but it + * may already have graph or edge attributes by the time this function is + * called). + * \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. It is guaranteed that the old and the + * new graph instances are different when this callback is called. + * Implementors may safely assume that the new graph has no vertex attributes + * yet (but it may already have graph or edge attributes by the time this + * function is called). + * \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 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). Note that the old and the new graph instances \em may + * be the same. If the two graph instances are \em not the same, implementors + * may safely assume that the new graph has no edge attributes yet (but it + * may already have graph or vertex attributes by the time this function is + * called). + * \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. It is guaranteed that the old and the + * new graph instances are different when this callback is called. + * Implementors may safely assume that the new graph has no edge attributes + * yet (but it may already have graph or vertex attributes by the time this + * function is called). + * \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 { + igraph_error_t (*init)(igraph_t *graph, igraph_vector_ptr_t *attr); + void (*destroy)(igraph_t *graph); + igraph_error_t (*copy)(igraph_t *to, const igraph_t *from, igraph_bool_t ga, + igraph_bool_t va, igraph_bool_t ea); + igraph_error_t (*add_vertices)(igraph_t *graph, igraph_integer_t nv, igraph_vector_ptr_t *attr); + igraph_error_t (*permute_vertices)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_t *idx); + igraph_error_t (*combine_vertices)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_t *merges, + const igraph_attribute_combination_t *comb); + igraph_error_t (*add_edges)(igraph_t *graph, const igraph_vector_int_t *edges, + igraph_vector_ptr_t *attr); + igraph_error_t (*permute_edges)(const igraph_t *graph, + igraph_t *newgraph, const igraph_vector_int_t *idx); + igraph_error_t (*combine_edges)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_t *merges, + const igraph_attribute_combination_t *comb); + igraph_error_t (*get_info)(const igraph_t *graph, + igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, igraph_vector_int_t *etypes); + igraph_bool_t (*has_attr)(const igraph_t *graph, igraph_attribute_elemtype_t type, + const char *name); + igraph_error_t (*gettype)(const igraph_t *graph, igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, const char *name); + igraph_error_t (*get_numeric_graph_attr)(const igraph_t *graph, const char *name, + igraph_vector_t *value); + igraph_error_t (*get_string_graph_attr)(const igraph_t *graph, const char *name, + igraph_strvector_t *value); + igraph_error_t (*get_bool_graph_attr)(const igraph_t *igraph, const char *name, + igraph_vector_bool_t *value); + igraph_error_t (*get_numeric_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_vector_t *value); + igraph_error_t (*get_string_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_strvector_t *value); + igraph_error_t (*get_bool_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value); + igraph_error_t (*get_numeric_edge_attr)(const igraph_t *graph, const char *name, + igraph_es_t es, + igraph_vector_t *value); + igraph_error_t (*get_string_edge_attr)(const igraph_t *graph, const char *name, + igraph_es_t es, + igraph_strvector_t *value); + igraph_error_t (*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 igraph_error_t igraph_cattribute_VANV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_t *result); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EANV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_t *result); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VASV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_strvector_t *result); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EASV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_strvector_t *result); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VABV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_bool_t *result); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EABV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_bool_t *result); + +IGRAPH_EXPORT igraph_error_t igraph_cattribute_list(const igraph_t *graph, + igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, igraph_vector_int_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 igraph_error_t igraph_cattribute_GAN_set(igraph_t *graph, const char *name, + igraph_real_t value); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_GAB_set(igraph_t *graph, const char *name, + igraph_bool_t value); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_GAS_set(igraph_t *graph, const char *name, + const char *value); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAN_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_real_t value); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAB_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_bool_t value); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAS_set(igraph_t *graph, const char *name, + igraph_integer_t vid, const char *value); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAN_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_real_t value); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAB_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_bool_t value); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAS_set(igraph_t *graph, const char *name, + igraph_integer_t eid, const char *value); + +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, + const igraph_strvector_t *sv); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v); +IGRAPH_EXPORT igraph_error_t 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/include/igraph_bipartite.h b/include/igraph_bipartite.h new file mode 100644 index 0000000..f32fe8a --- /dev/null +++ b/include/igraph_bipartite.h @@ -0,0 +1,112 @@ +/* -*- 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_datatype.h" +#include "igraph_constants.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_matrix.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Bipartite networks */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types, + const igraph_vector_int_t *edges, + igraph_bool_t directed); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_bipartite_projection(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_t *proj1, + igraph_t *proj2, + igraph_vector_int_t *multiplicity1, + igraph_vector_int_t *multiplicity2, + igraph_integer_t probe1); + +IGRAPH_EXPORT igraph_error_t igraph_biadjacency(igraph_t *graph, igraph_vector_bool_t *types, + const igraph_matrix_t *input, igraph_bool_t directed, + igraph_neimode_t mode, igraph_bool_t multiple); + +IGRAPH_EXPORT igraph_error_t igraph_get_biadjacency(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_matrix_t *res, + igraph_vector_int_t *row_ids, + igraph_vector_int_t *col_ids); + +IGRAPH_EXPORT igraph_error_t igraph_is_bipartite(const igraph_t *graph, + igraph_bool_t *res, + igraph_vector_bool_t *types); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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); + +/* Deprecated functions: */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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 IGRAPH_DEPRECATED igraph_error_t igraph_get_incidence( + const igraph_t *graph, const igraph_vector_bool_t *types, igraph_matrix_t *res, + igraph_vector_int_t *row_ids, igraph_vector_int_t *col_ids +); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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 +); + +__END_DECLS + +#endif diff --git a/include/igraph_bitset.h b/include/igraph_bitset.h new file mode 100644 index 0000000..fcf0937 --- /dev/null +++ b/include/igraph_bitset.h @@ -0,0 +1,267 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2024 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_BITSET_H +#define IGRAPH_BITSET_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_vector.h" + +/* Required for MSVC intrinsics such as __popcnt and __popcnt64 */ +#ifdef _MSC_VER + #include "intrin.h" +#endif + +__BEGIN_DECLS + +/** + * \ingroup bitset + * \section igraph_bitset_accessing_elements Accessing elements + * + * The simplest way to access an element of a bitset is + * to use the \ref IGRAPH_BIT_TEST(), \ref IGRAPH_BIT_SET() and \ref IGRAPH_BIT_CLEAR() macros. + * + * + * There are a few other macros which allow manual manipulation of bitsets. + * Those are \ref VECTOR(), \ref IGRAPH_BIT_SLOT(), \ref IGRAPH_BIT_MASK() and + * \ref IGRAPH_BIT_NSLOTS(). + */ + +/** + * \ingroup bitset + * \define IGRAPH_BIT_MASK + * \brief Computes mask used to access a specific bit of an integer. + * + * \experimental + * + * Used in combination with \ref IGRAPH_BIT_SLOT() to access an element of a bitset. + * + * Usage: + * \verbatim IGRAPH_BIT_MASK(10) \endverbatim + * to obtain an integer where only the 11th least significant bit is set. + * + * Note that passing negative values here results in undefined behaviour. + * + * \param b The only bit index that should have its bit set. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_MASK(i) ((igraph_uint_t)(1) << ((i) % IGRAPH_INTEGER_SIZE)) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_SLOT + * \brief Computes index used to access a specific slot of a bitset. + * + * \experimental + * + * Used in combination with \ref IGRAPH_BIT_MASK to access an element of a bitset. + * + * Usage: + * \verbatim IGRAPH_BIT_SLOT(70) \endverbatim + * will return 1 if using 64-bit words or 2 if using 32-bit words. + * + * \param i The bit index whose slot should be determined. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_SLOT(i) ((i) / IGRAPH_INTEGER_SIZE) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_SET + * \brief Sets a specific bit in a bitset to 1 without altering other bits. + * + * \experimental + * + * Usage: + * \verbatim IGRAPH_BIT_SET(bitset, 3) \endverbatim + * will set the fourth least significant bit in the bitset to 1. + * + * \param bitset The bitset + * \param i The bit index that should have its bit set to 1 after the operation. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_SET(bitset, i) (VECTOR((bitset))[IGRAPH_BIT_SLOT(i)] |= IGRAPH_BIT_MASK(i)) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_CLEAR + * \brief Sets a specific bit in a bitset to 0 without altering other bits. + * + * \experimental + * + * Usage: + * \verbatim IGRAPH_BIT_CLEAR(bitset, 4) \endverbatim + * will set the fifth least significant bit in the bitset to 0. + * + * \param bitset The bitset + * \param i The bit index that should have its bit set to 0 after the operation. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_CLEAR(bitset, i) (VECTOR((bitset))[IGRAPH_BIT_SLOT(i)] &= ~IGRAPH_BIT_MASK(i)) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_TEST + * \brief Tests whether a bit is set in a bitset. + * + * \experimental + * + * Returns 0 if the bit at the specified bit index is not set, + * otherwise returns a non-zero value. + * + * Usage: + * \verbatim IGRAPH_BIT_TEST(bitset, 7) \endverbatim + * will test the eighth least significant bit in the bitset. + * + * \param bitset The bitset + * \param i The bit index that should have its bit tested. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_TEST(bitset, i) (VECTOR((bitset))[IGRAPH_BIT_SLOT(i)] & IGRAPH_BIT_MASK(i)) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_NSLOTS + * \brief Computes the number of slots required to store a specified number of bits. + * + * \experimental + * + * Usage: + * \verbatim IGRAPH_BIT_NSLOTS(70) \endverbatim + * will return 2 if using 64-bit words and 3 if using 32-bit words. + * \verbatim IGRAPH_BIT_NSLOTS(128) \endverbatim + * will return 2 if using 64-bit words and 4 if using 32-bit words. + * + * \param nbits The specified number of bits. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_NSLOTS(nbits) ((nbits + IGRAPH_INTEGER_SIZE - (igraph_integer_t)(1)) / IGRAPH_INTEGER_SIZE) + + +#if defined(__GNUC__) + /* GCC and Clang support these six builtins from very early versions. */ + + #define IGRAPH_I_POPCOUNT32(x) __builtin_popcount(x) + #define IGRAPH_I_POPCOUNT64(x) __builtin_popcountll(x) + + /* The result of the following four builtins is undefined for zero input, + * therefore we handle this specially. + * See https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */ + #define IGRAPH_I_CTZ32(x) (x ? __builtin_ctz(x) : 32) + #define IGRAPH_I_CTZ64(x) (x ? __builtin_ctzll(x) : 64) + #define IGRAPH_I_CLZ32(x) (x ? __builtin_clz(x) : 32) + #define IGRAPH_I_CLZ64(x) (x ? __builtin_clzll(x) : 64) + +#else + /* Non-GNU compilers, i.e. MSVC and others. Attempt to use MSVC intrinsics + * when available, otherwise use fallback implementation. */ + + #if IGRAPH_INTEGER_SIZE == 32 + #ifdef HAVE__POPCNT + #define IGRAPH_I_POPCOUNT32(x) __popcnt(x) + #else + igraph_integer_t igraph_i_popcnt(igraph_uint_t x); + #define IGRAPH_I_POPCOUNT32(x) igraph_i_popcnt(x) + #endif + #elif IGRAPH_INTEGER_SIZE == 64 + #ifdef HAVE__POPCNT64 + #define IGRAPH_I_POPCOUNT64(x) __popcnt64(x) + #else + igraph_integer_t igraph_i_popcnt(igraph_uint_t x); + #define IGRAPH_I_POPCOUNT64(x) igraph_i_popcnt(x) + #endif + #else + #error "Unexpected IGRAPH_INTEGER_SIZE value." + #endif + + igraph_integer_t igraph_i_ctz32(igraph_uint_t x); + igraph_integer_t igraph_i_ctz64(igraph_uint_t x); + igraph_integer_t igraph_i_clz32(igraph_uint_t x); + igraph_integer_t igraph_i_clz64(igraph_uint_t x); + #define IGRAPH_I_CTZ32(x) igraph_i_ctz32(x) + #define IGRAPH_I_CTZ64(x) igraph_i_ctz64(x) + #define IGRAPH_I_CLZ32(x) igraph_i_clz32(x) + #define IGRAPH_I_CLZ64(x) igraph_i_clz64(x) + +#endif + + +#if IGRAPH_INTEGER_SIZE == 32 + #define IGRAPH_POPCOUNT IGRAPH_I_POPCOUNT32 + #define IGRAPH_CLZ IGRAPH_I_CLZ32 + #define IGRAPH_CTZ IGRAPH_I_CTZ32 +#elif IGRAPH_INTEGER_SIZE == 64 + #define IGRAPH_POPCOUNT IGRAPH_I_POPCOUNT64 + #define IGRAPH_CLZ IGRAPH_I_CLZ64 + #define IGRAPH_CTZ IGRAPH_I_CTZ64 +#else + #error "Unexpected IGRAPH_INTEGER_SIZE value." +#endif + +#define IGRAPH_CLO(x) IGRAPH_CLZ(~(x)) +#define IGRAPH_CTO(x) IGRAPH_CTZ(~(x)) + +typedef struct { + igraph_integer_t size; + igraph_uint_t *stor_begin; + igraph_uint_t *stor_end; +} igraph_bitset_t; + +IGRAPH_EXPORT igraph_error_t igraph_bitset_init(igraph_bitset_t *bitset, igraph_integer_t size); +IGRAPH_EXPORT void igraph_bitset_destroy(igraph_bitset_t *bitset); +IGRAPH_EXPORT igraph_error_t igraph_bitset_init_copy(igraph_bitset_t *dest, const igraph_bitset_t *src); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_capacity(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_size(const igraph_bitset_t *bitset); +IGRAPH_EXPORT igraph_error_t igraph_bitset_reserve(igraph_bitset_t *bitset, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_error_t igraph_bitset_resize(igraph_bitset_t *bitset, igraph_integer_t new_size); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_popcount(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_countl_zero(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_countl_one(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_countr_zero(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_countr_one(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_bitset_is_all_zero(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_bitset_is_all_one(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_bitset_is_any_zero(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_bitset_is_any_one(const igraph_bitset_t *bitset); +IGRAPH_EXPORT void igraph_bitset_or(igraph_bitset_t *dest, const igraph_bitset_t *src1, const igraph_bitset_t *src2); +IGRAPH_EXPORT void igraph_bitset_and(igraph_bitset_t *dest, const igraph_bitset_t *src1, const igraph_bitset_t *src2); +IGRAPH_EXPORT void igraph_bitset_xor(igraph_bitset_t *dest, const igraph_bitset_t *src1, const igraph_bitset_t *src2); +IGRAPH_EXPORT void igraph_bitset_not(igraph_bitset_t *dest, const igraph_bitset_t *src); +IGRAPH_EXPORT void igraph_bitset_fill(igraph_bitset_t *bitset, igraph_bool_t value); +IGRAPH_EXPORT void igraph_bitset_null(igraph_bitset_t *bitset); +IGRAPH_EXPORT igraph_error_t igraph_bitset_fprint(const igraph_bitset_t *bitset, FILE *file); +IGRAPH_EXPORT igraph_error_t igraph_bitset_print(const igraph_bitset_t *bitset); + +#define IGRAPH_BITSET_INIT_FINALLY(bitset, size) \ +do { IGRAPH_CHECK(igraph_bitset_init(bitset, size)); \ + IGRAPH_FINALLY(igraph_bitset_destroy, bitset); } while (0) + +__END_DECLS + +#endif /* IGRAPH_BITSET_H */ diff --git a/include/igraph_bitset_list.h b/include/igraph_bitset_list.h new file mode 100644 index 0000000..f45bc12 --- /dev/null +++ b/include/igraph_bitset_list.h @@ -0,0 +1,51 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2024 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_BITSET_LIST_H +#define IGRAPH_BITSET_LIST_H + +#include "igraph_bitset.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* List of graphs */ +/* -------------------------------------------------- */ + +#define BITSET_LIST +#define BASE_BITSET +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_BITSET +#undef BITSET_LIST + +#define IGRAPH_BITSET_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_bitset_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_bitset_list_destroy, v); } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_blas.h b/include/igraph_blas.h new file mode 100644 index 0000000..ae7daa4 --- /dev/null +++ b/include/igraph_blas.h @@ -0,0 +1,72 @@ +/* -*- 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_BLAS_H +#define IGRAPH_BLAS_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_matrix.h" + +__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. + * Please see http://www.netlib.org/blas/ for details and a reference + * implementation in Fortran. igraph contains some wrapper functions + * that can be used to call BLAS routines in a somewhat more + * user-friendly way. Not all BLAS routines are included in igraph, + * and even those which are included might not have wrappers; + * the extension of the set of wrapped functions will probably be driven + * by igraph's internal requirements. The wrapper functions usually + * substitute double-precision floating point arrays used by BLAS with + * \type igraph_vector_t and \type igraph_matrix_t instances and also + * remove those parameters (such as the number of rows/columns) that + * can be inferred from the passed arguments directly. + * + */ + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_blas_dgemm(igraph_bool_t transpose_a, + igraph_bool_t transpose_b, igraph_real_t alpha, const igraph_matrix_t* a, + const igraph_matrix_t* b, igraph_real_t beta, igraph_matrix_t* c); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_real_t *res); + +__END_DECLS + +#endif diff --git a/include/igraph_centrality.h b/include/igraph_centrality.h new file mode 100644 index 0000000..816815b --- /dev/null +++ b/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_error.h" +#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_iterators.h" +#include "igraph_arpack.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Centrality */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_closeness(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_int_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 igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_int_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 igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, + igraph_bool_t directed, + const igraph_vector_t *weights); +IGRAPH_EXPORT igraph_error_t 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); +IGRAPH_EXPORT igraph_error_t igraph_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + const igraph_vs_t sources, const igraph_vs_t targets, + const igraph_vector_t *weights); +IGRAPH_EXPORT igraph_error_t igraph_edge_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t directed, + const igraph_vs_t sources, const igraph_vs_t targets, + const igraph_vector_t *weights); + +/** + * \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 igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, igraph_vector_t *hub_vector, + igraph_vector_t *authority_vector, + igraph_real_t *value, igraph_bool_t scale, + const igraph_vector_t *weights, + igraph_arpack_options_t *options); + +IGRAPH_EXPORT igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, + igraph_vs_t vids, const igraph_vector_t *weights); + +IGRAPH_EXPORT igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, + igraph_vector_t *ins, igraph_vector_t *outs); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t igraph_centralization(const igraph_vector_t *scores, + igraph_real_t theoretical_max, + igraph_bool_t normalized); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_centralization_betweenness_tmax(const igraph_t *graph, + igraph_integer_t nodes, + igraph_bool_t directed, + igraph_real_t *res); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_centralization_closeness_tmax(const igraph_t *graph, + igraph_integer_t nodes, + igraph_neimode_t mode, + igraph_real_t *res); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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_DEPRECATED IGRAPH_EXPORT igraph_error_t 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_DEPRECATED IGRAPH_EXPORT igraph_error_t 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); + +__END_DECLS + +#endif diff --git a/include/igraph_cliques.h b/include/igraph_cliques.h new file mode 100644 index 0000000..141dd26 --- /dev/null +++ b/include/igraph_cliques.h @@ -0,0 +1,116 @@ +/* -*- 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_error.h" +#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Cliques, maximal independent vertex sets */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques( + const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_integer_t min_size, igraph_integer_t max_size +); +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_file(const igraph_t *graph, + FILE *outfile, + igraph_integer_t min_size, + igraph_integer_t max_size); +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_count(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t min_size, + igraph_integer_t max_size); +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_subset( + const igraph_t *graph, const igraph_vector_int_t *subset, + igraph_vector_int_list_t *res, igraph_integer_t *no, + FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size +); +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_hist(const igraph_t *graph, + igraph_vector_t *hist, + igraph_integer_t min_size, + igraph_integer_t max_size); + +IGRAPH_EXPORT igraph_error_t igraph_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_integer_t min_size, igraph_integer_t max_size); +IGRAPH_EXPORT igraph_error_t igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t min_size, igraph_integer_t max_size); +IGRAPH_EXPORT igraph_error_t igraph_largest_cliques(const igraph_t *graph, + igraph_vector_int_list_t *cliques); +IGRAPH_EXPORT igraph_error_t igraph_clique_number(const igraph_t *graph, igraph_integer_t *no); +IGRAPH_EXPORT igraph_error_t igraph_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res, + igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal); +IGRAPH_EXPORT igraph_error_t igraph_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res); +IGRAPH_EXPORT igraph_error_t igraph_weighted_clique_number(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_real_t *res); +IGRAPH_EXPORT igraph_error_t igraph_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res, + igraph_integer_t min_size, + igraph_integer_t max_size); +IGRAPH_EXPORT igraph_error_t igraph_largest_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res); +IGRAPH_EXPORT igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res); +IGRAPH_EXPORT igraph_error_t 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. The clique is owned by the clique search + * routine. You do not need to destroy or free it if you do not want to store + * it; however, if you want to hold on to it for a longer period of time, you + * need to make a copy of it on your own and store the copy itself. + * \param arg This extra argument was passed to \ref + * igraph_cliques_callback() when it was called. + * \return Error code; \c IGRAPH_SUCCESS to continue the search or + * \c IGRAPH_STOP to stop the search without signaling an error. + */ +typedef igraph_error_t igraph_clique_handler_t(const igraph_vector_int_t *clique, void *arg); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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/include/igraph_cocitation.h b/include/igraph_cocitation.h new file mode 100644 index 0000000..4dfd406 --- /dev/null +++ b/include/igraph_cocitation.h @@ -0,0 +1,67 @@ +/* -*- 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_error.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 igraph_error_t igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids); +IGRAPH_EXPORT igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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/include/igraph_cohesive_blocks.h b/include/igraph_cohesive_blocks.h new file mode 100644 index 0000000..a52cf68 --- /dev/null +++ b/include/igraph_cohesive_blocks.h @@ -0,0 +1,43 @@ +/* -*- 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_COHESIVE_BLOCKS_H +#define IGRAPH_COHESIVE_BLOCKS_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_vector.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, + igraph_vector_int_list_t *blocks, + igraph_vector_int_t *cohesion, + igraph_vector_int_t *parent, + igraph_t *block_tree); + +__END_DECLS + +#endif diff --git a/include/igraph_coloring.h b/include/igraph_coloring.h new file mode 100644 index 0000000..845cd77 --- /dev/null +++ b/include/igraph_coloring.h @@ -0,0 +1,55 @@ +/* + 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" +#include "igraph_error.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 the vertex with largest number of already colored neighbors. + * \enumval IGRAPH_COLORING_GREEDY_DSATUR + * Choose the vertex with largest number of unique colors in its neighborhood, i.e. its + * "saturation degree". When multiple vertices have the same saturation degree, choose + * the one with the most not yet colored neighbors. Added in igraph 0.10.4. This heuristic + * is known as "DSatur", and was proposed in + * Daniel Brélaz: New methods to color the vertices of a graph, + * Commun. ACM 22, 4 (1979), 251–256. https://doi.org/10.1145/359094.359101 + */ +typedef enum { + IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS = 0, + IGRAPH_COLORING_GREEDY_DSATUR = 1 +} igraph_coloring_greedy_t; + +IGRAPH_EXPORT igraph_error_t 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/include/igraph_community.h b/include/igraph_community.h new file mode 100644 index 0000000..c38cef1 --- /dev/null +++ b/include/igraph_community.h @@ -0,0 +1,259 @@ +/* -*- 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_arpack.h" +#include "igraph_constants.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* K-Cores and K-Truss */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_coreness( + const igraph_t *graph, igraph_vector_int_t *cores, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_trussness( + const igraph_t* graph, igraph_vector_int_t* trussness); + +/* -------------------------------------------------- */ +/* Community Structure */ +/* -------------------------------------------------- */ + +/* TODO: cut.community */ +/* TODO: edge.type.matrix */ +/* TODO: */ + +IGRAPH_EXPORT igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, + igraph_real_t *modularity, + igraph_vector_int_t *membership, + const igraph_vector_t *weights); + +IGRAPH_EXPORT igraph_error_t igraph_community_spinglass(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *membership, + igraph_vector_int_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_spinglass_implementation_t implementation, + igraph_real_t gamma_minus); + +IGRAPH_EXPORT igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_integer_t vertex, + igraph_vector_int_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 igraph_error_t igraph_community_walktrap(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_integer_t steps, + igraph_matrix_int_t *merges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership); + +IGRAPH_EXPORT igraph_error_t igraph_community_infomap(const igraph_t * graph, + const igraph_vector_t *e_weights, + const igraph_vector_t *v_weights, + igraph_integer_t nb_trials, + igraph_vector_int_t *membership, + igraph_real_t *codelength); + +IGRAPH_EXPORT igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, + igraph_vector_int_t *removed_edges, + igraph_vector_t *edge_betweenness, + igraph_matrix_int_t *merges, + igraph_vector_int_t *bridges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership, + igraph_bool_t directed, + const igraph_vector_t *weights); +IGRAPH_EXPORT igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, + const igraph_bool_t directed, + const igraph_vector_int_t *edges, + const igraph_vector_t *weights, + igraph_matrix_int_t *merges, + igraph_vector_int_t *bridges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership); + +IGRAPH_EXPORT igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_matrix_int_t *merges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership); + +IGRAPH_EXPORT igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, + igraph_integer_t nodes, + igraph_integer_t steps, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize); +IGRAPH_EXPORT igraph_error_t igraph_le_community_to_membership(const igraph_matrix_int_t *merges, + igraph_integer_t steps, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize); + +IGRAPH_EXPORT igraph_error_t igraph_community_voronoi( + const igraph_t *graph, + igraph_vector_int_t *membership, igraph_vector_int_t *generators, igraph_real_t *modularity, + const igraph_vector_t *lengths, const igraph_vector_t *weights, + igraph_neimode_t mode, igraph_real_t r); + +IGRAPH_EXPORT igraph_error_t igraph_modularity(const igraph_t *graph, + const igraph_vector_int_t *membership, + const igraph_vector_t *weights, + const igraph_real_t resolution, + const igraph_bool_t directed, + igraph_real_t *modularity); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_reindex_membership(igraph_vector_int_t *membership, + igraph_vector_int_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 igraph_error_t igraph_community_leading_eigenvector_callback_t( + const igraph_vector_int_t *membership, + igraph_integer_t comm, + igraph_real_t eigenvalue, + const igraph_vector_t *eigenvector, + igraph_arpack_function_t *arpack_multiplier, + void *arpack_extra, + void *extra); + +IGRAPH_EXPORT igraph_error_t igraph_community_leading_eigenvector(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_matrix_int_t *merges, + igraph_vector_int_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_list_t *eigenvectors, + igraph_vector_t *history, + igraph_community_leading_eigenvector_callback_t *callback, + void *callback_extra); + +IGRAPH_EXPORT igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, + igraph_integer_t no_of_communities, + igraph_vector_int_t *membership); + +IGRAPH_EXPORT igraph_error_t igraph_community_label_propagation(const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_neimode_t mode, + const igraph_vector_t *weights, + const igraph_vector_int_t *initial, + const igraph_vector_bool_t *fixed); + +IGRAPH_EXPORT igraph_error_t igraph_community_multilevel(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_real_t resolution, + igraph_vector_int_t *membership, + igraph_matrix_int_t *memberships, + igraph_vector_t *modularity); + +IGRAPH_EXPORT igraph_error_t 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, + const igraph_integer_t n_iterations, + igraph_vector_int_t *membership, + igraph_integer_t *nb_clusters, + igraph_real_t *quality); +/* -------------------------------------------------- */ +/* Community Structure Comparison */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_compare_communities(const igraph_vector_int_t *comm1, + const igraph_vector_int_t *comm2, + igraph_real_t* result, + igraph_community_comparison_t method); +IGRAPH_EXPORT igraph_error_t igraph_split_join_distance(const igraph_vector_int_t *comm1, + const igraph_vector_int_t *comm2, + igraph_integer_t* distance12, + igraph_integer_t* distance21); + +__END_DECLS + +#endif diff --git a/include/igraph_complex.h b/include/igraph_complex.h new file mode 100644 index 0000000..6288d65 --- /dev/null +++ b/include/igraph_complex.h @@ -0,0 +1,113 @@ +/* -*- 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_FUNCATTR_CONST igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta); + +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, + igraph_complex_t z2, + igraph_real_t tol); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_bool_t igraph_complex_almost_equals(igraph_complex_t z1, + igraph_complex_t z2, + igraph_real_t eps); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_real_t igraph_complex_arg(igraph_complex_t z); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_real_t igraph_complex_abs(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_real_t igraph_complex_logabs(igraph_complex_t z); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_add(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sub(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_mul(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_div(igraph_complex_t z1, + igraph_complex_t z2); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_add_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_add_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sub_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_mul_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_div_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_div_imag(igraph_complex_t z, + igraph_real_t y); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_conj(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_neg(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_inv(igraph_complex_t z); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sqrt(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_exp(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_pow(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_pow_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_log(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_log10(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_log_b(igraph_complex_t z, + igraph_complex_t b); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sin(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_cos(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_tan(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sec(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_csc(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_cot(igraph_complex_t z); + +IGRAPH_EXPORT int igraph_complex_printf(igraph_complex_t val); +IGRAPH_EXPORT int igraph_complex_fprintf(FILE *file, igraph_complex_t val); +IGRAPH_EXPORT int igraph_complex_printf_aligned(int width, igraph_complex_t val); +IGRAPH_EXPORT int igraph_complex_fprintf_aligned(FILE *file, int width, igraph_complex_t val); +IGRAPH_EXPORT int igraph_complex_snprintf(char *str, size_t size, igraph_complex_t val); + +__END_DECLS + +#endif diff --git a/include/igraph_components.h b/include/igraph_components.h new file mode 100644 index 0000000..30721b8 --- /dev/null +++ b/include/igraph_components.h @@ -0,0 +1,73 @@ +/* -*- 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_datatype.h" +#include "igraph_error.h" +#include "igraph_graph_list.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" /* because of igraph_decompose_destroy() */ + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Components */ +/* -------------------------------------------------- */ + +/* Deprecated alias to igraph_connected_components; will be removed in 0.11 */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_clusters(const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no, + igraph_connectedness_t mode); +IGRAPH_EXPORT igraph_error_t igraph_connected_components(const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no, + igraph_connectedness_t mode); +IGRAPH_EXPORT igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, + igraph_connectedness_t mode); +IGRAPH_EXPORT igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *components, + igraph_connectedness_t mode, + igraph_integer_t maxcompno, igraph_integer_t minelements); +IGRAPH_EXPORT igraph_error_t igraph_articulation_points(const igraph_t *graph, + igraph_vector_int_t *res); +IGRAPH_EXPORT igraph_error_t igraph_biconnected_components(const igraph_t *graph, + igraph_integer_t *no, + igraph_vector_int_list_t *tree_edges, + igraph_vector_int_list_t *component_edges, + igraph_vector_int_list_t *components, + igraph_vector_int_t *articulation_points); +IGRAPH_EXPORT igraph_error_t igraph_is_biconnected(const igraph_t *graph, igraph_bool_t *result); +IGRAPH_EXPORT igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridges); + +/* Deprecated in igraph 0.10 when we switched to igraph_graph_list_t. Will be + * removed in 0.11 */ +IGRAPH_EXPORT IGRAPH_DEPRECATED void igraph_decompose_destroy(igraph_vector_ptr_t *complist); + +__END_DECLS + +#endif diff --git a/include/igraph_config.h.in b/include/igraph_config.h.in new file mode 100644 index 0000000..99e6fee --- /dev/null +++ b/include/igraph_config.h.in @@ -0,0 +1,55 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-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_CONFIG_H +#define IGRAPH_CONFIG_H + +#include "igraph_decls.h" + +__BEGIN_DECLS + +/** + * \define IGRAPH_INTEGER_SIZE + * + * Specifies the size of igraph's integer data type; must be one of 32 (for + * 32-bit integers) or 64 (for 64-bit integers). + */ +#define IGRAPH_INTEGER_SIZE @IGRAPH_INTEGER_SIZE@ + +#define IGRAPH_DEPRECATED_ENUMVAL @IGRAPH_DEPRECATED_ENUMVAL@ + +/** + * \define IGRAPH_BOOL_TYPE + * + * Specifies the C type to be used for igraph_bool_t. This is added here _only_ + * to support the R interface, where we want to be able to create views into + * R boolean vectors and treat them as an igraph_vector_bool_t, which requires + * us to align igraph_bool_t with R's boolean type. + * + * Any other use-case of overriding igraph's bool type is completely + * unsupported. + */ +#define IGRAPH_BOOL_TYPE bool + +__END_DECLS + +#endif diff --git a/include/igraph_constants.h b/include/igraph_constants.h new file mode 100644 index 0000000..8a7dbcf --- /dev/null +++ b/include/igraph_constants.h @@ -0,0 +1,220 @@ +/* -*- 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_config.h" +#include "igraph_decls.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; + +/* Do not renumber the following values! Some internal code treats them as bitmasks + * and assumes that IGRAPH_ALL == IGRAPH_IN | IGRAPH_OUT and IGRAPH_IN & IGRAPH_OUT == 0. */ +typedef enum { IGRAPH_OUT = 1, IGRAPH_IN = 2, IGRAPH_ALL = 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, + IGRAPH_ADJ_UPPER, IGRAPH_ADJ_LOWER, IGRAPH_ADJ_MIN, + IGRAPH_ADJ_PLUS, + IGRAPH_ADJ_MAX, + } 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_WHEEL_OUT = 0, IGRAPH_WHEEL_IN, + IGRAPH_WHEEL_UNDIRECTED, + IGRAPH_WHEEL_MUTUAL + } igraph_wheel_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_CONFIGURATION = 0, /* Configuration model, allowing non-simple graphs */ + IGRAPH_DEGSEQ_VL, /* Viger-Latapy, generates simple connected graphs */ + IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE, /* Fast heuristic, generates simple graphs */ + IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE, /* Configuration model, generates simple graphs */ + IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE, /* Edge-switching MCMC, generates simple graphs */ + + /* Deprecated, kept for backwards compatibility: */ + IGRAPH_DEGSEQ_SIMPLE IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_CONFIGURATION, + IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE, + IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE + } 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 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; + +typedef enum { IGRAPH_VORONOI_FIRST = 0, + IGRAPH_VORONOI_LAST, + IGRAPH_VORONOI_RANDOM + } igraph_voronoi_tiebreaker_t; + +typedef enum { IGRAPH_CHUNG_LU_ORIGINAL = 0, + IGRAPH_CHUNG_LU_MAXENT, + IGRAPH_CHUNG_LU_NR + } igraph_chung_lu_t; + +typedef enum { IGRAPH_ROW_MAJOR = 0, + IGRAPH_COLUMN_MAJOR = 1 + } igraph_matrix_storage_t; + +__END_DECLS + +#endif diff --git a/include/igraph_constructors.h b/include/igraph_constructors.h new file mode 100644 index 0000000..d734ef4 --- /dev/null +++ b/include/igraph_constructors.h @@ -0,0 +1,107 @@ +/* -*- 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_error.h" +#include "igraph_types.h" +#include "igraph_matrix.h" +#include "igraph_datatype.h" +#include "igraph_graphicality.h" +#include "igraph_sparsemat.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Constructors, deterministic */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_t *edges, igraph_integer_t n, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + int first, ...); +IGRAPH_EXPORT igraph_error_t igraph_adjacency( + igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, + igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_weighted_adjacency( + igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, + igraph_vector_t *weights, igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_sparse_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_sparse_weighted_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, igraph_vector_t *weights, igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, + igraph_integer_t center); +IGRAPH_EXPORT igraph_error_t igraph_wheel(igraph_t *graph, igraph_integer_t n, igraph_wheel_mode_t mode, + igraph_integer_t center); +IGRAPH_EXPORT igraph_error_t igraph_hypercube(igraph_t *graph, + igraph_integer_t n, igraph_bool_t directed); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, + igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular); +IGRAPH_EXPORT igraph_error_t igraph_square_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, + igraph_bool_t directed, igraph_bool_t mutual, const igraph_vector_bool_t *circular); +IGRAPH_EXPORT igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + igraph_bool_t mutual, igraph_bool_t circular); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, + igraph_tree_mode_t type); +IGRAPH_EXPORT igraph_error_t igraph_kary_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, + igraph_tree_mode_t type); +IGRAPH_EXPORT igraph_error_t igraph_symmetric_tree(igraph_t *graph, const igraph_vector_int_t *branches, + igraph_tree_mode_t type); +IGRAPH_EXPORT igraph_error_t igraph_regular_tree(igraph_t *graph, igraph_integer_t h, igraph_integer_t k, + igraph_tree_mode_t type); +IGRAPH_EXPORT igraph_error_t igraph_tree_from_parent_vector(igraph_t *graph, const igraph_vector_int_t *parents, + igraph_tree_mode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer); +IGRAPH_EXPORT igraph_error_t igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_full_multipartite(igraph_t *graph, igraph_vector_int_t *types, const igraph_vector_int_t *n, + igraph_bool_t directed, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_turan(igraph_t *graph, igraph_vector_int_t *types, igraph_integer_t n, igraph_integer_t r); +IGRAPH_EXPORT igraph_error_t igraph_full_citation(igraph_t *graph, igraph_integer_t n, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_atlas(igraph_t *graph, igraph_integer_t number); +IGRAPH_EXPORT igraph_error_t igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, + const igraph_matrix_int_t *W, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_linegraph(const igraph_t *graph, igraph_t *linegraph); + +IGRAPH_EXPORT igraph_error_t igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_circulant(igraph_t *graph, igraph_integer_t n, const igraph_vector_int_t *l, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_generalized_petersen(igraph_t *graph, igraph_integer_t n, igraph_integer_t k); +IGRAPH_EXPORT igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_famous(igraph_t *graph, const char *name); +IGRAPH_EXPORT igraph_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, + const igraph_vector_int_t *shifts, + igraph_integer_t repeats); +IGRAPH_EXPORT igraph_error_t igraph_lcf(igraph_t *graph, igraph_integer_t n, ...); +IGRAPH_EXPORT igraph_error_t igraph_realize_degree_sequence(igraph_t *graph, + const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, + igraph_edge_type_sw_t allowed_edge_types, + igraph_realize_degseq_t method); +IGRAPH_EXPORT igraph_error_t igraph_triangular_lattice(igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, igraph_bool_t mutual); +IGRAPH_EXPORT igraph_error_t igraph_hexagonal_lattice(igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, igraph_bool_t mutual); +IGRAPH_EXPORT igraph_error_t igraph_realize_bipartite_degree_sequence(igraph_t *graph, const igraph_vector_int_t *deg1, const igraph_vector_int_t *deg2, const igraph_edge_type_sw_t allowed_edge_types, const igraph_realize_degseq_t method); + +__END_DECLS + +#endif diff --git a/include/igraph_conversion.h b/include/igraph_conversion.h new file mode 100644 index 0000000..d47d7f6 --- /dev/null +++ b/include/igraph_conversion.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_CONVERSION_H +#define IGRAPH_CONVERSION_H + +#include "igraph_decls.h" +#include "igraph_constants.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_matrix.h" +#include "igraph_sparsemat.h" +#include "igraph_attributes.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Conversion */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_get_adjacency( + const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type, + const igraph_vector_t *weights, igraph_loops_t loops +); +IGRAPH_EXPORT igraph_error_t igraph_get_adjacency_sparse( + const igraph_t *graph, igraph_sparsemat_t *res, igraph_get_adjacency_t type, + const igraph_vector_t *weights, igraph_loops_t loops +); + +IGRAPH_EXPORT igraph_error_t igraph_get_stochastic( + const igraph_t *graph, igraph_matrix_t *matrix, igraph_bool_t column_wise, + const igraph_vector_t *weights +); + +IGRAPH_EXPORT igraph_error_t igraph_get_stochastic_sparse( + const igraph_t *graph, igraph_sparsemat_t *res, igraph_bool_t column_wise, + const igraph_vector_t *weights +); + +/* Deprecated, will be removed in 0.11. Use igraph_get_adjacency_sparse() instead, paying attention to differences. */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res); + +/* Deprecated, will be removed in 0.11. Use igraph_get_stochastic_sparse() instead, paying attention to differences. */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_get_stochastic_sparsemat(const igraph_t *graph, + igraph_sparsemat_t *res, + igraph_bool_t column_wise); + +IGRAPH_EXPORT igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *res, igraph_bool_t bycol); + +IGRAPH_EXPORT igraph_error_t igraph_to_directed(igraph_t *graph, + igraph_to_directed_t flags); +IGRAPH_EXPORT igraph_error_t igraph_to_undirected(igraph_t *graph, + igraph_to_undirected_t mode, + const igraph_attribute_combination_t *edge_comb); +IGRAPH_EXPORT igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t *prufer); + +__END_DECLS + +#endif diff --git a/include/igraph_cycles.h b/include/igraph_cycles.h new file mode 100644 index 0000000..dd58700 --- /dev/null +++ b/include/igraph_cycles.h @@ -0,0 +1,30 @@ + +#ifndef IGRAPH_CYCLES_H +#define IGRAPH_CYCLES_H + +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_fundamental_cycles( + const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t start_vid, + igraph_integer_t bfs_cutoff, + const igraph_vector_t *weights); + +IGRAPH_EXPORT igraph_error_t igraph_minimum_cycle_basis( + const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t bfs_cutoff, + igraph_bool_t complete, + igraph_bool_t use_cycle_order, + const igraph_vector_t *weights); + +__END_DECLS + +#endif diff --git a/include/igraph_datatype.h b/include/igraph_datatype.h new file mode 100644 index 0000000..c0ee080 --- /dev/null +++ b/include/igraph_datatype.h @@ -0,0 +1,127 @@ +/* -*- 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_DATATYPE_H +#define IGRAPH_DATATYPE_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +struct igraph_i_property_cache_t; +typedef struct igraph_i_property_cache_t igraph_i_property_cache_t; + +typedef enum { + /* Stores whether the graph has at least one self-loop. */ + IGRAPH_PROP_HAS_LOOP = 0, + + /* Stores whether the graph has at least one multi-edge, taking into account + * edge directions in directed graphs. In other words, this property should + * be false for a directed graph with edges (a, b) and (b, a), and true + * for a directed graph with edges (a, b) and (a, b) again. */ + IGRAPH_PROP_HAS_MULTI, + + /* Stores whether the graph has at least one reciprocal edge pair. Ignored + * in undirected graphs. This property should be true for a directed graph + * with edges (a, b) and (b, a), and false for a directed graph with + * edges (a, b) and (a, b) again. Self-loops (a, a) are not considered + * reciprocal. */ + IGRAPH_PROP_HAS_MUTUAL, + + /* Stores whether the graph is weakly connected. */ + IGRAPH_PROP_IS_WEAKLY_CONNECTED, + + /* Stores whether the graph is strongly connected. Ignored in undirected graphs. */ + IGRAPH_PROP_IS_STRONGLY_CONNECTED, + + /* Stores whether the graph is a directed acyclic graph. Not used for + * undirected graphs. */ + IGRAPH_PROP_IS_DAG, + + /* Stores whether the graph is a forest, i.e. an undirected or directed + * graph that is cycle-free even if we ignore edge directions. */ + IGRAPH_PROP_IS_FOREST, + + /* Dummy value used to count enum values */ + IGRAPH_PROP_I_SIZE +} igraph_cached_property_t; + +/** + * \ingroup internal + * \struct igraph_t + * \brief The internal data structure for storing graphs. + * + * It is simple and efficient. It has the following members: + * - 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. + * - oi The index of the edge list by the first column. Thus + * the first edge according to this order goes from + * \c from[oi[0]] to \c to[oi[0]]. The length of + * this vector is the same as the number of edges in the graph. + * - 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 + * \c os[v]is This is basically the same as os, but this time + * for the incoming edges. + * + * 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_int_t from; + igraph_vector_int_t to; + igraph_vector_int_t oi; + igraph_vector_int_t ii; + igraph_vector_int_t os; + igraph_vector_int_t is; + void *attr; + igraph_i_property_cache_t *cache; +} igraph_t; + +IGRAPH_EXPORT void igraph_invalidate_cache(const igraph_t* graph); + +__END_DECLS + +#endif diff --git a/include/igraph_decls.h b/include/igraph_decls.h new file mode 100644 index 0000000..dd5bd53 --- /dev/null +++ b/include/igraph_decls.h @@ -0,0 +1,60 @@ +#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) + +/* The pure function attribute of GCC-compatible compilers indicates + * that the function does not have side-effects, i.e. it does not + * modify global memory. This enables additional compiler optimizations + * such as common subexpression elimination. + * + * The const attribute is similar but with much more stringent requirements. + * The function must also not read global memory. Generally, const functions + * should not take pointers, and must compute the return value solely based + * on their input. + */ +#ifdef __GNUC__ +#define IGRAPH_FUNCATTR_PURE __attribute__((__pure__)) +#define IGRAPH_FUNCATTR_CONST __attribute__((__const__)) +#else +#define IGRAPH_FUNCATTR_PURE +#define IGRAPH_FUNCATTR_CONST +#endif + +/* IGRAPH_ASSUME() provides hints to the compiler about conditions + * that are true yet the compiler cannot deduce. Use with great care. + * Assuming a condition that is not actually true leads to undefined behaviour. */ +#if defined(__clang__) + /* For Clang, see https://clang.llvm.org/docs/LanguageExtensions.html */ +# if __has_builtin(__builtin_assume) +# define IGRAPH_ASSUME(expr) __builtin_assume(expr) +# else +# define IGRAPH_ASSUME(expr) /* empty */ +# endif +#elif defined(__GNUC__) && !defined(__ICC) + /* Introduced in GCC 4.5, https://gcc.gnu.org/gcc-4.5/changes.html */ +# define IGRAPH_ASSUME(expr) do { if (expr) {} else { __builtin_unreachable(); } } while (0) +#elif defined(_MSC_VER) || defined(__ICC) +# define IGRAPH_ASSUME(expr) __assume(expr) +#else +# define IGRAPH_ASSUME(expr) /* empty */ +#endif + +/* IGRAPH_I_STRINGIFY(X) evaluates X and converts the result to a string. */ +#define IGRAPH_I_STRINGIFY_I(X) #X +#define IGRAPH_I_STRINGIFY(X) IGRAPH_I_STRINGIFY_I(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/include/igraph_dqueue.h b/include/igraph_dqueue.h new file mode 100644 index 0000000..53c8b0e --- /dev/null +++ b/include/igraph_dqueue.h @@ -0,0 +1,71 @@ +/* -*- 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_DQUEUE_H +#define IGRAPH_DQUEUE_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* double ended queue, very useful */ +/* -------------------------------------------------- */ + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "igraph_dqueue_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "igraph_dqueue_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "igraph_dqueue_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_BOOL + +#define BASE_INT +#include "igraph_pmt.h" +#include "igraph_dqueue_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#define IGRAPH_DQUEUE_NULL { 0,0,0,0 } +#define IGRAPH_DQUEUE_INIT_FINALLY(q, capacity) \ + do { IGRAPH_CHECK(igraph_dqueue_init(q, capacity)); \ + IGRAPH_FINALLY(igraph_dqueue_destroy, q); } while (0) +#define IGRAPH_DQUEUE_INT_INIT_FINALLY(q, capacity) \ + do { IGRAPH_CHECK(igraph_dqueue_int_init(q, capacity)); \ + IGRAPH_FINALLY(igraph_dqueue_int_destroy, q); } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_dqueue_pmt.h b/include/igraph_dqueue_pmt.h new file mode 100644 index 0000000..1db6cd6 --- /dev/null +++ b/include/igraph_dqueue_pmt.h @@ -0,0 +1,50 @@ +/* -*- 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 igraph_error_t FUNCTION(igraph_dqueue, init)(TYPE(igraph_dqueue)* q, igraph_integer_t capacity); +IGRAPH_EXPORT void FUNCTION(igraph_dqueue, destroy)(TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_dqueue, full)(TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t 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 IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_dqueue, head)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_dqueue, back)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, get)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx); +IGRAPH_DEPRECATED IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx); diff --git a/include/igraph_eigen.h b/include/igraph_eigen.h new file mode 100644 index 0000000..122eeb8 --- /dev/null +++ b/include/igraph_eigen.h @@ -0,0 +1,101 @@ +/* -*- 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_error.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 igraph_error_t 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 igraph_error_t 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 igraph_error_t 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); + +__END_DECLS + +#endif diff --git a/include/igraph_embedding.h b/include/igraph_embedding.h new file mode 100644 index 0000000..2462b01 --- /dev/null +++ b/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_error.h" +#include "igraph_arpack.h" +#include "igraph_eigen.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim); + +__END_DECLS + +#endif diff --git a/include/igraph_epidemics.h b/include/igraph_epidemics.h new file mode 100644 index 0000000..a13f8cb --- /dev/null +++ b/include/igraph_epidemics.h @@ -0,0 +1,68 @@ +/* -*- 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_error.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 igraph_error_t igraph_sir_init(igraph_sir_t *sir); +IGRAPH_EXPORT void igraph_sir_destroy(igraph_sir_t *sir); + +IGRAPH_EXPORT igraph_error_t 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/include/igraph_error.h b/include/igraph_error.h new file mode 100644 index 0000000..69e2654 --- /dev/null +++ b/include/igraph_error.h @@ -0,0 +1,985 @@ +/* -*- 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_ERROR_H +#define IGRAPH_ERROR_H + +#include "igraph_decls.h" +#include "igraph_config.h" + +#include + +__BEGIN_DECLS + +/* This file contains the igraph error handling. + * Most bits are taken literally from the GSL library (with the GSL_ + * prefix renamed to IGRAPH_), as I couldn't find a better way to do + * them. */ + +/* With some compilers, we use function attributes to help diagnostics + * and optimizations. These are not part of the public API, do not use + * them outside of igraph itself. + * + * IGRAPH_FUNCATTR_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. + * + * IGRAPH_FUNCATTR_PRINTFLIKE(string, first) marks a function as having a printf-like syntax, + * allowing the compiler to check that the format specifiers match argument types. + * 'string' is the index of the string-argument and 'first' is the index of the + * first argument to check against format specifiers. + */ +#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_FUNCATTR_NORETURN __attribute__((__noreturn__)) +#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) __attribute__((__format__(printf, string, first))) +#elif defined(_MSC_VER) +/* Compilers that support the MSVC syntax. */ +#define IGRAPH_FUNCATTR_NORETURN __declspec(noreturn) +#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) +#else +#define IGRAPH_FUNCATTR_NORETURN +#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) +#endif + +/* IGRAPH_PREPROCESSOR_WARNING(reason) is a macro that evaluates to nothing + * but triggers a preprocessor warning with the given message, if the compiler + * supports this functionality. + */ +#if defined(__GNUC__) +#define IGRAPH_PREPROCESSOR_WARNING(reason) _Pragma(IGRAPH_I_STRINGIFY(GCC warning reason)) +#else +#define IGRAPH_PREPROCESSOR_WARNING(reason) /* empty */ +#endif + +/** + * \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 + * 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, + * it is without doubt avoidable in larger projects. Please read further + * if your project requires more sophisticated error handling. You can + * safely skip the rest of this chapter otherwise. + * + */ + +/** + * \section error_handlers Error handlers + * + * + * If \a igraph runs into an error - an invalid argument was supplied + * to a function, or we've ran out of memory - the control is + * transferred to the \emb error handler \eme function. + * + * The default error handler is \ref igraph_error_handler_abort which + * prints an error message and aborts the program. + * + * + * The \ref igraph_set_error_handler() function can be used to set a new + * error handler function of type \ref igraph_error_handler_t; see the + * documentation of this type for details. + * + * + * There are two other predefined error handler functions, + * \ref igraph_error_handler_ignore and \ref igraph_error_handler_printignore. + * These deallocate the temporarily allocated memory (more about this + * later) and return with the error code. The latter also prints an + * error message. If you use these error handlers you need to take + * care about possible errors yourself by checking the return value of + * (almost) every non-void \a igraph function. + * + * Independently of the error handler installed, all functions in the + * library do their best to leave their arguments + * \em semantically unchanged if an error + * happens. By semantically we mean that the implementation of an + * object supplied as an argument might change, but its + * \quote meaning \endquote in most cases does not. The rare occasions + * when this rule is violated are documented in this manual. + * + */ + +/** + * \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 + * \type void as well. The error codes are defined by the + * \ref igraph_error_type_t enumeration. + * + */ + +/** + * \section writing_error_handlers Writing error handlers + * + * + * The contents of the rest of this chapter might be useful only + * for those who want to create an interface to \a igraph from another + * language, or use igraph from a GUI application. 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. For example, the R interface uses + * R's native printing facilities to communicate errors, while the Python + * interface converts them into Python exceptions. + * + * + * + * The two main tasks of the error handler are to report the error + * (i.e. print the error message) and ensure proper resource cleanup. + * This is ensured by calling \ref IGRAPH_FINALLY_FREE(), which deallocates + * some of the temporary memory to avoid 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(). + * + * + * + * As of \a igraph 0.10, temporary memory is dellocated in stages, through + * multiple calls to the error handler (and indirectly to \ref IGRAPH_FINALLY_FREE()). + * Therefore, error handlers that do not abort the program + * immediately are expected to return. The error handler should not perform + * a longjmp, as this may lead to some of the memory not + * getting freed. + * + */ + +/** + * \section error_handling_internals Error handling internals + * + * + * If an error happens, the functions in the library call the + * \ref IGRAPH_ERROR() macro with a textual description of the error and an + * \a igraph error code. This macro calls (through the \ref + * igraph_error() function) the installed error handler. Another useful + * macro is \ref IGRAPH_CHECK(). This checks the return value of its + * argument, which is normally a function call, and calls \ref + * IGRAPH_ERROR() if it is not \c IGRAPH_SUCCESS. + * + */ + +/** + * \section deallocating_memory Deallocating memory + * + * + * If a function runs into an error (and the program is not aborted) + * the error handler should deallocate all temporary memory. This is + * done by storing the address and the destroy function of all temporary + * objects in a stack. The \ref IGRAPH_FINALLY function declares an object as + * temporary by placing its address in the stack. If an \a igraph function returns + * with success it calls \ref IGRAPH_FINALLY_CLEAN() with the + * number of objects to remove from the stack. If an error happens + * however, the error handler should call \ref IGRAPH_FINALLY_FREE() to + * deallocate each object added to the stack. This means that the + * temporary objects allocated in the calling function (and etc.) will + * be freed as well. + * + */ + +/** + * \section writing_functions_error_handling Writing \a igraph functions with + * proper error handling + * + * + * There are some simple rules to keep in order to have functions + * behaving well in erroneous situations. First, check the arguments + * of the functions and call \ref IGRAPH_ERROR() if they are invalid. Second, + * call \ref IGRAPH_FINALLY on each dynamically allocated object and call + * \ref IGRAPH_FINALLY_CLEAN() with the proper argument before returning. Third, use + * \ref IGRAPH_CHECK on all \a igraph function calls which can generate errors. + * + * + * The size of the stack used for this bookkeeping is fixed, and + * small. If you want to allocate several objects, write a destroy + * function which can deallocate all of these. See the + * 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. + * + * + * \example examples/simple/igraph_contract_vertices.c + */ + +/** + * \typedef igraph_error_type_t + * \brief Error code type. + * These are the possible values returned by \a igraph functions. + * Note that these are interesting only if you defined an error handler + * with \ref igraph_set_error_handler(). Otherwise the program is aborted + * and the function causing the error never returns. + * + * \enumval IGRAPH_SUCCESS The function successfully completed its task. + * \enumval IGRAPH_FAILURE Something went wrong. You'll almost never + * meet this error as normally more specific error codes are used. + * \enumval IGRAPH_ENOMEM There wasn't enough memory to allocate + * on the heap. + * \enumval IGRAPH_PARSEERROR A parse error was found in a file. + * \enumval IGRAPH_EINVAL A parameter's value is invalid. 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. + * \enumval IGRAPH_EINVEVECTOR Invalid vector of vertex IDs. A vertex ID + * is either negative or bigger than the number of vertices minus one. + * \enumval IGRAPH_EINVVID Invalid vertex ID, negative or too big. + * \enumval IGRAPH_NONSQUARE A non-square matrix was received while a + * square matrix was expected. + * \enumval IGRAPH_EINVMODE Invalid mode parameter. + * \enumval IGRAPH_EFILE A file operation failed. 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. + * \enumval IGRAPH_DIVERGED A numeric algorithm failed to converge. + * \enumval IGRAPH_ARPACK_PROD Matrix-vector product failed (not used any more). + * \enumval IGRAPH_ARPACK_NPOS N must be positive. + * \enumval IGRAPH_ARPACK_NEVNPOS NEV must be positive. + * \enumval IGRAPH_ARPACK_NCVSMALL NCV must be bigger. + * \enumval IGRAPH_ARPACK_NONPOSI Maximum number of iterations should be positive. + * \enumval IGRAPH_ARPACK_WHICHINV Invalid WHICH parameter. + * \enumval IGRAPH_ARPACK_BMATINV Invalid BMAT parameter. + * \enumval IGRAPH_ARPACK_WORKLSMALL WORKL is too small. + * \enumval IGRAPH_ARPACK_TRIDERR LAPACK error in tridiagonal eigenvalue calculation. + * \enumval IGRAPH_ARPACK_ZEROSTART Starting vector is zero. + * \enumval IGRAPH_ARPACK_MODEINV MODE is invalid. + * \enumval IGRAPH_ARPACK_MODEBMAT MODE and BMAT are not compatible. + * \enumval IGRAPH_ARPACK_ISHIFT ISHIFT must be 0 or 1. + * \enumval IGRAPH_ARPACK_NEVBE NEV and WHICH='BE' are incompatible. + * \enumval IGRAPH_ARPACK_NOFACT Could not build an Arnoldi factorization. + * \enumval IGRAPH_ARPACK_FAILED No eigenvalues to sufficient accuracy. + * \enumval IGRAPH_ARPACK_HOWMNY HOWMNY is invalid. + * \enumval IGRAPH_ARPACK_HOWMNYS HOWMNY='S' is not implemented. + * \enumval IGRAPH_ARPACK_EVDIFF Different number of converged Ritz values. + * \enumval IGRAPH_ARPACK_SHUR Error from calculation of a real Schur form. + * \enumval IGRAPH_ARPACK_LAPACK LAPACK (dtrevc) error for calculating eigenvectors. + * \enumval IGRAPH_ARPACK_UNKNOWN Unknown ARPACK error. + * \enumval IGRAPH_ENEGLOOP Negative loop detected while calculating shortest paths. + * \enumval IGRAPH_EINTERNAL Internal error, likely a bug in igraph. + * \enumval IGRAPH_EDIVZERO Big integer division by zero. + * \enumval 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 + * method for the given attribute type. + * \enumval IGRAPH_ELAPACK A LAPACK call resulted in an error. + * \enumval IGRAPH_EDRL Internal error in the DrL layout generator; not used + * any more (replaced by IGRAPH_EINTERNAL). + * \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. + * \enumval IGRAPH_ERANGE Maximum vertex or edge count exceeded. + * \enumval IGRAPH_ENOSOL Input problem has no solution. + */ + +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, /* unused, reserved */ + 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 IGRAPH_DEPRECATED_ENUMVAL = 54, + IGRAPH_EOVERFLOW = 55, + IGRAPH_EGLP = 56, + IGRAPH_CPUTIME = 57, + IGRAPH_EUNDERFLOW = 58, + IGRAPH_ERWSTUCK = 59, + IGRAPH_STOP = 60, + IGRAPH_ERANGE = 61, + IGRAPH_ENOSOL = 62 +} igraph_error_type_t; +/* Each enum value above must have a corresponding error string in + * igraph_i_error_strings[] in core/error.c + * + * Information on undocumented codes: + * - IGRAPH_STOP signals a request to stop in functions like igraph_i_maximal_cliques_bk() + */ + +/** + * \section error_handling_threads Error handling and threads + * + * + * It is likely that the \a igraph error handling + * method is \em not thread-safe, mainly because of + * the static global stack which is used to store the address of the + * temporarily allocated objects. This issue might be addressed in a + * later version of \a igraph. + * + */ + +/** + * \typedef igraph_error_t + * \brief Return type for functions returning an error code. + * + * This type is used as the return type of igraph functions that return an + * error code. It is a type alias because \type igraph_error_t used to be + * an \c int, and was used slightly differenly than \type igraph_error_type_t. + */ +typedef igraph_error_type_t igraph_error_t; + +/** + * \typedef igraph_error_handler_t + * \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. + * \param line The number of the line in the source file which triggered + * the error + * \param igraph_errno The \a igraph error code. + */ + +typedef void igraph_error_handler_t(const char *reason, const char *file, + int line, igraph_error_t 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. + */ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN 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. + */ + +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. + */ + +IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_printignore; + +IGRAPH_EXPORT igraph_error_handler_t *igraph_set_error_handler(igraph_error_handler_t* new_handler); + + +/* 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 Triggers an error. + * + * \a igraph functions usually use this macro when they notice an error. + * It calls + * \ref igraph_error() with the proper parameters and if that returns + * the macro returns the "calling" function as well, with the error + * code. If for some (suspicious) reason you want to call the error + * handler without returning from the current function, call + * \ref igraph_error() directly. + * + * \param reason Textual description of the error. This should be + * something more descriptive than the text associated with the error + * code. 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. + * \param igraph_errno The \a igraph error code. + */ + +#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) + +IGRAPH_EXPORT igraph_error_t igraph_error(const char *reason, const char *file, + int line, igraph_error_t 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) + + +IGRAPH_FUNCATTR_PRINTFLIKE(1,5) +IGRAPH_EXPORT igraph_error_t igraph_errorf(const char *reason, const char *file, + int line, igraph_error_t igraph_errno, + ...); + +IGRAPH_EXPORT igraph_error_t igraph_errorvf(const char *reason, const char *file, + int line, igraph_error_t igraph_errno, + va_list ap); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE const char *igraph_strerror(const igraph_error_t igraph_errno); + +#define IGRAPH_ERROR_SELECT_2(a,b) ((a) != IGRAPH_SUCCESS ? (a) : ((b) != IGRAPH_SUCCESS ? (b) : IGRAPH_SUCCESS)) +#define IGRAPH_ERROR_SELECT_3(a,b,c) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_2(b,c)) +#define IGRAPH_ERROR_SELECT_4(a,b,c,d) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_3(b,c,d)) +#define IGRAPH_ERROR_SELECT_5(a,b,c,d,e) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_4(b,c,d,e)) + +/* Now comes the more convenient error handling macro arsenal. + * Ideas taken from exception.{h,c} by Laurent Deniau see + * http://cern.ch/Laurent.Deniau/html/oopc/oopc.html#Exceptions for more + * information. We don't use the exception handling code though. */ + +struct igraph_i_protectedPtr { + int level; + void *ptr; + void (*func)(void*); +}; + +typedef void igraph_finally_func_t(void *); + +IGRAPH_EXPORT void IGRAPH_FINALLY_REAL(igraph_finally_func_t *func, void *ptr); + +/** + * \function IGRAPH_FINALLY_CLEAN + * \brief Signals clean deallocation of objects. + * + * Removes the specified number of objects from the stack of + * temporarily allocated objects. It is typically called + * immediately after manually destroying the objects: + * + * + * igraph_vector_t vector; + * igraph_vector_init(&vector, 10); + * IGRAPH_FINALLY(igraph_vector_destroy, &vector); + * // use vector + * igraph_vector_destroy(&vector); + * IGRAPH_FINALLY_CLEAN(1); + * + * + * \param num The number of objects to remove from the bookkeeping + * stack. + */ + +IGRAPH_EXPORT void IGRAPH_FINALLY_CLEAN(int num); + +/** + * \function IGRAPH_FINALLY_FREE + * \brief Deallocates objects registered at the current level. + * + * Calls the destroy function for all objects in the current level + * of the stack of temporarily allocated objects, i.e. up to the + * nearest mark set by IGRAPH_FINALLY_ENTER(). + * This function must only be called from an error handler. + * It is \em not appropriate to use it + * instead of destroying each unneeded object of a function, as it + * destroys the temporary objects of the caller function (and so on) + * as well. + */ + +IGRAPH_EXPORT void IGRAPH_FINALLY_FREE(void); + +IGRAPH_EXPORT void IGRAPH_FINALLY_ENTER(void); +IGRAPH_EXPORT void IGRAPH_FINALLY_EXIT(void); + +/** + * \function IGRAPH_FINALLY_STACK_SIZE + * \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 + * you want to make sure it handles errors properly. A properly written + * igraph routine should not leave pointers to temporarily allocated objects + * in the finally stack, because otherwise an \ref IGRAPH_FINALLY_FREE call + * in another igraph function would result in freeing these objects as well + * (and this is really hard to debug, since the error will be not in that + * function that shows erroneous behaviour). Therefore, it is advised to + * write your own test cases and examine \ref IGRAPH_FINALLY_STACK_SIZE + * before and after your test cases - the numbers should be equal. + */ +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. + */ +#define IGRAPH_FINALLY_STACK_EMPTY (IGRAPH_FINALLY_STACK_SIZE() == 0) + +/** + * \define IGRAPH_FINALLY + * \brief Registers an object for deallocation. + * + * 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. After manual deallocation, objects are removed + * from the stack using \ref IGRAPH_FINALLY_CLEAN(). + * + * \param func The function which is normally called to + * destroy the object. + * \param ptr Pointer to the object itself. + */ + +#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(GCC_VERSION_MAJOR) && (GCC_VERSION_MAJOR >= 3) + #define IGRAPH_UNLIKELY(a) __builtin_expect((a), 0) + #define IGRAPH_LIKELY(a) __builtin_expect((a), 1) +#else + #define IGRAPH_UNLIKELY(a) a + #define IGRAPH_LIKELY(a) a +#endif + +#if IGRAPH_VERIFY_FINALLY_STACK == 1 +#define IGRAPH_CHECK(a) \ + do { \ + int enter_stack_size = IGRAPH_FINALLY_STACK_SIZE(); \ + igraph_error_t igraph_i_ret=(a); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) {\ + IGRAPH_ERROR("", igraph_i_ret); \ + } \ + if (IGRAPH_UNLIKELY(enter_stack_size != IGRAPH_FINALLY_STACK_SIZE())) { \ + IGRAPH_FATAL("Non-matching number of IGRAPH_FINALLY and IGRAPH_FINALLY_CLEAN."); \ + } \ + } while (0) +#else +/** + * \define IGRAPH_CHECK + * \brief Checks the return value of a function call. + * + * \param expr An expression, usually a function call. It is guaranteed to + * be evaluated only once. + * + * 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 + * \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 (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(expr) \ + do { \ + igraph_error_t igraph_i_ret = (expr); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) {\ + IGRAPH_ERROR("", igraph_i_ret); \ + } \ + } while (0) +#endif + +/** + * \define IGRAPH_CHECK_CALLBACK + * \brief Checks the return value of a callback. + * + * Identical to \ref IGRAPH_CHECK, but treats \c IGRAPH_STOP as a normal + * (non-erroneous) return code. This macro is used in some igraph functions + * that allow the user to hook into a long-running calculation with a callback + * function. When the user-defined callback function returns \c IGRAPH_SUCCESS, + * the calculation will proceed normally. Returning \c IGRAPH_STOP from the + * callback will terminate the calculation without reporting an error. Returning + * any other value from the callback is treated as an error code, and igraph + * will trigger the necessary cleanup functions before exiting the function. + * + * + * Note that \c IGRAPH_CHECK_CALLBACK does not handle \c IGRAPH_STOP by any + * means except returning it in the variable pointed to by \c code. It is the + * responsibility of the caller to handle \c IGRAPH_STOP accordingly. + * + * \param expr An expression, usually a call to a user-defined callback function. + * It is guaranteed to be evaluated only once. + * \param code Pointer to an optional variable of type igraph_error_t; + * the value of this variable will be set to the error code if it is not a null + * pointer. + */ +#define IGRAPH_CHECK_CALLBACK(expr, code) \ + do { \ + igraph_error_t igraph_i_ret = (expr); \ + *(code) = igraph_i_ret; \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS && igraph_i_ret != IGRAPH_STOP)) { \ + IGRAPH_ERROR("", igraph_i_ret); \ + } \ + } while (0) + +/** + * \define IGRAPH_CHECK_OOM + * \brief Checks for out-of-memory conditions after a memory allocation. + * + * This function should be called on pointers after memory allocations. The + * function checks whether the returned pointer is NULL, and if so, sets an + * error message with the \c IGRAPH_ENOMEM error code. + * + * \param ptr The pointer to check. + * \param message The error message to use when the pointer is \c NULL. + */ +#define IGRAPH_CHECK_OOM(ptr, message) \ + do { \ + if (IGRAPH_UNLIKELY(!ptr)) { \ + IGRAPH_ERROR(message, IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ + } \ + } while (0) + +/** + * \section about_igraph_warnings Warning messages + * + * + * \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. + * + * + * + * \a igraph warnings are handled similarly to errors. There is a + * separate warning handler function that is called whenever + * 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, \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 + * \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. + */ + +typedef void igraph_warning_handler_t(const char *reason, + const char *file, int line); + + +IGRAPH_EXPORT igraph_warning_handler_t *igraph_set_warning_handler(igraph_warning_handler_t* new_handler); + +IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_ignore; +IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_print; + +IGRAPH_EXPORT void igraph_warning(const char *reason, + const char *file, int line); + +/** + * \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__, \ + __VA_ARGS__); \ + } while (0) + + +IGRAPH_FUNCATTR_PRINTFLIKE(1,4) +IGRAPH_EXPORT void igraph_warningf(const char *reason, + const char *file, int line, ...); + +/** + * \define IGRAPH_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, IGRAPH_FILE_BASENAME, __LINE__); \ + } 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); + +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_FUNCATTR_NORETURN igraph_fatal_handler_t igraph_fatal_handler_abort; + +IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatal(const char *reason, + const char *file, int line); +IGRAPH_FUNCATTR_PRINTFLIKE(1,4) +IGRAPH_EXPORT IGRAPH_FUNCATTR_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 typical 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 (IGRAPH_UNLIKELY(!(condition))) { \ + igraph_fatal("Assertion failed: " #condition, IGRAPH_FILE_BASENAME, __LINE__); \ + } \ + } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_eulerian.h b/include/igraph_eulerian.h new file mode 100644 index 0000000..bb9a089 --- /dev/null +++ b/include/igraph_eulerian.h @@ -0,0 +1,39 @@ +/* -*- 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" +#include "igraph_error.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle); +IGRAPH_EXPORT igraph_error_t igraph_eulerian_path(const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res); +IGRAPH_EXPORT igraph_error_t igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res); + +__END_DECLS + +#endif diff --git a/include/igraph_flow.h b/include/igraph_flow.h new file mode 100644 index 0000000..354f29f --- /dev/null +++ b/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_error.h" +#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Maximum flows, minimum cuts & such */ +/* -------------------------------------------------- */ + +/** + * \typedef igraph_maxflow_stats_t + * \brief Data structure holding 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 { + igraph_integer_t nopush, norelabel, nogap, nogapnodes, nobfs; +} igraph_maxflow_stats_t; + +IGRAPH_EXPORT igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, + igraph_vector_t *flow, igraph_vector_int_t *cut, + igraph_vector_int_t *partition, igraph_vector_int_t *partition2, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity, + igraph_maxflow_stats_t *stats); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, + igraph_vector_int_t *cut, igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *capacity); +IGRAPH_EXPORT igraph_error_t igraph_mincut(const igraph_t *graph, + igraph_real_t *value, + igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_vector_int_t *cut, + const igraph_vector_t *capacity); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks); + +IGRAPH_EXPORT igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target); +IGRAPH_EXPORT igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks); + +IGRAPH_EXPORT igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target); +IGRAPH_EXPORT igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target); + +IGRAPH_EXPORT igraph_error_t igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks); +IGRAPH_EXPORT igraph_error_t igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks); + +/* s-t cut listing related stuff */ + +IGRAPH_EXPORT igraph_error_t igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, + igraph_vector_t *capacity); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_reverse_residual_graph(const igraph_t *graph, + const igraph_vector_t *capacity, + igraph_t *residual, + const igraph_vector_t *flow); + +IGRAPH_EXPORT igraph_error_t igraph_dominator_tree(const igraph_t *graph, + igraph_integer_t root, + igraph_vector_int_t *dom, + igraph_t *domtree, + igraph_vector_int_t *leftout, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_all_st_cuts(const igraph_t *graph, + igraph_vector_int_list_t *cuts, + igraph_vector_int_list_t *partition1s, + igraph_integer_t source, + igraph_integer_t target); + +IGRAPH_EXPORT igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, + igraph_vector_int_list_t *cuts, + igraph_vector_int_list_t *partition1s, + igraph_integer_t source, + igraph_integer_t target, + const igraph_vector_t *capacity); + +IGRAPH_EXPORT igraph_error_t 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/include/igraph_foreign.h b/include/igraph_foreign.h new file mode 100644 index 0000000..a0625cb --- /dev/null +++ b/include/igraph_foreign.h @@ -0,0 +1,113 @@ +/* -*- 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_error.h" +#include "igraph_types.h" +#include "igraph_strvector.h" + +#include + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Read and write foreign formats */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, + igraph_integer_t n, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, + igraph_bool_t names, igraph_add_weights_t weights, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_pajek(igraph_t *graph, FILE *instream); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, + igraph_integer_t index); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, + igraph_strvector_t *problem, + igraph_vector_int_t *label, + igraph_integer_t *source, + igraph_integer_t *target, + igraph_vector_t *capacity, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_dimacs_flow(igraph_t *graph, FILE *instream, + igraph_strvector_t *problem, + igraph_vector_int_t *label, + igraph_integer_t *source, + igraph_integer_t *target, + igraph_vector_t *capacity, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_gml(igraph_t *graph, FILE *instream); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, + igraph_bool_t directed); + +typedef unsigned int igraph_write_gml_sw_t; + +enum { + IGRAPH_WRITE_GML_DEFAULT_SW = 0x0, /* default settings */ + IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW = 0x1 /* only encode " characters, nothing else */ +}; + +IGRAPH_EXPORT igraph_error_t igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, + const char *names, const char *weights); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, + const char *names, const char *weights, + igraph_bool_t isolates); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, + igraph_bool_t prefixattr); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outstream, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, + igraph_write_gml_sw_t options, + const igraph_vector_t *id, const char *creator); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, + const char* vertex_attr_name, const char* edge_attr_name); + +/* -------------------------------------------------- */ +/* Convenience functions for temporary locale setting */ +/* -------------------------------------------------- */ + +typedef struct igraph_safelocale_s *igraph_safelocale_t; + +IGRAPH_EXPORT igraph_error_t igraph_enter_safelocale(igraph_safelocale_t *loc); +IGRAPH_EXPORT void igraph_exit_safelocale(igraph_safelocale_t *loc); + +__END_DECLS + +#endif diff --git a/include/igraph_games.h b/include/igraph_games.h new file mode 100644 index 0000000..3c4cbdb --- /dev/null +++ b/include/igraph_games.h @@ -0,0 +1,235 @@ +/* -*- 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_datatype.h" +#include "igraph_error.h" +#include "igraph_matrix.h" +#include "igraph_matrix_list.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Constructors, games (=stochastic) */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_int_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 igraph_error_t 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 igraph_error_t igraph_erdos_renyi_game_gnm(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, + igraph_bool_t directed, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_int_t *out_deg, + const igraph_vector_int_t *in_deg, + igraph_degseq_t method); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, + igraph_integer_t nodes, + igraph_integer_t m, + const igraph_vector_int_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 igraph_error_t 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_int_t *outseq, + igraph_bool_t outpref, + igraph_real_t zero_appeal, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, + igraph_integer_t nodes, + igraph_integer_t m, + const igraph_vector_int_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 igraph_error_t 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_int_t *node_type_vec); +IGRAPH_EXPORT igraph_error_t 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_int_t *node_type_vec); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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_int_t *node_type_vec, + igraph_bool_t directed, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t 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_int_t *node_type_out_vec, + igraph_vector_int_t *node_type_in_vec, + igraph_bool_t loops); + +IGRAPH_EXPORT igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, igraph_bool_t multiple); +IGRAPH_EXPORT igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_int_t *types, + const igraph_vector_t *pref, + igraph_integer_t edges_per_step, + igraph_bool_t directed); + +IGRAPH_EXPORT igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_int_t *types, + const igraph_matrix_t *pref, + igraph_integer_t edges_per_step, + igraph_bool_t directed); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_chung_lu_game(igraph_t *graph, + const igraph_vector_t *expected_out_deg, + const igraph_vector_t *expected_in_deg, + igraph_bool_t loops, + igraph_chung_lu_t variant); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, + const igraph_vector_int_t *mlist, + const igraph_vector_list_t *rholist, + const igraph_matrix_list_t *Clist, + igraph_real_t p); + +IGRAPH_EXPORT igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, + igraph_real_t corr, igraph_real_t p, + const igraph_vector_int_t *permutation); + +IGRAPH_EXPORT igraph_error_t 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_int_t *permutation); + +IGRAPH_EXPORT igraph_error_t igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + igraph_random_tree_t method); + +IGRAPH_EXPORT igraph_error_t igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, + igraph_bool_t directed); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, + igraph_matrix_t *res); + +/* Deprecated functions: */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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 +); + +__END_DECLS + +#endif diff --git a/include/igraph_graph_list.h b/include/igraph_graph_list.h new file mode 100644 index 0000000..7f82326 --- /dev/null +++ b/include/igraph_graph_list.h @@ -0,0 +1,60 @@ +/* -*- 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_GRAPH_LIST_H +#define IGRAPH_GRAPH_LIST_H + +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* List of graphs */ +/* -------------------------------------------------- */ + +#define GRAPH_LIST +#define BASE_GRAPH +#define EXTRA_TYPE_FIELDS igraph_bool_t directed; +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef EXTRA_TYPE_FIELDS +#undef BASE_GRAPH +#undef GRAPH_LIST + +void igraph_graph_list_set_directed(igraph_graph_list_t* list, igraph_bool_t directed); + +/* -------------------------------------------------- */ +/* Helper macros */ +/* -------------------------------------------------- */ + +#define IGRAPH_GRAPH_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_graph_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_graph_list_destroy, v); } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_graphicality.h b/include/igraph_graphicality.h new file mode 100644 index 0000000..f639217 --- /dev/null +++ b/include/igraph_graphicality.h @@ -0,0 +1,55 @@ +/* -*- 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_error.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +typedef unsigned int 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 igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, + const igraph_vector_int_t *in_degrees, + const igraph_edge_type_sw_t allowed_edge_types, + igraph_bool_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_is_bigraphical(const igraph_vector_int_t *degrees1, + const igraph_vector_int_t *degrees2, + const igraph_edge_type_sw_t allowed_edge_types, + igraph_bool_t *res); + +__END_DECLS + +#endif // IGRAPH_GRAPHICALITY_H diff --git a/include/igraph_graphlets.h b/include/igraph_graphlets.h new file mode 100644 index 0000000..b80165a --- /dev/null +++ b/include/igraph_graphlets.h @@ -0,0 +1,53 @@ +/* -*- 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_error.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_graphlets_candidate_basis(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_list_t *cliques, + igraph_vector_t *thresholds); + +IGRAPH_EXPORT igraph_error_t igraph_graphlets_project(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_vector_int_list_t *cliques, + igraph_vector_t *Mu, igraph_bool_t startMu, + igraph_integer_t niter); + +IGRAPH_EXPORT igraph_error_t igraph_graphlets(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_list_t *cliques, + igraph_vector_t *Mu, igraph_integer_t niter); + +__END_DECLS + +#endif diff --git a/include/igraph_heap.h b/include/igraph_heap.h new file mode 100644 index 0000000..3eb81c2 --- /dev/null +++ b/include/igraph_heap.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_HEAP_H +#define IGRAPH_HEAP_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Heap */ +/* -------------------------------------------------- */ + +/** + * Heap data type. + * \ingroup internal + */ + +#define BASE_IGRAPH_REAL +#define HEAP_TYPE_MAX +#include "igraph_pmt.h" +#include "igraph_heap_pmt.h" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MAX +#define HEAP_TYPE_MIN +#include "igraph_pmt.h" +#include "igraph_heap_pmt.h" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MIN +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#define HEAP_TYPE_MAX +#include "igraph_pmt.h" +#include "igraph_heap_pmt.h" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MAX +#define HEAP_TYPE_MIN +#include "igraph_pmt.h" +#include "igraph_heap_pmt.h" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MIN +#undef BASE_INT + +#define BASE_CHAR +#define HEAP_TYPE_MAX +#include "igraph_pmt.h" +#include "igraph_heap_pmt.h" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MAX +#define HEAP_TYPE_MIN +#include "igraph_pmt.h" +#include "igraph_heap_pmt.h" +#include "igraph_pmt_off.h" +#undef HEAP_TYPE_MIN +#undef BASE_CHAR + +#define IGRAPH_HEAP_NULL { 0,0,0 } + +__END_DECLS + +#endif diff --git a/include/igraph_heap_pmt.h b/include/igraph_heap_pmt.h new file mode 100644 index 0000000..d501700 --- /dev/null +++ b/include/igraph_heap_pmt.h @@ -0,0 +1,40 @@ +/* -*- 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; + igraph_bool_t destroy; +} TYPE(igraph_heap); + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *t, const BASE *data, igraph_integer_t len); +IGRAPH_EXPORT void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT void FUNCTION(igraph_heap, clear)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_heap, empty)(const TYPE(igraph_heap)* h); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_heap, top)(const TYPE(igraph_heap)* h); +IGRAPH_EXPORT BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_heap, size)(const TYPE(igraph_heap)* h); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, igraph_integer_t capacity); diff --git a/include/igraph_hrg.h b/include/igraph_hrg.h new file mode 100644 index 0000000..6ceeb4c --- /dev/null +++ b/include/igraph_hrg.h @@ -0,0 +1,132 @@ +/* -*- 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_HRG_H +#define IGRAPH_HRG_H + +#include "igraph_decls.h" + +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_graph_list.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/** + * \struct igraph_hrg_t + * \brief 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 + * tree vertices. The first vertex is always the root vertex, so + * the first element of the vector is the left child of the root + * vertex. Internal vertices are denoted with negative numbers, + * starting from -1 and going down, i.e. the root vertex is + * -1. Leaf vertices are denoted by non-negative number, starting + * from zero and up. + * \member right Vector that contains the right children of the + * vertices, with the same encoding as the \c left vector. + * \member prob The connection probabilities attached to the internal + * vertices, the first number belongs to the root vertex + * (i.e. internal vertex -1), the second to internal vertex -2, + * etc. + * \member edges The number of edges in the subtree below the given + * internal vertex. + * \member vertices The number of vertices in the subtree below the + * given internal vertex, including itself. + */ + +typedef struct igraph_hrg_t { + igraph_vector_int_t left; + igraph_vector_int_t right; + igraph_vector_t prob; + igraph_vector_int_t vertices; + igraph_vector_int_t edges; +} igraph_hrg_t; + +IGRAPH_EXPORT igraph_error_t igraph_hrg_init(igraph_hrg_t *hrg, igraph_integer_t n); +IGRAPH_EXPORT void igraph_hrg_destroy(igraph_hrg_t *hrg); +IGRAPH_EXPORT igraph_integer_t igraph_hrg_size(const igraph_hrg_t *hrg); +IGRAPH_EXPORT igraph_error_t igraph_hrg_resize(igraph_hrg_t *hrg, igraph_integer_t newsize); + +IGRAPH_EXPORT igraph_error_t igraph_hrg_fit( + const igraph_t *graph, igraph_hrg_t *hrg, igraph_bool_t start, + igraph_integer_t steps +); + +IGRAPH_EXPORT igraph_error_t igraph_hrg_sample( + const igraph_hrg_t *hrg, igraph_t *sample +); + +IGRAPH_EXPORT igraph_error_t igraph_hrg_sample_many( + const igraph_hrg_t *hrg, igraph_graph_list_t *samples, + igraph_integer_t num_samples +); + +IGRAPH_EXPORT igraph_error_t igraph_hrg_game( + igraph_t *graph, const igraph_hrg_t *hrg +); + +IGRAPH_EXPORT igraph_error_t igraph_from_hrg_dendrogram( + igraph_t *graph, const igraph_hrg_t *hrg, igraph_vector_t *prob +); + +IGRAPH_EXPORT igraph_error_t igraph_hrg_consensus(const igraph_t *graph, + igraph_vector_int_t *parents, + igraph_vector_t *weights, + igraph_hrg_t *hrg, + igraph_bool_t start, + igraph_integer_t num_samples); + +IGRAPH_EXPORT igraph_error_t igraph_hrg_predict(const igraph_t *graph, + igraph_vector_int_t *edges, + igraph_vector_t *prob, + igraph_hrg_t *hrg, + igraph_bool_t start, + igraph_integer_t num_samples, + igraph_integer_t num_bins); + +IGRAPH_EXPORT igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, + const igraph_t *graph, + const igraph_vector_t *prob); + +/* Deprecated functions: */ + +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_error_t igraph_hrg_dendrogram( + igraph_t *graph, const igraph_hrg_t *hrg +); + +__END_DECLS + +#endif /* IGRAPH_HRG_H */ diff --git a/include/igraph_interface.h b/include/igraph_interface.h new file mode 100644 index 0000000..88731a3 --- /dev/null +++ b/include/igraph_interface.h @@ -0,0 +1,139 @@ +/* -*- 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_error.h" +#include "igraph_iterators.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Interface */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_copy(igraph_t *to, const igraph_t *from); +IGRAPH_EXPORT igraph_error_t igraph_add_edges(igraph_t *graph, const igraph_vector_int_t *edges, + void *attr); +IGRAPH_EXPORT igraph_error_t igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, + void *attr); +IGRAPH_EXPORT igraph_error_t igraph_delete_edges(igraph_t *graph, igraph_es_t edges); +IGRAPH_EXPORT igraph_error_t igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices); +IGRAPH_EXPORT igraph_error_t igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, + igraph_vector_int_t *idx, + igraph_vector_int_t *invidx); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_vcount(const igraph_t *graph); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_ecount(const igraph_t *graph); +IGRAPH_EXPORT igraph_error_t igraph_neighbors(const igraph_t *graph, igraph_vector_int_t *neis, igraph_integer_t vid, + igraph_neimode_t mode); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_is_directed(const igraph_t *graph); +IGRAPH_EXPORT igraph_error_t igraph_degree_1(const igraph_t *graph, igraph_integer_t *deg, + igraph_integer_t vid, igraph_neimode_t mode, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, + const igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_edge(const igraph_t *graph, igraph_integer_t eid, + igraph_integer_t *from, igraph_integer_t *to); +IGRAPH_EXPORT igraph_error_t igraph_edges(const igraph_t *graph, igraph_es_t eids, + igraph_vector_int_t *edges); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, + const igraph_vector_int_t *pairs, + igraph_bool_t directed, igraph_bool_t error); +IGRAPH_EXPORT igraph_error_t igraph_get_all_eids_between(const igraph_t *graph, igraph_vector_int_t *eids, + igraph_integer_t source, igraph_integer_t target, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_incident(const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t vid, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_is_same_graph(const igraph_t *graph1, const igraph_t *igraph2, igraph_bool_t *res); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_i_property_cache_get_bool(const igraph_t *graph, igraph_cached_property_t prop); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_i_property_cache_has(const igraph_t *graph, igraph_cached_property_t prop); +IGRAPH_EXPORT void igraph_i_property_cache_set_bool(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value); +IGRAPH_EXPORT void igraph_i_property_cache_set_bool_checked(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value); +IGRAPH_EXPORT void igraph_i_property_cache_invalidate(const igraph_t *graph, igraph_cached_property_t prop); +IGRAPH_EXPORT void igraph_i_property_cache_invalidate_all(const igraph_t *graph); + +#define IGRAPH_RETURN_IF_CACHED_BOOL(graphptr, prop, resptr) \ + do { \ + if (igraph_i_property_cache_has((graphptr), (prop))) { \ + *(resptr) = igraph_i_property_cache_get_bool((graphptr), (prop)); \ + return IGRAPH_SUCCESS; \ + } \ + } while (0) + +/** + * \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)[(igraph_integer_t)(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) [(igraph_integer_t)(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/include/igraph_interrupt.h b/include/igraph_interrupt.h new file mode 100644 index 0000000..6d8fb85 --- /dev/null +++ b/include/igraph_interrupt.h @@ -0,0 +1,128 @@ +/* -*- 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_H +#define IGRAPH_INTERRUPT_H + +#include "igraph_decls.h" +#include "igraph_error.h" + +__BEGIN_DECLS + +/* This file contains the igraph interruption handling. */ + +/** + * \section interrupthandlers Interruption handlers + * + * + * \a igraph is designed to be embeddable into several higher level + * languages (R and Python interfaces are included in the original + * package). Since most higher level languages consider internal \a igraph + * calls as atomic, interruption requests (like Ctrl-C in Python) must + * be handled differently depending on the environment \a igraph embeds + * into. + * + * An \emb interruption handler \eme is a function which is called regularly + * by \a igraph during long calculations. A typical usage of the interruption + * handler is to check whether the user tried to interrupt the calculation + * and return an appropriate value to signal this condition. For example, + * in R, one must call an internal R function regularly to check for + * interruption requests, and the \a igraph interruption handler is the + * perfect place to do that. + * + * If you are using the plain C interface of \a igraph or if you are + * allowed to replace the operating system's interruption handler (like + * SIGINT in Un*x systems), these calls are not of much use to you. + * + * The default interruption handler is empty. + * The \ref igraph_set_interruption_handler() function can be used to set a + * new interruption handler function of type + * \ref igraph_interruption_handler_t, see the + * documentation of this type for details. + * + */ + +/** + * \section writing_interruption_handlers Writing interruption handlers + * + * + * You can write and install interruption handlers simply by defining a + * function of type \ref igraph_interruption_handler_t and calling + * \ref igraph_set_interruption_handler(). This feature is useful for + * interface writers, because usually this is the only way to allow handling + * of Ctrl-C and similar keypresses properly. + * + * + * Your interruption handler will be called regularly during long operations + * (so it is not guaranteed to be called during operations which tend to be + * short, like adding single edges). An interruption handler accepts no + * parameters and must return \c IGRAPH_SUCCESS if the calculation should go on. All + * other return values are considered to be a request for interruption, + * and the caller function would return a special error code, \c IGRAPH_INTERRUPTED. + * It is up to your error handler function to handle this error properly. + * + */ + +/** + * \section writing_functions_interruption_handling Writing \a igraph functions with + * proper interruption handling + * + * + * There is practically a simple rule that should be obeyed when writing + * \a igraph functions. If the calculation is expected to take a long time + * in large graphs (a simple rule of thumb is to assume this for every + * function with a time complexity of at least O(n^2)), call + * \ref IGRAPH_ALLOW_INTERRUPTION in regular intervals like every 10th + * iteration or so. + * + */ + +/** + * \typedef igraph_interruption_handler_t + * + * This is the type of the interruption handler functions. + * + * \param data reserved for possible future use + * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise. + */ + +typedef igraph_error_t igraph_interruption_handler_t (void* data); + +/** + * \function igraph_allow_interruption + * + * This is the function which is called (usually via the + * \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. + */ + +IGRAPH_EXPORT igraph_error_t igraph_allow_interruption(void* data); + +IGRAPH_EXPORT igraph_interruption_handler_t * igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler); + +__END_DECLS + +#endif diff --git a/include/igraph_iterators.h b/include/igraph_iterators.h new file mode 100644 index 0000000..144b435 --- /dev/null +++ b/include/igraph_iterators.h @@ -0,0 +1,421 @@ +/* -*- 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_ITERATORS_H +#define IGRAPH_ITERATORS_H + +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_constants.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Vertex selectors */ +/* -------------------------------------------------- */ + +typedef enum { + IGRAPH_VS_ALL, + IGRAPH_VS_ADJ, + IGRAPH_VS_NONE, + IGRAPH_VS_1, + IGRAPH_VS_VECTORPTR, + IGRAPH_VS_VECTOR, + IGRAPH_VS_RANGE, + IGRAPH_VS_NONADJ, +} igraph_vs_type_t; + +typedef struct igraph_vs_t { + igraph_vs_type_t type; + union { + igraph_integer_t vid; /* single vertex */ + const igraph_vector_int_t *vecptr; /* vector of vertices */ + struct { + igraph_integer_t vid; + igraph_neimode_t mode; + } adj; /* adjacent vertices */ + struct { + igraph_integer_t start; /* first index (inclusive) */ + igraph_integer_t end; /* last index (exclusive) */ + } range; /* range of vertices */ + } data; +} igraph_vs_t; + +IGRAPH_EXPORT igraph_error_t igraph_vs_all(igraph_vs_t *vs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_vs_t igraph_vss_all(void); + +IGRAPH_EXPORT igraph_error_t igraph_vs_adj(igraph_vs_t *vs, + igraph_integer_t vid, igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_vs_none(igraph_vs_t *vs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_vs_t igraph_vss_none(void); + +IGRAPH_EXPORT igraph_error_t igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_vs_t igraph_vss_1(igraph_integer_t vid); + +IGRAPH_EXPORT igraph_error_t igraph_vs_vector(igraph_vs_t *vs, + const igraph_vector_int_t *v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_vs_t igraph_vss_vector(const igraph_vector_int_t *v); + +IGRAPH_EXPORT igraph_error_t igraph_vs_vector_small(igraph_vs_t *vs, ...); + +IGRAPH_EXPORT igraph_error_t igraph_vs_vector_copy(igraph_vs_t *vs, + const igraph_vector_int_t *v); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to); + +IGRAPH_EXPORT igraph_error_t igraph_vs_range(igraph_vs_t *vs, igraph_integer_t start, igraph_integer_t end); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end); + +IGRAPH_EXPORT void igraph_vs_destroy(igraph_vs_t *vs); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs); + +IGRAPH_EXPORT igraph_error_t igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src); + +IGRAPH_EXPORT igraph_error_t igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, + igraph_vector_int_t *v); +IGRAPH_EXPORT igraph_error_t igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, + igraph_integer_t *result); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_vs_type_t igraph_vs_type(const igraph_vs_t *vs); + +/* -------------------------------------------------- */ +/* Vertex iterators */ +/* -------------------------------------------------- */ + +typedef enum { + IGRAPH_VIT_RANGE, + IGRAPH_VIT_VECTOR, + IGRAPH_VIT_VECTORPTR, +} igraph_vit_type_t; + +typedef struct igraph_vit_t { + igraph_vit_type_t type; + igraph_integer_t pos; + igraph_integer_t start; /* first index */ + igraph_integer_t end; /* one past last index */ + const igraph_vector_int_t *vec; +} igraph_vit_t; + +/** + * \section IGRAPH_VIT Stepping over the vertices + * + * After creating an iterator with \ref igraph_vit_create(), it + * points to the first vertex in the vertex determined by the vertex + * selector (if there is any). The \ref IGRAPH_VIT_NEXT() macro steps + * to the next vertex, \ref IGRAPH_VIT_END() checks whether there are + * more vertices to visit, \ref IGRAPH_VIT_SIZE() gives the total size + * of the vertices visited so far and to be visited. \ref + * IGRAPH_VIT_RESET() resets the iterator, it will point to the first + * vertex again. Finally \ref IGRAPH_VIT_GET() gives the current vertex + * pointed to by the iterator (call this only if \ref IGRAPH_VIT_END() + * is false). + * + * + * Here is an example on how to step over the neighbors of vertex 0: + * + * igraph_vs_t vs; + * igraph_vit_t vit; + * ... + * igraph_vs_adj(&vs, 0, IGRAPH_ALL); + * igraph_vit_create(&graph, vs, &vit); + * while (!IGRAPH_VIT_END(vit)) { + * printf(" %" IGRAPH_PRId, IGRAPH_VIT_GET(vit)); + * IGRAPH_VIT_NEXT(vit); + * } + * printf("\n"); + * ... + * igraph_vit_destroy(&vit); + * igraph_vs_destroy(&vs); + * + * + */ + +/** + * \define IGRAPH_VIT_NEXT + * \brief Next vertex. + * + * Steps the iterator to the next vertex. Only call this function if + * \ref IGRAPH_VIT_END() returns false. + * \param vit The vertex iterator to step. + * + * Time complexity: O(1). + */ +#define IGRAPH_VIT_NEXT(vit) (++((vit).pos)) +/** + * \define IGRAPH_VIT_END + * \brief Are we at the end? + * + * Checks whether there are more vertices to step to. + * \param vit The vertex iterator to check. + * \return Logical value, if true there are no more vertices to step + * to. + * + * Time complexity: O(1). + */ +#define IGRAPH_VIT_END(vit) ((vit).pos >= (vit).end) +/** + * \define IGRAPH_VIT_SIZE + * \brief Size of a vertex iterator. + * + * Gives the number of vertices in a vertex iterator. + * \param vit The vertex iterator. + * \return The number of vertices. + * + * Time complexity: O(1). + */ +#define IGRAPH_VIT_SIZE(vit) ((vit).end - (vit).start) +/** + * \define IGRAPH_VIT_RESET + * \brief Reset a vertex iterator. + * + * Resets a vertex iterator. After calling this macro the iterator + * will point to the first vertex. + * \param vit The vertex iterator. + * + * Time complexity: O(1). + */ +#define IGRAPH_VIT_RESET(vit) ((vit).pos = (vit).start) +/** + * \define IGRAPH_VIT_GET + * \brief Query the current position. + * + * Gives the vertex ID of the current vertex pointed to by the + * iterator. + * \param vit The vertex iterator. + * \return The vertex ID of the current vertex. + * + * Time complexity: O(1). + */ +#define IGRAPH_VIT_GET(vit) \ + ((igraph_integer_t)(((vit).type == IGRAPH_VIT_RANGE) ? (vit).pos : \ + VECTOR(*(vit).vec)[(vit).pos])) + +IGRAPH_EXPORT igraph_error_t 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); + +IGRAPH_EXPORT igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t *v); + +/* -------------------------------------------------- */ +/* Edge Selectors */ +/* -------------------------------------------------- */ + +typedef enum { + IGRAPH_ES_ALL, + IGRAPH_ES_ALLFROM, + IGRAPH_ES_ALLTO, + IGRAPH_ES_INCIDENT, + IGRAPH_ES_NONE, + IGRAPH_ES_1, + IGRAPH_ES_VECTORPTR, + IGRAPH_ES_VECTOR, + IGRAPH_ES_RANGE, + IGRAPH_ES_PAIRS, + IGRAPH_ES_PATH, + IGRAPH_ES_UNUSED_WAS_MULTIPAIRS, /* placeholder for deprecated IGRAPH_ES_MULTIPAIRS from igraph 0.10 */ + IGRAPH_ES_ALL_BETWEEN, +} igraph_es_type_t; + +typedef struct igraph_es_t { + igraph_es_type_t type; + union { + igraph_integer_t vid; + igraph_integer_t eid; + const igraph_vector_int_t *vecptr; + struct { + igraph_integer_t vid; + igraph_neimode_t mode; + } incident; + struct { + igraph_integer_t start; /* first index (inclusive) */ + igraph_integer_t end; /* last index (exclusive) */ + } range; + struct { + const igraph_vector_int_t *ptr; + igraph_bool_t mode; + } path; + struct { + igraph_integer_t from; + igraph_integer_t to; + igraph_bool_t directed; + } between; + } data; +} igraph_es_t; + +IGRAPH_EXPORT igraph_error_t igraph_es_all(igraph_es_t *es, + igraph_edgeorder_type_t order); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order); + +IGRAPH_EXPORT igraph_error_t igraph_es_incident(igraph_es_t *es, + igraph_integer_t vid, igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_es_none(igraph_es_t *es); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_es_t igraph_ess_none(void); + +IGRAPH_EXPORT igraph_error_t igraph_es_1(igraph_es_t *es, igraph_integer_t eid); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_es_t igraph_ess_1(igraph_integer_t eid); + +IGRAPH_EXPORT igraph_error_t igraph_es_vector(igraph_es_t *es, + const igraph_vector_int_t *v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_es_t igraph_ess_vector(const igraph_vector_int_t *v); + +IGRAPH_EXPORT igraph_error_t igraph_es_range(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_es_t igraph_ess_range(igraph_integer_t from, igraph_integer_t to); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to); + +IGRAPH_EXPORT igraph_error_t igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_int_t *v); + +IGRAPH_EXPORT igraph_error_t igraph_es_pairs(igraph_es_t *es, const igraph_vector_int_t *v, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, int first, ...); + +IGRAPH_EXPORT igraph_error_t igraph_es_path(igraph_es_t *es, const igraph_vector_int_t *v, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, int first, ...); + +IGRAPH_EXPORT igraph_error_t igraph_es_all_between( + igraph_es_t *es, igraph_integer_t from, igraph_integer_t to, + igraph_bool_t directed +); + +IGRAPH_EXPORT void igraph_es_destroy(igraph_es_t *es); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_es_is_all(const igraph_es_t *es); + +IGRAPH_EXPORT igraph_error_t igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src); + +IGRAPH_EXPORT igraph_error_t igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, + igraph_vector_int_t *v); +IGRAPH_EXPORT igraph_error_t igraph_es_size(const igraph_t *graph, const igraph_es_t *es, + igraph_integer_t *result); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_es_type_t igraph_es_type(const igraph_es_t *es); + + +/* -------------------------------------------------- */ +/* Edge Iterators */ +/* -------------------------------------------------- */ + +typedef enum { + IGRAPH_EIT_RANGE, + IGRAPH_EIT_VECTOR, + IGRAPH_EIT_VECTORPTR, +} igraph_eit_type_t; + +typedef struct igraph_eit_t { + igraph_eit_type_t type; + igraph_integer_t pos; + igraph_integer_t start; /* first index */ + igraph_integer_t end; /* one past last index */ + const igraph_vector_int_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 + * edges to visit, \ref IGRAPH_EIT_SIZE() gives the number of edges in + * the edge sequence, \ref IGRAPH_EIT_RESET() resets the iterator to + * the first edge and \ref IGRAPH_EIT_GET() returns the id of the + * current edge. + */ + +/** + * \define IGRAPH_EIT_NEXT + * \brief Next edge. + * + * Steps the iterator to the next edge. Call this function only if + * \ref IGRAPH_EIT_END() returns false. + * \param eit The edge iterator to step. + * + * Time complexity: O(1). + */ +#define IGRAPH_EIT_NEXT(eit) (++((eit).pos)) +/** + * \define IGRAPH_EIT_END + * \brief Are we at the end? + * + * Checks whether there are more edges to step to. + * \param wit The edge iterator to check. + * \return Logical value, if true there are no more edges + * to step to. + * + * Time complexity: O(1). + */ +#define IGRAPH_EIT_END(eit) ((eit).pos >= (eit).end) +/** + * \define IGRAPH_EIT_SIZE + * \brief Number of edges in the iterator. + * + * Gives the number of edges in an edge iterator. + * \param eit The edge iterator. + * \return The number of edges. + * + * Time complexity: O(1). + */ +#define IGRAPH_EIT_SIZE(eit) ((eit).end - (eit).start) +/** + * \define IGRAPH_EIT_RESET + * \brief Reset an edge iterator. + * + * Resets an edge iterator. After calling this macro the iterator will + * point to the first edge. + * \param eit The edge iterator. + * + * Time complexity: O(1). + */ +#define IGRAPH_EIT_RESET(eit) ((eit).pos = (eit).start) +/** + * \define IGRAPH_EIT_GET + * \brief Query an edge iterator. + * + * Gives the edge ID of the current edge pointed to by an iterator. + * \param eit The edge iterator. + * \return The id of the current edge. + * + * Time complexity: O(1). + */ +#define IGRAPH_EIT_GET(eit) \ + (igraph_integer_t)((((eit).type == IGRAPH_EIT_RANGE) ? (eit).pos : \ + VECTOR(*(eit).vec)[(eit).pos])) + +IGRAPH_EXPORT igraph_error_t 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); + +IGRAPH_EXPORT igraph_error_t igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_int_t *v); + +__END_DECLS + +#endif diff --git a/include/igraph_lapack.h b/include/igraph_lapack.h new file mode 100644 index 0000000..76b30f3 --- /dev/null +++ b/include/igraph_lapack.h @@ -0,0 +1,112 @@ +/* -*- 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_error.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 igraph_error_t igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, + int *info); +IGRAPH_EXPORT igraph_error_t igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, + const igraph_vector_int_t *ipiv, igraph_matrix_t *b); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_lapack_dgehrd(const igraph_matrix_t *A, + int ilo, int ihi, + igraph_matrix_t *result); + +__END_DECLS + +#endif diff --git a/include/igraph_layout.h b/include/igraph_layout.h new file mode 100644 index 0000000..1730f1a --- /dev/null +++ b/include/igraph_layout.h @@ -0,0 +1,298 @@ +/* -*- 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_datatype.h" +#include "igraph_error.h" +#include "igraph_iterators.h" +#include "igraph_matrix_list.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" +#include "igraph_matrix.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 igraph_error_t igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT igraph_error_t igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t order); +IGRAPH_EXPORT igraph_error_t igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t center, const igraph_vector_int_t *order); +IGRAPH_EXPORT igraph_error_t igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t width); +IGRAPH_EXPORT igraph_error_t 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 *weights, + const igraph_vector_t *minx, + const igraph_vector_t *maxx, + const igraph_vector_t *miny, + const igraph_vector_t *maxy); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, + igraph_neimode_t mode, + const igraph_vector_int_t *roots, + const igraph_vector_int_t *rootlevel); +IGRAPH_EXPORT igraph_error_t igraph_layout_reingold_tilford_circular(const igraph_t *graph, + igraph_matrix_t *res, + igraph_neimode_t mode, + const igraph_vector_int_t *roots, + const igraph_vector_int_t *rootlevel); +IGRAPH_EXPORT igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, + igraph_t *extd_graph, igraph_vector_int_t *extd_to_orig_eids, + const igraph_vector_int_t* layers, igraph_real_t hgap, + igraph_real_t vgap, igraph_integer_t maxiter, const igraph_vector_t *weights); + +IGRAPH_EXPORT igraph_error_t igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT igraph_error_t igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT igraph_error_t igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t width, igraph_integer_t height); +IGRAPH_EXPORT igraph_error_t 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 *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 igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_layout_mds(const igraph_t *graph, igraph_matrix_t *res, + const igraph_matrix_t *dist, igraph_integer_t dim); + +IGRAPH_EXPORT igraph_error_t 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, igraph_integer_t maxiter); + +IGRAPH_EXPORT igraph_error_t igraph_layout_umap(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_bool_t distances_are_weights); + +IGRAPH_EXPORT igraph_error_t igraph_layout_umap_3d(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_bool_t distances_are_weights); + +IGRAPH_EXPORT igraph_error_t igraph_layout_umap_compute_weights(const igraph_t *graph, + const igraph_vector_t *distances, + igraph_vector_t *weights); + + +/** + * \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 igraph_error_t igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, + igraph_layout_drl_default_t templ); +IGRAPH_EXPORT igraph_error_t 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); + +IGRAPH_EXPORT igraph_error_t 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); + +IGRAPH_EXPORT igraph_error_t igraph_layout_merge_dla(const igraph_vector_ptr_t *graphs, + const igraph_matrix_list_t *coords, + igraph_matrix_t *res); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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); + +/** + * \typedef igraph_root_choice_t + * \brief Root choice heuristic for tree visualizations. + * + * Used with \ref igraph_roots_for_tree_layout(). + */ + +typedef enum { + IGRAPH_ROOT_CHOICE_DEGREE, + IGRAPH_ROOT_CHOICE_ECCENTRICITY +} igraph_root_choice_t; + +IGRAPH_EXPORT igraph_error_t igraph_roots_for_tree_layout( + const igraph_t *graph, + igraph_neimode_t mode, + igraph_vector_int_t *roots, + igraph_root_choice_t use_eccentricity); + + +__END_DECLS + +#endif diff --git a/include/igraph_lsap.h b/include/igraph_lsap.h new file mode 100644 index 0000000..61898d4 --- /dev/null +++ b/include/igraph_lsap.h @@ -0,0 +1,18 @@ + +#ifndef IGRAPH_LSAP_H +#define IGRAPH_LSAP_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_matrix.h" +#include "igraph_vector.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_solve_lsap(const igraph_matrix_t *c, igraph_integer_t n, + igraph_vector_int_t *p); + +__END_DECLS + +#endif diff --git a/include/igraph_matching.h b/include/igraph_matching.h new file mode 100644 index 0000000..44b9082 --- /dev/null +++ b/include/igraph_matching.h @@ -0,0 +1,52 @@ +/* -*- 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_datatype.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Matchings in graphs */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_is_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, const igraph_vector_int_t* matching, + igraph_bool_t* result); +IGRAPH_EXPORT igraph_error_t igraph_is_maximal_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, const igraph_vector_int_t* matching, + igraph_bool_t* result); + +IGRAPH_EXPORT igraph_error_t 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_int_t* matching, + const igraph_vector_t* weights, igraph_real_t eps); + +__END_DECLS + +#endif diff --git a/include/igraph_matrix.h b/include/igraph_matrix.h new file mode 100644 index 0000000..f092d7a --- /dev/null +++ b/include/igraph_matrix.h @@ -0,0 +1,103 @@ +/* -*- 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_MATRIX_H +#define IGRAPH_MATRIX_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Matrix, very similar to vector */ +/* -------------------------------------------------- */ + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "igraph_matrix_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#include "igraph_pmt.h" +#include "igraph_matrix_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "igraph_matrix_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "igraph_matrix_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_BOOL + +#define BASE_COMPLEX +#include "igraph_pmt.h" +#include "igraph_matrix_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_COMPLEX + +#define IGRAPH_MATRIX_NULL { IGRAPH_VECTOR_NULL, 0, 0 } +#define IGRAPH_MATRIX_INIT_FINALLY(m, nr, nc) \ + do { IGRAPH_CHECK(igraph_matrix_init(m, nr, nc)); \ + IGRAPH_FINALLY(igraph_matrix_destroy, m); } while (0) +#define IGRAPH_MATRIX_INT_INIT_FINALLY(m, nr, nc) \ + do { IGRAPH_CHECK(igraph_matrix_int_init(m, nr, nc)); \ + IGRAPH_FINALLY(igraph_matrix_int_destroy, m); } while (0) + +/** + * \ingroup matrix + * \define MATRIX + * \brief Accessing an element of a matrix. + * + * Note that there are no range checks right now. + * This functionality might be redefined as a proper function later. + * \param m The matrix object. + * \param i The index of the row, starting with zero. + * \param j The index of the column, starting with zero. + * + * Time complexity: O(1). + */ +#define MATRIX(m,i,j) ((m).data.stor_begin[(m).nrow*(j)+(i)]) + +IGRAPH_DEPRECATED 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 IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_matrix_all_almost_e(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t eps); + +IGRAPH_EXPORT igraph_error_t igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_zapsmall(igraph_matrix_complex_t *m, igraph_real_t tol); + +__END_DECLS + +#endif diff --git a/include/igraph_matrix_list.h b/include/igraph_matrix_list.h new file mode 100644 index 0000000..1e44cf8 --- /dev/null +++ b/include/igraph_matrix_list.h @@ -0,0 +1,59 @@ +/* -*- 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_MATRIX_LIST_H +#define IGRAPH_MATRIX_LIST_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_matrix.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Flexible list of matrices */ +/* -------------------------------------------------- */ + +/* Indicate to igraph_typed_list_pmt.h that we are going to work with _matrices_ + * of the base type, not the base type directly */ +#define MATRIX_LIST + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#undef MATRIX_LIST + +/* -------------------------------------------------- */ +/* Helper macros */ +/* -------------------------------------------------- */ + +#define IGRAPH_MATRIX_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_matrix_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_matrix_list_destroy, v); } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_matrix_pmt.h b/include/igraph_matrix_pmt.h new file mode 100644 index 0000000..3269753 --- /dev/null +++ b/include/igraph_matrix_pmt.h @@ -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 + +*/ + +typedef struct TYPE(igraph_matrix) { + TYPE(igraph_vector) data; + igraph_integer_t nrow, ncol; +} TYPE(igraph_matrix); + +/*---------------*/ +/* Allocation */ +/*---------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init)( + TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init_array)( + TYPE(igraph_matrix)* m, const BASE* data, igraph_integer_t nrow, igraph_integer_t ncol, igraph_matrix_storage_t storage); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init_copy)( + TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_matrix, copy)( + TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); + +/*--------------------*/ +/* Accessing elements */ +/*--------------------*/ + +/* MATRIX */ +IGRAPH_EXPORT IGRAPH_DEPRECATED BASE FUNCTION(igraph_matrix, e)( + const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); +IGRAPH_EXPORT IGRAPH_DEPRECATED BASE* FUNCTION(igraph_matrix, e_ptr)( + const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_matrix, get)( + const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE* FUNCTION(igraph_matrix, get_ptr)( + const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, set)( + TYPE(igraph_matrix)* m, igraph_integer_t row, igraph_integer_t 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, + igraph_integer_t nrow, igraph_integer_t ncol); +IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view_from_vector)( + const TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, + igraph_integer_t ncol +); + +/*------------------*/ +/* Copying matrices */ +/*------------------*/ + +IGRAPH_EXPORT void FUNCTION(igraph_matrix, copy_to)(const TYPE(igraph_matrix) *m, BASE *to); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2); + +/*--------------------------*/ +/* Copying rows and columns */ +/*--------------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, get_row)( + const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, get_col)( + const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, set_row)( + TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, set_col)( + TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_rows)( + const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_int_t *rows); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_cols)( + const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_int_t *cols); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_rows_cols)( + const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, + const igraph_vector_int_t *rows, const igraph_vector_int_t *cols); + +/*-----------------------------*/ +/* Exchanging rows and columns */ +/*-----------------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_rows)( + TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_cols)( + TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_rowcol)( + TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m); + +/*-----------------------------*/ +/* Matrix operations */ +/*-----------------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT igraph_error_t 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_FUNCATTR_PURE igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t FUNCTION(igraph_matrix, max)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_min)( + const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_max)( + const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, minmax)( + const TYPE(igraph_matrix) *m, BASE *min, BASE *max); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_minmax)( + const TYPE(igraph_matrix) *m, igraph_integer_t *imin, igraph_integer_t *jmin, + igraph_integer_t *imax, igraph_integer_t *jmax); +#endif + +/*------------------------------*/ +/* Comparison */ +/*------------------------------*/ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, all_e)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +#ifndef NOTORDERED +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, all_l)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, all_g)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, all_le)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_matrix) *lhs, + const TYPE(igraph_matrix) *rhs); +#endif + +/*-------------------*/ +/* Matrix properties */ +/*-------------------*/ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_matrix, is_equal)(const TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +#ifndef NOTORDERED +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(igraph_matrix) *m1, + const TYPE(igraph_matrix) *m2); +#endif + +/*------------------------*/ +/* Searching for elements */ +/*------------------------*/ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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, igraph_integer_t from, BASE what, + igraph_integer_t *pos, igraph_integer_t *row, igraph_integer_t *col); + +/*------------------------*/ +/* Resizing operations */ +/*------------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, resize)( + TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, resize_min)( + TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add_cols)( + TYPE(igraph_matrix) *m, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add_rows)( + TYPE(igraph_matrix) *m, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, remove_col)( + TYPE(igraph_matrix) *m, igraph_integer_t col); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, remove_row)( + TYPE(igraph_matrix) *m, igraph_integer_t row); + +/*------------------------*/ +/* Print as text */ +/*------------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, FILE *file); + +#ifdef OUT_FORMAT +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, const char *format); +#endif /* OUT_FORMAT */ + +/*-----------------------------------------*/ +/* Operations specific to complex matrices */ +/*-----------------------------------------*/ + +#ifdef BASE_COMPLEX + +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_real(const igraph_matrix_complex_t *v, + igraph_matrix_t *real); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, + igraph_matrix_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, + igraph_matrix_t *real, + igraph_matrix_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_create(igraph_matrix_complex_t *v, + const igraph_matrix_t *real, + const igraph_matrix_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, + const igraph_matrix_t *r, + const igraph_matrix_t *theta); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_matrix_complex_all_almost_e(igraph_matrix_complex_t *lhs, + igraph_matrix_complex_t *rhs, + igraph_real_t eps); + +#endif /* BASE_COMPLEX */ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, permdelete_rows)( + TYPE(igraph_matrix) *m, igraph_integer_t *index, igraph_integer_t nremove); diff --git a/include/igraph_memory.h b/include/igraph_memory.h new file mode 100644 index 0000000..c381e32 --- /dev/null +++ b/include/igraph_memory.h @@ -0,0 +1,56 @@ +/* -*- 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 "igraph_decls.h" + +#include +#include + +__BEGIN_DECLS + +/* Helper macto to check if n*sizeof(t) overflows in IGRAPH_CALLOC and IGRAPH_REALLOC */ +#define IGRAPH_I_ALLOC_CHECK_OVERFLOW(n,t,expr) \ + (t*) ((0 <= (n) && ((size_t)(n)) <= SIZE_MAX / sizeof(t)) ? (expr) : NULL) + +#define IGRAPH_CALLOC(n,t) IGRAPH_I_ALLOC_CHECK_OVERFLOW(n, t, calloc(sizeof(t) * ((n) > 0 ? (n) : 1), 1)) +#define IGRAPH_MALLOC(n) malloc( (size_t) ((n) > 0 ? (n) : 1) ) +#define IGRAPH_REALLOC(p,n,t) IGRAPH_I_ALLOC_CHECK_OVERFLOW(n, t, realloc((void*)(p), sizeof(t) * ((n) > 0 ? (n) : 1))) +#define IGRAPH_FREE(p) (free( (void *)(p) ), (p) = NULL) + +/* These are deprecated and scheduled for removal in 0.11 */ +#define igraph_Calloc IGRAPH_CALLOC +#define igraph_Realloc IGRAPH_REALLOC +#define igraph_Free IGRAPH_FREE +/* Deprecated section ends here */ + +IGRAPH_EXPORT void *igraph_calloc(size_t count, size_t size); +IGRAPH_EXPORT void *igraph_malloc(size_t size); +IGRAPH_EXPORT void *igraph_realloc(void* ptr, size_t size); +IGRAPH_EXPORT void igraph_free(void *ptr); + +__END_DECLS + +#endif diff --git a/include/igraph_microscopic_update.h b/include/igraph_microscopic_update.h new file mode 100644 index 0000000..bff0e03 --- /dev/null +++ b/include/igraph_microscopic_update.h @@ -0,0 +1,61 @@ +/* -*- 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_error.h" +#include "igraph_iterators.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_deterministic_optimal_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_optimal_t optimality, + const igraph_vector_t *quantities, + igraph_vector_int_t *strategies, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_moran_process(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_t *quantities, + igraph_vector_int_t *strategies, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_roulette_wheel_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_bool_t islocal, + const igraph_vector_t *quantities, + igraph_vector_int_t *strategies, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_imitate_algorithm_t algo, + const igraph_vector_t *quantities, + igraph_vector_int_t *strategies, + igraph_neimode_t mode); + +__END_DECLS + +#endif diff --git a/include/igraph_mixing.h b/include/igraph_mixing.h new file mode 100644 index 0000000..b69a6c8 --- /dev/null +++ b/include/igraph_mixing.h @@ -0,0 +1,73 @@ +/* -*- 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_error.h" +#include "igraph_vector.h" +#include "igraph_matrix.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, + const igraph_vector_int_t *types, + igraph_real_t *res, + igraph_bool_t directed, + igraph_bool_t normalized); + +IGRAPH_EXPORT igraph_error_t igraph_assortativity(const igraph_t *graph, + const igraph_vector_t *values, + const igraph_vector_t *values_in, + igraph_real_t *res, + igraph_bool_t directed, + igraph_bool_t normalized); + +IGRAPH_EXPORT igraph_error_t igraph_assortativity_degree(const igraph_t *graph, + igraph_real_t *res, + igraph_bool_t directed); + +IGRAPH_EXPORT igraph_error_t igraph_joint_degree_matrix( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_matrix_t *jdm, + igraph_integer_t dout, igraph_integer_t din); + +IGRAPH_EXPORT igraph_error_t igraph_joint_degree_distribution( + const igraph_t *graph, const igraph_vector_t *weights, igraph_matrix_t *p, + igraph_neimode_t from_mode, igraph_neimode_t to_mode, + igraph_bool_t directed_neighbors, + igraph_bool_t normalized, + igraph_integer_t max_from_degree, igraph_integer_t max_to_degree); + +IGRAPH_EXPORT igraph_error_t igraph_joint_type_distribution( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_matrix_t *p, + const igraph_vector_int_t *from_types, const igraph_vector_int_t *to_types, + igraph_bool_t directed, igraph_bool_t normalized); + +__END_DECLS + +#endif diff --git a/include/igraph_motifs.h b/include/igraph_motifs.h new file mode 100644 index 0000000..6394b86 --- /dev/null +++ b/include/igraph_motifs.h @@ -0,0 +1,101 @@ +/* -*- 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_MOTIFS_H +#define IGRAPH_MOTIFS_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_iterators.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Graph motifs */ +/* -------------------------------------------------- */ + +/** + * \typedef igraph_motifs_handler_t + * \brief Callback type for \c igraph_motifs_randesu_callback. + * + * \ref igraph_motifs_randesu_callback() calls a specified callback + * function whenever a new motif is found during a motif search. This + * callback function must be of type \c igraph_motifs_handler_t. It has + * the following arguments: + * + * \param graph The graph that that algorithm is working on. Of course + * this must not be modified. + * \param vids The IDs of the vertices in the motif that has just been + * found. This vector is owned by the motif search algorithm, so do not + * modify or destroy it; make a copy of it if you need it later. + * \param isoclass The isomorphism class of the motif that has just been + * found. Use \ref igraph_graph_count() to find the maximum possible + * isoclass for graphs of a given size. See \ref igraph_isoclass and + * \ref igraph_isoclass_subgraph for more information. + * \param extra The extra argument that was passed to \ref + * igraph_motifs_randesu_callback(). + * \return \c IGRAPH_SUCCESS to continue the motif search, + * \c IGRAPH_STOP to stop the motif search and return to the caller + * normally. Any other return value is interpreted as an igraph error code, + * which will terminate the search and return the same error code to the + * caller. + * + * \sa \ref igraph_motifs_randesu_callback() + */ + +typedef igraph_error_t igraph_motifs_handler_t(const igraph_t *graph, + igraph_vector_int_t *vids, + igraph_integer_t isoclass, + void* extra); + +IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t size, const igraph_vector_t *cut_prob); + +IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_integer_t size, + const igraph_vector_t *cut_prob, + igraph_motifs_handler_t *callback, + void* extra); + +IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, + igraph_integer_t size, const igraph_vector_t *cut_prob, + igraph_integer_t sample_size, + const igraph_vector_int_t *sample); +IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, + igraph_integer_t size, const igraph_vector_t *cut_prob); + +IGRAPH_EXPORT igraph_error_t igraph_dyad_census(const igraph_t *graph, igraph_real_t *mut, + igraph_real_t *asym, igraph_real_t *null); +IGRAPH_EXPORT igraph_error_t igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_adjacent_triangles(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids); + +IGRAPH_EXPORT igraph_error_t igraph_list_triangles(const igraph_t *graph, + igraph_vector_int_t *res); + +__END_DECLS + +#endif diff --git a/include/igraph_neighborhood.h b/include/igraph_neighborhood.h new file mode 100644 index 0000000..d96d22c --- /dev/null +++ b/include/igraph_neighborhood.h @@ -0,0 +1,49 @@ +/* -*- 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_error.h" +#include "igraph_graph_list.h" +#include "igraph_iterators.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, igraph_integer_t mindist); +IGRAPH_EXPORT igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, igraph_integer_t mindist); +IGRAPH_EXPORT igraph_error_t igraph_neighborhood_graphs(const igraph_t *graph, igraph_graph_list_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, + igraph_integer_t mindist); + +__END_DECLS + +#endif diff --git a/include/igraph_nongraph.h b/include/igraph_nongraph.h new file mode 100644 index 0000000..63aa374 --- /dev/null +++ b/include/igraph_nongraph.h @@ -0,0 +1,115 @@ +/* -*- 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_error.h" +#include "igraph_matrix.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__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 + +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); + +/* -------------------------------------------------- */ +/* 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; \c NaN if it has + * not been calculated yet. 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. + * \member data The vector containing the original input data. May not be valid + * any more if the caller already destroyed the vector. + */ +typedef struct igraph_plfit_result_t { + igraph_bool_t continuous; + igraph_real_t alpha; + igraph_real_t xmin; + igraph_real_t L; + igraph_real_t D; + const igraph_vector_t* data; +} igraph_plfit_result_t; + +IGRAPH_EXPORT igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, + igraph_integer_t binwidth); +IGRAPH_EXPORT igraph_error_t igraph_random_sample(igraph_vector_int_t *res, igraph_integer_t l, igraph_integer_t h, + igraph_integer_t length); +IGRAPH_EXPORT igraph_error_t igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_int_t *resverts, + igraph_matrix_t *rescoords); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_bool_t igraph_almost_equals(double a, double b, double eps); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST int igraph_cmp_epsilon(double a, double b, double eps); + +IGRAPH_EXPORT igraph_error_t igraph_power_law_fit( + const igraph_vector_t* vector, igraph_plfit_result_t* result, + igraph_real_t xmin, igraph_bool_t force_continuous +); +IGRAPH_EXPORT igraph_error_t igraph_plfit_result_calculate_p_value( + const igraph_plfit_result_t* model, igraph_real_t* result, igraph_real_t precision +); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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 +); + +__END_DECLS + +#endif diff --git a/include/igraph_operators.h b/include/igraph_operators.h new file mode 100644 index 0000000..7b06e65 --- /dev/null +++ b/include/igraph_operators.h @@ -0,0 +1,99 @@ +/* -*- 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_datatype.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Graph operators */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT igraph_error_t igraph_disjoint_union(igraph_t *res, + const igraph_t *left, const igraph_t *right); +IGRAPH_EXPORT igraph_error_t igraph_disjoint_union_many(igraph_t *res, + const igraph_vector_ptr_t *graphs); +IGRAPH_EXPORT igraph_error_t igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); +IGRAPH_EXPORT igraph_error_t igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_int_list_t *edgemaps); +IGRAPH_EXPORT igraph_error_t igraph_join(igraph_t *res, const igraph_t *left, + const igraph_t *right); +IGRAPH_EXPORT igraph_error_t igraph_intersection(igraph_t *res, + const igraph_t *left, const igraph_t *right, + igraph_vector_int_t *edge_map1, + igraph_vector_int_t *edge_map2); +IGRAPH_EXPORT igraph_error_t igraph_intersection_many(igraph_t *res, + const igraph_vector_ptr_t *graphs, + igraph_vector_int_list_t *edgemaps); +IGRAPH_EXPORT igraph_error_t igraph_difference(igraph_t *res, + const igraph_t *orig, const igraph_t *sub); +IGRAPH_EXPORT igraph_error_t igraph_complementer(igraph_t *res, const igraph_t *graph, + igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); +IGRAPH_EXPORT igraph_error_t igraph_contract_vertices(igraph_t *graph, + const igraph_vector_int_t *mapping, + const igraph_attribute_combination_t *vertex_comb); +IGRAPH_EXPORT igraph_error_t igraph_permute_vertices(const igraph_t *graph, igraph_t *res, + const igraph_vector_int_t *permutation); +IGRAPH_EXPORT igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_graph_power(const igraph_t *graph, igraph_t *res, + igraph_integer_t order, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode); +IGRAPH_EXPORT igraph_error_t igraph_simplify(igraph_t *graph, + igraph_bool_t remove_multiple, igraph_bool_t remove_loops, + const igraph_attribute_combination_t *edge_comb); +IGRAPH_EXPORT igraph_error_t igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, + const igraph_vs_t vids, + igraph_subgraph_implementation_t impl, + igraph_vector_int_t *map, + igraph_vector_int_t *invmap); +IGRAPH_EXPORT igraph_error_t igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, + const igraph_vs_t vids, igraph_subgraph_implementation_t impl); +IGRAPH_EXPORT igraph_error_t igraph_induced_subgraph_edges( + const igraph_t *graph, igraph_vs_t vids, igraph_vector_int_t *edges); +IGRAPH_EXPORT igraph_error_t igraph_subgraph_from_edges(const igraph_t *graph, igraph_t *res, + const igraph_es_t eids, igraph_bool_t delete_vertices); +IGRAPH_EXPORT igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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/include/igraph_paths.h b/include/igraph_paths.h new file mode 100644 index 0000000..d0e7804 --- /dev/null +++ b/include/igraph_paths.h @@ -0,0 +1,364 @@ +/* -*- 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_constants.h" +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_iterators.h" +#include "igraph_matrix.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +/** + * \typedef igraph_astar_heuristic_func_t + * \brief Distance estimator for A* algorithm. + * + * \ref igraph_get_shortest_path_astar() uses a heuristic based on a distance + * estimate to the target vertex to guide its search, and determine + * which vertex to try next. The heurstic function is expected to compute + * an estimate of the distance between \p from and \p to. In order for + * \ref igraph_get_shortest_path_astar() to find an exact shortest path, + * the distance must not be overestimated, i.e. the heuristic function + * must be \em admissible. + * + * \param result The result of the heuristic, i.e. the estimated distance. + * A lower value will mean this vertex will be a better candidate for + * exploration. + * \param from The vertex ID of the candidate vertex will be passed here. + * \param to The vertex ID of the endpoint of the path, i.e. the \c to parameter + * given to \ref igraph_get_shortest_path_astar(), will be passed here. + * \param extra The \c extra argument that was passed to + * \ref igraph_get_shortest_path_astar(). + * \return Error code. Must return \c IGRAPH_SUCCESS if there were no errors. + * This can be used to break off the algorithm if something unexpected happens, + * like a failed memory allocation (\c IGRAPH_ENOMEM). + * + * \sa \ref igraph_get_shortest_path_astar() + */ +typedef igraph_error_t igraph_astar_heuristic_func_t( + igraph_real_t *result, + igraph_integer_t from, igraph_integer_t to, + void *extra); + +typedef enum { + IGRAPH_FLOYD_WARSHALL_AUTOMATIC = 0, + IGRAPH_FLOYD_WARSHALL_ORIGINAL = 1, + IGRAPH_FLOYD_WARSHALL_TREE = 2 +} igraph_floyd_warshall_algorithm_t; + +IGRAPH_EXPORT igraph_error_t igraph_diameter(const igraph_t *graph, igraph_real_t *res, + igraph_integer_t *from, igraph_integer_t *to, + igraph_vector_int_t *vertex_path, igraph_vector_int_t *edge_path, + igraph_bool_t directed, igraph_bool_t unconn); +IGRAPH_EXPORT igraph_error_t igraph_diameter_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *res, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_vector_int_t *vertex_path, + igraph_vector_int_t *edge_path, + igraph_bool_t directed, + igraph_bool_t unconn); + +IGRAPH_EXPORT igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t from, const igraph_vs_t to, + igraph_neimode_t mode, igraph_real_t cutoff); +IGRAPH_EXPORT igraph_error_t igraph_distances(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t from, const igraph_vs_t to, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_distances_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 igraph_error_t igraph_distances_dijkstra_cutoff(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_real_t cutoff); +IGRAPH_EXPORT igraph_error_t igraph_distances_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 igraph_error_t igraph_distances_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 igraph_error_t igraph_distances_floyd_warshall(const igraph_t *graph, + igraph_matrix_t *res, + igraph_vs_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_floyd_warshall_algorithm_t method); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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 IGRAPH_DEPRECATED igraph_error_t 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 IGRAPH_DEPRECATED igraph_error_t 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 IGRAPH_DEPRECATED igraph_error_t 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 igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, const igraph_vs_t to, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges); +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges); +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges); + +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path_bellman_ford(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path_astar(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_astar_heuristic_func_t *heuristic, + void *extra); + +IGRAPH_EXPORT igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_vector_int_t *nrgeo, + igraph_integer_t from, const igraph_vs_t to, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_vector_int_t *nrgeo, + igraph_integer_t from, igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, + igraph_real_t *unconnected, igraph_bool_t directed); + +IGRAPH_EXPORT igraph_error_t igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *weights, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_eccentricity(const igraph_t *graph, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_eccentricity_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_radius(const igraph_t *graph, igraph_real_t *radius, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_radius_dijkstra(const igraph_t *graph, const igraph_vector_t *weights, + igraph_real_t *radius, igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_graph_center(const igraph_t *graph, + igraph_vector_int_t *res, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_graph_center_dijkstra( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_int_t *res, igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_pseudo_diameter(const igraph_t *graph, + igraph_real_t *diameter, + igraph_integer_t vid_start, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_bool_t directed, + igraph_bool_t unconn); +IGRAPH_EXPORT igraph_error_t igraph_pseudo_diameter_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *diameter, + igraph_integer_t vid_start, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_bool_t directed, + igraph_bool_t unconn); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_random_walk(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t start, + igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck); + +IGRAPH_EXPORT igraph_error_t igraph_get_k_shortest_paths(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_list_t *vertex_paths, + igraph_vector_int_list_t *edge_paths, + igraph_integer_t k, + igraph_integer_t from, + igraph_integer_t to, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_spanner(const igraph_t *graph, + igraph_vector_int_t *spanner, + igraph_real_t stretch, + const igraph_vector_t *weights); + +IGRAPH_EXPORT igraph_error_t igraph_get_widest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges); +IGRAPH_EXPORT igraph_error_t igraph_get_widest_path(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_widest_path_widths_floyd_warshall(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 igraph_error_t igraph_widest_path_widths_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 igraph_error_t igraph_voronoi(const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_t *distances, + const igraph_vector_int_t *generators, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_voronoi_tiebreaker_t tiebreaker); + +IGRAPH_EXPORT igraph_error_t igraph_expand_path_to_pairs(igraph_vector_int_t *path); + +IGRAPH_EXPORT igraph_error_t igraph_vertex_path_from_edge_path( + const igraph_t *graph, igraph_integer_t start, + const igraph_vector_int_t *edge_path, igraph_vector_int_t *vertex_path, + igraph_neimode_t mode); + +/* Deprecated functions: */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_random_edge_walk(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_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/include/igraph_pmt.h b/include/igraph_pmt.h new file mode 100644 index 0000000..de90c1e --- /dev/null +++ b/include/igraph_pmt.h @@ -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 + +*/ + +#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) +#define CONCAT5x(a,b,c,d,e) a ## _ ## b ## _ ## c ## _ ## d ## _ ## e +#define CONCAT5(a,b,c,d,e) CONCAT5x(a,b,c,d,e) + +#if defined(BASE_IGRAPH_REAL) + #define BASE igraph_real_t + #define BASE_VECTOR igraph_vector_t + #define BASE_MATRIX igraph_matrix_t + #define SHORT + #define OUT_FORMAT "%g" + #define PRINTFUNC(val) igraph_real_printf(val) + #define SNPRINTFUNC(str, size, val) igraph_real_snprintf(str, size, val) + #define FPRINTFUNC_ALIGNED(file, width, val) igraph_real_fprintf_aligned(file, width, val) + #define FPRINTFUNC(file, val) igraph_real_fprintf(file, val) + #define ZERO 0.0 + #define ONE 1.0 + #define MULTIPLICITY 1 + +#elif defined(BASE_CHAR) + #define BASE char + #define BASE_VECTOR igraph_vector_char_t + #define BASE_MATRIX igraph_matrix_char_t + #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 BASE_VECTOR igraph_vector_bool_t + #define BASE_MATRIX igraph_matrix_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 BASE_VECTOR igraph_vector_int_t + #define BASE_MATRIX igraph_matrix_int_t + #define SHORT int + #define OUT_FORMAT "%" IGRAPH_PRId + #define ZERO 0 + #define ONE 1 + #define MULTIPLICITY 1 + +#elif defined(BASE_FORTRAN_INT) + #define BASE int + #define SHORT fortran_int + #define OUT_FORMAT "%d" + #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 BASE_VECTOR igraph_vector_complex_t + #define BASE_MATRIX igraph_matrix_complex_t + #define SHORT complex + #define PRINTFUNC(val) igraph_complex_printf(val) + #define SNPRINTFUNC(str, size, val) igraph_complex_snprintf(str, size, val) + #define FPRINTFUNC_ALIGNED(file, width, val) igraph_complex_fprintf_aligned(file, width, val) + #define FPRINTFUNC(file, val) igraph_complex_fprintf(file, val) + #define ZERO {{0.0, 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))) + +#elif defined(BASE_GRAPH) + #define BASE igraph_t + +#elif defined(BASE_BITSET) + #define BASE igraph_bitset_t + +#else + #error unknown BASE_ directive +#endif + +#if defined(VECTOR_LIST) + #if defined(BASE_IGRAPH_REAL) + #define FUNCTION(c) CONCAT2x(igraph_vector_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_vector_list,c) + #define TYPE igraph_vector_list_t + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define FUNCTION(c) CONCAT2x(igraph_vector_bool_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_vector_bool_list,c) + #define TYPE igraph_vector_bool_list_t + #else + #define FUNCTION(c) CONCAT4(igraph_vector,SHORT,list,c) + #define INTERNAL_FUNCTION(c) CONCAT4(igraph_i_vector,SHORT,list,c) + #define TYPE CONCAT3(igraph_vector,SHORT,list_t) + #endif +#elif defined(MATRIX_LIST) + #if defined(BASE_IGRAPH_REAL) + #define FUNCTION(c) CONCAT2x(igraph_matrix_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_matrix_list,c) + #define TYPE igraph_matrix_list_t + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define FUNCTION(c) CONCAT2x(igraph_matrix_bool_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_matrix_bool_list,c) + #define TYPE igraph_matrix_bool_list_t + #else + #define FUNCTION(c) CONCAT4(igraph_matrix,SHORT,list,c) + #define INTERNAL_FUNCTION(c) CONCAT4(igraph_i_matrix,SHORT,list,c) + #define TYPE CONCAT3(igraph_matrix,SHORT,list_t) + #endif +#elif defined(GRAPH_LIST) + #define FUNCTION(c) CONCAT2x(igraph_graph_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_graph_list,c) + #define TYPE igraph_graph_list_t +#elif defined(BITSET_LIST) + #define FUNCTION(c) CONCAT2x(igraph_bitset_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_bitset_list,c) + #define TYPE igraph_bitset_list_t + +#else + #if defined(BASE_IGRAPH_REAL) + #define FUNCTION(a,c) CONCAT2(a,c) + #define TYPE(a) CONCAT2(a,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(a) CONCAT3x(a,bool,t) + #else + #define FUNCTION(a,c) CONCAT3(a,SHORT,c) + #define TYPE(a) CONCAT3(a,SHORT,t) + #endif +#endif + +#if defined(HEAP_TYPE_MIN) + #define HEAPMORE < + #define HEAPMOREEQ <= + #define HEAPLESS > + #define HEAPLESSEQ >= + #undef FUNCTION + #undef INTERNAL_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/include/igraph_pmt_off.h b/include/igraph_pmt_off.h new file mode 100644 index 0000000..03ef43c --- /dev/null +++ b/include/igraph_pmt_off.h @@ -0,0 +1,186 @@ +/* -*- 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 + +*/ + +#ifdef ATOMIC + #undef ATOMIC +#endif + +#ifdef ATOMIC_IO + #undef ATOMIC_IO +#endif + +#ifdef BASE + #undef BASE +#endif + +#ifdef BASE_EPSILON + #undef BASE_EPSILON +#endif + +#ifdef BASE_MATRIX + #undef BASE_MATRIX +#endif + +#ifdef BASE_VECTOR + #undef BASE_VECTOR +#endif + +#ifdef CONCAT2 + #undef CONCAT2 +#endif + +#ifdef CONCAT2x + #undef CONCAT2x +#endif + +#ifdef CONCAT3 + #undef CONCAT3 +#endif + +#ifdef CONCAT3x + #undef CONCAT3x +#endif + +#ifdef CONCAT4 + #undef CONCAT4 +#endif + +#ifdef CONCAT4x + #undef CONCAT4x +#endif + +#ifdef CONCAT5 + #undef CONCAT5 +#endif + +#ifdef CONCAT5x + #undef CONCAT5x +#endif + +#ifdef FP + #undef FP +#endif + +#ifdef FUNCTION + #undef FUNCTION +#endif + +#ifdef IN_FORMAT + #undef IN_FORMAT +#endif + +#ifdef INTERNAL_FUNCTION + #undef INTERNAL_FUNCTION +#endif + +#ifdef MULTIPLICITY + #undef MULTIPLICITY +#endif + +#ifdef ONE + #undef ONE +#endif + +#ifdef OUT_FORMAT + #undef OUT_FORMAT +#endif + +#ifdef SHORT + #undef SHORT +#endif + +#ifdef TYPE + #undef TYPE +#endif + +#ifdef ZERO + #undef ZERO +#endif + +#ifdef HEAPMORE + #undef HEAPMORE +#endif + +#ifdef HEAPLESS + #undef HEAPLESS +#endif + +#ifdef HEAPMOREEQ + #undef HEAPMOREEQ +#endif + +#ifdef HEAPLESSEQ + #undef HEAPLESSEQ +#endif + +#ifdef SUM + #undef SUM +#endif + +#ifdef SQ + #undef SQ +#endif + +#ifdef PROD + #undef PROD +#endif + +#ifdef NOTORDERED + #undef NOTORDERED +#endif + +#ifdef EQ + #undef EQ +#endif + +#ifdef DIFF + #undef DIFF +#endif + +#ifdef DIV + #undef DIV +#endif + +#ifdef NOABS + #undef NOABS +#endif + +#ifdef PRINTFUNC + #undef PRINTFUNC +#endif + +#ifdef SNPRINTFUNC + #undef SNPRINTFUNC +#endif + +#ifdef FPRINTFUNC_ALIGNED + #undef FPRINTFUNC_ALIGNED +#endif + +#ifdef FPRINTFUNC + #undef FPRINTFUNC +#endif + +#ifdef UNSIGNED + #undef UNSIGNED +#endif diff --git a/include/igraph_progress.h b/include/igraph_progress.h new file mode 100644 index 0000000..f85139a --- /dev/null +++ b/include/igraph_progress.h @@ -0,0 +1,184 @@ +/* -*- 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_PROGRESS_H +#define IGRAPH_PROGRESS_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" + +__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. + * + * + * + * To see the progress of a computation, the user has to install a + * progress handler, as there is none installed by default. + * If an igraph function supports progress reporting, then it + * calls the installed progress handler periodically, and passes a + * percentage value to it, the percentage of computation already + * performed. To install a progress handler, you need to call + * \ref igraph_set_progress_handler(). Currently there is a single + * pre-defined progress handler, called \ref + * igraph_progress_handler_stderr(). + * + */ + +/** + * \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() + * function. + * + * + * + * One can assume that the first progress handler call from a + * calculation will be call with zero as the \p percentage argument, + * and the last call from a function will have 100 as the \p + * percentage argument. Note, however, that if an error happens in the + * middle of a computation, then the 100 percent call might be + * omitted. + * + */ + +/** + * \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 + * 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 + * 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 + * \ref igraph_progress() calls. + * + */ + +/** + * \section progress_and_threads Multi-threaded programs + * + * + * In multi-threaded programs, each thread has its own progress + * handler, if thread-local storage is supported and igraph is + * thread-safe. See the \ref IGRAPH_THREAD_SAFE macro for checking + * whether an igraph build is thread-safe. + * + */ + +/* -------------------------------------------------- */ +/* Progress handlers */ +/* -------------------------------------------------- */ + +/** + * \typedef igraph_progress_handler_t + * \brief Type of progress handler functions + * + * This is the type of the igraph progress handler functions. + * There is currently one such predefined function, + * \ref igraph_progress_handler_stderr(), but the user can + * write and set up more sophisticated ones. + * \param message A string describing the function or algorithm + * that is reporting the progress. Current igraph functions + * always use the name \p message argument if reporting from the + * same function. + * \param percent Numeric, the percentage that was completed by the + * algorithm or function. + * \param data User-defined data. Current igraph functions that + * report progress pass a null pointer here. Users can + * write their own progress handlers and functions with progress + * reporting, and then pass some meaningfull context here. + * \return If the return value of the progress handler is not + * \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 igraph_error_t igraph_progress_handler_t(const char *message, igraph_real_t percent, + void *data); + +IGRAPH_EXPORT extern igraph_progress_handler_t igraph_progress_handler_stderr; + +IGRAPH_EXPORT igraph_progress_handler_t * igraph_set_progress_handler(igraph_progress_handler_t new_handler); + +IGRAPH_EXPORT igraph_error_t igraph_progress(const char *message, igraph_real_t percent, void *data); + +IGRAPH_EXPORT igraph_error_t igraph_progressf(const char *message, igraph_real_t percent, void *data, + ...); + +/** + * \define IGRAPH_PROGRESS + * \brief Report progress. + * + * The standard way to report progress from an igraph function + * \param message A string, a textual message that references the + * calculation under progress. + * \param percent Numeric scalar, the percentage that is complete. + * \param data User-defined data, this can be used in user-defined + * progress handler functions, from user-written igraph functions. + * \return If the progress handler returns with \c IGRAPH_INTERRUPTED, + * then this macro frees up the igraph allocated memory for + * temporary data and returns to the caller with \c + * IGRAPH_INTERRUPTED. + */ + +#define IGRAPH_PROGRESS(message, percent, data) \ + do { \ + if (igraph_progress((message), (percent), (data)) != IGRAPH_SUCCESS) { \ + IGRAPH_FINALLY_FREE(); \ + return IGRAPH_INTERRUPTED; \ + } \ + } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_psumtree.h b/include/igraph_psumtree.h new file mode 100644 index 0000000..2588680 --- /dev/null +++ b/include/igraph_psumtree.h @@ -0,0 +1,52 @@ +/* -*- 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_PSUMTREE_H +#define IGRAPH_PSUMTREE_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +typedef struct { + igraph_vector_t v; + igraph_integer_t size; + igraph_integer_t offset; +} igraph_psumtree_t; + +IGRAPH_EXPORT igraph_error_t igraph_psumtree_init(igraph_psumtree_t *t, igraph_integer_t size); +IGRAPH_EXPORT void igraph_psumtree_reset(igraph_psumtree_t *t); +IGRAPH_EXPORT void igraph_psumtree_destroy(igraph_psumtree_t *t); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, igraph_integer_t idx); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_psumtree_size(const igraph_psumtree_t *t); +IGRAPH_EXPORT igraph_error_t igraph_psumtree_search(const igraph_psumtree_t *t, igraph_integer_t *idx, + igraph_real_t elem); +IGRAPH_EXPORT igraph_error_t igraph_psumtree_update(igraph_psumtree_t *t, igraph_integer_t idx, + igraph_real_t new_value); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t); + +__END_DECLS + +#endif diff --git a/include/igraph_qsort.h b/include/igraph_qsort.h new file mode 100644 index 0000000..94e5e7f --- /dev/null +++ b/include/igraph_qsort.h @@ -0,0 +1,40 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#ifndef IGRAPH_QSORT_H +#define IGRAPH_QSORT_H + +#include "igraph_decls.h" + +#include + +__BEGIN_DECLS + +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 + +#endif diff --git a/include/igraph_random.h b/include/igraph_random.h new file mode 100644 index 0000000..2b2a80a --- /dev/null +++ b/include/igraph_random.h @@ -0,0 +1,190 @@ +/* -*- 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" +#include "igraph_types.h" +#include "igraph_vector.h" + +#include +#include +#include + +__BEGIN_DECLS + +/* The new RNG interface is (somewhat) modelled on the GSL */ + +/* When implementing your own RNG in igraph, the following methods must be + * supplied in the corresponding igraph_rng_type_t structure: + * + * - init() + * - destroy() + * - seed() + * - get() + * + * Optionally, you can provide specialized routines for several distributions + * in the following functions: + * + * - get_int() + * - get_real() + * - get_norm() + * - get_geom() + * - get_binom() + * - get_exp() + * - get_gamma() + * - get_pois() + * + * The best is probably to define get() leave the others as NULL; igraph will use + * default implementations for these. + * + * Note that if all that you would do in a get_real() implementation is to + * generate random bits with get() and divide by the maximum, don't do that; + * The default implementation takes care of calling get() a sufficient number of + * times to utilize most of the precision of the igraph_real_t type, and generate + * accurate variates. Inaccuracies in the output of get_real() can get magnified + * when using the default generators for non-uniform distributions. + * When implementing get_real(), the sampling range must be half-open, i.e. [0, 1). + * If unsure, leave get_real() unimplemented and igraph will provide an implementation + * in terms of get(). + * + * When implementing get_int(), you do not need to check whether lo < hi; + * the caller is responsible for ensuring that this is the case. You can always + * assume that hi > lo. Note that both endpoints are _inclusive_, and you must + * make sure that your generation scheme works for both 32-bit and 64-bit + * versions of igraph_integer_t as igraph can be compiled for both cases. If + * you are unsure, leave get_int() unimplemented and igraph will provide its + * own implementation based on get(). + */ +typedef struct igraph_rng_type_t { + const char *name; + uint8_t bits; + + /* Initialization and destruction */ + igraph_error_t (*init)(void **state); + void (*destroy)(void *state); + + /* Seeding */ + igraph_error_t (*seed)(void *state, igraph_uint_t seed); + + /* Fundamental generator: return as many random bits as the RNG supports in + * a single round */ + igraph_uint_t (*get)(void *state); + + /* Optional generators; defaults are provided by igraph that rely solely + * on get() */ + igraph_integer_t (*get_int)(void *state, igraph_integer_t l, igraph_integer_t h); + 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, igraph_integer_t 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_real_t (*get_pois)(void *state, igraph_real_t mu); +} igraph_rng_type_t; + +typedef struct igraph_rng_t { + const igraph_rng_type_t *type; + void *state; + igraph_bool_t is_seeded; +} igraph_rng_t; + +/* --------------------------------- */ + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_rng_seed(igraph_rng_t *rng, igraph_uint_t seed); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_uint_t igraph_rng_max(const igraph_rng_t *rng); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE const char *igraph_rng_name(const igraph_rng_t *rng); + +IGRAPH_EXPORT igraph_integer_t igraph_rng_get_integer( + igraph_rng_t *rng, igraph_integer_t l, igraph_integer_t 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, igraph_integer_t n, igraph_real_t p +); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_gamma( + igraph_rng_t *rng, igraph_real_t shape, igraph_real_t scale +); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_pois(igraph_rng_t *rng, igraph_real_t rate); +IGRAPH_EXPORT igraph_error_t 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_mt19937; +IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_pcg32; +IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_pcg64; + +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() + +#else + +#define RNG_BEGIN() \ + do { if (!igraph_rng_default()->is_seeded) { \ + igraph_rng_seed(igraph_rng_default(), time(0)); \ + igraph_rng_default()->is_seeded = true; \ + } } while (0) +#define RNG_END() \ + do { /* nothing */ } while (0) +#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_EXP(rate) (igraph_rng_get_exp(igraph_rng_default(),(rate))) +#define RNG_POIS(rate) (igraph_rng_get_pois(igraph_rng_default(),(rate))) +#define RNG_GAMMA(shape, scale) \ + (igraph_rng_get_gamma(igraph_rng_default(), (shape), (scale))) + +__END_DECLS + +#endif diff --git a/include/igraph_reachability.h b/include/igraph_reachability.h new file mode 100644 index 0000000..472655e --- /dev/null +++ b/include/igraph_reachability.h @@ -0,0 +1,49 @@ +/* + IGraph library. + Copyright (C) 2024 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_REACHABILITY_H +#define IGRAPH_REACHABILITY_H + +#include "igraph_bitset_list.h" +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_reachability( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize, + igraph_integer_t *no_of_components, + igraph_bitset_list_t *reach, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_count_reachable( + const igraph_t *graph, + igraph_vector_int_t *counts, + igraph_neimode_t mode); + + +IGRAPH_EXPORT igraph_error_t igraph_transitive_closure(const igraph_t *graph, + igraph_t* closure); + +__END_DECLS + +#endif // IGRAPH_REACHABILITY_H diff --git a/include/igraph_scan.h b/include/igraph_scan.h new file mode 100644 index 0000000..bfc47c4 --- /dev/null +++ b/include/igraph_scan.h @@ -0,0 +1,72 @@ +/* -*- 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_constants.h" +#include "igraph_error.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *weights, igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t 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_EXPORT igraph_error_t igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_t k, + igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, + igraph_integer_t k, igraph_vector_t *res, + const igraph_vector_t *weights_them, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_local_scan_neighborhood_ecount(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + const igraph_vector_int_list_t *neighborhoods); +IGRAPH_EXPORT igraph_error_t igraph_local_scan_subset_ecount(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + const igraph_vector_int_list_t *neighborhoods); +__END_DECLS + +#endif diff --git a/include/igraph_separators.h b/include/igraph_separators.h new file mode 100644 index 0000000..7008d92 --- /dev/null +++ b/include/igraph_separators.h @@ -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 + +*/ + +#ifndef IGRAPH_SEPARATORS_H +#define IGRAPH_SEPARATORS_H + +#include "igraph_decls.h" + +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_iterators.h" +#include "igraph_types.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_is_separator(const igraph_t *graph, + const igraph_vs_t candidate, + igraph_bool_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_all_minimal_st_separators(const igraph_t *graph, + igraph_vector_int_list_t *separators); + +IGRAPH_EXPORT igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, + const igraph_vs_t candidate, + igraph_bool_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_minimum_size_separators(const igraph_t *graph, + igraph_vector_int_list_t *separators); + +__END_DECLS + +#endif diff --git a/include/igraph_sparsemat.h b/include/igraph_sparsemat.h new file mode 100644 index 0000000..50ba936 --- /dev/null +++ b/include/igraph_sparsemat.h @@ -0,0 +1,312 @@ +/* -*- 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_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_datatype.h" +#include "igraph_arpack.h" + +#include + +__BEGIN_DECLS + +/* + * These types are private to igraph, and customized to use igraph_integer_t. + * Do not attempt to access them using a separate copy of the CXSparse library. + * Use the public igraph_sparsemat_... types instead. + */ +struct cs_igraph_sparse; +struct cs_igraph_symbolic; +struct cs_igraph_numeric; + +typedef struct { + struct cs_igraph_sparse *cs; +} igraph_sparsemat_t; + +typedef struct { + struct cs_igraph_symbolic *symbolic; +} igraph_sparsemat_symbolic_t; + +typedef struct { + struct cs_igraph_numeric *numeric; +} igraph_sparsemat_numeric_t; + +typedef enum { IGRAPH_SPARSEMAT_TRIPLET, + IGRAPH_SPARSEMAT_CC + } igraph_sparsemat_type_t; + +typedef struct { + const igraph_sparsemat_t *mat; + igraph_integer_t pos; + igraph_integer_t col; +} igraph_sparsemat_iterator_t; + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init( + igraph_sparsemat_t *A, igraph_integer_t rows, igraph_integer_t cols, + igraph_integer_t nzmax +); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_copy( + igraph_sparsemat_t *to, const igraph_sparsemat_t *from); +IGRAPH_EXPORT void igraph_sparsemat_destroy(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_realloc(igraph_sparsemat_t *A, igraph_integer_t nzmax); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_eye(igraph_sparsemat_t *A, + igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value, igraph_bool_t compress); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_diag(igraph_sparsemat_t *A, + igraph_integer_t nzmax, const igraph_vector_t *values, + igraph_bool_t compress); + +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nrow(const igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_integer_t 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 igraph_error_t 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 igraph_real_t igraph_sparsemat_get( + const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col +); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_sparsemat_entry(igraph_sparsemat_t *A, + igraph_integer_t row, igraph_integer_t col, igraph_real_t elem); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_compress(const igraph_sparsemat_t *A, + igraph_sparsemat_t *res); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_transpose( + const igraph_sparsemat_t *A, igraph_sparsemat_t *res +); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A, igraph_bool_t *result); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_dupl(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_sparsemat_dropzeros(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_multiply(const igraph_sparsemat_t *A, + const igraph_sparsemat_t *B, + igraph_sparsemat_t *res); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, + const igraph_vector_t *x, + igraph_vector_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_lsolve(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_ltsolve(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_usolve(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_utsolve(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res, + igraph_integer_t order); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_lusol(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res, + igraph_integer_t order, + igraph_real_t tol); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_print(const igraph_sparsemat_t *A, + FILE *outstream); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed); + +IGRAPH_EXPORT igraph_error_t igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed, const char *attr, + igraph_bool_t loops); + +IGRAPH_EXPORT igraph_error_t igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, + const igraph_matrix_t *mat, + igraph_real_t tol); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_sparsemat_lu(const igraph_sparsemat_t *A, + const igraph_sparsemat_symbolic_t *dis, + igraph_sparsemat_numeric_t *din, double tol); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_qr(const igraph_sparsemat_t *A, + const igraph_sparsemat_symbolic_t *dis, + igraph_sparsemat_numeric_t *din); + +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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 igraph_error_t igraph_sparsemat_symbqr(igraph_integer_t order, const igraph_sparsemat_t *A, + igraph_sparsemat_symbolic_t *dis); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_symblu(igraph_integer_t 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 igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A, + igraph_real_t *min, igraph_real_t *max); + +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, + igraph_real_t tol); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, + igraph_vector_t *res); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colsums(const igraph_sparsemat_t *A, + igraph_vector_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowmins(igraph_sparsemat_t *A, + igraph_vector_t *res); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colmins(igraph_sparsemat_t *A, + igraph_vector_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, + igraph_vector_t *res); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, + igraph_vector_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, + igraph_vector_t *res, + igraph_vector_int_t *pos); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by); + + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_add_rows(igraph_sparsemat_t *A, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_resize(igraph_sparsemat_t *A, + igraph_integer_t nrow, igraph_integer_t ncol, igraph_integer_t nzmax); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_getelements(const igraph_sparsemat_t *A, + igraph_vector_int_t *i, + igraph_vector_int_t *j, + igraph_vector_t *x); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, + const igraph_vector_t *fact); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, + const igraph_vector_t *fact); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, + const igraph_matrix_t *B, + igraph_matrix_t *res); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, + const igraph_sparsemat_t *B, + igraph_matrix_t *res); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_view(igraph_sparsemat_t *A, igraph_integer_t nzmax, igraph_integer_t m, igraph_integer_t n, + igraph_integer_t *p, igraph_integer_t *i, igraph_real_t *x, igraph_integer_t nz); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_sort(const igraph_sparsemat_t *A, + igraph_sparsemat_t *sorted); + +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nzmax(const igraph_sparsemat_t *A); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_neg(igraph_sparsemat_t *A); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_normalize_cols(igraph_sparsemat_t *sparsemat, + igraph_bool_t allow_zeros); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_normalize_rows(igraph_sparsemat_t *sparsemat, + igraph_bool_t allow_zeros); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_iterator_init( + igraph_sparsemat_iterator_t *it, const igraph_sparsemat_t *sparsemat +); +IGRAPH_EXPORT igraph_error_t 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 igraph_integer_t igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_integer_t 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 igraph_integer_t igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_copy( + igraph_sparsemat_t *to, const igraph_sparsemat_t *from); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_diag( + igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, + igraph_bool_t compress); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_eye( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value, igraph_bool_t compress); + +__END_DECLS + +#endif diff --git a/include/igraph_stack.h b/include/igraph_stack.h new file mode 100644 index 0000000..30f9b44 --- /dev/null +++ b/include/igraph_stack.h @@ -0,0 +1,71 @@ +/* -*- 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_STACK_H +#define IGRAPH_STACK_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Plain stack */ +/* -------------------------------------------------- */ + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "igraph_stack_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#include "igraph_pmt.h" +#include "igraph_stack_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#define BASE_CHAR +#include "igraph_pmt.h" +#include "igraph_stack_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_CHAR + +#define BASE_BOOL +#include "igraph_pmt.h" +#include "igraph_stack_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_BOOL + +#define IGRAPH_STACK_NULL { 0,0,0 } +#define IGRAPH_STACK_INIT_FINALLY(s, capacity) \ + do { IGRAPH_CHECK(igraph_stack_init(s, capacity)); \ + IGRAPH_FINALLY(igraph_stack_destroy, s); } while (0) +#define IGRAPH_STACK_INT_INIT_FINALLY(s, capacity) \ + do { IGRAPH_CHECK(igraph_stack_int_init(s, capacity)); \ + IGRAPH_FINALLY(igraph_stack_int_destroy, s); } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_stack_pmt.h b/include/igraph_stack_pmt.h new file mode 100644 index 0000000..d0a5a25 --- /dev/null +++ b/include/igraph_stack_pmt.h @@ -0,0 +1,48 @@ +/* -*- 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 + +/** + * Stack data type. + * \ingroup internal + */ + +typedef struct TYPE(igraph_stack) { + BASE* stor_begin; + BASE* stor_end; + BASE* end; +} TYPE(igraph_stack); + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, igraph_integer_t capacity); +IGRAPH_EXPORT void FUNCTION(igraph_stack, destroy)(TYPE(igraph_stack)* s); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, igraph_integer_t capacity); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_stack, empty)(TYPE(igraph_stack)* s); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_stack, capacity)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT void FUNCTION(igraph_stack, clear)(TYPE(igraph_stack)* s); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem); +IGRAPH_EXPORT BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_stack, top)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, print)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack)* s, FILE *file); diff --git a/include/igraph_statusbar.h b/include/igraph_statusbar.h new file mode 100644 index 0000000..d3bc899 --- /dev/null +++ b/include/igraph_statusbar.h @@ -0,0 +1,129 @@ +/* -*- 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_STATUSBAR_H +#define IGRAPH_STATUSBAR_H + +#include "igraph_decls.h" +#include "igraph_error.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, + * 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 + * 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 + * \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. + * Existing igraph functions pass a null pointer here. + * \return Error code. The current calculation will abort if you return anything + * else than \c IGRAPH_SUCCESS here. + */ + +typedef igraph_error_t igraph_status_handler_t(const char *message, void *data); + +IGRAPH_EXPORT extern igraph_status_handler_t igraph_status_handler_stderr; + +IGRAPH_EXPORT igraph_status_handler_t *igraph_set_status_handler(igraph_status_handler_t new_handler); + +IGRAPH_EXPORT igraph_error_t igraph_status(const char *message, void *data); + +/** + * \define IGRAPH_STATUS + * Report the status of an igraph function. + * + * Typically this function is called only a handful of times from + * an igraph function. E.g. if an algorithm has three major + * steps, then it is logical to call it three times, to + * signal the three major steps. + * \param message The status message. + * \param data Additional context, with user-defined semantics. + * Existing igraph functions pass a null pointer here. + * \return If the status handler returns with a value other than + * \c IGRAPH_SUCCESS, then the function that called this + * macro returns as well, with error code + * \c IGRAPH_INTERRUPTED. + */ + +#define IGRAPH_STATUS(message, data) \ + do { \ + if (igraph_status((message), (data)) != IGRAPH_SUCCESS) { \ + IGRAPH_FINALLY_FREE(); \ + return IGRAPH_INTERRUPTED; \ + } \ + } while (0) + +IGRAPH_EXPORT igraph_error_t igraph_statusf(const char *message, void *data, ...); + +/** + * \define IGRAPH_STATUSF + * Report the status from an igraph function + * + * This is the more flexible version of \ref IGRAPH_STATUS(), + * having a printf-like syntax. As this macro takes variable + * number of arguments, they must be all supplied as a single + * argument, enclosed in parentheses. Then \ref igraph_statusf() + * is called with the given arguments. + * \param args The arguments to pass to \ref igraph_statusf(). + * \return If the status handler returns with a value other than + * \c IGRAPH_SUCCESS, then the function that called this + * macro returns as well, with error code + * \c IGRAPH_INTERRUPTED. + */ + +#define IGRAPH_STATUSF(args) \ + do { \ + if (igraph_statusf args != IGRAPH_SUCCESS) { \ + IGRAPH_FINALLY_FREE(); \ + return IGRAPH_INTERRUPTED; \ + } \ + } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_structural.h b/include/igraph_structural.h new file mode 100644 index 0000000..e9df967 --- /dev/null +++ b/include/igraph_structural.h @@ -0,0 +1,180 @@ +/* -*- 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_datatype.h" +#include "igraph_error.h" +#include "igraph_constants.h" +#include "igraph_iterators.h" +#include "igraph_matrix.h" +#include "igraph_sparsemat.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Basic query functions */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_are_adjacent(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_count_multiple(const igraph_t *graph, igraph_vector_int_t *res, igraph_es_t es); +IGRAPH_EXPORT igraph_error_t igraph_count_multiple_1(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t eid); +IGRAPH_EXPORT igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_t *res, const igraph_vs_t vs); +IGRAPH_EXPORT igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, + igraph_vector_int_t *circle); +IGRAPH_EXPORT igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_count_loops(const igraph_t *graph, igraph_integer_t *loop_count); +IGRAPH_EXPORT igraph_error_t igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, + igraph_es_t es); +IGRAPH_EXPORT igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, + igraph_es_t es); +IGRAPH_EXPORT igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_has_mutual(const igraph_t *graph, igraph_bool_t *res, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_is_acyclic(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool_t *res, + igraph_vector_int_t *roots, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, + igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_mean_degree(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t ignore_loops, + igraph_reciprocity_t mode); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_sort_vertex_ids_by_degree(const igraph_t *graph, + igraph_vector_int_t *outvids, + igraph_vs_t vids, + igraph_neimode_t mode, + igraph_bool_t loops, + igraph_order_t order, + igraph_bool_t only_indices); +IGRAPH_EXPORT igraph_error_t igraph_is_perfect(const igraph_t *graph, igraph_bool_t *perfect); + +/* -------------------------------------------------- */ +/* Structural properties */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_is_complete(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_clique(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t directed, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_independent_vertex_set(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_int_t *res, + const igraph_vector_t *weights); +IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, + igraph_t *mst); +IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, + const igraph_vector_t *weights); +IGRAPH_EXPORT igraph_error_t igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_int_t *res, + igraph_integer_t vid); + +IGRAPH_EXPORT igraph_error_t igraph_subcomponent(const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vid, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, + igraph_neimode_t mode, const igraph_vector_int_t *roots, + igraph_vector_int_t *vertex_index); + +IGRAPH_EXPORT igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, + igraph_vector_int_t *alpha, + igraph_vector_int_t *alpham1); +IGRAPH_EXPORT igraph_error_t igraph_is_chordal(const igraph_t *graph, + const igraph_vector_int_t *alpha, + const igraph_vector_int_t *alpham1, + igraph_bool_t *chordal, + igraph_vector_int_t *fill_in, + igraph_t *newgraph); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_degree_correlation_vector( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_t *knnk, + igraph_neimode_t from_mode, igraph_neimode_t to_mode, + igraph_bool_t directed_neighbors); + +IGRAPH_EXPORT igraph_error_t igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_fas_algorithm_t algo); + +/* -------------------------------------------------- */ +/* Spectral Properties */ +/* -------------------------------------------------- */ + +/** + * \typedef igraph_laplacian_normalization_t + * \brief Normalization methods for a Laplacian matrix. + * + * Normalization methods for \ref igraph_get_laplacian() and + * \ref igraph_get_laplacian_sparse(). In the following, \c A refers to the + * (possibly weighted) adjacency matrix and \c D is a diagonal matrix containing + * degrees (unweighted case) or strengths (weighted case). Out-, in- or total degrees + * are used according to the \p mode parameter. + * + * \enumval IGRAPH_LAPLACIAN_UNNORMALIZED Unnormalized Laplacian, L = D - A. + * \enumval IGRAPH_LAPLACIAN_SYMMETRIC Symmetric normalized Laplacian, L = I - D^(-1/2) A D^(-1/2). + * \enumval IGRAPH_LAPLACIAN_LEFT Left-stochastic normalized Laplacian, L = I - D^-1 A. + * \enumval IGRAPH_LAPLACIAN_RIGHT Right-stochastic normalized Laplacian, L = I - A D^-1. + */ +typedef enum { + IGRAPH_LAPLACIAN_UNNORMALIZED = 0, + IGRAPH_LAPLACIAN_SYMMETRIC = 1, + IGRAPH_LAPLACIAN_LEFT = 2, + IGRAPH_LAPLACIAN_RIGHT = 3 +} igraph_laplacian_normalization_t; + +IGRAPH_EXPORT igraph_error_t igraph_get_laplacian( + const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, + igraph_laplacian_normalization_t normalization, + const igraph_vector_t *weights +); +IGRAPH_EXPORT igraph_error_t igraph_get_laplacian_sparse( + const igraph_t *graph, igraph_sparsemat_t *sparseres, igraph_neimode_t mode, + igraph_laplacian_normalization_t normalization, + const igraph_vector_t *weights +); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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/include/igraph_strvector.h b/include/igraph_strvector.h new file mode 100644 index 0000000..f7d2d59 --- /dev/null +++ b/include/igraph_strvector.h @@ -0,0 +1,115 @@ +/* -*- 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_error.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/** + * Vector of strings + * \ingroup internal + */ + +typedef struct s_igraph_strvector { + /* Empty strings "" are represented using NULL. */ + char **stor_begin; + char **stor_end; + char **end; +} igraph_strvector_t; + +/** + * \define STR + * \brief Indexing string vectors. + * + * This is a macro that allows to query the elements of a string vector, just + * like \ref igraph_strvector_get(). Note this macro cannot be used to set an + * element. Use \ref igraph_strvector_set() to set an element instead. + * + * \param sv The string vector + * \param i The the index of the element. + * \return The element at position \p i. + * + * Time complexity: O(1). + * + * \deprecated-by igraph_strvector_get 0.10.9 + */ +#define STR(sv,i) \ + (IGRAPH_PREPROCESSOR_WARNING("STR() is deprecated. Use igraph_strvector_get() instead.") \ + igraph_strvector_get(&sv, i)) + +#define IGRAPH_STRVECTOR_NULL { 0,0,0 } +#define IGRAPH_STRVECTOR_INIT_FINALLY(sv, size) \ + do { IGRAPH_CHECK(igraph_strvector_init(sv, size)); \ + IGRAPH_FINALLY( igraph_strvector_destroy, sv); } while (0) + +IGRAPH_EXPORT igraph_error_t igraph_strvector_init(igraph_strvector_t *sv, igraph_integer_t len); +IGRAPH_EXPORT void igraph_strvector_destroy(igraph_strvector_t *sv); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_strvector_size(const igraph_strvector_t *sv); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_strvector_capacity(const igraph_strvector_t *sv); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE const char *igraph_strvector_get(const igraph_strvector_t *sv, igraph_integer_t idx); +IGRAPH_EXPORT igraph_error_t igraph_strvector_set( + igraph_strvector_t *sv, igraph_integer_t idx, const char *value); +IGRAPH_EXPORT igraph_error_t igraph_strvector_set_len( + igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len); +IGRAPH_EXPORT void igraph_strvector_clear(igraph_strvector_t *sv); +IGRAPH_EXPORT void igraph_strvector_remove_section( + igraph_strvector_t *v, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT void igraph_strvector_remove( + igraph_strvector_t *v, igraph_integer_t elem); +IGRAPH_EXPORT igraph_error_t igraph_strvector_init_copy( + igraph_strvector_t *to, const igraph_strvector_t *from); +IGRAPH_EXPORT igraph_error_t igraph_strvector_append( + igraph_strvector_t *to, const igraph_strvector_t *from); +IGRAPH_EXPORT igraph_error_t igraph_strvector_merge( + igraph_strvector_t *to, igraph_strvector_t *from); +IGRAPH_EXPORT igraph_error_t igraph_strvector_resize( + igraph_strvector_t* v, igraph_integer_t newsize); +IGRAPH_EXPORT igraph_error_t igraph_strvector_push_back(igraph_strvector_t *v, + const char *value); +IGRAPH_EXPORT igraph_error_t igraph_strvector_push_back_len(igraph_strvector_t *v, + const char *value, igraph_integer_t len); +IGRAPH_EXPORT igraph_error_t igraph_strvector_print(const igraph_strvector_t *v, FILE *file, + const char *sep); + +IGRAPH_EXPORT igraph_error_t igraph_strvector_index(const igraph_strvector_t *v, + igraph_strvector_t *newv, + const igraph_vector_int_t *idx); + +IGRAPH_EXPORT igraph_error_t igraph_strvector_reserve(igraph_strvector_t *sv, + igraph_integer_t capacity); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_add(igraph_strvector_t *v, const char *value); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_copy( + igraph_strvector_t *to, const igraph_strvector_t *from); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_set2( + igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len +); + +__END_DECLS + +#endif diff --git a/include/igraph_threading.h.in b/include/igraph_threading.h.in new file mode 100644 index 0000000..1ddeb11 --- /dev/null +++ b/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/include/igraph_topology.h b/include/igraph_topology.h new file mode 100644 index 0000000..9ae7734 --- /dev/null +++ b/include/igraph_topology.h @@ -0,0 +1,315 @@ +/* -*- 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_error.h" +#include "igraph_types.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Directed acyclic graphs */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_topological_sorting( + const igraph_t *graph, igraph_vector_int_t *res, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_is_dag(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, + igraph_t *closure); + +/* -------------------------------------------------- */ +/* Graph isomorphisms */ +/* -------------------------------------------------- */ + +/* Common functions */ +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso); +IGRAPH_EXPORT igraph_error_t igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso); + +/* LAD */ +IGRAPH_EXPORT igraph_error_t igraph_subisomorphic_lad( + const igraph_t *pattern, const igraph_t *target, const igraph_vector_int_list_t *domains, + igraph_bool_t *iso, igraph_vector_int_t *map, igraph_vector_int_list_t *maps, + igraph_bool_t induced, igraph_integer_t 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_get_isomorphisms_vf2_callback(). + * \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_get_isomorphisms_vf2_callback() when it was called. + * \return \c IGRAPH_SUCCESS to continue the search, \c IGRAPH_STOP to + * terminate the search. Any other return value is interpreted as an + * igraph error code, which will then abort the search and return the + * same error code from the caller function. + */ + + +typedef igraph_error_t igraph_isohandler_t(const igraph_vector_int_t *map12, + const igraph_vector_int_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 igraph_error_t 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_int_t *map12, + igraph_vector_int_t *map21, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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_int_list_t *maps, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg); +IGRAPH_EXPORT igraph_error_t igraph_get_isomorphisms_vf2_callback( + 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +); + +/* Deprecated alias to igraph_get_isomorphisms_vf2_callback(), will be removed in 0.11 */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +); + +IGRAPH_EXPORT igraph_error_t 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_int_t *map12, + igraph_vector_int_t *map21, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t 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_int_list_t *maps, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg); +IGRAPH_EXPORT igraph_error_t igraph_get_subisomorphisms_vf2_callback( + 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +); + +/* Deprecated alias to igraph_get_subisomorphisms_vf2_callback(), will be removed in 0.11 */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +); + +/* BLISS family */ +/** + * \struct igraph_bliss_info_t + * \brief 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 https://users.aalto.fi/~tjunttil/bliss/ + * 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 igraph_error_t igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_int_t *labeling, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info); +IGRAPH_EXPORT igraph_error_t 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_int_t *map12, + igraph_vector_int_t *map21, + igraph_bliss_sh_t sh, + igraph_bliss_info_t *info1, igraph_bliss_info_t *info2); + +IGRAPH_EXPORT igraph_error_t igraph_count_automorphisms( + const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_automorphisms( + const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info); + +IGRAPH_EXPORT igraph_error_t igraph_automorphism_group( + const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_vector_int_list_t *generators, igraph_bliss_sh_t sh, + igraph_bliss_info_t *info +); + +/* Functions for small graphs (<= 4 vertices for directed graphs, <= 6 for undirected graphs) */ +IGRAPH_EXPORT igraph_error_t igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass); +IGRAPH_EXPORT igraph_error_t igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_int_t *vids, + igraph_integer_t *isoclass); +IGRAPH_EXPORT igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, + igraph_integer_t number, igraph_bool_t directed); + +IGRAPH_EXPORT igraph_error_t igraph_graph_count(igraph_integer_t n, igraph_bool_t directed, igraph_integer_t *count); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_isomorphic_34( + const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso +); + + + +__END_DECLS + +#endif diff --git a/include/igraph_transitivity.h b/include/igraph_transitivity.h new file mode 100644 index 0000000..9566561 --- /dev/null +++ b/include/igraph_transitivity.h @@ -0,0 +1,59 @@ +/* -*- 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_error.h" +#include "igraph_iterators.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, + igraph_real_t *res, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_transitivity_local_undirected(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_transitivity_avglocal_undirected(const igraph_t *graph, + igraph_real_t *res, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT igraph_error_t 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); +IGRAPH_EXPORT igraph_error_t igraph_ecc(const igraph_t *graph, + igraph_vector_t *res, + igraph_es_t eids, + igraph_integer_t k, + igraph_bool_t offset, + igraph_bool_t normalize); + +__END_DECLS + +#endif diff --git a/include/igraph_typed_list_pmt.h b/include/igraph_typed_list_pmt.h new file mode 100644 index 0000000..2110d55 --- /dev/null +++ b/include/igraph_typed_list_pmt.h @@ -0,0 +1,119 @@ +/* -*- 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 + +*/ + +#if defined(VECTOR_LIST) + /* It was indicated that every item in a list is a vector of the base type + * so let's define ITEM_TYPE appropriately */ + #define ITEM_TYPE BASE_VECTOR +#elif defined(MATRIX_LIST) + /* It was indicated that every item in a list is a matrix of the base type + * so let's define ITEM_TYPE appropriately */ + #define ITEM_TYPE BASE_MATRIX +#else + #define ITEM_TYPE BASE +#endif + +/** + * Vector list, dealing with lists of typed vectors efficiently. + * \ingroup types + */ + +typedef struct { + ITEM_TYPE* stor_begin; + ITEM_TYPE* stor_end; + ITEM_TYPE* end; +#ifdef EXTRA_TYPE_FIELDS + EXTRA_TYPE_FIELDS +#endif +} TYPE; + +/*--------------------*/ +/* Allocation */ +/*--------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(init)(TYPE* v, igraph_integer_t size); +IGRAPH_EXPORT void FUNCTION(destroy)(TYPE* v); + +/*--------------------*/ +/* Accessing elements */ +/*--------------------*/ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE ITEM_TYPE* FUNCTION(get_ptr)(const TYPE* v, igraph_integer_t pos); +IGRAPH_EXPORT void FUNCTION(set)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE ITEM_TYPE* FUNCTION(tail_ptr)(const TYPE *v); + +/*-----------------*/ +/* List properties */ +/*-----------------*/ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(capacity)(const TYPE* v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(empty)(const TYPE* v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(size)(const TYPE* v); + +/*------------------------*/ +/* Resizing operations */ +/*------------------------*/ + +IGRAPH_EXPORT void FUNCTION(clear)(TYPE* v); +IGRAPH_EXPORT igraph_error_t FUNCTION(reserve)(TYPE* v, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_error_t FUNCTION(resize)(TYPE* v, igraph_integer_t new_size); + +/*------------------------*/ +/* Adding/removing items */ +/*------------------------*/ + +IGRAPH_EXPORT void FUNCTION(discard)(TYPE* v, igraph_integer_t index); +IGRAPH_EXPORT void FUNCTION(discard_back)(TYPE* v); +IGRAPH_EXPORT void FUNCTION(discard_fast)(TYPE* v, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(insert)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(insert_copy)(TYPE* v, igraph_integer_t pos, const ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(insert_new)(TYPE* v, igraph_integer_t pos, ITEM_TYPE** result); +IGRAPH_EXPORT igraph_error_t FUNCTION(push_back)(TYPE* v, ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(push_back_copy)(TYPE* v, const ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(push_back_new)(TYPE* v, ITEM_TYPE** result); +IGRAPH_EXPORT ITEM_TYPE FUNCTION(pop_back)(TYPE* v); +IGRAPH_EXPORT igraph_error_t FUNCTION(remove)(TYPE* v, igraph_integer_t index, ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(remove_fast)(TYPE* v, igraph_integer_t index, ITEM_TYPE* e); +IGRAPH_EXPORT void FUNCTION(replace)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); +IGRAPH_EXPORT void FUNCTION(remove_consecutive_duplicates)(TYPE *v, igraph_bool_t (*eq)(const ITEM_TYPE*, const ITEM_TYPE*)); + +/*------------------*/ +/* Exchanging items */ +/*------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(permute)(TYPE *v, const igraph_vector_int_t *index); +IGRAPH_EXPORT igraph_error_t FUNCTION(reverse)(TYPE *v); +IGRAPH_EXPORT igraph_error_t FUNCTION(swap)(TYPE *v1, TYPE *v2); +IGRAPH_EXPORT igraph_error_t FUNCTION(swap_elements)(TYPE* v, igraph_integer_t i, igraph_integer_t j); + +/*-----------*/ +/* Sorting */ +/*-----------*/ + +IGRAPH_EXPORT void FUNCTION(sort)( + TYPE *v, int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*)); +IGRAPH_EXPORT igraph_error_t FUNCTION(sort_ind)( + TYPE *v, igraph_vector_int_t *ind, + int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) +); + +#undef ITEM_TYPE diff --git a/include/igraph_types.h b/include/igraph_types.h new file mode 100644 index 0000000..721d448 --- /dev/null +++ b/include/igraph_types.h @@ -0,0 +1,154 @@ +/* -*- 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_TYPES_H +#define IGRAPH_TYPES_H + +#include "igraph_decls.h" + +__BEGIN_DECLS + +#ifdef __cplusplus + #define __STDC_FORMAT_MACROS /* needed for PRId32 and PRId64 from inttypes.h on Linux */ +#endif + +#include "igraph_config.h" + +#include +#include +#include +#include +#include +#include + + +#if !defined(IGRAPH_INTEGER_SIZE) +# error "igraph integer size not defined; check the value of IGRAPH_INTEGER_SIZE when compiling" +#elif IGRAPH_INTEGER_SIZE == 64 +typedef int64_t igraph_integer_t; +typedef uint64_t igraph_uint_t; +#elif IGRAPH_INTEGER_SIZE == 32 +typedef int32_t igraph_integer_t; +typedef uint32_t igraph_uint_t; +#else +# error "Invalid igraph integer size; check the value of IGRAPH_INTEGER_SIZE when compiling" +#endif + +typedef double igraph_real_t; + +/* IGRAPH_BOOL_TYPE is set to 'bool' by default, and it is not meant to be + * overridden, except for the R interface where we know what we are doing. + * See igraph_config.h for more info */ +typedef IGRAPH_BOOL_TYPE igraph_bool_t; + +/* printf format specifier for igraph_integer_t */ +#if IGRAPH_INTEGER_SIZE == 64 +# define IGRAPH_PRId PRId64 +# define IGRAPH_PRIu PRIu64 +#else +# define IGRAPH_PRId PRId32 +# define IGRAPH_PRIu PRIu32 +#endif + +/* maximum and minimum allowed values for igraph_integer_t */ +#if IGRAPH_INTEGER_SIZE == 64 +# define IGRAPH_INTEGER_MAX INT64_MAX +# define IGRAPH_INTEGER_MIN INT64_MIN +#else +# define IGRAPH_INTEGER_MAX INT32_MAX +# define IGRAPH_INTEGER_MIN INT32_MIN +#endif + +/* maximum and minimum allowed values for igraph_uint_t */ +#if IGRAPH_INTEGER_SIZE == 64 +# define IGRAPH_UINT_MAX UINT64_MAX +# define IGRAPH_UINT_MIN UINT64_MIN +#else +# define IGRAPH_UINT_MAX UINT32_MAX +# define IGRAPH_UINT_MIN UINT32_MIN +#endif + + +/** + * \define IGRAPH_VCOUNT_MAX + * \brief The maximum number of vertices supported in igraph graphs. + * + * The value of this constant is one less than \c IGRAPH_INTEGER_MAX . + * When igraph is compiled in 32-bit mode, this means that you are limited + * to 231 – 2 (about 2.1 billion) vertices. In + * 64-bit mode, the limit is 263 – 2 so you are much + * more likely to hit out-of-memory issues due to other reasons before reaching + * this limit. + */ +#define IGRAPH_VCOUNT_MAX (IGRAPH_INTEGER_MAX-1) +/* The 'os' and 'is' vectors in igraph_t have vcount+1 elements, + * thus this cannot currently be larger than IGRAPH_INTEGER_MAX-1 + */ + +/** + * \define IGRAPH_ECOUNT_MAX + * \brief The maximum number of edges supported in igraph graphs. + * + * The value of this constant is half of \c IGRAPH_INTEGER_MAX . + * When igraph is compiled in 32-bit mode, this means that you are limited + * to approximately 230 (about 1.07 billion) + * vertices. In 64-bit mode, the limit is approximately + * 262 so you are much more likely to hit + * out-of-memory issues due to other reasons before reaching this limit. + */ +#define IGRAPH_ECOUNT_MAX (IGRAPH_INTEGER_MAX/2) +/* The endpoints of edges are often stored in a vector twice the length + * of the edge count, thus this cannot be larger than IGRAPH_INTEGER_MAX/2. + * Some of the overflow checking code relies on this. */ + +/* Replacements for printf that print doubles in the same way on all platforms + * (even for NaN and infinities) */ +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_printf_aligned(int width, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_fprintf_aligned(FILE *file, int width, 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 */ +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 ((double)INFINITY) +#define IGRAPH_POSINFINITY IGRAPH_INFINITY +#define IGRAPH_NEGINFINITY (-IGRAPH_INFINITY) + +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_finite(double x); +#define IGRAPH_FINITE(x) igraph_finite(x) + +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_nan(double x); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_inf(double x); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_posinf(double x); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_neginf(double x); + +#define IGRAPH_NAN ((double)NAN) + +__END_DECLS + +#endif diff --git a/include/igraph_vector.h b/include/igraph_vector.h new file mode 100644 index 0000000..24e8652 --- /dev/null +++ b/include/igraph_vector.h @@ -0,0 +1,165 @@ +/* -*- 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_complex.h" +#include "igraph_constants.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.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_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_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 + +/* -------------------------------------------------- */ +/* Type-specific vector functions */ +/* -------------------------------------------------- */ + +IGRAPH_EXPORT igraph_error_t igraph_vector_floor(const igraph_vector_t *from, igraph_vector_int_t *to); +IGRAPH_EXPORT igraph_error_t igraph_vector_round(const igraph_vector_t *from, igraph_vector_int_t *to); + +IGRAPH_DEPRECATED 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 IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_vector_all_almost_e(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t eps); + +IGRAPH_EXPORT igraph_error_t igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_zapsmall(igraph_vector_complex_t *v, igraph_real_t tol) ; + +IGRAPH_EXPORT igraph_error_t igraph_vector_is_nan(const igraph_vector_t *v, + igraph_vector_bool_t *is_nan); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_vector_is_all_finite(const igraph_vector_t *v); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_order2(igraph_vector_t *v); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_rank(const igraph_vector_t *v, igraph_vector_int_t *res, + igraph_integer_t nodes); + +IGRAPH_EXPORT igraph_error_t igraph_vector_int_pair_order(const igraph_vector_int_t* v, const igraph_vector_int_t *v2, + igraph_vector_int_t* res, igraph_integer_t maxval); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_int_order1(const igraph_vector_int_t* v, + igraph_vector_int_t* res, igraph_integer_t maxval); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_int_rank(const igraph_vector_int_t *v, igraph_vector_int_t *res, + igraph_integer_t nodes); + +__END_DECLS + +#endif diff --git a/include/igraph_vector_list.h b/include/igraph_vector_list.h new file mode 100644 index 0000000..f1d299d --- /dev/null +++ b/include/igraph_vector_list.h @@ -0,0 +1,73 @@ +/* -*- 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_VECTOR_LIST_H +#define IGRAPH_VECTOR_LIST_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Flexible list of vectors */ +/* -------------------------------------------------- */ + +/* Indicate to igraph_typed_list_pmt.h that we are going to work with _vectors_ + * of the base type, not the base type directly */ +#define VECTOR_LIST + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#undef VECTOR_LIST + +/* -------------------------------------------------- */ +/* Helper macros */ +/* -------------------------------------------------- */ + +#define IGRAPH_VECTOR_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_list_destroy, v); } while (0) +#define IGRAPH_VECTOR_BOOL_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_bool_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_bool_list_destroy, v); } while (0) +#define IGRAPH_VECTOR_CHAR_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_char_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_char_list_destroy, v); } while (0) +#define IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_int_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_int_list_destroy, v); } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_vector_pmt.h b/include/igraph_vector_pmt.h new file mode 100644 index 0000000..c303a78 --- /dev/null +++ b/include/igraph_vector_pmt.h @@ -0,0 +1,320 @@ +/* -*- 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 igraph_error_t FUNCTION(igraph_vector, init)( + TYPE(igraph_vector)* v, igraph_integer_t size); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_array)( + TYPE(igraph_vector)* v, const BASE* data, igraph_integer_t length); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_copy)( + TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); + +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_range)(TYPE(igraph_vector)*v, BASE start, BASE end); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to); +#endif + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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 IGRAPH_FUNCATTR_PURE igraph_integer_t 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. + * + * \param v The vector object. + * + * Time complexity: O(1). + */ +#define VECTOR(v) ((v).stor_begin) +#endif + +IGRAPH_EXPORT IGRAPH_DEPRECATED BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, igraph_integer_t pos); +IGRAPH_EXPORT IGRAPH_DEPRECATED BASE* FUNCTION(igraph_vector, e_ptr)(const TYPE(igraph_vector)* v, igraph_integer_t pos); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_vector, get)(const TYPE(igraph_vector)* v, igraph_integer_t pos); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE* FUNCTION(igraph_vector, get_ptr)(const TYPE(igraph_vector)* v, igraph_integer_t pos); +IGRAPH_EXPORT void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, igraph_integer_t pos, BASE value); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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); + +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, range)(TYPE(igraph_vector)*v, BASE start, BASE end); +#endif + +/*-----------------------*/ +/* Vector views */ +/*-----------------------*/ + +IGRAPH_EXPORT const TYPE(igraph_vector) *FUNCTION(igraph_vector, view)(const TYPE(igraph_vector) *v, + const BASE *data, + igraph_integer_t length); + +/*-----------------------*/ +/* Copying vectors */ +/*-----------------------*/ + +IGRAPH_EXPORT void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE* to); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2); + +/*-----------------------*/ +/* Exchanging elements */ +/*-----------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, swap_elements)( + TYPE(igraph_vector) *v, igraph_integer_t i, igraph_integer_t j); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, permute)(TYPE(igraph_vector) *v, + const igraph_vector_int_t *ind); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from); + +#ifndef NOABS + IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v); +#endif + +/*------------------------------*/ +/* Comparison */ +/*------------------------------*/ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +#ifndef NOTORDERED +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE int FUNCTION(igraph_vector, lex_cmp)( + const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE int FUNCTION(igraph_vector, colex_cmp)( + const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE int FUNCTION(igraph_vector, lex_cmp_untyped)(const void *lhs, const void *rhs); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, const void *rhs); +#endif + +/*------------------------------*/ +/* Finding minimum and maximum */ +/*------------------------------*/ + +#ifndef NOTORDERED +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT void FUNCTION(igraph_vector, minmax)( + const TYPE(igraph_vector) *v, BASE *min, BASE *max); +IGRAPH_EXPORT void FUNCTION(igraph_vector, which_minmax)( + const TYPE(igraph_vector) *v, igraph_integer_t *which_min, igraph_integer_t *which_max); +#endif + +/*-------------------*/ +/* Vector properties */ +/*-------------------*/ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, empty)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_vector, size)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_vector, sum)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t FUNCTION(igraph_vector, sumsq)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE BASE FUNCTION(igraph_vector, prod)(const TYPE(igraph_vector) *v); +#ifndef NOTORDERED +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, isininterval)(const TYPE(igraph_vector) *v, + BASE low, BASE high); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, any_smaller)(const TYPE(igraph_vector) *v, + BASE limit); +#endif +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, is_equal)(const TYPE(igraph_vector) *lhs, + const TYPE(igraph_vector) *rhs); +#ifndef NOTORDERED +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) *m1, + const TYPE(igraph_vector) *m2); +#endif + +/*------------------------*/ +/* Searching for elements */ +/*------------------------*/ + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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, igraph_integer_t from, BASE what, igraph_integer_t *pos); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)( + const TYPE(igraph_vector) *v, BASE what, igraph_integer_t *pos, + igraph_integer_t start, igraph_integer_t end); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch)( + const TYPE(igraph_vector) *v, BASE what, igraph_integer_t *pos); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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 igraph_error_t FUNCTION(igraph_vector, resize)( + TYPE(igraph_vector)* v, igraph_integer_t new_size); +IGRAPH_EXPORT void FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, reserve)( + TYPE(igraph_vector)* v, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t FUNCTION(igraph_vector, insert)( + TYPE(igraph_vector) *v, igraph_integer_t pos, BASE value); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove)( + TYPE(igraph_vector) *v, igraph_integer_t elem); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_fast)( + TYPE(igraph_vector) *v, igraph_integer_t elem); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_section)( + TYPE(igraph_vector) *v, igraph_integer_t from, igraph_integer_t 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 igraph_error_t FUNCTION(igraph_vector, qsort_ind)( + const TYPE(igraph_vector) *v, igraph_vector_int_t *inds, igraph_order_t order); + +#endif + +/*-----------*/ +/* Printing */ +/*-----------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file); + +#ifdef OUT_FORMAT +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, const char *format); +#endif /* OUT_FORMAT */ + +/*----------------------------------------*/ +/* Operations specific to complex vectors */ +/*----------------------------------------*/ + +#ifdef BASE_COMPLEX + +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_real(const igraph_vector_complex_t *v, + igraph_vector_t *real); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_imag(const igraph_vector_complex_t *v, + igraph_vector_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_realimag(const igraph_vector_complex_t *v, + igraph_vector_t *real, + igraph_vector_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_create(igraph_vector_complex_t *v, + const igraph_vector_t *real, + const igraph_vector_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_create_polar(igraph_vector_complex_t *v, + const igraph_vector_t *r, + const igraph_vector_t *theta); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_vector_complex_all_almost_e(const igraph_vector_complex_t *lhs, + const igraph_vector_complex_t *rhs, + igraph_real_t eps); + +#endif /* BASE_COMPLEX */ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector)*v, int no, ...); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector)*v, int no, ...); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector)*v, double endmark, ...); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector)*v, int endmark, ...); + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, move_interval)( + TYPE(igraph_vector) *v, igraph_integer_t begin, igraph_integer_t end, + igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_vector, move_interval2)( + TYPE(igraph_vector) *v, igraph_integer_t begin, igraph_integer_t end, + igraph_integer_t to); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem); +#endif +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, get_interval)( + const TYPE(igraph_vector) *v, TYPE(igraph_vector) *res, + igraph_integer_t from, igraph_integer_t to); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_vector, intersection_size_sorted)( + const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); +#endif +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *newv, + const igraph_vector_int_t *idx); + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, + const igraph_vector_int_t *idx); diff --git a/include/igraph_vector_ptr.h b/include/igraph_vector_ptr.h new file mode 100644 index 0000000..77ba89c --- /dev/null +++ b/include/igraph_vector_ptr.h @@ -0,0 +1,105 @@ +/* -*- 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_error.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 igraph_error_t igraph_vector_ptr_init(igraph_vector_ptr_t* v, igraph_integer_t size); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_init_array(igraph_vector_ptr_t* v, void *const *data, igraph_integer_t length); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_init_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); +IGRAPH_EXPORT const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v, + void *const *data, igraph_integer_t 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 igraph_error_t igraph_vector_ptr_reserve(igraph_vector_ptr_t* v, igraph_integer_t capacity); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_vector_ptr_empty(const igraph_vector_ptr_t* v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t 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 igraph_error_t igraph_vector_ptr_push_back(igraph_vector_ptr_t* v, void* e); +IGRAPH_EXPORT igraph_error_t 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 igraph_error_t igraph_vector_ptr_insert(igraph_vector_ptr_t *v, igraph_integer_t pos, void* e); +IGRAPH_EXPORT IGRAPH_DEPRECATED void* igraph_vector_ptr_e(const igraph_vector_ptr_t* v, igraph_integer_t pos); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE void* igraph_vector_ptr_get(const igraph_vector_ptr_t* v, igraph_integer_t pos); +IGRAPH_EXPORT void igraph_vector_ptr_set(igraph_vector_ptr_t* v, igraph_integer_t pos, void* value); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_resize(igraph_vector_ptr_t* v, igraph_integer_t newsize); +IGRAPH_EXPORT void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_permute(igraph_vector_ptr_t* v, const igraph_vector_int_t* index); +IGRAPH_EXPORT void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, igraph_integer_t pos); +IGRAPH_EXPORT void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int(*compar)(const void*, const void*)); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_sort_ind( + igraph_vector_ptr_t *v, igraph_vector_int_t *inds, int(*compar)(const void*, const void*)); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); + +/** + * \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/include/igraph_vector_type.h b/include/igraph_vector_type.h new file mode 100644 index 0000000..fef9d9f --- /dev/null +++ b/include/igraph_vector_type.h @@ -0,0 +1,33 @@ +/* -*- 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 + +*/ + +/** + * 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/include/igraph_version.h.in b/include/igraph_version.h.in new file mode 100644 index 0000000..fda4227 --- /dev/null +++ b/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 void igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor); + +__END_DECLS + +#endif diff --git a/include/igraph_visitor.h b/include/igraph_visitor.h new file mode 100644 index 0000000..358467e --- /dev/null +++ b/include/igraph_visitor.h @@ -0,0 +1,139 @@ +/* -*- 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_VISITOR_H +#define IGRAPH_VISITOR_H + +#include "igraph_decls.h" +#include "igraph_constants.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_datatype.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Visitor-like functions */ +/* -------------------------------------------------- */ + +/** + * \typedef igraph_bfshandler_t + * \brief Callback type for BFS function. + * + * \ref igraph_bfs() is able to call a callback function, whenever a + * new vertex is found, while doing the breadth-first search. This + * callback function must be of type \c igraph_bfshandler_t. It has + * the following arguments: + * + * \param graph The graph that the algorithm is working on. Of course + * this must not be modified. + * \param vid The id of the vertex just found by the breadth-first + * search. + * \param pred The id of the previous vertex visited. It is -1 if + * there is no previous vertex, because the current vertex is the root + * is a search tree. + * \param succ The id of the next vertex that will be visited. It is + * -1 if there is no next vertex, because the current vertex is the + * last one in a search tree. + * \param rank The rank of the current vertex, it starts with zero. + * \param dist The distance (number of hops) of the current vertex + * from the root of the current search tree. + * \param extra The extra argument that was passed to \ref + * igraph_bfs(). + * \return \c IGRAPH_SUCCESS if the BFS should continue, \c IGRAPH_STOP + * if the BFS should stop and return to the caller normally. Any other + * value is treated as an igraph error code, terminating the search and + * returning to the caller with the same error code. If a BFS is + * is terminated prematurely, then all elements of the result vectors + * that were not yet calculated at the point of the termination + * contain negative values. + * + * \sa \ref igraph_bfs() + */ + +typedef igraph_error_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); + +IGRAPH_EXPORT igraph_error_t igraph_bfs(const igraph_t *graph, + igraph_integer_t root, const igraph_vector_int_t *roots, + igraph_neimode_t mode, igraph_bool_t unreachable, + const igraph_vector_int_t *restricted, + igraph_vector_int_t *order, igraph_vector_int_t *rank, + igraph_vector_int_t *parents, + igraph_vector_int_t *pred, igraph_vector_int_t *succ, + igraph_vector_int_t *dist, igraph_bfshandler_t *callback, + void *extra); + +IGRAPH_EXPORT igraph_error_t igraph_bfs_simple(const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, + igraph_vector_int_t *order, igraph_vector_int_t *layers, + igraph_vector_int_t *parents); + +/** + * \function igraph_dfshandler_t + * \brief Callback type for the DFS function. + * + * \ref igraph_dfs() is able to call a callback function, whenever a + * new vertex is discovered, and/or whenever a subtree is + * completed. These callbacks must be of type \c + * igraph_dfshandler_t. They have the following arguments: + * + * \param graph The graph that the algorithm is working on. Of course + * this must not be modified. + * \param vid The id of the vertex just found by the depth-first + * search. + * \param dist The distance (number of hops) of the current vertex + * from the root of the current search tree. + * \param extra The extra argument that was passed to \ref + * igraph_dfs(). + * \return \c IGRAPH_SUCCESS if the DFS should continue, \c IGRAPH_STOP + * if the DFS should stop and return to the caller normally. Any other + * value is treated as an igraph error code, terminating the search and + * returning to the caller with the same error code. If a DFS is + * is terminated prematurely, then all elements of the result vectors + * that were not yet calculated at the point of the termination + * contain negative values. + * + * \sa \ref igraph_dfs() + */ + +typedef igraph_error_t igraph_dfshandler_t(const igraph_t *graph, + igraph_integer_t vid, + igraph_integer_t dist, + void *extra); + +IGRAPH_EXPORT igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, + igraph_neimode_t mode, igraph_bool_t unreachable, + igraph_vector_int_t *order, + igraph_vector_int_t *order_out, igraph_vector_int_t *parents, + igraph_vector_int_t *dist, igraph_dfshandler_t *in_callback, + igraph_dfshandler_t *out_callback, + void *extra); + +__END_DECLS + +#endif diff --git a/interfaces/CMakeLists.txt b/interfaces/CMakeLists.txt new file mode 100644 index 0000000..57a89d5 --- /dev/null +++ b/interfaces/CMakeLists.txt @@ -0,0 +1,28 @@ +# Check whether the user has Stimulus on its PATH +find_program(STIMULUS_COMMAND stimulus) + +# Add a custom targer that checks functions.yaml and types.yaml +if(STIMULUS_COMMAND) + add_custom_command( + OUTPUT test_stimulus_specifications.cpp + COMMAND ${STIMULUS_COMMAND} + -l ci:validate + -f ${CMAKE_CURRENT_SOURCE_DIR}/functions.yaml + -t ${CMAKE_CURRENT_SOURCE_DIR}/types.yaml + -o test_stimulus_specifications.cpp + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/functions.yaml + ${CMAKE_CURRENT_SOURCE_DIR}/types.yaml + COMMENT "Generating C++ checker for Stimulus function and type specifications..." + USES_TERMINAL + ) + add_executable(test_stimulus test_stimulus_specifications.cpp) + target_include_directories(test_stimulus PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include) + + add_custom_target( + check_stimulus + COMMAND test_stimulus + DEPENDS test_stimulus + COMMENT "Running C++ checker for Stimulus function and type specifications..." + ) +endif(STIMULUS_COMMAND) diff --git a/interfaces/functions.yaml b/interfaces/functions.yaml new file mode 100644 index 0000000..cc56a68 --- /dev/null +++ b/interfaces/functions.yaml @@ -0,0 +1,2654 @@ +# vim:set ts=4 sw=4 sts=4 et: +# +# This file is a YAML representation of the signatures of most igraph +# functions. They are currently used by some of the higher level interfaces to +# generate code using our internal tool called Stimulus +# +# See https://github.com/igraph/stimulus for more information + +####################################### +# The basic interface +####################################### + +igraph_empty: + PARAMS: OUT GRAPH graph, INTEGER n=0, BOOLEAN directed=True + +igraph_add_edges: + PARAMS: INOUT GRAPH graph, VERTEX_INDEX_PAIRS edges, ATTRIBUTES attr + DEPS: edges ON graph + +igraph_empty_attrs: + PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed, ATTRIBUTES attr + +igraph_add_vertices: + PARAMS: INOUT GRAPH graph, INTEGER nv, ATTRIBUTES attr + +igraph_copy: + PARAMS: OUT GRAPH to, IN GRAPH from + +igraph_delete_edges: + PARAMS: INOUT GRAPH graph, EDGE_SELECTOR edges + DEPS: edges ON graph + +igraph_delete_vertices: + PARAMS: INOUT GRAPH graph, VERTEX_SELECTOR vertices + DEPS: vertices ON graph + +igraph_delete_vertices_idx: + PARAMS: |- + INOUT GRAPH graph, VERTEX_SELECTOR vertices, + OPTIONAL OUT VECTOR_INT idx, + OPTIONAL OUT VECTOR_INT invidx + DEPS: vertices ON graph + +igraph_vcount: + PARAMS: GRAPH graph + RETURN: INTEGER + +igraph_ecount: + PARAMS: GRAPH graph + RETURN: INTEGER + +igraph_neighbors: + PARAMS: GRAPH graph, OUT VERTEX_INDICES neis, VERTEX vid, NEIMODE mode=ALL + +igraph_is_directed: + PARAMS: GRAPH graph + RETURN: BOOLEAN + +igraph_degree: + PARAMS: |- + GRAPH graph, OUT VECTOR_INT res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, + BOOLEAN loops + DEPS: vids ON graph + +igraph_edge: + PARAMS: GRAPH graph, INTEGER eid, OUT INTEGER from, OUT INTEGER to + +igraph_edges: + PARAMS: GRAPH graph, EDGE_SELECTOR eids, OUT VECTOR_INT edges + DEPS: eids ON graph + +igraph_get_eid: + PARAMS: |- + GRAPH graph, OUT EDGE eid, VERTEX from, VERTEX to, + BOOLEAN directed=True, BOOLEAN error=True + +igraph_get_eids: + PARAMS: |- + GRAPH graph, OUT EDGE_INDICES eids, VERTEX_INDEX_PAIRS pairs, + BOOLEAN directed=True, BOOLEAN error=True + DEPS: pairs ON graph + +igraph_get_all_eids_between: + PARAMS: |- + GRAPH graph, OUT EDGE_INDICES eids, VERTEX from, VERTEX to, + BOOLEAN directed=True + +igraph_incident: + PARAMS: GRAPH graph, OUT EDGE_INDICES eids, VERTEX vid, NEIMODE mode=ALL + +igraph_is_same_graph: + PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN res + +####################################### +# Constructors, deterministic +####################################### + +igraph_create: + PARAMS: OUT GRAPH graph, VECTOR_INT edges, INTEGER n=0, BOOLEAN directed=True + +igraph_adjacency: + PARAMS: |- + OUT GRAPH graph, MATRIX adjmatrix, ADJACENCY_MODE mode=DIRECTED, LOOPS loops=ONCE + +igraph_sparse_adjacency: + # adjmatrix is declared as INOUT because it might be modified during the + # construction to eliminate duplicate elements from the representation + PARAMS: |- + OUT GRAPH graph, INOUT SPARSEMAT adjmatrix, ADJACENCY_MODE mode=DIRECTED, LOOPS loops=ONCE + +igraph_sparse_weighted_adjacency: + # adjmatrix is declared as INOUT because it might be modified during the + # construction to eliminate duplicate elements from the representation + PARAMS: |- + OUT GRAPH graph, INOUT SPARSEMAT adjmatrix, ADJACENCY_MODE mode=DIRECTED, + OUT EDGEWEIGHTS weights, LOOPS loops=ONCE + +igraph_weighted_adjacency: + PARAMS: |- + OUT GRAPH graph, MATRIX adjmatrix, ADJACENCY_MODE mode=DIRECTED, + OUT EDGEWEIGHTS weights, LOOPS loops=ONCE + +igraph_star: + PARAMS: OUT GRAPH graph, INTEGER n, STAR_MODE mode=OUT, INTEGER center=0 + +igraph_wheel: + PARAMS: OUT GRAPH graph, INTEGER n, WHEEL_MODE mode=OUT, INTEGER center=0 + +igraph_hypercube: + PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=False + +igraph_square_lattice: + PARAMS: |- + OUT GRAPH graph, VECTOR_INT dimvector, INTEGER nei=1, + BOOLEAN directed=False, BOOLEAN mutual=False, OPTIONAL VECTOR_BOOL periodic + +igraph_triangular_lattice: + PARAMS: |- + OUT GRAPH graph, VECTOR_INT dimvector, BOOLEAN directed=False, BOOLEAN mutual=False + +igraph_ring: + PARAMS: |- + OUT GRAPH graph, INTEGER n, BOOLEAN directed=False, BOOLEAN mutual=False, + BOOLEAN circular=True + +igraph_kary_tree: + PARAMS: OUT GRAPH graph, INTEGER n, INTEGER children=2, TREE_MODE type=OUT + +igraph_symmetric_tree: + PARAMS: OUT GRAPH graph, VECTOR_INT branches, TREE_MODE type=OUT + +igraph_regular_tree: + PARAMS: OUT GRAPH graph, INTEGER h, INTEGER k=3, TREE_MODE type=UNDIRECTED + +igraph_full: + PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=False, BOOLEAN loops=False + +igraph_full_citation: + PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=True + +igraph_atlas: + PARAMS: OUT GRAPH graph, INTEGER number=0 + +igraph_extended_chordal_ring: + PARAMS: OUT GRAPH graph, INTEGER nodes, MATRIX_INT W, BOOLEAN directed=False + +igraph_connect_neighborhood: + PARAMS: INOUT GRAPH graph, INTEGER order=2, NEIMODE mode=ALL + +igraph_graph_power: + PARAMS: IN GRAPH graph, OUT GRAPH res, INTEGER order, BOOLEAN directed=False + +igraph_linegraph: + PARAMS: GRAPH graph, OUT GRAPH linegraph + +igraph_de_bruijn: + PARAMS: OUT GRAPH graph, INTEGER m, INTEGER n + +igraph_kautz: + PARAMS: OUT GRAPH graph, INTEGER m, INTEGER n + +igraph_famous: + PARAMS: OUT GRAPH graph, CSTRING name + +igraph_lcf_vector: + PARAMS: OUT GRAPH graph, INTEGER n, VECTOR_INT shifts, INTEGER repeats=1 + +igraph_adjlist: + PARAMS: |- + OUT GRAPH graph, ADJLIST adjlist, NEIMODE mode=OUT, + BOOLEAN duplicate=True + +igraph_full_bipartite: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, INTEGER n1, + INTEGER n2, BOOLEAN directed=False, NEIMODE mode=ALL + +igraph_full_multipartite: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT INDEX_VECTOR types, VECTOR_INT n, + BOOLEAN directed=False, NEIMODE mode=ALL + +igraph_realize_degree_sequence: + PARAMS: |- + OUT GRAPH graph, VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg=NULL, + EDGE_TYPE_SW allowed_edge_types=SIMPLE, REALIZE_DEGSEQ_METHOD method=SMALLEST + +igraph_realize_bipartite_degree_sequence: + PARAMS: |- + OUT GRAPH graph, VECTOR_INT degrees1, VECTOR_INT degrees2, + EDGE_TYPE_SW allowed_edge_types=SIMPLE, REALIZE_DEGSEQ_METHOD method=SMALLEST + +igraph_circulant: + PARAMS: OUT GRAPH graph, INTEGER n, VECTOR_INT shifts, BOOLEAN directed=False + +igraph_generalized_petersen: + PARAMS: OUT GRAPH graph, INTEGER n, INTEGER k + +igraph_turan: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT INDEX_VECTOR types, INTEGER n, INTEGER r + +igraph_weighted_sparsemat: + PARAMS: + OUT GRAPH graph, SPARSEMAT A, BOOLEAN directed, CSTRING attr, BOOLEAN loops=False + +####################################### +# Constructors, games +####################################### + +igraph_barabasi_game: + PARAMS: |- + OUT GRAPH graph, INTEGER n, REAL power=1.0, INTEGER m=1, + OPTIONAL VECTOR_INT outseq, BOOLEAN outpref=False, REAL A=1.0, + BOOLEAN directed=True, BARABASI_ALGORITHM algo=BAG, + OPTIONAL GRAPH start_from + +igraph_erdos_renyi_game_gnp: + PARAMS: OUT GRAPH graph, INTEGER n, REAL p, BOOLEAN directed=False, BOOLEAN loops=False + +igraph_erdos_renyi_game_gnm: + PARAMS: OUT GRAPH graph, INTEGER n, INTEGER m, BOOLEAN directed=False, BOOLEAN loops=False + +igraph_degree_sequence_game: + PARAMS: |- + OUT GRAPH graph, VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg, + DEGSEQ_MODE method=CONFIGURATION + +igraph_growing_random_game: + PARAMS: |- + OUT GRAPH graph, INTEGER n, INTEGER m=1, BOOLEAN directed=False, + BOOLEAN citation=False + +igraph_barabasi_aging_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INTEGER m=1, OPTIONAL VECTOR_INT outseq, + BOOLEAN outpref=False, REAL pa_exp=1.0, REAL aging_exp=0.0, INTEGER aging_bin=1, + REAL zero_deg_appeal=1.0, REAL zero_age_appeal=0.0, REAL deg_coef=1.0, + REAL age_coef=1.0, BOOLEAN directed=True + +igraph_recent_degree_game: + PARAMS: |- + OUT GRAPH graph, INTEGER n, REAL power=1.0, INTEGER window=1, + INTEGER m=1, OPTIONAL VECTOR_INT outseq, BOOLEAN outpref=False, + REAL zero_appeal=1.0, BOOLEAN directed=True + +igraph_recent_degree_aging_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INTEGER m=1, OPTIONAL VECTOR_INT outseq, + BOOLEAN outpref=False, REAL pa_exp=1.0, REAL aging_exp=0.0, INTEGER aging_bin=1, + INTEGER window=1, REAL zero_appeal=1.0, BOOLEAN directed=True + +igraph_callaway_traits_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INTEGER types, + INTEGER edges_per_step=1, VECTOR type_dist, MATRIX pref_matrix, + BOOLEAN directed=False, OPTIONAL OUT VECTOR_INT node_type_vec + +igraph_establishment_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INTEGER types, INTEGER k=1, + VECTOR type_dist, MATRIX pref_matrix, BOOLEAN directed=True, + OPTIONAL OUT VECTOR_INT node_type_vec + +igraph_grg_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, REAL radius, BOOLEAN torus=False, + OPTIONAL OUT VECTOR x, OPTIONAL OUT VECTOR y + +igraph_preference_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INTEGER types, + VECTOR type_dist, BOOLEAN fixed_sizes=False, + MATRIX pref_matrix, OUT VECTOR_INT node_type_vec, + BOOLEAN directed=False, BOOLEAN loops=False + +igraph_asymmetric_preference_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INTEGER out_types, INTEGER in_types, + MATRIX type_dist_matrix, MATRIX pref_matrix, + OUT VECTOR_INT node_type_out_vec, OUT VECTOR_INT node_type_in_vec, + BOOLEAN loops=False + +igraph_rewire_edges: + PARAMS: |- + INOUT GRAPH graph, REAL prob, BOOLEAN loops=False, + BOOLEAN multiple=False + +igraph_rewire_directed_edges: + PARAMS: |- + INOUT GRAPH graph, REAL prob, BOOLEAN loops=False, + NEIMODE mode=OUT + +igraph_watts_strogatz_game: + PARAMS: |- + OUT GRAPH graph, INTEGER dim, INTEGER size, INTEGER nei, + REAL p, BOOLEAN loops=False, BOOLEAN multiple=False + +igraph_lastcit_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INTEGER edges_per_node=1, + INTEGER agebins=1, VECTOR preference, BOOLEAN directed=True + +igraph_cited_type_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INDEX_VECTOR types, VECTOR pref, + INTEGER edges_per_step=1, BOOLEAN directed=True + +igraph_citing_cited_type_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, INDEX_VECTOR types, MATRIX pref, + INTEGER edges_per_step=1, BOOLEAN directed=True + +igraph_forest_fire_game: + PARAMS: |- + OUT GRAPH graph, INTEGER nodes, REAL fw_prob, REAL bw_factor=1, + INTEGER ambs=1, BOOLEAN directed=True + +igraph_simple_interconnected_islands_game: + PARAMS: |- + OUT GRAPH graph, INTEGER islands_n, INTEGER islands_size, + REAL islands_pin, INTEGER n_inter + +# Use a default of loops=True, as this is what's in the original Chung-Lu paper +igraph_chung_lu_game: + PARAMS: |- + OUT GRAPH graph, VECTOR out_weights, OPTIONAL VECTOR in_weights, + BOOLEAN loops=True, CHUNG_LU_VARIANT variant=ORIGINAL + +igraph_static_fitness_game: + PARAMS: |- + OUT GRAPH graph, INTEGER no_of_edges, VECTOR fitness_out, + OPTIONAL VECTOR fitness_in, BOOLEAN loops=False, + BOOLEAN multiple=False + +igraph_static_power_law_game: + PARAMS: |- + OUT GRAPH graph, INTEGER no_of_nodes, INTEGER no_of_edges, + REAL exponent_out, REAL exponent_in=-1, + BOOLEAN loops=False, BOOLEAN multiple=False, + BOOLEAN finite_size_correction=True + +igraph_k_regular_game: + PARAMS: |- + OUT GRAPH graph, INTEGER no_of_nodes, INTEGER k, + BOOLEAN directed=False, BOOLEAN multiple=False + +igraph_sbm_game: + PARAMS: |- + OUT GRAPH graph, INTEGER n, MATRIX pref_matrix, + VECTOR_INT block_sizes, BOOLEAN directed=False, + BOOLEAN loops=False + +igraph_hsbm_game: + INTERNAL: true + PARAMS: |- + OUT GRAPH graph, INTEGER n, INTEGER m, + VECTOR rho, MATRIX C, REAL p + +igraph_hsbm_list_game: + INTERNAL: true + PARAMS: |- + OUT GRAPH graph, INTEGER n, VECTOR_INT mlist, + VECTOR_LIST rholist, MATRIX_LIST Clist, REAL p + +igraph_correlated_game: + PARAMS: |- + GRAPH old_graph, OUT GRAPH new_graph, + REAL corr, REAL p=edge_density(old.graph), OPTIONAL INDEX_VECTOR permutation=NULL + +igraph_correlated_pair_game: + PARAMS: |- + OUT GRAPH graph1, OUT GRAPH graph2, INTEGER n, REAL corr, + REAL p, BOOLEAN directed=False, + OPTIONAL INDEX_VECTOR permutation=NULL + +igraph_dot_product_game: + PARAMS: OUT GRAPH graph, MATRIX vecs, BOOLEAN directed=False + +igraph_sample_sphere_surface: + PARAMS: |- + INTEGER dim, INTEGER n=1, REAL radius=1, + BOOLEAN positive=True, OUT MATRIX res + +igraph_sample_sphere_volume: + PARAMS: |- + INTEGER dim, INTEGER n=1, REAL radius=1, + BOOLEAN positive=True, OUT MATRIX res + +igraph_sample_dirichlet: + PARAMS: INTEGER n, VECTOR alpha, OUT MATRIX res + +####################################### +# Basic query functions +####################################### + +igraph_are_adjacent: + PARAMS: GRAPH graph, VERTEX v1, VERTEX v2, OUT BOOLEAN res + DEPS: v1 ON graph, v2 ON graph + +igraph_are_connected: + PARAMS: GRAPH graph, VERTEX v1, VERTEX v2, OUT BOOLEAN res + DEPS: v1 ON graph, v2 ON graph + +####################################### +# Structural properties +####################################### + +igraph_diameter: + PARAMS: |- + GRAPH graph, OUT REAL res, OUT INTEGER from, + OUT INTEGER to, OPTIONAL OUT VECTOR_INT vertex_path, + OPTIONAL OUT VECTOR_INT edge_path, + BOOLEAN directed=True, BOOLEAN unconnected=True + +igraph_diameter_dijkstra: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT REAL res, OUT INTEGER from, OUT INTEGER to, + OPTIONAL OUT VECTOR_INT vertex_path, + OPTIONAL OUT VECTOR_INT edge_path, + BOOLEAN directed=True, BOOLEAN unconnected=True + DEPS: weights ON graph + +igraph_closeness: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, + OPTIONAL OUT VECTOR_INT reachable_count, + OPTIONAL OUT BOOLEAN all_reachable, + VERTEX_SELECTOR vids=ALL, + NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, + BOOLEAN normalized=False + DEPS: vids ON graph, weights ON graph, res ON graph vids + +igraph_closeness_cutoff: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, + OPTIONAL OUT VECTOR_INT reachable_count, + OPTIONAL OUT BOOLEAN all_reachable, + VERTEX_SELECTOR vids=ALL, + NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, + BOOLEAN normalized=False, REAL cutoff=-1 + DEPS: vids ON graph, weights ON graph, res ON graph vids + +igraph_distances: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph + +igraph_distances_cutoff: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT, REAL cutoff=-1 + DEPS: from ON graph, to ON graph + +igraph_get_shortest_path: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, vertices ON graph, edges ON graph + +igraph_get_shortest_path_bellman_ford: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph + +igraph_get_shortest_path_dijkstra: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph + +igraph_get_shortest_path_astar: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, + OPTIONAL ASTAR_HEURISTIC_FUNC heuristic=NULL, EXTRA extra=NULL + DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph + +igraph_get_shortest_paths: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEXSET_LIST vertices, OPTIONAL OUT EDGESET_LIST edges, + VERTEX from, VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT, + OPTIONAL OUT VECTOR_INT parents, + OPTIONAL OUT VECTOR_INT inbound_edges + DEPS: edges ON graph, from ON graph, to ON graph + +igraph_get_all_shortest_paths: + PARAMS: |- + GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, + OPTIONAL OUT EDGESET_LIST edges, OPTIONAL OUT VECTOR_INT nrgeo, + VERTEX from, VERTEX_SELECTOR to, NEIMODE mode=OUT + DEPS: edges ON graph, from ON graph, to ON graph + +igraph_distances_dijkstra: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_distances_dijkstra_cutoff: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT, REAL cutoff=-1 + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_get_shortest_paths_dijkstra: + PARAMS: |- + GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, + OPTIONAL OUT EDGESET_LIST edges, VERTEX from, VERTEX_SELECTOR to=ALL, + EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, + OPTIONAL OUT VECTOR_INT parents=NULL, + OPTIONAL OUT VECTOR_INT inbound_edges=NULL + DEPS: |- + vertices ON graph, edges ON graph, from ON graph, to ON graph, + weights ON graph + +igraph_get_shortest_paths_bellman_ford: + PARAMS: |- + GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, + OPTIONAL OUT EDGESET_LIST edges, VERTEX from, VERTEX_SELECTOR to=ALL, + EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, + OPTIONAL OUT VECTOR_INT parents=NULL, + OPTIONAL OUT VECTOR_INT inbound_edges=NULL + DEPS: |- + vertices ON graph, edges ON graph, from ON graph, to ON graph, + weights ON graph + +igraph_get_all_shortest_paths_dijkstra: + PARAMS: |- + GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, + OPTIONAL OUT EDGESET_LIST edges, OPTIONAL OUT VECTOR_INT nrgeo, + VERTEX from, VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, + NEIMODE mode=OUT + DEPS: |- + weights ON graph, from ON graph, to ON graph, vertices ON graph, edges ON graph + +igraph_distances_bellman_ford: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_distances_johnson: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_distances_floyd_warshall: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, + FWALGORITHM method + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_voronoi: + PARAMS: |- + GRAPH graph, OUT VECTOR_INT membership, OUT VECTOR distances, + VERTEX_INDICES generators, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, VORONOI_TIEBREAKER tiebreaker=RANDOM + DEPS: weights ON graph, generators ON graph + +igraph_get_all_simple_paths: + PARAMS: |- + GRAPH graph, OUT VERTEX_INDICES res, VERTEX from, + VERTEX_SELECTOR to=ALL, INTEGER cutoff=-1, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, res ON graph + +igraph_get_k_shortest_paths: + PARAMS: |- + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, + OPTIONAL OUT VERTEXSET_LIST vertex_paths, + OPTIONAL OUT EDGESET_LIST edge_paths, + INTEGER k, VERTEX from, VERTEX to, NEIMODE mode=OUT + DEPS: |- + from ON graph, to ON graph, weights ON graph, vertex_paths ON graph, edge_paths ON graph + +igraph_get_widest_path: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT + DEPS: |- + from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph + +igraph_get_widest_paths: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEXSET_LIST vertices, OPTIONAL OUT EDGESET_LIST edges, + VERTEX from, VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights=NULL, + NEIMODE mode=OUT, OPTIONAL OUT VECTOR_INT parents, + OPTIONAL OUT VECTOR_INT inbound_edges + DEPS: |- + from ON graph, to ON graph, weights ON graph, vertices ON graph, + edges ON graph + +igraph_widest_path_widths_dijkstra: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_widest_path_widths_floyd_warshall: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_spanner: + PARAMS: |- + GRAPH graph, OUT EDGE_INDICES spanner, REAL stretch, OPTIONAL EDGEWEIGHTS weights + DEPS: weights ON graph + +igraph_subcomponent: + PARAMS: GRAPH graph, OUT VERTEX_INDICES res, VERTEX vid, NEIMODE mode=ALL + DEPS: vid ON graph, res ON graph + +igraph_betweenness: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + BOOLEAN directed=True, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph, vids ON graph, res ON graph vids + +igraph_betweenness_cutoff: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + BOOLEAN directed=True, EDGEWEIGHTS weights=NULL, + REAL cutoff=-1 + DEPS: vids ON graph, weights ON graph, res ON graph vids + +igraph_betweenness_subset: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + BOOLEAN directed=True, VERTEX_SELECTOR sources=ALL, VERTEX_SELECTOR targets=ALL, + EDGEWEIGHTS weights=NULL + DEPS: |- + vids ON graph, weights ON graph, res ON graph vids, sources ON graph, targets ON graph + +igraph_edge_betweenness: + PARAMS: |- + GRAPH graph, OUT VECTOR res, BOOLEAN directed=True, + EDGEWEIGHTS weights=NULL + DEPS: weights ON graph + +igraph_edge_betweenness_cutoff: + PARAMS: |- + GRAPH graph, OUT VECTOR res, BOOLEAN directed=True, + EDGEWEIGHTS weights=NULL, REAL cutoff=-1 + DEPS: weights ON graph + +igraph_edge_betweenness_subset: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGE_SELECTOR eids=ALL, + BOOLEAN directed=True, VERTEX_SELECTOR sources=ALL, VERTEX_SELECTOR targets=ALL, + EDGEWEIGHTS weights=NULL + DEPS: |- + eids ON graph, weights ON graph, res ON graph, sources ON graph, targets ON graph + +igraph_harmonic_centrality: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False + DEPS: weights ON graph, vids ON graph, res ON graph vids + +igraph_harmonic_centrality_cutoff: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False, + REAL cutoff=-1 + DEPS: vids ON graph, weights ON graph, res ON graph vids + +igraph_pagerank: + PARAMS: |- + GRAPH graph, PAGERANKALGO algo=PRPACK, + OUT VERTEX_QTY vector, OUT REAL value, + VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, + REAL damping=0.85, EDGEWEIGHTS weights=NULL, + INOUT PAGERANKOPT options=NULL + DEPS: |- + vids ON graph, weights ON graph, vector ON graph vids, + options ON algo + +igraph_personalized_pagerank: + PARAMS: |- + GRAPH graph, PAGERANKALGO algo=PRPACK, + OUT VERTEX_QTY vector, OUT REAL value, + VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, + REAL damping=0.85, OPTIONAL VECTOR personalized, + OPTIONAL EDGEWEIGHTS weights, + INOUT PAGERANKOPT options=NULL + DEPS: |- + vids ON graph, weights ON graph, vector ON graph vids, + options ON algo + +igraph_personalized_pagerank_vs: + PARAMS: |- + GRAPH graph, PAGERANKALGO algo=PRPACK, + PRIMARY OUT VERTEX_QTY vector, OUT REAL value, + VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, + REAL damping=0.85, + VERTEX_SELECTOR reset_vids, + OPTIONAL EDGEWEIGHTS weights=NULL, + INOUT PAGERANKOPT options=NULL + DEPS: |- + vids ON graph, weights ON graph, vector ON graph vids, + options ON algo + +igraph_rewire: + PARAMS: INOUT GRAPH rewire, INTEGER n, REWIRING_MODE mode=SIMPLE + +igraph_induced_subgraph: + PARAMS: GRAPH graph, OUT GRAPH res, VERTEX_SELECTOR vids, SUBGRAPH_IMPL impl=AUTO + DEPS: vids ON graph + +igraph_subgraph_from_edges: + PARAMS: GRAPH graph, OUT GRAPH res, EDGE_SELECTOR eids, BOOLEAN delete_vertices=True + DEPS: eids ON graph + +igraph_reverse_edges: + PARAMS: INOUT GRAPH graph, EDGE_SELECTOR eids=ALL + DEPS: eids ON graph + +igraph_average_path_length: + PARAMS: GRAPH graph, PRIMARY OUT REAL res, OUT REAL unconn_pairs=NULL, + BOOLEAN directed=True, BOOLEAN unconn=True + +igraph_average_path_length_dijkstra: + PARAMS: GRAPH graph, PRIMARY OUT REAL res, OUT REAL unconn_pairs=NULL, + EDGEWEIGHTS weights=NULL, BOOLEAN directed=True, BOOLEAN unconn=True + DEPS: weights ON graph + +igraph_path_length_hist: + PARAMS: |- + GRAPH graph, OUT VECTOR res, OUT REAL unconnected, + BOOLEAN directed=True + +igraph_simplify: + PARAMS: |- + INOUT GRAPH graph, BOOLEAN remove_multiple=True, + BOOLEAN remove_loops=True, + EDGE_ATTRIBUTE_COMBINATION edge_attr_comb=Default + +igraph_transitivity_undirected: + PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITY_MODE mode=NAN + +igraph_transitivity_local_undirected: + PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, TRANSITIVITY_MODE mode=NAN + DEPS: vids ON graph + +igraph_transitivity_avglocal_undirected: + PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITY_MODE mode=NAN + +igraph_transitivity_barrat: + PARAMS: |- + GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, + EDGEWEIGHTS weights=NULL, TRANSITIVITY_MODE mode=NAN + DEPS: res ON graph, vids ON graph, weights ON graph + +igraph_ecc: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGE_SELECTOR eids=ALL, + INTEGER k=3, BOOLEAN offset=False, BOOLEAN normalize=True + DEPS: res ON graph, eids ON graph + +igraph_reciprocity: + PARAMS: |- + GRAPH graph, OUT REAL res, BOOLEAN ignore_loops=True, + RECIP mode=DEFAULT + +igraph_constraint: + PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, OPTIONAL EDGEWEIGHTS weights + DEPS: vids ON graph, weights ON graph + +igraph_maxdegree: + PARAMS: |- + GRAPH graph, OUT INTEGER res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, + BOOLEAN loops=True + DEPS: vids ON graph + +igraph_density: + PARAMS: GRAPH graph, OUT REAL res, BOOLEAN loops=False + +igraph_mean_degree: + PARAMS: GRAPH graph, OUT REAL res, BOOLEAN loops=True + +igraph_neighborhood_size: + PARAMS: |- + GRAPH graph, OUT VECTOR_INT res, VERTEX_SELECTOR vids, INTEGER order, + NEIMODE mode=ALL, INTEGER mindist=0 + DEPS: vids ON graph + +igraph_neighborhood: + PARAMS: |- + GRAPH graph, OUT VERTEXSET_LIST res, + VERTEX_SELECTOR vids, INTEGER order, + NEIMODE mode=ALL, INTEGER mindist=0 + DEPS: res ON graph, vids ON graph + +igraph_neighborhood_graphs: + PARAMS: |- + GRAPH graph, OUT GRAPH_LIST res, VERTEX_SELECTOR vids, + INTEGER order, + NEIMODE mode=ALL, INTEGER mindist=0 + DEPS: vids ON graph + +igraph_topological_sorting: + PARAMS: GRAPH graph, OUT VECTOR_INT res, NEIMODE mode=OUT + +igraph_feedback_arc_set: + # Default algorithm is the approximate method because it is faster and the + # function is _not_ called igraph_minimum_feedback_arc_set + PARAMS: GRAPH graph, OUT EDGE_INDICES result, EDGEWEIGHTS weights=NULL, FAS_ALGORITHM algo=APPROX_EADES + DEPS: result ON graph, weights ON graph + +igraph_is_loop: + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL + DEPS: es ON graph + +igraph_is_dag: + PARAMS: GRAPH graph, OUT BOOLEAN res + +igraph_is_acyclic: + PARAMS: GRAPH graph, OUT BOOLEAN res + +igraph_is_simple: + PARAMS: GRAPH graph, OUT BOOLEAN res + +igraph_is_multiple: + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL + DEPS: es ON graph + +igraph_has_loop: + PARAMS: GRAPH graph, OUT BOOLEAN res + +igraph_has_multiple: + PARAMS: GRAPH graph, OUT BOOLEAN res + +igraph_count_loops: + PARAMS: GRAPH graph, OUT INTEGER loop_count + +igraph_count_multiple: + PARAMS: GRAPH graph, OUT VECTOR_INT res, EDGE_SELECTOR es=ALL + DEPS: es ON graph + +igraph_girth: + PARAMS: GRAPH graph, OUT REAL girth, OUT VERTEX_INDICES circle + DEPS: circle ON graph + +igraph_is_perfect: + PARAMS: GRAPH graph, OUT BOOLEAN res + +igraph_add_edge: + PARAMS: INOUT GRAPH graph, INTEGER from, INTEGER to + +igraph_eigenvector_centrality: + PARAMS: |- + GRAPH graph, OUT ALL_VERTEX_QTY vector, OUT REAL value, + BOOLEAN directed=False, BOOLEAN scale=True, + EDGEWEIGHTS weights=NULL, + INOUT ARPACKOPT options=ARPACK_DEFAULTS + DEPS: weights ON graph, vector ON graph + +igraph_hub_score: + PARAMS: |- + GRAPH graph, OUT ALL_VERTEX_QTY vector, OUT REAL value, + BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, + INOUT ARPACKOPT options=ARPACK_DEFAULTS + DEPS: weights ON graph, vector ON graph + +igraph_authority_score: + PARAMS: |- + GRAPH graph, OUT ALL_VERTEX_QTY vector, OUT REAL value, + BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, + INOUT ARPACKOPT options=ARPACK_DEFAULTS + DEPS: weights ON graph, vector ON graph + +igraph_hub_and_authority_scores: + PARAMS: |- + GRAPH graph, OUT ALL_VERTEX_QTY hub_vector, OUT ALL_VERTEX_QTY authority_vector, + OUT REAL value, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, + INOUT ARPACKOPT options=ARPACK_DEFAULTS + +igraph_unfold_tree: + PARAMS: |- + GRAPH graph, OUT GRAPH tree, NEIMODE mode=ALL, VECTOR_INT roots, + OPTIONAL OUT INDEX_VECTOR vertex_index + +igraph_is_mutual: + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL, BOOLEAN loops=True + DEPS: es ON graph + +igraph_has_mutual: + PARAMS: GRAPH graph, OUT BOOLEAN res, BOOLEAN loops=True + +igraph_maximum_cardinality_search: + PARAMS: GRAPH graph, OPTIONAL OUT INDEX_VECTOR alpha, OPTIONAL OUT VERTEX_INDICES alpham1 + DEPS: alpham1 ON graph + +igraph_is_chordal: + PARAMS: |- + GRAPH graph, OPTIONAL INDEX_VECTOR alpha=NULL, OPTIONAL VERTEX_INDICES alpham1=NULL, + OPTIONAL OUT BOOLEAN chordal, OPTIONAL OUT VECTOR_INT fillin, + OPTIONAL OUT GRAPH newgraph + DEPS: alpham1 ON graph + +igraph_avg_nearest_neighbor_degree: + PARAMS: |- + GRAPH graph, VERTEX_SELECTOR vids=ALL, + NEIMODE mode=ALL, NEIMODE neighbor_degree_mode=ALL, + OPTIONAL OUT VERTEX_QTY knn, OPTIONAL OUT VECTOR knnk, + EDGEWEIGHTS weights=NULL + DEPS: vids ON graph, weights ON graph, knn ON graph vids + +igraph_degree_correlation_vector: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VECTOR knnk, + NEIMODE from_mode=OUT, NEIMODE to_mode=IN, + BOOLEAN directed_neighbors=True + DEPS: weights ON graph + +igraph_strength: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + NEIMODE mode=ALL, BOOLEAN loops=True, EDGEWEIGHTS weights=NULL + DEPS: vids ON graph, weights ON graph, res ON graph vids + +igraph_centralization: + PARAMS: VECTOR scores, REAL theoretical_max=0, BOOLEAN normalized=True + RETURN: REAL + +igraph_centralization_degree: + PARAMS: |- + GRAPH graph, OUT VECTOR res, + NEIMODE mode=ALL, BOOLEAN loops=True, + OUT REAL centralization, OUT REAL theoretical_max, + BOOLEAN normalized=True + +igraph_centralization_degree_tmax: + # The general consensus is that the 'loops' argument of this function + # should not have a default value; see this comment from @torfason: + # https://github.com/igraph/rigraph/issues/369#issuecomment-939893681 + PARAMS: |- + OPTIONAL GRAPH graph, INTEGER nodes=0, NEIMODE mode=ALL, + BOOLEAN loops, OUT REAL res + +igraph_centralization_betweenness: + PARAMS: |- + GRAPH graph, OUT VECTOR res, + BOOLEAN directed=True, + OUT REAL centralization, + OUT REAL theoretical_max, + BOOLEAN normalized=True + +igraph_centralization_betweenness_tmax: + PARAMS: |- + OPTIONAL GRAPH graph, INTEGER nodes=0, + BOOLEAN directed=True, OUT REAL res + +igraph_centralization_closeness: + PARAMS: |- + GRAPH graph, OUT VECTOR res, + NEIMODE mode=OUT, OUT REAL centralization, + OUT REAL theoretical_max, + BOOLEAN normalized=True + +igraph_centralization_closeness_tmax: + PARAMS: |- + OPTIONAL GRAPH graph, INTEGER nodes=0, + NEIMODE mode=OUT, OUT REAL res + +igraph_centralization_eigenvector_centrality: + PARAMS: |- + GRAPH graph, OUT VECTOR vector, OUT REAL value, + BOOLEAN directed=False, BOOLEAN scale=True, + INOUT ARPACKOPT options=ARPACK_DEFAULTS, + OUT REAL centralization, OUT REAL theoretical_max, + BOOLEAN normalized=True + +igraph_centralization_eigenvector_centrality_tmax: + PARAMS: |- + OPTIONAL GRAPH graph, INTEGER nodes=0, + BOOLEAN directed=False, BOOLEAN scale=True, + OUT REAL res + +igraph_assortativity_nominal: + PARAMS: |- + GRAPH graph, INDEX_VECTOR types, OUT REAL res, + BOOLEAN directed=True, BOOLEAN normalized=True + +igraph_assortativity: + PARAMS: |- + GRAPH graph, VECTOR values, OPTIONAL VECTOR values_in, + OUT REAL res, BOOLEAN directed=True, BOOLEAN normalized=True + +igraph_assortativity_degree: + PARAMS: GRAPH graph, OUT REAL res, BOOLEAN directed=True + +igraph_joint_degree_matrix: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT MATRIX jdm, + INTEGER max_out_degree=-1, INTEGER max_in_degree=-1 + DEPS: weights ON graph + +igraph_joint_degree_distribution: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT MATRIX p, + NEIMODE from_mode=OUT, NEIMODE to_mode=IN, + BOOLEAN directed_neighbors=True, + BOOLEAN normalized=True, + INTEGER max_from_degree=-1, INTEGER max_to_degree=-1 + DEPS: weights ON graph + +igraph_joint_type_distribution: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT MATRIX p, + INDEX_VECTOR from_types, INDEX_VECTOR to_types=NULL, + BOOLEAN directed=True, + BOOLEAN normalized=True + DEPS: weights ON graph + +igraph_contract_vertices: + PARAMS: |- + INOUT GRAPH graph, INDEX_VECTOR mapping, + VERTEX_ATTRIBUTE_COMBINATION vertex_attr_comb=Default + +igraph_eccentricity: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + NEIMODE mode=ALL + DEPS: vids ON graph, res ON graph vids + +igraph_eccentricity_dijkstra: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + NEIMODE mode=ALL + DEPS: weights ON graph, vids ON graph, res ON graph vids + +igraph_graph_center: + PARAMS: |- + GRAPH graph, OUT VERTEX_INDICES res, NEIMODE mode=ALL + DEPS: res ON graph + +igraph_graph_center_dijkstra: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEX_INDICES res, NEIMODE mode=ALL + DEPS: weights ON graph, res ON graph + +igraph_radius: + PARAMS: GRAPH graph, OUT REAL radius, NEIMODE mode=ALL + +igraph_radius_dijkstra: + PARAMS: GRAPH graph, EDGEWEIGHTS weights=NULL, OUT REAL radius, NEIMODE mode=ALL + DEPS: weights ON graph + +igraph_pseudo_diameter: + PARAMS: |- + GRAPH graph, OUT REAL diameter, VERTEX start_vid, + OUT INTEGER from=NULL, OUT INTEGER to=NULL, + BOOLEAN directed=True, BOOLEAN unconnected=True + +igraph_pseudo_diameter_dijkstra: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT REAL diameter, VERTEX start_vid, + OUT INTEGER from=NULL, OUT INTEGER to=NULL, + BOOLEAN directed=True, BOOLEAN unconnected=True + DEPS: weights ON graph + +igraph_diversity: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEX_QTY res, + VERTEX_SELECTOR vids=ALL + DEPS: weights ON graph, vids ON graph, res ON graph vids + +igraph_random_walk: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEX_INDICES vertices, OUT EDGE_INDICES edges, + VERTEX start, NEIMODE mode=OUT, INTEGER steps, RWSTUCK stuck=RETURN + DEPS: start ON graph, weights ON graph, vertices ON graph, edges ON graph + +igraph_random_edge_walk: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT EDGE_INDICES edgewalk, + VERTEX start, NEIMODE mode=OUT, INTEGER steps, RWSTUCK stuck=RETURN + DEPS: start ON graph, weights ON graph, edgewalk ON graph + +igraph_global_efficiency: + PARAMS: GRAPH graph, OUT REAL res, EDGEWEIGHTS weights=NULL, BOOLEAN directed=True + DEPS: weights ON graph + +igraph_local_efficiency: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + EDGEWEIGHTS weights=NULL, BOOLEAN directed=True, NEIMODE mode=ALL + DEPS: vids ON graph, weights ON graph, res ON graph vids + +igraph_average_local_efficiency: + PARAMS: |- + GRAPH graph, OUT REAL res, EDGEWEIGHTS weights=NULL, + BOOLEAN directed=True, NEIMODE mode=ALL + DEPS: weights ON graph + +igraph_transitive_closure_dag: + PARAMS: GRAPH graph, OUT GRAPH closure + +igraph_transitive_closure: + PARAMS: GRAPH graph, OUT GRAPH closure + +igraph_trussness: + PARAMS: GRAPH graph, OUT VECTOR_INT trussness + +####################################### +# Degree sequences +####################################### + +igraph_is_bigraphical: + PARAMS: |- + VECTOR_INT degrees1, VECTOR_INT degrees2, + EDGE_TYPE_SW allowed_edge_types=SIMPLE, OUT BOOLEAN res + +igraph_is_graphical: + PARAMS: |- + VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg, + EDGE_TYPE_SW allowed_edge_types=SIMPLE, OUT BOOLEAN res + +####################################### +# Visitors +####################################### + +igraph_bfs: + PARAMS: |- + GRAPH graph, VERTEX root, OPTIONAL VERTEX_INDICES roots, + NEIMODE mode=OUT, BOOLEAN unreachable, + VERTEX_INDICES restricted, + OUT VERTEX_INDICES order, OUT VECTOR_INT rank, + OUT VECTOR_INT parents, + OUT VECTOR_INT pred, OUT VECTOR_INT succ, + OUT VECTOR_INT dist, BFS_FUNC callback, EXTRA extra + +igraph_bfs_simple: + PARAMS: |- + GRAPH graph, VERTEX root, + NEIMODE mode=OUT, + OUT VERTEX_INDICES order, + OUT VECTOR_INT layers, + OUT VECTOR_INT parents + +igraph_dfs: + PARAMS: |- + GRAPH graph, VERTEX root, NEIMODE mode=OUT, BOOLEAN unreachable, + OUT VERTEX_INDICES order, OUT VERTEX_INDICES order_out, + OUT VECTOR_INT father, OUT VECTOR_INT dist, + DFS_FUNC in_callback, DFS_FUNC out_callback, EXTRA extra + +####################################### +# Bipartite graphs +####################################### + +igraph_bipartite_projection_size: + PARAMS: |- + GRAPH graph, BIPARTITE_TYPES types=NULL, + OUT INTEGER vcount1, OUT INTEGER ecount1, + OUT INTEGER vcount2, OUT INTEGER ecount2 + DEPS: types ON graph + +igraph_bipartite_projection: + PARAMS: |- + GRAPH graph, BIPARTITE_TYPES types=NULL, + OUT GRAPH proj1, OUT GRAPH proj2, + OPTIONAL OUT VECTOR_INT multiplicity1, + OPTIONAL OUT VECTOR_INT multiplicity2, INTEGER probe1=-1 + DEPS: types ON graph + +igraph_create_bipartite: + PARAMS: |- + OUT GRAPH graph, IN BIPARTITE_TYPES types, + VECTOR_INT edges, BOOLEAN directed=False + +igraph_biadjacency: + PARAMS: |- + OUT GRAPH graph, OUT BIPARTITE_TYPES types, MATRIX incidence, + BOOLEAN directed=False, NEIMODE mode=ALL, + BOOLEAN multiple=False + +igraph_get_biadjacency: + PARAMS: |- + GRAPH graph, BIPARTITE_TYPES types=NULL, OUT MATRIX res, + OPTIONAL OUT INDEX_VECTOR row_ids, OPTIONAL OUT INDEX_VECTOR col_ids + DEPS: types ON graph + +igraph_is_bipartite: + PARAMS: GRAPH graph, OUT BOOLEAN res, OPTIONAL OUT BIPARTITE_TYPES type + +igraph_bipartite_game_gnp: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, + INTEGER n1, INTEGER n2, REAL p, BOOLEAN directed=False, + NEIMODE mode=ALL + +igraph_bipartite_game_gnm: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, + INTEGER n1, INTEGER n2, INTEGER m, BOOLEAN directed=False, + NEIMODE mode=ALL + +igraph_bipartite_game: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, + ERDOS_RENYI_TYPE type, INTEGER n1, INTEGER n2, REAL p=0.0, + INTEGER m=0, BOOLEAN directed=False, NEIMODE mode=ALL + + +####################################### +# Spectral properties +####################################### + +igraph_get_laplacian: + PARAMS: |- + GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, + LAPLACIAN_NORMALIZATION normalization=UNNORMALIZED, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph + +igraph_get_laplacian_sparse: + PARAMS: |- + GRAPH graph, OUT SPARSEMAT sparseres, NEIMODE mode=OUT, + LAPLACIAN_NORMALIZATION normalization=UNNORMALIZED, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph + +####################################### +# Components +####################################### + +igraph_connected_components: + PARAMS: |- + GRAPH graph, PRIMARY OUT VECTOR_INT membership, OUT VECTOR_INT csize, + OUT INTEGER no, CONNECTEDNESS mode=WEAK + +igraph_is_connected: + PARAMS: GRAPH graph, OUT BOOLEAN res, CONNECTEDNESS mode=WEAK + +igraph_decompose: + PARAMS: |- + GRAPH graph, OUT GRAPH_LIST components, CONNECTEDNESS mode=WEAK, + INTEGER maxcompno=-1, INTEGER minelements=1 + +igraph_articulation_points: + PARAMS: GRAPH graph, OUT VERTEX_INDICES res + DEPS: res ON graph + +igraph_biconnected_components: + PARAMS: |- + GRAPH graph, OUT INTEGER no, + OPTIONAL OUT EDGESET_LIST tree_edges, + OPTIONAL OUT EDGESET_LIST component_edges, + OPTIONAL OUT VERTEXSET_LIST components, + OUT VERTEX_INDICES articulation_points + DEPS: |- + tree_edges ON graph, component_edges ON graph, + components ON graph, articulation_points ON graph + +igraph_bridges: + PARAMS: GRAPH graph, OUT EDGE_INDICES res + DEPS: res ON graph + +igraph_is_biconnected: + PARAMS: GRAPH graph, OUT BOOLEAN res + +igraph_count_reachable: + PARAMS: GRAPH graph, OUT VECTOR_INT counts, NEIMODE mode + +####################################### +# Cliques +####################################### + +igraph_is_clique: + PARAMS: |- + GRAPH graph, VERTEX_SELECTOR candidate, BOOLEAN directed=False, + OUT BOOLEAN res + DEPS: candidate ON graph + +igraph_cliques: + PARAMS: |- + GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, + INTEGER max_size=0 + DEPS: res ON graph + +igraph_cliques_callback: + PARAMS: |- + GRAPH graph, INTEGER min_size=0, INTEGER max_size=0, + CLIQUE_FUNC cliquehandler_fn, EXTRA arg + +igraph_clique_size_hist: + PARAMS: |- + GRAPH graph, OUT VECTOR hist, INTEGER min_size=0, INTEGER max_size=0 + +igraph_largest_cliques: + PARAMS: GRAPH graph, OUT VERTEXSET_LIST res + DEPS: res ON graph + +igraph_maximal_cliques: + PARAMS: GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, INTEGER max_size=0 + DEPS: res ON graph + +igraph_maximal_cliques_subset: + PARAMS: |- + GRAPH graph, VERTEX_INDICES subset, PRIMARY OUT VERTEXSET_LIST res, + OUT INTEGER no, OUTFILE outfile=NULL, INTEGER min_size=0, INTEGER max_size=0 + DEPS: subset ON graph, res ON graph + +igraph_maximal_cliques_callback: + PARAMS: |- + GRAPH graph, CLIQUE_FUNC cliquehandler_fn, EXTRA arg, + INTEGER min_size=0, INTEGER max_size=0 + +igraph_maximal_cliques_count: + PARAMS: |- + GRAPH graph, OUT INTEGER no, INTEGER min_size=0, INTEGER max_size=0 + +igraph_maximal_cliques_file: + PARAMS: |- + GRAPH graph, OUTFILE res, INTEGER min_size=0, INTEGER max_size=0 + +igraph_maximal_cliques_hist: + PARAMS: |- + GRAPH graph, OUT VECTOR hist, INTEGER min_size=0, INTEGER max_size=0 + +igraph_clique_number: + PARAMS: GRAPH graph, OUT INTEGER no + +igraph_weighted_cliques: + PARAMS: |- + GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSET_LIST res, + REAL min_weight=0, REAL max_weight=0, BOOLEAN maximal=False + DEPS: vertex_weights ON graph, res ON graph + +igraph_largest_weighted_cliques: + PARAMS: |- + GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSET_LIST res + DEPS: vertex_weights ON graph, res ON graph + +igraph_weighted_clique_number: + PARAMS: GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT REAL res + DEPS: vertex_weights ON graph + +igraph_is_independent_vertex_set: + PARAMS: GRAPH graph, VERTEX_SELECTOR candidate, OUT BOOLEAN res + DEPS: candidate ON graph + +igraph_independent_vertex_sets: + PARAMS: |- + GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, + INTEGER max_size=0 + DEPS: res ON graph + +igraph_largest_independent_vertex_sets: + PARAMS: GRAPH graph, OUT VERTEXSET_LIST res + DEPS: res ON graph + +igraph_maximal_independent_vertex_sets: + PARAMS: GRAPH graph, OUT VERTEXSET_LIST res + DEPS: res ON graph + +igraph_independence_number: + PARAMS: GRAPH graph, OUT INTEGER no + +####################################### +# Layouts +####################################### + +igraph_layout_random: + PARAMS: GRAPH graph, OUT MATRIX res + +igraph_layout_circle: + PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR order=ALL + DEPS: order ON graph + +igraph_layout_star: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX center=V(graph)[1], + OPTIONAL INDEX_VECTOR order=NULL + DEPS: center ON graph + +igraph_layout_grid: + PARAMS: GRAPH graph, OUT MATRIX res, INTEGER width=0 + +igraph_layout_grid_3d: + PARAMS: GRAPH graph, OUT MATRIX res, INTEGER width=0, INTEGER height=0 + +igraph_layout_fruchterman_reingold: + PARAMS: |- + GRAPH graph, INOUT MATRIX coords=NULL, + BOOLEAN use_seed=False, INTEGER niter=500, + REAL start_temp=sqrt(vcount(graph)), + LAYOUT_GRID grid=AUTO, EDGEWEIGHTS weights=NULL, + OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, + OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, + DEPRECATED coolexp, DEPRECATED maxdelta, DEPRECATED area, + DEPRECATED repulserad + DEPS: weights ON graph + +igraph_layout_kamada_kawai: + PARAMS: |- + GRAPH graph, INOUT MATRIX coords, BOOLEAN use_seed=False, + INTEGER maxiter=500, REAL epsilon=0.0, + REAL kkconst=vcount(graph), EDGEWEIGHTS weights=NULL, + OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, + OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy + DEPS: weights ON graph + +igraph_layout_lgl: + PARAMS: |- + GRAPH graph, OUT MATRIX res, INTEGER maxiter=150, REAL maxdelta=VCOUNT(graph), + REAL area=VCOUNT(graph)^2, REAL coolexp=1.5, REAL repulserad=VCOUNT(graph)^3, REAL cellsize=VCOUNT(graph), + INTEGER root=-1 + +igraph_layout_reingold_tilford: + PARAMS: |- + GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, + OPTIONAL VERTEX_INDICES roots, OPTIONAL VECTOR_INT rootlevel + +igraph_layout_reingold_tilford_circular: + PARAMS: |- + GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, + OPTIONAL VERTEX_INDICES roots, OPTIONAL VECTOR_INT rootlevel + +igraph_roots_for_tree_layout: + PARAMS: |- + GRAPH graph, NEIMODE mode=OUT, OUT VERTEX_INDICES roots, ROOTCHOICE heuristic + DEPS: roots ON graph + +igraph_layout_random_3d: + PARAMS: GRAPH graph, OUT MATRIX res + +igraph_layout_sphere: + PARAMS: GRAPH graph, OUT MATRIX res + +igraph_layout_fruchterman_reingold_3d: + PARAMS: |- + GRAPH graph, INOUT MATRIX coords=NULL, + BOOLEAN use_seed=False, INTEGER niter=500, + REAL start_temp=sqrt(vcount(graph)), + EDGEWEIGHTS weights=NULL, + OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, + OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, + OPTIONAL VECTOR minz, OPTIONAL VECTOR maxz, + DEPRECATED coolexp, DEPRECATED maxdelta, DEPRECATED area, + DEPRECATED repulserad + DEPS: weights ON graph + +igraph_layout_kamada_kawai_3d: + PARAMS: |- + GRAPH graph, INOUT MATRIX coords, BOOLEAN use_seed=False, + INTEGER maxiter=500, REAL epsilon=0.0, + REAL kkconst=vcount(graph), EDGEWEIGHTS weights=NULL, + OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, + OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, + OPTIONAL VECTOR minz, OPTIONAL VECTOR maxz + DEPS: weights ON graph + +igraph_layout_graphopt: + PARAMS: |- + GRAPH graph, INOUT MATRIX res, INTEGER niter=500, + REAL node_charge=0.001, REAL node_mass=30, + REAL spring_length=0, REAL spring_constant=1, + REAL max_sa_movement=5, BOOLEAN use_seed=False + +igraph_layout_drl: + PARAMS: |- + GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, + DRL_OPTIONS options=drl_defaults$default, OPTIONAL EDGEWEIGHTS weights + +igraph_layout_drl_3d: + PARAMS: |- + GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, + DRL_OPTIONS options=drl_defaults$default, OPTIONAL EDGEWEIGHTS weights + +igraph_layout_merge_dla: + PARAMS: GRAPH_PTR_LIST graphs, MATRIX_LIST coords, OUT MATRIX res + +igraph_layout_sugiyama: + PARAMS: |- + GRAPH graph, OUT MATRIX res, OPTIONAL OUT GRAPH extd_graph, + OPTIONAL OUT INDEX_VECTOR extd_to_orig_eids, + OPTIONAL INDEX_VECTOR layers=NULL, + REAL hgap=1, REAL vgap=1, INTEGER maxiter=100, + EDGEWEIGHTS weights=NULL + DEPS: weights ON graph + +igraph_layout_mds: + PARAMS: |- + GRAPH graph, OUT MATRIX res, OPTIONAL MATRIX dist, INTEGER dim=2 + +igraph_layout_bipartite: + PARAMS: |- + GRAPH graph, BIPARTITE_TYPES types=NULL, OUT MATRIX res, + REAL hgap=1, REAL vgap=1, INTEGER maxiter=100 + DEPS: types ON graph + +igraph_layout_gem: + PARAMS: |- + GRAPH graph, INOUT MATRIX res=matrix(), + BOOLEAN use_seed=False, + INTEGER maxiter=40*vcount(graph)^2, + REAL temp_max=vcount(graph), + REAL temp_min=1/10, REAL temp_init=sqrt(vcount(graph)) + +igraph_layout_davidson_harel: + PARAMS: |- + GRAPH graph, INOUT MATRIX res=matrix(), + BOOLEAN use_seed=False, INTEGER maxiter=10, + INTEGER fineiter=FINEITER, REAL cool_fact=0.75, + REAL weight_node_dist=1.0, REAL weight_border=0.0, + REAL weight_edge_lengths=ELENW, + REAL weight_edge_crossings=ECROSSW, + REAL weight_node_edge_dist=NEDISTW + +igraph_layout_umap: + PARAMS: |- + GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, + OPTIONAL VECTOR distances=NULL, REAL min_dist=0.0, INTEGER epochs=200, + BOOLEAN distances_are_weights=False + +igraph_layout_umap_3d: + PARAMS: |- + GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, + OPTIONAL VECTOR distances=NULL, REAL min_dist=0.0, INTEGER epochs=200, + BOOLEAN distances_are_weights=False + +igraph_layout_umap_compute_weights: + PARAMS: |- + GRAPH graph, VECTOR distances, INOUT VECTOR weights + +####################################### +# Cocitation and other similarity measures +####################################### + +igraph_cocitation: + PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL + DEPS: vids ON graph + +igraph_bibcoupling: + PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL + DEPS: vids ON graph + +igraph_similarity_dice: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: vids ON graph + +igraph_similarity_dice_es: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGE_SELECTOR es=ALL, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: es ON graph + +igraph_similarity_dice_pairs: + PARAMS: |- + GRAPH graph, OUT VECTOR res, VERTEX_INDEX_PAIRS pairs, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: pairs ON graph + +igraph_similarity_inverse_log_weighted: + PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL + DEPS: vids ON graph + +igraph_similarity_jaccard: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: vids ON graph res, mode ON vids + +igraph_similarity_jaccard_es: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGE_SELECTOR es=ALL, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: es ON graph + +igraph_similarity_jaccard_pairs: + PARAMS: |- + GRAPH graph, OUT VECTOR res, VERTEX_INDEX_PAIRS pairs, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: pairs ON graph + +####################################### +# Community structure +####################################### + +igraph_compare_communities: + PARAMS: |- + VECTOR_INT comm1, VECTOR_INT comm2, OUT REAL res, COMMCMP method=VI + +igraph_community_spinglass: + PARAMS: |- + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, OUT REAL modularity, + OUT REAL temperature, OUT VECTOR_INT membership, OUT VECTOR_INT csize, + INTEGER spins=25, BOOLEAN parupdate=False, REAL starttemp=1, REAL stoptemp=0.01, + REAL coolfact=0.99, SPINCOMMUPDATE update_rule=CONFIG, REAL gamma=1.0, + SPINGLASS_IMPLEMENTATION implementation=ORIG, REAL lambda=1.0 + DEPS: weights ON graph + +igraph_community_spinglass_single: + PARAMS: |- + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, INTEGER vertex, + OUT VECTOR_INT community, OUT REAL cohesion, OUT REAL adhesion, + OUT INTEGER inner_links, OUT INTEGER outer_links, + INTEGER spins=25, SPINCOMMUPDATE update_rule=CONFIG, REAL gamma=1.0 + DEPS: weights ON graph + +igraph_community_walktrap: + PARAMS: |- + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, INTEGER steps=4, + OUT MATRIX_INT merges, OUT VECTOR modularity, OUT VECTOR_INT membership + DEPS: weights ON graph + +igraph_community_edge_betweenness: + PARAMS: |- + GRAPH graph, OUT VECTOR_INT removed_edges, OPTIONAL OUT VECTOR edge_betweenness, + OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT INDEX_VECTOR bridges, + OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership, + BOOLEAN directed=True, OPTIONAL EDGEWEIGHTS weights=NULL + DEPS: weights ON graph + +igraph_community_eb_get_merges: + PARAMS: |- + GRAPH graph, BOOLEAN directed, EDGE_INDICES edges, OPTIONAL EDGEWEIGHTS weights, + OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT INDEX_VECTOR bridges, + OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership + DEPS: weights ON graph + +igraph_community_fastgreedy: + PARAMS: |- + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, OUT MATRIX_INT merges, + OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership + DEPS: weights ON graph + +igraph_community_to_membership: + PARAMS: |- + MATRIX_INT merges, INTEGER nodes, INTEGER steps, + OPTIONAL OUT VECTOR_INT membership, OPTIONAL OUT VECTOR_INT csize + +igraph_le_community_to_membership: + PARAMS: |- + MATRIX_INT merges, INTEGER steps, INOUT VECTOR_INT membership, + OPTIONAL OUT VECTOR_INT csize + +igraph_modularity: + PARAMS: |- + GRAPH graph, VECTOR_INT membership, OPTIONAL EDGEWEIGHTS weights=NULL, + REAL resolution=1.0, BOOLEAN directed=True, OUT REAL modularity + DEPS: weights ON graph + +igraph_modularity_matrix: + PARAMS: |- + GRAPH graph, + OPTIONAL EDGEWEIGHTS weights, + REAL resolution=1.0, + OUT MATRIX modmat, + BOOLEAN directed=True + DEPS: weights ON graph + +igraph_reindex_membership: + PARAMS: |- + INOUT VECTOR_INT membership, OUT INDEX_VECTOR new_to_old, + OUT INTEGER nb_clusters + +igraph_community_leading_eigenvector: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT VECTOR_INT membership, + INTEGER steps=-1, + INOUT ARPACKOPT options=ARPACK_DEFAULTS, + OPTIONAL OUT REAL modularity, BOOLEAN start=False, + OPTIONAL OUT VECTOR eigenvalues, + OPTIONAL OUT VECTOR_LIST eigenvectors, + OPTIONAL OUT VECTOR history, + LEVCFUNC callback, EXTRA callback_extra + +igraph_community_fluid_communities: + PARAMS: |- + GRAPH graph, INTEGER no_of_communities, OUT VECTOR_INT membership + +igraph_community_label_propagation: + PARAMS: |- + GRAPH graph, OUT VECTOR_INT membership, NEIMODE mode=ALL, + OPTIONAL EDGEWEIGHTS weights, OPTIONAL INDEX_VECTOR initial, + OPTIONAL VECTOR_BOOL fixed + DEPS: weights ON graph + +igraph_community_multilevel: + PARAMS: |- + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, REAL resolution=1.0, + OUT VECTOR_INT membership, OPTIONAL OUT MATRIX_INT memberships, + OPTIONAL OUT VECTOR modularity + DEPS: weights ON graph + +igraph_community_optimal_modularity: + PARAMS: |- + GRAPH graph, OUT REAL modularity, OPTIONAL OUT VECTOR_INT membership, + OPTIONAL EDGEWEIGHTS weights + DEPS: weights ON graph + +igraph_community_leiden: + PARAMS: |- + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, + OPTIONAL VERTEXWEIGHTS vertex_weights, + REAL resolution, REAL beta=0.01, BOOLEAN start, INTEGER n_iterations=2, + OPTIONAL INOUT VECTOR_INT membership, + OUT INTEGER nb_clusters, OUT REAL quality + DEPS: weights ON graph, vertex_weights ON graph + +igraph_split_join_distance: + PARAMS: |- + VECTOR_INT comm1, VECTOR_INT comm2, OUT INTEGER distance12, + OUT INTEGER distance21 + +igraph_community_infomap: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS e_weights=NULL, + VERTEXWEIGHTS v_weights=NULL, INTEGER nb_trials=10, + OUT VECTOR_INT membership, OUT REAL codelength + DEPS: e_weights ON graph, v_weights ON graph + +igraph_community_voronoi: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VECTOR_INT membership, + OPTIONAL OUT VERTEX_INDICES generators, + OPTIONAL OUT REAL modularity, + OPTIONAL EDGE_LENGTHS lengths, OPTIONAL EDGEWEIGHTS weights, + NEIMODE mode=OUT, REAL radius=-1 + DEPS: generators ON graph, weights ON graph, lengths ON graph + +####################################### +# Graphlets +####################################### + +igraph_graphlets: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VERTEXSET_LIST cliques, OUT VECTOR Mu, INTEGER niter=1000 + DEPS: weights ON graph, cliques ON graph + +igraph_graphlets_candidate_basis: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VERTEXSET_LIST cliques, OUT VECTOR thresholds + DEPS: weights ON graph, cliques ON graph + +igraph_graphlets_project: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + VERTEXSET_LIST cliques, INOUT VECTOR Muc, + BOOLEAN startMu=False, INTEGER niter=1000 + DEPS: weights ON graph + +####################################### +# Hierarchical random graphs +####################################### + +igraph_hrg_fit: + PARAMS: |- + GRAPH graph, INOUT HRG hrg=Default, BOOLEAN start=False, + INTEGER steps=0 + +igraph_hrg_sample: + PARAMS: HRG hrg, OUT GRAPH sample + +igraph_hrg_sample_many: + PARAMS: HRG hrg, OUT GRAPH_LIST samples, INTEGER num_samples + +igraph_hrg_game: + PARAMS: OUT GRAPH graph, HRG hrg + +igraph_hrg_consensus: + PARAMS: |- + GRAPH graph, OUT VECTOR_INT parents, OUT VECTOR weights, + INOUT HRG hrg=Default, BOOLEAN start=False, + INTEGER num_samples=10000 + +igraph_hrg_predict: + PARAMS: |- + GRAPH graph, OUT VERTEX_INDICES edges, OUT VECTOR prob, + INOUT HRG hrg=Default, BOOLEAN start=False, + INTEGER num_samples=10000, INTEGER num_bins=25 + DEPS: edges ON graph + +igraph_hrg_create: + PARAMS: OUT HRG hrg, GRAPH graph, VECTOR prob + DEPS: prob ON graph + +igraph_hrg_resize: + PARAMS: INOUT HRG hrg, INTEGER newsize + +igraph_hrg_size: + PARAMS: HRG hrg + RETURN: INTEGER + +igraph_from_hrg_dendrogram: + PARAMS: OUT GRAPH graph, HRG hrg, OUT VECTOR prob + +####################################### +# Conversion +####################################### + +igraph_get_adjacency: + PARAMS: |- + GRAPH graph, OUT MATRIX res, GETADJACENCY type=BOTH, + EDGEWEIGHTS weights=NULL, LOOPS loops=ONCE + DEPS: + weights ON graph + +igraph_get_adjacency_sparse: + PARAMS: |- + GRAPH graph, OUT SPARSEMAT sparsemat, GETADJACENCY type=BOTH, + EDGEWEIGHTS weights=NULL, LOOPS loops=ONCE + DEPS: + weights ON graph + +igraph_get_edgelist: + PARAMS: GRAPH graph, OUT VECTOR_INT res, BOOLEAN bycol=False + +igraph_get_stochastic: + PARAMS: |- + GRAPH graph, OUT MATRIX res, BOOLEAN column_wise=False, + EDGEWEIGHTS weights=NULL + DEPS: + weights ON graph + +igraph_get_stochastic_sparse: + PARAMS: |- + GRAPH graph, OUT SPARSEMAT sparsemat, BOOLEAN column_wise=False, + EDGEWEIGHTS weights=NULL + DEPS: + weights ON graph + +igraph_to_directed: + PARAMS: INOUT GRAPH graph, TODIRECTED mode=MUTUAL + +igraph_to_undirected: + PARAMS: |- + INOUT GRAPH graph, TOUNDIRECTED mode=COLLAPSE, + EDGE_ATTRIBUTE_COMBINATION edge_attr_comb=Default + +####################################### +# Read and write foreign formats +####################################### + +igraph_read_graph_edgelist: + PARAMS: OUT GRAPH graph, INFILE instream, INTEGER n=0, BOOLEAN directed=True + +igraph_read_graph_ncol: + PARAMS: |- + OUT GRAPH graph, INFILE instream, OPTIONAL VECTOR_STR predefnames, + BOOLEAN names=True, ADD_WEIGHTS weights=True, BOOLEAN directed=True + +igraph_read_graph_lgl: + PARAMS: |- + OUT GRAPH graph, INFILE instream, BOOLEAN names=True, + ADD_WEIGHTS weights=True, BOOLEAN directed=True + +igraph_read_graph_pajek: + PARAMS: OUT GRAPH graph, INFILE instream + +igraph_read_graph_graphml: + PARAMS: OUT GRAPH graph, INFILE instream, INTEGER index=0 + +igraph_read_graph_dimacs_flow: + PARAMS: |- + OUT GRAPH graph, INFILE instream, + OPTIONAL OUT VECTOR_STR problem, OPTIONAL OUT VECTOR_INT label, + OPTIONAL OUT INTEGER source, OPTIONAL OUT INTEGER target, + OPTIONAL OUT VECTOR capacity, BOOLEAN directed=True + +igraph_read_graph_graphdb: + PARAMS: OUT GRAPH graph, INFILE instream, BOOLEAN directed=False + +igraph_read_graph_gml: + PARAMS: OUT GRAPH graph, INFILE instream + +igraph_read_graph_dl: + PARAMS: OUT GRAPH graph, INFILE instream, BOOLEAN directed=True + +igraph_write_graph_edgelist: + PARAMS: GRAPH graph, OUTFILE outstream + +igraph_write_graph_ncol: + PARAMS: GRAPH graph, OUTFILE outstream, CSTRING names="name", CSTRING weights="weight" + +igraph_write_graph_lgl: + PARAMS: |- + GRAPH graph, OUTFILE outstream, CSTRING names="name", CSTRING weights="weight", + BOOLEAN isolates=True + +igraph_write_graph_leda: + PARAMS: GRAPH graph, OUTFILE outstream, CSTRING names="name", CSTRING weights="weight" + +igraph_write_graph_graphml: + PARAMS: GRAPH graph, OUTFILE outstream, BOOLEAN prefixattr=True + +igraph_write_graph_pajek: + PARAMS: GRAPH graph, OUTFILE outstream + +igraph_write_graph_dimacs_flow: + PARAMS: |- + GRAPH graph, OUTFILE outstream, VERTEX source=0, VERTEX target=0, + VECTOR capacity + +igraph_write_graph_gml: + PARAMS: GRAPH graph, OUTFILE outstream, WRITE_GML_SW options=DEFAULT, VECTOR id, CSTRING creator=NULL + +igraph_write_graph_dot: + PARAMS: GRAPH graph, OUTFILE outstream + +####################################### +# Motifs +####################################### + +igraph_motifs_randesu: + PARAMS: GRAPH graph, OUT VECTOR hist, INTEGER size=3, VECTOR cut_prob + +igraph_motifs_randesu_estimate: + PARAMS: |- + GRAPH graph, OUT INTEGER est, INTEGER size=3, VECTOR cut_prob, + INTEGER sample_size, OPTIONAL VECTOR_INT sample + +igraph_motifs_randesu_no: + PARAMS: GRAPH graph, OUT INTEGER no, INTEGER size=3, VECTOR cut_prob + +igraph_dyad_census: + PARAMS: GRAPH graph, OUT REAL mut, OUT REAL asym, OUT REAL null + RETURN: ERROR + +igraph_triad_census: + PARAMS: GRAPH graph, OUT VECTOR res + RETURN: ERROR + +igraph_adjacent_triangles: + PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL + DEPS: vids ON graph + +igraph_local_scan_0: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL, + NEIMODE mode=OUT + DEPS: weights ON graph + +igraph_local_scan_0_them: + PARAMS: |- + GRAPH us, GRAPH them, OUT VECTOR res, + EDGEWEIGHTS weights_them=NULL, NEIMODE mode=OUT + DEPS: weights_them ON them + +igraph_local_scan_1_ecount: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL, + NEIMODE mode=OUT + DEPS: weights ON graph + +igraph_local_scan_1_ecount_them: + PARAMS: |- + GRAPH us, GRAPH them, OUT VECTOR res, + EDGEWEIGHTS weights_them=NULL, NEIMODE mode=OUT + DEPS: weights_them ON them + +igraph_local_scan_k_ecount: + PARAMS: |- + GRAPH graph, INTEGER k, OUT VECTOR res, EDGEWEIGHTS weights=NULL, + NEIMODE mode=OUT + DEPS: weights ON graph + +igraph_local_scan_k_ecount_them: + PARAMS: |- + GRAPH us, GRAPH them, INTEGER k, OUT VECTOR res, + EDGEWEIGHTS weights_them=NULL, NEIMODE mode=OUT + DEPS: weights_them ON them + +igraph_local_scan_neighborhood_ecount: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL, + VERTEXSET_LIST neighborhoods + DEPS: weights ON graph + +igraph_local_scan_subset_ecount: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL, + VERTEXSET_LIST subsets + DEPS: weights ON graph + +igraph_list_triangles: + PARAMS: GRAPH graph, OUT VERTEX_INDICES res + DEPS: res ON graph + +####################################### +# Graph operators +####################################### + +igraph_disjoint_union: + PARAMS: OUT GRAPH res, GRAPH left, GRAPH right + +igraph_disjoint_union_many: + PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs + +igraph_join: + PARAMS: OUT GRAPH res, GRAPH left, GRAPH right + +igraph_union: + PARAMS: |- + OUT GRAPH res, GRAPH left, GRAPH right, + OUT INDEX_VECTOR edge_map_left, OUT INDEX_VECTOR edge_map_right + DEPS: edge_map_left ON left, edge_map_right ON right + +igraph_union_many: + PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs, OUT VECTOR_INT_LIST edgemaps + +igraph_intersection: + PARAMS: |- + OUT GRAPH res, GRAPH left, GRAPH right, + OUT INDEX_VECTOR edge_map_left, OUT INDEX_VECTOR edge_map_right + DEPS: edge_map_left ON left, edge_map_right ON right + +igraph_intersection_many: + PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs, OUT VECTOR_INT_LIST edgemaps + +igraph_difference: + PARAMS: OUT GRAPH res, GRAPH orig, GRAPH sub + +igraph_complementer: + PARAMS: OUT GRAPH res, GRAPH graph, BOOLEAN loops=False + +igraph_compose: + PARAMS: |- + OUT GRAPH res, GRAPH g1, GRAPH g2, + OUT INDEX_VECTOR edge_map1, OUT INDEX_VECTOR edge_map2 + DEPS: edge_map1 ON g1, edge_map2 ON g2 + +igraph_induced_subgraph_map: + PARAMS: |- + GRAPH graph, OUT GRAPH res, VERTEX_SELECTOR vids, SUBGRAPH_IMPL impl, + OPTIONAL OUT INDEX_VECTOR map, OPTIONAL OUT INDEX_VECTOR invmap + DEPS: vids ON graph + +####################################### +# Maximum flows, minimum cuts +####################################### + +igraph_gomory_hu_tree: + PARAMS: GRAPH graph, OUT GRAPH tree, OPTIONAL OUT VECTOR flows, OPTIONAL EDGE_CAPACITY capacity + DEPS: capacity ON graph + +igraph_maxflow: + PARAMS: |- + GRAPH graph, OUT REAL value, OPTIONAL OUT VECTOR flow, + OUT EDGE_INDICES cut, OPTIONAL OUT VERTEX_INDICES partition1, + OPTIONAL OUT VERTEX_INDICES partition2, VERTEX source, VERTEX target, + OPTIONAL EDGE_CAPACITY capacity, OPTIONAL OUT MAXFLOW_STATS stats + DEPS: |- + capacity ON graph, source ON graph, target ON graph, + partition1 ON graph, partition2 ON graph, flow ON graph, + cut ON graph + +igraph_maxflow_value: + PARAMS: |- + GRAPH graph, OUT REAL value, VERTEX source, VERTEX target, + OPTIONAL EDGE_CAPACITY capacity, OPTIONAL OUT MAXFLOW_STATS stats + DEPS: source ON graph, target ON graph, capacity ON graph + +igraph_mincut: + PARAMS: |- + GRAPH graph, OUT REAL value, OUT VERTEX_INDICES partition1, + OUT VERTEX_INDICES partition2, OUT EDGE_INDICES cut, + OPTIONAL EDGE_CAPACITY capacity + DEPS: capacity ON graph, partition1 ON graph, partition2 ON graph, cut ON graph + +igraph_mincut_value: + PARAMS: GRAPH graph, OUT REAL res, OPTIONAL EDGE_CAPACITY capacity + DEPS: + capacity ON graph + +igraph_residual_graph: + PARAMS: |- + GRAPH graph, EDGE_CAPACITY capacity, OUT GRAPH residual, + OUT EDGE_CAPACITY residual_capacity, VECTOR flow + DEPS: capacity ON graph, flow ON graph, residual_capacity ON residual + +igraph_reverse_residual_graph: + PARAMS: |- + GRAPH graph, EDGE_CAPACITY capacity, OUT GRAPH residual, + VECTOR flow + DEPS: capacity ON graph, flow ON graph + +igraph_st_mincut: + PARAMS: |- + GRAPH graph, OUT REAL value, OUT EDGE_INDICES cut, + OPTIONAL OUT VERTEX_INDICES partition1, + OPTIONAL OUT VERTEX_INDICES partition2, + VERTEX source, VERTEX target, OPTIONAL EDGE_CAPACITY capacity + DEPS: |- + capacity ON graph, source ON graph, target ON graph, + partition1 ON graph, partition2 ON graph, cut ON graph + +igraph_st_mincut_value: + PARAMS: |- + GRAPH graph, OUT REAL res, VERTEX source, VERTEX target, + OPTIONAL EDGE_CAPACITY capacity + DEPS: source ON graph, target ON graph, capacity ON graph + +igraph_st_vertex_connectivity: + PARAMS: |- + GRAPH graph, OUT INTEGER res, VERTEX source, VERTEX target, + VCONNNEI neighbors=NUMBER_OF_NODES + DEPS: source ON graph, target ON graph + +igraph_vertex_connectivity: + PARAMS: GRAPH graph, OUT INTEGER res, BOOLEAN checks=True + +igraph_st_edge_connectivity: + PARAMS: GRAPH graph, OUT INTEGER res, VERTEX source, VERTEX target + DEPS: source ON graph, target ON graph + +igraph_edge_connectivity: + PARAMS: GRAPH graph, OUT INTEGER res, BOOLEAN checks=True + +igraph_edge_disjoint_paths: + PARAMS: GRAPH graph, OUT INTEGER res, VERTEX source, VERTEX target + DEPS: source ON graph, target ON graph + +igraph_vertex_disjoint_paths: + PARAMS: GRAPH graph, OUT INTEGER res, VERTEX source, VERTEX target + DEPS: source ON graph, target ON graph + +igraph_adhesion: + PARAMS: GRAPH graph, OUT INTEGER res, BOOLEAN checks=True + +igraph_cohesion: + PARAMS: GRAPH graph, OUT INTEGER res, BOOLEAN checks=True + +####################################### +# Listing s-t cuts, separators +####################################### + +igraph_dominator_tree: + PARAMS: |- + GRAPH graph, VERTEX root, OUT INDEX_VECTOR dom, + OPTIONAL OUT GRAPH domtree, OUT VERTEX_INDICES leftout, + NEIMODE mode=OUT + DEPS: root ON graph, leftout ON graph + +igraph_all_st_cuts: + PARAMS: |- + GRAPH graph, OPTIONAL OUT EDGESET_LIST cuts, + OPTIONAL OUT VERTEXSET_LIST partition1s, + VERTEX source, VERTEX target + DEPS: |- + source ON graph, target ON graph, cuts ON graph, + partition1s ON graph + +igraph_all_st_mincuts: + PARAMS: |- + GRAPH graph, OUT REAL value, + OPTIONAL OUT EDGESET_LIST cuts, + OPTIONAL OUT VERTEXSET_LIST partition1s, + VERTEX source, VERTEX target, OPTIONAL EDGE_CAPACITY capacity + DEPS: |- + capacity ON graph, source ON graph, target ON graph, + cuts ON graph, partition1s ON graph + +igraph_even_tarjan_reduction: + PARAMS: GRAPH graph, OUT GRAPH graphbar, OPTIONAL OUT EDGE_CAPACITY capacity + DEPS: |- + capacity ON graphbar + +igraph_is_separator: + PARAMS: GRAPH graph, VERTEX_SELECTOR candidate, OUT BOOLEAN res + DEPS: candidate ON graph + +igraph_is_minimal_separator: + PARAMS: GRAPH graph, VERTEX_SELECTOR candidate, OUT BOOLEAN res + DEPS: candidate ON graph + +igraph_all_minimal_st_separators: + PARAMS: GRAPH graph, OUT VERTEXSET_LIST separators + DEPS: separators ON graph + +igraph_minimum_size_separators: + PARAMS: GRAPH graph, OUT VERTEXSET_LIST separators + DEPS: separators ON graph + +igraph_cohesive_blocks: + PARAMS: |- + GRAPH graph, OUT VERTEXSET_LIST blocks, + OUT VECTOR_INT cohesion, OUT INDEX_VECTOR parent, + OUT GRAPH blockTree + DEPS: blocks ON graph + +####################################### +# K-Cores +####################################### + +igraph_coreness: + PARAMS: GRAPH graph, OUT VECTOR_INT cores, NEIMODE mode=ALL + +####################################### +# Graph isomorphism +####################################### + +igraph_isoclass: + PARAMS: GRAPH graph, OUT INTEGER isoclass + +igraph_isomorphic: + PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN iso + +igraph_isoclass_subgraph: + PARAMS: GRAPH graph, VECTOR_INT vids, OUT INTEGER isoclass + DEPS: vids ON graph + +igraph_isoclass_create: + PARAMS: OUT GRAPH graph, INTEGER size, INTEGER number, BOOLEAN directed=True + +igraph_isomorphic_vf2: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + OPTIONAL VERTEX_COLOR vertex_color1, + OPTIONAL VERTEX_COLOR vertex_color2, + OPTIONAL EDGE_COLOR edge_color1, + OPTIONAL EDGE_COLOR edge_color2, + OUT BOOLEAN iso, + OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, + OPTIONAL ISOCOMPAT_FUNC node_compat_fn, + OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, + EXTRA extra + DEPS: |- + vertex_color1 ON graph1, vertex_color2 ON graph2, + edge_color1 ON graph1, edge_color2 ON graph2 + +igraph_count_isomorphisms_vf2: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, + EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, + OUT INTEGER count, ISOCOMPAT_FUNC node_compat_fn, + ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra + DEPS: |- + vertex_color1 ON graph1, vertex_color2 ON graph2, + edge_color1 ON graph1, edge_color2 ON graph2 + +igraph_get_isomorphisms_vf2: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, + EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, + OUT VECTOR_INT_LIST maps, ISOCOMPAT_FUNC node_compat_fn, + ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra + DEPS: |- + vertex_color1 ON graph1, vertex_color2 ON graph2, + edge_color1 ON graph1, edge_color2 ON graph2 + +igraph_subisomorphic: + PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN iso + +igraph_subisomorphic_vf2: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + OPTIONAL VERTEX_COLOR vertex_color1, OPTIONAL VERTEX_COLOR vertex_color2, + OPTIONAL EDGE_COLOR edge_color1, OPTIONAL EDGE_COLOR edge_color2, + OUT BOOLEAN iso, + OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, + OPTIONAL ISOCOMPAT_FUNC node_compat_fn, OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, + EXTRA extra + DEPS: |- + vertex_color1 ON graph1, vertex_color2 ON graph2, + edge_color1 ON graph1, edge_color2 ON graph2 + +igraph_get_subisomorphisms_vf2_callback: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + OPTIONAL VERTEX_COLOR vertex_color1, OPTIONAL VERTEX_COLOR vertex_color2, + OPTIONAL EDGE_COLOR edge_color1, OPTIONAL EDGE_COLOR edge_color2, + OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, + ISOMORPHISM_FUNC ishohandler_fn, OPTIONAL ISOCOMPAT_FUNC node_compat_fn, + OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, EXTRA arg + DEPS: |- + vertex_color1 ON graph1, vertex_color2 ON graph2, + edge_color1 ON graph1, edge_color2 ON graph2 + +igraph_count_subisomorphisms_vf2: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, + EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, + OUT INTEGER count, ISOCOMPAT_FUNC node_compat_fn, + ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra + DEPS: |- + vertex_color1 ON graph1, vertex_color2 ON graph2, + edge_color1 ON graph1, edge_color2 ON graph2 + +igraph_get_subisomorphisms_vf2: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, + EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, + OUT VECTOR_INT_LIST maps, ISOCOMPAT_FUNC node_compat_fn, + ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra + DEPS: |- + vertex_color1 ON graph1, vertex_color2 ON graph2, + edge_color1 ON graph1, edge_color2 ON graph2 + +igraph_canonical_permutation: + PARAMS: |- + GRAPH graph, OPTIONAL VERTEX_COLOR colors, + OUT INDEX_VECTOR labeling, BLISSSH sh="fm", OUT BLISSINFO info + DEPS: colors ON graph + +igraph_permute_vertices: + PARAMS: GRAPH graph, OUT GRAPH res, INDEX_VECTOR permutation + +igraph_isomorphic_bliss: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + OPTIONAL VERTEX_COLOR colors1, OPTIONAL VERTEX_COLOR colors2, + OUT BOOLEAN iso, OPTIONAL OUT INDEX_VECTOR map12, + OPTIONAL OUT INDEX_VECTOR map21, BLISSSH sh="fm", + OPTIONAL OUT BLISSINFO info1, OPTIONAL OUT BLISSINFO info2 + DEPS: colors1 ON graph1, colors2 ON graph2 + +igraph_count_automorphisms: + PARAMS: |- + GRAPH graph, OPTIONAL VERTEX_COLOR colors, BLISSSH sh="fm", OUT BLISSINFO info + DEPS: colors ON graph + +igraph_automorphism_group: + PARAMS: |- + GRAPH graph, OPTIONAL VERTEX_COLOR colors, PRIMARY OUT VERTEXSET_LIST generators, + BLISSSH sh="fm", OUT BLISSINFO info + DEPS: colors ON graph, generators ON graph + +igraph_subisomorphic_lad: + PARAMS: |- + GRAPH pattern, GRAPH target, OPTIONAL VERTEXSET_LIST domains, + OPTIONAL OUT BOOLEAN iso, OUT INDEX_VECTOR map, + OPTIONAL OUT VECTOR_INT_LIST maps, BOOLEAN induced, INTEGER time_limit + +igraph_simplify_and_colorize: + # Despite their names, vertex_color and edge_color are not really colors + # but _multiplicities_, so we simply use VECTOR_INT there + PARAMS: |- + GRAPH graph, OUT GRAPH res, OUT VECTOR_INT vertex_color, OUT VECTOR_INT edge_color + DEPS: vertex_color ON graph, edge_color ON graph + +igraph_graph_count: + PARAMS: INTEGER n, BOOLEAN directed=False, OUT INTEGER count + +####################################### +# Matching +####################################### + +igraph_is_matching: + PARAMS: |- + GRAPH graph, OPTIONAL BIPARTITE_TYPES types, + INDEX_VECTOR matching, OUT BOOLEAN res + DEPS: types ON graph, matching ON graph + +igraph_is_maximal_matching: + PARAMS: |- + GRAPH graph, OPTIONAL BIPARTITE_TYPES types, + INDEX_VECTOR matching, OUT BOOLEAN res + DEPS: types ON graph + +igraph_maximum_bipartite_matching: + PARAMS: |- + GRAPH graph, OPTIONAL BIPARTITE_TYPES types, + OPTIONAL OUT INTEGER matching_size, + OPTIONAL OUT REAL matching_weight, + OUT INDEX_VECTOR matching, + OPTIONAL EDGEWEIGHTS weights, REAL eps=.Machine$double.eps + DEPS: types ON graph, weights ON graph + +####################################### +# Embedding +####################################### + +igraph_adjacency_spectral_embedding: + PARAMS: |- + GRAPH graph, INTEGER no, EDGEWEIGHTS weights=NULL, + EIGENWHICHPOS which=ASE, BOOLEAN scaled=True, OUT MATRIX X, + OPTIONAL OUT MATRIX Y, OPTIONAL OUT VECTOR D, + VECTOR cvec=AsmDefaultCvec, + INOUT ARPACKOPT options=ARPACK_DEFAULTS + DEPS: weights ON graph, cvec ON graph + +igraph_laplacian_spectral_embedding: + PARAMS: |- + GRAPH graph, INTEGER no, EDGEWEIGHTS weights=NULL, + EIGENWHICHPOS which=ASE, + LSETYPE type=Default, BOOLEAN scaled=True, OUT MATRIX X, + OPTIONAL OUT MATRIX Y, OPTIONAL OUT VECTOR D, + INOUT ARPACKOPT options=ARPACK_DEFAULTS + DEPS: weights ON graph, type ON graph + +####################################### +# Eigensolvers +####################################### + +igraph_eigen_adjacency: + PARAMS: |- + GRAPH graph, EIGENALGO algorithm=ARPACK, + EIGENWHICH which=Default, + INOUT ARPACKOPT options=ARPACK_DEFAULTS, + INOUT ARPACKSTORAGE storage, OUT VECTOR values, OUT MATRIX vectors, + OUT VECTOR_COMPLEX cmplxvalues, OUT MATRIX_COMPLEX cmplxvectors + +####################################### +# Fitting power laws +####################################### + +igraph_power_law_fit: + PARAMS: |- + VECTOR data, OUT PLFIT res, REAL xmin=-1, + BOOLEAN force_continuous=False + +####################################### +# Dynamics, on networks +####################################### + +igraph_sir: + PARAMS: |- + GRAPH graph, REAL beta, REAL gamma, INTEGER no_sim=100, + OUT SIR_LIST res + +####################################### +# Other, not graph related +####################################### + +igraph_running_mean: + PARAMS: VECTOR data, OUT VECTOR res, INTEGER binwidth + +igraph_random_sample: + PARAMS: OUT VECTOR_INT res, INTEGER l, INTEGER h, INTEGER length + +igraph_convex_hull: + PARAMS: MATRIX data, OUT INDEX_VECTOR resverts, OUT MATRIX rescoords + +igraph_dim_select: + PARAMS: VECTOR sv, OUT INTEGER dim + +igraph_almost_equals: + PARAMS: DOUBLE a, DOUBLE b, DOUBLE eps + RETURN: BOOLEAN + +igraph_cmp_epsilon: + PARAMS: DOUBLE a, DOUBLE b, DOUBLE eps + RETURN: INT + +igraph_eigen_matrix: + PARAMS: |- + MATRIX A, SPARSEMAT sA, ARPACKFUNC fun, INT n, EXTRA extra, + EIGENALGO algorithm, EIGENWHICH which, INOUT ARPACKOPT options=ARPACK_DEFAULTS, + INOUT ARPACKSTORAGE storage, OUT VECTOR_COMPLEX values, OUT MATRIX_COMPLEX vectors + +igraph_eigen_matrix_symmetric: + PARAMS: |- + MATRIX A, SPARSEMAT sA, ARPACKFUNC fun, INT n, EXTRA extra, + EIGENALGO algorithm, EIGENWHICH which, INOUT ARPACKOPT options=ARPACK_DEFAULTS, + INOUT ARPACKSTORAGE storage, OUT VECTOR values, OUT MATRIX vectors + +igraph_solve_lsap: + PARAMS: MATRIX c, INTEGER n, OUT VECTOR_INT p + +####################################### +# Eulerian functions +####################################### + +igraph_is_eulerian: + PARAMS: GRAPH graph, OUT BOOLEAN has_path, OUT BOOLEAN has_cycle + +igraph_eulerian_path: + PARAMS: GRAPH graph, OPTIONAL OUT EDGE_INDICES edge_res, OPTIONAL OUT VERTEX_INDICES vertex_res + DEPS: edge_res ON graph, vertex_res ON graph + +igraph_eulerian_cycle: + PARAMS: GRAPH graph, OPTIONAL OUT EDGE_INDICES edge_res, OPTIONAL OUT VERTEX_INDICES vertex_res + DEPS: edge_res ON graph, vertex_res ON graph + +####################################### +# Cycle bases +####################################### + +igraph_fundamental_cycles: + PARAMS: GRAPH graph, OUT EDGESET_LIST basis, OPTIONAL VERTEX start, INTEGER bfs_cutoff, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph, basis ON graph, start ON graph + +igraph_minimum_cycle_basis: + PARAMS: GRAPH graph, OUT EDGESET_LIST basis, INTEGER bfs_cutoff, BOOLEAN complete, BOOLEAN use_cycle_order, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph, basis ON graph + +####################################### +# Trees +####################################### + +igraph_is_tree: + PARAMS: GRAPH graph, PRIMARY OUT BOOLEAN res, OPTIONAL OUT VERTEX root, NEIMODE mode=OUT + DEPS: root ON graph + +igraph_is_forest: + PARAMS: GRAPH graph, PRIMARY OUT BOOLEAN res, OPTIONAL OUT VERTEX_INDICES roots, NEIMODE mode=OUT + DEPS: roots ON graph + +igraph_from_prufer: + PARAMS: OUT GRAPH graph, INDEX_VECTOR prufer + +igraph_to_prufer: + PARAMS: GRAPH graph, OUT INDEX_VECTOR prufer + +igraph_tree_from_parent_vector: + PARAMS: OUT GRAPH graph, INDEX_VECTOR parents, TREE_MODE type=OUT + +igraph_is_complete: + PARAMS: GRAPH graph, OUT BOOLEAN res + +igraph_minimum_spanning_tree: + PARAMS: GRAPH graph, OUT EDGE_INDICES res, EDGEWEIGHTS weights=NULL + DEPS: res ON graph, weights ON graph + +igraph_minimum_spanning_tree_unweighted: + PARAMS: GRAPH graph, OUT GRAPH mst + +igraph_minimum_spanning_tree_prim: + PARAMS: GRAPH graph, OUT GRAPH mst, EDGEWEIGHTS weights + DEPS: weights ON graph + +igraph_random_spanning_tree: + PARAMS: GRAPH graph, OUT EDGE_INDICES res, OPTIONAL VERTEX vid + DEPS: res ON graph, vid ON graph + +igraph_tree_game: + PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=False, RANDOM_TREE_METHOD method=LERW + +####################################### +# Coloring +####################################### + +igraph_vertex_coloring_greedy: + PARAMS: GRAPH graph, OUT VERTEX_COLOR colors, GREEDY_COLORING_HEURISTIC heuristic=NEIGHBORS + DEPS: colors ON graph + +####################################### +# Microscopic update +####################################### + +igraph_deterministic_optimal_imitation: + PARAMS: |- + GRAPH graph, VERTEX vid, OPTIMALITY optimality=MAXIMUM, ALL_VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: vid ON graph, quantities ON graph, strategies ON graph + +igraph_moran_process: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, INOUT ALL_VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: weights ON graph, quantities ON graph, strategies ON graph + +igraph_roulette_wheel_imitation: + PARAMS: |- + GRAPH graph, VERTEX vid, BOOLEAN is_local, ALL_VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: vid ON graph, quantities ON graph, strategies ON graph + +igraph_stochastic_imitation: + PARAMS: |- + GRAPH graph, VERTEX vid, IMITATE_ALGORITHM algo, ALL_VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: vid ON graph, quantities ON graph, strategies ON graph + +####################################### +# Other, (yet) undocumented functions +####################################### + +igraph_convergence_degree: + PARAMS: GRAPH graph, OUT VECTOR result, OUT VECTOR in, OUT VECTOR out + +igraph_has_attribute_table: + RETURN: BOOLEAN + +####################################### +# Progress, status handling +####################################### + +igraph_progress: + PARAMS: CSTRING message, REAL percent, EXTRA data + +igraph_status: + PARAMS: CSTRING message, EXTRA data + +igraph_strerror: + PARAMS: ERROR igraph_errno + RETURN: CSTRING + +####################################### +# Other functions, documented, graph related +####################################### + +igraph_expand_path_to_pairs: + PARAMS: INOUT VERTEX_INDICES path + +igraph_invalidate_cache: + PARAMS: GRAPH graph + RETURN: VOID + +igraph_vertex_path_from_edge_path: + PARAMS: |- + GRAPH graph, VERTEX start, EDGE_INDICES edge_path, + OUT VERTEX_INDICES vertex_path, NEIMODE mode=OUT + +####################################### +# Meta info +####################################### + +igraph_version: + PARAMS: |- + OPTIONAL OUT CSTRING version_string, OPTIONAL OUT INT major, + OPTIONAL OUT INT minor, OPTIONAL OUT INT subminor + RETURN: VOID diff --git a/interfaces/types.yaml b/interfaces/types.yaml new file mode 100644 index 0000000..3c15618 --- /dev/null +++ b/interfaces/types.yaml @@ -0,0 +1,638 @@ +# vim:set ts=4 sw=4 sts=4 et: +# +# This file is a YAML representation of the types used in the functions.yaml +# function specification file. It provides the meaning of each type in comments +# and also specifies the C types corresponding to each abstract type. +# +# See https://github.com/igraph/stimulus for more information + +############################################################################### +## Core igraph data types +############################################################################### + +INTEGER: + # An ordinary igraph integer + CTYPE: igraph_integer_t + +REAL: + # An ordinary igraph floating-point number + CTYPE: igraph_real_t + +BOOLEAN: + # An ordinary igraph Boolean value + CTYPE: igraph_bool_t + +COMPLEX: + # An ordinary igraph complex number + CTYPE: igraph_complex_t + +ERROR: + # An igraph error code + CTYPE: igraph_error_t + +############################################################################### +## C data types +############################################################################### + +INT: + # A C integer + CTYPE: int + +LONGINT: + # A C long integer + CTYPE: long int + +CSTRING: + # A null-terminated immutable C string + CTYPE: const char* + +INFILE: + # A file, already open for reading + CTYPE: FILE* + +OUTFILE: + # A file, already open for writing + CTYPE: FILE* + +DOUBLE: + # A C double + CTYPE: double + +VOID: + # C void + CTYPE: void + +############################################################################### +# Vectors, matrices and other template types +############################################################################### + +INDEX_VECTOR: + # A vector of integer indices that should adapt to the conventions of the + # host language (i.e. 1-based for R, Mathematica, Octave etc., 0-based for + # Python and similar). + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +VECTOR: + # A vector of floating-point numbers + CTYPE: igraph_vector_t + FLAGS: BY_REF + +VECTOR_INT: + # A vector of igraph integers + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +VECTOR_BOOL: + # A vector of Boolean values + CTYPE: igraph_vector_bool_t + FLAGS: BY_REF + +VECTOR_COMPLEX: + # A vector of igraph complex numbers + CTYPE: igraph_vector_complex_t + +VECTOR_STR: + # A vector of strings + CTYPE: igraph_strvector_t + FLAGS: BY_REF + +VECTOR_LIST: + # A list containing vectors of floating-point numbers + CTYPE: igraph_vector_list_t + FLAGS: BY_REF + +VECTOR_INT_LIST: + # A list containing vectors of integers + CTYPE: igraph_vector_int_list_t + FLAGS: BY_REF + +MATRIX: + # A matrix of floating-point numbers + CTYPE: igraph_matrix_t + FLAGS: BY_REF + +MATRIX_INT: + # A matrix of igraph integers + CTYPE: igraph_matrix_int_t + FLAGS: BY_REF + +MATRIX_COMPLEX: + # A matrix of igraph complex numbers + CTYPE: igraph_matrix_complex_t + +MATRIX_LIST: + # A list containing matrices of floating-point numbers + CTYPE: igraph_matrix_list_t + FLAGS: BY_REF + +SPARSEMAT: + # A sparse matrix of floating-point numbers + CTYPE: igraph_sparsemat_t + FLAGS: BY_REF + +############################################################################### +# Vertices, edges, vertex and edge selectors +############################################################################### + +EDGE: + # A single edge index + CTYPE: igraph_integer_t + +EDGE_INDICES: + # An integer vector containing edge indices. + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +EDGE_SELECTOR: + # An igraph edge selector. Typically used only as an input argument type. + CTYPE: igraph_es_t + +VERTEX: + # A single vertex index + CTYPE: igraph_integer_t + +VERTEX_INDICES: + # An integer vector containing vertex indices. + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +VERTEX_INDEX_PAIRS: + # An integer vector containing pairs of vertex indices, in a flattened + # representation + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +VERTEX_SELECTOR: + # An igraph vertex selector. Typically used only as an input argument type. + CTYPE: igraph_vs_t + +############################################################################### +# Specialized vectors with semantic meaning +############################################################################### + +BIPARTITE_TYPES: + # A vector containing Booleans that define the two partitions of a + # bipartite graph + CTYPE: igraph_vector_bool_t + FLAGS: BY_REF + +EDGE_CAPACITY: + # A vector containing edge capacities (typically for max-flow algorithms) + CTYPE: igraph_vector_t + FLAGS: BY_REF + +EDGE_COLOR: + # A vector containing edge colors + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +EDGE_LENGTHS: + # A vector containing edge lengths + CTYPE: igraph_vector_t + FLAGS: BY_REF + +EDGEWEIGHTS: + # A vector containing edge weights + CTYPE: igraph_vector_t + FLAGS: BY_REF + +EDGESET_LIST: + # A list containing vectors of igraph integers where each such + # vector represents a sequence of edge indices. + CTYPE: igraph_vector_int_list_t + FLAGS: BY_REF + +GRAPH_LIST: + # A list containing graphs (owned by the list itself) + CTYPE: igraph_graph_list_t + FLAGS: BY_REF + +GRAPH_PTR_LIST: + # A vector containing pointers to graph objects (not owned by the vector) + CTYPE: igraph_vector_ptr_t + FLAGS: BY_REF + +ALL_VERTEX_QTY: + # A vector of floating-point numbers where each entry corresponds to + # one of the vertices in a graph and its value represents some quantity + # associated to the vertex with the same index. Higher-level interfaces may + # use this type to provide a "named vector" such that each entry can be + # indexed either by the vertex index or by the vertex name. + CTYPE: igraph_vector_t + FLAGS: BY_REF + +VERTEX_QTY: + # Same as ALL_VERTEX_QTY, but only for a subset of vertices, + # typically referred to by a `vids` argument. + CTYPE: igraph_vector_t + FLAGS: BY_REF + +SIR_LIST: + # A vector containing pointers to igraph_sir_t objects + CTYPE: igraph_vector_ptr_t + FLAGS: BY_REF + +VERTEXSET_LIST: + # A list containing vectors of igraph integers where each such + # vector represents a sequence of vertex indices. + CTYPE: igraph_vector_int_list_t + FLAGS: BY_REF + +VERTEX_COLOR: + # A vector containing vertex colors + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +VERTEXWEIGHTS: + # A vector containing vertex weights + CTYPE: igraph_vector_t + FLAGS: BY_REF + +############################################################################### +# Graph representations +############################################################################### + +GRAPH: + # An igraph graph + CTYPE: igraph_t + FLAGS: BY_REF + +ADJLIST: + # A graph represented as an adjacency list + CTYPE: igraph_adjlist_t + FLAGS: BY_REF + +INCLIST: + # A graph represented as an incidence list + CTYPE: igraph_inclist_t + FLAGS: BY_REF + +############################################################################### +# Enums +############################################################################### + +ADD_WEIGHTS: + # Whether to add the weights of the edges read from a file to the graph + # being created + CTYPE: igraph_add_weights_t + FLAGS: ENUM + +ADJACENCY_MODE: + # Enum that describes how an adjacency matrix should be constructed + CTYPE: igraph_adjacency_t + FLAGS: ENUM + +BARABASI_ALGORITHM: + # Enum that describes the various implementations of the Barabasi model + # that igraph supports + CTYPE: igraph_barabasi_algorithm_t + FLAGS: ENUM + +BLISSSH: + # Enum containing splitting heuristics for the Bliss algorithm + CTYPE: igraph_bliss_sh_t + FLAGS: ENUM + +CHUNG_LU_VARIANT: + # Enum that describes the Chung-Lu model variants supported by igraph + CTYPE: igraph_chung_lu_t + FLAGS: ENUM + +COMMCMP: + # Enum containing identifiers for community comparison methods + CTYPE: igraph_community_comparison_t + FLAGS: ENUM + +CONNECTEDNESS: + # Enum that selects between weak and strong connectivity + CTYPE: igraph_connectedness_t + FLAGS: ENUM + +DEGSEQ_MODE: + # Enum that describes the various implementations of generating a graph + # with an arbitrary degree sequence + CTYPE: igraph_degseq_t + FLAGS: ENUM + +EIGENALGO: + # Enum used for selecting an algorithm that determines the eigenvalues + # and eigenvectors of some input + CTYPE: igraph_eigen_algorithm_t + FLAGS: ENUM + +EIGENWHICHPOS: + # Enum representing which eigenvalues to use in the spectral embedding + # algorithm + CTYPE: igraph_eigen_which_position_t + FLAGS: ENUM + +ERDOS_RENYI_TYPE: + # Enum that says wheter a GNM (n vertices, m edges) or + # GNP (n vertices, every edge exists with probability p) + # graph is created + CTYPE: igraph_erdos_renyi_t + FLAGS: ENUM + +FAS_ALGORITHM: + # Enum representing feedback arc set algorithms + CTYPE: igraph_fas_algorithm_t + FLAGS: ENUM + +FWALGORITHM: + # Enum that describes the variant of the Floyd-Warshall algorithm to use in + # Floyd-Warshall graph distances computing function + CTYPE: igraph_floyd_warshall_algorithm_t + FLAGS: ENUM + +GETADJACENCY: + # Enum storing how to retrieve the adjacency matrix from a graph + CTYPE: igraph_get_adjacency_t + FLAGS: ENUM + +GREEDY_COLORING_HEURISTIC: + # Enum representing different heuristics for a greedy vertex coloring + CTYPE: igraph_coloring_greedy_t + FLAGS: ENUM + +IMITATE_ALGORITHM: + # This enum controls which algorithm to use in stochastic imitation + CTYPE: igraph_imitate_algorithm_t + FLAGS: ENUM + +LAPLACIAN_NORMALIZATION: + # Enum representing the possible normalization methods of a Laplacian + # matrix + CTYPE: igraph_laplacian_normalization_t + FLAGS: ENUM + +LAYOUT_GRID: + # Whether to use the fast (but less accurate) grid-based version of a + # layout algorithm that supports it (typically the Fruchterman-Reingold + # layout) + CTYPE: igraph_layout_grid_t + FLAGS: ENUM + +LOOPS: + # Enum that describes how loop edges should be handled in undirected graphs + # in functions that support it. Possible options are: no loops, loops + # counted once, loops counted twice + CTYPE: igraph_loops_t + FLAGS: ENUM + +LSETYPE: + # Enum storing the possible types (definitions) of the Laplacian matrix + # to use in the Laplacian spectral embedding algorithms + CTYPE: igraph_laplacian_spectral_embedding_type_t + FLAGS: ENUM + +NEIMODE: + # Enum that describes how a particular function should take into account + # the neighbors of vertices + CTYPE: igraph_neimode_t + FLAGS: ENUM + +OPTIMALITY: + # This enum controls which algorithm to use in deterministic optimal imitation + CTYPE: igraph_optimal_t + FLAGS: ENUM + +ORDER: + # Whether ordering should be ascending or descending + CTYPE: igraph_order_t + FLAGS: ENUM + +PAGERANKALGO: + # Enum that describes the various implementations of the PageRank algorithm + CTYPE: igraph_pagerank_algo_t + FLAGS: ENUM + +RANDOM_TREE_METHOD: + # Enum that describes the various implementation of the uniform random tree + # sampling method + CTYPE: igraph_random_tree_t + FLAGS: ENUM + +REALIZE_DEGSEQ_METHOD: + # Enum that describes the various methods for realizing a graph with an + # arbitrary degree sequence + CTYPE: igraph_realize_degseq_t + FLAGS: ENUM + +RECIP: + # Enum that describes how the reciprocity of a graph should be calculated + CTYPE: igraph_reciprocity_t + FLAGS: ENUM + +REWIRING_MODE: + # Enum for the rewiring modes of igraph_rewire() + CTYPE: igraph_rewiring_t + FLAGS: ENUM + +ROOTCHOICE: + # Enum for the heuristic of igraph_roots_for_tree_layout() + CTYPE: igraph_root_choice_t + FLAGS: ENUM + +RWSTUCK: + # Enum that describes what igraph should do when a random walk gets stuck + # in a sink vertex + CTYPE: igraph_random_walk_stuck_t + FLAGS: ENUM + +SPINCOMMUPDATE: + # Enum containing update modes for the spinglass community detection + # algorithm + CTYPE: igraph_spincomm_update_t + FLAGS: ENUM + +SPINGLASS_IMPLEMENTATION: + # Enum that describes the various implementations of the spinglass community + # detection algorithm + CTYPE: igraph_spinglass_implementation_t + FLAGS: ENUM + +STAR_MODE: + # Enum that describes how a star graph should be constructed + CTYPE: igraph_star_mode_t + FLAGS: ENUM + +SUBGRAPH_IMPL: + # Enum that describes how igraph should create an induced subgraph of a + # graph + CTYPE: igraph_subgraph_implementation_t + FLAGS: ENUM + +TODIRECTED: + # Enum representing the possible ways to convert an undirected graph to a + # directed one + CTYPE: igraph_to_directed_t + FLAGS: ENUM + +TOUNDIRECTED: + # Enum representing the possible ways to convert a directed graph to an + # undirected one + CTYPE: igraph_to_undirected_t + FLAGS: ENUM + +TRANSITIVITY_MODE: + # Enum that specifies how isolated vertices should be handled in transitivity + # calcuations + CTYPE: igraph_transitivity_mode_t + FLAGS: ENUM + +TREE_MODE: + # Enum that describes how a tree graph should be constructed + CTYPE: igraph_tree_mode_t + FLAGS: ENUM + +VCONNNEI: + # Enum specifying what to do in vertex connectivity tests when the two + # vertices being tested are already connected + CTYPE: igraph_vconn_nei_t + FLAGS: ENUM + +VORONOI_TIEBREAKER: + # Enum specifying what to do when two vertices are at equal distance from + # multiple generators while computing Voronoi partitionings + CTYPE: igraph_voronoi_tiebreaker_t + FLAGS: ENUM + +WHEEL_MODE: + # Enum that describes how a star graph should be constructed + CTYPE: igraph_wheel_mode_t + FLAGS: ENUM + +############################################################################### +# Switches / flags / bits +############################################################################### + +EDGE_TYPE_SW: + # Flag bitfield that specifies what sort of edges are allowed in an + # algorithm + CTYPE: igraph_edge_type_sw_t + FLAGS: BITS + +WRITE_GML_SW: + # Flag bitfield that specifies how to write GML files. + CTYPE: igraph_write_gml_sw_t + FLAGS: BITS + +############################################################################### +# Callbacks +############################################################################### + +ARPACKFUNC: + # ARPACK matrix multiplication function. + CTYPE: igraph_arpack_function_t + +CLIQUE_FUNC: + # Callback function for igraph_cliques_callback(). called with every clique + # that was found by the function. + CTYPE: igraph_clique_handler_t + +BFS_FUNC: + # Callback function for igraph_bfs(). Called with every vertex that was + # visited during the BFS traversal. + CTYPE: igraph_bfshandler_t + +DFS_FUNC: + # Callback function for igraph_dfs(). Called with every vertex that was + # visited during the DFS traversal. + CTYPE: igraph_dfshandler_t + +ISOCOMPAT_FUNC: + # Callback function for isomorphism algorithms that determines whether two + # vertices are compatible or not. + CTYPE: igraph_isocompat_t + +ISOMORPHISM_FUNC: + # Callback function that is called by isomorphism functions when an + # isomorphism is found + CTYPE: igraph_isohandler_t + +LEVCFUNC: + # Callback function for igraph_leading_eigenvector_community(). Called + # after each eigenvalue / eigenvector calculation. + CTYPE: igraph_community_leading_eigenvector_callback_t + +############################################################################### +# Miscellaneous +############################################################################### + +ARPACKOPT: + # Structure that contains the options of the ARPACK eigensolver. + CTYPE: igraph_arpack_options_t + FLAGS: BY_REF + +ARPACKSTORAGE: + # Pointer to a general-purpose memory block that ARPACK-based algorithms + # may use as a working area. + CTYPE: igraph_arpack_storage_t + FLAGS: BY_REF + +ASTAR_HEURISTIC_FUNC: + # A* heuristic function + CTYPE: igraph_astar_heuristic_func_t + +ATTRIBUTES: + # An opaque data structure that a high-level interface may use to pass + # information about graph/vertex/edge attributes to a low-level igraph + # C function + CTYPE: void + FLAGS: BY_REF + +BLISSINFO: + # Struct holding information about the internal statistics of a single + # run of the Bliss algorithm + CTYPE: igraph_bliss_info_t + +DRL_OPTIONS: + # Structure containing the options of the DrL layout algorithm + CTYPE: igraph_layout_drl_options_t + FLAGS: BY_REF + +EDGE_ATTRIBUTE_COMBINATION: + # Structure specifying how the attributes of edges should be combined + # during graph operations that may merge multiple edges into a single one + CTYPE: igraph_attribute_combination_t + FLAGS: BY_REF + +EIGENWHICH: + # Structure representing which eigenvalue(s) to use in the spectral embedding + # algorithm + CTYPE: igraph_eigen_which_t + FLAGS: BY_REF + +EXTRA: + # Thunk argument that usually accompanies callback functions and can be used + # to provide user-specific data or context to the callback function + CTYPE: void + FLAGS: BY_REF + +HRG: + # Structure storing a fitted hierarchical random graph model + CTYPE: igraph_hrg_t + FLAGS: BY_REF + +MAXFLOW_STATS: + # Structure storing statistics about a single run of a max-flow algorithm + CTYPE: igraph_maxflow_stats_t + FLAGS: BY_REF + +PAGERANKOPT: + # Enum that describes the PageRank options pointer, which is used only if + # the PageRank implementation uses ARPACK + CTYPE: igraph_arpack_options_t + FLAGS: BY_REF + +PLFIT: + # Structure representing the result of a power-law fitting algorithms + CTYPE: igraph_plfit_result_t + FLAGS: BY_REF + +VERTEX_ATTRIBUTE_COMBINATION: + # Structure specifying how the attributes of vertices should be combined + # during graph operations that may merge multiple vertices into a single one + CTYPE: igraph_attribute_combination_t + FLAGS: BY_REF diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..16f3c57 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,494 @@ + +# Traverse subdirectories +add_subdirectory(centrality/prpack) +add_subdirectory(cliques/cliquer) +add_subdirectory(isomorphism/bliss) + +# Generate lexers and parsers +set(PARSER_SOURCES) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/io/parsers) +foreach(FORMAT dl gml lgl ncol pajek) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/io/parsers/${FORMAT}-parser.c) + list(APPEND PARSER_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/io/parsers/${FORMAT}-lexer.c + ${CMAKE_CURRENT_SOURCE_DIR}/io/parsers/${FORMAT}-parser.c + ) + else() + if (BISON_VERSION VERSION_GREATER_EQUAL 3) + set(bison_no_deprecated -Wno-deprecated) + endif() + if (NOT FLEX_KEEP_LINE_NUMBERS) + set(bison_hide_line_numbers --no-lines) + set(flex_hide_line_numbers --noline) + endif() + bison_target( + ${FORMAT}_parser io/${FORMAT}-parser.y ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-parser.c + COMPILE_FLAGS "${bison_hide_line_numbers} ${bison_no_deprecated}" + ) + flex_target( + ${FORMAT}_lexer io/${FORMAT}-lexer.l ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-lexer.c + COMPILE_FLAGS "${flex_hide_line_numbers}" + DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-lexer.h + ) + add_flex_bison_dependency(${FORMAT}_lexer ${FORMAT}_parser) + list(APPEND PARSER_SOURCES ${BISON_${FORMAT}_parser_OUTPUTS} ${FLEX_${FORMAT}_lexer_OUTPUTS}) + endif() +endforeach() +add_custom_target(parsersources SOURCES ${PARSER_SOURCES}) + +# Declare the files needed to compile the igraph library +add_library( + igraph + core/array.c + core/bitset.c + core/bitset_list.c + core/buckets.c + core/cutheap.c + core/dqueue.c + core/error.c + core/estack.c + core/fixed_vectorlist.c + core/genheap.c + core/grid.c + core/heap.c + core/indheap.c + core/interruption.c + core/marked_queue.c + core/matrix.c + core/matrix_list.c + core/memory.c + core/printing.c + core/progress.c + core/psumtree.c + core/set.c + core/sparsemat.c + core/stack.c + core/statusbar.c + core/strvector.c + core/trie.c + core/vector.c + core/vector_list.c + core/vector_ptr.c + + math/complex.c + math/safe_intop.c + math/utils.c + + linalg/arpack.c + linalg/blas.c + linalg/eigen.c + linalg/lapack.c + + random/random.c + random/rng_glibc2.c + random/rng_mt19937.c + random/rng_pcg32.c + random/rng_pcg64.c + + graph/adjlist.c + graph/attributes.c + graph/basic_query.c + graph/caching.c + graph/cattributes.c + graph/graph_list.c + graph/iterators.c + graph/type_common.c + graph/type_indexededgelist.c + graph/visitors.c + + constructors/adjacency.c + constructors/atlas.c + constructors/basic_constructors.c + constructors/circulant.c + constructors/de_bruijn.c + constructors/famous.c + constructors/full.c + constructors/generalized_petersen.c + constructors/kautz.c + constructors/lattices.c + constructors/lcf.c + constructors/linegraph.c + constructors/prufer.c + constructors/regular.c + constructors/trees.c + + + games/barabasi.c + games/callaway_traits.c + games/chung_lu.c + games/citations.c + games/correlated.c + games/degree_sequence_vl/gengraph_degree_sequence.cpp + games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp + games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp + games/degree_sequence_vl/gengraph_mr-connected.cpp + games/degree_sequence.c + games/dotproduct.c + games/erdos_renyi.c + games/establishment.c + games/forestfire.c + games/grg.c + games/growing_random.c + games/islands.c + games/k_regular.c + games/preference.c + games/recent_degree.c + games/sbm.c + games/static_fitness.c + games/tree.c + games/watts_strogatz.c + + centrality/betweenness.c + centrality/centrality_other.c + centrality/centralization.c + centrality/closeness.c + centrality/coreness.c + centrality/eigenvector.c + centrality/hub_authority.c + centrality/pagerank.c + centrality/truss.cpp + centrality/prpack.cpp + + cliques/cliquer_wrapper.c + cliques/cliques.c + cliques/maximal_cliques.c + cliques/glet.c + + community/community_misc.c + community/edge_betweenness.c + community/fast_modularity.c + community/fluid.c + community/infomap/infomap_FlowGraph.cc + community/infomap/infomap_Greedy.cc + community/infomap/infomap.cc + community/label_propagation.c + community/leading_eigenvector.c + community/leiden.c + community/louvain.c + community/modularity.c + community/optimal_modularity.c + community/spinglass/clustertool.cpp + community/spinglass/NetDataTypes.cpp + community/spinglass/NetRoutines.cpp + community/spinglass/pottsmodel_2.cpp + community/voronoi.c + community/walktrap/walktrap_communities.cpp + community/walktrap/walktrap_graph.cpp + community/walktrap/walktrap_heap.cpp + community/walktrap/walktrap.cpp + + connectivity/cohesive_blocks.c + connectivity/components.c + connectivity/separators.c + connectivity/reachability.c + + flow/flow.c + flow/flow_conversion.c + flow/st-cuts.c + + hrg/hrg_types.cc + hrg/hrg.cc + + io/dimacs.c + io/dl.c + io/dot.c + io/edgelist.c + io/graphml.c + io/gml-tree.c + io/gml.c + io/graphdb.c + io/leda.c + io/lgl.c + io/ncol.c + io/pajek.c + io/parse_utils.c + ${PARSER_SOURCES} + + layout/circular.c + layout/davidson_harel.c + layout/drl/DensityGrid.cpp + layout/drl/DensityGrid_3d.cpp + layout/drl/drl_graph.cpp + layout/drl/drl_graph_3d.cpp + layout/drl/drl_layout.cpp + layout/drl/drl_layout_3d.cpp + layout/fruchterman_reingold.c + layout/gem.c + layout/graphopt.c + layout/kamada_kawai.c + layout/large_graph.c + layout/layout_bipartite.c + layout/layout_grid.c + layout/layout_random.c + layout/mds.c + layout/merge_dla.c + layout/merge_grid.c + layout/reingold_tilford.c + layout/sugiyama.c + layout/umap.c + + operators/add_edge.c + operators/complementer.c + operators/compose.c + operators/connect_neighborhood.c + operators/contract.c + operators/difference.c + operators/disjoint_union.c + operators/intersection.c + operators/join.c + operators/misc_internal.c + operators/permute.c + operators/reverse.c + operators/rewire.c + operators/rewire_edges.c + operators/simplify.c + operators/subgraph.c + operators/union.c + + paths/all_shortest_paths.c + paths/astar.c + paths/bellman_ford.c + paths/dijkstra.c + paths/distances.c + paths/eulerian.c + paths/floyd_warshall.c + paths/histogram.c + paths/johnson.c + paths/random_walk.c + paths/shortest_paths.c + paths/simple_paths.c + paths/sparsifier.c + paths/unweighted.c + paths/voronoi.c + paths/widest_paths.c + + properties/basic_properties.c + properties/complete.c + properties/constraint.c + properties/convergence_degree.c + properties/dag.c + properties/degrees.c + properties/ecc.c + properties/girth.c + properties/loops.c + properties/multiplicity.c + properties/neighborhood.c + properties/perfect.c + properties/spectral.c + properties/trees.c + properties/triangles.c + + isomorphism/bliss.cc + isomorphism/isoclasses.c + isomorphism/lad.c + isomorphism/isomorphism_misc.c + isomorphism/queries.c + isomorphism/vf2.c + + misc/bipartite.c + misc/chordality.c + misc/cocitation.c + misc/coloring.c + misc/conversion.c + misc/cycle_bases.c + misc/degree_sequence.cpp + misc/embedding.c + misc/feedback_arc_set.c + misc/graphicality.c + misc/matching.c + misc/microscopic_update.c + misc/mixing.c + misc/motifs.c + misc/order_cycle.cpp + misc/other.c + misc/power_law_fit.c + misc/scan.c + misc/sir.c + misc/spanning_trees.c + + internal/glpk_support.c + internal/hacks.c + internal/lsap.c + internal/qsort_r.c + internal/qsort.c + internal/utils.c + internal/zeroin.c + + version.c + + # Vendored library sources. Yes, this is horrible. + $,$,$>,$,> + $,$,> + $,$,> + $,$,> + $,$,> + $,$,> + $,$,> +) + +# Required by Xcode new build system +add_dependencies(igraph parsersources) + +# Set soname for the library +set_target_properties(igraph PROPERTIES VERSION "3.1.8") +set_target_properties(igraph PROPERTIES SOVERSION 3) + +# Add extra compiler definitions if needed +target_compile_definitions( + igraph + PRIVATE + IGRAPH_VERIFY_FINALLY_STACK=$,1,0> +) +# target_compile_options( +# # -Wconversion could be useful? +# igraph PRIVATE -Wshorten-64-to-32 +# ) + +# Make sure that a macro named IGRAPH_FILE_BASENAME is provided in every +# compiler call so we can use these in debug messages without revealing the +# full path of the file on the machine where it was compiled +define_file_basename_for_sources(igraph) + +# Add include path. Includes are in ../include but they get installed to +# /include/igraph, hence the two options. We also have some private +# includes that are generated at compile time but are not part of the public +# interface. +target_include_directories( + igraph + PUBLIC + $ + $ + $ + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/vendor + + # Vendored library include paths + "$<$:$>" + "$<$:$>" + + # Include paths for dependencies + "$<$:${GLPK_INCLUDE_DIR}>" + "$<$:${GMP_INCLUDE_DIR}>" + "$<$:${LIBXML2_INCLUDE_DIRS}>" + "$<$:${PLFIT_INCLUDE_DIRS}>" +) + +if(MATH_LIBRARY) + target_link_libraries(igraph PUBLIC ${MATH_LIBRARY}) +endif() + +if(ARPACK_LIBRARIES) + target_link_libraries(igraph PRIVATE ${ARPACK_LIBRARIES}) +endif() + +if(BLAS_FOUND) + target_link_libraries(igraph PRIVATE ${BLAS_LIBRARIES}) +endif() + +if(GLPK_LIBRARIES) + target_link_libraries(igraph PRIVATE ${GLPK_LIBRARIES}) +endif() + +if(GMP_LIBRARIES) + target_link_libraries(igraph PRIVATE ${GMP_LIBRARIES}) +endif() + +if(LAPACK_LIBRARIES) + target_link_libraries(igraph PRIVATE ${LAPACK_LIBRARIES}) +endif() + +if(LIBXML2_LIBRARIES) + target_link_libraries(igraph PRIVATE ${LIBXML2_LIBRARIES}) +endif() + +if(PLFIT_LIBRARIES) + target_link_libraries(igraph PRIVATE ${PLFIT_LIBRARIES}) +endif() + +# Link igraph statically to some of the libraries from the subdirectories +target_link_libraries( + igraph + PRIVATE + bliss cliquer cxsparse_vendored pcg prpack +) + +if (NOT BUILD_SHARED_LIBS) + target_compile_definitions(igraph PUBLIC IGRAPH_STATIC) +else() + target_compile_definitions(igraph PRIVATE igraph_EXPORTS) +endif() + +if(MSVC) + # Add MSVC-specific include path for some headers that are missing on Windows + target_include_directories(igraph PRIVATE ${PROJECT_SOURCE_DIR}/msvc/include) +endif() + +# Turn on all warnings for GCC, clang and MSVC +use_all_warnings(igraph) + +# GNUInstallDirs be included before generating the pkgconfig file, as it defines +# CMAKE_INSTALL_LIBDIR and CMAKE_INSTALL_INCLUDEDIR variables. +include(GNUInstallDirs) + +# Generate pkgconfig file +include(pkgconfig_helpers) + +include(GenerateExportHeader) +generate_export_header(igraph + STATIC_DEFINE IGRAPH_STATIC + EXPORT_FILE_NAME ${PROJECT_BINARY_DIR}/include/igraph_export.h +) + +# Provide an igraph-config.cmake file in the installation directory so +# users can find the installed igraph library with FIND_PACKAGE(igraph) +# from their CMakeLists.txt files +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${PROJECT_SOURCE_DIR}/etc/cmake/igraph-config.cmake.in + ${PROJECT_BINARY_DIR}/igraph-config.cmake + # Install destination selected according to https://wiki.debian.org/CMake + # and by looking at how eigen3 does it in Ubuntu + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/igraph +) +write_basic_package_version_file( + ${PROJECT_BINARY_DIR}/igraph-config-version.cmake + VERSION ${PACKAGE_VERSION_BASE} + COMPATIBILITY SameMinorVersion +) + +# Define how to install the library +install( + TARGETS igraph bliss cliquer cxsparse_vendored pcg prpack + EXPORT igraph_targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) +install( + DIRECTORY ${PROJECT_SOURCE_DIR}/include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/igraph + FILES_MATCHING PATTERN "*.h" +) +install( + DIRECTORY ${PROJECT_BINARY_DIR}/include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/igraph + FILES_MATCHING PATTERN "*.h" +) +install( + FILES ${PROJECT_BINARY_DIR}/igraph.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig +) +install( + FILES ${PROJECT_BINARY_DIR}/igraph-config.cmake + ${PROJECT_BINARY_DIR}/igraph-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/igraph +) +install( + EXPORT igraph_targets + FILE igraph-targets.cmake + NAMESPACE igraph:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/igraph +) diff --git a/src/centrality/betweenness.c b/src/centrality/betweenness.c new file mode 100644 index 0000000..9adff20 --- /dev/null +++ b/src/centrality/betweenness.c @@ -0,0 +1,1285 @@ +/* + 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_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_nongraph.h" +#include "igraph_progress.h" +#include "igraph_stack.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +/* + * We provide separate implementations of single-source shortest path searches, + * one with incidence lists and one with adjacency lists. We use the implementation + * based on adjacency lists when possible (i.e. when weights are not needed) to + * avoid an expensive IGRAPH_OTHER() lookup on edge IDs. The cost of this macro + * comes from the inability of the branch predictor to predict accurately whether + * the condition in the macro will be true or not. + * + * The following four functions are very similar in their structure. If you make + * a modification to one of them, consider whether the same modification makes + * sense in the context of the remaining three functions as well. + */ + +/** + * Internal function to calculate the single source shortest paths for the + * vertex unweighted case. + * + * \param graph the graph to calculate the single source shortest paths on + * \param source the source node + * \param dist distance of each node from the source node \em plus one; + * must be filled with zeros initially + * \param nrgeo vector storing the number of geodesics from the source node + * to each node; must be filled with zeros initially + * \param stack stack in which the nodes are pushed in the order they are + * discovered during the traversal + * \param parents adjacent list that starts empty and that stores the IDs + * of the vertices that lead to a given node during the traversal + * \param adjlist the adjacency list of the graph + * \param cutoff cutoff length of shortest paths + */ +static igraph_error_t igraph_i_sspf( + igraph_integer_t source, + igraph_vector_t *dist, + igraph_real_t *nrgeo, + igraph_stack_int_t *stack, + igraph_adjlist_t *parents, + const igraph_adjlist_t *adjlist, + igraph_real_t cutoff) { + + igraph_dqueue_int_t queue; + const igraph_vector_int_t *neis; + igraph_vector_int_t *v; + igraph_integer_t nlen; + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&queue, 100); + + IGRAPH_CHECK(igraph_dqueue_int_push(&queue, source)); + VECTOR(*dist)[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_dqueue_int_empty(&queue)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&queue); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && VECTOR(*dist)[actnode] > cutoff + 1) { + /* Reset variables if node is too distant */ + VECTOR(*dist)[actnode] = 0; + nrgeo[actnode] = 0; + igraph_vector_int_clear(igraph_adjlist_get(parents, actnode)); + continue; + } + + /* Record that we have visited this node */ + IGRAPH_CHECK(igraph_stack_int_push(stack, actnode)); + + /* Examine the neighbors of this node */ + neis = igraph_adjlist_get(adjlist, actnode); + nlen = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nlen; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + + if (VECTOR(*dist)[neighbor] == 0) { + /* We have found 'neighbor' for the first time */ + VECTOR(*dist)[neighbor] = VECTOR(*dist)[actnode] + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&queue, neighbor)); + } + + if (VECTOR(*dist)[neighbor] == VECTOR(*dist)[actnode] + 1 && + (VECTOR(*dist)[neighbor] <= cutoff + 1 || cutoff < 0)) { + /* Only add if the node is not more distant than the cutoff */ + v = igraph_adjlist_get(parents, neighbor); + IGRAPH_CHECK(igraph_vector_int_push_back(v, actnode)); + nrgeo[neighbor] += nrgeo[actnode]; + } + } + } + + igraph_dqueue_int_destroy(&queue); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Internal function to calculate the single source shortest paths for the + * edge unweighted case. + * + * \param graph the graph to calculate the single source shortest paths on + * \param source the source node + * \param dist distance of each node from the source node \em plus one; + * must be filled with zeros initially + * \param nrgeo vector storing the number of geodesics from the source node + * to each node; must be filled with zeros initially + * \param stack stack in which the nodes are pushed in the order they are + * discovered during the traversal + * \param parents incidence list that starts empty and that stores the IDs + * of the edges that lead to a given node during the traversal + * \param inclist the incidence list of the graph + * \param cutoff cutoff length of shortest paths + */ +static igraph_error_t igraph_i_sspf_edge( + const igraph_t *graph, + igraph_integer_t source, + igraph_vector_t *dist, + igraph_real_t *nrgeo, + igraph_stack_int_t *stack, + igraph_inclist_t *parents, + const igraph_inclist_t *inclist, + igraph_real_t cutoff) { + + igraph_dqueue_int_t queue; + const igraph_vector_int_t *neis; + igraph_vector_int_t *v; + igraph_integer_t nlen; + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&queue, 100); + + IGRAPH_CHECK(igraph_dqueue_int_push(&queue, source)); + VECTOR(*dist)[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_dqueue_int_empty(&queue)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&queue); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && VECTOR(*dist)[actnode] > cutoff + 1) { + /* Reset variables if node is too distant */ + VECTOR(*dist)[actnode] = 0; + nrgeo[actnode] = 0; + igraph_vector_int_clear(igraph_inclist_get(parents, actnode)); + continue; + } + + /* Record that we have visited this node */ + IGRAPH_CHECK(igraph_stack_int_push(stack, actnode)); + + /* Examine the neighbors of this node */ + neis = igraph_inclist_get(inclist, actnode); + nlen = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, actnode); + + if (VECTOR(*dist)[neighbor] == 0) { + /* We have found 'neighbor' for the first time */ + VECTOR(*dist)[neighbor] = VECTOR(*dist)[actnode] + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&queue, neighbor)); + } + + if (VECTOR(*dist)[neighbor] == VECTOR(*dist)[actnode] + 1 && + (VECTOR(*dist)[neighbor] <= cutoff + 1 || cutoff < 0)) { + /* Only add if the node is not more distant than the cutoff */ + v = igraph_inclist_get(parents, neighbor); + IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); + nrgeo[neighbor] += nrgeo[actnode]; + } + } + } + + igraph_dqueue_int_destroy(&queue); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Internal function to calculate the single source shortest paths for the vertex + * weighted case. + * + * \param graph the graph to calculate the single source shortest paths on + * \param weights the weights of the edges + * \param source the source node + * \param dist distance of each node from the source node \em plus one; + * must be filled with zeros initially + * \param nrgeo vector storing the number of geodesics from the source node + * to each node; must be filled with zeros initially + * \param stack stack in which the nodes are pushed in the order they are + * discovered during the traversal + * \param parents adjacency list that starts empty and that stores the IDs + * of the vertices that lead to a given node during the traversal + * \param inclist the incidence list of the graph + * \param cutoff cutoff length of shortest paths + */ +static igraph_error_t igraph_i_sspf_weighted( + const igraph_t *graph, + igraph_integer_t source, + igraph_vector_t *dist, + igraph_real_t *nrgeo, + const igraph_vector_t *weights, + igraph_stack_int_t *stack, + igraph_adjlist_t *parents, + const igraph_inclist_t *inclist, + igraph_real_t cutoff) { + + const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; + + int cmp_result; + igraph_2wheap_t queue; + const igraph_vector_int_t *neis; + igraph_vector_int_t *v; + igraph_integer_t nlen; + + /* TODO: this is an O|V| step here. We could save some time by pre-allocating + * the two-way heap in the caller and re-using it here */ + IGRAPH_CHECK(igraph_2wheap_init(&queue, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_2wheap_destroy, &queue); + + igraph_2wheap_push_with_index(&queue, source, -1.0); + VECTOR(*dist)[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_2wheap_empty(&queue)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&queue); + igraph_real_t mindist = -igraph_2wheap_delete_max(&queue); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && mindist > cutoff + 1.0) { + /* Reset variables if node is too distant */ + VECTOR(*dist)[minnei] = 0; + nrgeo[minnei] = 0; + igraph_vector_int_clear(igraph_adjlist_get(parents, minnei)); + continue; + } + + /* Record that we have visited this node */ + IGRAPH_CHECK(igraph_stack_int_push(stack, minnei)); + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_inclist_get(inclist, minnei); + nlen = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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 */ + v = igraph_adjlist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); + VECTOR(*v)[0] = minnei; + nrgeo[to] = nrgeo[minnei]; + VECTOR(*dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&queue, to, -altdist)); + } else if (cmp_result < 0) { + /* This is a shorter path */ + v = igraph_adjlist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); + VECTOR(*v)[0] = minnei; + nrgeo[to] = nrgeo[minnei]; + VECTOR(*dist)[to] = altdist; + igraph_2wheap_modify(&queue, 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 */ + v = igraph_adjlist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_push_back(v, minnei)); + nrgeo[to] += nrgeo[minnei]; + } + } + } + + igraph_2wheap_destroy(&queue); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Internal function to calculate the single source shortest paths for the edge + * weighted case. + * + * \param graph the graph to calculate the single source shortest paths on + * \param weights the weights of the edges + * \param source the source node + * \param dist distance of each node from the source node \em plus one; + * must be filled with zeros initially + * \param nrgeo vector storing the number of geodesics from the source node + * to each node; must be filled with zeros initially + * \param stack stack in which the nodes are pushed in the order they are + * discovered during the traversal + * \param parents incidence list that starts empty and that stores the IDs + * of the edges that lead to a given node during the traversal + * \param inclist the incidence list of the graph + * \param cutoff cutoff length of shortest paths + */ +static igraph_error_t igraph_i_sspf_weighted_edge( + const igraph_t *graph, + igraph_integer_t source, + igraph_vector_t *dist, + igraph_real_t *nrgeo, + const igraph_vector_t *weights, + igraph_stack_int_t *stack, + igraph_inclist_t *parents, + const igraph_inclist_t *inclist, + igraph_real_t cutoff) { + + const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; + + int cmp_result; + igraph_2wheap_t queue; + const igraph_vector_int_t *neis; + igraph_vector_int_t *v; + igraph_integer_t nlen; + + /* TODO: this is an O|V| step here. We could save some time by pre-allocating + * the two-way heap in the caller and re-using it here */ + IGRAPH_CHECK(igraph_2wheap_init(&queue, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_2wheap_destroy, &queue); + + igraph_2wheap_push_with_index(&queue, source, -1.0); + VECTOR(*dist)[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_2wheap_empty(&queue)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&queue); + igraph_real_t mindist = -igraph_2wheap_delete_max(&queue); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && mindist > cutoff + 1.0) { + /* Reset variables if node is too distant */ + VECTOR(*dist)[minnei] = 0; + nrgeo[minnei] = 0; + igraph_vector_int_clear(igraph_inclist_get(parents, minnei)); + continue; + } + + /* Record that we have visited this node */ + IGRAPH_CHECK(igraph_stack_int_push(stack, minnei)); + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_inclist_get(inclist, minnei); + nlen = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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 */ + v = igraph_inclist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); + VECTOR(*v)[0] = edge; + nrgeo[to] = nrgeo[minnei]; + VECTOR(*dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&queue, to, -altdist)); + } else if (cmp_result < 0) { + /* This is a shorter path */ + v = igraph_inclist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); + VECTOR(*v)[0] = edge; + nrgeo[to] = nrgeo[minnei]; + VECTOR(*dist)[to] = altdist; + igraph_2wheap_modify(&queue, 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 */ + v = igraph_inclist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); + nrgeo[to] += nrgeo[minnei]; + } + } + } + + igraph_2wheap_destroy(&queue); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_betweenness_check_weights( + const igraph_vector_t* weights, igraph_integer_t no_of_edges +) { + igraph_real_t minweight; + + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must match the number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } else if (minweight <= IGRAPH_SHORTEST_PATH_EPSILON) { + IGRAPH_WARNING( + "Some weights are smaller than epsilon, calculations may " + "suffer from numerical precision issues." + ); + } + } + } + + return IGRAPH_SUCCESS; +} + +/***** 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. + */ +igraph_error_t 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); +} + +/** + * \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. + */ +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_adjlist_t adjlist, parents; + igraph_inclist_t inclist; + igraph_integer_t source, j, neighbor; + igraph_stack_int_t S; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_vector_t dist; + /* 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 'igraph_real_t' instead. While a 'igraph_real_t' 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 'igraph_real_t', even + * for a 101*101 grid graph. */ + igraph_real_t *nrgeo = 0; + igraph_real_t *tmpscore; + igraph_vector_t v_tmpres, *tmpres = &v_tmpres; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); + + if (weights) { + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + } else { + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + } + + IGRAPH_CHECK(igraph_adjlist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &parents); + + IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &S); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for betweenness calculation."); + IGRAPH_FINALLY(igraph_free, nrgeo); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for betweenness calculation."); + IGRAPH_FINALLY(igraph_free, tmpscore); + + if (igraph_vs_is_all(&vids)) { + /* result covers all vertices */ + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + tmpres = res; + } else { + /* result needed only for a subset of the vertices */ + IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); + } + + for (source = 0; source < no_of_nodes; source++) { + + /* Loop invariant that is valid at this point: + * + * - the stack S is empty + * - the 'dist' vector contains zeros only + * - the 'nrgeo' array contains zeros only + * - the 'tmpscore' array contains zeros only + * - the 'parents' adjacency list contains empty vectors only + */ + + IGRAPH_PROGRESS("Betweenness centrality: ", 100.0 * source / no_of_nodes, 0); + IGRAPH_ALLOW_INTERRUPTION(); + + /* Conduct a single-source shortest path search from the source node */ + if (weights) { + IGRAPH_CHECK(igraph_i_sspf_weighted(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, cutoff)); + } else { + IGRAPH_CHECK(igraph_i_sspf(source, &dist, nrgeo, &S, &parents, &adjlist, cutoff)); + } + + /* Aggregate betweenness scores for the nodes we have reached in this + * traversal */ + while (!igraph_stack_int_empty(&S)) { + igraph_integer_t actnode = igraph_stack_int_pop(&S); + igraph_vector_int_t *neis = igraph_adjlist_get(&parents, actnode); + igraph_integer_t nneis = igraph_vector_int_size(neis); + igraph_real_t coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; + + for (j = 0; j < nneis; j++) { + neighbor = VECTOR(*neis)[j]; + tmpscore[neighbor] += nrgeo[neighbor] * coeff; + } + + if (actnode != source) { + VECTOR(*tmpres)[actnode] += tmpscore[actnode]; + } + + /* Reset variables to ensure that the 'for' loop invariant will + * still be valid in the next iteration */ + + VECTOR(dist)[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(neis); + } + + } /* for source < no_of_nodes */ + + /* 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 (j = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), j++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + VECTOR(*res)[j] = VECTOR(*tmpres)[node]; + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(tmpres); + IGRAPH_FINALLY_CLEAN(2); + } + + if (!directed || !igraph_is_directed(graph)) { + igraph_vector_scale(res, 0.5); + } + + IGRAPH_PROGRESS("Betweenness centrality: ", 100.0, 0); + + IGRAPH_FREE(nrgeo); + IGRAPH_FREE(tmpscore); + igraph_vector_destroy(&dist); + igraph_stack_int_destroy(&S); + igraph_adjlist_destroy(&parents); + if (weights) { + igraph_inclist_destroy(&inclist); + } else { + igraph_adjlist_destroy(&adjlist); + } + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} + +/***** Edge betweenness *****/ + + +/** + * \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. + */ +igraph_error_t 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. + */ +igraph_error_t 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) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_inclist_t inclist, parents; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_vector_t dist; + igraph_real_t *nrgeo; + igraph_real_t *tmpscore; + igraph_integer_t source, j; + igraph_stack_int_t S; + + IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_inclist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &parents); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for edge betweenness calculation."); + IGRAPH_FINALLY(igraph_free, nrgeo); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + if (tmpscore == 0) { + IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, tmpscore); + + IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &S); + + IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); + igraph_vector_null(result); + + for (source = 0; source < no_of_nodes; source++) { + + /* Loop invariant that is valid at this point: + * + * - the stack S is empty + * - the 'dist' vector contains zeros only + * - the 'nrgeo' array contains zeros only + * - the 'tmpscore' array contains zeros only + * - the 'parents' incidence list contains empty vectors only + */ + + IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0 * source / no_of_nodes, 0); + IGRAPH_ALLOW_INTERRUPTION(); + + /* Conduct a single-source shortest path search from the source node */ + if (weights) { + IGRAPH_CHECK(igraph_i_sspf_weighted_edge(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, cutoff)); + } else { + IGRAPH_CHECK(igraph_i_sspf_edge(graph, source, &dist, nrgeo, &S, &parents, &inclist, cutoff)); + } + + /* Aggregate betweenness scores for the edges we have reached in this + * traversal */ + while (!igraph_stack_int_empty(&S)) { + igraph_integer_t actnode = igraph_stack_int_pop(&S); + igraph_vector_int_t *fatv = igraph_inclist_get(&parents, actnode); + igraph_integer_t fatv_len = igraph_vector_int_size(fatv); + igraph_real_t coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; + + for (j = 0; j < fatv_len; j++) { + igraph_integer_t fedge = VECTOR(*fatv)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, fedge, actnode); + tmpscore[neighbor] += nrgeo[neighbor] * coeff; + VECTOR(*result)[fedge] += nrgeo[neighbor] * coeff; + } + + /* Reset variables to ensure that the 'for' loop invariant will + * still be valid in the next iteration */ + + VECTOR(dist)[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(fatv); + } + } /* source < no_of_nodes */ + + if (!directed || !igraph_is_directed(graph)) { + igraph_vector_scale(result, 0.5); + } + + IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0); + + igraph_stack_int_destroy(&S); + igraph_inclist_destroy(&inclist); + igraph_inclist_destroy(&parents); + igraph_vector_destroy(&dist); + IGRAPH_FREE(tmpscore); + IGRAPH_FREE(nrgeo); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_betweenness_subset + * \brief Betweenness centrality for a subset of source and target vertices. + * + * This function computes the subset-limited version of betweenness centrality + * by considering only those shortest paths that lie between vertices in a given + * source and target subset. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * betweenness score for the subset of vertices. + * \param vids The vertices for which the subset-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 sources A vertex selector for the sources of the shortest paths taken + * into considuration in the betweenness calculation. + * \param targets A vertex selector for the targets of the shortest paths taken + * into considuration in the betweenness calculation. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for temporary data. + * \c IGRAPH_EINVVID, invalid vertex ID passed in \p vids, + * \p sources or \p targets + * + * Time complexity: O(|S||E|), where + * |S| is the number of vertices in the subset and + * |E| is the number of edges in the graph. + * + * \sa \ref igraph_betweenness() to calculate the exact vertex betweenness and + * \ref igraph_betweenness_cutoff() to calculate the range-limited vertex + * betweenness. + */ +igraph_error_t igraph_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + const igraph_vs_t sources, const igraph_vs_t targets, + const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_sources; + igraph_integer_t no_of_processed_sources; + igraph_adjlist_t adjlist, parents; + igraph_inclist_t inclist; + igraph_integer_t source, j; + igraph_stack_int_t S; + igraph_vector_t v_tmpres, *tmpres = &v_tmpres; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_integer_t father; + igraph_vector_t dist; + igraph_real_t *nrgeo; + igraph_real_t *tmpscore; + igraph_vit_t vit; + bool *is_target; + + IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); + + IGRAPH_CHECK(igraph_vs_size(graph, &sources, &no_of_sources)); + + if (weights) { + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + } else { + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + } + + IGRAPH_CHECK(igraph_adjlist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &parents); + + IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &S); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for subset betweenness calculation."); + IGRAPH_FINALLY(igraph_free, nrgeo); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for subset betweenness calculation."); + IGRAPH_FINALLY(igraph_free, tmpscore); + + is_target = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(is_target, "Insufficient memory for subset betweenness calculation."); + IGRAPH_FINALLY(igraph_free, is_target); + + IGRAPH_CHECK(igraph_vit_create(graph, targets, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + is_target[IGRAPH_VIT_GET(vit)] = true; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + if (!igraph_vs_is_all(&vids)) { + /* result needed only for a subset of the vertices */ + IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); + } else { + /* result covers all vertices */ + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + tmpres = res; + } + + IGRAPH_CHECK(igraph_vit_create(graph, sources, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + for ( + no_of_processed_sources = 0, IGRAPH_VIT_RESET(vit); + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), no_of_processed_sources++ + ) { + source = IGRAPH_VIT_GET(vit); + + IGRAPH_PROGRESS( + "Betweenness centrality (subset): ", + 100.0 * no_of_processed_sources / no_of_sources, 0 + ); + IGRAPH_ALLOW_INTERRUPTION(); + + /* Loop invariant that is valid at this point: + * + * - the stack S is empty + * - the 'dist' vector contains zeros only + * - the 'nrgeo' array contains zeros only + * - the 'tmpscore' array contains zeros only + * - the 'parents' adjacency list contains empty vectors only + */ + + /* TODO: there is more room for optimization here; the single-source + * shortest path search runs until it reaches all the nodes in the + * component of the source node even if we are only interested in a + * smaller target subset. We could stop the search when all target + * nodes were reached. + */ + + /* Conduct a single-source shortest path search from the source node */ + if (weights) { + IGRAPH_CHECK(igraph_i_sspf_weighted(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, -1)); + } else { + IGRAPH_CHECK(igraph_i_sspf(source, &dist, nrgeo, &S, &parents, &adjlist, -1)); + } + + /* Aggregate betweenness scores for the nodes we have reached in this + * traversal */ + while (!igraph_stack_int_empty(&S)) { + igraph_integer_t actnode = igraph_stack_int_pop(&S); + igraph_vector_int_t *fatv = igraph_adjlist_get(&parents, actnode); + igraph_integer_t fatv_len = igraph_vector_int_size(fatv); + igraph_real_t coeff; + + if (is_target[actnode]) { + coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; + } else { + coeff = tmpscore[actnode] / nrgeo[actnode]; + } + + for (j = 0; j < fatv_len; j++) { + father = VECTOR(*fatv)[j]; + tmpscore[father] += nrgeo[father] * coeff; + } + + if (actnode != source) { + VECTOR(*tmpres)[actnode] += tmpscore[actnode]; + } + + /* Reset variables to ensure that the 'for' loop invariant will + * still be valid in the next iteration */ + + VECTOR(dist)[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(fatv); + } + } + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + /* 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 (j = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), j++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + VECTOR(*res)[j] = VECTOR(*tmpres)[node]; + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(tmpres); + IGRAPH_FINALLY_CLEAN(2); + } + + if (!directed || !igraph_is_directed(graph)) { + igraph_vector_scale(res, 0.5); + } + + IGRAPH_FREE(is_target); + IGRAPH_FREE(tmpscore); + IGRAPH_FREE(nrgeo); + igraph_vector_destroy(&dist); + igraph_stack_int_destroy(&S); + igraph_adjlist_destroy(&parents); + if (weights) { + igraph_inclist_destroy(&inclist); + } else { + igraph_adjlist_destroy(&adjlist); + } + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_edge_betweenness_subset + * \brief Edge betweenness centrality for a subset of source and target vertices. + * + * This function computes the subset-limited version of edge betweenness centrality + * by considering only those shortest paths that lie between vertices in a given + * source and target subset. + * + * \param graph The graph object. + * \param res The result of the computation, vector containing the + * betweenness scores for the edges. + * \param eids The edges for which the subset-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 weight vector for weighted + * betweenness. No edge weight may be NaN. Supply a null + * pointer here for unweighted betweenness. + * \param sources A vertex selector for the sources of the shortest paths taken + * into considuration in the betweenness calculation. + * \param targets A vertex selector for the targets of the shortest paths taken + * into considuration in the betweenness calculation. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for temporary data. + * \c IGRAPH_EINVVID, invalid vertex ID passed in \p sources or \p targets + * + * Time complexity: O(|S||E|), where + * |S| is the number of vertices in the subset and + * |E| is the number of edges in the graph. + * + * \sa \ref igraph_edge_betweenness() to compute the exact edge betweenness and + * \ref igraph_edge_betweenness_cutoff() to compute the range-limited edge betweenness. + */ +igraph_error_t igraph_edge_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t directed, + const igraph_vs_t sources, const igraph_vs_t targets, + const igraph_vector_t *weights) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_sources; + igraph_integer_t no_of_processed_sources; + igraph_inclist_t inclist, parents; + igraph_vit_t vit; + igraph_eit_t eit; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_vector_t dist; + igraph_vector_t v_tmpres, *tmpres = &v_tmpres; + igraph_real_t *nrgeo; + igraph_real_t *tmpscore; + igraph_integer_t source, j; + bool *is_target; + igraph_stack_int_t S; + + IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); + + IGRAPH_CHECK(igraph_vs_size(graph, &sources, &no_of_sources)); + + is_target = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(is_target, "Insufficient memory for subset edge betweenness calculation."); + IGRAPH_FINALLY(igraph_free, is_target); + + IGRAPH_CHECK(igraph_vit_create(graph, targets, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + is_target[IGRAPH_VIT_GET(vit)] = true; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + IGRAPH_CHECK(igraph_inclist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &parents); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for subset edge betweenness calculation."); + IGRAPH_FINALLY(igraph_free, nrgeo); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for subset edge betweenness calculation."); + IGRAPH_FINALLY(igraph_free, tmpscore); + + IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &S); + + if (!igraph_es_is_all(&eids)) { + /* result needed only for a subset of the vertices */ + IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_edges); + } else { + /* result covers all vertices */ + IGRAPH_CHECK(igraph_vector_resize(res, no_of_edges)); + igraph_vector_null(res); + tmpres = res; + } + + IGRAPH_CHECK(igraph_vit_create(graph, sources, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + for ( + no_of_processed_sources = 0, IGRAPH_VIT_RESET(vit); + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), no_of_processed_sources++ + ) { + source = IGRAPH_VIT_GET(vit); + + IGRAPH_PROGRESS( + "Edge betweenness centrality (subset): ", + 100.0 * no_of_processed_sources / no_of_sources, 0 + ); + IGRAPH_ALLOW_INTERRUPTION(); + + /* Loop invariant that is valid at this point: + * + * - the stack S is empty + * - the 'dist' vector contains zeros only + * - the 'nrgeo' array contains zeros only + * - the 'tmpscore' array contains zeros only + * - the 'parents' incidence list contains empty vectors only + */ + + /* TODO: there is more room for optimization here; the single-source + * shortest path search runs until it reaches all the nodes in the + * component of the source node even if we are only interested in a + * smaller target subset. We could stop the search when all target + * nodes were reached. + */ + + /* Conduct a single-source shortest path search from the source node */ + if (weights) { + IGRAPH_CHECK(igraph_i_sspf_weighted_edge(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, -1)); + } else { + IGRAPH_CHECK(igraph_i_sspf_edge(graph, source, &dist, nrgeo, &S, &parents, &inclist, -1)); + } + + /* Aggregate betweenness scores for the nodes we have reached in this + * traversal */ + while (!igraph_stack_int_empty(&S)) { + igraph_integer_t actnode = igraph_stack_int_pop(&S); + igraph_vector_int_t *fatv = igraph_inclist_get(&parents, actnode); + igraph_integer_t fatv_len = igraph_vector_int_size(fatv); + igraph_real_t coeff; + + if (is_target[actnode]) { + coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; + } else { + coeff = tmpscore[actnode] / nrgeo[actnode]; + } + + for (j = 0; j < fatv_len; j++) { + igraph_integer_t father_edge = VECTOR(*fatv)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, father_edge, actnode); + tmpscore[neighbor] += nrgeo[neighbor] * coeff; + VECTOR(*tmpres)[father_edge] += nrgeo[neighbor] * coeff; + } + + /* Reset variables to ensure that the 'for' loop invariant will + * still be valid in the next iteration */ + + VECTOR(dist)[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(fatv); + } + } + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + /* Keep only the requested edges */ + if (!igraph_es_is_all(&eids)) { + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (j = 0, IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), j++) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + VECTOR(*res)[j] = VECTOR(*tmpres)[edge]; + } + + igraph_eit_destroy(&eit); + igraph_vector_destroy(tmpres); + IGRAPH_FINALLY_CLEAN(2); + } + + + if (!directed || !igraph_is_directed(graph)) { + igraph_vector_scale(res, 0.5); + } + + igraph_stack_int_destroy(&S); + IGRAPH_FREE(tmpscore); + IGRAPH_FREE(nrgeo); + igraph_vector_destroy(&dist); + igraph_inclist_destroy(&parents); + igraph_inclist_destroy(&inclist); + IGRAPH_FREE(is_target); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} diff --git a/src/centrality/centrality_internal.h b/src/centrality/centrality_internal.h new file mode 100644 index 0000000..4479a34 --- /dev/null +++ b/src/centrality/centrality_internal.h @@ -0,0 +1,37 @@ +/* -*- 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_CENTRALITY_INTERNAL_H +#define IGRAPH_CENTRALITY_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vector); + +__END_DECLS + +#endif diff --git a/src/centrality/centrality_other.c b/src/centrality/centrality_other.c new file mode 100644 index 0000000..6130b25 --- /dev/null +++ b/src/centrality/centrality_other.c @@ -0,0 +1,52 @@ +/* -*- 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 "centrality/centrality_internal.h" + +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. + */ + igraph_integer_t n = igraph_vector_size(vector); + igraph_real_t mi, ma; + + if (n == 0) { + return false; + } + + igraph_vector_minmax(vector, &mi, &ma); + + if (mi >= 0) { + return false; + } + if (ma <= 0) { + return true; + } + + /* is the most negative value larger in magnitude than the most positive? */ + return (-mi/ma > 1); +} diff --git a/src/centrality/centralization.c b/src/centrality/centralization.c new file mode 100644 index 0000000..1f27ca1 --- /dev/null +++ b/src/centrality/centralization.c @@ -0,0 +1,662 @@ +/* -*- 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_structural.h" +#include "igraph_vector.h" + +#include "core/math.h" + +/** + * \function igraph_centralization + * \brief 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) { + + igraph_integer_t no_of_nodes = igraph_vector_size(scores); + igraph_real_t cent; + + if (no_of_nodes != 0) { + igraph_real_t 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 + * \brief 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. + */ + +igraph_error_t 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_strength(graph, scores, igraph_vss_all(), mode, loops, 0)); + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_centralization_degree_tmax + * \brief 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(). + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_centralization_betweenness + * \brief 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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_centralization_betweenness_tmax + * \brief 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(). + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_centralization_closeness + * \brief 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. + */ + +igraph_error_t 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, /*normalized=*/ 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_centralization_closeness_tmax + * \brief 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(). + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_centralization_eigenvector_centrality + * \brief 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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_centralization_eigenvector_centrality_tmax + * \brief 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(). + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} diff --git a/src/centrality/closeness.c b/src/centrality/closeness.c new file mode 100644 index 0000000..1bb7d3b --- /dev/null +++ b/src/centrality/closeness.c @@ -0,0 +1,808 @@ +/* -*- 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" + +/***** 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|) for the unweighted case and O(n|E|log|V|+|V|) + * for the weighted case, where n is the number + * of vertices for which the calculation is done, |V| is the number of vertices + * 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. + */ +igraph_error_t igraph_closeness(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_int_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 igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, + igraph_vector_t *res, + igraph_vector_int_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_distances_dijkstra() for the implementation + details and the dirty tricks. */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + igraph_2wheap_t Q; + igraph_vit_t vit; + igraph_integer_t nodes_to_calc; + + igraph_lazy_inclist_t inclist; + igraph_integer_t i, j; + + igraph_vector_t dist; + igraph_vector_int_t which; + igraph_integer_t 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 (isnan(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_CHECK(igraph_vector_int_resize(reachable_count, nodes_to_calc)); + } + + if (all_reachable) { + *all_reachable = true; /* 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_int_init(&which, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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++) { + + igraph_integer_t 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_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); + igraph_integer_t nlen; + + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + 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++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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_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 = false; + } + } + } /* !IGRAPH_VIT_END(vit) */ + + igraph_vector_int_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_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: At most O(n|E|) for the unweighted case and O(n|E|log|V|+|V|) + * for the weighted case, where n is the number + * of vertices for which the calculation is done, |V| is the number of vertices + * and |E| is the number of edges in the graph. The timing decreases with smaller + * cutoffs in a way that depends on the graph structure. + * + * \sa \ref igraph_closeness() to calculate the exact closeness centrality. + */ + +igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t already_counted; + igraph_vector_int_t *neis; + igraph_integer_t i, j; + igraph_integer_t nodes_reached; + igraph_adjlist_t allneis; + + igraph_integer_t actdist = 0; + + igraph_dqueue_int_t q; + + igraph_integer_t 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_CHECK(igraph_vector_int_resize(reachable_count, nodes_to_calc)); + } + + if (all_reachable) { + *all_reachable = true; /* be optimistic */ + } + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode for closeness.", IGRAPH_EINVMODE); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&already_counted, no_of_nodes); + IGRAPH_DQUEUE_INT_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_int_clear(&q); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, IGRAPH_VIT_GET(vit))); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + VECTOR(already_counted)[IGRAPH_VIT_GET(vit)] = i + 1; + + IGRAPH_PROGRESS("Closeness: ", 100.0 * i / nodes_to_calc, NULL); + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&q); + actdist = igraph_dqueue_int_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); + igraph_integer_t nei_count = igraph_vector_int_size(neis); + for (j = 0; j < nei_count; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (VECTOR(already_counted)[neighbor] == i + 1) { + continue; + } + VECTOR(already_counted)[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_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 = false; + } + } + } + + IGRAPH_PROGRESS("Closeness: ", 100.0, NULL); + + /* Clean */ + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&already_counted); + igraph_vit_destroy(&vit); + igraph_adjlist_destroy(&allneis); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +/***** Harmonic centrality *****/ + +static igraph_error_t 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) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t already_counted; + igraph_vector_int_t *neis; + igraph_integer_t i, j; + igraph_adjlist_t allneis; + + igraph_integer_t actdist = 0; + + igraph_dqueue_int_t q; + + igraph_integer_t 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_INT_INIT_FINALLY(&already_counted, no_of_nodes); + IGRAPH_DQUEUE_INT_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++) + { + igraph_integer_t source = IGRAPH_VIT_GET(vit); + + igraph_dqueue_int_clear(&q); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_int_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_int_empty(&q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&q); + actdist = igraph_dqueue_int_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); + igraph_integer_t nei_count = igraph_vector_int_size(neis); + for (j = 0; j < nei_count; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (VECTOR(already_counted)[neighbor] == i + 1) { + continue; + } + VECTOR(already_counted)[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_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_int_destroy(&q); + igraph_vector_int_destroy(&already_counted); + igraph_vit_destroy(&vit); + igraph_adjlist_destroy(&allneis); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t 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_distances_dijkstra() for the implementation + details and the dirty tricks. */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + igraph_2wheap_t Q; + igraph_vit_t vit; + igraph_integer_t nodes_to_calc; + + igraph_lazy_inclist_t inclist; + igraph_integer_t i, j; + + igraph_vector_t dist; + igraph_vector_int_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 (isnan(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_int_init(&which, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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++) { + + igraph_integer_t 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_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); + igraph_integer_t nlen; + + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + 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++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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_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_int_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: At most O(n|E|) for the unweighted case and O(n|E|log|V|+|V|) + * for the weighted case, where n is the number + * of vertices for which the calculation is done, |V| is the number of vertices + * and |E| is the number of edges in the graph. The timing decreases with smaller + * cutoffs in a way that depends on the graph structure. + * + * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_betweenness(). + */ + +igraph_error_t 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|) for the unweighted case and O(n*|E|log|V|+|V|) + * for the weighted case, where n is the number + * of vertices for which the calculation is done, |V| is the number of vertices + * and |E| is the number of edges in the graph. + * + * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_degree(), \ref igraph_betweenness(). + */ + +igraph_error_t 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/centrality/coreness.c b/src/centrality/coreness.c new file mode 100644 index 0000000..e48d18f --- /dev/null +++ b/src/centrality/coreness.c @@ -0,0 +1,156 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2023 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_community.h" + +#include "igraph_memory.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" + +/** + * \function igraph_coreness + * \brief The coreness of the vertices in a graph. + * + * 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. + * https://arxiv.org/abs/cs/0310049 + * + * \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. + */ + +igraph_error_t igraph_coreness(const igraph_t *graph, + igraph_vector_int_t *cores, igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t *bin, *vert, *pos; + igraph_integer_t maxdeg; + igraph_vector_int_t neis; + igraph_neimode_t omode; + + if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) { + IGRAPH_ERROR("Invalid mode in k-cores.", IGRAPH_EINVMODE); + } + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + omode = IGRAPH_REVERSE_MODE(mode); + + /* catch null graph */ + if (no_of_nodes == 0) { + igraph_vector_int_clear(cores); + return IGRAPH_SUCCESS; + } + + vert = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(vert, "Insufficient memory for k-cores."); + IGRAPH_FINALLY(igraph_free, vert); + + pos = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(pos, "Insufficient memory for k-cores."); + IGRAPH_FINALLY(igraph_free, pos); + + /* maximum degree + degree of vertices */ + IGRAPH_CHECK(igraph_degree(graph, cores, igraph_vss_all(), mode, /* loops= */ true)); + + /* null graph was already handled earlier, 'cores' is not empty */ + maxdeg = igraph_vector_int_max(cores); + + bin = IGRAPH_CALLOC(maxdeg + 1, igraph_integer_t); + IGRAPH_CHECK_OOM(bin, "Insufficient memory for k-cores."); + IGRAPH_FINALLY(igraph_free, bin); + + /* degree histogram */ + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + bin[ VECTOR(*cores)[i] ] += 1; + } + + /* start pointers */ + for (igraph_integer_t d = 0, start = 0; d <= maxdeg; d++) { + igraph_integer_t k = bin[d]; + bin[d] = start; + start += k; + } + + /* sort in vert (and corrupt bin) */ + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + pos[i] = bin[VECTOR(*cores)[i]]; + vert[pos[i]] = i; + bin[VECTOR(*cores)[i]] += 1; + } + + /* correct bin */ + for (igraph_integer_t d = maxdeg; d > 0; d--) { + bin[d] = bin[d - 1]; + } + bin[0] = 0; + + /* this is the main algorithm */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t v = vert[i]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, omode)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < nei_count; j++) { + igraph_integer_t u = VECTOR(neis)[j]; + if (VECTOR(*cores)[u] > VECTOR(*cores)[v]) { + igraph_integer_t du = VECTOR(*cores)[u]; + igraph_integer_t pu = pos[u]; + igraph_integer_t pw = bin[du]; + igraph_integer_t 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_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + + igraph_free(bin); + igraph_free(pos); + igraph_free(vert); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/centrality/eigenvector.c b/src/centrality/eigenvector.c new file mode 100644 index 0000000..55425a2 --- /dev/null +++ b/src/centrality/eigenvector.c @@ -0,0 +1,552 @@ +/* -*- 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_adjlist.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_structural.h" +#include "igraph_topology.h" + +#include "centrality/centrality_internal.h" + +#include + +/* Multiplies vector 'from' by the unweighted adjacency matrix and stores the result in 'to'. */ +static igraph_error_t adjmat_mul_unweighted(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_adjlist_t *adjlist = extra; + igraph_vector_int_t *neis; + igraph_integer_t 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] += from[nei]; + } + } + + return IGRAPH_SUCCESS; +} + +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; + +/* Multiplies vector 'from' by the weighted adjacency matrix and stores the result in 'to'. */ +static igraph_error_t adjmat_mul_weighted(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; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*edges)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + to[i] += w * from[nei]; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_bool_t negative_weights = false; + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); + } + + if (igraph_ecount(graph) == 0) { + /* special case: empty graph */ + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(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_ERRORF("Weights vector length (%" IGRAPH_PRId ") not equal to " + "number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + igraph_vector_size(weights), igraph_ecount(graph)); + } + /* Safe to call minmax, ecount == 0 case was caught earlier */ + igraph_vector_minmax(weights, &min, &max); + if (min == 0 && max == 0) { + /* special case: all weights are zeros */ + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(igraph_vector_resize(vector, igraph_vcount(graph))); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + + if (min < 0) { + /* When there are negative weights, the eigenvalue and the eigenvector are no + * longer guaranteed to be non-negative. */ + negative_weights = true; + IGRAPH_WARNING("Negative weight in graph. The largest eigenvalue " + "will be selected, but it may not be the largest in magnitude. " + "Some eigenvector centralities may be negative."); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&values, 0); + IGRAPH_MATRIX_INIT_FINALLY(&vectors, no_of_nodes, 1); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), + IGRAPH_ALL, IGRAPH_LOOPS, weights)); + RNG_BEGIN(); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(degree)[i]) { + /* Note: Keep random perturbation non-negative. */ + MATRIX(vectors, i, 0) = VECTOR(degree)[i] + RNG_UNIF(0, 1e-4); + } else { + MATRIX(vectors, i, 0) = 0.01; + } + } + RNG_END(); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + options->n = (int) no_of_nodes; + 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(adjmat_mul_unweighted, + &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(adjmat_mul_weighted, + &data, options, 0, &values, &vectors)); + + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } + + if (vector) { + igraph_real_t amax = 0; + igraph_integer_t which = 0; + IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); + + if (!negative_weights && 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); + VECTOR(values)[0] = 0; + } else { + for (i = 0; i < no_of_nodes; 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) */ + if (! negative_weights) { + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + } + } + + if (value) { + *value = VECTOR(values)[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; +} + +static igraph_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + igraph_bool_t negative_weights = false; + + if (igraph_ecount(graph) == 0) { + /* special case: empty graph */ + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(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_CHECK(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_ERRORF("Weights vector length (%" IGRAPH_PRId ") not equal to " + "number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + igraph_vector_size(weights), igraph_ecount(graph)); + } + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + igraph_vector_minmax(weights, &min, &max); + + if (min < 0.0) { + /* When there are negative weights, the eigenvalue and the eigenvector are no + * longer guaranteed to be non-negative, or even real-valued. */ + negative_weights = true; + IGRAPH_WARNING("Negative weights in directed graph, eigenpair may be complex."); + } + if (min == 0.0 && max == 0.0) { + /* special case: all weights are zeros */ + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(igraph_vector_resize(vector, igraph_vcount(graph))); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + } + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); + } + + options->n = (int) no_of_nodes; + options->start = 1; + options->nev = 1; + /* Use higher NCV than usual as long directed cycles cause convergence issues. + * According to numerical experiments, a cycle graph C_n tends to need about + * NCV = n/4 at minimum. */ + options->ncv = no_of_nodes > 30 ? 30 : no_of_nodes; + /* 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, no_of_nodes, 1); + + IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS, weights)); + RNG_BEGIN(); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(indegree)[i]) { + /* Note: Keep random perturbation non-negative. */ + MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(0, 1e-4); + } else { + MATRIX(vectors, i, 0) = 0.01; + } + } + 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(adjmat_mul_unweighted, + &adjlist, options, NULL, &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(adjmat_mul_weighted, + &data, options, NULL, &values, &vectors)); + + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } + + if (vector) { + igraph_real_t amax = 0; + igraph_integer_t which = 0; + + IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); + + if (!negative_weights && 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 < no_of_nodes; 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) */ + if (! negative_weights) { + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + } + + if (value) { + *value = MATRIX(values, 0, 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_eigenvector_centrality + * \brief 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_j = sum_i w_ij c_i, where w_ij is the weight + * of the edge connecting vertex \c i to \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 (strongly) connected graphs. + * Undirected 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. + * + * + * When working with directed graphs, consider using hub and authority + * scores instead, see \ref igraph_hub_and_authority_scores(). + * + * \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 (indicating no edge weights), or a vector + * giving the weights of the edges. Weights should be positive to guarantee + * a meaningful result. The algorithm might produce complex numbers when some + * weights are negative and the graph is directed. In this case only + * the real part is reported. + * \param options Options to ARPACK. See \ref igraph_arpack_options_t + * for details. Supply \c NULL here to use the defaults. 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. + * \ref igraph_hub_and_authority_scores() for a similar pair of measures + * intended for directed graphs. + * + * \example examples/simple/eigenvector_centrality.c + */ + +igraph_error_t 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 (!options) { + options = igraph_arpack_options_get_default(); + } + + 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); + } +} diff --git a/src/centrality/hub_authority.c b/src/centrality/hub_authority.c new file mode 100644 index 0000000..9145a4b --- /dev/null +++ b/src/centrality/hub_authority.c @@ -0,0 +1,523 @@ +/* -*- 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_adjlist.h" +#include "igraph_blas.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "centrality/centrality_internal.h" + +#include + +/* 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; + +static igraph_error_t igraph_i_kleinberg_unweighted_hub_to_auth( + igraph_integer_t n, igraph_vector_t *to, const igraph_real_t *from, + igraph_adjlist_t *in) { + igraph_vector_int_t *neis; + igraph_integer_t i, j, nlen; + + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(in, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*to)[i] = 0.0; + for (j = 0; j < nlen; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + VECTOR(*to)[i] += from[nei]; + } + } + return IGRAPH_SUCCESS; +} + +/* ARPACK auxiliary routine for the unweighted HITS algorithm */ +static igraph_error_t 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 *out = data->out; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + igraph_integer_t i, j, nlen; + + igraph_i_kleinberg_unweighted_hub_to_auth(n, tmp, from, data->in); + + 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] += VECTOR(*tmp)[nei]; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_kleinberg_weighted_hub_to_auth(igraph_integer_t n, + igraph_vector_t *to, const igraph_real_t *from, igraph_inclist_t *in, + const igraph_t *g, const igraph_vector_t *weights) { + igraph_vector_int_t *neis; + igraph_integer_t nlen, i, j; + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(in, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*to)[i] = 0.0; + for (j = 0; j < nlen; j++) { + igraph_integer_t nei_edge = VECTOR(*neis)[j]; + igraph_integer_t nei = IGRAPH_OTHER(g, nei_edge, i); + VECTOR(*to)[i] += from[nei] * VECTOR(*weights)[nei_edge]; + } + } + return IGRAPH_SUCCESS; +} + +/* ARPACK auxiliary routine for the weighted HITS algorithm */ +static igraph_error_t 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 *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; + igraph_integer_t i, j, nlen; + + igraph_i_kleinberg_weighted_hub_to_auth(n, tmp, from, data->in, g, weights); + + 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++) { + igraph_integer_t nei_edge = VECTOR(*neis)[j]; + igraph_integer_t nei = IGRAPH_OTHER(g, nei_edge, i); + to[i] += VECTOR(*tmp)[nei] * VECTOR(*weights)[nei_edge]; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hub_and_authority_scores + * \brief Kleinberg's hub and authority scores (HITS). + * + * Hub and authority scores are a generalization of the ideas behind + * eigenvector centrality to directed graphs. The authority score of + * a vertex is proportional to the sum of the hub scores of vertices + * that point to it. Conversely, the hub score of a vertex is proportional + * to the sum of authority scores of vertices that it points to. These + * concepts are also known under the name Hyperlink-Induced Topic Search (HITS). + * + * + * The hub and authority scores of the vertices are defined as the principal + * eigenvectors of A A^T and A^T A, respectively, + * where A is the adjacency matrix of the graph and A^T + * is its transposed. + * + * + * If vector \c h and \c a contain hub and authority scores, then the two + * scores are related by h = Aa and a = A^T h. + * When the principal eigenvalue of A A^T is degenerate, there + * is no unique solution to the hub- and authority-score problem. + * igraph guarantees that the scores that are returned are matching, i.e. are + * related by these formulas, even in this situation. + * + * + * The concept of hub and authority scores were developed for \em directed graphs. + * In undirected graphs, both the hub and authority scores are equal to the + * eigenvector centrality, which can be computed using + * \ref igraph_eigenvector_centrality(). + * + * + * 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). + * https://doi.org/10.1145/324133.324140 + * Also appears as IBM Research Report RJ 10076, May + * 1997. + * + * \param graph The input graph. Can be directed and undirected. + * \param hub_vector Pointer to an initialized vector, the hub scores are + * stored here. If a null pointer then it is ignored. + * \param authority_vector Pointer to an initialized vector, the authority scores are + * stored here. If a null pointer then it is ignored. + * \param value If not a null pointer then the eigenvalue + * corresponding to the calculated eigenvectors 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 (meaning 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. Supply \c NULL here to use the defaults. Note that the function + * overwrites the n (number of vertices) parameter and + * it always starts the calculation from a 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_pagerank(), \ref igraph_personalized_pagerank(); + * \ref igraph_eigenvector_centrality() for a similar measure intended + * for undirected graphs. + */ +igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, + igraph_vector_t *hub_vector, igraph_vector_t *authority_vector, + igraph_real_t *value, igraph_bool_t scale, + const igraph_vector_t *weights, igraph_arpack_options_t *options) { + + /* The current implementation computes hub scores, i.e the principal + * eigenvector of A A^T, and transforms these to authority scores as + * authority = A^T hub. */ + + igraph_adjlist_t inadjlist, outadjlist; + igraph_inclist_t ininclist, outinclist; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_t tmp; + igraph_vector_t values; + igraph_matrix_t vectors; + igraph_i_kleinberg_data_t extra; + igraph_i_kleinberg_data2_t extra2; + igraph_vector_t *my_hub_vector_p; + igraph_vector_t my_hub_vector; + igraph_bool_t negative_weights = false; + + if (igraph_ecount(graph) == 0) { + /* special case: empty graph */ + if (value) { + *value = igraph_ecount(graph) ? 1.0 : IGRAPH_NAN; + } + if (hub_vector) { + IGRAPH_CHECK(igraph_vector_resize(hub_vector, no_of_nodes)); + igraph_vector_fill(hub_vector, 1.0); + } + if (authority_vector) { + IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); + igraph_vector_fill(authority_vector, 1.0); + } + return IGRAPH_SUCCESS; + } + + if (weights) { + igraph_real_t min, max; + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERRORF( + "Weights vector length (%" IGRAPH_PRId ") should match number of " + "edges (%" IGRAPH_PRId ") when calculating " + "hub or authority scores.", + IGRAPH_EINVAL, + igraph_vector_size(weights), + igraph_ecount(graph)); + } + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + igraph_vector_minmax(weights, &min, &max); + + if (min < 0.0) { + /* When there are negative weights, the principal eigenvalue and the eigenvector + * are no longer guaranteed to be non-negative. */ + negative_weights = true; + IGRAPH_WARNING("Negative weight in graph. The largest eigenvalue " + "will be selected, but it may not be the largest in magnitude. " + "Some hub and authority scores may be negative."); + } + + if (min == 0 && max == 0) { + /* special case: all weights are zeros */ + if (value) { + *value = IGRAPH_NAN; + } + if (hub_vector) { + IGRAPH_CHECK(igraph_vector_resize(hub_vector, no_of_nodes)); + igraph_vector_fill(hub_vector, 1); + } + if (authority_vector) { + IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); + igraph_vector_fill(authority_vector, 1); + } + return IGRAPH_SUCCESS; + } + } + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK", IGRAPH_EOVERFLOW); + } + + if (!options) { + options = igraph_arpack_options_get_default(); + } + + options->n = no_of_nodes; + 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 (weights == NULL) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &inadjlist, IGRAPH_IN, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &inadjlist); + IGRAPH_CHECK(igraph_adjlist_init(graph, &outadjlist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &outadjlist); + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &ininclist, IGRAPH_IN, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &ininclist); + IGRAPH_CHECK(igraph_inclist_init(graph, &outinclist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &outinclist); + } + + IGRAPH_CHECK(igraph_strength(graph, &tmp, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, weights)); + RNG_BEGIN(); + for (igraph_integer_t i = 0; i < options->n; i++) { + if (VECTOR(tmp)[i] != 0) { + /* Note: Keep random perturbation non-negative. */ + MATRIX(vectors, i, 0) = VECTOR(tmp)[i] + RNG_UNIF(0, 1e-4); + } else { + MATRIX(vectors, i, 0) = 0.01; + } + } + RNG_END(); + + 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] = 'A'; + + if (weights == NULL) { + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_unweighted, &extra, + options, 0, &values, &vectors)); + } else { + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_weighted, &extra2, + options, 0, &values, &vectors)); + } + + + if (value) { + *value = VECTOR(values)[0]; + } + + if (hub_vector || authority_vector) { + if (!hub_vector) { + IGRAPH_VECTOR_INIT_FINALLY(&my_hub_vector, options->n); + my_hub_vector_p = &my_hub_vector; + } else { + my_hub_vector_p = hub_vector; + } + igraph_real_t amax = 0; + igraph_integer_t which = 0; + + IGRAPH_CHECK(igraph_vector_resize(my_hub_vector_p, options->n)); + for (igraph_integer_t i = 0; i < options->n; i++) { + igraph_real_t tmp; + VECTOR(*my_hub_vector_p)[i] = MATRIX(vectors, i, 0); + tmp = fabs(VECTOR(*my_hub_vector_p)[i]); + if (tmp > amax) { + amax = tmp; + which = i; + } + } + if (scale && amax != 0) { + igraph_vector_scale(my_hub_vector_p, 1 / VECTOR(*my_hub_vector_p)[which]); + } else if (igraph_i_vector_mostly_negative(my_hub_vector_p)) { + igraph_vector_scale(my_hub_vector_p, -1.0); + } + + /* Correction for numeric inaccuracies (eliminating -0.0) */ + if (! negative_weights) { + for (igraph_integer_t i = 0; i < options->n; i++) { + if (VECTOR(*my_hub_vector_p)[i] < 0) { + VECTOR(*my_hub_vector_p)[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); + + if (authority_vector) { + igraph_real_t norm; + IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); + igraph_vector_null(authority_vector); + if (weights == NULL) { + igraph_i_kleinberg_unweighted_hub_to_auth(no_of_nodes, authority_vector, &VECTOR(*my_hub_vector_p)[0], &inadjlist); + } else { + igraph_i_kleinberg_weighted_hub_to_auth(no_of_nodes, authority_vector, &VECTOR(*my_hub_vector_p)[0], &ininclist, graph, weights); + } + if (!scale) { + norm = 1.0 / igraph_blas_dnrm2(authority_vector); + } else { + norm = 1.0 / igraph_vector_max(authority_vector); + } + igraph_vector_scale(authority_vector, norm); + } + + if (!hub_vector && authority_vector) { + igraph_vector_destroy(&my_hub_vector); + IGRAPH_FINALLY_CLEAN(1); + } + if (weights == NULL) { + igraph_adjlist_destroy(&outadjlist); + igraph_adjlist_destroy(&inadjlist); + IGRAPH_FINALLY_CLEAN(2); + } else { + igraph_inclist_destroy(&outinclist); + igraph_inclist_destroy(&ininclist); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hub_score + * \brief Kleinberg's hub scores. + * + * \deprecated-by igraph_hub_and_authority_scores 0.10.5 + * + * 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_hub_and_authority_scores() to compute + * hub and authrotity scores efficiently at the same time, + * \ref igraph_authority_score() for the companion measure, + * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), + * \ref igraph_eigenvector_centrality() for similar measures. + */ + +igraph_error_t 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_hub_and_authority_scores(graph, vector, NULL, value, scale, weights, options); +} + +/** + * \function igraph_authority_score + * \brief Kleinberg's authority scores. + * + * \deprecated-by igraph_hub_and_authority_scores 0.10.5 + * + * 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_and_authority_scores() to compute + * hub and authrotity scores efficiently at the same time, + * \ref igraph_hub_score() for the companion measure, + * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), + * \ref igraph_eigenvector_centrality() for similar measures. + */ + +igraph_error_t 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_hub_and_authority_scores(graph, NULL, vector, value, scale, weights, options); +} diff --git a/src/centrality/pagerank.c b/src/centrality/pagerank.c new file mode 100644 index 0000000..375170b --- /dev/null +++ b/src/centrality/pagerank.c @@ -0,0 +1,715 @@ +/* -*- 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_adjlist.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "centrality/prpack_internal.h" + +#include + +static igraph_error_t 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); + +typedef struct { + const igraph_t *graph; + igraph_adjlist_t *adjlist; + igraph_real_t damping; + igraph_vector_t *outdegree; + igraph_vector_t *tmp; + igraph_vector_t *reset; +} pagerank_data_t; + +typedef struct { + 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; +} pagerank_data_weighted_t; + +/* The two pagerank_operator functions below update the probabilities of a random walker + * being in each of the vertices after one step of the walk. */ + +static igraph_error_t pagerank_operator_unweighted(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + 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; + igraph_integer_t 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++) { + igraph_integer_t nei = 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 IGRAPH_SUCCESS; +} + +static igraph_error_t pagerank_operator_weighted(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + pagerank_data_weighted_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; + igraph_integer_t i, j, nlen; + igraph_real_t sumfrom = 0.0; + igraph_vector_int_t *neis; + igraph_real_t fact = 1 - data->damping; + + /* + printf("PageRank weighted: multiplying vector: "); + for (i=0; i 0) { + sumfrom += from[i] * fact; + VECTOR(*tmp)[i] = from[i] / VECTOR(*outdegree)[i]; + } else { + sumfrom += from[i]; + /* The following value is used only when all outgoing edges have + * weight zero (as opposed to there being no outgoing edges at all). + * We set it to zero to avoid a 0.0*inf situation when computing + * to[i] below. */ + VECTOR(*tmp)[i] = 0; + } + } + + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(inclist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + to[i] += VECTOR(*weights)[edge] * VECTOR(*tmp)[nei]; + } + to[i] *= data->damping; + } + + /* printf("sumfrom = %.6f\n", (float)sumfrom); */ + + if (reset) { + /* Running personalized PageRank */ + for (i = 0; i < n; i++) { + to[i] += sumfrom * VECTOR(*reset)[i]; + } + } else { + /* Traditional PageRank with uniform reset vector */ + sumfrom /= n; + for (i = 0; i < n; i++) { + to[i] += sumfrom; + } + } + + /* + printf("PageRank weighted: multiplied vector: "); + for (i=0; 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, which phrases the PageRank calculation + * as an eigenvalue problem, which is then solved using 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. PRPACK uses an + * algebraic method, i.e. solves a linear system to obtain the PageRank + * scores. + * + * + * 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. + * https://doi.org/10.1016/S0169-7552(98)00110-X + * + * \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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, + * the eigenvalue corresponding to the PageRank vector is stored here. It is + * expected to be exactly one. Checking this value can be used to diagnose cases + * when ARPACK failed to converge to the leading eigenvector. + * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. + * \param vids The vertex IDs for which the PageRank is returned. This parameter + * is only for convenience. Computing PageRank for fewer than all vertices will + * not speed up the calculation. + * \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. Supply \c NULL here to use the defaults. 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 + */ + +igraph_error_t 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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, + * the eigenvalue corresponding to the PageRank vector is stored here. It is + * expected to be exactly one. Checking this value can be used to diagnose cases + * when ARPACK failed to converge to the leading eigenvector. + * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. + * \param vids The vertex IDs for which the PageRank is returned. This parameter + * is only for convenience. Computing PageRank for fewer than all vertices will + * not speed up the calculation. + * \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. Supply \c NULL here to use the defaults. 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. + */ + +igraph_error_t 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)[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 IGRAPH_SUCCESS; +} + +/** + * \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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, + * the eigenvalue corresponding to the PageRank vector is stored here. It is + * expected to be exactly one. Checking this value can be used to diagnose cases + * when ARPACK failed to converge to the leading eigenvector. + * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. + * \param vids The vertex IDs for which the PageRank is returned. This parameter + * is only for convenience. Computing PageRank for fewer than all vertices will + * not speed up the calculation. + * \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. Supply \c NULL here to use the defaults. 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. + */ +igraph_error_t 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 ? options : igraph_arpack_options_get_default() + ); + } 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 igraph_error_t 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; + + igraph_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + igraph_real_t reset_sum; /* used only when reset != NULL */ + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); + } + + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid length of weights vector when calculating PageRank scores.", IGRAPH_EINVAL); + } + + if (reset && igraph_vector_size(reset) != no_of_nodes) { + IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); + } + + if (reset) { + reset_sum = igraph_vector_sum(reset); + if (no_of_nodes > 0 && reset_sum == 0) { + IGRAPH_ERROR("The sum of the elements in the reset vector must not be zero.", IGRAPH_EINVAL); + } + + igraph_real_t reset_min = igraph_vector_min(reset); + if (reset_min < 0) { + IGRAPH_ERROR("The reset vector must not contain negative elements.", IGRAPH_EINVAL); + } + if (isnan(reset_min)) { + IGRAPH_ERROR("The reset vector must not contain NaN values.", 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) { + if (reset && no_of_nodes > 0) { + IGRAPH_CHECK(igraph_vector_update(vector, reset)); + igraph_vector_scale(vector, 1.0 / reset_sum); + } else { + IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); + igraph_vector_fill(vector, 1.0 / no_of_nodes); + } + } + return IGRAPH_SUCCESS; + } + + options->n = (int) no_of_nodes; + options->nev = 1; + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rnsolve */ + options->which[0] = 'L'; options->which[1] = 'R'; + options->start = 1; /* no random start vector */ + + directed = directed && igraph_is_directed(graph); + + if (weights) { + igraph_real_t min, max; + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + igraph_vector_minmax(weights, &min, &max); + if (min < 0) { + IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); + } + if (isnan(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 */ + IGRAPH_CHECK(igraph_vector_init_copy(&normalized_reset, reset)); + IGRAPH_FINALLY(igraph_vector_destroy, &normalized_reset); + + igraph_vector_scale(&normalized_reset, 1.0 / reset_sum); + } + + IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(), + directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS, weights)); + IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), + directed ? IGRAPH_IN : IGRAPH_ALL, IGRAPH_LOOPS, weights)); + + /* Set up an appropriate starting vector. We start from the (possibly weight) in-degrees + * plus some small random noise to avoid convergence problems. */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(indegree)[i] > 0) { + /* Note: Keep random perturbation non-negative. */ + MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(0, 1e-4); + } else { + MATRIX(vectors, i, 0) = 1; + } + } + + if (!weights) { + + igraph_adjlist_t adjlist; + 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_adjlist_init(graph, &adjlist, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_arpack_rnsolve(pagerank_operator_unweighted, + &data, options, NULL, &values, &vectors)); + + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + } else { + + igraph_inclist_t inclist; + pagerank_data_weighted_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); + + IGRAPH_CHECK(igraph_arpack_rnsolve(pagerank_operator_weighted, + &data, options, NULL, &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) { + igraph_vit_t vit; + igraph_integer_t 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, 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/centrality/prpack.cpp b/src/centrality/prpack.cpp new file mode 100644 index 0000000..5c9b32f --- /dev/null +++ b/src/centrality/prpack.cpp @@ -0,0 +1,134 @@ +/* -*- 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" + +#include + +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. + */ +igraph_error_t 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) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + + igraph_integer_t i, no_of_nodes = igraph_vcount(graph); + + double *u = nullptr; + std::unique_ptr v; + + 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 (isnan(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.reset(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.get(); + } + + // 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; + IGRAPH_CHECK(prpack_graph.convert_from_igraph(graph, weights, directed)); + prpack_solver solver(&prpack_graph, false); + std::unique_ptr res( solver.solve(damping, 1e-10, u, v.get(), "") ); + + // Delete the personalization vector + v.reset(); + + // 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 + { + // Use of igraph "finally" stack is safe in this block + // since no exceptions can be thrown from here. + + igraph_vit_t vit; + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + igraph_integer_t 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[IGRAPH_VIT_GET(vit)]; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + } + + // PRPACK calculates PageRank scores by solving a linear system, + // so there is no eigenvalue. We return an exact 1.0 in all cases. + if (value) { + *value = 1.0; + } + + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END; +} diff --git a/src/centrality/prpack/CMakeLists.txt b/src/centrality/prpack/CMakeLists.txt new file mode 100644 index 0000000..8bf5291 --- /dev/null +++ b/src/centrality/prpack/CMakeLists.txt @@ -0,0 +1,44 @@ +# Declare the files needed to compile the PRPACK-related stuff +add_library( + prpack + OBJECT + prpack_base_graph.cpp + prpack_igraph_graph.cpp + prpack_preprocessed_ge_graph.cpp + prpack_preprocessed_gs_graph.cpp + prpack_preprocessed_scc_graph.cpp + prpack_preprocessed_schur_graph.cpp + prpack_result.cpp + prpack_solver.cpp + prpack_utils.cpp +) + +target_compile_definitions( + prpack + PUBLIC + PRPACK_IGRAPH_SUPPORT=1 +) + +target_include_directories( + prpack + PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_BINARY_DIR}/include +) + +if (BUILD_SHARED_LIBS) + set_property(TARGET prpack PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +# Since these are included as object files, they should call the +# function as is (without visibility specification) +target_compile_definitions(prpack PRIVATE IGRAPH_STATIC) + +# PRPACK attempts to use OpenMP pragmas, so check whether we need any extra +# compiler flags to support it +if(IGRAPH_OPENMP_SUPPORT) + target_link_libraries(prpack PRIVATE OpenMP::OpenMP_CXX) +endif() + +# Turn on all warnings for GCC, clang and MSVC +use_all_warnings(prpack) diff --git a/src/centrality/prpack/prpack.h b/src/centrality/prpack/prpack.h new file mode 100644 index 0000000..bcddf37 --- /dev/null +++ b/src/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/centrality/prpack/prpack_base_graph.cpp b/src/centrality/prpack/prpack_base_graph.cpp new file mode 100644 index 0000000..f89febb --- /dev/null +++ b/src/centrality/prpack/prpack_base_graph.cpp @@ -0,0 +1,345 @@ +#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(g->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; +} + +#if 0 +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); +} +#endif + +prpack_base_graph::~prpack_base_graph() { + delete[] heads; + delete[] tails; + delete[] vals; +} + +#if 0 +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; +} +#endif + +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(); +#if 0 + void read_smat(std::FILE* f, const bool weighted); + void read_edges(std::FILE* f); + void read_ascii(std::FILE* f); +#endif + 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); +#if 0 + prpack_base_graph(const char* filename, const char* format, const bool weighted); +#endif + prpack_base_graph(int nverts, int nedges, std::pair* edges); + // destructor + ~prpack_base_graph(); + // operations + void normalize_weights(); + }; + +} + +#endif diff --git a/src/centrality/prpack/prpack_csc.h b/src/centrality/prpack/prpack_csc.h new file mode 100644 index 0000000..91f352d --- /dev/null +++ b/src/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/centrality/prpack/prpack_csr.h b/src/centrality/prpack/prpack_csr.h new file mode 100644 index 0000000..3c8c7b7 --- /dev/null +++ b/src/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/centrality/prpack/prpack_edge_list.h b/src/centrality/prpack/prpack_edge_list.h new file mode 100644 index 0000000..a84466f --- /dev/null +++ b/src/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/centrality/prpack/prpack_igraph_graph.cpp b/src/centrality/prpack/prpack_igraph_graph.cpp new file mode 100644 index 0000000..fa84057 --- /dev/null +++ b/src/centrality/prpack/prpack_igraph_graph.cpp @@ -0,0 +1,176 @@ +#include "prpack_igraph_graph.h" +#include +#include +#include + +#include "igraph_interface.h" + +using namespace prpack; +using namespace std; + +#ifdef PRPACK_IGRAPH_SUPPORT + +igraph_error_t prpack_igraph_graph::convert_from_igraph( + const igraph_t *g, const igraph_vector_t *weights, bool directed) { + + const bool treat_as_directed = igraph_is_directed(g) && directed; + const igraph_integer_t vcount = igraph_vcount(g); + const igraph_integer_t ecount = igraph_ecount(g); + double *p_weight; + int *p_head; + + if (vcount > INT_MAX) { + IGRAPH_ERROR("Too many vertices for PRPACK.", IGRAPH_EINVAL); + } + if (ecount > (treat_as_directed ? INT_MAX : INT_MAX/2)) { + IGRAPH_ERROR("Too many edges for PRPACK.", IGRAPH_EINVAL); + } + + if (weights && igraph_vector_size(weights) != ecount) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + + // Get the number of vertices and edges. For undirected graphs, we add + // an edge in both directions. + num_vs = (int) vcount; + num_es = (int) ecount; + 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) { + p_weight = vals = new double[num_es]; + } + + // Count the number of ignored edges (those with negative or zero weight) + int num_ignored_es = 0; + + if (treat_as_directed) { + // Use of igraph "finally" stack is safe in this block + // since no exceptions can be thrown from here. + + // Select all the edges and iterate over them by the source vertices + // Add the edges + igraph_eit_t eit; + IGRAPH_CHECK(igraph_eit_create(g, igraph_ess_all(IGRAPH_EDGEORDER_TO), &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t eid = IGRAPH_EIT_GET(eit); + IGRAPH_EIT_NEXT(eit); + + // Handle the weight + if (weights != NULL) { + // Does this edge have zero or negative weight? + if (VECTOR(*weights)[eid] < 0) { + // Negative weights are disallowed. + IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); + } else if (isnan(VECTOR(*weights)[eid])) { + IGRAPH_ERROR("Edge weights must not be NaN.", IGRAPH_EINVAL); + } else if (VECTOR(*weights)[eid] == 0) { + // Edges with zero weight are ignored. + 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); + IGRAPH_FINALLY_CLEAN(1); + } else { + // Use of igraph "finally" stack is safe in this block + // since no exceptions can be thrown from here. + + // Select all the edges and iterate over them by the target vertices + igraph_vector_int_t neis; + IGRAPH_CHECK(igraph_vector_int_init(&neis, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + + for (int i = 0; i < num_vs; i++) { + IGRAPH_CHECK(igraph_incident(g, &neis, i, IGRAPH_ALL)); + + int temp = igraph_vector_int_size(&neis); + + // TODO: should loop edges be added in both directions? + int *p_head_copy = p_head; + for (int j = 0; j < temp; j++) { + if (weights != NULL) { + if (VECTOR(*weights)[VECTOR(neis)[j]] <= 0) { + // Ignore + num_ignored_es++; + continue; + } + + *p_weight = VECTOR(*weights)[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_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + } + + // Decrease num_es by the number of ignored edges + num_es -= num_ignored_es; + + // Finalize the tails vector + for (int i = 0, sum = 0; i < num_vs; ++i) { + int 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"); + */ + + return IGRAPH_SUCCESS; +} + +// PRPACK_IGRAPH_SUPPORT +#endif diff --git a/src/centrality/prpack/prpack_igraph_graph.h b/src/centrality/prpack/prpack_igraph_graph.h new file mode 100644 index 0000000..6bbcaa0 --- /dev/null +++ b/src/centrality/prpack/prpack_igraph_graph.h @@ -0,0 +1,33 @@ +#ifndef PRPACK_IGRAPH_GRAPH +#define PRPACK_IGRAPH_GRAPH + +#ifdef PRPACK_IGRAPH_SUPPORT + +#include "prpack_base_graph.h" + +#include "igraph_datatype.h" +#include "igraph_vector.h" + +namespace prpack { + + class prpack_igraph_graph : public prpack_base_graph { + public: + // constructors + prpack_igraph_graph() { } + + // We use a separate function to carry out the actual construction of the graph. + // The base class constructor sets the heads/tails/vals arrays to NULL, + // so these can safely be delete'ed by the destructor when + // convert_from_igraph() fails. + igraph_error_t convert_from_igraph(const igraph_t *g, + const igraph_vector_t *weights, + bool directed = true); + }; + +} + +// PRPACK_IGRAPH_SUPPORT +#endif + +// PRPACK_IGRAPH_GRAPH +#endif diff --git a/src/centrality/prpack/prpack_preprocessed_ge_graph.cpp b/src/centrality/prpack/prpack_preprocessed_ge_graph.cpp new file mode 100644 index 0000000..303e30d --- /dev/null +++ b/src/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/centrality/prpack/prpack_preprocessed_ge_graph.h b/src/centrality/prpack/prpack_preprocessed_ge_graph.h new file mode 100644 index 0000000..678568f --- /dev/null +++ b/src/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/centrality/prpack/prpack_preprocessed_graph.h b/src/centrality/prpack/prpack_preprocessed_graph.h new file mode 100644 index 0000000..4f5a318 --- /dev/null +++ b/src/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/centrality/prpack/prpack_preprocessed_gs_graph.cpp b/src/centrality/prpack/prpack_preprocessed_gs_graph.cpp new file mode 100644 index 0000000..2cefd5d --- /dev/null +++ b/src/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/centrality/prpack/prpack_preprocessed_gs_graph.h b/src/centrality/prpack/prpack_preprocessed_gs_graph.h new file mode 100644 index 0000000..eec3fe2 --- /dev/null +++ b/src/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/centrality/prpack/prpack_preprocessed_scc_graph.cpp b/src/centrality/prpack/prpack_preprocessed_scc_graph.cpp new file mode 100644 index 0000000..a34ad7e --- /dev/null +++ b/src/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/centrality/prpack/prpack_preprocessed_scc_graph.h b/src/centrality/prpack/prpack_preprocessed_scc_graph.h new file mode 100644 index 0000000..6584c3f --- /dev/null +++ b/src/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/centrality/prpack/prpack_preprocessed_schur_graph.cpp b/src/centrality/prpack/prpack_preprocessed_schur_graph.cpp new file mode 100644 index 0000000..2a422d0 --- /dev/null +++ b/src/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/centrality/prpack/prpack_preprocessed_schur_graph.h b/src/centrality/prpack/prpack_preprocessed_schur_graph.h new file mode 100644 index 0000000..a0593b1 --- /dev/null +++ b/src/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/centrality/prpack/prpack_result.cpp b/src/centrality/prpack/prpack_result.cpp new file mode 100644 index 0000000..1fef884 --- /dev/null +++ b/src/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/centrality/prpack/prpack_result.h b/src/centrality/prpack/prpack_result.h new file mode 100644 index 0000000..00d96b9 --- /dev/null +++ b/src/centrality/prpack/prpack_result.h @@ -0,0 +1,30 @@ +#ifndef PRPACK_RESULT +#define PRPACK_RESULT + +#include +#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; + int64_t num_es_touched; + std::string method; + int converged; + // constructor + prpack_result(); + // destructor + ~prpack_result(); + }; + +} + +#endif diff --git a/src/centrality/prpack/prpack_solver.cpp b/src/centrality/prpack/prpack_solver.cpp new file mode 100644 index 0000000..4d2d1f5 --- /dev/null +++ b/src/centrality/prpack/prpack_solver.cpp @@ -0,0 +1,886 @@ +#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); +} + +#if 0 +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)); +} +#endif + +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/centrality/prpack/prpack_solver.h b/src/centrality/prpack/prpack_solver.h new file mode 100644 index 0000000..91a3cef --- /dev/null +++ b/src/centrality/prpack/prpack_solver.h @@ -0,0 +1,180 @@ +#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); +#if 0 + prpack_solver(const char* filename, const char* format, const bool weighted); +#endif + // 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/centrality/prpack/prpack_utils.cpp b/src/centrality/prpack/prpack_utils.cpp new file mode 100644 index 0000000..c23eaf8 --- /dev/null +++ b/src/centrality/prpack/prpack_utils.cpp @@ -0,0 +1,58 @@ +/** + * @file prpack_utils.cpp + * An assortment of utility functions for reporting errors, checking time, + * and working with vectors. + */ + +#include "prpack_utils.h" + +#ifdef PRPACK_IGRAPH_SUPPORT +#include "igraph_error.h" +#else +#include +#endif + +#include +using namespace prpack; +using namespace std; + +#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, NULL); + 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_FATALF("Internal error in PRPACK: %s", msg.c_str()); +#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/centrality/prpack/prpack_utils.h b/src/centrality/prpack/prpack_utils.h new file mode 100644 index 0000000..261370d --- /dev/null +++ b/src/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/centrality/prpack_internal.h b/src/centrality/prpack_internal.h new file mode 100644 index 0000000..938a41e --- /dev/null +++ b/src/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_H +#define IGRAPH_PRPACK_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_iterators.h" + +#include "igraph_interface.h" + +__BEGIN_DECLS + +igraph_error_t 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/centrality/truss.cpp b/src/centrality/truss.cpp new file mode 100644 index 0000000..b4fbc60 --- /dev/null +++ b/src/centrality/truss.cpp @@ -0,0 +1,286 @@ +/* + Copyright 2017 The Johns Hopkins University Applied Physics Laboratory LLC. All Rights Reserved. + Copyright 2021 The igraph team. + + Truss algorithm for cohesive subgroups. + + Author: Alex Perrone + Date: 2017-08-03 + Minor edits: The igraph team, 2021 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + 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_community.h" + +#include "igraph_adjlist.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_motifs.h" +#include "igraph_structural.h" + +#include "core/exceptions.h" +#include "core/interruption.h" + +using std::vector; +using std::unordered_set; + + +// Unpack the triangles as a vector of vertices to be a vector of edges. +// So, instead of the triangle specified as vertices [1, 2, 3], return the +// edges as [1, 2, 1, 3, 2, 3] so that the support can be computed. +static igraph_error_t igraph_truss_i_unpack(const igraph_vector_int_t *tri, igraph_vector_int_t *unpacked_tri) { + igraph_integer_t num_triangles = igraph_vector_int_size(tri); + + IGRAPH_CHECK(igraph_vector_int_resize(unpacked_tri, 2 * num_triangles)); + + for (igraph_integer_t i = 0, j = 0; i < num_triangles; i += 3, j += 6) { + VECTOR(*unpacked_tri)[j] = VECTOR(*unpacked_tri)[j+2] = VECTOR(*tri)[i]; + VECTOR(*unpacked_tri)[j+1] = VECTOR(*unpacked_tri)[j+4] = VECTOR(*tri)[i+1]; + VECTOR(*unpacked_tri)[j+3] = VECTOR(*unpacked_tri)[j+5] = VECTOR(*tri)[i+2]; + } + + return IGRAPH_SUCCESS; +} + + +// Compute the edge support, i.e. number of triangles each edge occurs in. +// Time complexity: O(m), where m is the number of edges listed in eid. +static void igraph_truss_i_compute_support(const igraph_vector_int_t *eid, igraph_vector_int_t *support) { + igraph_integer_t m = igraph_vector_int_size(eid); + for (igraph_integer_t i = 0; i < m; ++i) { + VECTOR(*support)[VECTOR(*eid)[i]] += 1; + } +} + + +/* internal function doing the computations once the support is defined */ +static igraph_error_t igraph_i_trussness(const igraph_t *graph, igraph_vector_int_t *support, + igraph_vector_int_t *trussness) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + + igraph_adjlist_t adjlist; + igraph_vector_int_t commonNeighbors; + igraph_vector_bool_t completed; + + // C++ data structures + vector< unordered_set > vec; + + // Allocate memory for result + igraph_integer_t no_of_edges = igraph_vector_int_size(support); + IGRAPH_CHECK(igraph_vector_int_resize(trussness, no_of_edges)); + if (no_of_edges == 0) { + return IGRAPH_SUCCESS; + } + + // Get max possible value = max entry in support. + // This cannot be computed if there are no edges, hence the above check + igraph_integer_t max = igraph_vector_int_max(support); + + // Initialize completed edges. + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&completed, no_of_edges); + + // The vector of levels. Each level of the vector is a set of edges initially + // at that level of support, where support is # of triangles the edge is in. + vec.resize(max + 1); + + // Add each edge to its appropriate level of support. + for (igraph_integer_t i = 0; i < no_of_edges; ++i) { + vec[VECTOR(*support)[i]].insert(i); // insert edge i into its support level + } + + // Record the trussness of edges at level 0. These edges are not part + // of any triangles, so there's not much to do and we "complete" them + for (auto edge : vec[0]) { + VECTOR(*trussness)[edge] = 2; + VECTOR(completed)[edge] = true; + } + + // Initialize variables needed below. + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + IGRAPH_VECTOR_INT_INIT_FINALLY(&commonNeighbors, 0); + + // Move through the levels, one level at a time, starting at first level. + for (igraph_integer_t level = 1; level <= max; ++level) { + + /* Track down edges one at a time */ + while (!vec[level].empty()) { + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_integer_t seed = *vec[level].begin(); // pull out the first edge + vec[level].erase(seed); // remove the first element + + /* Find the vertices of this edge */ + igraph_integer_t fromVertex = IGRAPH_FROM(graph, seed); + igraph_integer_t toVertex = IGRAPH_TO(graph, seed); + + /* Find neighbors of both vertices. If they run into each other, + * there is a triangle. We rely on the neighbor lists being sorted, + * as guaranteed by igraph_adjlist_init(), when computing intersections. */ + igraph_vector_int_t *fromNeighbors = igraph_adjlist_get(&adjlist, fromVertex); + igraph_vector_int_t *toNeighbors = igraph_adjlist_get(&adjlist, toVertex); + igraph_vector_int_t *q1 = fromNeighbors; + igraph_vector_int_t *q2 = toNeighbors; + + if (igraph_vector_int_size(q1) > igraph_vector_int_size(q2)) { + // case: #fromNeighbors > #toNeigbors, so make q1 the smaller set. + q1 = toNeighbors; + q2 = fromNeighbors; + } + + // Intersect the neighbors. + IGRAPH_CHECK(igraph_vector_int_intersect_sorted(q1, q2, &commonNeighbors)); + + /* Go over the overlapping neighbors and check each */ + igraph_integer_t ncommon = igraph_vector_int_size(&commonNeighbors); + for (igraph_integer_t j = 0; j < ncommon; j++) { + igraph_integer_t n = VECTOR(commonNeighbors)[j]; // the common neighbor + igraph_integer_t e1, e2; + IGRAPH_CHECK(igraph_get_eid(graph, &e1, fromVertex, n, IGRAPH_UNDIRECTED, /* error= */ true)); + IGRAPH_CHECK(igraph_get_eid(graph, &e2, toVertex, n, IGRAPH_UNDIRECTED, /* error= */ true)); + + bool e1_complete = VECTOR(completed)[e1]; + bool e2_complete = VECTOR(completed)[e2]; + + if (!e1_complete && !e2_complete) { + igraph_integer_t newLevel; + + // Demote this edge, if higher than current level. + if (VECTOR(*support)[e1] > level) { + VECTOR(*support)[e1] -= 1; // decrement the level + newLevel = VECTOR(*support)[e1]; + vec[newLevel].insert(e1); + vec[newLevel + 1].erase(e1); // the old level + } + // Demote this edge, if higher than current level. + if (VECTOR(*support)[e2] > level) { + VECTOR(*support)[e2] -= 1; // decrement the level + newLevel = VECTOR(*support)[e2]; + vec[newLevel].insert(e2); + vec[newLevel + 1].erase(e2); // the old level + } + } + } + // Record this edge; its level is its trussness. + VECTOR(*trussness)[seed] = level + 2; + VECTOR(completed)[seed] = true; // mark as complete + igraph_vector_int_clear(&commonNeighbors); + } // end while + } // end for-loop over levels + + // Clean up. + igraph_vector_int_destroy(&commonNeighbors); + igraph_adjlist_destroy(&adjlist); + igraph_vector_bool_destroy(&completed); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END; +} + + +/** + * \function igraph_trussness + * \brief Finding the "trussness" of the edges in a network. + * + * A k-truss is a subgraph in which every edge occurs in at least k-2 triangles + * in the subgraph. The trussness of an edge indicates the highest k-truss that + * the edge occurs in. + * + * + * This function returns the highest \c k for each edge. If you are interested in + * a particular k-truss subgraph, you can subset the graph to those edges + * which are >= k because each k-truss is a subgraph of a (k–1)-truss + * Thus, to get all 4-trusses, take k >= 4 because the 5-trusses, 6-trusses, + * etc. need to be included. + * + * + * The current implementation of this function iteratively decrements support + * of each edge using O(|E|) space and O(|E|^1.5) time. The implementation does + * not support multigraphs; use \ref igraph_simplify() to collapse edges before + * calling this function. + * + * + * Reference: + * + * + * See Algorithm 2 in: + * Wang, Jia, and James Cheng. "Truss decomposition in massive networks." + * Proceedings of the VLDB Endowment 5.9 (2012): 812-823. + * https://doi.org/10.14778/2311906.2311909 + * + * \param graph The input graph. Loop edges are allowed; multigraphs are not. + * \param truss Pointer to initialized vector of truss values that will + * indicate the highest k-truss each edge occurs in. It will be resized as + * needed. + * \return Error code. + * + * Time complexity: It should be O(|E|^1.5) according to the reference. + */ +igraph_error_t igraph_trussness(const igraph_t* graph, igraph_vector_int_t* trussness) { + igraph_vector_int_t triangles, support, unpacked_triangles, eid; + igraph_bool_t is_multigraph; + + /* Check whether the graph is a multigraph; trussness will not work for these */ + IGRAPH_CHECK(igraph_has_multiple(graph, &is_multigraph)); + if (! is_multigraph && igraph_is_directed(graph)) { + /* Directed graphs with mutual edges are effectively multigraphs + * when edge directions are ignored. */ + IGRAPH_CHECK(igraph_has_mutual(graph, &is_multigraph, /* loops */ false)); + } + if (is_multigraph) { + IGRAPH_ERROR("Trussness is not implemented for graphs with multi-edges.", IGRAPH_UNIMPLEMENTED); + } + + /* Manage the stack to make it memory safe: do not change the order of + * initialization of the following four vectors */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&support, igraph_ecount(graph)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eid, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&unpacked_triangles, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&triangles, 0); + + // List the triangles as vertex triplets. + IGRAPH_CHECK(igraph_list_triangles(graph, &triangles)); + + // Unpack the triangles from vertex list to edge list. + IGRAPH_CHECK(igraph_truss_i_unpack(&triangles, &unpacked_triangles)); + igraph_vector_int_destroy(&triangles); + IGRAPH_FINALLY_CLEAN(1); + + // Get the edge IDs of the unpacked triangles. Note: a given eid can occur + // multiple times in this list if it is in multiple triangles. + IGRAPH_CHECK(igraph_get_eids(graph, &eid, &unpacked_triangles, /* directed = */ false, /* error = */ true)); + igraph_vector_int_destroy(&unpacked_triangles); + IGRAPH_FINALLY_CLEAN(1); + + // Compute the support of the edges. + igraph_truss_i_compute_support(&eid, &support); + igraph_vector_int_destroy(&eid); + IGRAPH_FINALLY_CLEAN(1); + + // Compute the trussness of the edges. + IGRAPH_CHECK(igraph_i_trussness(graph, &support, trussness)); + igraph_vector_int_destroy(&support); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/cliques/cliquer/CMakeLists.txt b/src/cliques/cliquer/CMakeLists.txt new file mode 100644 index 0000000..7926f4e --- /dev/null +++ b/src/cliques/cliquer/CMakeLists.txt @@ -0,0 +1,27 @@ +# Declare the files needed to compile Cliquer +add_library( + cliquer + OBJECT + EXCLUDE_FROM_ALL + cliquer.c + cliquer_graph.c + reorder.c +) + +target_include_directories( + cliquer + PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_BINARY_DIR}/include + ${PROJECT_BINARY_DIR}/src +) + +if (BUILD_SHARED_LIBS) + set_property(TARGET cliquer PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +# Since these are included as object files, they should call the +# function as is (without visibility specification) +target_compile_definitions(cliquer PRIVATE IGRAPH_STATIC) + +use_all_warnings(cliquer) diff --git a/src/cliques/cliquer/README b/src/cliques/cliquer/README new file mode 100644 index 0000000..e238708 --- /dev/null +++ b/src/cliques/cliquer/README @@ -0,0 +1,61 @@ + +Cliquer - routines for clique searching +--------------------------------------- + + +Cliquer is a set of C routines for finding cliques in an arbitrary +weighted graph. It uses an exact branch-and-bound algorithm recently +developed by Patric Ostergard. It is designed with the aim of being +efficient while still being flexible and easy to use. + +Cliquer was developed on Linux, and it should compile without +modification on most modern UNIX systems. Other operating systems may +require minor changes to the source code. + +Features: + + * support for both weighted and unweighted graphs (faster routines + for unweighted graphs) + * search for maximum clique / maximum-weight clique + * search for clique with size / weight within a given range + * restrict search to maximal cliques + * store found cliques in memory + * call a user-defined function for every clique found + * Cliquer is re-entrant, so you can use the clique-searching + functions from within the callback function + +The full documentation can be obtained via the www page of +Cliquer . + + +License + +Cliquer is Copyright (C) 2002 Sampo Niskanen, Patric Ostergard. + +Cliquer is licensed under the GNU General Public License as published +by the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. The full license is included in +the file LICENSE. + +Basically, you can use Cliquer for any purpose, provided that any +programs or modifications you make and distribute are also licensed +under the GNU GPL. + +ABSOLUTELY NO GUARANTEES OR WARRANTIES are made concerning the +suitability, correctness, or any other aspect of these routines. + + +Contact + +Cliquer was mainly written by Sampo Niskanen . + +For bug-fixes, feedback, and, in particular, for putting your +name on the mailing list for important information regarding Cliquer, +please contact: + +Patric Ostergard +Department of Communications and Networking +Aalto University +P.O. Box 13000, 00076 Aalto +FINLAND + diff --git a/src/cliques/cliquer/cliquer.c b/src/cliques/cliquer/cliquer.c new file mode 100644 index 0000000..218e16a --- /dev/null +++ b/src/cliques/cliquer/cliquer.c @@ -0,0 +1,1850 @@ + +/* + * 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 igraph_error_t sub_unweighted_all(int *table, int size, int min_size, int max_size, + boolean maximal, graph_t *g, + clique_options *opts, CLIQUER_LARGE_INT *num_found); +static igraph_error_t 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 *weight_found); + + +static igraph_error_t store_clique(set_t clique, graph_t *g, clique_options *opts); +static boolean is_maximal(set_t clique, graph_t *g); +static igraph_error_t 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 unnecessary 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 + * num_found - number of cliques found + * + * 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]. + */ +static igraph_error_t unweighted_clique_search_all( + int *table, int start, int min_size, int max_size, boolean maximal, + graph_t *g, clique_options *opts, CLIQUER_LARGE_INT *num_found +) { + /* + struct timeval timeval; + struct tms tms; + */ + int i, j; + int v; + int *newtable; + int newsize; + CLIQUER_LARGE_INT r; + CLIQUER_LARGE_INT count=0; + igraph_error_t retval = IGRAPH_SUCCESS; + + 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; + + if (num_found) { + *num_found = count; + } + + return retval; +} + +/* + * 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 + * num_found - number of cliques found + * + * 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 igraph_error_t sub_unweighted_all(int *table, int size, int min_size, int max_size, + boolean maximal, graph_t *g, + clique_options *opts, CLIQUER_LARGE_INT *num_found) { + igraph_error_t retval = IGRAPH_SUCCESS; + 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++; + retval = store_clique(current_clique, g, opts); + if (retval) { + *num_found = count; + return retval == IGRAPH_STOP ? IGRAPH_SUCCESS : retval; + } + } + if (max_size <= 0) { + /* If we add another element, size will be too big. */ + *num_found = count; + return IGRAPH_SUCCESS; + } + } + + if (size < min_size) { + *num_found = count; + return IGRAPH_SUCCESS; + } + + /* 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 unnecessary loops (next size == p1-newtable) */ + if (p1-newtable < min_size-1) { + continue; + } + + SET_ADD_ELEMENT(current_clique,v); + retval = sub_unweighted_all(newtable,p1-newtable, + min_size-1,max_size-1,maximal,g,opts,&n); + SET_DEL_ELEMENT(current_clique,v); + count += n; + if (retval || n < 0) { + break; + } + count+=n; + } + temp_list[temp_count++]=newtable; + + *num_found = count; + return retval; +} + + + + +/***** 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 igraph_error_t weighted_clique_search_single(int *table, int min_weight, + int max_weight, graph_t *g, + clique_options *opts, int *result) { + /* + 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; + igraph_error_t retval = IGRAPH_SUCCESS; + + ASSERT(result != NULL); + + 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]); + *result = g->weights[table[i]]; + return IGRAPH_SUCCESS; + } + } + + *result = 0; + return IGRAPH_SUCCESS; + } + + 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. */ + *result = search_weight; + return IGRAPH_SUCCESS; + } + 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); + retval=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, &search_weight); + SET_DEL_ELEMENT(current_clique,v); + if (retval || 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. */ + *result = 0; + } else { + *result = clique_size[table[i-1]]; + } + + return retval; +} + + +/* + * 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 + * num_found - number of cliques found + * + * 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 necessarily number of cliques + * in graph, if user/time_function aborts). + */ +static igraph_error_t weighted_clique_search_all(int *table, int start, + int min_weight, int max_weight, + boolean maximal, graph_t *g, + clique_options *opts, int* num_found) { + /* + struct timeval timeval; + struct tms tms; + */ + int i,j; + int v; + int *newtable; + int newsize; + int newweight; + igraph_error_t retval = IGRAPH_SUCCESS; + + 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); + retval=sub_weighted_all(newtable,newsize,newweight, + g->weights[v],min_weight-1,INT_MAX, + min_weight,max_weight,maximal,g,opts,&j); + SET_DEL_ELEMENT(current_clique,v); + + if (retval || 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; + + if (num_found) { + *num_found = clique_list_count; + } + + return retval; +} + +/* + * 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 + * weight_found - weight of the 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. + * + * All cliques of suitable weight found are stored according to opts. + * + * 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 igraph_error_t 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* weight_found) { + igraph_error_t retval = IGRAPH_SUCCESS; + 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. */ + retval = store_clique(current_clique,g,opts); + if (retval) { + *weight_found = -1; + return retval == IGRAPH_STOP ? IGRAPH_SUCCESS : retval; + } + } + if (current_weight >= max_weight) { + /* Clique too heavy. */ + *weight_found = min_weight-1; + return IGRAPH_SUCCESS; + } + } + 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) { + *weight_found = current_weight; + return IGRAPH_SUCCESS; + } else { + *weight_found = min_weight-1; + return IGRAPH_SUCCESS; + } + } else { + *weight_found = prune_low; + return IGRAPH_SUCCESS; + } + } + + /* 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 unnecessary loops */ + if (current_weight+w+newweight <= prune_low) { + continue; + } + + SET_ADD_ELEMENT(current_clique,v); + retval=sub_weighted_all(newtable,p1-newtable, + newweight, + current_weight+w, + prune_low,prune_high, + min_weight,max_weight,maximal, + g,opts, &prune_low); + SET_DEL_ELEMENT(current_clique,v); + if (retval || (prune_low<0) || (prune_low>=prune_high)) { + /* Impossible to find larger clique. */ + break; + } + } + temp_list[temp_count++]=newtable; + + *weight_found = prune_low; + return IGRAPH_SUCCESS; +} + + + + +/***** Helper functions *****/ + + +/* + * store_clique() + * + * Stores a clique according to given user options. + * + * clique - the clique to store + * opts - storage options + * + * Returns the same igraph error code as the one returned by + * opts->user_function(). Returns IGRAPH_SUCCESS if no callback is defined. + */ +static igraph_error_t 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) { + return opts->user_function(clique, g, opts); + } + + return IGRAPH_SUCCESS; +} + +/* + * 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[]. + */ +igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, + boolean maximal, clique_options *opts, set_t *clique) { + int i; + int *table; + set_t s; + igraph_error_t retval = IGRAPH_SUCCESS; + CLIQUER_LARGE_INT found; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=&clique_default_options; + + ASSERT(clique!=NULL); + 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--; + *clique = NULL; + return IGRAPH_SUCCESS; + } + + /* + 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; + retval = unweighted_clique_search_all( + table, i, min_size, max_size, maximal, g, &localopts, &found + ); + set_free(current_clique); + if (retval || !found) { + current_clique=NULL; + } else { + current_clique=s; + } + } + } + + cleanreturn: + *clique = 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 retval; +} + + +/* + * 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 + * num_found - the number of cliques found. This can be less than the number + * of cliques in the graph iff opts->time_function() returns + * FALSE (request abort) or opts->user_function() returns an + * igraph error code + * + * 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(). + */ +igraph_error_t clique_unweighted_find_all( + graph_t *g, int min_size, int max_size, boolean maximal, clique_options *opts, + CLIQUER_LARGE_INT *num_found +) { + int i; + int *table; + CLIQUER_LARGE_INT count; + igraph_error_t retval = IGRAPH_SUCCESS; + + 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--; + if (num_found) { + *num_found = 0; + } + return IGRAPH_SUCCESS; + } + + /* + 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; + + retval = unweighted_clique_search_all(table, i, min_size, max_size, maximal, g, opts, &count); + + 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. + */ +igraph_error_t clique_find_single( + graph_t *g, int min_weight, int max_weight, boolean maximal, + clique_options *opts, set_t *clique +) { + int i; + int *table; + set_t s; + igraph_error_t retval = IGRAPH_SUCCESS; + int weight_found; + int num_found; + + ENTRANCE_SAVE(); + entrance_level++; + + if (opts==NULL) + opts=&clique_default_options; + + ASSERT(clique!=NULL); + 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--; + *clique = NULL; + return IGRAPH_SUCCESS; + } + + /* + 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--; + *clique = NULL; + return IGRAPH_SUCCESS; + } + } + + weight_multiplier = g->weights[0]; + entrance_level--; + retval = clique_unweighted_find_single(g, min_weight, max_weight, maximal, opts, &s); + ENTRANCE_RESTORE(); + *clique = s; + return retval; + } + + /* 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; + + retval = weighted_clique_search_single(table, min_weight, max_weight, g, opts, &weight_found); + + if (retval || weight_found == 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; + + retval = weighted_clique_search_all( + table, i, min_weight, max_weight, maximal, g, &localopts, &num_found + ); + + if (retval || !weight_found) { + 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--; + + *clique = s; + + return retval; +} + + + + + +/* + * 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 + * num_found - the number of cliques found. This can be less than the number + * of cliques in the graph iff opts->time_function() returns + * FALSE (request abort) or opts->user_function() returns an + * igraph error code + * + * 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. + */ +igraph_error_t clique_find_all(graph_t *g, int min_weight, int max_weight, + boolean maximal, clique_options *opts, int *num_found) { + int i,n; + int *table; + CLIQUER_LARGE_INT r; + igraph_error_t retval = IGRAPH_SUCCESS; + + 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--; + if (num_found) { + *num_found = 0; + } + return IGRAPH_SUCCESS; + } + + /* + 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--; + if (num_found) { + *num_found = 0; + } + return IGRAPH_SUCCESS; + } + } + + weight_multiplier = g->weights[0]; + entrance_level--; + retval = clique_unweighted_find_all(g, min_weight, max_weight, maximal, opts, &r); + ENTRANCE_RESTORE(); + if (num_found) { + *num_found = r; + } + return retval; + } + + /* 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 */ + retval = weighted_clique_search_single(table, min_weight, INT_MAX, g, opts, &n); + if (retval || 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 */ + retval = weighted_clique_search_all(table, i, min_weight, max_weight, maximal, g, opts, &n); + +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--; + + if (num_found) { + *num_found = n; + } + + return retval; +} + + + + + + + + + + + + + + + + +#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; + + igraph_error_t (*user_function)(set_t,graph_t *,clique_options *); + void *user_data; + set_t *clique_list; + int clique_list_length; +}; + +/* Weighted clique functions */ +extern igraph_error_t clique_max_weight( + graph_t *g, clique_options *opts, int *weight_found +); +extern igraph_error_t clique_find_single( + graph_t *g, int min_weight, int max_weight, boolean maximal, + clique_options *opts, set_t *clique +); +extern igraph_error_t clique_find_all( + graph_t *g, int req_weight, boolean exact, boolean maximal, + clique_options *opts, int *num_found +); + +/* Unweighted clique functions */ +#define clique_unweighted_max_size clique_unweighted_max_weight +extern igraph_error_t clique_unweighted_max_weight( + graph_t *g, clique_options *opts, int *weight_found +); +extern igraph_error_t clique_unweighted_find_single( + graph_t *g, int min_size, int max_size, boolean maximal, + clique_options *opts, set_t *clique +); +extern igraph_error_t clique_unweighted_find_all( + graph_t *g, int min_size, int max_size, boolean maximal, + clique_options *opts, CLIQUER_LARGE_INT *num_found +); + +/* 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/cliques/cliquer/cliquer_graph.c b/src/cliques/cliquer/cliquer_graph.c new file mode 100644 index 0000000..e94ee20 --- /dev/null +++ b/src/cliques/cliquer/cliquer_graph.c @@ -0,0 +1,764 @@ + +/* + * 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 "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/cliques/cliquer/cliquerconf.h b/src/cliques/cliquer/cliquerconf.h new file mode 100644 index 0000000..47d923b --- /dev/null +++ b/src/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/cliques/cliquer/graph.h b/src/cliques/cliquer/graph.h new file mode 100644 index 0000000..56f92af --- /dev/null +++ b/src/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/cliques/cliquer/misc.h b/src/cliques/cliquer/misc.h new file mode 100644 index 0000000..618b5c1 --- /dev/null +++ b/src/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/cliques/cliquer/reorder.c b/src/cliques/cliquer/reorder.c new file mode 100644 index 0000000..15d197a --- /dev/null +++ b/src/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/cliques/cliquer/reorder.h b/src/cliques/cliquer/reorder.h new file mode 100644 index 0000000..5c06d31 --- /dev/null +++ b/src/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/cliques/cliquer/set.h b/src/cliques/cliquer/set.h new file mode 100644 index 0000000..bca7aa0 --- /dev/null +++ b/src/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; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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_CLIQUER_H +#define IGRAPH_CLIQUER_H + +#include "igraph_decls.h" +#include "igraph_cliques.h" + +__BEGIN_DECLS + +igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_integer_t min_size, igraph_integer_t max_size); + +igraph_error_t igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t min_size, igraph_integer_t max_size); + +igraph_error_t 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); + +igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res, + igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal); + +igraph_error_t igraph_i_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res); + +igraph_error_t igraph_i_weighted_clique_number(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_real_t *res); + +__END_DECLS + +#endif // IGRAPH_CLIQUER_H diff --git a/src/cliques/cliquer_wrapper.c b/src/cliques/cliquer_wrapper.c new file mode 100644 index 0000000..7e2ca16 --- /dev/null +++ b/src/cliques/cliquer_wrapper.c @@ -0,0 +1,459 @@ +/* + IGraph library. + Copyright (C) 2016-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, see . + */ + +#include "igraph_error.h" +#include "igraph_interface.h" + +#include "core/interruption.h" +#include "cliques/cliquer_internal.h" +#include "cliques/cliquer/cliquer.h" + +#include "config.h" + +#include + +/* We shall use this option struct for all calls to Cliquer */ +static IGRAPH_THREAD_LOCAL clique_options igraph_cliquer_opt = { + reorder_by_default, NULL, NULL, NULL, NULL, NULL, NULL, 0 +}; + + +/* Convert an igraph graph to a Cliquer graph */ +static igraph_error_t igraph_to_cliquer(const igraph_t *ig, graph_t **cg) { + igraph_integer_t vcount, ecount; + igraph_integer_t i; + + if (igraph_is_directed(ig)) { + IGRAPH_WARNING("Edge directions are ignored for clique calculations."); + } + + vcount = igraph_vcount(ig); + ecount = igraph_ecount(ig); + + if (vcount > INT_MAX) { + IGRAPH_ERROR("Graph too large for Cliquer.", IGRAPH_EOVERFLOW); + } + + *cg = graph_new((int) vcount); + + for (i = 0; i < ecount; ++i) { + igraph_integer_t s, t; + s = IGRAPH_FROM(ig, i); + t = IGRAPH_TO(ig, i); + if (s != t) { + GRAPH_ADD_EDGE(*cg, s, t); + } + } + + return IGRAPH_SUCCESS; +} + + +/* Copy weights to a Cliquer graph */ +static igraph_error_t set_weights(const igraph_vector_t *vertex_weights, graph_t *g) { + igraph_integer_t i; + + IGRAPH_ASSERT(vertex_weights != NULL); + + if (igraph_vector_size(vertex_weights) != g->n) { + IGRAPH_ERROR("Invalid vertex weight vector length.", IGRAPH_EINVAL); + } + + for (i = 0; i < g->n; ++i) { + g->weights[i] = VECTOR(*vertex_weights)[i]; + if (g->weights[i] != VECTOR(*vertex_weights)[i]) { + IGRAPH_WARNING("Only integer vertex weights are supported; weights will be truncated to their integer parts."); + } + if (g->weights[i] <= 0) { + IGRAPH_ERROR("Vertex weights must be positive.", IGRAPH_EINVAL); + } + } + + return IGRAPH_SUCCESS; +} + + +/* Find all cliques. */ + +typedef struct { + igraph_vector_int_t clique; + igraph_vector_int_list_t* result; +} igraph_i_cliquer_cliques_user_data_t; + +static igraph_error_t igraph_i_cliquer_cliques_user_data_init( + igraph_i_cliquer_cliques_user_data_t* data, + igraph_vector_int_list_t* result +) { + data->result = result; + igraph_vector_int_list_clear(result); + return igraph_vector_int_init(&data->clique, 0); +} + +static void igraph_i_cliquer_cliques_user_data_destroy( + igraph_i_cliquer_cliques_user_data_t* data +) { + igraph_vector_int_destroy(&data->clique); + data->result = 0; +} + +static igraph_error_t collect_cliques_callback(set_t s, graph_t *g, clique_options *opt) { + int i; + igraph_integer_t j; + igraph_i_cliquer_cliques_user_data_t* data = (igraph_i_cliquer_cliques_user_data_t *) opt->user_data; + + IGRAPH_UNUSED(g); + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_vector_int_resize(&data->clique, set_size(s))); + + i = -1; j = 0; + while ((i = set_return_next(s, i)) >= 0) { + VECTOR(data->clique)[j++] = i; + } + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(data->result, &data->clique)); + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_integer_t min_size, igraph_integer_t max_size) { + graph_t *g; + igraph_integer_t vcount = igraph_vcount(graph); + igraph_i_cliquer_cliques_user_data_t data; + + if (vcount == 0) { + igraph_vector_int_list_clear(res); + return IGRAPH_SUCCESS; + } + + if (min_size <= 0) { + min_size = 1; + } + if (max_size <= 0) { + max_size = 0; + } + + if (max_size > INT_MAX) { + max_size = INT_MAX; + } + + if (max_size > 0 && max_size < min_size) { + IGRAPH_ERROR("Maximum clique size must not be smaller than the minimum clique size.", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); + IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); + + IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + IGRAPH_FINALLY(graph_free, g); + + igraph_cliquer_opt.user_data = &data; + igraph_cliquer_opt.user_function = &collect_cliques_callback; + + IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); + + graph_free(g); + igraph_i_cliquer_cliques_user_data_destroy(&data); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/* Count cliques of each size. */ + +static igraph_error_t count_cliques_callback(set_t s, graph_t *g, clique_options *opt) { + igraph_vector_t *hist; + + IGRAPH_UNUSED(g); + + IGRAPH_ALLOW_INTERRUPTION(); + + hist = (igraph_vector_t *) opt->user_data; + VECTOR(*hist)[set_size(s) - 1] += 1; + + return IGRAPH_SUCCESS; +} + +igraph_error_t 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; + igraph_integer_t 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 > INT_MAX) { + max_size = INT_MAX; + } + + 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_CHECK(igraph_to_cliquer(graph, &g)); + IGRAPH_FINALLY(graph_free, g); + + IGRAPH_CHECK(igraph_vector_resize(hist, max_size)); + igraph_vector_null(hist); + igraph_cliquer_opt.user_data = hist; + igraph_cliquer_opt.user_function = &count_cliques_callback; + + IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); + + for (i = max_size; i > 0; --i) { + if (VECTOR(*hist)[i - 1] > 0) { + break; + } + } + IGRAPH_CHECK(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_vector_int_t *clique; + igraph_clique_handler_t *handler; + void *arg; +}; + +static igraph_error_t callback_callback(set_t s, graph_t *g, clique_options *opt) { + struct callback_data *cd; + int i; + igraph_integer_t j; + igraph_error_t retval; + + IGRAPH_UNUSED(g); + + IGRAPH_ALLOW_INTERRUPTION(); + + cd = (struct callback_data *) opt->user_data; + + IGRAPH_CHECK(igraph_vector_int_resize(cd->clique, set_size(s))); + + i = -1; j = 0; + while ((i = set_return_next(s, i)) >= 0) { + VECTOR(*cd->clique)[j++] = i; + } + + retval = (*(cd->handler))(cd->clique, cd->arg); + + return retval; +} + +igraph_error_t 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; + igraph_vector_int_t current_clique; + 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 > INT_MAX) { + max_size = INT_MAX; + } + + if (max_size > 0 && max_size < min_size) { + IGRAPH_ERROR("Maximum clique size must not be smaller than the minimum clique size.", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + IGRAPH_FINALLY(graph_free, g); + + IGRAPH_VECTOR_INT_INIT_FINALLY(¤t_clique, min_size); + + cd.clique = ¤t_clique; + cd.handler = cliquehandler_fn; + cd.arg = arg; + igraph_cliquer_opt.user_data = &cd; + igraph_cliquer_opt.user_function = &callback_callback; + + IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); + + igraph_vector_int_destroy(¤t_clique); + graph_free(g); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/* Find weighted cliques in given weight range. */ + +igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_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); + igraph_i_cliquer_cliques_user_data_t data; + + if (vcount == 0) { + igraph_vector_int_list_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("Maximum clique weight must not be smaller than minimum clique weight.", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); + IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); + + IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + IGRAPH_FINALLY(graph_free, g); + + IGRAPH_CHECK(set_weights(vertex_weights, g)); + + igraph_cliquer_opt.user_data = &data; + igraph_cliquer_opt.user_function = &collect_cliques_callback; + + IGRAPH_CHECK(clique_find_all(g, (int) min_weight, (int) max_weight, maximal, &igraph_cliquer_opt, NULL)); + + graph_free(g); + igraph_i_cliquer_cliques_user_data_destroy(&data); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/* Find largest weighted cliques. */ + +igraph_error_t igraph_i_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res) { + graph_t *g; + igraph_integer_t vcount = igraph_vcount(graph); + igraph_i_cliquer_cliques_user_data_t data; + + if (vcount == 0) { + igraph_vector_int_list_clear(res); + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); + IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); + + IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + IGRAPH_FINALLY(graph_free, g); + + IGRAPH_CHECK(set_weights(vertex_weights, g)); + + igraph_cliquer_opt.user_data = &data; + igraph_cliquer_opt.user_function = &collect_cliques_callback; + + IGRAPH_CHECK(clique_find_all(g, 0, 0, FALSE, &igraph_cliquer_opt, NULL)); + + graph_free(g); + igraph_i_cliquer_cliques_user_data_destroy(&data); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/* Find weight of largest weight clique. */ + +static igraph_error_t check_interruption_callback(set_t s, graph_t *g, clique_options *opt) { + IGRAPH_UNUSED(s); IGRAPH_UNUSED(g); IGRAPH_UNUSED(opt); + IGRAPH_ALLOW_INTERRUPTION(); + return IGRAPH_SUCCESS; +} + +igraph_error_t 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); + int res_int; + + if (vcount == 0) { + if (res) { + *res = 0; + } + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + IGRAPH_FINALLY(graph_free, g); + + IGRAPH_CHECK(set_weights(vertex_weights, g)); + + igraph_cliquer_opt.user_function = check_interruption_callback; + + IGRAPH_CHECK(clique_max_weight(g, &igraph_cliquer_opt, &res_int)); + + graph_free(g); + IGRAPH_FINALLY_CLEAN(1); + + if (res) { + *res = res_int; + } + + return IGRAPH_SUCCESS; +} diff --git a/src/cliques/cliques.c b/src/cliques/cliques.c new file mode 100644 index 0000000..4ce00bd --- /dev/null +++ b/src/cliques/cliques.c @@ -0,0 +1,1053 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2023 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_cliques.h" + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "igraph_adjlist.h" +#include "igraph_interface.h" + +#include "cliques/cliquer_internal.h" +#include "core/interruption.h" +#include "core/set.h" + +static igraph_error_t igraph_i_find_k_indsets( + const igraph_t *graph, + igraph_integer_t size, + const igraph_integer_t *member_storage, + igraph_integer_t **new_member_storage, + igraph_integer_t old_count, + igraph_integer_t *new_count, + igraph_vector_int_t *neis) { + + igraph_integer_t l, m, n, new_member_storage_size; + const igraph_integer_t *c1, *c2; + igraph_integer_t v1, v2; + igraph_bool_t ok; + + /* Allocate the storage */ + *new_member_storage = IGRAPH_REALLOC(*new_member_storage, + (size_t) (size * old_count), + igraph_integer_t); + IGRAPH_CHECK_OOM(*new_member_storage, "Insufficient memory for independent vertex sets."); + + new_member_storage_size = size * old_count; + IGRAPH_FINALLY(igraph_free, *new_member_storage); + + m = n = 0; + + /* Now consider all pairs of i-1-indsets and see if they can be merged */ + for (igraph_integer_t j = 0; j < old_count; j++) { + for (igraph_integer_t k = j + 1; k < old_count; k++) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Since indsets are represented by their vertex indices in increasing + * order, two indsets can be merged iff they have exactly the same + * indices excluding one AND there is no 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 */ + IGRAPH_ASSERT(l != size-1); + /* 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_integer_t); + IGRAPH_CHECK_OOM(*new_member_storage, "igraph_independent_vertex_sets failed"); + new_member_storage_size *= 2; + IGRAPH_FINALLY(igraph_free, *new_member_storage); + } + } + } + + /* Calculate how many independent vertex sets we have found */ + *new_count = n / size; + + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \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 an initialized list of integer vectors. The cliques + * will be stored here as vectors of vertex IDs. + * \param min_size Integer specifying the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer specifying 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 + */ +igraph_error_t igraph_cliques(const igraph_t *graph, igraph_vector_int_list_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 specifying the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer specifying 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 + * + */ +igraph_error_t 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_int_t. Destroying and + * freeing this vector is left up to the user. Use \ref igraph_vector_int_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 specifying the minimum size of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer specifying 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 + * + */ +igraph_error_t 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. + * + * + * Only positive integer vertex weights are supported. + * + * + * 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 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 an initialized list of integer vectors. The cliques + * will be stored here as vectors of vertex IDs. + * \param min_weight Integer specifying the minimum weight of the cliques to be + * returned. If negative or zero, no lower bound will be used. + * \param max_weight Integer specifying 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 + * + */ +igraph_error_t igraph_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_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. + * + * The weight of a clique is the sum of the weights of its vertices. + * This function finds the clique(s) having the largest weight in the graph. + * + * + * Only positive integer vertex weights are supported. + * + * + * 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 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 an initialized list of integer vectors. The cliques + * will be stored here as vectors of vertex IDs. + * \return Error code. + * + * \sa \ref igraph_weighted_cliques(), \ref igraph_weighted_clique_number(), \ref igraph_largest_cliques() + * + * Time complexity: TODO + */ +igraph_error_t igraph_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_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 weight of a clique is the sum of the weights of its vertices. + * This function finds the weight of the largest weight clique. + * + * + * Only positive integer vertex weights are supported. + * + * + * 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 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 + * + */ +igraph_error_t 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; + } +} + +static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets( + const igraph_t *graph, + igraph_vector_int_list_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 an initialized list of integer vectors. The cliques + * will be stored here as vectors of vertex IDs. + * \param min_size Integer specifying the minimum size of the sets to be + * returned. If negative or zero, no lower bound will be used. + * \param max_size Integer specifying 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 + */ +igraph_error_t igraph_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res, + igraph_integer_t min_size, + igraph_integer_t max_size) { + igraph_integer_t no_of_nodes; + igraph_vector_int_t neis, *indset; + igraph_integer_t *member_storage, *new_member_storage, *c1; + igraph_vector_int_t new_member_storage_view; + igraph_integer_t indset_count, old_indset_count; + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored during independent vertex set calculations."); + } + + 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_int_list_clear(res); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + /* Will be resized later, if needed. */ + member_storage = IGRAPH_CALLOC(1, igraph_integer_t); + IGRAPH_CHECK_OOM(member_storage, "Insufficient memory for independent vertex set calculation."); + 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_integer_t); + IGRAPH_CHECK_OOM(new_member_storage, "Insufficient memory for independent vertex set calculation."); + IGRAPH_FINALLY(igraph_free, new_member_storage); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + new_member_storage[i] = i; + } + indset_count = no_of_nodes; + old_indset_count = 0; + + /* Add size 1 indsets if requested */ + if (min_size <= 1) { + IGRAPH_CHECK(igraph_vector_int_list_resize(res, no_of_nodes)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + indset = igraph_vector_int_list_get_ptr(res, i); + IGRAPH_CHECK(igraph_vector_int_push_back(indset, i)); + } + } + + for (igraph_integer_t i = 2; i <= max_size && indset_count > 1; i++) { + + /* Here new_member_storage contains the independent vertex sets 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_indset_count = indset_count; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Calculate the independent vertex sets */ + + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_CHECK(igraph_i_find_k_indsets(graph, i, member_storage, + &new_member_storage, + old_indset_count, + &indset_count, + &neis)); + 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 (igraph_integer_t j = 0, k = 0; j < indset_count; j++, k += i) { + igraph_vector_int_view(&new_member_storage_view, new_member_storage + k, i); + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &new_member_storage_view)); + } + } + + } /* i <= max_size && clique_count != 0 */ + + IGRAPH_FREE(new_member_storage); + IGRAPH_FREE(member_storage); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \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 an initialized list of integer vectors. The cliques + * will be stored here as vectors of vertex IDs. + * \return Error code. + * + * \sa \ref igraph_independent_vertex_sets(), \ref + * igraph_maximal_independent_vertex_sets(). + * + * Time complexity: TODO + */ + +igraph_error_t igraph_largest_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res) { + return igraph_i_maximal_or_largest_cliques_or_indsets(graph, res, 0, true, false); +} + +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_int_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 igraph_error_t igraph_i_maximal_independent_vertex_sets_backtrack( + const igraph_t *graph, + igraph_vector_int_list_t *res, + igraph_i_max_ind_vsets_data_t *clqdata, + igraph_integer_t level) { + + igraph_integer_t v1, v2, v3, c, j, k; + igraph_vector_int_t *neis1, *neis2; + igraph_bool_t f; + igraph_integer_t it_state; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (level >= clqdata->matrix_size - 1) { + igraph_integer_t size = 0; + + if (res) { + igraph_vector_int_t vec, *newvec; + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + + for (v1 = 0; v1 < clqdata->matrix_size; v1++) { + if (clqdata->IS[v1] == 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(&vec, v1)); + } + } + + size = igraph_vector_int_size(&vec); + + /* Trick for efficient insertion of a new vector into a vector list: + * Instead of copying the vector contents, we add an empty vector to + * the list, then swap it with the vector to-be-added in O(1) time. */ + if (!clqdata->keep_only_largest) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(res, &newvec)); + igraph_vector_int_swap(newvec, &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 */ + igraph_vector_int_list_clear(res); + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(res, &newvec)); + igraph_vector_int_swap(newvec, &vec); + } else if (size == clqdata->largest_set_size) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(res, &newvec)); + igraph_vector_int_swap(newvec, &vec); + } + } + + igraph_vector_int_destroy(&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 = 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 = VECTOR(*neis1)[j]) <= level) { + clqdata->IS[v2]++; + j++; + } + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, v1)); + j = 0; + while (j < VECTOR(clqdata->deg)[v1] && + (v2 = 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] = c; + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, v1)); + clqdata->IS[v1] = 0; + + f = true; + j = 0; + while (j < VECTOR(clqdata->deg)[v1] && + (v2 = VECTOR(*neis1)[j]) <= level) { + if (clqdata->IS[v2] == 0) { + IGRAPH_CHECK(igraph_set_add(&clqdata->buckets[v1], j)); + neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); + k = 0; + while (k < VECTOR(clqdata->deg)[v2] && + (v3 = VECTOR(*neis2)[k]) <= level) { + clqdata->IS[v3]--; + if (clqdata->IS[v3] == 0) { + f = false; + } + k++; + } + } + clqdata->IS[v2]++; + j++; + } + + if (f) { + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, v1)); + } + + j = 0; + while (j < VECTOR(clqdata->deg)[v1] && + (v2 = VECTOR(*neis1)[j]) <= level) { + clqdata->IS[v2]--; + j++; + } + + it_state = 0; + while (igraph_set_iterate(&clqdata->buckets[v1], &it_state, &j)) { + v2 = VECTOR(*neis1)[j]; + neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); + k = 0; + while (k < VECTOR(clqdata->deg)[v2] && + (v3 = VECTOR(*neis2)[k]) <= level) { + clqdata->IS[v3]++; + k++; + } + } + igraph_set_clear(&clqdata->buckets[v1]); + } + } + + return IGRAPH_SUCCESS; +} + +/* TODO (ugly hack): + * + * This version does not know the length of the array, and is safe to use + * ONLY on arrays which have not been completely filled out and were + * originally initialized to zero. It relies on igraph_set_inited() + * returning false when igraph_set_t is all-zero-bytes. + * This function is meant for use with IGRAPH_FINALLY. + * + * Should probably be replaced with a proper igraph_vector_ptr_t. + */ +static void free_set_array_incomplete(igraph_set_t *array) { + igraph_integer_t i = 0; + while (igraph_set_inited(array + i)) { + igraph_set_destroy(array + i); + i++; + } + IGRAPH_FREE(array); +} + +static void free_set_array(igraph_set_t *array, igraph_integer_t n) { + for (igraph_integer_t i=0; i < n; i++) { + igraph_set_destroy(&array[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 an initialized list of integer vectors. The cliques + * will be stored here as vectors of vertex IDs. + * \return Error code. + * + * \sa \ref igraph_maximal_cliques(), \ref + * igraph_independence_number() + * + * Time complexity: TODO. + */ +igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res) { + igraph_i_max_ind_vsets_data_t clqdata; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored during independent vertex set calculations."); + } + + clqdata.matrix_size = no_of_nodes; + clqdata.keep_only_largest = false; + + 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); + IGRAPH_CHECK_OOM(clqdata.IS, "Insufficient memory for maximal independent vertex sets."); + IGRAPH_FINALLY(igraph_free, clqdata.IS); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&clqdata.deg, no_of_nodes); + for (igraph_integer_t 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); + IGRAPH_CHECK_OOM(clqdata.buckets, "Insufficient memory for maximal independent vertex sets."); + IGRAPH_FINALLY(free_set_array_incomplete, clqdata.buckets); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0)); + } + + igraph_vector_int_list_clear(res); + + /* Do the show */ + clqdata.largest_set_size = 0; + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, &clqdata, 0)); + + /* Cleanup */ + free_set_array(clqdata.buckets, no_of_nodes); + igraph_vector_int_destroy(&clqdata.deg); + IGRAPH_FREE(clqdata.IS); + igraph_adjlist_destroy(&clqdata.adj_list); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t 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_vcount(graph); + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored during independence number calculations."); + } + + clqdata.matrix_size = no_of_nodes; + clqdata.keep_only_largest = false; + + 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); + IGRAPH_CHECK_OOM(clqdata.IS, "Insufficient memory for independence number calculation."); + IGRAPH_FINALLY(igraph_free, clqdata.IS); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&clqdata.deg, no_of_nodes); + for (igraph_integer_t 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); + IGRAPH_CHECK_OOM(clqdata.buckets, "Insufficient memory for independence number calculation."); + IGRAPH_FINALLY(free_set_array_incomplete, clqdata.buckets); + + for (igraph_integer_t 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 */ + free_set_array(clqdata.buckets, no_of_nodes); + igraph_vector_int_destroy(&clqdata.deg); + IGRAPH_FREE(clqdata.IS); + igraph_adjlist_destroy(&clqdata.adj_list); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +/*************************************************************************/ +/* MAXIMAL CLIQUES, LARGEST CLIQUES */ +/*************************************************************************/ + +static igraph_error_t igraph_i_maximal_cliques_store_max_size(const igraph_vector_int_t* clique, void* data) { + igraph_integer_t* result = (igraph_integer_t*)data; + if (*result < igraph_vector_int_size(clique)) { + *result = igraph_vector_int_size(clique); + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_largest_cliques_store(const igraph_vector_int_t* clique, void* data) { + igraph_vector_int_list_t* result = (igraph_vector_int_list_t*)data; + igraph_integer_t n; + + /* Is the current clique at least as large as the others that we have found? */ + if (!igraph_vector_int_list_empty(result)) { + igraph_vector_int_t* first; + + n = igraph_vector_int_size(clique); + first = igraph_vector_int_list_get_ptr(result, 0); + if (n < igraph_vector_int_size(first)) { + return IGRAPH_SUCCESS; + } + + if (n > igraph_vector_int_size(first)) { + igraph_vector_int_list_clear(result); + } + } + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(result, clique)); + + return IGRAPH_SUCCESS; +} + +/** + * \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 list of integer vectors. The cliques + * will be stored here as vectors of vertex IDs. + * \return Error code. + * + * \sa \ref igraph_cliques(), \ref igraph_maximal_cliques() + * + * Time complexity: O(3^(|V|/3)) worst case. + */ + +igraph_error_t igraph_largest_cliques(const igraph_t *graph, igraph_vector_int_list_t *res) { + igraph_vector_int_list_clear(res); + IGRAPH_CHECK(igraph_maximal_cliques_callback(graph, &igraph_i_largest_cliques_store, (void*)res, 0, 0)); + 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. + */ +igraph_error_t 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 igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, + igraph_vector_int_list_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_vcount(graph); + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored for largest independent vertex set or clique calculations."); + } + + 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); + IGRAPH_CHECK_OOM(clqdata.IS, "Insufficient memory for largest independent sets or cliques."); + IGRAPH_FINALLY(igraph_free, clqdata.IS); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&clqdata.deg, no_of_nodes); + for (igraph_integer_t 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); + IGRAPH_CHECK_OOM(clqdata.buckets, "Insufficient memory for largest independent sets or cliques."); + IGRAPH_FINALLY(free_set_array_incomplete, clqdata.buckets); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0)); + } + + if (res) { + igraph_vector_int_list_clear(res); + } + + /* Do the show */ + clqdata.largest_set_size = 0; + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, &clqdata, 0)); + + /* Cleanup */ + free_set_array(clqdata.buckets, no_of_nodes); + igraph_vector_int_destroy(&clqdata.deg); + igraph_free(clqdata.IS); + igraph_adjlist_destroy(&clqdata.adj_list); + IGRAPH_FINALLY_CLEAN(4); + + if (clique_number) { + *clique_number = clqdata.largest_set_size; + } + + return IGRAPH_SUCCESS; +} diff --git a/src/cliques/glet.c b/src/cliques/glet.c new file mode 100644 index 0000000..f70bc74 --- /dev/null +++ b/src/cliques/glet.c @@ -0,0 +1,899 @@ +/* -*- 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_interface.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; + igraph_integer_t nc; +} igraph_i_subclique_next_free_t; + +static void igraph_i_subclique_next_free(void *ptr) { + igraph_i_subclique_next_free_t *data = ptr; + igraph_integer_t 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 \ref igraph_vector_int_t, 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 igraph_error_t igraph_i_subclique_next(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_vector_int_t *ids, + const igraph_vector_int_list_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_int_t neis, newedges; + igraph_integer_t c, nc = igraph_vector_int_list_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 = { NULL, NULL, NULL, 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); + IGRAPH_CHECK_OOM(*resultids, "Cannot calculate next cliques."); + freedata.resultids = *resultids; + + *resultweights = IGRAPH_CALLOC(nc, igraph_vector_t); + IGRAPH_CHECK_OOM(*resultweights, "Cannot calculate next cliques."); + freedata.resultweights = *resultweights; + + *result = IGRAPH_CALLOC(nc, igraph_t); + IGRAPH_CHECK_OOM(*result, "Cannot calculate next cliques."); + freedata.result = *result; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&newedges, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&map, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 10); + + if (clique_thr) { + IGRAPH_CHECK(igraph_vector_resize(clique_thr, nc)); + } + if (next_thr) { + IGRAPH_CHECK(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_int_t *clique = igraph_vector_int_list_get_ptr(cliques, c); + igraph_real_t minweight = IGRAPH_INFINITY, nextweight = IGRAPH_INFINITY; + igraph_integer_t e, v, clsize = igraph_vector_int_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_int_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_CHECK(igraph_incident(graph, &neis, node, IGRAPH_ALL)); + neilen = igraph_vector_int_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_CHECK(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_CHECK(igraph_vector_int_init(newids, 0)); + IGRAPH_CHECK(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_CHECK(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_CHECK(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_CHECK(igraph_vector_int_push_back(newids, VECTOR(*ids)[to])); + } + IGRAPH_CHECK(igraph_vector_push_back(neww, w)); + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(map)[from])); + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(map)[to])); + } + } + + IGRAPH_CHECK(igraph_create(newgraph, &newedges, nov, IGRAPH_UNDIRECTED)); + + /* --------------------------------------------------- */ + + } /* c < nc */ + + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&mark); + igraph_vector_int_destroy(&map); + igraph_vector_int_destroy(&newedges); + IGRAPH_FINALLY_CLEAN(6); /* + freedata */ + + return IGRAPH_SUCCESS; +} + +static void igraph_i_graphlets_destroy_clique_list(igraph_vector_ptr_t *vl) { + igraph_integer_t i, n = igraph_vector_ptr_size(vl); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = (igraph_vector_int_t*) VECTOR(*vl)[i]; + if (v) { + igraph_vector_int_destroy(v); + IGRAPH_FREE(v); + } + } + igraph_vector_ptr_destroy(vl); +} + +static igraph_error_t 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_int_list_t mycliques; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t subv; + igraph_t subg; + igraph_t *newgraphs = NULL; + igraph_vector_t *newweights = NULL; + igraph_vector_int_t *newids = NULL; + igraph_vector_t clique_thr, next_thr; + igraph_i_subclique_next_free_t freedata = { NULL, NULL, NULL, 0 }; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&mycliques, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&subv, 0); + + /* We start by finding cliques at the lowest threshold */ + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + if (VECTOR(*weights)[i] >= startthr) { + IGRAPH_CHECK(igraph_vector_int_push_back(&subv, i)); + } + } + IGRAPH_CHECK(igraph_subgraph_from_edges(graph, &subg, igraph_ess_vector(&subv), /*delete_vertices=*/ 0)); + IGRAPH_FINALLY(igraph_destroy, &subg); + IGRAPH_CHECK(igraph_maximal_cliques(&subg, &mycliques, /*min_size=*/ 0, /*max_size=*/ 0)); + igraph_destroy(&subg); + igraph_vector_int_destroy(&subv); + IGRAPH_FINALLY_CLEAN(2); + + const igraph_integer_t nocliques = igraph_vector_int_list_size(&mycliques); + + /* Get the next cliques and thresholds */ + IGRAPH_VECTOR_INIT_FINALLY(&next_thr, 0); + IGRAPH_VECTOR_INIT_FINALLY(&clique_thr, 0); + + IGRAPH_CHECK(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_CHECK(igraph_vector_append(thresholds, &clique_thr)); + + igraph_vector_destroy(&clique_thr); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_vector_ptr_resize(cliques, igraph_vector_ptr_size(cliques) + nocliques)); + for (igraph_integer_t i = 0, j = igraph_vector_ptr_size(cliques) - 1; i < nocliques; i++, j--) { + igraph_vector_int_t *cl = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(cl, "Cannot find graphlets."); + IGRAPH_FINALLY(igraph_free, cl); + + *cl = igraph_vector_int_list_pop_back(&mycliques); + + /* From this point onwards, _we_ own the clique and not `mycliques'. + * We pass on the ownership to `cliques' */ + VECTOR(*cliques)[j] = cl; + IGRAPH_FINALLY_CLEAN(1); + + const igraph_integer_t n = igraph_vector_int_size(cl); + for (igraph_integer_t k = 0; k < n; k++) { + igraph_integer_t node = VECTOR(*cl)[k]; + VECTOR(*cl)[k] = VECTOR(*ids)[node]; + } + igraph_vector_int_sort(cl); + } + + igraph_vector_int_list_destroy(&mycliques); /* contents was copied over to `cliques' */ + IGRAPH_FINALLY_CLEAN(1); + + /* Recursive calls for cliques found */ + for (igraph_integer_t i = 0; i < nocliques; 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_CHECK(igraph_i_graphlets(g, w_sub, cliques, thresholds, ids_sub, VECTOR(next_thr)[i])); + } + } + + igraph_vector_destroy(&next_thr); + igraph_i_subclique_next_free(&freedata); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +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; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; + igraph_real_t t_a = VECTOR(*ddata->thresholds)[*aa]; + igraph_real_t t_b = VECTOR(*ddata->thresholds)[*bb]; + igraph_vector_int_t *v_a, *v_b; + igraph_integer_t s_a, s_b; + + if (t_a < t_b) { + return -1; + } else if (t_a > t_b) { + return 1; + } + + v_a = (igraph_vector_int_t*) VECTOR(*ddata->cliques)[*aa]; + v_b = (igraph_vector_int_t*) VECTOR(*ddata->cliques)[*bb]; + s_a = igraph_vector_int_size(v_a); + s_b = igraph_vector_int_size(v_b); + + if (s_a < s_b) { + return -1; + } else if (s_a > s_b) { + return 1; + } else { + return 0; + } +} + +static igraph_error_t 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. */ + + igraph_integer_t i, iptr, nocliques = igraph_vector_ptr_size(cliques); + igraph_vector_int_t order; + igraph_i_graphlets_filter_t sortdata = { cliques, thresholds }; + + IGRAPH_CHECK(igraph_vector_int_init_range(&order, 0, nocliques)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order); + + igraph_qsort_r(VECTOR(order), nocliques, sizeof(VECTOR(order)[0]), &sortdata, + igraph_i_graphlets_filter_cmp); + + for (i = 0; i < nocliques - 1; i++) { + igraph_integer_t ri = VECTOR(order)[i]; + igraph_vector_int_t *needle = VECTOR(*cliques)[ri]; + igraph_real_t thr_i = VECTOR(*thresholds)[ri]; + igraph_integer_t n_i = igraph_vector_int_size(needle); + + for (igraph_integer_t j = i + 1; j < nocliques; j++) { + igraph_integer_t rj = VECTOR(order)[j]; + igraph_real_t thr_j = VECTOR(*thresholds)[rj]; + igraph_vector_int_t *hay; + igraph_integer_t 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_int_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) { + igraph_integer_t ei = VECTOR(*needle)[pi]; + igraph_integer_t 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_int_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_int_t *v = VECTOR(*cliques)[i]; + if (v) { + VECTOR(*cliques)[iptr] = v; + VECTOR(*thresholds)[iptr] = VECTOR(*thresholds)[i]; + iptr++; + } + } + IGRAPH_CHECK(igraph_vector_ptr_resize(cliques, iptr)); + IGRAPH_CHECK(igraph_vector_resize(thresholds, iptr)); + + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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 list of integer vectors. The graphlet basis is + * stored here. Each element of the list is an integer vector of + * vertex IDs, encoding a single basis subgraph. + * \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(). + */ + +igraph_error_t igraph_graphlets_candidate_basis(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_list_t *cliques, + igraph_vector_t *thresholds) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_real_t minthr; + igraph_vector_int_t ids; + igraph_bool_t simple; + igraph_integer_t i, no_of_cliques; + igraph_vector_ptr_t mycliques; + + /* 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); + } + if (igraph_is_directed(graph)) { + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + igraph_bool_t has_mutual; + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); + } + } + + /* Internally, we will still use igraph_vector_ptr_t instead of + * igraph_vector_int_list_t to manage the list of cliques; this is because + * we are going to append & filter the list and it's more complicated to + * do with an igraph_vector_int_list_t */ + IGRAPH_CHECK(igraph_vector_ptr_init(&mycliques, 0)); + IGRAPH_FINALLY(igraph_i_graphlets_destroy_clique_list, &mycliques); + + igraph_vector_int_list_clear(cliques); + igraph_vector_clear(thresholds); + + minthr = igraph_vector_min(weights); + + IGRAPH_CHECK(igraph_vector_int_init_range(&ids, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &ids); + + IGRAPH_CHECK(igraph_i_graphlets(graph, weights, &mycliques, thresholds, &ids, minthr)); + + igraph_vector_int_destroy(&ids); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_i_graphlets_filter(&mycliques, thresholds)); + + /* Pass ownership of cliques in `mycliques' to `cliques' so the user does + * not have to work with igraph_vector_ptr_t */ + no_of_cliques = igraph_vector_ptr_size(&mycliques); + for (i = 0; i < no_of_cliques; i++) { + IGRAPH_CHECK(igraph_vector_int_list_push_back( + cliques, VECTOR(mycliques)[i] + )); + IGRAPH_FREE(VECTOR(mycliques)[i]); + } + + /* `mycliques' is now empty so we can clear and destroy */ + igraph_vector_ptr_clear(&mycliques); + igraph_vector_ptr_destroy(&mycliques); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* TODO: not made static because it is used by the R interface */ +igraph_error_t igraph_i_graphlets_project( + const igraph_t *graph, const igraph_vector_t *weights, + const igraph_vector_int_list_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, + igraph_integer_t niter, igraph_integer_t vid1 +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_cliques = igraph_vector_int_list_size(cliques); + igraph_vector_int_t vcl, vclidx, ecl, eclidx, cel, celidx; + igraph_vector_int_t edgelist; + igraph_vector_t newweights, normfact; + igraph_integer_t 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 (igraph_is_directed(graph)) { + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + igraph_bool_t has_mutual; + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); + } + } + + if (!startMu) { + IGRAPH_CHECK(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_VECTOR_INT_INIT_FINALLY(&vclidx, no_of_nodes + 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&celidx, no_cliques + 3); + for (i = 0, total_vertices = 0, total_edges = 0; i < no_cliques; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); + igraph_integer_t j, n = igraph_vector_int_size(v); + total_vertices += n; + total_edges += n * (n - 1) / 2; + VECTOR(celidx)[i + 2] = total_edges; + for (j = 0; j < n; j++) { + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&vcl, total_vertices); + for (i = 0; i < no_cliques; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); + igraph_integer_t j, n = igraph_vector_int_size(v); + for (j = 0; j < n; j++) { + igraph_integer_t vv = VECTOR(*v)[j] - vid1; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&ecl, total_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eclidx, no_of_edges + 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, no_of_edges * 2); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, /*by_col=*/ 0)); + for (i = 0, e = 0, ptr = 0; e < no_of_edges; e++) { + igraph_integer_t from = VECTOR(edgelist)[i++]; + igraph_integer_t to = VECTOR(edgelist)[i++]; + igraph_integer_t from_s = VECTOR(vclidx)[from]; + igraph_integer_t from_e = VECTOR(vclidx)[from + 1]; + igraph_integer_t to_s = VECTOR(vclidx)[to]; + igraph_integer_t to_e = VECTOR(vclidx)[to + 1]; + VECTOR(eclidx)[e] = ptr; + while (from_s < from_e && to_s < to_e) { + igraph_integer_t from_v = VECTOR(vcl)[from_s]; + igraph_integer_t 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_int_destroy(&edgelist); + IGRAPH_FINALLY_CLEAN(1); + + /* Convert the edge-clique list to a clique-edge list */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&cel, total_edges); + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t ecl_s = VECTOR(eclidx)[i], ecl_e = VECTOR(eclidx)[i + 1], j; + for (j = ecl_s; j < ecl_e; j++) { + igraph_integer_t cl = VECTOR(ecl)[j]; + igraph_integer_t epos = VECTOR(celidx)[cl + 1]; + VECTOR(cel)[epos] = i; + VECTOR(celidx)[cl + 1] += 1; + } + } + + /* Normalizing factors for the iteration */ + IGRAPH_VECTOR_INIT_FINALLY(&normfact, no_cliques); + for (i = 0; i < no_cliques; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); + igraph_integer_t n = igraph_vector_int_size(v); + VECTOR(normfact)[i] = n * (n + 1) / 2; + } + + /* We have the clique-edge list, so do the projection now */ + IGRAPH_VECTOR_INIT_FINALLY(&newweights, no_of_edges); + for (i = 0; i < niter; i++) { + for (e = 0; e < no_of_edges; e++) { + igraph_integer_t start = VECTOR(eclidx)[e]; + igraph_integer_t end = VECTOR(eclidx)[e + 1]; + VECTOR(newweights)[e] = 0.0001; + while (start < end) { + igraph_integer_t clique = VECTOR(ecl)[start++]; + VECTOR(newweights)[e] += VECTOR(*Mu)[clique]; + } + } + for (e = 0; e < no_cliques; e++) { + igraph_real_t sumratio = 0; + igraph_integer_t start = VECTOR(celidx)[e]; + igraph_integer_t end = VECTOR(celidx)[e + 1]; + while (start < end) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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 An initialized list of integer vectors. The graphlet basis is + * stored here. Each element of the list is an integer vector of + * vertex IDs, encoding a single basis subgraph. + * \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. + * \param startMu If true, 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(). + */ + +igraph_error_t igraph_graphlets_project(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_vector_int_list_t *cliques, + igraph_vector_t *Mu, igraph_bool_t startMu, + igraph_integer_t niter) { + + return igraph_i_graphlets_project(graph, weights, cliques, Mu, startMu, + niter, /*vid1=*/ 0); +} + +typedef struct igraph_i_graphlets_order_t { + const igraph_vector_int_list_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; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) 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 list of integer vectors. The graphlet basis is + * stored here. Each element of the list is an integer vector of + * vertex IDs, encoding a single basis subgraph. + * \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(). + */ + +igraph_error_t igraph_graphlets(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_list_t *cliques, + igraph_vector_t *Mu, igraph_integer_t niter) { + + igraph_integer_t nocliques; + igraph_vector_t thresholds; + igraph_vector_int_t order; + igraph_i_graphlets_order_t sortdata = { cliques, Mu }; + + IGRAPH_VECTOR_INIT_FINALLY(&thresholds, 0); + IGRAPH_CHECK(igraph_graphlets_candidate_basis(graph, weights, cliques, &thresholds)); + igraph_vector_destroy(&thresholds); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_graphlets_project(graph, weights, cliques, Mu, /*startMu=*/ false, niter)); + + nocliques = igraph_vector_int_list_size(cliques); + IGRAPH_CHECK(igraph_vector_int_init_range(&order, 0, nocliques)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order); + + igraph_qsort_r(VECTOR(order), nocliques, sizeof(VECTOR(order)[0]), &sortdata, + igraph_i_graphlets_order_cmp); + + IGRAPH_CHECK(igraph_vector_int_list_permute(cliques, &order)); + IGRAPH_CHECK(igraph_vector_index_int(Mu, &order)); + + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/cliques/maximal_cliques.c b/src/cliques/maximal_cliques.c new file mode 100644 index 0000000..66d0de7 --- /dev/null +++ b/src/cliques/maximal_cliques.c @@ -0,0 +1,562 @@ +/* -*- 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 igraph_error_t igraph_i_maximal_cliques_reorder_adjlists( + const igraph_vector_int_t *PX, + igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, + const igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist); + +static igraph_error_t igraph_i_maximal_cliques_select_pivot( + const igraph_vector_int_t *PX, + igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, + const igraph_vector_int_t *pos, + const igraph_adjlist_t *adjlist, + igraph_integer_t *pivot, + igraph_vector_int_t *nextv, + igraph_integer_t oldPS, igraph_integer_t oldXE); + +static igraph_error_t igraph_i_maximal_cliques_down( + igraph_vector_int_t *PX, + igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, + igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, igraph_integer_t mynextv, + igraph_vector_int_t *R, + igraph_integer_t *newPS, igraph_integer_t *newXE); + +static igraph_error_t igraph_i_maximal_cliques_PX( + igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t *PE, + igraph_integer_t *XS, igraph_integer_t XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, igraph_integer_t v, + igraph_vector_int_t *H); + +static igraph_error_t igraph_i_maximal_cliques_up( + igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t PE, + igraph_integer_t XS, igraph_integer_t XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, + igraph_vector_int_t *R, + igraph_vector_int_t *H); + +#define PRINT_PX do { \ + igraph_integer_t j; \ + printf("PX="); \ + for (j=0; j= sPS && avneipos <= sPE) { + if (pp != avnei) { + igraph_integer_t tmp = *avnei; + *avnei = *pp; + *pp = tmp; + } + pp++; + } + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_maximal_cliques_select_pivot( + const igraph_vector_int_t *PX, + igraph_integer_t PS, igraph_integer_t PE, + igraph_integer_t XS, igraph_integer_t XE, + const igraph_vector_int_t *pos, + const igraph_adjlist_t *adjlist, + igraph_integer_t *pivot, + igraph_vector_int_t *nextv, + igraph_integer_t oldPS, igraph_integer_t oldXE) { + igraph_vector_int_t *pivotvectneis; + igraph_integer_t j, pivotvectlen; + igraph_integer_t i, usize = -1; + igraph_integer_t 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++) { + igraph_integer_t av = VECTOR(*PX)[i]; + igraph_vector_int_t *avneis = igraph_adjlist_get(adjlist, av); + igraph_integer_t *avp = VECTOR(*avneis); + igraph_integer_t avlen = igraph_vector_int_size(avneis); + igraph_integer_t *ave = avp + avlen; + igraph_integer_t *avnei = avp, *pp = avp; + + for (; avnei < ave; avnei++) { + igraph_integer_t avneipos = VECTOR(*pos)[(*avnei)]; + if (avneipos < soldPS || avneipos > soldXE) { + break; + } + if (avneipos >= sPS && avneipos <= sPE) { + if (pp != avnei) { + igraph_integer_t 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++) { + igraph_integer_t vcand = VECTOR(*PX)[j]; + igraph_bool_t nei = false; + igraph_integer_t k = 0; + for (k = 0; k < pivotvectlen; k++) { + igraph_integer_t unv = VECTOR(*pivotvectneis)[k]; + igraph_integer_t unvpos = VECTOR(*pos)[unv]; + if (unvpos < sPS || unvpos > sPE) { + break; + } + if (unv == vcand) { + nei = true; + break; + } + } + if (!nei) { + IGRAPH_CHECK(igraph_vector_int_push_back(nextv, vcand)); + } + } + + return IGRAPH_SUCCESS; +} + +#define SWAP(p1,p2) do { \ + igraph_integer_t v1=VECTOR(*PX)[p1]; \ + igraph_integer_t 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 igraph_error_t igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, + igraph_integer_t PS, igraph_integer_t PE, + igraph_integer_t XS, igraph_integer_t XE, + igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, igraph_integer_t mynextv, + igraph_vector_int_t *R, + igraph_integer_t *newPS, igraph_integer_t *newXE) { + + igraph_vector_int_t *vneis = igraph_adjlist_get(adjlist, mynextv); + igraph_integer_t j, vneislen = igraph_vector_int_size(vneis); + igraph_integer_t sPS = PS + 1, sPE = PE + 1, sXS = XS + 1, sXE = XE + 1; + + *newPS = PE + 1; *newXE = XS - 1; + for (j = 0; j < vneislen; j++) { + igraph_integer_t vnei = VECTOR(*vneis)[j]; + igraph_integer_t 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 igraph_error_t igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, + igraph_integer_t PS, igraph_integer_t *PE, igraph_integer_t *XS, igraph_integer_t XE, + igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, igraph_integer_t v, + igraph_vector_int_t *H +) { + + igraph_integer_t vpos = VECTOR(*pos)[v] - 1; + igraph_integer_t 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 igraph_error_t igraph_i_maximal_cliques_up( + igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t PE, + igraph_integer_t XS, igraph_integer_t XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, + igraph_vector_int_t *R, + igraph_vector_int_t *H +) { + igraph_integer_t vv; + + IGRAPH_UNUSED(PS); + IGRAPH_UNUSED(PE); + IGRAPH_UNUSED(XE); + IGRAPH_UNUSED(adjlist); + + igraph_vector_int_pop_back(R); + + while ((vv = igraph_vector_int_pop_back(H)) != -1) { + igraph_integer_t vvpos = VECTOR(*pos)[vv]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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 list of integer vectors. The maximal cliques + * will be returned here as vectors of vertex IDs. 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 + */ + +igraph_error_t igraph_maximal_cliques( + const igraph_t *graph, igraph_vector_int_list_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 + * \brief 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 + */ + +igraph_error_t 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.* + * + */ + +igraph_error_t 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 + * \brief 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 a list of integer vectors; 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. + * + */ + +igraph_error_t igraph_maximal_cliques_subset( + const igraph_t *graph, const igraph_vector_int_t *subset, + igraph_vector_int_list_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_int_t. The vector is + * owned by the maximal clique search routine so users are expected to make a + * copy of the vector using \ref igraph_vector_int_init_copy() if they want to + * hold on to it. + * + * + * 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. + * + */ + +igraph_error_t 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. + * + */ + +igraph_error_t 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/cliques/maximal_cliques_template.h b/src/cliques/maximal_cliques_template.h new file mode 100644 index 0000000..be0824b --- /dev/null +++ b/src/cliques/maximal_cliques_template.h @@ -0,0 +1,366 @@ +/* -*- 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_int_list_t *res +#define RESNAME res +#define SUFFIX +#define RECORD do { \ + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, R)); \ + } while (0) +#define PREPARE do { \ + igraph_vector_int_list_clear(res); \ + } while (0) +#define CLEANUP +#define FOR_LOOP_OVER_VERTICES for (i=0; i hsize) { \ + igraph_integer_t hcapacity = igraph_vector_capacity(hist); \ + igraph_integer_t j; \ + igraph_error_t 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); /* LCOV_EXCL_LINE */ \ + for (j=hsize; j < clsize; j++) \ + VECTOR(*hist)[j] = 0; \ + } \ + VECTOR(*hist)[clsize-1] += 1; \ + } while (0) +#define PREPARE \ + igraph_vector_clear(hist); \ + IGRAPH_CHECK(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 */ + igraph_integer_t 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 */ + igraph_integer_t 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) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +igraph_error_t 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_int_t coreness; + igraph_vector_int_t order; + igraph_vector_int_t rank; /* TODO: this is not needed */ + igraph_integer_t 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; + igraph_error_t err; + IGRAPH_UNUSED(nn); + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Edge directions are ignored for maximal clique " + "calculation"); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&coreness, no_of_nodes); + IGRAPH_CHECK(igraph_coreness(graph, &coreness, /*mode=*/ IGRAPH_ALL)); + IGRAPH_CHECK(igraph_vector_int_qsort_ind(&coreness, &order, IGRAPH_ASCENDING)); + for (ii = 0; ii < no_of_nodes; ii++) { + igraph_integer_t v = VECTOR(order)[ii]; + VECTOR(rank)[v] = ii; + } + + igraph_vector_int_destroy(&coreness); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(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); + + PREPARE; + + FOR_LOOP_OVER_VERTICES { + igraph_integer_t v; + igraph_integer_t vrank; + igraph_vector_int_t *vneis; + igraph_integer_t vdeg; + igraph_integer_t Pptr, Xptr, PS, PE, XS, XE; + igraph_integer_t 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++) { + igraph_integer_t 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++) { + igraph_integer_t vv = VECTOR(PX)[j]; + igraph_vector_int_t *fadj = igraph_adjlist_get(&fulladjlist, vv); + igraph_vector_int_t *radj = igraph_adjlist_get(&adjlist, vv); + igraph_integer_t k, fn = igraph_vector_int_size(fadj); + igraph_vector_int_clear(radj); + for (k = 0; k < fn; k++) { + igraph_integer_t nei = VECTOR(*fadj)[k]; + igraph_integer_t 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_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +#undef RESTYPE +#undef RESNAME +#undef SUFFIX +#undef RECORD +#undef PREPARE +#undef CLEANUP +#undef FOR_LOOP_OVER_VERTICES +#undef FOR_LOOP_OVER_VERTICES_PREPARE diff --git a/src/community/community_misc.c b/src/community/community_misc.c new file mode 100644 index 0000000..5666944 --- /dev/null +++ b/src/community/community_misc.c @@ -0,0 +1,922 @@ +/* -*- 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_memory.h" +#include "igraph_sparsemat.h" + +#include +#include + +/** + * \function igraph_community_to_membership + * \brief Creates a membership vector from a 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_connected_components() + * 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. + */ +igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, + igraph_integer_t nodes, + igraph_integer_t steps, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize) { + + igraph_integer_t no_of_nodes = nodes; + igraph_integer_t components = no_of_nodes - steps; + igraph_integer_t i, found = 0; + igraph_vector_int_t tmp; + igraph_vector_bool_t already_merged; + igraph_vector_int_t own_membership; + igraph_bool_t using_own_membership = false; + + if (steps > igraph_matrix_int_nrow(merges)) { + IGRAPH_ERRORF("Number of steps is greater than number of rows in merges matrix: found %" + IGRAPH_PRId " steps, %" IGRAPH_PRId " rows.", IGRAPH_EINVAL, steps, igraph_matrix_int_nrow(merges)); + } + + if (igraph_matrix_int_ncol(merges) != 2) { + IGRAPH_ERRORF("The merges matrix should have two columns, but has %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_matrix_int_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_INT_INIT_FINALLY(&own_membership, no_of_nodes); + using_own_membership = true; + membership = &own_membership; + } + + if (membership) { + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_null(membership); + } + if (csize) { + IGRAPH_CHECK(igraph_vector_int_resize(csize, components)); + igraph_vector_int_null(csize); + } + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&already_merged, steps + no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, steps); + + for (i = steps - 1; i >= 0; i--) { + igraph_integer_t c1 = MATRIX(*merges, i, 0); + igraph_integer_t c2 = MATRIX(*merges, i, 1); + + if (VECTOR(already_merged)[c1] == 0) { + VECTOR(already_merged)[c1] = true; + } else { + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %" IGRAPH_PRId ".", IGRAPH_EINVAL, c1); + } + if (VECTOR(already_merged)[c2] == 0) { + VECTOR(already_merged)[c2] = true; + } else { + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %" IGRAPH_PRId ".", IGRAPH_EINVAL, c2); + } + + /* new component? */ + if (VECTOR(tmp)[i] == 0) { + found++; + VECTOR(tmp)[i] = found; + } + + if (c1 < no_of_nodes) { + igraph_integer_t cid = 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) { + igraph_integer_t cid = 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++) { + igraph_integer_t tmp = 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_int_destroy(&tmp); + igraph_vector_bool_destroy(&already_merged); + IGRAPH_FINALLY_CLEAN(2); + + if (using_own_membership) { + igraph_vector_int_destroy(&own_membership); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_reindex_membership + * \brief Makes the IDs in a membership vector contiguous. + * + * 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 \c 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 \c NULL, this will be + * updated to reflect the number of distinct + * clusters found in membership. + * + * Time complexity: should be O(n) for n elements. + */ +igraph_error_t igraph_reindex_membership(igraph_vector_int_t *membership, + igraph_vector_int_t *new_to_old, + igraph_integer_t *nb_clusters) { + + igraph_integer_t i, n = igraph_vector_int_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_int_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++) { + igraph_integer_t c = VECTOR(*membership)[i]; + + if (c < 0) { + IGRAPH_ERRORF("Membership indices should be non-negative. " + "Found member of cluster %" IGRAPH_PRId ".", IGRAPH_EINVAL, c); + } + + if (c >= n) { + IGRAPH_ERRORF("Membership indices should be less than total number of vertices. " + "Found member of cluster %" IGRAPH_PRId ", but only %" IGRAPH_PRId " 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_int_push_back(new_to_old, c)); + } + } + } + + /* Assign new membership */ + for (i = 0; i < n; i++) { + igraph_integer_t c = 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 igraph_error_t igraph_i_compare_communities_vi(const igraph_vector_int_t *v1, + const igraph_vector_int_t *v2, igraph_real_t* result); +static igraph_error_t igraph_i_compare_communities_nmi(const igraph_vector_int_t *v1, + const igraph_vector_int_t *v2, igraph_real_t* result); +static igraph_error_t igraph_i_compare_communities_rand(const igraph_vector_int_t *v1, + const igraph_vector_int_t *v2, igraph_real_t* result, igraph_bool_t adjust); +static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1, + const igraph_vector_int_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). + * + * + * Some of these measures are defined based on the entropy of a discrete + * random variable associated with a given clustering \c C of vertices. + * Let \c p_i be the probability that a randomly picked vertex would be part + * of cluster \c i. Then the entropy of the clustering is + * + * + * H(C) = - \sum_i p_i log p_i + * + * + * Similarly, we can define the joint entropy of two clusterings \c C_1 and \c C_2 + * based on the probability \c p_ij that a random vertex is part of cluster \c i + * in the first clustering and cluster \c j in the second one: + * + * + * H(C_1, C_2) = - \sum_ii p_ij log p_ij + * + * + * The mutual information of \c C_1 and \c C_2 is then + * MI(C_1, C_2) = H(C_1) + H(C_2) - H(C_1, C_2) >= 0 . + * A large mutual information indicates a high overlap between the two clusterings. + * The normalized mutual information, as computed by igraph, is + * + * + * NMI(C_1, C_2) = 2 MI(C_1, C_2) / (H(C_1) + H(C_2)). + * + * + * It takes its value from the interval (0, 1], with 1 achieved when the two clusterings + * coincide. + * + * + * The variation of information is defined as + * VI(C_1, C_2) = [H(C_1) - MI(C_1, C_2)] + [H(C_2) - MI(C_1, C_2)]. + * Lower values of the variation of information indicate a smaller difference between + * the two clusterings, with VI = 0 achieved precisely when they coincide. + * igraph uses natural units for the variation of information, i.e. it uses the + * natural logarithm when computing entropies. + * + * + * The Rand index is defined as the probability that the two clusterings agree + * about the cluster memberships of a randomly chosen vertex \em pair. All vertex + * pairs are considered, and the two clusterings are considered to be in agreement + * about the memberships of a vertex pair if either the two vertices are in the + * same cluster in both clusterings, or they are in different clusters in both + * clusterings. The Rand index is then the number of vertex pairs in agreement, + * divided by the total number of vertex pairs. A Rand index of zero means that + * the two clusterings disagree about the membership of all vertex pairs, while + * 1 means that the two clusterings are identical. + * + * + * The adjusted Rand index is similar to the Rand index, but it takes into + * account that agreement between the two clusterings may also occur by chance + * even if the two clusterings are chosen completely randomly. The adjusted + * Rand index therefore subtracts the expected fraction of agreements from the + * value of the Rand index, and divides the result by one minus the expected + * fraction of agreements. The maximum value of the adjusted Rand index is + * still 1 (similarly to the Rand index), indicating maximum agreement, but + * the value may be less than zero if there is \em less agreement between the + * two clusterings than what would be expected by chance. + * + * + * For an explanation of the split-join distance, see \ref igraph_split_join_distance(). + * + * + * References: + * + * + * Meilă 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. + * https://doi.org/10.1007/978-3-540-45167-9_14 + * + * + * Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community structure + * identification. J Stat Mech P09008, 2005. + * https://doi.org/10.1088/1742-5468/2005/09/P09008 + * + * + * 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. + * https://ir.cwi.nl/pub/4461 + * + * + * Rand WM: Objective criteria for the evaluation of clustering methods. + * J Am Stat Assoc 66(336):846-850, 1971. + * https://doi.org/10.2307/2284239 + * + * + * Hubert L and Arabie P: Comparing partitions. Journal of Classification + * 2:193-218, 1985. + * https://doi.org/10.1007/BF01908075 + * + * \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. + * + * \sa \ref igraph_split_join_distance(). + * + * Time complexity: O(n log(n)). + */ +igraph_error_t igraph_compare_communities(const igraph_vector_int_t *comm1, + const igraph_vector_int_t *comm2, igraph_real_t* result, + igraph_community_comparison_t method) { + igraph_vector_int_t c1, c2; + + if (igraph_vector_int_size(comm1) != igraph_vector_int_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_int_init_copy(&c1, comm1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c1); + + IGRAPH_CHECK(igraph_vector_int_init_copy(&c2, comm2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c2); + + IGRAPH_CHECK(igraph_reindex_membership(&c1, NULL, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c2, NULL, 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_int_destroy(&c1); + igraph_vector_int_destroy(&c2); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * \sa \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)). + */ +igraph_error_t igraph_split_join_distance(const igraph_vector_int_t *comm1, + const igraph_vector_int_t *comm2, igraph_integer_t *distance12, + igraph_integer_t *distance21) { + igraph_vector_int_t c1, c2; + + if (igraph_vector_int_size(comm1) != igraph_vector_int_size(comm2)) { + IGRAPH_ERRORF("Community membership vectors have different lengths: %" IGRAPH_PRId " and %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_vector_int_size(comm1), igraph_vector_int_size(comm2)); + } + + /* Copy and reindex membership vectors to make sure they are continuous */ + IGRAPH_CHECK(igraph_vector_int_init_copy(&c1, comm1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c1); + + IGRAPH_CHECK(igraph_vector_int_init_copy(&c2, comm2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c2); + + IGRAPH_CHECK(igraph_reindex_membership(&c1, NULL, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c2, NULL, NULL)); + + IGRAPH_CHECK(igraph_i_split_join_distance(&c1, &c2, distance12, distance21)); + + /* Clean up everything */ + igraph_vector_int_destroy(&c1); + igraph_vector_int_destroy(&c2); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * 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 igraph_error_t igraph_i_entropy_and_mutual_information(const igraph_vector_int_t* v1, + const igraph_vector_int_t* v2, double* h1, double* h2, double* mut_inf) { + igraph_integer_t i, n; + igraph_integer_t k1; + igraph_integer_t k2; + igraph_real_t *p1, *p2; + igraph_sparsemat_t m; + igraph_sparsemat_t mu; /* uncompressed */ + igraph_sparsemat_iterator_t mit; + + n = igraph_vector_int_size(v1); + if (n == 0) { + *h1 = 0; + *h2 = 0; + *mut_inf = 0; + return IGRAPH_SUCCESS; + } + k1 = igraph_vector_int_max(v1) + 1; + k2 = igraph_vector_int_max(v2) + 1; + p1 = IGRAPH_CALLOC(k1, igraph_real_t); + if (p1 == 0) { + IGRAPH_ERROR("Insufficient memory for computing community entropy.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, p1); + p2 = IGRAPH_CALLOC(k2, igraph_real_t); + if (p2 == 0) { + IGRAPH_ERROR("Insufficient memory for computing community entropy.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, p2); + + /* Calculate the entropy of v1 */ + *h1 = 0.0; + for (i = 0; i < n; i++) { + p1[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[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_sparsemat_init(&mu, k1, k2, n)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_sparsemat_entry( + &mu, VECTOR(*v1)[i], + VECTOR(*v2)[i], 1 + )); + } + + IGRAPH_CHECK(igraph_sparsemat_compress(&mu, &m)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); + IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); + + IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); + while (!igraph_sparsemat_iterator_end(&mit)) { + double p = igraph_sparsemat_iterator_get(&mit)/ n; + *mut_inf += p * (log(p) - p1[igraph_sparsemat_iterator_row(&mit)] - p2[igraph_sparsemat_iterator_col(&mit)]); + igraph_sparsemat_iterator_next(&mit); + } + igraph_sparsemat_destroy(&m); + igraph_sparsemat_destroy(&mu); + IGRAPH_FREE(p1); IGRAPH_FREE(p2); + + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +/** + * 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 igraph_error_t igraph_i_compare_communities_nmi(const igraph_vector_int_t *v1, const igraph_vector_int_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 igraph_error_t igraph_i_compare_communities_vi(const igraph_vector_int_t *v1, const igraph_vector_int_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 igraph_error_t igraph_i_confusion_matrix(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, + igraph_sparsemat_t *m) { + igraph_integer_t k1, k2, i, n; + + n = igraph_vector_int_size(v1); + if (n == 0) { + IGRAPH_CHECK(igraph_sparsemat_resize(m, 0, 0, 0)); + return IGRAPH_SUCCESS; + } + + k1 = igraph_vector_int_max(v1) + 1; + k2 = igraph_vector_int_max(v2) + 1; + IGRAPH_CHECK(igraph_sparsemat_resize(m, k1, k2, n)); + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_sparsemat_entry( + m, VECTOR(*v1)[i], 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 igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, + igraph_integer_t* distance12, igraph_integer_t* distance21) { + igraph_integer_t n = igraph_vector_int_size(v1); + igraph_vector_t rowmax, colmax; + igraph_sparsemat_t m; + igraph_sparsemat_t mu; /* uncompressed */ + igraph_sparsemat_iterator_t mit; + + if (n == 0) { + *distance12 = 0; + *distance21 = 0; + return IGRAPH_SUCCESS; + } + /* Calculate the confusion matrix */ + IGRAPH_CHECK(igraph_sparsemat_init(&mu, 1, 1, 0)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); + IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &mu)); + + /* Initialize vectors that will store the row/columnwise maxima */ + IGRAPH_VECTOR_INIT_FINALLY(&rowmax, igraph_sparsemat_nrow(&mu)); + IGRAPH_VECTOR_INIT_FINALLY(&colmax, igraph_sparsemat_ncol(&mu)); + + /* Find the row/columnwise maxima */ + igraph_sparsemat_compress(&mu, &m); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); + IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); + IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); + while (!igraph_sparsemat_iterator_end(&mit)) { + igraph_real_t value = igraph_sparsemat_iterator_get(&mit); + igraph_integer_t row = igraph_sparsemat_iterator_row(&mit); + igraph_integer_t col = igraph_sparsemat_iterator_col(&mit); + if (value > VECTOR(rowmax)[row]) { + VECTOR(rowmax)[row] = value; + } + if (value > VECTOR(colmax)[col]) { + VECTOR(colmax)[col] = value; + } + igraph_sparsemat_iterator_next(&mit); + } + + /* 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_sparsemat_destroy(&m); + igraph_sparsemat_destroy(&mu); + IGRAPH_FINALLY_CLEAN(4); + + 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 igraph_error_t igraph_i_compare_communities_rand( + const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, + igraph_real_t *result, igraph_bool_t adjust) { + igraph_sparsemat_t m; + igraph_sparsemat_t mu; /* uncompressed */ + igraph_sparsemat_iterator_t mit; + igraph_vector_t rowsums, colsums; + igraph_integer_t i, nrow, ncol; + igraph_real_t rand, n; + igraph_real_t frac_pairs_in_1, frac_pairs_in_2; + + if (igraph_vector_int_size(v1) <= 1) { + IGRAPH_ERRORF("Rand indices not defined for only zero or one vertices. " + "Found membership vector of size %" IGRAPH_PRId ".", IGRAPH_EINVAL, igraph_vector_int_size(v1)); + } + + /* Calculate the confusion matrix */ + IGRAPH_CHECK(igraph_sparsemat_init(&mu, 1, 1, 0)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); + IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &mu)); + + /* 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_sparsemat_nrow(&mu); + ncol = igraph_sparsemat_ncol(&mu); + n = igraph_vector_int_size(v1); + IGRAPH_VECTOR_INIT_FINALLY(&rowsums, nrow); + IGRAPH_VECTOR_INIT_FINALLY(&colsums, ncol); + IGRAPH_CHECK(igraph_sparsemat_rowsums(&mu, &rowsums)); + IGRAPH_CHECK(igraph_sparsemat_colsums(&mu, &colsums)); + + /* Start calculating the unadjusted Rand index */ + rand = 0.0; + igraph_sparsemat_compress(&mu, &m); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); + IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); + + IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); + while (!igraph_sparsemat_iterator_end(&mit)) { + igraph_real_t value = igraph_sparsemat_iterator_get(&mit); + rand += (value / n) * (value - 1) / (n - 1); + igraph_sparsemat_iterator_next(&mit); + } + + 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_sparsemat_destroy(&m); + igraph_sparsemat_destroy(&mu); + IGRAPH_FINALLY_CLEAN(4); + + *result = rand; + + return IGRAPH_SUCCESS; +} diff --git a/src/community/edge_betweenness.c b/src/community/edge_betweenness.c new file mode 100644 index 0000000..16b64f1 --- /dev/null +++ b/src/community/edge_betweenness.c @@ -0,0 +1,767 @@ +/* -*- 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_nongraph.h" +#include "igraph_progress.h" +#include "igraph_stack.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +#include + +static igraph_error_t igraph_i_rewrite_membership_vector(igraph_vector_int_t *membership) { + const igraph_integer_t no = igraph_vector_int_max(membership) + 1; + igraph_vector_int_t idx; + igraph_integer_t realno = 0; + const igraph_integer_t len = igraph_vector_int_size(membership); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, no); + for (igraph_integer_t i = 0; i < len; i++) { + const igraph_integer_t t = 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_int_destroy(&idx); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, + const igraph_bool_t directed, + const igraph_vector_int_t *edges, + const igraph_vector_t *weights, + igraph_matrix_int_t *res, + igraph_vector_int_t *bridges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership) { + + igraph_vector_int_t mymembership; + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_real_t maxmod = -1; + igraph_integer_t midx = 0; + igraph_integer_t no_comps; + const igraph_bool_t use_directed = directed && igraph_is_directed(graph); + igraph_integer_t max_merges; + + if (membership) { + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + } + if (modularity || res || bridges) { + IGRAPH_CHECK(igraph_connected_components(graph, NULL, NULL, &no_comps, IGRAPH_WEAK)); + max_merges = no_of_nodes - no_comps; + + if (modularity) { + IGRAPH_CHECK(igraph_vector_resize(modularity, + max_merges + 1)); + } + if (res) { + IGRAPH_CHECK(igraph_matrix_int_resize(res, max_merges, + 2)); + } + if (bridges) { + IGRAPH_CHECK(igraph_vector_int_resize(bridges, max_merges)); + } + } + + IGRAPH_CHECK(igraph_vector_int_init_range(&mymembership, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &mymembership); + + if (membership) { + IGRAPH_CHECK(igraph_vector_int_update(membership, &mymembership)); + } + + IGRAPH_CHECK(igraph_modularity(graph, &mymembership, weights, + /* resolution */ 1, + use_directed, &maxmod)); + if (modularity) { + VECTOR(*modularity)[0] = maxmod; + } + + for (igraph_integer_t i = igraph_vector_int_size(edges) - 1; i >= 0; i--) { + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + igraph_integer_t c1 = VECTOR(mymembership)[from]; + igraph_integer_t c2 = VECTOR(mymembership)[to]; + igraph_real_t actmod; + + 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; + } + + /* The new cluster has id no_of_nodes+midx+1 */ + for (igraph_integer_t 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_CHECK(igraph_vector_int_update(membership, &mymembership)); + } + } + } + + midx++; + } + } + + if (membership) { + IGRAPH_CHECK(igraph_i_rewrite_membership_vector(membership)); + } + + igraph_vector_int_destroy(&mymembership); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/** + * \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. Merges happen when the + * edge removal process is run backwards and two components become + * connected. + * + * \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 n+1, etc. The matrix will be resized as needed. + * \param bridges Pointer to an initialized vector of \c NULL. If not + * \c NULL then the indices into \p edges of all edges which caused + * one of the merges will be put here. This is equal to all edge removals + * which separated the network into more components, in reverse order. + * \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. + */ +igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, + const igraph_bool_t directed, + const igraph_vector_int_t *edges, + const igraph_vector_t *weights, + igraph_matrix_int_t *res, + igraph_vector_int_t *bridges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t ptr; + igraph_integer_t midx = 0; + igraph_integer_t no_comps; + const igraph_integer_t no_removed_edges = igraph_vector_int_size(edges); + igraph_integer_t max_merges; + + if (! igraph_vector_int_isininterval(edges, 0, no_of_edges-1)) { + IGRAPH_ERROR("Invalid edge ID.", IGRAPH_EINVAL); + } + if (no_removed_edges < no_of_edges) { + IGRAPH_ERRORF("Number of removed edges (%" IGRAPH_PRId ") should be equal to " + "number of edges in graph (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + no_removed_edges, no_of_edges); + } + + /* catch null graph early */ + if (no_of_nodes == 0) { + if (res) { + IGRAPH_CHECK(igraph_matrix_int_resize(res, 0, 2)); + } + if (bridges) { + igraph_vector_int_clear(bridges); + } + if (modularity) { + IGRAPH_CHECK(igraph_vector_resize(modularity, 1)); + VECTOR(*modularity)[0] = IGRAPH_NAN; + } + if (membership) { + igraph_vector_int_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_connected_components(graph, NULL, NULL, &no_comps, IGRAPH_WEAK)); + + max_merges = no_of_nodes - no_comps; + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, no_of_nodes * 2 - 1); + if (res) { + IGRAPH_CHECK(igraph_matrix_int_resize(res, max_merges, 2)); + } + if (bridges) { + IGRAPH_CHECK(igraph_vector_int_resize(bridges, max_merges)); + } + + for (igraph_integer_t i = igraph_vector_int_size(edges) - 1; i >= 0; i--) { + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t from, to, c1, c2, idx; + IGRAPH_CHECK(igraph_edge(graph, edge, &from, &to)); + idx = from + 1; + while (VECTOR(ptr)[idx - 1] != 0) { + idx = VECTOR(ptr)[idx - 1]; + } + c1 = idx - 1; + idx = to + 1; + while (VECTOR(ptr)[idx - 1] != 0) { + idx = 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; + } + + 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_int_destroy(&ptr); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Find the smallest active element in the vector */ +static igraph_integer_t igraph_i_vector_which_max_not_null(const igraph_vector_t *v, + const bool *passive) { + igraph_integer_t 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). https://doi.org/10.1073/pnas.122653799 + * + * + * 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 splits into two components, + * then after a while one of these components splits again into two smaller + * components, and so on until all edges are removed. This is a divisive + * hierarchical approach, the result of which is a dendrogram. + * + * + * In directed graphs, when \p directed is set to true, the directed version + * of betweenness and modularity are used, however, only splits into + * \em weakly connected components are detected. + * + * \param graph The input graph. + * \param removed_edges 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 + * \c NULL then the indices into \p result of all edges which caused + * one of the \p merges will be put here. This is equivalent to all edge removals + * which separated the network into more components, in reverse order. + * \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. Controls whether to calculate directed + * betweenness (i.e. directed paths) for directed graphs, and whether + * to use the directed version of modularity. 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 + */ +igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, + igraph_vector_int_t *removed_edges, + igraph_vector_t *edge_betweenness, + igraph_matrix_int_t *merges, + igraph_vector_int_t *bridges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership, + igraph_bool_t directed, + const igraph_vector_t *weights) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + double *distance, *tmpscore; + double *nrgeo; + + igraph_inclist_t elist_out, elist_in, parents; + igraph_inclist_t *elist_out_p, *elist_in_p; + igraph_vector_int_t *neip; + igraph_integer_t neino; + igraph_vector_t eb; + igraph_integer_t maxedge, pos; + igraph_integer_t from, to; + igraph_bool_t result_owned = false; + igraph_stack_int_t stack; + igraph_real_t steps, steps_done; + + bool *passive; + + /* Needed only for the unweighted case */ + igraph_dqueue_int_t q; + + /* Needed only for the weighted case */ + igraph_2wheap_t heap; + + if (removed_edges == NULL) { + removed_edges = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(removed_edges, "Insufficient memory for edge betweenness-based community detection."); + IGRAPH_FINALLY(igraph_free, removed_edges); + + IGRAPH_VECTOR_INT_INIT_FINALLY(removed_edges, 0); + result_owned = true; + } + + 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); + IGRAPH_CHECK_OOM(distance, "Insufficient memory for edge betweenness-based community detection."); + IGRAPH_FINALLY(igraph_free, distance); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, double); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for edge betweenness-based community detection."); + IGRAPH_FINALLY(igraph_free, nrgeo); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, double); + IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for edge betweenness-based community detection."); + IGRAPH_FINALLY(igraph_free, tmpscore); + + if (weights == NULL) { + IGRAPH_DQUEUE_INT_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 (isnan(minweight)) { + IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); + } + } + + if (membership != NULL) { + IGRAPH_WARNING("Membership vector will be selected based on the highest " + "modularity score."); + } + + if (modularity != NULL || membership != NULL) { + 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(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &parents); + } + + IGRAPH_STACK_INT_INIT_FINALLY(&stack, no_of_nodes); + + IGRAPH_CHECK(igraph_vector_int_resize(removed_edges, 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, bool); + IGRAPH_CHECK_OOM(passive, "Insufficient memory for edge betweenness-based community detection."); + 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 (igraph_integer_t 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 == NULL) { + /* Unweighted variant follows */ + + /* The following for loop is copied almost intact from + * igraph_edge_betweenness_cutoff */ + for (igraph_integer_t 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_int_clear(&stack); /* it should be empty anyway... */ + + IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); + + nrgeo[source] = 1; + distance[source] = 0; + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + + neip = igraph_inclist_get(elist_out_p, actnode); + neino = igraph_vector_int_size(neip); + for (igraph_integer_t i = 0; i < neino; i++) { + igraph_integer_t edge = VECTOR(*neip)[i]; + igraph_integer_t neighbor = 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_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, neighbor)); + } + } + } /* while !igraph_dqueue_int_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_int_empty(&stack)) { + igraph_integer_t actnode = igraph_stack_int_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 (igraph_integer_t i = 0; i < neino; i++) { + igraph_integer_t edge = VECTOR(*neip)[i]; + igraph_integer_t 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 */ + + const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; + int cmp_result; + + /* The following for loop is copied almost intact from + * igraph_i_edge_betweenness_cutoff_weighted */ + for (igraph_integer_t 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_CHECK(igraph_2wheap_push_with_index(&heap, source, 0)); + distance[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_2wheap_empty(&heap)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&heap); + igraph_real_t mindist = -igraph_2wheap_delete_max(&heap); + + IGRAPH_CHECK(igraph_stack_int_push(&stack, minnei)); + + neip = igraph_inclist_get(elist_out_p, minnei); + neino = igraph_vector_int_size(neip); + + for (igraph_integer_t i = 0; i < neino; i++) { + igraph_integer_t edge = VECTOR(*neip)[i]; + igraph_integer_t 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; + + /* Note: curdist == 0 means infinity, and for this case + * cmp_result should be -1. However, this case is handled + * specially below, without referring to cmp_result. */ + cmp_result = igraph_cmp_epsilon(altdist, curdist - 1, eps); + + if (curdist == 0) { + /* This is the first finite distance to 'to' */ + v = igraph_inclist_get(&parents, 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 (cmp_result < 0) { + /* This is a shorter path */ + v = igraph_inclist_get(&parents, to); + igraph_vector_int_resize(v, 1); + VECTOR(*v)[0] = edge; + nrgeo[to] = nrgeo[minnei]; + distance[to] = altdist + 1.0; + igraph_2wheap_modify(&heap, to, -altdist); + } else if (cmp_result == 0) { + /* Another path with the same length */ + v = igraph_inclist_get(&parents, to); + IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); + nrgeo[to] += nrgeo[minnei]; + } + } + } /* igraph_2wheap_empty(&Q) */ + + while (!igraph_stack_int_empty(&stack)) { + igraph_integer_t w = igraph_stack_int_pop(&stack); + igraph_vector_int_t *parv = igraph_inclist_get(&parents, w); + igraph_integer_t parv_len = igraph_vector_int_size(parv); + + for (igraph_integer_t i = 0; i < parv_len; i++) { + igraph_integer_t fedge = VECTOR(*parv)[i]; + igraph_integer_t 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(parv); + } + } /* 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(*removed_edges)[e] = maxedge; + if (edge_betweenness) { + VECTOR(*edge_betweenness)[e] = VECTOR(eb)[maxedge]; + if (!directed) { + VECTOR(*edge_betweenness)[e] /= 2.0; + } + } + passive[maxedge] = true; + IGRAPH_CHECK(igraph_edge(graph, 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_int_destroy(&stack); + IGRAPH_FINALLY_CLEAN(3); + + if (weights == NULL) { + igraph_dqueue_int_destroy(&q); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_2wheap_destroy(&heap); + igraph_inclist_destroy(&parents); + 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, removed_edges, weights, merges, + bridges, modularity, + membership)); + } + + if (result_owned) { + igraph_vector_int_destroy(removed_edges); + IGRAPH_FREE(removed_edges); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/community/fast_modularity.c b/src/community/fast_modularity.c new file mode 100644 index 0000000..cc3bb75 --- /dev/null +++ b/src/community/fast_modularity.c @@ -0,0 +1,1088 @@ +/* -*- 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 { + igraph_integer_t first; /* first member of the community pair */ + igraph_integer_t 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 { + igraph_integer_t 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 true if the maximum is different from the previous one, + * false otherwise. */ +static igraph_bool_t igraph_i_fastgreedy_community_rescan_max(igraph_i_fastgreedy_community *comm) { + igraph_integer_t 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 = NULL; + return true; + } + + 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 true; + } else { + return false; + } +} + +/* Destroys the global community list object */ +static void igraph_i_fastgreedy_community_list_destroy( + igraph_i_fastgreedy_community_list *list) { + igraph_integer_t i; + for (i = 0; i < list->n; i++) { + igraph_vector_ptr_destroy(&list->e[i].neis); + } + IGRAPH_FREE(list->e); + if (list->heapindex != NULL) { + IGRAPH_FREE(list->heapindex); + } + if (list->heap != NULL) { + 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, igraph_integer_t idx) { + igraph_integer_t 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, igraph_integer_t idx) { + igraph_integer_t 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) { + igraph_integer_t 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) { + igraph_integer_t 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(" (%" IGRAPH_PRId ", %" IGRAPH_PRId ", %.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("%" IGRAPH_PRId " ", 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) { + igraph_integer_t 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: %" IGRAPH_PRId ", %" IGRAPH_PRId " and %" IGRAPH_PRId "\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, igraph_integer_t idx) { + igraph_real_t old; + igraph_integer_t commidx; + + /* First adjust the index */ + commidx = list->heap[list->no_of_communities - 1]->maxdq->first; + list->heapindex[commidx] = 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, igraph_integer_t idx, igraph_integer_t comm) { + igraph_integer_t 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] = 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, igraph_integer_t c, igraph_integer_t k) { + igraph_integer_t i, n; + igraph_bool_t rescan = false; + 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 = true; + } + 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): %" IGRAPH_PRId "\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; + igraph_integer_t diff; + cp1 = *(igraph_i_fastgreedy_commpair**)p1; + cp2 = *(igraph_i_fastgreedy_commpair**)p2; + diff = cp1->second - cp2->second; + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; +} + +/* 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, igraph_integer_t index, + igraph_i_fastgreedy_commpair *changed_pair) { + igraph_vector_ptr_t *vec; + igraph_integer_t i, n; + igraph_bool_t can_skip_sort = false; + igraph_i_fastgreedy_commpair *other_pair; + + vec = &list->e[index].neis; + if (changed_pair != NULL) { + /* 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 */ + IGRAPH_ASSERT(i < n); + + /* 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 = true; + } + + 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 igraph_bool_t igraph_i_fastgreedy_community_update_dq( + igraph_i_fastgreedy_community_list *list, + igraph_i_fastgreedy_commpair *p, igraph_real_t newdq) { + + igraph_integer_t 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 true; + } 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 false; + } 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 true; +} + +/** + * \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 + */ +igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_matrix_int_t *merges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership) { + igraph_integer_t no_of_edges, no_of_nodes, no_of_joins, total_joins; + igraph_integer_t i, j, k, n, m, from, to, dummy, best_no_of_joins; + igraph_eit_t edgeit; + igraph_i_fastgreedy_commpair *pairs, *p1, *p2; + igraph_i_fastgreedy_community_list communities; + igraph_vector_t a; + igraph_vector_int_t degrees; + igraph_real_t q, *dq, bestq, weight_sum, loop_weight_sum; + igraph_bool_t has_multiple; + igraph_matrix_int_t merges_local; + + /*igraph_integer_t 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 };*/ + /*igraph_integer_t 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) { + 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 (isnan(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 != NULL && merges == NULL) { + /* We need the merge matrix because the user wants the membership + * vector, so we allocate one on our own */ + IGRAPH_CHECK(igraph_matrix_int_init(&merges_local, total_joins, 2)); + IGRAPH_FINALLY(igraph_matrix_int_destroy, &merges_local); + merges = &merges_local; + } + + if (merges != NULL) { + IGRAPH_CHECK(igraph_matrix_int_resize(merges, total_joins, 2)); + igraph_matrix_int_null(merges); + } + + if (modularity != NULL) { + 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)[IGRAPH_FROM(graph, i)] += VECTOR(*weights)[i]; + VECTOR(a)[IGRAPH_TO(graph, i)] += VECTOR(*weights)[i]; + } + } else { + debug("Calculating degrees\n"); + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, true)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(a)[i] = VECTOR(degrees)[i]; + } + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(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_CALLOC(no_of_nodes, igraph_i_fastgreedy_community); + IGRAPH_CHECK_OOM(communities.e, "Insufficient memory for fast greedy community detection."); + IGRAPH_FINALLY(igraph_free, communities.e); + + communities.heap = IGRAPH_CALLOC(no_of_nodes, igraph_i_fastgreedy_community*); + IGRAPH_CHECK_OOM(communities.heap, "Insufficient memory for fast greedy community detection."); + IGRAPH_FINALLY(igraph_free, communities.heap); + + communities.heapindex = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(communities.heapindex, "Insufficient memory for fast greedy community detection."); + + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY(igraph_i_fastgreedy_community_list_destroy, &communities); + + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_vector_ptr_init(&communities.e[i].neis, 0)); + communities.e[i].id = i; + communities.e[i].size = 1; + } + + /* Create list of community pairs from edges */ + debug("Allocating dq vector\n"); + dq = IGRAPH_CALLOC(no_of_edges, igraph_real_t); + IGRAPH_CHECK_OOM(dq, "Insufficient memory for fast greedy community detection."); + IGRAPH_FINALLY(igraph_free, dq); + + debug("Creating community pair list\n"); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + pairs = IGRAPH_CALLOC(2 * no_of_edges, igraph_i_fastgreedy_commpair); + IGRAPH_CHECK_OOM(pairs, "Insufficient memory for fast greedy community detection."); + 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)) { + igraph_integer_t eidx = IGRAPH_EIT_GET(edgeit); + + /* Create the pairs themselves */ + from = IGRAPH_FROM(graph, eidx); to = IGRAPH_TO(graph, eidx); + 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_CHECK(igraph_vector_ptr_push_back(&communities.e[from].neis, &pairs[i])); + IGRAPH_CHECK(igraph_vector_ptr_push_back(&communities.e[to].neis, &pairs[i + 1])); + /* Update maximums */ + if (communities.e[from].maxdq == NULL || *communities.e[from].maxdq->dq < *pairs[i].dq) { + communities.e[from].maxdq = &pairs[i]; + } + if (communities.e[to].maxdq == NULL || *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, NULL); + /* Isolated vertices and vertices with loop edges only won't be stored in + * the heap (to avoid maxdq == NULL) */ + if (communities.e[i].maxdq != NULL) { + communities.heap[j] = &communities.e[i]; + communities.heapindex[i] = 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] == NULL) { + break; /* no more communities */ + } + if (communities.heap[0]->maxdq == NULL) { + 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: %" IGRAPH_PRId "-%" IGRAPH_PRId " and %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p1->first, p1->second, + p2->first, p2->second); + if (p1->second < p2->second) { + /* Considering p1 from now on */ + debug(" Considering: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p1->first, p1->second); + if (p1->second == from) { + debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\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: %" IGRAPH_PRId "-%" IGRAPH_PRId " and %" IGRAPH_PRId "-%" IGRAPH_PRId "\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: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p2->first, p2->second); + if (p2->second == to) { + debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\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 anymore. 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 = NULL; + while (i < n) { + p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i]; + if (p1->second == from) { + debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\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: %" IGRAPH_PRId "-%" IGRAPH_PRId "\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 = NULL; + + /* 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 = 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 (merges != NULL) { + if (no_of_joins < total_joins) { + igraph_integer_t *ivec; + igraph_integer_t merges_nrow = igraph_matrix_int_nrow(merges); + + ivec = IGRAPH_CALLOC(merges_nrow, igraph_integer_t); + IGRAPH_CHECK_OOM(ivec, "Insufficient memory for fast greedy community detection."); + IGRAPH_FINALLY(igraph_free, ivec); + + for (i = 0; i < no_of_joins; i++) { + ivec[i] = i + 1; + } + + igraph_matrix_int_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_CHECK(igraph_vector_resize(modularity, no_of_joins + 1)); + } + + /* Internally, the algorithm does not create NaN values. + * If the graph has no edges, the final modularity will be zero. + * We change this to NaN for consistency. */ + if (modularity && no_of_edges == 0) { + IGRAPH_ASSERT(no_of_joins == 0); + VECTOR(*modularity)[0] = IGRAPH_NAN; + } + + 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, + no_of_nodes, + /*steps=*/ best_no_of_joins, + membership, + /*csize=*/ 0)); + } + + if (merges == &merges_local) { + igraph_matrix_int_destroy(&merges_local); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +#ifdef IGRAPH_FASTCOMM_DEBUG + #undef IGRAPH_FASTCOMM_DEBUG +#endif diff --git a/src/community/fluid.c b/src/community/fluid.c new file mode 100644 index 0000000..7f34dc4 --- /dev/null +++ b/src/community/fluid.c @@ -0,0 +1,262 @@ +/* -*- 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. Weighted graphs are not supported. + * + * + * 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. https://doi.org/10.1007/978-3-319-72150-7_19 + * + * \param graph The input graph. The graph must be simple and connected. + * Edge directions will be ignored. + * \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|) + */ +igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, + igraph_integer_t no_of_communities, + igraph_vector_int_t *membership) { + /* Declaration of variables */ + igraph_integer_t no_of_nodes, i, j, k, kv1; + igraph_adjlist_t al; + igraph_real_t max_density; + igraph_bool_t is_simple, is_connected, running; + igraph_vector_t density, label_counters; + igraph_vector_int_t dominant_labels, node_order, 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_int_resize(membership, no_of_nodes)); + igraph_vector_int_null(membership); + } + return IGRAPH_SUCCESS; + } + if (no_of_communities < 1) { + IGRAPH_ERROR("Number of requested communities must be greater than zero.", IGRAPH_EINVAL); + } + if (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, &is_simple)); + if (!is_simple) { + IGRAPH_ERROR("Fluid community detection supports only simple graphs.", IGRAPH_EINVAL); + } + if (igraph_is_directed(graph)) { + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + igraph_bool_t has_mutual; + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + IGRAPH_ERROR("Fluid community detection supports only simple graphs.", IGRAPH_EINVAL); + } + } + IGRAPH_CHECK(igraph_is_connected(graph, &is_connected, IGRAPH_WEAK)); + if (!is_connected) { + 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_int_resize(membership, no_of_nodes)); + + /* Initialize density and com_to_numvertices vectors */ + IGRAPH_CHECK(igraph_vector_init(&density, no_of_communities)); + IGRAPH_FINALLY(igraph_vector_destroy, &density); + IGRAPH_CHECK(igraph_vector_int_init(&com_to_numvertices, no_of_communities)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &com_to_numvertices); + + /* Initialize node ordering vector */ + IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + + /* Initialize the membership vector with 0 values */ + igraph_vector_int_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_int_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)[VECTOR(node_order)[i]] = i + 1; + /* 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_INT_INIT_FINALLY(&dominant_labels, no_of_communities); + + IGRAPH_CHECK(igraph_vector_init(&label_counters, no_of_communities)); + IGRAPH_FINALLY(igraph_vector_destroy, &label_counters); + + RNG_BEGIN(); + + /* running is the convergence boolean variable */ + running = true; + while (running) { + /* Declarations of variables used inside main loop */ + igraph_integer_t 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 = false; + + /* Shuffle the node ordering vector */ + IGRAPH_CHECK(igraph_vector_int_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_int_clear(&dominant_labels); + igraph_vector_null(&label_counters); + + /* Obtain actual node index */ + v1 = VECTOR(node_order)[i]; + /* Take into account same label in updating rule */ + kv1 = 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_int_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 = VECTOR(*membership)[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_int_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_int_push_back(&dominant_labels, k)); + } + } + + if (!igraph_vector_int_empty(&dominant_labels)) { + /* Maintain same label if it exists in dominant_labels */ + same_label_in_dominant = igraph_vector_int_contains(&dominant_labels, kv1); + + if (!same_label_in_dominant) { + /* We need at least one more iteration */ + running = true; + + /* Select randomly from the dominant labels */ + rand_idx = RNG_INTEGER(0, igraph_vector_int_size(&dominant_labels) - 1); + k = 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; + IGRAPH_ASSERT(VECTOR(*membership)[i] >= 0); /* all vertices must have a community assigned */ + } + + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(1); + + igraph_vector_int_destroy(&node_order); + igraph_vector_destroy(&density); + igraph_vector_int_destroy(&com_to_numvertices); + igraph_vector_destroy(&label_counters); + igraph_vector_int_destroy(&dominant_labels); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} diff --git a/src/community/infomap/infomap.cc b/src/community/infomap/infomap.cc new file mode 100644 index 0000000..88bb2ac --- /dev/null +++ b/src/community/infomap/infomap.cc @@ -0,0 +1,323 @@ +/* -*- 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 "igraph_community.h" + +#include "core/exceptions.h" +#include "core/interruption.h" + +#include "infomap_Node.h" +#include "infomap_FlowGraph.h" +#include "infomap_Greedy.h" + +#include +#include + +// This is necessary for GCC 5 and earlier, where including +// makes isnan() unusable without the std:: prefix, even if +// was included as well. +using std::isnan; + +/****************************************************************************/ +static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { + + // save the original graph + FlowGraph cpy_fgraph(fgraph); + + igraph_integer_t Nnode = cpy_fgraph.Nnode; + // "real" number of vertex, ie. number of vertex of the graph + + igraph_integer_t iteration = 0; + double outer_oldCodeLength, newCodeLength; + + std::vector initial_move; + bool initial_move_done = true; + + // re-use vector in loop for better performance + std::vector subMoveTo; + + 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.resize(Nnode); + // new_cluster_id --> old_cluster_id (save curent clustering state) + + initial_move_done = false; + + subMoveTo.clear(); // 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.resize(Nnode); + // vid_cpy_fgraph --> new_cluster_id (new partition) + + igraph_integer_t subModIndex = 0; + + for (igraph_integer_t i = 0 ; i < fgraph.Nnode ; i++) { + // partition each non trivial module + size_t sub_Nnode = fgraph.node[i].members.size(); + if (sub_Nnode > 1) { // If the module is not trivial + const std::vector &sub_members = fgraph.node[i].members; + + // extraction of the subgraph + FlowGraph sub_fgraph(cpy_fgraph, sub_members); + sub_fgraph.initiate(); + + // recursif call of partitionment on the subgraph + infomap_partition(sub_fgraph, true); + + // Record membership changes + for (igraph_integer_t j = 0; j < sub_fgraph.Nnode; j++) { + for (const auto &v : sub_fgraph.node[j].members) { + subMoveTo[sub_members[v]] = subModIndex; + } + initial_move[subModIndex] = i; + subModIndex++; + } + } 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 (igraph_integer_t i = 0; i < fgraph.Nnode; i++) { // for each module + for (const auto &v : fgraph.node[i].members) { // for each vertex (of the module) + initial_move[v] = i; + } + } + } + + fgraph.back_to(cpy_fgraph); + if (! subMoveTo.empty()) { + Greedy cpy_greedy(&fgraph); + + cpy_greedy.setMove(subMoveTo); + cpy_greedy.apply(false); + } + } + /**********************************************************************/ + // SECOND PART: greedy optimizing it self + // =========================================== + double oldCodeLength; + + do { + // greedy optimizing object creation + Greedy greedy(&fgraph); + + // Initial move to apply ? + if (!initial_move_done && ! initial_move.empty()) { + initial_move_done = true; + greedy.setMove(initial_move); + } + + oldCodeLength = greedy.codeLength; + bool moved = true; + double inner_oldCodeLength = 1000; + + while (moved) { // main greedy optimizing loop + inner_oldCodeLength = greedy.codeLength; + moved = greedy.optimize(); + + if (fabs(greedy.codeLength - inner_oldCodeLength) < 1.0e-10) + // if the move does'n reduce the codelenght -> exit ! + { + moved = false; + } + } + + // transform the network to network of modules: + greedy.apply(true); + newCodeLength = greedy.codeLength; + } while (oldCodeLength - newCodeLength > 1.0e-10); + // while there is some improvement + + iteration++; + if (!rcall) { + IGRAPH_ALLOW_INTERRUPTION(); + } + } while (outer_oldCodeLength - newCodeLength > 1.0e-10); + + 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. This algorithm takes edge directions + * into account. + * + * + * For more details, see the visualization of the math and the map generator + * at https://www.mapequation.org . The original paper describing the algorithm + * is: M. Rosvall and C. T. Bergstrom, Maps of information flow reveal community + * structure in complex networks, PNAS 105, 1118 (2008) + * (https://dx.doi.org/10.1073/pnas.0706851105, https://arxiv.org/abs/0707.0609). + * A more detailed paper about the algorithm is: M. Rosvall, D. Axelsson, and + * C. T. Bergstrom, The map equation, Eur. Phys. J. Special Topics 178, 13 (2009). + * (https://dx.doi.org/10.1140/epjst/e2010-01179-1, https://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 . + * Integration in igraph was 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 the original + * implementation) you can use \ref igraph_rng_seed(). + * + * \param graph The input graph. Edge directions are taken into account. + * \param e_weights Numeric vector giving the weights of the edges. + * The random walker will favour edges with high weights over + * edges with low weights; the probability of picking a particular + * outbound edge from a node is directly proportional to its weight. + * If it is \c NULL then all edges will have equal + * weights. The weights are expected to be non-negative. + * \param v_weights Numeric vector giving the weights of the vertices. + * Vertices with higher weights are favoured by the random walker + * when it needs to "teleport" to a new node after getting stuck in + * a sink node (i.e. a node with no outbound edges). The probability + * of picking a vertex when the random walker teleports is directly + * proportional to the weight of the vertex. If this argument is \c NULL + * then all vertices will have equal weights. 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. + */ +igraph_error_t igraph_community_infomap(const igraph_t * graph, + const igraph_vector_t *e_weights, + const igraph_vector_t *v_weights, + igraph_integer_t nb_trials, + igraph_vector_int_t *membership, + igraph_real_t *codelength) { + + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + + if (e_weights) { + const igraph_integer_t ecount = igraph_ecount(graph); + if (igraph_vector_size(e_weights) != ecount) { + IGRAPH_ERROR("Invalid edge weight vector length.", IGRAPH_EINVAL); + } + if (ecount > 0) { + /* Allow both positive and zero weights. + * The conversion to Infomap format will simply skip zero-weight edges/ */ + igraph_real_t minweight = igraph_vector_min(e_weights); + if (minweight < 0) { + IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Edge weights must not be NaN values.", IGRAPH_EINVAL); + } + } + } + + if (v_weights) { + const igraph_integer_t vcount = igraph_vcount(graph); + if (igraph_vector_size(v_weights) != vcount) { + IGRAPH_ERROR("Invalid vertex weight vector length.", IGRAPH_EINVAL); + } + if (vcount > 0) { + /* TODO: Currently we require strictly positive. Can this be + * relaxed to non-negative values? */ + igraph_real_t minweight = igraph_vector_min(v_weights); + if (minweight <= 0) { + IGRAPH_ERROR("Vertex weights must be positive.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Vertex weights must not be NaN values.", IGRAPH_EINVAL); + } + } + } + + FlowGraph fgraph(graph, e_weights, v_weights); + + // compute stationary distribution + fgraph.initiate(); + + double shortestCodeLength = 1000.0; + + // create membership vector + igraph_integer_t Nnode = fgraph.Nnode; + IGRAPH_CHECK(igraph_vector_int_resize(membership, Nnode)); + + for (igraph_integer_t trial = 0; trial < nb_trials; trial++) { + FlowGraph cpy_fgraph(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 (igraph_integer_t i = 0 ; i < cpy_fgraph.Nnode ; i++) { + size_t Nmembers = cpy_fgraph.node[i].members.size(); + for (size_t k = 0; k < Nmembers; k++) { + //cluster[ cpy_fgraph->node[i].members[k] ] = i; + VECTOR(*membership)[cpy_fgraph.node[i].members[k]] = i; + } + } + } + } + + *codelength = shortestCodeLength / log(2.0); + + IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, NULL)); + + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END; +} diff --git a/src/community/infomap/infomap_FlowGraph.cc b/src/community/infomap/infomap_FlowGraph.cc new file mode 100644 index 0000000..405dc6a --- /dev/null +++ b/src/community/infomap/infomap_FlowGraph.cc @@ -0,0 +1,382 @@ +/* -*- 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" + +using namespace std; + +void FlowGraph::init(igraph_integer_t n, const igraph_vector_t *v_weights) { + alpha = 0.15; + beta = 1.0 - alpha; + Nnode = n; + node.reserve(Nnode); + if (v_weights) { + for (igraph_integer_t i = 0; i < Nnode; i++) { + node.emplace_back(i, VECTOR(*v_weights)[i]); + } + } else { + for (igraph_integer_t i = 0; i < Nnode; i++) { + node.emplace_back(i, 1.0); + } + } +} + +FlowGraph::FlowGraph(igraph_integer_t n) { + init(n, nullptr); +} + +/* 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) { + + igraph_integer_t n = igraph_vcount(graph); + init(n, v_weights); + + bool directed = igraph_is_directed(graph); + + double linkWeight = 1.0; + igraph_integer_t from, to; + + igraph_integer_t Nlinks = igraph_ecount(graph); + if (!directed) { + Nlinks = Nlinks * 2 ; + } + for (igraph_integer_t i = 0; i < Nlinks; i++) { + if (!directed) { // not directed + if (i % 2 == 0) { + linkWeight = e_weights ? 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 ? VECTOR(*e_weights)[i] : 1.0; + igraph_edge(graph, i, &from, &to); + } + + // Populate node from igraph_graph + // Negative edge weights were checked for already. + // We skip adding zero-weight edges. + if (linkWeight > 0.0) { + if (from != to) { + node[from].outLinks.emplace_back(to, linkWeight); + node[to].inLinks.emplace_back(from, linkWeight); + } + } + } +} + +FlowGraph::FlowGraph(const FlowGraph &fgraph) { + igraph_integer_t n = fgraph.Nnode; + init(n, nullptr); + for (igraph_integer_t i = 0; i < n; i++) { + 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(const FlowGraph &fgraph, const vector &sub_members) { + igraph_integer_t sub_Nnode = sub_members.size(); + + init(sub_Nnode, nullptr); + + //XXX: use set of integer to ensure that elements are sorted + set sub_mem(sub_members.begin(), sub_members.end()); + + auto it_mem = sub_mem.begin(); + + vector sub_renumber(fgraph.Nnode, -1); + // id --> sub_id + + for (igraph_integer_t j = 0; j < sub_Nnode; j++) { + igraph_integer_t 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 + + size_t orig_NoutLinks = fgraph.node[orig_nr].outLinks.size(); + size_t orig_NinLinks = fgraph.node[orig_nr].inLinks.size(); + + sub_renumber[orig_nr] = j; + + for (size_t k = 0; k < orig_NoutLinks; k++) { + igraph_integer_t to = fgraph.node[orig_nr].outLinks[k].first; + igraph_integer_t 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.emplace_back(to_newnr, link_weight); + node[to_newnr].inLinks.emplace_back(j, link_weight); + } + } + } + + for (size_t k = 0; k < orig_NinLinks; k++) { + igraph_integer_t to = fgraph.node[orig_nr].inLinks[k].first; + igraph_integer_t 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.emplace_back(to_newnr, link_weight); + node[to_newnr].outLinks.emplace_back(j, link_weight); + } + } + } + it_mem++; + } +} + + +/** Swap the graph with the one given + the graph is "re" calibrate + but NOT the given one. + */ +void FlowGraph::swap(FlowGraph &fgraph) noexcept { + node.swap(fgraph.node); + + igraph_integer_t Nnode_tmp = fgraph.Nnode; + fgraph.Nnode = Nnode; + 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 (igraph_integer_t i = 0; i < Nnode; i++) { + totTeleportWeight += node[i].teleportWeight; + } + + for (igraph_integer_t 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 + size_t NoutLinks = node[i].outLinks.size(); + double sum = node[i].selfLink; // Take care of self-links + for (size_t j = 0; j < NoutLinks; j++) { + sum += node[i].outLinks[j].second; + } + node[i].selfLink /= sum; + for (size_t j = 0; j < NoutLinks; j++) { + node[i].outLinks[j].second /= sum; + } + } + } + + // Calculate steady state matrix + eigenvector(); + + // Update links to represent flow + for (igraph_integer_t 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()) { + size_t NoutLinks = node[i].outLinks.size(); + for (size_t 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 (size_t j = 0; j < NoutLinks; j++) { + size_t NinLinks = node[node[i].outLinks[j].first].inLinks.size(); + for (size_t 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 (igraph_integer_t 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 (igraph_integer_t 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(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 (igraph_integer_t i = 0; i < Ndanglings; i++) { + danglingSize += size_tmp[danglings[i]]; + } + + // Flow from teleportation + for (igraph_integer_t i = 0; i < Nnode; i++) { + node[i].size = (alpha + beta * danglingSize) * node[i].teleportWeight; + } + + // Flow from network steps + for (igraph_integer_t i = 0; i < Nnode; i++) { + node[i].size += beta * node[i].selfLink * size_tmp[i]; + size_t Nlinks = node[i].outLinks.size(); + for (size_t 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 (igraph_integer_t i = 0; i < Nnode; i++) { + sum += node[i].size; + } + sqdiff_old = sqdiff; + sqdiff = 0.0; + for (igraph_integer_t 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 (igraph_integer_t 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() noexcept { + exit_log_exit = 0.0; + exitFlow = 0.0; + size_log_size = 0.0; + + for (igraph_integer_t 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(const FlowGraph &fgraph) { + // delete current nodes and copy original ones + Nnode = fgraph.Nnode; + node = fgraph.node; + + // 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/community/infomap/infomap_FlowGraph.h b/src/community/infomap/infomap_FlowGraph.h new file mode 100644 index 0000000..6364d5d --- /dev/null +++ b/src/community/infomap/infomap_FlowGraph.h @@ -0,0 +1,81 @@ +/* -*- 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 INFOMAP_FLOWGRAPH_H +#define INFOMAP_FLOWGRAPH_H + +#include "infomap_Node.h" + +#include "igraph_datatype.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +#include +#include +#include + +inline double plogp(double x) { + return x > 0.0 ? x*std::log(x) : 0.0; +} + +class FlowGraph { +private: + void init(igraph_integer_t n, const igraph_vector_t *nodeWeights); + +public: + explicit FlowGraph(igraph_integer_t n); + + FlowGraph(const FlowGraph &fgraph); + FlowGraph(const FlowGraph &fgraph, const std::vector &sub_members); + + FlowGraph(const igraph_t *graph, const igraph_vector_t *e_weights, + const igraph_vector_t *v_weights); + + void swap(FlowGraph &fgraph) noexcept; + + void initiate(); + void eigenvector(); + void calibrate() noexcept; + + void back_to(const FlowGraph &fgraph); + + /*************************************************************************/ + std::vector node; + igraph_integer_t Nnode; + + double alpha, beta; + + igraph_integer_t 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; +}; + +#endif // INFOMAP_FLOWGRAPH_H diff --git a/src/community/infomap/infomap_Greedy.cc b/src/community/infomap/infomap_Greedy.cc new file mode 100644 index 0000000..954e916 --- /dev/null +++ b/src/community/infomap/infomap_Greedy.cc @@ -0,0 +1,522 @@ +/* -*- 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 +#include +#include + +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 + + node_index(Nnode), + + mod_empty(Nnode), + mod_exit(Nnode), + mod_size(Nnode), + mod_danglingSize(Nnode), + mod_teleportWeight(Nnode), + mod_members(Nnode) +{ + nodeSize_log_nodeSize = graph->nodeSize_log_nodeSize; + exit_log_exit = graph->exit_log_exit; + size_log_size = graph->size_log_size; + exitFlow = graph->exitFlow; + + const std::vector &node = graph->node; + for (igraph_integer_t 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 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; + const std::vector &node = graph->node; + + RNG_BEGIN(); + + // Generate random enumeration of nodes + vector randomOrder(Nnode); + for (igraph_integer_t i = 0; i < Nnode; i++) { + randomOrder[i] = i; + } + + for (igraph_integer_t i = 0; i < Nnode - 1; i++) { + igraph_integer_t randPos = RNG_INTEGER(i, Nnode - 1); + // swap i & randPos + igraph_integer_t tmp = randomOrder[i]; + randomOrder[i] = randomOrder[randPos]; + randomOrder[randPos] = tmp; + } + + igraph_uint_t offset = 1; + vector redirect(Nnode, 0); + vector > > flowNtoM(Nnode); + + for (igraph_integer_t k = 0; k < Nnode; k++) { + + // Pick nodes in random order + igraph_integer_t flip = randomOrder[k]; + igraph_integer_t oldM = node_index[flip]; + + // Reset offset when igraph_integer_t overflows + if (offset > IGRAPH_INTEGER_MAX) { + for (igraph_integer_t j = 0; j < Nnode; j++) { + redirect[j] = 0; + } + offset = 1; + } + // Size of vector with module links + igraph_integer_t NmodLinks = 0; + // For all outLinks + size_t 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 (size_t j = 0; j < NoutLinks; j++) { + igraph_integer_t 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 + size_t NinLinks = node[flip].inLinks.size(); + for (size_t j = 0; j < NinLinks; j++) { + igraph_integer_t 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 (igraph_integer_t j = 0; j < NmodLinks; j++) { + igraph_integer_t 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] > 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 (igraph_integer_t j = 0; j < NmodLinks - 1; j++) { + igraph_integer_t randPos = RNG_INTEGER(j, NmodLinks - 1); + igraph_integer_t 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; + } + + igraph_integer_t 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 (igraph_integer_t j = 0; j < NmodLinks; j++) { + + igraph_integer_t 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] == 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) { + + //old fct prepare(sort) + vector modSnode; // will give IDs of no-empty modules (nodes) + modSnode.reserve(Nnode); + + igraph_integer_t Nmod = 0; + for (igraph_integer_t i = 0; i < Nnode; i++) { + if (mod_members[i] > 0) { + Nmod++; + modSnode.push_back(i); + } + } + + if (sort) { + // sort by mod_size + std::sort(modSnode.begin(), modSnode.end(), + [&](size_t a, size_t b) { return mod_size[a] > mod_size[b]; } ); + } + + // Create the new graph + FlowGraph tmp_fgraph(Nmod); + vector &node_tmp = tmp_fgraph.node ; + + const vector &node = graph->node; + + vector nodeInMod(Nnode); + + // creation of new nodes + for (igraph_integer_t i = 0; i < Nmod; i++) { + node_tmp[i].members.clear(); // 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; + } + + // Calculate outflow of links to different modules + vector > outFlowNtoM(Nmod); + + for (igraph_integer_t i = 0; i < Nnode; i++) { + igraph_integer_t 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 ) ); + + for (const auto &link : node[i].outLinks) { + igraph_integer_t nb = link.first; + igraph_integer_t nb_M = nodeInMod[node_index[nb]]; + double nb_flow = link.second; + if (nb != i) { + // inserts key nb_M if it does not exist + outFlowNtoM[i_M][nb_M] += nb_flow; + } + } + } + + // Create outLinks at new level + for (igraph_integer_t i = 0; i < Nmod; i++) { + for (const auto &item : outFlowNtoM[i]) { + if (item.first != i) { + node_tmp[i].outLinks.emplace_back(item); + } + } + } + + // Calculate inflow of links from different modules + vector > inFlowNtoM(Nmod); + + for (igraph_integer_t i = 0; i < Nnode; i++) { + igraph_integer_t i_M = nodeInMod[node_index[i]]; + for (const auto &inLink : node[i].inLinks) { + igraph_integer_t nb = inLink.first; + igraph_integer_t nb_M = nodeInMod[node_index[nb]]; + double nb_flow = inLink.second; + if (nb != i) { + // inserts key nb_M if it does not exist + inFlowNtoM[i_M][nb_M] += nb_flow; + } + } + } + + // Create inLinks at new level + for (igraph_integer_t i = 0; i < Nmod; i++) { + for (const auto &item : inFlowNtoM[i]) { + if (item.first != i) { + node_tmp[i].inLinks.emplace_back(item); + } + } + } + + // Option to move to empty module + mod_empty.clear(); + Nempty = 0; + + //swap node between tmp_graph and graph, then destroy tmp_fgraph + graph->swap(tmp_fgraph); + Nnode = Nmod; +} + + +/** + * 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 + */ + + +/* Compute the new CodeSize if modules are merged as indicated by moveTo + */ +void Greedy::setMove(const std::vector &moveTo) { + const std::vector &node = graph->node; + for (igraph_integer_t i = 0 ; i < Nnode ; i++) { // pour chaque module + igraph_integer_t oldM = i; + igraph_integer_t 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 + for (const auto &outLink : node[i].outLinks) { + igraph_integer_t nb_M = node_index[outLink.first]; + double nb_flow = outLink.second; + if (nb_M == oldM) { + outFlowOldM += nb_flow; + } else if (nb_M == newM) { + outFlowNewM += nb_flow; + } + } + + // For all inLinks + for (const auto &inLink : node[i].inLinks) { + igraph_integer_t nb_M = node_index[inLink.first]; + double nb_flow = inLink.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] == 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/community/infomap/infomap_Greedy.h b/src/community/infomap/infomap_Greedy.h new file mode 100644 index 0000000..87bf0b5 --- /dev/null +++ b/src/community/infomap/infomap_Greedy.h @@ -0,0 +1,74 @@ +/* -*- 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 INFOMAP_GREEDY_H +#define INFOMAP_GREEDY_H + +#include "infomap_Node.h" +#include "infomap_FlowGraph.h" + +#include "igraph_random.h" + +#include + +class Greedy { +public: + explicit Greedy(FlowGraph *fgraph); + // initialise les attributs par rapport au graph + + void setMove(const std::vector &moveTo); + bool optimize(); + void apply(bool sort); + + /**************************************************************************/ + +public: + double codeLength; + +private: + FlowGraph * graph; + igraph_integer_t Nnode; + + double exit; + double exitFlow; + double exit_log_exit; + double size_log_size; + double nodeSize_log_nodeSize; + + double alpha, beta; + // local copy of fgraph alpha, beta (=alpha - Nnode = graph->Nnode;1) + + std::vector node_index; // module number of each node + + igraph_integer_t Nempty = 0; + 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; +}; + +#endif // INFOMAP_GREEDY_H diff --git a/src/community/infomap/infomap_Node.h b/src/community/infomap/infomap_Node.h new file mode 100644 index 0000000..c3ef8c6 --- /dev/null +++ b/src/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 INFOMAP_NODE_H +#define INFOMAP_NODE_H + +#include "igraph_interface.h" + +#include + +struct Node { + + Node() = default; + Node(igraph_integer_t modulenr, double tpweight) : + teleportWeight(tpweight) + { + members.push_back(modulenr); // members = [nodenr] + } + + std::vector members; + std::vector< std::pair > inLinks; + std::vector< std::pair > outLinks; + double selfLink = 0.0; + + double teleportWeight = 0.0; + double danglingSize = 0.0; + double exit = 0.0; + double size = 0.0; +}; + +#endif // INFOMAP_NODE_H diff --git a/src/community/label_propagation.c b/src/community/label_propagation.c new file mode 100644 index 0000000..adaf2ba --- /dev/null +++ b/src/community/label_propagation.c @@ -0,0 +1,464 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2007-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, see . +*/ + +#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 "core/interruption.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. + * + * + * For directed graphs, it is important to know that labels can circulate + * freely only within the strongly connected components of the graph and + * may propagate in only one direction (or not at all) \em between strongly + * connected components. You should treat directed edges as directed only + * if you are aware of the consequences. + * + * + * References: + * + * + * 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 + * + * + * Å ubelj, L.: Label propagation for clustering. Chapter in "Advances in + * Network Clustering and Blockmodeling" edited by P. Doreian, V. Batagelj + * & A. Ferligoj (Wiley, New York, 2018). + * https://doi.org/10.1002/9781119483298.ch5 + * https://arxiv.org/abs/1709.05634 + * + * \param graph The input graph. Note that the algorithm wsa originally + * defined for undirected graphs. You are advised to set \p mode to + * \c IGRAPH_ALL if you pass a directed graph here to treat it as + * undirected. + * \param membership The membership vector, the result is returned here. + * For each vertex it gives the ID of its community (label). + * \param mode Whether to consider edge directions for the label propagation, + * and if so, which direction the labels should propagate. Ignored for + * undirected graphs. \c IGRAPH_ALL means to ignore edge directions (even + * in directed graphs). \c IGRAPH_OUT means to propagate labels along the + * natural direction of the edges. \c IGRAPH_IN means to propagate labels + * \em backwards (i.e. from head to tail). It is advised to set this to + * \c IGRAPH_ALL unless you are specifically interested in the effect of + * edge directions. + * \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. Note that vertices without labels + * cannot be fixed. The fixed status will be ignored for these with a + * warning. Also note that label numbers by themselves have no meaning, + * and igraph may renumber labels. However, co-membership constraints + * will be respected: two vertices can be fixed to be in the same or in + * different communities. + * \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. Note that igraph will calculate the + * \em directed modularity if the input graph is directed, even if + * you set \p mode to \c IGRAPH_ALL + * \return Error code. + * + * Time complexity: O(m+n) + * + * \example examples/simple/igraph_community_label_propagation.c + */ +igraph_error_t igraph_community_label_propagation(const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_neimode_t mode, + const igraph_vector_t *weights, + const igraph_vector_int_t *initial, + const igraph_vector_bool_t *fixed) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_not_fixed_nodes = no_of_nodes; + igraph_integer_t i, j, k; + igraph_adjlist_t al; + igraph_inclist_t il; + igraph_bool_t running, control_iteration; + igraph_bool_t unlabelled_left; + igraph_neimode_t reversed_mode; + int iter = 0; /* interruption counter */ + + igraph_vector_t label_counters; /* real type, stores weight sums */ + igraph_vector_int_t 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 (isnan(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_int_resize(membership, no_of_nodes)); + + if (initial) { + if (igraph_vector_int_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] = 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); /* LCOV_EXCL_LINE */ + } + + IGRAPH_FINALLY(igraph_free, fixed_copy); + IGRAPH_CHECK(igraph_vector_bool_init_copy(fixed_copy, fixed)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, fixed_copy); + } + + VECTOR(*fixed_copy)[i] = false; + } else { + no_of_not_fixed_nodes--; + } + } + } + } + + i = igraph_vector_int_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; + } + } + + reversed_mode = IGRAPH_REVERSE_MODE(mode); + + /* 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, reversed_mode, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + } else { + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, reversed_mode, 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_INT_INIT_FINALLY(&dominant_labels, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nonzero_labels, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&dominant_labels, 2)); + + /* Initialize node ordering vector with only the not fixed nodes */ + if (fixed_copy) { + IGRAPH_VECTOR_INT_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_int_init_range(&node_order, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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 = true; + running = true; + while (running) { + igraph_integer_t v1, num_neis; + igraph_real_t max_count; + igraph_vector_int_t *neis; + igraph_vector_int_t *ineis; + igraph_bool_t was_zero; + + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); + + if (control_iteration) { + /* If we are in the control iteration, we expect in the beginning of + the iteration that 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 = false; + } else { + /* Shuffle the node ordering vector if we are in the label updating iteration */ + IGRAPH_CHECK(igraph_vector_int_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 = VECTOR(node_order)[i]; + + /* Count the weights corresponding to different labels */ + igraph_vector_int_clear(&dominant_labels); + igraph_vector_int_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 = VECTOR(*membership)[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)[VECTOR(*ineis)[j]]; + if (was_zero && VECTOR(label_counters)[k] != 0) { + /* counter just became nonzero */ + IGRAPH_CHECK(igraph_vector_int_push_back(&nonzero_labels, k)); + } + if (max_count < VECTOR(label_counters)[k]) { + max_count = VECTOR(label_counters)[k]; + IGRAPH_CHECK(igraph_vector_int_resize(&dominant_labels, 1)); + VECTOR(dominant_labels)[0] = k; + } else if (max_count == VECTOR(label_counters)[k]) { + IGRAPH_CHECK(igraph_vector_int_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 = VECTOR(*membership)[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_int_push_back(&nonzero_labels, k)); + } + if (max_count < VECTOR(label_counters)[k]) { + max_count = VECTOR(label_counters)[k]; + IGRAPH_CHECK(igraph_vector_int_resize(&dominant_labels, 1)); + VECTOR(dominant_labels)[0] = k; + } else if (max_count == VECTOR(label_counters)[k]) { + IGRAPH_CHECK(igraph_vector_int_push_back(&dominant_labels, k)); + } + } + } + + if (igraph_vector_int_size(&dominant_labels) > 0) { + if (control_iteration) { + /* Check if the _current_ label of the node is also dominant */ + if (VECTOR(label_counters)[VECTOR(*membership)[v1]] != max_count) { + /* Nope, we need at least one more iteration */ + running = true; + } + } + else { + /* Select randomly from the dominant labels */ + k = RNG_INTEGER(0, igraph_vector_int_size(&dominant_labels) - 1); + VECTOR(*membership)[v1] = VECTOR(dominant_labels)[k]; + } + } + + /* Clear the nonzero elements in label_counters */ + num_neis = igraph_vector_int_size(&nonzero_labels); + for (j = 0; j < num_neis; j++) { + VECTOR(label_counters)[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 :) and use it as an integer vector from now on */ + igraph_vector_fill(&label_counters, -1); + j = 0; + unlabelled_left = false; + for (i = 0; i < no_of_nodes; i++) { + k = 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 = (igraph_integer_t) VECTOR(label_counters)[k]; + } + } else { + /* This is an unlabeled vertex */ + unlabelled_left = true; + } + 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_int_t q; + igraph_vector_int_t neis; + + /* In the directed case, the outcome depends on the node ordering, thus we + * shuffle nodes one more time. */ + IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + + for (i=0; i < no_of_not_fixed_nodes; ++i) { + igraph_integer_t 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_int_push(&q, v); + VECTOR(*membership)[v] = j; + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t ni, num_neis; + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + num_neis = igraph_vector_int_size(&neis); + + for (ni = 0; ni < num_neis; ++ni) { + igraph_integer_t neighbor = VECTOR(neis)[ni]; + if (IS_UNLABELLED(neighbor)) { + VECTOR(*membership)[neighbor] = j; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + } + } + } + j++; + } + } + + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&q); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_int_destroy(&node_order); + igraph_vector_destroy(&label_counters); + igraph_vector_int_destroy(&dominant_labels); + igraph_vector_int_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/community/leading_eigenvector.c b/src/community/leading_eigenvector.c new file mode 100644 index 0000000..91b88c4 --- /dev/null +++ b/src/community/leading_eigenvector.c @@ -0,0 +1,840 @@ +/* -*- 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_random.h" +#include "igraph_structural.h" + +#include "core/interruption.h" + +#include + +/** + * \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_int_t *idx; + igraph_vector_int_t *idx2; + igraph_adjlist_t *adjlist; + igraph_inclist_t *inclist; + igraph_vector_t *tmp; + igraph_integer_t no_of_edges; + igraph_vector_int_t *mymembership; + igraph_integer_t 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 igraph_error_t 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; + igraph_integer_t size = n; + igraph_vector_int_t *idx = data->idx; + igraph_vector_int_t *idx2 = data->idx2; + igraph_vector_t *tmp = data->tmp; + igraph_adjlist_t *adjlist = data->adjlist; + igraph_real_t ktx, ktx2; + igraph_integer_t no_of_edges = data->no_of_edges; + igraph_vector_int_t *mymembership = data->mymembership; + igraph_integer_t comm = data->comm; + + /* Ax */ + for (igraph_integer_t j = 0; j < size; j++) { + igraph_integer_t oldid = VECTOR(*idx)[j]; + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); + igraph_integer_t nlen = igraph_vector_int_size(neis); + to[j] = 0.0; + VECTOR(*tmp)[j] = 0.0; + for (igraph_integer_t k = 0; k < nlen; k++) { + igraph_integer_t nei = VECTOR(*neis)[k]; + igraph_integer_t neimemb = VECTOR(*mymembership)[nei]; + if (neimemb == comm) { + to[j] += from[ VECTOR(*idx2)[nei] ]; + VECTOR(*tmp)[j] += 1; + } + } + } + + /* Now calculate k^Tx/2m */ + ktx = 0.0; ktx2 = 0.0; + for (igraph_integer_t j = 0; j < size; j++) { + igraph_integer_t oldid = VECTOR(*idx)[j]; + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); + igraph_integer_t 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 (igraph_integer_t j = 0; j < size; j++) { + igraph_integer_t oldid = 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 (igraph_integer_t j = 0; j < size; j++) { + to[j] -= VECTOR(*tmp)[j] * from[j]; + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t size = n; + igraph_vector_int_t *idx = data->idx; + igraph_vector_int_t *idx2 = data->idx2; + igraph_vector_t *tmp = data->tmp; + igraph_inclist_t *inclist = data->inclist; + igraph_real_t ktx, ktx2; + igraph_vector_int_t *mymembership = data->mymembership; + igraph_integer_t 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 (igraph_integer_t j = 0; j < size; j++) { + igraph_integer_t oldid = VECTOR(*idx)[j]; + igraph_vector_int_t *inc = igraph_inclist_get(inclist, oldid); + igraph_integer_t nlen = igraph_vector_int_size(inc); + to[j] = 0.0; + VECTOR(*tmp)[j] = 0.0; + for (igraph_integer_t k = 0; k < nlen; k++) { + igraph_integer_t edge = VECTOR(*inc)[k]; + igraph_real_t w = VECTOR(*weights)[edge]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, oldid); + igraph_integer_t neimemb = VECTOR(*mymembership)[nei]; + if (neimemb == comm) { + to[j] += from[ VECTOR(*idx2)[nei] ] * w; + VECTOR(*tmp)[j] += w; + } + } + } + + /* k^Tx/2m */ + ktx = 0.0; ktx2 = 0.0; + for (igraph_integer_t j = 0; j < size; j++) { + igraph_integer_t oldid = 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 (igraph_integer_t j = 0; j < size; j++) { + igraph_integer_t oldid = 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 (igraph_integer_t j = 0; j < size; j++) { + to[j] -= VECTOR(*tmp)[j] * from[j]; + } + + return IGRAPH_SUCCESS; +} + +static void igraph_i_error_handler_none(const char *reason, const char *file, + int line, igraph_error_t 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). + * https://doi.org/10.1103/PhysRevE.74.036104 + * + * \param graph The input graph. Edge directions will be ignored. + * \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. Unlike with most other hierarchicaly + * community detection functions in igraph, the integers in this matrix + * represent community indices, not vertex indices. 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 if 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. Supply \c NULL here to use the + * defaults. \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 list of vectors. Each eigenvector is stored in an + * \ref igraph_vector_t object. + * \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 \c IGRAPH_STOP, then the + * community finding algorithm stops. If it returns \c IGRAPH_SUCCESS, + * the algorithm continues normally. Any other return value is considered + * an igraph error code and will terminete the algorithm with the same + * error code. 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. + */ +igraph_error_t igraph_community_leading_eigenvector( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_matrix_int_t *merges, + igraph_vector_int_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_list_t *eigenvectors, + igraph_vector_t *history, + igraph_community_leading_eigenvector_callback_t *callback, + void *callback_extra) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_dqueue_int_t tosplit; + igraph_vector_int_t idx, idx2; + igraph_vector_t mymerges; + igraph_vector_t strength, tmp; + igraph_vector_t start_vec; + igraph_integer_t staken = 0; + igraph_adjlist_t adjlist; + igraph_inclist_t inclist; + igraph_integer_t i, j, k, l; + igraph_integer_t communities; + igraph_vector_int_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_real_t sumweights = 0.0; + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph too large for ARPACK.", IGRAPH_EOVERFLOW); + } + + if (weights && no_of_edges != igraph_vector_size(weights)) { + IGRAPH_ERROR("Weight vector length does not match number of edges.", IGRAPH_EINVAL); + } + + if (start && !membership) { + IGRAPH_ERROR("Cannot start from given configuration if memberships missing.", IGRAPH_EINVAL); + } + + if (start && membership && + igraph_vector_int_size(membership) != no_of_nodes) { + IGRAPH_ERROR("Supplied memberhsip vector length does not match number of vertices.", + IGRAPH_EINVAL); + } + + if (start && membership && igraph_vector_int_max(membership) >= no_of_nodes) { + IGRAPH_WARNING("Too many communities in membership start vector."); + } + + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Directed graph supplied, edge directions will be ignored."); + } + + if (steps < 0 || steps > no_of_nodes - 1) { + steps = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + } + + if (!membership) { + mymembership = &vmembership; + IGRAPH_VECTOR_INT_INIT_FINALLY(mymembership, 0); + } + + IGRAPH_VECTOR_INIT_FINALLY(&mymerges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&mymerges, steps * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, 0); + if (eigenvalues) { + igraph_vector_clear(eigenvalues); + } + if (eigenvectors) { + igraph_vector_list_clear(eigenvectors); + } + + if (!start) { + /* Calculate the weakly connected components in the graph and use them as + * an initial split */ + IGRAPH_CHECK(igraph_connected_components(graph, mymembership, &idx, 0, IGRAPH_WEAK)); + communities = igraph_vector_int_size(&idx); + 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 = igraph_vector_int_max(mymembership) + 1; + 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_int_resize(&idx, communities)); + igraph_vector_int_null(&idx); + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t t = VECTOR(*mymembership)[i]; + VECTOR(idx)[t] += 1; + } + } + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&tosplit, 100); + for (i = 0; i < communities; i++) { + if (VECTOR(idx)[i] > 2) { + IGRAPH_CHECK(igraph_dqueue_int_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) { + /* There are no eigenvectors associated to these steps because the + * splits were given by the user (or by the components of the graph) + * so we push empty vectors */ + IGRAPH_CHECK(igraph_vector_list_push_back_new(eigenvectors, NULL)); + } + 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_int_resize(&idx, no_of_nodes)); + igraph_vector_int_null(&idx); + IGRAPH_VECTOR_INT_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); + } + + if (options == NULL) { + options = igraph_arpack_options_get_default(); + } + + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ + 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_int_empty(&tosplit) && staken < steps) { + igraph_integer_t comm = igraph_dqueue_int_pop_back(&tosplit); + /* depth first search */ + igraph_integer_t size = 0; + + 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; + } + + options->n = (int) size; + 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; + + /* Use a random start vector, but don't let ARPACK generate the + * start vector -- we want to use our own RNG. Also, we want to generate + * values close to +1 and -1 as this is what the eigenvector should + * look like if there _is_ some kind of a community structure at this + * step to discover. Experiments showed that shuffling a vector + * containing equal number of slightly perturbed +/-1 values yields + * convergence in most cases. */ + options->start = 1; + options->mxiter = options->mxiter > 10000 ? options->mxiter : 10000; /* use more iterations, we've had convergence problems with 3000 */ + RNG_BEGIN(); + for (i = 0; i < options->n; i++) { + storage.resid[i] = (i % 2 ? 1 : -1) + RNG_UNIF(-0.1, 0.1); + } + RNG_END(); + igraph_vector_view(&start_vec, storage.resid, options->n); + IGRAPH_CHECK(igraph_vector_shuffle(&start_vec)); + + { + igraph_error_t retval; + igraph_error_handler_t *errh = + igraph_set_error_handler(igraph_i_error_handler_none); + retval = igraph_arpack_rssolve(arpcb1, &extra, options, &storage, /*values=*/ 0, /*vectors=*/ 0); + igraph_set_error_handler(errh); + if (retval != IGRAPH_SUCCESS && retval != IGRAPH_ARPACK_MAXIT && retval != IGRAPH_ARPACK_NOSHIFT) { + IGRAPH_ERROR("ARPACK call failed", retval); + } + } + + 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; + igraph_error_t ret; + + igraph_vector_view(&vv, storage.v, size); + IGRAPH_CHECK_CALLBACK( + callback( + mymembership, comm, storage.d[0], &vv, arpcb1, + &extra, callback_extra + ), &ret + ); + + if (ret == IGRAPH_STOP) { + break; + } + } + + if (eigenvalues) { + IGRAPH_CHECK(igraph_vector_push_back(eigenvalues, storage.d[0])); + } + + if (eigenvectors) { + igraph_vector_t *v; + /* TODO: this would be faster if we had an igraph_vector_list_push_back_new_with_size_hint */ + IGRAPH_CHECK(igraph_vector_list_push_back_new(eigenvectors, &v)); + IGRAPH_CHECK(igraph_vector_resize(v, size)); + for (i = 0; i < size; i++) { + VECTOR(*v)[i] = storage.v[i]; + } + } + + if (storage.d[0] <= 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) { + 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) { + if (history) { + IGRAPH_CHECK(igraph_vector_push_back(history, + IGRAPH_LEVC_HIST_FAILED)); + IGRAPH_CHECK(igraph_vector_push_back(history, comm)); + } + continue; + } + + communities++; + + /* Rewrite the mymembership vector */ + for (j = 0; j < size; j++) { + if (storage.v[j] < 0) { + igraph_integer_t oldid = 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_int_push(&tosplit, communities - 1)); + } + if (size - l > 1) { + IGRAPH_CHECK(igraph_dqueue_int_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_int_destroy(&tosplit); + igraph_vector_destroy(&tmp); + igraph_vector_int_destroy(&idx2); + IGRAPH_FINALLY_CLEAN(3); + + /* reform the mymerges vector */ + if (merges) { + igraph_vector_int_null(&idx); + l = igraph_vector_size(&mymerges); + k = communities; + j = 0; + IGRAPH_CHECK(igraph_matrix_int_resize(merges, l / 2, 2)); + for (i = l; i > 0; i -= 2) { + igraph_integer_t from = VECTOR(mymerges)[i - 1]; + igraph_integer_t to = 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++; + } + } + + igraph_vector_int_destroy(&idx); + igraph_vector_destroy(&mymerges); + IGRAPH_FINALLY_CLEAN(2); + + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, mymembership, weights, + /* resolution */ 1, + IGRAPH_UNDIRECTED, modularity)); + } + + if (!membership) { + igraph_vector_int_destroy(mymembership); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_le_community_to_membership + * \brief 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_leading_eigenvector() 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 are stored here, + * if this is not a null pointer, but an initialized vector. + * \return Error code. + * + * Time complexity: O(|V|), the number of vertices. + */ +igraph_error_t igraph_le_community_to_membership(const igraph_matrix_int_t *merges, + igraph_integer_t steps, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize) { + + igraph_integer_t no_of_nodes = igraph_vector_int_size(membership); + igraph_vector_int_t fake_memb; + igraph_integer_t components, i; + + if (no_of_nodes > 0) { + components = igraph_vector_int_max(membership) + 1; + } else { + components = 0; + } + if (components > no_of_nodes) { + IGRAPH_ERRORF("Invalid membership vector: number of components (%" IGRAPH_PRId ") must " + "not be greater than the number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, components, no_of_nodes); + } + if (steps >= components) { + IGRAPH_ERRORF("Number of steps (%" IGRAPH_PRId ") must be smaller than number of components (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, steps, components); + } + + IGRAPH_VECTOR_INT_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: %" IGRAPH_PRId ".", IGRAPH_EINVAL, VECTOR(*membership)[i]); + } + VECTOR(fake_memb)[ 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, 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_int_resize(csize, components - steps)); + igraph_vector_int_null(csize); + } + + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*membership)[i] = VECTOR(fake_memb)[ VECTOR(*membership)[i] ]; + if (csize) { + VECTOR(*csize)[ VECTOR(*membership)[i] ] += 1; + } + } + + igraph_vector_int_destroy(&fake_memb); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/community/leiden.c b/src/community/leiden.c new file mode 100644 index 0000000..693e49f --- /dev/null +++ b/src/community/leiden.c @@ -0,0 +1,1042 @@ +/* -*- 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_bitset.h" +#include "igraph_constructors.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_vector_list.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. + * Only moves that strictly improve the quality are considered. + * + * 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 igraph_error_t 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_int_t *membership, + igraph_bool_t *changed) { + + igraph_dqueue_int_t unstable_nodes; + igraph_real_t max_diff = 0.0, diff = 0.0; + const igraph_integer_t n = igraph_vcount(graph); + igraph_bitset_t neighbor_cluster_added, node_is_stable; + igraph_vector_t cluster_weights, edge_weights_per_cluster; + igraph_vector_int_t neighbor_clusters; + igraph_vector_int_t node_order; + igraph_vector_int_t nb_nodes_per_cluster; + igraph_stack_int_t empty_clusters; + igraph_integer_t c, nb_neigh_clusters; + int iter = 0; + + /* Initialize queue of unstable nodes and whether node is stable. Only + * unstable nodes are in the queue. */ + IGRAPH_BITSET_INIT_FINALLY(&node_is_stable, n); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&unstable_nodes, n); + + /* Shuffle nodes */ + IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); + + /* Add to the queue */ + for (igraph_integer_t i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_dqueue_int_push(&unstable_nodes, VECTOR(node_order)[i])); + } + + /* Initialize cluster weights and nb nodes */ + IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nb_nodes_per_cluster, n); + for (igraph_integer_t i = 0; i < n; i++) { + c = VECTOR(*membership)[i]; + VECTOR(cluster_weights)[c] += VECTOR(*node_weights)[i]; + VECTOR(nb_nodes_per_cluster)[c] += 1; + } + + /* Initialize empty clusters */ + IGRAPH_STACK_INT_INIT_FINALLY(&empty_clusters, n); + for (c = 0; c < n; c++) + if (VECTOR(nb_nodes_per_cluster)[c] == 0) { + IGRAPH_CHECK(igraph_stack_int_push(&empty_clusters, c)); + } + + /* Initialize vectors to be used in calculating differences */ + IGRAPH_VECTOR_INIT_FINALLY(&edge_weights_per_cluster, n); + + /* Initialize neighboring cluster */ + IGRAPH_BITSET_INIT_FINALLY(&neighbor_cluster_added, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, n); + + /* Iterate while the queue is not empty */ + while (!igraph_dqueue_int_empty(&unstable_nodes)) { + igraph_integer_t v = igraph_dqueue_int_pop(&unstable_nodes); + igraph_integer_t best_cluster, current_cluster = VECTOR(*membership)[v]; + igraph_integer_t degree; + 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_int_push(&empty_clusters, current_cluster)); + } + + /* Find out neighboring clusters */ + c = igraph_stack_int_top(&empty_clusters); + VECTOR(neighbor_clusters)[0] = c; + IGRAPH_BIT_SET(neighbor_cluster_added, c); + 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 (igraph_integer_t i = 0; i < degree; i++) { + igraph_integer_t e = VECTOR(*edges)[i]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + if (u != v) { + c = VECTOR(*membership)[u]; + if (!IGRAPH_BIT_TEST(neighbor_cluster_added, c)) { + IGRAPH_BIT_SET(neighbor_cluster_added, c); + 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 (igraph_integer_t 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; + /* Only consider strictly improving moves. + * Note that this is important in considering convergence. + */ + if (diff > max_diff) { + best_cluster = c; + max_diff = diff; + } + VECTOR(edge_weights_per_cluster)[c] = 0.0; + IGRAPH_BIT_CLEAR(neighbor_cluster_added, c); + } + + /* 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_int_top(&empty_clusters)) { + igraph_stack_int_pop(&empty_clusters); + } + + /* Mark node as stable */ + IGRAPH_BIT_SET(node_is_stable, v); + + /* Add stable neighbours that are not part of the new cluster to the queue */ + if (best_cluster != current_cluster) { + *changed = true; + VECTOR(*membership)[v] = best_cluster; + + for (igraph_integer_t i = 0; i < degree; i++) { + igraph_integer_t e = VECTOR(*edges)[i]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + if (IGRAPH_BIT_TEST(node_is_stable, u) && VECTOR(*membership)[u] != best_cluster) { + IGRAPH_CHECK(igraph_dqueue_int_push(&unstable_nodes, u)); + IGRAPH_BIT_CLEAR(node_is_stable, u); + } + } + } + + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + + IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, nb_clusters)); + + igraph_vector_int_destroy(&neighbor_clusters); + igraph_bitset_destroy(&neighbor_cluster_added); + igraph_vector_destroy(&edge_weights_per_cluster); + igraph_stack_int_destroy(&empty_clusters); + igraph_vector_int_destroy(&nb_nodes_per_cluster); + igraph_vector_destroy(&cluster_weights); + igraph_vector_int_destroy(&node_order); + igraph_dqueue_int_destroy(&unstable_nodes); + igraph_bitset_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 igraph_error_t igraph_i_community_leiden_clean_refined_membership( + const igraph_vector_int_t* node_subset, + igraph_vector_int_t *refined_membership, + igraph_integer_t* nb_refined_clusters) { + const igraph_integer_t n = igraph_vector_int_size(node_subset); + igraph_vector_int_t new_cluster; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_cluster, n); + + /* 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 (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t v = VECTOR(*node_subset)[i]; + igraph_integer_t c = VECTOR(*refined_membership)[v]; + if (VECTOR(new_cluster)[c] == 0) { + VECTOR(new_cluster)[c] = *nb_refined_clusters; + *nb_refined_clusters += 1; + } + } + + /* Assign new cluster */ + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t v = VECTOR(*node_subset)[i]; + igraph_integer_t c = 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_int_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 igraph_error_t 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_int_t *node_subset, + const igraph_vector_int_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_int_t *refined_membership) { + igraph_vector_int_t node_order; + igraph_bitset_t non_singleton_cluster, neighbor_cluster_added; + igraph_real_t max_diff, total_cum_trans_diff, diff = 0.0, total_node_weight = 0.0; + const igraph_integer_t n = igraph_vector_int_size(node_subset); + igraph_vector_t cluster_weights, cum_trans_diff, edge_weights_per_cluster, external_edge_weight_per_cluster_in_subset; + igraph_vector_int_t neighbor_clusters; + igraph_vector_int_t *edges, nb_nodes_per_cluster; + igraph_integer_t degree, nb_neigh_clusters; + + /* Initialize cluster weights */ + IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); + + /* Initialize number of nodes per cluster */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&nb_nodes_per_cluster, n); + + /* Initialize external edge weight per cluster in subset */ + IGRAPH_VECTOR_INIT_FINALLY(&external_edge_weight_per_cluster_in_subset, n); + + /* Initialize administration for a singleton partition */ + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t v = 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 (igraph_integer_t j = 0; j < degree; j++) { + igraph_integer_t e = VECTOR(*edges)[j]; + igraph_integer_t u = 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_int_init_copy(&node_order, node_subset)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); + + /* Initialize non singleton clusters */ + IGRAPH_BITSET_INIT_FINALLY(&non_singleton_cluster, n); + + /* Initialize vectors to be used in calculating differences */ + IGRAPH_VECTOR_INIT_FINALLY(&edge_weights_per_cluster, n); + + /* Initialize neighboring cluster */ + IGRAPH_BITSET_INIT_FINALLY(&neighbor_cluster_added, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, n); + + /* Initialize cumulative transformed difference */ + IGRAPH_VECTOR_INIT_FINALLY(&cum_trans_diff, n); + + RNG_BEGIN(); + + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t v = VECTOR(node_order)[i]; + igraph_integer_t chosen_cluster, best_cluster, current_cluster = VECTOR(*refined_membership)[v]; + + if (!IGRAPH_BIT_TEST(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; + IGRAPH_BIT_SET(neighbor_cluster_added, current_cluster); + nb_neigh_clusters = 1; + for (igraph_integer_t j = 0; j < degree; j++) { + igraph_integer_t e = VECTOR(*edges)[j]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + if (u != v && VECTOR(*membership)[u] == cluster_subset) { + igraph_integer_t c = VECTOR(*refined_membership)[u]; + if (!IGRAPH_BIT_TEST(neighbor_cluster_added, c)) { + IGRAPH_BIT_SET(neighbor_cluster_added, c); + 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 (igraph_integer_t j = 0; j < nb_neigh_clusters; j++) { + igraph_integer_t c = 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; + IGRAPH_BIT_CLEAR(neighbor_cluster_added, c); + } + + /* 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); + igraph_integer_t 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 (igraph_integer_t j = 0; j < degree; j++) { + igraph_integer_t e = VECTOR(*edges)[j]; + igraph_integer_t u = 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; + + IGRAPH_BIT_SET(non_singleton_cluster, chosen_cluster); + } + } /* 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_int_destroy(&neighbor_clusters); + igraph_bitset_destroy(&neighbor_cluster_added); + igraph_vector_destroy(&edge_weights_per_cluster); + igraph_bitset_destroy(&non_singleton_cluster); + igraph_vector_int_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. + * + * It is assumed that the incoming list of integer vectors is already sized + * appropriately (i.e. it has at least as many items as the number of clusters + * in the membership vector), and that each item in the list of integer vectors + * is empty. + */ +static igraph_error_t igraph_i_community_get_clusters(const igraph_vector_int_t *membership, igraph_vector_int_list_t *clusters) { + igraph_integer_t n = igraph_vector_int_size(membership); + igraph_vector_int_t *cluster; + + for (igraph_integer_t i = 0; i < n; i++) { + /* Get cluster for node i */ + cluster = igraph_vector_int_list_get_ptr(clusters, VECTOR(*membership)[i]); + + /* Add node i to cluster vector */ + IGRAPH_CHECK(igraph_vector_int_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 igraph_error_t 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_int_t *membership, const igraph_vector_int_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_int_t *aggregated_membership) { + igraph_vector_int_t aggregated_edges; + igraph_vector_t edge_weight_to_cluster; + igraph_vector_int_list_t refined_clusters; + igraph_vector_int_t *incident_edges; + igraph_vector_int_t neighbor_clusters; + igraph_bitset_t neighbor_cluster_added; + igraph_integer_t c, degree, nb_neigh_clusters; + + /* Get refined clusters */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&refined_clusters, nb_refined_clusters); + IGRAPH_CHECK(igraph_i_community_get_clusters(refined_membership, &refined_clusters)); + + /* Initialize new edges */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&aggregated_edges, 0); + + /* 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_int_resize(aggregated_membership, nb_refined_clusters)); + + IGRAPH_VECTOR_INIT_FINALLY(&edge_weight_to_cluster, nb_refined_clusters); + + /* Initialize neighboring cluster */ + IGRAPH_BITSET_INIT_FINALLY(&neighbor_cluster_added, nb_refined_clusters); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, nb_refined_clusters); + + /* Check per cluster */ + for (c = 0; c < nb_refined_clusters; c++) { + igraph_vector_int_t* refined_cluster = igraph_vector_int_list_get_ptr(&refined_clusters, c); + igraph_integer_t n_c = igraph_vector_int_size(refined_cluster); + igraph_integer_t v = -1; + + /* Calculate the total edge weight to other clusters */ + VECTOR(*aggregated_node_weights)[c] = 0.0; + nb_neigh_clusters = 0; + for (igraph_integer_t i = 0; i < n_c; i++) { + v = VECTOR(*refined_cluster)[i]; + incident_edges = igraph_inclist_get(edges_per_node, v); + degree = igraph_vector_int_size(incident_edges); + + for (igraph_integer_t j = 0; j < degree; j++) { + igraph_integer_t e = VECTOR(*incident_edges)[j]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + igraph_integer_t c2 = VECTOR(*refined_membership)[u]; + + if (c2 > c) { + if (!IGRAPH_BIT_TEST(neighbor_cluster_added, c2)) { + IGRAPH_BIT_SET(neighbor_cluster_added, c2); + 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 (igraph_integer_t i = 0; i < nb_neigh_clusters; i++) { + igraph_integer_t c2 = VECTOR(neighbor_clusters)[i]; + + /* Add edge */ + IGRAPH_CHECK(igraph_vector_int_push_back(&aggregated_edges, c)); + IGRAPH_CHECK(igraph_vector_int_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; + IGRAPH_BIT_CLEAR(neighbor_cluster_added, c2); + } + + VECTOR(*aggregated_membership)[c] = VECTOR(*membership)[v]; + + } + + igraph_vector_int_destroy(&neighbor_clusters); + igraph_bitset_destroy(&neighbor_cluster_added); + igraph_vector_destroy(&edge_weight_to_cluster); + igraph_vector_int_list_destroy(&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_int_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 igraph_error_t igraph_i_community_leiden_quality( + const igraph_t *graph, const igraph_vector_t *edge_weights, const igraph_vector_t *node_weights, + const igraph_vector_int_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; + igraph_integer_t 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); + igraph_integer_t from = IGRAPH_FROM(graph, e), to = IGRAPH_TO(graph, e); + total_edge_weight += VECTOR(*edge_weights)[e]; + /* We add the internal edge weights */ + if (VECTOR(*membership)[from] == VECTOR(*membership)[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_VECTOR_INIT_FINALLY(&cluster_weights, n); + 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 igraph_error_t 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_int_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality, + igraph_bool_t *changed) { + igraph_integer_t nb_refined_clusters; + igraph_integer_t i, c, n = igraph_vcount(graph); + igraph_t aggregated_graph, *i_graph; + igraph_vector_t aggregated_edge_weights, aggregated_node_weights; + igraph_vector_int_t aggregated_membership; + igraph_vector_t *i_edge_weights, *i_node_weights; + igraph_vector_int_t *i_membership; + igraph_vector_t tmp_edge_weights, tmp_node_weights; + igraph_vector_int_t tmp_membership; + igraph_vector_int_t refined_membership; + igraph_vector_int_t aggregate_node; + igraph_vector_int_list_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_VECTOR_INIT_FINALLY(&tmp_edge_weights, 0); + IGRAPH_VECTOR_INIT_FINALLY(&tmp_node_weights, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp_membership, 0); + + /* Initialize clusters */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&clusters, n); + + /* Initialize aggregate nodes, which initially is identical to simply the + * nodes in the graph. */ + IGRAPH_CHECK(igraph_vector_int_init_range(&aggregate_node, 0, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &aggregate_node); + + /* Initialize refined membership */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&refined_membership, 0); + + /* Initialize aggregated graph */ + IGRAPH_CHECK(igraph_empty(&aggregated_graph, 0, IGRAPH_UNDIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &aggregated_graph); + + /* Initialize aggregated edge weights */ + IGRAPH_VECTOR_INIT_FINALLY(&aggregated_edge_weights, 0); + + /* Initialize aggregated node weights */ + IGRAPH_VECTOR_INIT_FINALLY(&aggregated_node_weights, 0); + + /* Initialize aggregated membership */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&aggregated_membership, 0); + + /* 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); + } + + /* We start out with no changes, whenever a node is moved, this will be set to true. */ + *changed = false; + 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, + changed)); + + /* 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++) { + igraph_integer_t 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_int_resize(&refined_membership, igraph_vcount(i_graph))); + + /* Refine each cluster */ + nb_refined_clusters = 0; + for (c = 0; c < *nb_clusters; c++) { + igraph_vector_int_t* cluster = igraph_vector_int_list_get_ptr(&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_int_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_CHECK(igraph_vector_int_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] = 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_int_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_int_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_int_destroy(&refined_membership); + igraph_vector_int_destroy(&aggregate_node); + igraph_vector_int_list_destroy(&clusters); + igraph_vector_int_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. Only + * moves that strictly improve the quality function are made. 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. Note that even if a single iteration did not result in any change, + * it is still possible that a subsequent iteration might find some + * improvement. Each iteration explores different subsets of nodes to consider + * for moving from one cluster to another. 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. If \c NULL, every node + * has equal weight of 1. + * \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 n_iterations Iterate the core Leiden algorithm for the indicated number + * of times. If this is a negative number, it will continue iterating until + * an iteration did not change the clustering. + * \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 \ref igraph_vector_int_init_range(). + * \param nb_clusters The number of clusters contained in \c membership. + * If \c NULL, the number of clusters will not be returned. + * \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 + */ +igraph_error_t 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, + const igraph_integer_t n_iterations, + igraph_vector_int_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) { + igraph_vector_t *i_edge_weights, *i_node_weights; + igraph_integer_t i_nb_clusters; + igraph_integer_t n = igraph_vcount(graph); + + if (!nb_clusters) { + nb_clusters = &i_nb_clusters; + } + + if (start) { + if (!membership) { + IGRAPH_ERROR("Cannot start optimization if membership is missing.", IGRAPH_EINVAL); + } + + if (igraph_vector_int_size(membership) != n) { + IGRAPH_ERROR("Initial membership length does not equal the number of vertices.", IGRAPH_EINVAL); + } + } else { + if (!membership) + IGRAPH_ERROR("Membership vector should be supplied and initialized, " + "even when not starting optimization from it.", IGRAPH_EINVAL); + + IGRAPH_CHECK(igraph_vector_int_range(membership, 0, n)); + } + + + 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); + IGRAPH_CHECK_OOM(i_edge_weights, "Leiden algorithm failed, could not allocate memory for edge weights."); + 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); + IGRAPH_CHECK_OOM(i_node_weights, "Leiden algorithm failed, could not allocate memory for node weights."); + 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 iteratively. We either + * perform a fixed number of iterations, or we perform + * iterations until the quality remains unchanged. Even if + * a single iteration did not change anything, a subsequent + * iteration may still find some improvement. This is because + * each iteration explores different subsets of nodes. + */ + igraph_bool_t changed = false; + for (igraph_integer_t itr = 0; + n_iterations >= 0 ? itr < n_iterations : !changed; + itr++) { + IGRAPH_CHECK(igraph_i_community_leiden(graph, i_edge_weights, i_node_weights, + resolution_parameter, beta, + membership, nb_clusters, quality, &changed)); + } + + 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/community/louvain.c b/src/community/louvain.c new file mode 100644 index 0000000..9f887f2 --- /dev/null +++ b/src/community/louvain.c @@ -0,0 +1,721 @@ +/* -*- 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_conversion.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_qsort.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 { + igraph_integer_t 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_int_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.0; + igraph_real_t m = communities->weight_sum; + + for (igraph_integer_t 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 { + igraph_integer_t from; + igraph_integer_t to; + igraph_integer_t id; +} igraph_i_multilevel_link; + +static int igraph_i_multilevel_link_cmp(const void *a, const void *b) { + igraph_integer_t diff; + + diff = ((igraph_i_multilevel_link*)a)->from - ((igraph_i_multilevel_link*)b)->from; + + if (diff < 0) { + return -1; + } else if (diff > 0) { + return 1; + } + + diff = ((igraph_i_multilevel_link*)a)->to - ((igraph_i_multilevel_link*)b)->to; + + if (diff < 0) { + return -1; + } else if (diff > 0) { + return 1; + } else { + return 0; + } +} + +/* removes multiple edges and returns new edge IDs for each edge in |E|log|E| */ +static igraph_error_t igraph_i_multilevel_simplify_multiple(igraph_t *graph, igraph_vector_int_t *eids) { + igraph_integer_t ecount = igraph_ecount(graph); + igraph_integer_t l = -1, last_from = -1, last_to = -1; + igraph_bool_t directed = igraph_is_directed(graph); + igraph_vector_int_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_int_resize(eids, ecount)); + + links = IGRAPH_CALLOC(ecount, igraph_i_multilevel_link); + IGRAPH_CHECK_OOM(links, "Multi-level community structure detection failed."); + IGRAPH_FINALLY(igraph_free, links); + + for (igraph_integer_t i = 0; i < ecount; i++) { + links[i].from = IGRAPH_FROM(graph, i); + links[i].to = IGRAPH_TO(graph, i); + links[i].id = i; + } + + igraph_qsort(links, (size_t) ecount, sizeof(igraph_i_multilevel_link), + igraph_i_multilevel_link_cmp); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + for (igraph_integer_t 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_int_push_back(&edges, last_from); + igraph_vector_int_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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +typedef struct { + igraph_integer_t community; + igraph_real_t weight; +} igraph_i_multilevel_community_link; + +static int igraph_i_multilevel_community_link_cmp(const void *a, const void *b) { + igraph_integer_t diff = ( + ((igraph_i_multilevel_community_link*)a)->community - + ((igraph_i_multilevel_community_link*)b)->community + ); + return diff < 0 ? -1 : diff > 0 ? 1 : 0; +} + +/** + * 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 igraph_error_t igraph_i_multilevel_community_links( + const igraph_t *graph, + const igraph_i_multilevel_community_list *communities, + igraph_integer_t vertex, igraph_vector_int_t *edges, + igraph_real_t *weight_all, igraph_real_t *weight_inside, igraph_real_t *weight_loop, + igraph_vector_int_t *links_community, igraph_vector_t *links_weight) { + + igraph_integer_t n, last = -1, c = -1; + igraph_real_t weight = 1; + igraph_integer_t to, to_community; + igraph_integer_t community = VECTOR(*(communities->membership))[vertex]; + igraph_i_multilevel_community_link *links; + + *weight_all = *weight_inside = *weight_loop = 0; + + igraph_vector_int_clear(links_community); + igraph_vector_clear(links_weight); + + /* Get the list of incident edges */ + IGRAPH_CHECK(igraph_incident(graph, edges, vertex, IGRAPH_ALL)); + + n = igraph_vector_int_size(edges); + links = IGRAPH_CALLOC(n, igraph_i_multilevel_community_link); + IGRAPH_CHECK_OOM(links, "Multi-level community structure detection failed."); + IGRAPH_FINALLY(igraph_free, links); + + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t eidx = 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 = 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 (igraph_integer_t i = 0; i < n; i++) { + to_community = links[i].community; + if (to_community != last) { + IGRAPH_CHECK(igraph_vector_int_push_back(links_community, to_community)); + IGRAPH_CHECK(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 IGRAPH_SUCCESS; +} + +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[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 igraph_error_t igraph_i_multilevel_shrink(igraph_t *graph, igraph_vector_int_t *membership) { + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + + IGRAPH_ASSERT(igraph_vector_int_size(membership) == no_of_nodes); + + if (no_of_nodes == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2*no_of_edges); + + IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, NULL)); + + /* Create the new edgelist */ + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, /* bycol= */ false)); + for (igraph_integer_t i=0; i < 2*no_of_edges; i++) { + VECTOR(edges)[i] = VECTOR(*membership)[ VECTOR(edges)[i] ]; + } + + /* Create the new graph */ + igraph_destroy(graph); + no_of_nodes = igraph_vector_int_max(membership) + 1; + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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 igraph_error_t igraph_i_community_multilevel_step( + igraph_t *graph, + igraph_vector_t *weights, + igraph_vector_int_t *membership, + igraph_real_t *modularity, + const igraph_real_t resolution) { + + igraph_integer_t vcount = igraph_vcount(graph); + igraph_integer_t ecount = igraph_ecount(graph); + igraph_real_t q, pass_q; + /* int pass; // used only for debugging */ + igraph_bool_t changed; + igraph_vector_int_t links_community; + igraph_vector_t links_weight; + igraph_vector_int_t edges; + igraph_vector_int_t temp_membership; + igraph_i_multilevel_community_list communities; + igraph_vector_int_t node_order; + + IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, vcount)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + igraph_vector_int_shuffle(&node_order); + + /* Initialize data structures */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&links_community, 0); + IGRAPH_VECTOR_INIT_FINALLY(&links_weight, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&temp_membership, vcount); + IGRAPH_CHECK(igraph_vector_int_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.0 * igraph_vector_sum(weights); + communities.membership = membership; + communities.item = IGRAPH_CALLOC(vcount, igraph_i_multilevel_community); + IGRAPH_CHECK_OOM(communities.item, "Multi-level community structure detection failed."); + IGRAPH_FINALLY(igraph_free, communities.item); + + /* Still initializing the communities data structure */ + for (igraph_integer_t 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 (igraph_integer_t i = 0; i < ecount; i++) { + igraph_integer_t ffrom = IGRAPH_FROM(graph, i), fto = IGRAPH_TO(graph, i); + igraph_real_t weight = 1; + + weight = VECTOR(*weights)[i]; + communities.item[ffrom].weight_all += weight; + communities.item[fto].weight_all += weight; + if (ffrom == fto) { + communities.item[ffrom].weight_inside += 2 * weight; + } + } + + q = igraph_i_multilevel_community_modularity(&communities, resolution); + /* pass = 1; */ + + do { /* Pass begin */ + igraph_integer_t temp_communities_no = communities.communities_no; + + pass_q = q; + changed = false; + + /* Save the current membership, it will be restored in case of worse result */ + IGRAPH_CHECK(igraph_vector_int_update(&temp_membership, communities.membership)); + + for (igraph_integer_t 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; + igraph_integer_t old_id, new_id, n, ni; + + ni = VECTOR(node_order)[i]; + + igraph_i_multilevel_community_links(graph, &communities, + ni, &edges, + &weight_all, &weight_inside, + &weight_loop, &links_community, + &links_weight); + + old_id = VECTOR(*(communities.membership))[ni]; + new_id = old_id; + + /* Update old community */ + VECTOR(*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_int_size(&links_community); + + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t c = VECTOR(links_community)[j]; + igraph_real_t w = VECTOR(links_weight)[j]; + + igraph_real_t q_gain = + igraph_i_multilevel_community_modularity_gain(&communities, c, 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 */ + VECTOR(*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 = true; + } + } + + 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_int_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, NULL, 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_int_update(&temp_membership, membership)); + IGRAPH_CHECK(igraph_i_multilevel_shrink(graph, &temp_membership)); + igraph_vector_int_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_null(weights); + + for (igraph_integer_t i = 0; i < ecount; i++) { + VECTOR(*weights)[VECTOR(edges)[i]] += VECTOR(links_weight)[i]; + } + + igraph_free(communities.item); + igraph_vector_int_destroy(&links_community); + igraph_vector_destroy(&links_weight); + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&node_order); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * The original version of 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 + */ + +igraph_error_t igraph_community_multilevel(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_real_t resolution, + igraph_vector_int_t *membership, + igraph_matrix_int_t *memberships, + igraph_vector_t *modularity) { + + igraph_t g; + igraph_vector_t w; + igraph_vector_int_t m; + igraph_vector_int_t level_membership; + igraph_real_t prev_q = -1, q = -1; + igraph_integer_t level = 1; + igraph_integer_t vcount = igraph_vcount(graph); + igraph_integer_t ecount = igraph_ecount(graph); + + /* 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 (weights) { + if (igraph_vector_size(weights) != ecount) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + if (ecount > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Weight vector must not be negative.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + } + if (resolution < 0.0) { + IGRAPH_ERROR("The resolution parameter must be non-negative.", IGRAPH_EINVAL); + } + + /* 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_init_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_INT_INIT_FINALLY(&m, vcount); + IGRAPH_VECTOR_INT_INIT_FINALLY(&level_membership, vcount); + + if (memberships || membership) { + /* Put each vertex in its own community */ + for (igraph_integer_t 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_int_resize(memberships, 0, vcount)); + } + if (modularity) { + /* Clear the modularity vector */ + igraph_vector_clear(modularity); + } + + while (true) { + /* 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 (igraph_integer_t i = 0; i < vcount; i++) { + /* Readjust the membership vector */ + VECTOR(level_membership)[i] = VECTOR(m)[ 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_int_add_rows(memberships, 1)); + IGRAPH_CHECK(igraph_matrix_int_set_row(memberships, &level_membership, level - 1)); + } + + /* debug("Level: %d Communities: %ld Modularity: %f\n", level, 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_int_t tmp; + igraph_real_t mod; + + IGRAPH_CHECK(igraph_vector_int_init_range(&tmp, 0, vcount)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp); + + IGRAPH_CHECK(igraph_modularity(graph, &tmp, weights, resolution, + /* only undirected */ false, &mod)); + + igraph_vector_int_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_int_resize(membership, vcount)); + for (igraph_integer_t 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_int_destroy(&m); + igraph_vector_destroy(&w); + igraph_vector_int_destroy(&level_membership); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} diff --git a/src/community/modularity.c b/src/community/modularity.c new file mode 100644 index 0000000..5167bc4 --- /dev/null +++ b/src/community/modularity.c @@ -0,0 +1,395 @@ +/* -*- 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_interface.h" +#include "igraph_structural.h" + +/** + * \function igraph_modularity + * \brief Calculates 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 - γ k_i k_j / (2m)) δ(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), δ(i,j)=1 if i=j and 0 otherwise, + * and the sum goes over all \c i, \c j pairs of vertices. Note that in this formula, + * the diagonal of the adjacency matrix contains twice the number of self-loops. + * + * + * The resolution parameter \c γ 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 γ = 1. + * + * + * Modularity can also be calculated on directed graphs. This only requires a relatively + * modest change, + * + * + * Q = 1/m sum_ij (A_ij - γ k^out_i k^in_j / m) δ(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 \c γ, 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 The resolution parameter \c γ. Must not be negative. + * 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. + */ +igraph_error_t igraph_modularity(const igraph_t *graph, + const igraph_vector_int_t *membership, + const igraph_vector_t *weights, + const igraph_real_t resolution, + const igraph_bool_t directed, + igraph_real_t *modularity) { + + igraph_vector_t k_out, k_in; + igraph_integer_t no_of_partitions; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_real_t e; /* count/fraction of edges/weights within partitions */ + igraph_real_t m; /* edge count / weight sum */ + igraph_integer_t 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_int_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 not be 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(). */ + + no_of_partitions = igraph_vector_int_max(membership) + 1; + + if (igraph_vector_int_min(membership) < 0) { + IGRAPH_ERROR("Invalid membership vector: negative entry.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INIT_FINALLY(&k_out, no_of_partitions); + IGRAPH_VECTOR_INIT_FINALLY(&k_in, no_of_partitions); + + e = 0.0; + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) + IGRAPH_ERROR("Weight vector size differs from number of edges.", + IGRAPH_EINVAL); + m = 0.0; + for (igraph_integer_t 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 = VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; + c2 = VECTOR(*membership)[ IGRAPH_TO(graph, i) ]; + if (c1 == c2) { + e += directed_multiplier * w; + } + VECTOR(k_out)[c1] += w; + VECTOR(k_in)[c2] += w; + m += w; + } + } else { + m = no_of_edges; + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + c1 = VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; + c2 = VECTOR(*membership)[ IGRAPH_TO(graph, i) ]; + if (c1 == c2) { + e += 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 ) ); + e /= directed_multiplier * m; + + if (m > 0) { + *modularity = e; + for (igraph_integer_t i = 0; i < no_of_partitions; i++) { + *modularity -= resolution * VECTOR(k_out)[i] * VECTOR(k_in)[i]; + } + } else { + *modularity = IGRAPH_NAN; + } + + igraph_vector_destroy(&k_out); + igraph_vector_destroy(&k_in); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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); + from = IGRAPH_FROM(graph, edge); + to = IGRAPH_TO(graph, edge); + 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 Calculates the modularity matrix. + * + * This function returns the modularity matrix, which is defined as + * + * + * B_ij = A_ij - γ k_i k_j / (2m) + * + * + * for undirected graphs, where \c A_ij is the adjacency matrix, \c γ 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 - γ 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 of the adjacency + * matrix and degrees 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 The resolution parameter \c γ. Must not be negative. + * 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() + */ +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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; + igraph_vector_int_t deg_int, in_deg_int, out_deg_int; + igraph_integer_t 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 not be negative.", IGRAPH_EINVAL); + } + + if (!igraph_is_directed(graph)) { + directed = false; + } + 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_VECTOR_INT_INIT_FINALLY(&in_deg_int, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_deg_int, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, &in_deg_int, igraph_vss_all(), IGRAPH_IN, + IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph, &out_deg_int, igraph_vss_all(), IGRAPH_OUT, + IGRAPH_LOOPS)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(in_deg)[i] = VECTOR(in_deg_int)[i]; + VECTOR(out_deg)[i] = VECTOR(out_deg_int)[i]; + } + igraph_vector_int_destroy(&in_deg_int); + igraph_vector_int_destroy(&out_deg_int); + IGRAPH_FINALLY_CLEAN(2); + } 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_VECTOR_INT_INIT_FINALLY(°_int, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °_int, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(deg)[i] = VECTOR(deg_int)[i]; + } + igraph_vector_int_destroy(°_int); + IGRAPH_FINALLY_CLEAN(1); + } 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_init_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/community/optimal_modularity.c b/src/community/optimal_modularity.c new file mode 100644 index 0000000..53d9506 --- /dev/null +++ b/src/community/optimal_modularity.c @@ -0,0 +1,298 @@ +/* -*- 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 "math/safe_intop.h" + +#include "config.h" + +#ifdef HAVE_GLPK + #include +#endif + +#include + +/** + * \function igraph_community_optimal_modularity + * \brief 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 + * https://doi.org/10.1109/TKDE.2007.190689. + * + * + * Note that exact 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. + * When GLPK is not available, \c IGRAPH_UNIMPLEMENTED is returned. + * + * \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 + */ + +igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, + igraph_real_t *modularity, + igraph_vector_int_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_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t no_of_variables; + igraph_integer_t i, j, k, l; + int 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 (isnan(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_int_resize(membership, no_of_nodes)); + igraph_vector_int_null(membership); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); + } + return IGRAPH_SUCCESS; + } + + /* no_of_variables = no_of_nodes * (no_of_nodes + 1) / 2; + * + * Here we do not use IGRAPH_SAFE_N_CHOOSE_2 because later we rely on + * (no_of_nodes + 1) * no_of_nodes not overflowing even before the + * division by 2. See IDX() macro. + */ + IGRAPH_SAFE_MULT(no_of_nodes + 1, no_of_nodes, &no_of_variables); + no_of_variables /= 2; + if (no_of_variables > INT_MAX) { + IGRAPH_ERROR("Problem too large for GLPK.", IGRAPH_EOVERFLOW); + } + + 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_int_resize(membership, no_of_nodes)); + igraph_vector_int_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, (int) no_of_variables); + + /* variables are binary */ + for (i = 0; i < no_of_variables; i++) { + glp_set_col_kind(ip, (int)(st + i), GLP_BV); + } + +#define IDX(a,b) (int)((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) { + igraph_integer_t comm = 0; /* id of the last community that was found */ + IGRAPH_CHECK(igraph_vector_int_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/community/spinglass/NetDataTypes.cpp b/src/community/spinglass/NetDataTypes.cpp new file mode 100644 index 0000000..fecfacd --- /dev/null +++ b/src/community/spinglass/NetDataTypes.cpp @@ -0,0 +1,102 @@ +/* -*- 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" + +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(const NNode* neighbour) { + DLList_Iter iter; + NLink *l_cur, *link = nullptr; + 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 nullptr; + } +} + +igraph_integer_t NNode::Disconnect_From(NNode* neighbour) { + //sollen doppelte Links erlaubt sein?? s.o. + neighbours.fDelete(neighbour); + n_links.fDelete(Get_LinkToNeighbour(neighbour)); + neighbour->n_links.fDelete(neighbour->Get_LinkToNeighbour(this)); + neighbour->neighbours.fDelete(this); + return 1; +} + +igraph_integer_t NNode::Disconnect_From_All() { + igraph_integer_t number_of_neighbours = 0; + while (neighbours.Size()) { + Disconnect_From(neighbours.Pop()); + number_of_neighbours++; + } + return number_of_neighbours ; +} diff --git a/src/community/spinglass/NetDataTypes.h b/src/community/spinglass/NetDataTypes.h new file mode 100644 index 0000000..648cd20 --- /dev/null +++ b/src/community/spinglass/NetDataTypes.h @@ -0,0 +1,568 @@ +/* -*- 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 "igraph_types.h" + +#include +#include + +// In igraph, we set node names to be a string representation of the one-based +// vertex ID. This takes at most 20 characters. Add one for a potential sign +// (should not happen) and one more for the null terminator. +#define SPINGLASS_MAX_NAME_LEN 22 + +//########################################################################################### + +struct HUGE_INDEX { + unsigned int field_index; + igraph_integer_t in_field_index; +}; + +template +class HugeArray { + igraph_integer_t size = 2; + unsigned int highest_field_index = 0; + const igraph_integer_t max_bit_left = 1UL << 31; //wir setzen das 31. Bit auf 1 + igraph_integer_t max_index = 0; + DATA *data; + DATA *fields[32]; +public: + HugeArray(); + HugeArray(const HugeArray &) = delete; + HugeArray & operator = (const HugeArray &) = delete; + ~HugeArray(); + HUGE_INDEX get_huge_index(igraph_integer_t) const; + DATA &Set(igraph_integer_t index); + DATA Get(igraph_integer_t index) { return Set(index); } + DATA &operator[](igraph_integer_t index) { return Set(index); } + igraph_integer_t Size() const { return max_index; } +} ; + +//############################################################################################### +template class DLList; +template class DL_Indexed_List; +template using ClusterList= DLList; +template class DLList_Iter; + +template +class DLItem { + friend class DLList ; + friend class DL_Indexed_List; + friend class DLList_Iter; + + L_DATA item; + igraph_integer_t index; + DLItem *previous; + DLItem *next; + DLItem(L_DATA i, igraph_integer_t ind); + DLItem(L_DATA i, igraph_integer_t ind, DLItem *p, DLItem *n); +public: + void del() { + delete item; + } +}; + +template +class DLList { + friend class DLList_Iter; +protected: + DLItem *head; + DLItem *tail; + igraph_integer_t number_of_items = 0; + virtual DLItem *pInsert(L_DATA, DLItem*); + virtual L_DATA pDelete(DLItem*); +public: + DLList(); + DLList(const DLList &) = delete; + DLList & operator = (const DLList &) = delete; + virtual ~DLList(); + igraph_integer_t Size() const { + return number_of_items; + } + int fDelete(L_DATA); + virtual L_DATA Push(L_DATA); + virtual L_DATA Pop(); + virtual L_DATA Get(igraph_integer_t); + igraph_integer_t Is_In_List(L_DATA); + void delete_items(); +}; + +template +class DL_Indexed_List : public DLList { + DLItem *pInsert(L_DATA, DLItem*) final; + L_DATA pDelete(DLItem*) final; + HugeArray*> array; + igraph_integer_t last_index = 0; + +public: + DL_Indexed_List() = default; + L_DATA Push(L_DATA) final; + L_DATA Pop() final; + L_DATA Get(igraph_integer_t) final; +}; + +//##################################################################################################### + +template class DLList_Iter { + const DLList *list = nullptr; + const DLItem *current = nullptr; + bool end_reached = true; + +public: + L_DATA Next(); + L_DATA Previous(); + L_DATA First(const DLList *l); + L_DATA Last(const DLList *l); + bool End() const { + return end_reached; + } + bool Swap(DLList_Iter); //swapt die beiden Elemente, wenn sie in der gleichen Liste stehen!! +}; + +//##################################################################################################### + +class NLink; + +class NNode { + igraph_integer_t index; + igraph_integer_t cluster_index; + igraph_integer_t marker = 0; + double weight = 0.0; + + DLList neighbours; //list with pointers to neighbours + DLList n_links; + DLList *global_link_list; + char name[SPINGLASS_MAX_NAME_LEN]; +public : + NNode(igraph_integer_t ind, igraph_integer_t c_ind, DLList *ll, const char *n) : + index(ind), cluster_index(c_ind), global_link_list(ll) + { + strcpy(name, n); + } + NNode(const NNode &) = delete; + NNode &operator=(const NNode &) = delete; + ~NNode() { Disconnect_From_All(); } + + igraph_integer_t Get_Index() const { + return index; + } + igraph_integer_t Get_ClusterIndex() const { + return cluster_index; + } + igraph_integer_t Get_Marker() const { + return marker; + } + void Set_Marker(igraph_integer_t m) { + marker = m; + } + void Set_ClusterIndex(igraph_integer_t ci) { + cluster_index = ci; + } + igraph_integer_t Get_Degree() const { + return (neighbours.Size()); + } + const char *Get_Name() { + return name; + } + void Set_Name(const char *n) { + strcpy(name, n); + } + double Get_Weight() const { + return weight; + } + void Set_Weight(double w) { + weight = w; + } + int Connect_To(NNode*, double); + const DLList *Get_Neighbours() const { + return &neighbours; + } + const DLList *Get_Links() const { + return &n_links; + } + igraph_integer_t Disconnect_From(NNode*); + igraph_integer_t Disconnect_From_All(); + NLink *Get_LinkToNeighbour(const NNode *neighbour); +}; + +//##################################################################################################### + +class NLink { + + NNode *start; + NNode *end; + double weight; + +public : + NLink(NNode *s, NNode *e, double w) : start(s), end(e), weight(w) { } + NLink(const NLink &) = delete; + NLink & operator = (const NLink &) = delete; + ~NLink() { start->Disconnect_From(end); } + + NNode *Get_Start() { return start; } + NNode *Get_End() { return end; } + const NNode *Get_Start() const { return start; } + const NNode *Get_End() const { return end; } + + double Get_Weight() const { return weight; } +}; + +//##################################################################################################### + +struct network { + DL_Indexed_List node_list; + DL_Indexed_List link_list; + DL_Indexed_List*> cluster_list; + double sum_weights; + + network() = default; + network (const network &) = delete; + network & operator = (const network &) = delete; + + ~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; + } + } +}; + +template +HugeArray::HugeArray() { + data = new DATA[2]; //ein extra Platz fuer das Nullelement + data[0] = 0; + data[1] = 0; + for (auto & field : fields) { + field = nullptr; + } + 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(igraph_integer_t index) const { + HUGE_INDEX h_index; + unsigned int shift_index = 0; + igraph_integer_t 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 = igraph_integer_t(1) << h_index.field_index; // in help_index wird das hoechste besetzte Bit von Index gesetzt + h_index.in_field_index = (index ^ help_index); // index XOR help_index, womit alle bits unter dem hoechsten erhalten bleiben + return h_index; +} + +template +DATA &HugeArray::Set(igraph_integer_t index) { + igraph_integer_t data_size; + while (size < index + 1) { + highest_field_index++; + data_size = 1UL << highest_field_index; + data = new DATA[data_size]; + for (igraph_integer_t i = 0; i < data_size; i++) { + data[i] = 0; + } + size = size + data_size; //overflow noch abfangen + fields[highest_field_index] = data; + } + HUGE_INDEX h_index = get_huge_index(index); + data = fields[h_index.field_index]; + if (max_index < index) { + max_index = index; + } + return data[h_index.in_field_index]; +} + + +//############################################################################### +template +DLItem::DLItem(L_DATA i, igraph_integer_t ind) : + item(i), index(ind), previous(nullptr), next(nullptr) { } + +template +DLItem::DLItem(L_DATA i, igraph_integer_t ind, DLItem *p, DLItem *n) : + item(i), index(ind), previous(p), next(n) { } + +//###################################################################################################################### +template +DLList::DLList() { + head = new DLItem(NULL, 0); //fuer head und Tail gibt es das gleiche Array-Element!! Vorsicht!! + tail = new DLItem(NULL, 0); + + 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; +} + +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) { + auto *i = new DLItem(data, number_of_items + 1, pos->previous, pos); + pos->previous->next = i; + pos->previous = i; + number_of_items++; + return i; +} +//privates delete +template +L_DATA DLList::pDelete(DLItem *i) { + assert(number_of_items > 0); + L_DATA data = i->item; + i->previous->next = i->next; + i->next->previous = i->previous; + delete i; + number_of_items--; + return data; +} + +//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 = pInsert(data, tail); + return tmp->item; +} + +template +L_DATA DLList::Pop() { + return pDelete(tail->previous); +} + + +template +L_DATA DLList::Get(igraph_integer_t pos) { + if ((pos < 1) || (pos > (number_of_items + 1))) { + return 0; + } + DLItem *cur = head; + while (pos--) { + cur = cur->next; + } + return (cur->item); +} + +//gibt Index des gesuchte Listenelement zurueck, besser waere eigentlich zeiger +template +igraph_integer_t DLList::Is_In_List(L_DATA data) { + DLItem *cur = head, *next; + igraph_integer_t pos = 0; + while (cur) { + next = cur->next; + if (cur->item == data) { + return pos ; + } + cur = next; + pos++; + } + return 0; +} + +//###################################################################################################################### + +//privates Insert +template +DLItem *DL_Indexed_List::pInsert(L_DATA data, DLItem *pos) { + auto *i = new DLItem(data, last_index, pos->previous, pos); + pos->previous->next = i; + pos->previous = i; + this->number_of_items++; + array[last_index] = i; + last_index++; + return i; +} +//privates delete +template +L_DATA DL_Indexed_List::pDelete(DLItem *i) { + assert(this->number_of_items > 0); + 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); + return tmp->item; +} + +template +L_DATA DL_Indexed_List::Pop() { + return pDelete(this->tail->previous); +} + +template +L_DATA DL_Indexed_List::Get(igraph_integer_t pos) { + if (pos > this->number_of_items - 1) { + return 0; + } + return array[pos]->item; +} + +//##################################################################################### + +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(const 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(const 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/community/spinglass/NetRoutines.cpp b/src/community/spinglass/NetRoutines.cpp new file mode 100644 index 0000000..67e4298 --- /dev/null +++ b/src/community/spinglass/NetRoutines.cpp @@ -0,0 +1,81 @@ +/* -*- 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" + +igraph_error_t igraph_i_read_network_spinglass( + const igraph_t *graph, const igraph_vector_t *weights, + network *net, igraph_bool_t use_weights) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + double sum_weight; + + for (igraph_integer_t vid = 0; vid < no_of_nodes; vid++) { + char name[SPINGLASS_MAX_NAME_LEN]; + snprintf(name, sizeof(name) / sizeof(name[0]), "%" IGRAPH_PRId "", vid+1); + net->node_list.Push(new NNode(vid, 0, &net->link_list, name)); + } + + sum_weight = 0.0; + for (igraph_integer_t eid = 0; eid < no_of_edges; eid++) { + igraph_integer_t v1 = IGRAPH_FROM(graph, eid); + igraph_integer_t v2 = IGRAPH_TO(graph, eid); + igraph_real_t w = use_weights ? VECTOR(*weights)[eid] : 1.0; + + NNode *node1 = net->node_list.Get(v1); + NNode *node2 = net->node_list.Get(v2); + + node1->Connect_To(node2, w); + + sum_weight += w; + } + + net->sum_weights = sum_weight; + + return IGRAPH_SUCCESS; +} diff --git a/src/community/spinglass/NetRoutines.h b/src/community/spinglass/NetRoutines.h new file mode 100644 index 0000000..98fd5fe --- /dev/null +++ b/src/community/spinglass/NetRoutines.h @@ -0,0 +1,55 @@ +/* -*- 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" + +igraph_error_t igraph_i_read_network_spinglass( + const igraph_t *graph, const igraph_vector_t *weights, + network *net, igraph_bool_t use_weights); + +#endif diff --git a/src/community/spinglass/clustertool.cpp b/src/community/spinglass/clustertool.cpp new file mode 100644 index 0000000..06b1c77 --- /dev/null +++ b/src/community/spinglass/clustertool.cpp @@ -0,0 +1,648 @@ +/* -*- 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_components.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "core/interruption.h" +#include "core/exceptions.h" + +static igraph_error_t igraph_i_community_spinglass_orig( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *membership, + igraph_vector_int_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 igraph_error_t igraph_i_community_spinglass_negative( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *membership, + igraph_vector_int_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_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 ignored by 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. + * \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. 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. It is not implemented in the \c IGRAPH_SPINCOMM_INP_NEG + * implementation. + * \param starttemp Real number, the temperature at the start. A reasonable + * default is 1.0. + * \param stoptemp Real number, the algorithm stops at this temperature. A + * reasonable default is 0.01. + * \param coolfact Real number, the cooling factor for the simulated + * annealing. A reasonable default is 0.99. + * \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, + * acting as a resolution parameter. Smaller values typically lead to + * larger clusters, larger values typically lead to smaller clusters. + * \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 + * an implementation that allows negative edge weights. + * \param gamma_minus Real number. Parameter for the \c IGRAPH_SPINCOMM_IMP_NEG + * implementation. This acts as a resolution parameter for the negative part + * of the network. Smaller values of \p gamma_minus leads to fewer negative + * edges within clusters. If this argument is set to zero, the algorithm + * reduces to a graph coloring algorithm when all edges have negative + * weights, 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. + * + */ + +igraph_error_t igraph_community_spinglass(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *membership, + igraph_vector_int_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_spinglass_implementation_t implementation, + 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, + gamma_minus); + break; + default: + IGRAPH_ERROR("Unknown implementation in spinglass community detection.", + IGRAPH_EINVAL); + } + ); +} + +static igraph_error_t igraph_i_community_spinglass_orig( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *membership, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t changes, runs; + igraph_bool_t use_weights = false; + 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 for spinglass community detection.", IGRAPH_EINVAL); + } + if (weights) { + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + use_weights = true; + if (igraph_vector_size(weights) > 0 && igraph_vector_min(weights) < 0) { + IGRAPH_ERROR( + "Weights must not be negative when using the original implementation of spinglass communities. " + "Select the implementation meant for negative weights.", + IGRAPH_EINVAL); + } + } + if (coolfact < 0 || coolfact >= 1.0) { + IGRAPH_ERROR("Cooling factor must be positive and strictly smaller than 1.", IGRAPH_EINVAL); + } + if (gamma < 0.0) { + IGRAPH_ERROR("Gamma value must not be negative.", IGRAPH_EINVAL); + } + if ( !(starttemp == 0 && stoptemp == 0) ) { + if (! (starttemp > 0 && stoptemp > 0)) { + IGRAPH_ERROR("Starting and stopping temperatures must be both positive or both zero.", + IGRAPH_EINVAL); + } + if (starttemp <= stoptemp) { + IGRAPH_ERROR("The starting temperature must be larger than the stopping temperature.", + 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_int_resize(membership, no_of_nodes)); + igraph_vector_int_null(membership); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, nullptr, 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_int_resize(csize, no_of_nodes)); + igraph_vector_int_fill(csize, 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_spinglass(graph, weights, + &net, use_weights)); + + prob = 2.0 * net.sum_weights / double(net.node_list.Size()) + / double(net.node_list.Size() - 1); + + PottsModel pm(&net, 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_integer_t vertex, + igraph_vector_int_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 = false; + char startnode[SPINGLASS_MAX_NAME_LEN]; + + /* 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_spinglass(graph, weights, + &net, use_weights)); + + PottsModel pm(&net, spins, update_rule); + + /* initialize the random number generator */ + RNG_BEGIN(); + + /* to be expected, 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, sizeof(startnode) / sizeof(startnode[0]), "%" IGRAPH_PRId "", vertex + 1); + pm.FindCommunityFromStart(gamma, startnode, community, + cohesion, adhesion, inner_links, outer_links); + + RNG_END(); + ); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_community_spinglass_negative( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *membership, + igraph_vector_int_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_real_t gamma_minus) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t runs; + igraph_bool_t use_weights = false; + 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 weights.", + 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 for spinglass community detection.", IGRAPH_EINVAL); + } + if (weights) { + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + use_weights = true; + } + if (coolfact < 0 || coolfact >= 1.0) { + IGRAPH_ERROR("Cooling factor must be positive and strictly smaller than 1.", IGRAPH_EINVAL); + } + if (gamma < 0.0) { + IGRAPH_ERROR("Gamma value must not be negative.", IGRAPH_EINVAL); + } + if ( !(starttemp == 0 && stoptemp == 0) ) { + if (! (starttemp > 0 && stoptemp > 0)) { + IGRAPH_ERROR("Starting and stopping temperatures must be both positive or both zero.", + IGRAPH_EINVAL); + } + if (starttemp <= stoptemp) { + IGRAPH_ERROR("The starting temperature must be larger than the stopping temperature.", + 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_int_resize(membership, no_of_nodes)); + igraph_vector_int_null(membership); + } + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, nullptr, 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_int_resize(csize, no_of_nodes)); + igraph_vector_int_fill(csize, 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_size(weights) > 0) { + 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_spinglass(graph, weights, + &net, use_weights)); + + bool directed = igraph_is_directed(graph); + + PottsModelN pm(&net, 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; + while (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) { + break; + } + } /* 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); + igraph_matrix_destroy(&normalized_adhesion); + igraph_matrix_destroy(&adhesion); + IGRAPH_FINALLY_CLEAN(2); + + RNG_END(); + + return IGRAPH_SUCCESS; +} diff --git a/src/community/spinglass/pottsmodel_2.cpp b/src/community/spinglass/pottsmodel_2.cpp new file mode 100644 index 0000000..f454154 --- /dev/null +++ b/src/community/spinglass/pottsmodel_2.cpp @@ -0,0 +1,1707 @@ +/* -*- 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 "igraph_random.h" +#include "core/interruption.h" + +#include +#include + +using namespace std; + +//################################################################################################# +PottsModel::PottsModel(network *n, igraph_integer_t qvalue, int m) : + net(n), q(qvalue), operation_mode(m), Qmatrix(qvalue+1) +{ + DLList_Iter iter; + const NNode *n_cur; + igraph_integer_t *i_ptr; + //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); + while (!iter.End()) { + if (k_max < n_cur->Get_Degree()) { + k_max = n_cur->Get_Degree(); + } + i_ptr = new igraph_integer_t; + *i_ptr = 0; + new_spins.Push(i_ptr); + i_ptr = new igraph_integer_t; + *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 [] 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. +//#################################################### +igraph_integer_t PottsModel::assign_initial_conf(igraph_integer_t spin) { + igraph_integer_t s; + DLList_Iter iter; + DLList_Iter l_iter; + NNode *n_cur; + const NLink *l_cur; + double sum_weight; + + // initialize colorfield + for (igraph_integer_t 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); + + // 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(); + } + + return net->node_list.Size(); +} + +//##################################################################### +// 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; + igraph_integer_t 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(); + 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 (igraph_integer_t i = 0; i <= q; i++) { + Q += Qmatrix[i][i] - Qa[i] * Qa[i] / double(2.0 * net->sum_weights); + } + Q /= double(2.0 * net->sum_weights); + return Q; +} + +//########################################################################## +// 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; + HeatBathParallelLookup(gamma, prob, kT, 50); + } + kT *= 1.1; // just to be sure... + 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 +//############################################################## +igraph_integer_t PottsModel::HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps) { + DLList_Iter net_iter; + DLList_Iter l_iter; + DLList_Iter i_iter, i_iter2; + NNode *node, *n_cur; + NLink *l_cur; + unsigned int sweep; + igraph_integer_t *SPIN, *P_SPIN, old_spin, new_spin, spin_opt; + igraph_integer_t changes; + double h, delta = 0, deltaE, deltaEmin, w, degree; + 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 neighbors of each type? + // set them all zero + for (igraph_integer_t 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()) { + 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(); + switch (operation_mode) { + case 0: { + delta = 1.0; + break; + } + case 1: { //newman modularity + prob = degree / total_degree_sum; + delta = degree; + break; + } + default: + IGRAPH_FATAL("Must not reach here."); + } + + + spin_opt = old_spin; + deltaEmin = 0.0; + for (igraph_integer_t 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) { + 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 l_iter; + NNode *node, *n_cur; + NLink *l_cur; + igraph_integer_t new_spin, spin_opt, old_spin; + unsigned int sweep; + igraph_integer_t r; + igraph_integer_t changes; + double delta = 0, h, deltaE, deltaEmin, w, degree; + + sweep = 0; + changes = 0; + while (sweep < max_sweeps) { + sweep++; + //ueber alle Knoten im Netz + for (igraph_integer_t n = 0; n < num_of_nodes; n++) { + r = RNG_INTEGER(0, num_of_nodes - 1); + node = net->node_list.Get(r); + // Wir zaehlen, wieviele Nachbarn von jedem spin vorhanden sind + // erst mal alles Null setzen + for (igraph_integer_t 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()) { + 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(); + switch (operation_mode) { + case 0: { + delta = 1.0; + break; + } + case 1: { //newman modularity + prob = degree / total_degree_sum; + delta = degree; + break; + } + default: + IGRAPH_FATAL("Must not reach here."); + } + + + spin_opt = old_spin; + deltaEmin = 0.0; + for (igraph_integer_t 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 +//##################################################################################### +igraph_integer_t PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps) { + DLList_Iter net_iter; + DLList_Iter l_iter; + DLList_Iter i_iter, i_iter2; + NNode *node, *n_cur; + NLink *l_cur; + igraph_integer_t new_spin, spin_opt, old_spin; + igraph_integer_t *SPIN, *P_SPIN; + unsigned int sweep; + igraph_integer_t max_q; + igraph_integer_t changes; + double h, delta = 0, norm, r, beta, minweight, prefac = 0, w, degree; + bool cyclic = false/*, found*/; + igraph_integer_t 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 + for (igraph_integer_t 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()) { + 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(); + 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; + } + default: + IGRAPH_FATAL("Must not reach here."); + } + spin_opt = old_spin; + beta = 1.0 / kT * prefac; + minweight = 0.0; + weights[old_spin] = 0.0; + for (igraph_integer_t 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 (igraph_integer_t 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); + new_spin = 1; + while (new_spin <= q) { + if (r <= weights[new_spin]) { + spin_opt = new_spin; + break; + } else { + r -= weights[new_spin]; + } + new_spin++; + } + //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 (igraph_integer_t i = 1; i <= q; i++) if (color_field[i] > max_q) { + max_q = igraph_integer_t(color_field[i]); + } + + //again, we would not like to end up in cyclic attractors + if (cyclic && changes) { + 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 l_iter; + NNode *node, *n_cur; + NLink *l_cur; + igraph_integer_t new_spin, spin_opt, old_spin; + unsigned int sweep; + igraph_integer_t max_q; + igraph_integer_t rn; + igraph_integer_t changes; + double degree, w, delta = 0, h; + double norm, r, beta, minweight, prefac = 0; + igraph_integer_t 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 (igraph_integer_t n = 0; n < number_of_nodes; n++) { + rn = RNG_INTEGER(0, number_of_nodes - 1); + + node = net->node_list.Get(rn); + // initialize the neighbours and the weights + for (igraph_integer_t 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()) { + 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(); + 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; + } + default: + IGRAPH_FATAL("Must not reach here."); + } + spin_opt = old_spin; + beta = 1.0 / kT * prefac; + minweight = 0.0; + weights[old_spin] = 0.0; + for (igraph_integer_t 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 (igraph_integer_t 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 = RNG_UNIF(0, norm); + new_spin = 1; + while (new_spin <= q) { + if (r <= weights[new_spin]) { + spin_opt = new_spin; + break; + } else { + r -= weights[new_spin]; + } + new_spin++; + } + //------------------------------- + //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 (igraph_integer_t i = 1; i <= q; i++) if (color_field[i] > max_q) { + max_q = igraph_integer_t(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, + const char *nodename, + igraph_vector_int_t *result, + igraph_real_t *cohesion, + igraph_real_t *adhesion, + igraph_integer_t *my_inner_links, + igraph_integer_t *my_outer_links) const { + DLList_Iter iter, iter2; + DLList_Iter l_iter; + DLList to_do; + DLList community; + NNode *start_node = nullptr, *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; + igraph_integer_t community_marker = 5; + igraph_integer_t to_do_marker = 10; + double inner_links = 0, outer_links = 0, aff_r, aff_s; + + // 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; + 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) { + 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()) { + 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 (neighbor->Get_Marker() != community_marker && neighbor->Get_Marker() != to_do_marker) { + to_do.Push(neighbor); + neighbor->Set_Marker(to_do_marker); + } + 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 = nullptr; + 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 (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 (delta_aff_add <= max_delta_aff) { + max_delta_aff = delta_aff_add; + max_aff_node = node; + add = true; + } + 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 (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(); + } + 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; + // 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; + } + 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(); + //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 (neighbor->Get_Marker() != community_marker && 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); + } + IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ + } + //################### + //write the node in the community to a file + //################### + 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_int_clear(result); + while (!iter.End()) { + IGRAPH_CHECK(igraph_vector_int_push_back(result, node->Get_Index())); + node = iter.Next(); + } + } + igraph_integer_t size = community.Size(); + return size; +} + +//################################################################################################ +// this Function writes the clusters to disk +//################################################################################################ +igraph_integer_t PottsModel::WriteClusters(igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *csize, + igraph_vector_int_t *membership, + double kT, double gamma) const { + const NNode *n_cur, *n_cur2; + DLList_Iter iter, iter2; + HugeArray inner_links; + HugeArray outer_links; + HugeArray nodes; + + if (temperature) { + *temperature = kT; + } + + if (csize || membership || modularity) { + // TODO: count the number of clusters + for (igraph_integer_t 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 (igraph_integer_t 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_int_clear(csize); + for (igraph_integer_t spin = 1; spin <= q; spin++) { + if (nodes[spin] > 0) { + inner_links[spin] /= 2; + IGRAPH_CHECK(igraph_vector_int_push_back(csize, nodes[spin])); + } + } + } + + //die Elemente der Cluster + if (membership) { + igraph_integer_t no = -1; + IGRAPH_CHECK(igraph_vector_int_resize(membership, num_of_nodes)); + for (igraph_integer_t 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) { + VECTOR(*membership)[ n_cur->Get_Index() ] = no; + } + n_cur = iter.Next(); + } + } + } + + return num_of_nodes; +} + +//################################################################################################# +PottsModelN::PottsModelN(network *n, igraph_integer_t num_communities, bool directed) : + net(n), q(num_communities), num_nodes(net->node_list.Size()), is_directed(directed) +{ } +//####################################################### +//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) { + igraph_integer_t s; + DLList_Iter l_iter; + const NNode *n_cur; + const NLink *l_cur; + + if (init_spins) { + // 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 igraph_integer_t[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 igraph_integer_t[q + 1]; //The number of nodes in each community + + + //Initialize communities + for (igraph_integer_t 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 (igraph_integer_t 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; + + for (igraph_integer_t v = 0; v < num_nodes; v++) { + if (init_spins) { + s = RNG_INTEGER(1, q); //The new spin s + spin[v] = 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(); + 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; + } + + 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 l_iter; + const NNode *node, *n_cur; + const 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. + */ + igraph_integer_t new_spin, spin_opt, old_spin; + unsigned int sweep; //current sweep + igraph_integer_t 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) + igraph_integer_t 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.0 / 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 (igraph_integer_t 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 (igraph_integer_t 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(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; + + while (new_spin <= q) { + if (r <= weights[new_spin]) { + spin_opt = new_spin; //We have found are new spin + break; + } else { + r -= weights[new_spin]; //Perhaps the next spin is the one we want + } + + new_spin++; + } + + 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; +} + +igraph_integer_t PottsModelN::WriteClusters(igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *community_size, + igraph_vector_int_t *membership, + igraph_matrix_t *adhesion, + igraph_matrix_t *normalised_adhesion, + igraph_real_t *polarization, + double t, + double d_p, + double d_n) { + +#ifdef SPINGLASS_DEBUG + printf("Start writing clusters.\n"); +#endif + //Reassign each community so that we retrieve a community assignment 1 through num_communities + auto *cluster_assign = new igraph_integer_t[q + 1]; + for (igraph_integer_t i = 0; i <= q; i++) { + cluster_assign[i] = 0; + } + + igraph_integer_t num_clusters = 0; + + //Find out what the new communities will be + for (igraph_integer_t i = 0; i < num_nodes; i++) { + igraph_integer_t 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 + } + } + + //And now assign each node to its new community + q = num_clusters; + for (igraph_integer_t i = 0; i < num_nodes; i++) { +#ifdef SPINGLASS_DEBUG + printf("Setting node %d to %d.\n", i, cluster_assign[spin[i]]); +#endif + igraph_integer_t 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_int_resize(community_size, q)); + for (igraph_integer_t 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_int_resize(membership, num_nodes)); + for (igraph_integer_t 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 = nullptr; + double **num_links_neg = nullptr; + //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 ( igraph_integer_t 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 (igraph_integer_t i = 0; i <= q; i++) { + for (igraph_integer_t j = 0; j <= q; j++) { + num_links_pos[i][j] = 0.0; + num_links_neg[i][j] = 0.0; + } + } + + DLList_Iter iter_l; + const NLink *l_cur = iter_l.First(&net->link_list); + + double w = 0.0; + + while (!iter_l.End()) { + w = l_cur->Get_Weight(); + igraph_integer_t a = spin[l_cur->Get_Start()->Get_Index()]; + igraph_integer_t 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 (igraph_integer_t i = 1; i <= q; i++) { + for (igraph_integer_t 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); + 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 ( igraph_integer_t 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 (igraph_integer_t i = 0; i < q; i++) { + for (igraph_integer_t 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/community/spinglass/pottsmodel_2.h b/src/community/spinglass/pottsmodel_2.h new file mode 100644 index 0000000..1cecba4 --- /dev/null +++ b/src/community/spinglass/pottsmodel_2.h @@ -0,0 +1,165 @@ +/* -*- 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; + const size_t n; + +public: + explicit SimpleMatrix(size_t n_) : n(n_) { data = new double[n*n]; } + SimpleMatrix(const SimpleMatrix &) = delete; + ~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: + //these lists are needed to keep track of spin states for parallel update mode + DL_Indexed_List new_spins; + DL_Indexed_List previous_spins; + + HugeArray*> correlation; + network *net; + igraph_integer_t q; + unsigned int operation_mode; + SimpleMatrix Qmatrix; + double* Qa; + double* weights; + double total_degree_sum; + igraph_integer_t num_of_nodes; + igraph_integer_t num_of_links; + igraph_integer_t k_max = 0; + double acceptance = 0; + double* neighbours; + double* color_field; +public: + PottsModel(network *net, igraph_integer_t q, int norm_by_degree); + ~PottsModel(); + + igraph_integer_t assign_initial_conf(igraph_integer_t spin); + + double initialize_Qmatrix(); + double calculate_Q(); + + double FindStartTemp(double gamma, double prob, double ts); + igraph_integer_t HeatBathParallelLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps); + double HeatBathLookupZeroTemp(double gamma, double prob, unsigned int max_sweeps); + igraph_integer_t HeatBathParallelLookup(double gamma, double prob, double kT, unsigned int max_sweeps); + double HeatBathLookup(double gamma, double prob, double kT, unsigned int max_sweeps); + + igraph_integer_t WriteClusters(igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *csize, igraph_vector_int_t *membership, + double kT, double gamma) const; + + double FindCommunityFromStart(double gamma, const char *nodename, + igraph_vector_int_t *result, + igraph_real_t *cohesion, + igraph_real_t *adhesion, + igraph_integer_t *inner_links, + igraph_integer_t *outer_links) const; +}; + + +class PottsModelN { +private: + HugeArray*> correlation; + network *net; + + igraph_integer_t 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) + igraph_integer_t num_nodes; //number of nodes + bool is_directed; + + bool is_init = false; + + double *degree_pos_in = nullptr; //Postive indegree of the nodes (or sum of weights) + double *degree_neg_in = nullptr; //Negative indegree of the nodes (or sum of weights) + double *degree_pos_out = nullptr; //Postive outdegree of the nodes (or sum of weights) + double *degree_neg_out = nullptr; //Negative outdegree of the nodes (or sum of weights) + + double *degree_community_pos_in = nullptr; //Positive sum of indegree for communities + double *degree_community_neg_in = nullptr; //Negative sum of indegree for communities + double *degree_community_pos_out = nullptr; //Positive sum of outegree for communities + double *degree_community_neg_out = nullptr; //Negative sum of outdegree for communities + + igraph_integer_t *csize = nullptr; //The number of nodes in each community + igraph_integer_t *spin = nullptr; //The membership of each node + + double *neighbours = nullptr; //Array of neighbours of a vertex in each community + double *weights = nullptr; //Weights of all possible transitions to another community + +public: + PottsModelN(network *n, igraph_integer_t 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); + igraph_integer_t WriteClusters(igraph_real_t *modularity, + igraph_real_t *temperature, + igraph_vector_int_t *community_size, + igraph_vector_int_t *membership, + igraph_matrix_t *adhesion, + igraph_matrix_t *normalised_adhesion, + igraph_real_t *polarization, + double t, + double d_p, + double d_n); +}; + +#endif diff --git a/src/community/voronoi.c b/src/community/voronoi.c new file mode 100644 index 0000000..aeaeac8 --- /dev/null +++ b/src/community/voronoi.c @@ -0,0 +1,656 @@ +/* + IGraph library. + Copyright (C) 2023 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_community.h" + +#include "igraph_adjlist.h" +#include "igraph_bitset.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_nongraph.h" +#include "igraph_paths.h" +#include "igraph_structural.h" +#include "igraph_transitivity.h" + +#include "core/indheap.h" + +/** + * Unweighted local relative density for some vertices. + * + * This function ignores self-loops and edge multiplicities. + * For isolated vertices, zero is returned. + * + * \param graph The input graph. + * \param res Pointer to a vector, the result will be stored here. + * \param vs Vertex selector, the vertices for which to perform the calculation. + * \return Error code. + * + * Time complexity: TODO. + */ +static igraph_error_t igraph_i_local_relative_density(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vs) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t vs_size; + igraph_vector_int_t nei_mask; /* which nodes are in the local neighbourhood? */ + igraph_vector_int_t nei_done; /* which local nodes have already been processed? -- avoids duplicate processing in multigraphs */ + igraph_lazy_adjlist_t al; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&nei_mask, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nei_done, no_of_nodes); + + IGRAPH_CHECK(igraph_vit_create(graph, vs, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + vs_size = IGRAPH_VIT_SIZE(vit); + + IGRAPH_CHECK(igraph_vector_resize(res, vs_size)); + + for (igraph_integer_t i=0; ! IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t w = IGRAPH_VIT_GET(vit); + igraph_integer_t int_count = 0, ext_count = 0; + + igraph_vector_int_t *w_neis = igraph_lazy_adjlist_get(&al, w); + IGRAPH_CHECK_OOM(w_neis, "Cannot calculate local relative density."); + + igraph_integer_t dw = igraph_vector_int_size(w_neis); + + /* mark neighbours of w, as well as w itself */ + for (igraph_integer_t j=0; j < dw; ++j) { + VECTOR(nei_mask)[ VECTOR(*w_neis)[j] ] = i + 1; + } + VECTOR(nei_mask)[w] = i + 1; + + /* all incident edges of w are internal */ + int_count += dw; + VECTOR(nei_done)[w] = i + 1; + + for (igraph_integer_t j=0; j < dw; ++j) { + igraph_integer_t v = VECTOR(*w_neis)[j]; + + if (VECTOR(nei_done)[v] == i + 1) { + continue; + } else { + VECTOR(nei_done)[v] = i + 1; + } + + igraph_vector_int_t *v_neis = igraph_lazy_adjlist_get(&al, v); + IGRAPH_CHECK_OOM(v_neis, "Cannot calculate local relative density."); + + igraph_integer_t dv = igraph_vector_int_size(v_neis); + + for (igraph_integer_t k=0; k < dv; ++k) { + igraph_integer_t u = VECTOR(*v_neis)[k]; + + if (VECTOR(nei_mask)[u] == i + 1) { + int_count += 1; + } else { + ext_count += 1; + } + } + } + + IGRAPH_ASSERT(int_count % 2 == 0); + int_count /= 2; + + VECTOR(*res)[i] = int_count == 0 ? 0.0 : (igraph_real_t) int_count / (igraph_real_t) (int_count + ext_count); + } + + igraph_vit_destroy(&vit); + igraph_vector_int_destroy(&nei_done); + igraph_vector_int_destroy(&nei_mask); + igraph_lazy_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +/* Weighted local density: we simply multiply the unweighted local relative density with the undirected strength. */ +static igraph_error_t weighted_local_density(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights) { + igraph_vector_t str; + + IGRAPH_CHECK(igraph_i_local_relative_density(graph, res, igraph_vss_all())); + + IGRAPH_VECTOR_INIT_FINALLY(&str, igraph_vcount(graph)); + + IGRAPH_CHECK(igraph_strength(graph, &str, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS, weights)); + + igraph_vector_mul(res, &str); + + igraph_vector_destroy(&str); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/** + * Chooses the generated points for the Voronoi partitioning. + * + * Each generator has the highest local density within a radius \p r around it. + * + * Additionally, if rmax != NULL, the longest distance reached will be stored here. + * This may be smaller than \p r. This feature is used to determine the largest r + * value worth considering, through calling this function with r = INFINITY. + */ +static igraph_error_t choose_generators( + const igraph_t *graph, + igraph_vector_int_t *generators, + igraph_real_t *rmax, + const igraph_vector_t *local_rel_dens, + const igraph_vector_t *lengths, + igraph_neimode_t mode, + igraph_real_t r) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t ord; + igraph_bitset_t excluded; + igraph_integer_t excluded_count; + igraph_inclist_t il; + igraph_2wheap_t q; + igraph_real_t radius_max; + + /* ord[i] is the index of the ith largest element of local_rel_dens */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&ord, 0); + IGRAPH_CHECK(igraph_vector_qsort_ind(local_rel_dens, &ord, IGRAPH_DESCENDING)); + + /* If excluded[v] is true, then v is closer to some already chosen generator than r */ + IGRAPH_BITSET_INIT_FINALLY(&excluded, no_of_nodes); + excluded_count = 0; + + /* The input graph is expected to be simple, but we still set IGRAPH_LOOPS, + * as inclist_init() performs better this way. */ + IGRAPH_CHECK(igraph_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + IGRAPH_CHECK(igraph_2wheap_init(&q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &q); + + radius_max = -IGRAPH_INFINITY; + igraph_vector_int_clear(generators); + for (igraph_integer_t i=0; i < no_of_nodes; i++) { + igraph_integer_t g = VECTOR(ord)[i]; + + if (IGRAPH_BIT_TEST(excluded, g)) continue; + + IGRAPH_CHECK(igraph_vector_int_push_back(generators, g)); + + igraph_2wheap_clear(&q); + IGRAPH_CHECK(igraph_2wheap_push_with_index(&q, g, -0.0)); + while (!igraph_2wheap_empty(&q)) { + igraph_integer_t vid = igraph_2wheap_max_index(&q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&q); + + /* Exceeded cutoff distance, do not search further along this path. */ + if (mindist > r) continue; + + /* Note: We cannot stop the search after hitting an excluded vertex + * because it is possible that another non-excluded one is reachable only + * through this one. */ + if (! IGRAPH_BIT_TEST(excluded, vid)) { + IGRAPH_BIT_SET(excluded, vid); + excluded_count++; + } + + if (mindist > radius_max) { + radius_max = mindist; + } + + igraph_vector_int_t *inc_edges = igraph_inclist_get(&il, vid); + igraph_integer_t inc_count = igraph_vector_int_size(inc_edges); + for (igraph_integer_t j=0; j < inc_count; j++) { + igraph_integer_t edge = VECTOR(*inc_edges)[j]; + igraph_real_t weight = VECTOR(*lengths)[edge]; + + /* Optimization: do not follow infinite-length edges. */ + if (weight == IGRAPH_INFINITY) { + continue; + } + + igraph_integer_t to = IGRAPH_OTHER(graph, edge, vid); + igraph_real_t altdist = mindist + weight; + + if (!igraph_2wheap_has_elem(&q, to)) { + /* This is the first non-infinite distance */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&q, to, -altdist)); + } else if (igraph_2wheap_has_active(&q, to)) { + igraph_real_t curdist = -igraph_2wheap_get(&q, to); + if (altdist < curdist) { + /* This is a shorter path */ + igraph_2wheap_modify(&q, to, -altdist); + } + } + } + } + + /* All vertices have been excluded, no need to search further. */ + if (excluded_count == no_of_nodes) break; + } + + if (rmax) { + *rmax = radius_max; + } + + igraph_2wheap_destroy(&q); + igraph_inclist_destroy(&il); + igraph_bitset_destroy(&excluded); + igraph_vector_int_destroy(&ord); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +/* Find the smallest and largest reasonable values of r to consider for the purpose + * of choosing generator points. */ +static igraph_error_t estimate_minmax_r( + const igraph_t *graph, + const igraph_vector_t *local_rel_dens, + const igraph_vector_t *lengths, + igraph_neimode_t mode, + igraph_real_t *minr, igraph_real_t *maxr) { + + igraph_vector_int_t generators; + + /* As minimum distance, we use the shortest edge length. This may be shorter than the shortest + * incident edge of a generator point, but underestimating the minimum distance does not affect + * the radius optimization negatively. */ + *minr = igraph_vector_min(lengths); + + /* To determine the maximum distance, we run a generator selection with r=INFINITY, + * and record the longest actual distance encountered in the process. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&generators, 0); + IGRAPH_CHECK(choose_generators(graph, &generators, maxr, local_rel_dens, lengths, mode, IGRAPH_INFINITY)); + igraph_vector_int_destroy(&generators); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +typedef igraph_error_t optfun_t(double x, double *res, void *extra); + +/* This is the coefficient of the second order part when fitting a quadratic + * polynomial to the points given in the argument. */ +static igraph_real_t coeff2( + igraph_real_t x1, igraph_real_t x2, igraph_real_t x3, + igraph_real_t f1, igraph_real_t f2, igraph_real_t f3) { + igraph_real_t num = x1*(f3 - f2) + x2*(f1 - f3) + x3*(f2 - f1); + igraph_real_t denom = (x1 - x2)*(x1 - x3)*(x2 - x3); + return num / denom; +} + +/* Given the stationary point of the quadratic fit to the given points */ +static igraph_real_t peakx( + igraph_real_t x1, igraph_real_t x2, igraph_real_t x3, + igraph_real_t f1, igraph_real_t f2, igraph_real_t f3) { + igraph_real_t x1s = x1*x1, x2s = x2*x2, x3s = x3*x3; + igraph_real_t num = f3 * (x1s - x2s) + f1 * (x2s - x3s) + f2 * (x3s - x1s); + igraph_real_t denom = f3 * (x1 - x2) + f1 * (x2 - x3) + f2 * (x3 - x1); + return 0.5 * num / denom; +} + +/** + * Simple Brent's method optimizer, with some specializations for the use + * case at hand (see code comments). It must be called with x2 > x1. + * The optimal argument is the last one for which f() is invoked. + * f() is expected to record this in 'extra'. + */ +static igraph_error_t brent_opt(optfun_t *f, igraph_real_t x1, igraph_real_t x2, void *extra) { + igraph_real_t lo = x1, hi = x2; + + IGRAPH_ASSERT(isfinite(lo)); + IGRAPH_ASSERT(isfinite(hi)); + + /* We choose the initial x3 point to be closer to x1 than x2. + * This is so that if f1 == f2, the next computed point (newx) + * would not coincide with x3. */ + igraph_real_t x3 = 0.6*x1 + 0.4*x2; + igraph_real_t f1, f2, f3; + + IGRAPH_CHECK(f(x1, &f1, extra)); + + /* Catch special case that would wreak havoc in the optimizer. */ + if (x1 == x2) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(f(x2, &f2, extra)); + IGRAPH_CHECK(f(x3, &f3, extra)); + + /* We expect that the middle point, f3, is greater than the boundary points. */ + + /* Currently, we do not handle the case when f3 < f1. */ + if (f1 > f3) { + IGRAPH_ERROR("Optimizer did not converge while maximizing modularity for Voronoi communities.", + IGRAPH_DIVERGED); + } + + /* It sometimes happens in disconnected graphs that the maximum is reached at or near the + * top of the radius range. If so, we bisect the (x3, x2) interval to search for a configuration + * where f3 >= f2. */ + if (f2 > f3) { + /* Limit iterations to 'maxiter'. */ + const int maxiter = 10; + int i; + for (i=0; i < maxiter; ++i) { + x1 = x3; f1 = f3; + x3 = 0.5 * (x1 + x2); + IGRAPH_CHECK(f(x3, &f3, extra)); + + if (f3 >= f2) break; + } + /* If no maximum was found in 'maxiter' bisections, just take the upper end of the range. */ + if (i == maxiter) { + IGRAPH_CHECK(f(x2, &f2, extra)); + return IGRAPH_SUCCESS; + } + } + + /* Limit iterations to 20 */ + for (int i=0; i < 20; ++i) { + igraph_real_t newx, newf; + + newx = peakx(x1, x2, x3, f1, f2, f3); + IGRAPH_CHECK(f(newx, &newf, extra)); + + /* We need to decide whether we drop (x1, f1) or (x2, f2) for the following iterations. + * The sign of a1 (or a2) determines whether dropping x1 (or x2) yields a convex or concave + * parabola in the next iteration. We need a negative sign = concave parabola, + * as we are looking for a maximum. We always keep (x3, f3) as it was the last added point. */ + igraph_real_t a1 = coeff2(x2, x3, newx, f2, f3, newf); + igraph_real_t a2 = coeff2(x1, x3, newx, f1, f3, newf); + + /* We cannot continue without the Brent optimizer switching to minimization. + * Terminate search, accepting the current result. */ + if (a1 >= 0 && a2 >= 0) { + break; + } + + if (a1 <= a2) { + x1 = x2; + x2 = x3; + x3 = newx; + + f1 = f2; + f2 = f3; + f3 = newf; + } else { + x2 = x1; + x1 = x3; + x3 = newx; + + f2 = f1; + f1 = f3; + f3 = newf; + } + + /* Check if value goes out of initial interval. */ + if (x3 < lo || x3 > hi) { + IGRAPH_ERROR("Optimizer did not converge while maximizing modularity for Voronoi communities.", + IGRAPH_DIVERGED); + } + + /* We exploit the fact that we are optimizing a discrete valued function, and we can + * detect convergence by checking that the function value stays exactly the same. + * + * As an optimization, we only check whether the two of the three f values are the same. + * Almost always, when this is the case, another iteration would not yield a better + * maximum, however, saving a call to f() improves performance noticeably. + */ + const igraph_real_t eps = 1e-10; + int c1 = igraph_cmp_epsilon(f1, f3, eps); + int c2 = igraph_cmp_epsilon(f2, f3, eps); + if (c1 == 0 || c2 == 0) { + break; + } + } + + return IGRAPH_SUCCESS; +} + + +/* Work data for get_modularity() */ +typedef struct { + const igraph_t *graph; + const igraph_vector_t *local_dens; + const igraph_vector_t *lengths; + const igraph_vector_t *weights; + igraph_neimode_t mode; + igraph_vector_int_t *generators; + igraph_vector_int_t *membership; + igraph_real_t modularity; +} get_modularity_work_t; + + +/* Objective function used with brent_opt(), it computes the modularity for a given radius. */ +static igraph_error_t get_modularity(igraph_real_t r, igraph_real_t *modularity, void *extra) { + get_modularity_work_t *gm = extra; + + IGRAPH_CHECK(choose_generators(gm->graph, gm->generators, NULL, + gm->local_dens, gm->lengths, gm->mode, + r)); + IGRAPH_CHECK(igraph_voronoi(gm->graph, gm->membership, NULL, + gm->generators, gm->lengths, + gm->mode, IGRAPH_VORONOI_RANDOM)); + IGRAPH_CHECK(igraph_modularity(gm->graph, gm->membership, gm->weights, + 1, gm->mode == IGRAPH_ALL ? IGRAPH_UNDIRECTED : IGRAPH_DIRECTED, + &gm->modularity)); + *modularity = gm->modularity; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_community_voronoi + * \brief Finds communities using Voronoi partitioning. + * + * \experimental + * + * This function finds communities using a Voronoi partitioning of vertices based + * on the given edge lengths divided by the edge clustering coefficient + * (\ref igraph_ecc()). The generator vertices are chosen to be those with the + * largest local relative density within a radius \p r, with the local relative + * density of a vertex defined as + * s m / (m + k), where \c s is the strength of the vertex, + * \c m is the number of edges within the vertex's first order neighborhood, + * while \c k is the number of edges with only one endpoint within this + * neighborhood. + * + * + * References: + * + * + * Deritei et al., Community detection by graph Voronoi diagrams, + * New Journal of Physics 16, 063007 (2014) + * https://doi.org/10.1088/1367-2630/16/6/063007 + * + * + * Molnár et al., Community Detection in Directed Weighted Networks using Voronoi Partitioning, + * Scientific Reports 14, 8124 (2024) + * https://doi.org/10.1038/s41598-024-58624-4 + * + * \param graph The input graph. It must be simple. + * \param membership If not \c NULL, the membership of each vertex is returned here. + * \param generators If not \c NULL, the generator points used for Voronoi partitioning are returned here. + * \param modularity If not \c NULL, the modularity score of the partitioning is returned here. + * \param lengths Edge lengths, or \c NULL to consider all edges as having unit length. + * Voronoi partitioning will use edge lengths equal to lengths / ECC where ECC is the edge + * clustering coefficient. + * \param weights Edge weights, or \c NULL to consider all edges as having unit weight. + * Weights are used when selecting generator points, as well as for computing modularity. + * \param mode If \c IGRAPH_OUT, distances from generator points to all other nodes are considered. + * If \c IGRAPH_IN, the reverse distances are used. If \c IGRAPH_ALL, edge directions are ignored. + * This parameter is ignored for undirected graphs. + * \param r The radius/resolution to use when selecting generator points. The larger this value, the + * fewer partitions there will be. Pass in a negative value to automatically select the radius + * that maximizes modularity. + * \return Error code. + * + * \sa \ref igraph_voronoi(), \ref igraph_ecc(). + * + * Time complexity: TODO. + */ +igraph_error_t igraph_community_voronoi( + const igraph_t *graph, + igraph_vector_int_t *membership, igraph_vector_int_t *generators, + igraph_real_t *modularity, + const igraph_vector_t *lengths, const igraph_vector_t *weights, + igraph_neimode_t mode, igraph_real_t r) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_t local_rel_dens; + igraph_vector_t lengths2; /* lengths2 = lengths / ecc */ + igraph_vector_int_t imembership, igenerators; + igraph_vector_int_t *pmembership, *pgenerators; + igraph_bool_t simple; + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (lengths && igraph_vector_size(lengths) != no_of_edges) { + IGRAPH_ERROR("Edge length vector size does not match edge count.", IGRAPH_EINVAL); + } + + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Edge length vector size does not match edge count.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_is_simple(graph, &simple)); + if (! simple) { + IGRAPH_ERROR("The graph must be simple for Voronoi communities.", IGRAPH_EINVAL); + } + if (igraph_is_directed(graph) && mode == IGRAPH_ALL) { + igraph_bool_t has_mutual; + /* When the graph is directed but edge directions are ignored, + * mutual edges are effectively multi-edges. */ + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + IGRAPH_ERROR("The graph must be simple for Voronoi communities. " + "Mutual directed edges are effectively multi-edges when ignoring edge directions.", + IGRAPH_EINVAL); + } + } + + if (no_of_edges == 0) { + /* Also handles no_of_nodes <= 1 */ + if (membership) { + IGRAPH_CHECK(igraph_vector_int_range(membership, 0, no_of_nodes)); + } + if (generators) { + IGRAPH_CHECK(igraph_vector_int_range(generators, 0, no_of_nodes)); + } + return IGRAPH_SUCCESS; + } + + if (! generators) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&igenerators, no_of_nodes); + pgenerators = &igenerators; + } else { + pgenerators = generators; + } + + if (! membership) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&imembership, no_of_nodes); + pmembership = &imembership; + } else { + pmembership = membership; + } + + if (lengths) { + igraph_real_t m = igraph_vector_min(lengths); + if (isnan(m)) { + IGRAPH_ERROR("Edge lengths must not be NaN.", IGRAPH_EINVAL); + } + if (m < 0) { + IGRAPH_ERROR("Edge lengths must be non-negative.", IGRAPH_EINVAL); + } + } + + if (weights) { + igraph_real_t m = igraph_vector_min(weights); + if (isnan(m)) { + IGRAPH_ERROR("Edge weights must not be NaN.", IGRAPH_EINVAL); + } + if (m <= 0) { + IGRAPH_ERROR("Edge weights must be positive.", IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&local_rel_dens, 0); + + IGRAPH_CHECK(weighted_local_density(graph, &local_rel_dens, weights)); + + IGRAPH_VECTOR_INIT_FINALLY(&lengths2, 0); + IGRAPH_CHECK(igraph_ecc(graph, &lengths2, igraph_ess_all(IGRAPH_EDGEORDER_ID), 3, true, true)); + + /* Note: ECC is never NaN but it may be Inf */ + for (igraph_integer_t i=0; i < no_of_edges; i++) { + VECTOR(lengths2)[i] = 1 / (VECTOR(lengths2)[i]); + } + if (lengths) { + igraph_vector_mul(&lengths2, lengths); + } + + if (r < 0) { + igraph_real_t minr, maxr; + + IGRAPH_CHECK(estimate_minmax_r(graph, &local_rel_dens, &lengths2, mode, &minr, &maxr)); + + get_modularity_work_t gm = { + graph, + &local_rel_dens, + &lengths2, + weights, + mode, + pgenerators, + pmembership, + /* modularity */ IGRAPH_NAN + }; + IGRAPH_CHECK(brent_opt(get_modularity, minr, maxr, &gm)); + if (modularity) { + *modularity = gm.modularity; + } + } else { + IGRAPH_CHECK(choose_generators(graph, pgenerators, NULL, &local_rel_dens, &lengths2, mode, r)); + IGRAPH_CHECK(igraph_voronoi(graph, membership, NULL, pgenerators, &lengths2, mode, IGRAPH_VORONOI_RANDOM)); + if (modularity) { + IGRAPH_CHECK(igraph_modularity(graph, membership, weights, 1, + mode == IGRAPH_ALL ? IGRAPH_UNDIRECTED : IGRAPH_DIRECTED, modularity)); + } + } + + if (! generators) { + igraph_vector_int_destroy(&igenerators); + IGRAPH_FINALLY_CLEAN(1); + } + if (! membership) { + igraph_vector_int_destroy(&imembership); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&local_rel_dens); + igraph_vector_destroy(&lengths2); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/community/walktrap/walktrap.cpp b/src/community/walktrap/walktrap.cpp new file mode 100644 index 0000000..1fb9d09 --- /dev/null +++ b/src/community/walktrap/walktrap.cpp @@ -0,0 +1,233 @@ +/* -*- 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" + +#include +#include + +// This is necessary for GCC 5 and earlier, where including +// makes isnan() unusable without the std:: prefix, even if +// was included as well. +using std::isnan; + +using namespace igraph::walktrap; + +/** + * \function igraph_community_walktrap + * \brief Community finding using a random walk based similarity measure. + * + * 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 \c 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 \c 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 \c NULL pointer, then + * the membership vector corresponding to the maximal modularity + * score is stored here. + * \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 + */ + +igraph_error_t igraph_community_walktrap(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_integer_t steps, + igraph_matrix_int_t *merges, + igraph_vector_t *modularity, + igraph_vector_int_t *membership) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t comp_count; + igraph_matrix_int_t imerges, *pmerges = merges; + igraph_vector_t imodularity, *pmodularity = modularity; + + if (steps <= 0) { + IGRAPH_ERROR("Length of random walks must be positive for walktrap community detection.", IGRAPH_EINVAL); + } + + if (steps > INT_MAX) { + IGRAPH_ERROR("Length of random walks too large for walktrap community detection.", IGRAPH_EINVAL); + } + + int length = steps; + + if (weights) { + 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 non-negative.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + } + + if (membership) { + /* We need both 'modularity' and 'merges' to compute 'membership'. + * If they were not provided by the called, we allocate these here. */ + + if (! modularity) { + IGRAPH_VECTOR_INIT_FINALLY(&imodularity, 0); + pmodularity = &imodularity; + } + + if (! merges) { + IGRAPH_MATRIX_INT_INIT_FINALLY(&imerges, 0, 0); + pmerges = &imerges; + } + } + + IGRAPH_HANDLE_EXCEPTIONS( + Graph G; + IGRAPH_CHECK(G.convert_from_igraph(graph, weights)); + + if (pmerges || pmodularity) { + IGRAPH_CHECK(igraph_connected_components(graph, /*membership=*/ NULL, /*csize=*/ NULL, + &comp_count, IGRAPH_WEAK)); + } + if (pmerges) { + IGRAPH_CHECK(igraph_matrix_int_resize(pmerges, no_of_nodes - comp_count, 2)); + } + if (pmodularity) { + IGRAPH_CHECK(igraph_vector_resize(pmodularity, no_of_nodes - comp_count + 1)); + igraph_vector_null(pmodularity); + } + Communities C(&G, length, pmerges, pmodularity); + + while (!C.H->is_empty()) { + IGRAPH_ALLOW_INTERRUPTION(); + C.merge_nearest_communities(); + } + ); + + if (membership) { + igraph_integer_t m; + m = no_of_nodes > 0 ? igraph_vector_which_max(pmodularity) : 0; + IGRAPH_CHECK(igraph_community_to_membership(pmerges, no_of_nodes, + /*steps=*/ m, + membership, + /*csize=*/ NULL)); + + if (! merges) { + igraph_matrix_int_destroy(&imerges); + IGRAPH_FINALLY_CLEAN(1); + } + if (! modularity) { + igraph_vector_destroy(&imodularity); + IGRAPH_FINALLY_CLEAN(1); + } + } + + /* The walktrap implementation cannot work with NaN values internally, + * and produces 0 for the modularity of edgeless graphs. We correct + * this to NaN in the last step for consistency. */ + if (modularity && no_of_edges == 0) { + VECTOR(*modularity)[0] = IGRAPH_NAN; + } + + return IGRAPH_SUCCESS; +} diff --git a/src/community/walktrap/walktrap_communities.cpp b/src/community/walktrap/walktrap_communities.cpp new file mode 100644 index 0000000..7844b7c --- /dev/null +++ b/src/community/walktrap/walktrap_communities.cpp @@ -0,0 +1,786 @@ +/* -*- 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 = nullptr; +IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector1 = nullptr; +IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector2 = nullptr; +IGRAPH_THREAD_LOCAL int* Probabilities::id = nullptr; +IGRAPH_THREAD_LOCAL int* Probabilities::vertices1 = nullptr; +IGRAPH_THREAD_LOCAL int* Probabilities::vertices2 = nullptr; +IGRAPH_THREAD_LOCAL int Probabilities::current_id = 0; + + +Neighbor::Neighbor() { + next_community1 = nullptr; + previous_community1 = nullptr; + next_community2 = nullptr; + previous_community2 = nullptr; + heap_index = -1; +} + +Probabilities::~Probabilities() { + delete[] P; + delete[] vertices; +} + +Probabilities::Probabilities(int community) { + Graph* G = C->G; + int nb_vertices1 = 0; + int nb_vertices2 = 0; + + double initial_proba = 1. / static_cast(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++) { + double 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]; + double 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]; + double 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; + } + } + } + } + double* 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 double[G->nb_vertices]; + size = G->nb_vertices; + vertices = nullptr; + 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 double[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++; + } + } + } +} + +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; + + double w1 = C->communities[community1].size / static_cast(C->communities[community1].size + C->communities[community2].size); + double w2 = C->communities[community2].size / static_cast(C->communities[community1].size + C->communities[community2].size); + + + if (P1->size == C->G->nb_vertices) { + P = new double[C->G->nb_vertices]; + size = C->G->nb_vertices; + vertices = nullptr; + + 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 double[C->G->nb_vertices]; + size = C->G->nb_vertices; + vertices = nullptr; + + 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 double[C->G->nb_vertices]; + size = C->G->nb_vertices; + vertices = nullptr; + 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 double[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]]; + } + } + } + } +} + +double Probabilities::compute_distance(const Probabilities* P2) const { + double r = 0.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; +} + +Community::Community() { + P = nullptr; + first_neighbor = nullptr; + last_neighbor = nullptr; + sub_community_of = -1; + sub_communities[0] = -1; + sub_communities[1] = -1; + sigma = 0.; + internal_weight = 0.; + total_weight = 0.; +} + +Community::~Community() { + delete P; +} + + +Communities::Communities(Graph* graph, int random_walks_length, + igraph_matrix_int_t *pmerges, + igraph_vector_t *pmodularity) { + G = graph; + merges = pmerges; + mergeidx = 0; + modularity = pmodularity; + + Probabilities::C = this; + Probabilities::length = random_walks_length; + Probabilities::tmp_vector1 = new double[G->nb_vertices]; + Probabilities::tmp_vector2 = new double[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); + IGRAPH_ASSUME(G->nb_vertices >= 0); // avoid false-positive GCC warnings + communities = new Community[2 * G->nb_vertices]; + + // init the n single vertex communities + + 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); + } + + /* int c = 0; */ + Neighbor* N = H->get_first(); + if (N == nullptr) { + 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(); + /* 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 << "."; */ + /* } */ + /* } */ + } + + if (modularity) { + double Q = 0.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); + } + } + Q /= G->total_weight; + VECTOR(*modularity)[mergeidx] = Q; + } +} + +Communities::~Communities() { + delete[] members; + delete[] communities; + delete H; + + delete[] Probabilities::tmp_vector1; + delete[] Probabilities::tmp_vector2; + delete[] Probabilities::id; + delete[] Probabilities::vertices1; + delete[] Probabilities::vertices2; +} + +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 = nullptr; + } else { + N->previous_community2 = nullptr; + } + } + 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); +} + +void Communities::add_neighbor(Neighbor* N) { + communities[N->community1].add_neighbor(N); + communities[N->community2].add_neighbor(N); + H->add(N); +} + +void Communities::update_neighbor(Neighbor* N, double new_delta_sigma) { + N->delta_sigma = new_delta_sigma; + H->update(N); +} + +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 = nullptr; + } + if (communities[c2].P) { + delete communities[c2].P; + communities[c2].P = nullptr; + } + +// 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); + } + } + + 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(); + } + + double d = N->delta_sigma; + remove_neighbor(N); + + merge_communities(N); + + if (merges) { + MATRIX(*merges, mergeidx, 0) = N->community1; + MATRIX(*merges, mergeidx, 1) = N->community2; + } + + mergeidx++; + + if (modularity) { + double Q = 0.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); + } + } + Q /= 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) const { + if (!communities[community1].P) { + communities[community1].P = new Probabilities(community1); + } + if (!communities[community2].P) { + communities[community2].P = new Probabilities(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/community/walktrap/walktrap_communities.h b/src/community/walktrap/walktrap_communities.h new file mode 100644 index 0000000..484079f --- /dev/null +++ b/src/community/walktrap/walktrap_communities.h @@ -0,0 +1,161 @@ +/* -*- 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 "config.h" + +namespace igraph { + +namespace walktrap { + +class Communities; +class Probabilities { +public: + static IGRAPH_THREAD_LOCAL double* tmp_vector1; // + static IGRAPH_THREAD_LOCAL double* 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 + double* P; // the probabilities + + double compute_distance(const Probabilities* P2) const; // compute the squared distance r^2 between this probability vector and P2 + explicit 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. + + + double sigma; // sigma(C) of the community + double internal_weight; // sum of the weight of the internal edges + double 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 communities, -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 add_neighbor(Neighbor* N); + void remove_neighbor(Neighbor* N); + + Community(); // create an empty community + ~Community(); // destructor +}; + +class Communities { +private: + igraph_matrix_int_t *merges; + igraph_integer_t mergeidx; + igraph_vector_t *modularity; + +public: + 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, + igraph_matrix_int_t *merges = nullptr, + igraph_vector_t *modularity = nullptr); // 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) const; // compute delta_sigma(c1,c2) + + void remove_neighbor(Neighbor* N); + void add_neighbor(Neighbor* N); + void update_neighbor(Neighbor* N, double new_delta_sigma); +}; + +} +} /* end of namespaces */ + +#endif // WALKTRAP_COMMUNITIES_H diff --git a/src/community/walktrap/walktrap_graph.cpp b/src/community/walktrap/walktrap_graph.cpp new file mode 100644 index 0000000..56e31fc --- /dev/null +++ b/src/community/walktrap/walktrap_graph.cpp @@ -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 + +*/ + +/* 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 +#include + +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 = nullptr; + total_weight = 0.; +} + +Vertex::~Vertex() { + delete[] edges; +} + +Graph::Graph() { + nb_vertices = 0; + nb_edges = 0; + vertices = nullptr; + total_weight = 0.; +} + +Graph::~Graph () { + delete[] vertices; +} + +class Edge_list { +public: + int* V1; + int* V2; + double* W; + + int size; + int size_max; + + void add(int v1, int v2, double w); + Edge_list() { + size = 0; + size_max = 1024; + V1 = new int[1024]; + V2 = new int[1024]; + W = new double[1024]; + } + + ~Edge_list() { + delete[] V1; + delete[] V2; + delete[] W; + } +}; + +void Edge_list::add(int v1, int v2, double w) { + if (size == size_max) { + int* tmp1 = new int[2 * size_max]; + int* tmp2 = new int[2 * size_max]; + double* tmp3 = new double[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++; +} + +igraph_error_t Graph::convert_from_igraph(const igraph_t *graph, + const igraph_vector_t *weights) { + Graph &G = *this; + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + // Avoid warnings with GCC when compiling with LTO. + IGRAPH_ASSUME(no_of_nodes >= 0); + IGRAPH_ASSUME(no_of_edges >= 0); + + // Refactoring the walktrap code to support larger graphs is pointless + // as running the algorithm on them would take an impractically long time. + if (no_of_nodes > INT_MAX || no_of_edges > INT_MAX) { + IGRAPH_ERROR("Graph too large for walktrap community detection.", IGRAPH_EINVAL); + } + + Edge_list EL; + + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + igraph_real_t w = weights ? VECTOR(*weights)[i] : 1.0; + EL.add(IGRAPH_FROM(graph, i), IGRAPH_TO(graph, i), w); + } + + G.nb_vertices = no_of_nodes; + 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++) { + int deg = G.vertices[i].degree; + double 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++) { + /* Check for zero strength, as it may lead to crashes the in walktrap algorithm. + * See https://github.com/igraph/igraph/pull/2043 */ + if (G.vertices[i].total_weight == 0) { + /* G.vertices will be destroyed by Graph::~Graph() */ + IGRAPH_ERROR("Vertex with zero strength found: all vertices must have positive strength for walktrap.", + IGRAPH_EINVAL); + } + 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 IGRAPH_SUCCESS; +} + +} +} diff --git a/src/community/walktrap/walktrap_graph.h b/src/community/walktrap/walktrap_graph.h new file mode 100644 index 0000000..2347e8c --- /dev/null +++ b/src/community/walktrap/walktrap_graph.h @@ -0,0 +1,101 @@ +/* -*- 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 + double 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 + double 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 + double total_weight; // total weight of the edges + Vertex* vertices; // array of the vertices + + Graph(); // create an empty graph + ~Graph(); // destructor + + igraph_error_t convert_from_igraph(const igraph_t *igraph, const igraph_vector_t *weights); +}; + +} +} /* end of namespaces */ + +#endif // WALKTRAP_GRAPH_H diff --git a/src/community/walktrap/walktrap_heap.cpp b/src/community/walktrap/walktrap_heap.cpp new file mode 100644 index 0000000..dcb0518 --- /dev/null +++ b/src/community/walktrap/walktrap_heap.cpp @@ -0,0 +1,142 @@ +/* -*- 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 nullptr; + } 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); +} + +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() const { + return (size == 0); +} diff --git a/src/community/walktrap/walktrap_heap.h b/src/community/walktrap/walktrap_heap.h new file mode 100644 index 0000000..90b4f31 --- /dev/null +++ b/src/community/walktrap/walktrap_heap.h @@ -0,0 +1,107 @@ +/* -*- 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 + + double delta_sigma; // the delta sigma between the two communities + double 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 + bool is_empty() const; + + explicit Neighbor_heap(int max_size); + ~Neighbor_heap(); +}; + +} +} /* end of namespaces */ + +#endif // WALKTRAP_HEAP_H diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 0000000..28cf4e2 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,41 @@ +#ifndef IGRAPH_PRIVATE_CONFIG_H +#define IGRAPH_PRIVATE_CONFIG_H + +#include "igraph_config.h" + +#cmakedefine HAVE_STRCASECMP 1 +#cmakedefine HAVE_STRNCASECMP 1 +#cmakedefine HAVE__STRICMP 1 +#cmakedefine HAVE__STRNICMP 1 +#cmakedefine HAVE_STRDUP 1 +#cmakedefine HAVE_STRNDUP 1 +#cmakedefine HAVE_USELOCALE 1 +#cmakedefine HAVE_XLOCALE 1 +#cmakedefine HAVE__CONFIGTHREADLOCALE 1 + +#cmakedefine HAVE_BUILTIN_OVERFLOW 1 + +#cmakedefine HAVE__UMUL128 1 +#cmakedefine HAVE___UMULH 1 +#cmakedefine HAVE___UINT128_T 1 + +#cmakedefine HAVE__POPCNT64 1 +#cmakedefine HAVE__POPCNT 1 +#cmakedefine HAVE__BITSCANFORWARD64 1 +#cmakedefine HAVE__BITSCANFORWARD 1 +#cmakedefine HAVE__BITSCANREVERSE64 1 +#cmakedefine HAVE__BITSCANREVERSE 1 + +#cmakedefine HAVE_GLPK 1 +#cmakedefine HAVE_LIBXML 1 + +#cmakedefine INTERNAL_BLAS 1 +#cmakedefine INTERNAL_LAPACK 1 +#cmakedefine INTERNAL_ARPACK 1 +#cmakedefine INTERNAL_GMP 1 + + +#define IGRAPH_F77_SAVE static @TLS_KEYWORD@ +#define IGRAPH_THREAD_LOCAL @TLS_KEYWORD@ + +#endif diff --git a/src/connectivity/cohesive_blocks.c b/src/connectivity/cohesive_blocks.c new file mode 100644 index 0000000..7850d0e --- /dev/null +++ b/src/connectivity/cohesive_blocks.c @@ -0,0 +1,567 @@ +/* -*- 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) { + igraph_integer_t 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(VECTOR(*ptr)[i]); /* also sets it to NULL */ + } + } +} + +/* 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 igraph_error_t igraph_i_cb_components(igraph_t *graph, + const igraph_vector_bool_t *excluded, + igraph_vector_int_t *components, + igraph_integer_t *no, + /* working area follows */ + igraph_vector_int_t *compid, + igraph_dqueue_int_t *Q, + igraph_vector_int_t *neis) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + igraph_integer_t cno = 0; + + igraph_vector_int_clear(components); + igraph_dqueue_int_clear(Q); + IGRAPH_CHECK(igraph_vector_int_resize(compid, no_of_nodes)); + igraph_vector_int_null(compid); + + for (i = 0; i < no_of_nodes; i++) { + + if (VECTOR(*compid)[i]) { + continue; + } + if (VECTOR(*excluded)[i]) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_int_push(Q, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(components, i)); + VECTOR(*compid)[i] = ++cno; + + while (!igraph_dqueue_int_empty(Q)) { + igraph_integer_t node = igraph_dqueue_int_pop(Q); + igraph_integer_t j, n; + IGRAPH_CHECK(igraph_neighbors(graph, neis, node, IGRAPH_ALL)); + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + igraph_integer_t v = VECTOR(*neis)[j]; + if (VECTOR(*excluded)[v]) { + if (VECTOR(*compid)[v] != cno) { + VECTOR(*compid)[v] = cno; + IGRAPH_CHECK(igraph_vector_int_push_back(components, v)); + } + } else { + if (VECTOR(*compid)[v] == 0) { + VECTOR(*compid)[v] = cno; /* could be anything positive */ + IGRAPH_CHECK(igraph_vector_int_push_back(components, v)); + IGRAPH_CHECK(igraph_dqueue_int_push(Q, v)); + } + } + } + } /* while !igraph_dqueue_int_empty */ + + IGRAPH_CHECK(igraph_vector_int_push_back(components, -1)); + + } /* for ik. Thus a hiearchy of vertex subsets + * is found, with the entire graph G at its root. + * + * + * This function implements cohesive blocking and + * calculates the complete cohesive block hierarchy of a graph. + * + * + * 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. + * https://doi.org/10.2307/3088904 + * + * \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 + * list of integers vectors; the cohesive blocks will be stored here. + * Each block is encoded with a vector of type \ref igraph_vector_int_t 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 vector list. + * \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 vector list) 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 + */ + +igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, + igraph_vector_int_list_t *blocks, + igraph_vector_int_t *cohesion, + igraph_vector_int_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. + */ + + /* In theory, Q could be an igraph_graph_list_t; however, in that case + * we would not be able to pop off graphs from the front of the list as + * all elements of an igraph_graph_list_t are expected to be initialized, + * valid graphs. That's why we use an igraph_vector_ptr_t instead. */ + + igraph_vector_ptr_t Q; + igraph_vector_int_list_t Qmapping; + igraph_vector_int_t Qparent; + igraph_vector_int_t Qcohesion; + igraph_vector_bool_t Qcheck; + igraph_integer_t Qptr = 0; + igraph_integer_t conn; + igraph_bool_t is_simple; + + igraph_t *graph_copy; + + igraph_vector_int_list_t separators; + igraph_vector_int_t compvertices; + igraph_vector_int_t components; + igraph_vector_int_t newmapping; + igraph_vector_bool_t marked; + + igraph_vector_int_t compid; + igraph_dqueue_int_t bfsQ; + igraph_vector_int_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); + } + + if (blocks) { + igraph_vector_int_list_clear(blocks); + } + if (cohesion) { + igraph_vector_int_clear(cohesion); + } + if (parent) { + igraph_vector_int_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_VECTOR_INT_LIST_INIT_FINALLY(&Qmapping, 1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&Qparent, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Qcohesion, 1); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&Qcheck, 1); + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&separators, 0); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&compvertices, 0); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&marked, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&bfsQ, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &bfsQ); + IGRAPH_VECTOR_INT_INIT_FINALLY(&compid, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&components, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newmapping, 0); + + /* Put the input graph in the queue */ + graph_copy = IGRAPH_CALLOC(1, igraph_t); + IGRAPH_CHECK_OOM(graph_copy, "Insufficient memory for cohesive blocking."); + + IGRAPH_CHECK(igraph_copy(graph_copy, graph)); + VECTOR(Q)[0] = graph_copy; + VECTOR(Qparent)[0] = -1; /* Has no parent */ + IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /*checks=*/ true)); + VECTOR(Qcohesion)[0] = conn; + VECTOR(Qcheck)[0] = false; + + /* 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]; + igraph_integer_t mynodes = igraph_vcount(mygraph); + igraph_integer_t i, nsep; + igraph_integer_t no, kept = 0; + igraph_integer_t cptr = 0; + igraph_integer_t nsepv = 0; + igraph_bool_t addedsep = false; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Get the separators */ + IGRAPH_CHECK(igraph_minimum_size_separators(mygraph, &separators)); + nsep = igraph_vector_int_list_size(&separators); + + /* 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_int_t *v = igraph_vector_int_list_get_ptr(&separators, i); + igraph_integer_t j, n = igraph_vector_int_size(v); + for (j = 0; j < n; j++) { + igraph_integer_t vv = VECTOR(*v)[j]; + if (!VECTOR(marked)[vv]) { + nsepv++; + VECTOR(marked)[vv] = true; + } + } + } + + /* 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 = true; + for (i = 0; i < mynodes; i++) { + if (VECTOR(marked)[i]) { + IGRAPH_CHECK(igraph_vector_int_push_back(&components, i)); + } + } + IGRAPH_CHECK(igraph_vector_int_push_back(&components, -1)); + no++; + } + + for (i = 0; i < no; i++) { + igraph_t *newgraph; + igraph_integer_t maxdeg; + + igraph_vector_int_clear(&compvertices); + + while (true) { + igraph_integer_t v = VECTOR(components)[cptr++]; + if (v < 0) { + break; + } + IGRAPH_CHECK(igraph_vector_int_push_back(&compvertices, v)); + } + + newgraph = IGRAPH_CALLOC(1, igraph_t); + IGRAPH_CHECK_OOM(newgraph, "Insufficient memory for cohesive blocking."); + IGRAPH_FINALLY(igraph_free, newgraph); + + IGRAPH_CHECK(igraph_induced_subgraph_map(mygraph, newgraph, + igraph_vss_vector(&compvertices), + IGRAPH_SUBGRAPH_AUTO, + /*map=*/ NULL, + /*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_int_list_push_back_copy(&Qmapping, &newmapping)); + IGRAPH_CHECK(igraph_vertex_connectivity(newgraph, &newconn, + /*checks=*/ 1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&Qcohesion, newconn)); + IGRAPH_CHECK(igraph_vector_int_push_back(&Qparent, Qptr)); + IGRAPH_CHECK(igraph_vector_bool_push_back(&Qcheck, + mycheck || addedsep)); + } else { + igraph_destroy(newgraph); + igraph_free(newgraph); + IGRAPH_FINALLY_CLEAN(2); + } + } + + igraph_destroy(mygraph); + igraph_free(mygraph); + VECTOR(Q)[Qptr] = NULL; + + Qptr++; + } + + igraph_vector_int_destroy(&newmapping); + igraph_vector_int_destroy(&components); + igraph_vector_int_destroy(&compid); + igraph_dqueue_int_destroy(&bfsQ); + igraph_vector_int_destroy(&neis); + igraph_vector_bool_destroy(&marked); + igraph_vector_int_destroy(&compvertices); + igraph_vector_int_list_destroy(&separators); + IGRAPH_FINALLY_CLEAN(8); + + if (blocks || cohesion || parent || block_tree) { + igraph_integer_t noblocks = Qptr, badblocks = 0; + igraph_vector_bool_t removed; + igraph_integer_t i, resptr = 0; + igraph_vector_int_t rewritemap; + + IGRAPH_CHECK(igraph_vector_bool_init(&removed, noblocks)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed); + IGRAPH_CHECK(igraph_vector_int_init(&rewritemap, noblocks)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &rewritemap); + + for (i = 1; i < noblocks; i++) { + igraph_integer_t p = VECTOR(Qparent)[i]; + while (VECTOR(removed)[p]) { + p = VECTOR(Qparent)[p]; + } + if (VECTOR(Qcohesion)[p] >= VECTOR(Qcohesion)[i]) { + VECTOR(removed)[i] = true; + badblocks++; + } + } + + /* Rewrite the mappings */ + for (i = 1; i < Qptr; i++) { + igraph_integer_t j, n, p = VECTOR(Qparent)[i]; + igraph_vector_int_t *mapping, *pmapping; + + if (p == 0) { + continue; + } + + mapping = igraph_vector_int_list_get_ptr(&Qmapping, i); + pmapping = igraph_vector_int_list_get_ptr(&Qmapping, p); + + n = igraph_vector_int_size(mapping); + for (j = 0; j < n; j++) { + igraph_integer_t v = 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++) { + igraph_integer_t j, ic; + igraph_vector_int_t *ivec; + if (!VECTOR(Qcheck)[i] || VECTOR(removed)[i]) { + continue; + } + ivec = igraph_vector_int_list_get_ptr(&Qmapping, i); + ic = VECTOR(Qcohesion)[i]; + for (j = 1; j < noblocks; j++) { + igraph_vector_int_t *jvec; + igraph_integer_t jc; + if (j == i || !VECTOR(Qcheck)[j] || VECTOR(removed)[j]) { + continue; + } + jvec = igraph_vector_int_list_get_ptr(&Qmapping, j); + jc = VECTOR(Qcohesion)[j]; + if (igraph_i_cb_isin(ivec, jvec) && jc >= ic) { + badblocks++; + VECTOR(removed)[i] = true; + break; + } + } + } + + noblocks -= badblocks; + + if (blocks) { + IGRAPH_CHECK(igraph_vector_int_list_resize(blocks, noblocks)); + } + if (cohesion) { + IGRAPH_CHECK(igraph_vector_int_resize(cohesion, noblocks)); + } + if (parent) { + IGRAPH_CHECK(igraph_vector_int_resize(parent, noblocks)); + } + + for (i = 0; i < Qptr; i++) { + if (VECTOR(removed)[i]) { + continue; + } + VECTOR(rewritemap)[i] = resptr; + if (cohesion) { + VECTOR(*cohesion)[resptr] = VECTOR(Qcohesion)[i]; + } + if (parent || block_tree) { + igraph_integer_t 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) { + IGRAPH_CHECK( + igraph_vector_int_update( + igraph_vector_int_list_get_ptr(blocks, resptr), + igraph_vector_int_list_get_ptr(&Qmapping, i) + ) + ); + igraph_vector_int_clear(igraph_vector_int_list_get_ptr(&Qmapping, i)); + } + resptr++; + } + + /* Plus the original graph */ + if (blocks) { + igraph_integer_t num_vertices = igraph_vcount(graph); + igraph_vector_int_t *orig = igraph_vector_int_list_get_ptr(blocks, 0); + IGRAPH_CHECK(igraph_vector_int_resize(orig, num_vertices)); + for (i = 0; i < num_vertices; i++) { + VECTOR(*orig)[i] = i; + } + } + + if (block_tree) { + igraph_vector_int_t edges; + igraph_integer_t eptr = 0; + IGRAPH_VECTOR_INT_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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&rewritemap); + igraph_vector_bool_destroy(&removed); + IGRAPH_FINALLY_CLEAN(2); + + } + + igraph_vector_bool_destroy(&Qcheck); + igraph_vector_int_destroy(&Qcohesion); + igraph_vector_int_destroy(&Qparent); + igraph_vector_int_list_destroy(&Qmapping); + IGRAPH_FINALLY_CLEAN(4); + + igraph_vector_ptr_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); /* + the elements of Q, they were already destroyed */ + + return IGRAPH_SUCCESS; +} diff --git a/src/connectivity/components.c b/src/connectivity/components.c new file mode 100644 index 0000000..87832ec --- /dev/null +++ b/src/connectivity/components.c @@ -0,0 +1,1637 @@ +/* -*- 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_bitset.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.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" + +static igraph_error_t igraph_i_connected_components_weak( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no +); +static igraph_error_t igraph_i_connected_components_strong( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no +); + +/** + * \ingroup structural + * \function igraph_clusters + * \brief Calculates the (weakly or strongly) connected components in a graph (deprecated alias). + * + * \deprecated-by igraph_connected_components 0.10 + */ + +igraph_error_t igraph_clusters(const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no, + igraph_connectedness_t mode) { + return igraph_connected_components(graph, membership, csize, no, mode); +} + +/** + * \ingroup structural + * \function igraph_connected_components + * \brief Calculates the (weakly or strongly) connected components in a graph. + * + * When computing strongly connected components, the components will be + * indexed in topological order. In other words, vertex \c v is reachable + * from vertex \c u precisely when membership[u] <= membership[v]. + * + * \param graph The graph object to analyze. + * \param membership For every vertex the ID of its component is given. + * The vector has to be preinitialized and will be resized as needed. + * Alternatively this argument can be \c NULL, in which case it is ignored. + * \param csize For every component it gives its size, the order being defined + * by the component IDs. The vector must be preinitialized and will be + * resized as needed. 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 + * components will be stored here. + * \param mode For directed graph this specifies whether to calculate + * weakly or strongly connected components. Possible values: + * \clist + * \cli IGRAPH_WEAK + * Compute weakly connected components, i.e. ignore edge directions. + * \cli IGRAPH_STRONG + * Compute strongly connnected components, i.e. considr edge directions. + * \endclist + * This parameter is ignored for undirected graphs. + * \return Error code. + * + * Time complexity: O(|V|+|E|), where |V| and |E| are the number of vertices + * and edges in the graph. + * + * \example examples/simple/igraph_contract_vertices.c + */ + +igraph_error_t igraph_connected_components( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no, igraph_connectedness_t mode +) { + if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) { + return igraph_i_connected_components_weak(graph, membership, csize, no); + } else if (mode == IGRAPH_STRONG) { + return igraph_i_connected_components_strong(graph, membership, csize, no); + } + + IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); +} + +static igraph_error_t igraph_i_connected_components_weak( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_components; + igraph_bitset_t already_added; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; + + /* Memory for result, csize is dynamically allocated */ + if (membership) { + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + } + if (csize) { + igraph_vector_int_clear(csize); + } + + /* Try to make use of cached information. */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) && + igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED)) { + /* If we know that the graph is weakly connected from the cache, + * we can return the result right away. We keep in mind that + * the null graph is considered disconnected, therefore any connected + * graph has precisely one component. */ + if (membership) { + /* All vertices are members of the same component, + * component number 0. */ + igraph_vector_int_null(membership); + } + if (csize) { + /* The size of the single component is the same as the vertex count. */ + IGRAPH_CHECK(igraph_vector_int_push_back(csize, no_of_nodes)); + } + if (no) { + /* There is one component. */ + *no = 1; + } + return IGRAPH_SUCCESS; + } + + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + /* The algorithm */ + + no_of_components = 0; + for (igraph_integer_t first_node = 0; first_node < no_of_nodes; ++first_node) { + igraph_integer_t act_component_size; + + if (IGRAPH_BIT_TEST(already_added, first_node)) { + continue; + } + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_BIT_SET(already_added, first_node); + act_component_size = 1; + if (membership) { + VECTOR(*membership)[first_node] = no_of_components; + } + IGRAPH_CHECK(igraph_dqueue_int_push(&q, first_node)); + + while ( !igraph_dqueue_int_empty(&q) ) { + igraph_integer_t act_node = igraph_dqueue_int_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, act_node, IGRAPH_ALL)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); + for (igraph_integer_t i = 0; i < nei_count; i++) { + igraph_integer_t neighbor = VECTOR(neis)[i]; + if (IGRAPH_BIT_TEST(already_added, neighbor)) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_BIT_SET(already_added, neighbor); + act_component_size++; + if (membership) { + VECTOR(*membership)[neighbor] = no_of_components; + } + } + } + + no_of_components++; + if (csize) { + IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size)); + } + } + + /* Cleaning up */ + + if (no) { + *no = no_of_components; + } + + /* Clean up */ + igraph_bitset_destroy(&already_added); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); + + /* Update cache */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, no_of_components == 1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_connected_components_strong( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL; + igraph_integer_t num_seen; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + igraph_integer_t no_of_components = 0; + igraph_vector_int_t out = IGRAPH_VECTOR_NULL; + igraph_adjlist_t adjlist; + + /* Memory for result, csize is dynamically allocated */ + if (membership) { + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + } + if (csize) { + igraph_vector_int_clear(csize); + } + + /* Try to make use of cached information. */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED) && + igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED)) { + /* If we know that the graph is strongly connected from the cache, + * we can return the result right away. We keep in mind that + * the null graph is considered disconnected, therefore any connected + * graph has precisely one component. */ + if (membership) { + /* All vertices are members of the same component, + * component number 0. */ + igraph_vector_int_null(membership); + } + if (csize) { + /* The size of the single component is the same as the vertex count. */ + IGRAPH_CHECK(igraph_vector_int_push_back(csize, no_of_nodes)); + } + if (no) { + /* There is one component. */ + *no = 1; + } + return IGRAPH_SUCCESS; + } + + /* The result */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes)); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + num_seen = 0; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + const igraph_vector_int_t *tmp; + + IGRAPH_ALLOW_INTERRUPTION(); + + tmp = igraph_adjlist_get(&adjlist, i); + if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act_node = igraph_dqueue_int_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 */ + igraph_integer_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1]; + if (VECTOR(next_nei)[neighbor] == 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + } + VECTOR(next_nei)[act_node]++; + } else { + /* we've met this vertex and it has no more children */ + IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node)); + igraph_dqueue_int_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_int_null(&next_nei); /* mark already added vertices */ + num_seen = 0; + + while (!igraph_vector_int_empty(&out)) { + igraph_integer_t act_component_size; + igraph_integer_t grandfather = igraph_vector_int_pop_back(&out); + + if (VECTOR(next_nei)[grandfather] != 0) { + continue; + } + VECTOR(next_nei)[grandfather] = 1; + act_component_size = 1; + if (membership) { + VECTOR(*membership)[grandfather] = no_of_components; + } + IGRAPH_CHECK(igraph_dqueue_int_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_int_empty(&q)) { + igraph_integer_t act_node = igraph_dqueue_int_pop_back(&q); + const igraph_vector_int_t *tmp = igraph_adjlist_get(&adjlist, act_node); + const igraph_integer_t n = igraph_vector_int_size(tmp); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t neighbor = VECTOR(*tmp)[i]; + if (VECTOR(next_nei)[neighbor] != 0) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + VECTOR(next_nei)[neighbor] = 1; + act_component_size++; + if (membership) { + VECTOR(*membership)[neighbor] = no_of_components; + } + + 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_components++; + if (csize) { + IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size)); + } + } + + IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); + + if (no) { + *no = no_of_components; + } + + /* Clean up */ + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&out); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&next_nei); + IGRAPH_FINALLY_CLEAN(4); + + /* Update cache */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, no_of_components == 1); + if (no_of_components == 1) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_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 + * + * + * The return value of this function is cached in the graph itself, separately + * for weak and strong connectivity. Calling the function multiple times with + * no modifications to the graph in between will return a cached value in O(1) + * time. + * + * \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. + */ + +igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, + igraph_connectedness_t mode) { + + igraph_cached_property_t prop; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no; + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_WEAK; + } + + switch (mode) { + case IGRAPH_WEAK: + prop = IGRAPH_PROP_IS_WEAKLY_CONNECTED; + break; + + case IGRAPH_STRONG: + prop = IGRAPH_PROP_IS_STRONGLY_CONNECTED; + break; + + default: + IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); + } + + IGRAPH_RETURN_IF_CACHED_BOOL(graph, prop, res); + + 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 = false; + } else if (no_of_nodes == 1) { + *res = true; + } else if (mode == IGRAPH_WEAK) { + IGRAPH_CHECK(igraph_i_is_connected_weak(graph, res)); + } else { /* mode == IGRAPH_STRONG */ + /* 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 = false; + } else { + IGRAPH_CHECK(igraph_i_connected_components_strong(graph, NULL, NULL, &no)); + *res = (no == 1); + } + } + + /* Cache updates are done in igraph_i_connected_components_strong() and + * igraph_i_is_connected_weak() because those might be called from other + * places and we want to make use of the caching if so */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) { + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t added_count; + igraph_bitset_t already_added; + igraph_vector_int_t neis; + igraph_dqueue_int_t q; + + /* By convention, the null graph is not considered connected. + * See https://github.com/igraph/igraph/issues/1538 */ + if (no_of_nodes == 0) { + *res = false; + goto exit; + } + + /* A connected graph has at least |V| - 1 edges. */ + if (no_of_edges < no_of_nodes - 1) { + *res = false; + goto exit; + } + + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 10); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + /* Try to find at least two components */ + IGRAPH_BIT_SET(already_added, 0); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + + added_count = 1; + while (! igraph_dqueue_int_empty(&q)) { + IGRAPH_ALLOW_INTERRUPTION(); + + const igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_ALL)); + const igraph_integer_t nei_count = igraph_vector_int_size(&neis); + + for (igraph_integer_t i = 0; i < nei_count; i++) { + const igraph_integer_t neighbor = VECTOR(neis)[i]; + if (IGRAPH_BIT_TEST(already_added, neighbor)) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + added_count++; + IGRAPH_BIT_SET(already_added, neighbor); + + if (added_count == no_of_nodes) { + /* We have already reached all nodes: the graph is connected. + * We can stop the traversal now. */ + goto done; + } + } + } + +done: + /* Connected? */ + *res = (added_count == no_of_nodes); + + igraph_bitset_destroy(&already_added); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); + +exit: + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, *res); + if (igraph_is_directed(graph) && !(*res)) { + /* If the graph is not weakly connected, it is not strongly connected + * either so we can also cache that */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, *res); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_decompose_destroy + * \brief Frees the contents of a pointer vector holding graphs. + * + * This function destroys and frees all igraph_t + * objects held in \p complist. However, it does not destroy + * \p complist itself. Use \ref igraph_vector_ptr_destroy() to destroy + * \p complist. + * + * \param complist The list of graphs to destroy. + * + * Time complexity: O(n), n is the number of items. + * + * \deprecated 0.10.0 + */ + +void igraph_decompose_destroy(igraph_vector_ptr_t *complist) { + igraph_integer_t i, n; + + n = igraph_vector_ptr_size(complist); + for (i = 0; i < n; i++) { + if (VECTOR(*complist)[i] != 0) { + igraph_destroy(VECTOR(*complist)[i]); + IGRAPH_FREE(VECTOR(*complist)[i]); + } + } +} + +static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, + igraph_graph_list_t *components, + igraph_integer_t maxcompno, igraph_integer_t minelements); + +static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, + igraph_graph_list_t *components, + igraph_integer_t maxcompno, igraph_integer_t minelements); + +/** + * \function igraph_decompose + * \brief Decomposes a graph into connected components. + * + * Creates a 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 when there is only a single component in the original graph. + * + * \param graph The original graph. + * \param components This list of graphs will contain the individual components. + * It should be initialized before calling this function and will be resized + * to hold the graphs. + * \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 + */ + +igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *components, + igraph_connectedness_t mode, + igraph_integer_t maxcompno, igraph_integer_t 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 igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, + igraph_graph_list_t *components, + igraph_integer_t maxcompno, igraph_integer_t minelements) { + + igraph_integer_t actstart; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t resco = 0; /* number of graphs created so far */ + igraph_bitset_t already_added; + igraph_dqueue_int_t q; + igraph_vector_int_t verts; + igraph_vector_int_t neis; + igraph_vector_int_t vids_old2new; + igraph_integer_t i; + igraph_t newg; + + + if (maxcompno < 0) { + maxcompno = IGRAPH_INTEGER_MAX; + } + + igraph_graph_list_clear(components); + + /* already_added keeps track of what nodes made it into a graph already */ + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_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 (IGRAPH_BIT_TEST(already_added, actstart)) { + continue; + } + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_vector_int_clear(&verts); + + /* add the node itself */ + IGRAPH_BIT_SET(already_added, actstart); + IGRAPH_CHECK(igraph_vector_int_push_back(&verts, actstart)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actstart)); + + /* add the neighbors, recursively */ + while (!igraph_dqueue_int_empty(&q) ) { + /* pop from the queue of this component */ + igraph_integer_t actvert = igraph_dqueue_int_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actvert, IGRAPH_ALL)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); + /* iterate over the neighbors */ + for (i = 0; i < nei_count; i++) { + igraph_integer_t neighbor = VECTOR(neis)[i]; + if (IGRAPH_BIT_TEST(already_added, neighbor)) { + continue; + } + /* add neighbor */ + IGRAPH_BIT_SET(already_added, neighbor); + + /* recursion: append neighbor to the queues */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor)); + } + } + + /* ok, we have a component */ + if (igraph_vector_int_size(&verts) < minelements) { + continue; + } + + IGRAPH_CHECK(igraph_i_induced_subgraph_map( + graph, &newg, igraph_vss_vector(&verts), + IGRAPH_SUBGRAPH_AUTO, &vids_old2new, + /* invmap = */ 0, /* map_is_prepared = */ 1 + )); + IGRAPH_FINALLY(igraph_destroy, &newg); + IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg)); + IGRAPH_FINALLY_CLEAN(1); /* ownership of newg now taken by 'components' */ + 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_int_destroy(&vids_old2new); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&verts); + igraph_dqueue_int_destroy(&q); + igraph_bitset_destroy(&already_added); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, + igraph_graph_list_t *components, + igraph_integer_t maxcompno, igraph_integer_t minelements) { + + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + /* this is a heap used twice for checking what nodes have + * been counted already */ + igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL; + + igraph_integer_t i, n, num_seen; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + + igraph_integer_t no_of_components = 0; + + igraph_vector_int_t out = IGRAPH_VECTOR_NULL; + const igraph_vector_int_t* tmp; + + igraph_adjlist_t adjlist; + igraph_vector_int_t verts; + igraph_vector_int_t vids_old2new; + igraph_t newg; + + if (maxcompno < 0) { + maxcompno = IGRAPH_INTEGER_MAX; + } + + igraph_graph_list_clear(components); + + /* The result */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes)); + + igraph_vector_int_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_int_push(&q, i)); + + /* consume the tree from this node ("root") recursively + * until there is no more */ + while (!igraph_dqueue_int_empty(&q)) { + /* this looks up but does NOT consume the queue */ + igraph_integer_t act_node = igraph_dqueue_int_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 */ + igraph_integer_t neighbor = VECTOR(*tmp)[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_int_push(&q, neighbor)); + } + VECTOR(next_nei)[act_node]++; + } else { + /* we've met this vertex and it has no more children */ + IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node)); + /* this consumes the queue, since there's nowhere to go */ + igraph_dqueue_int_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_int_null(&next_nei); /* mark already added vertices */ + + /* number of components built */ + num_seen = 0; + while (!igraph_vector_int_empty(&out) && no_of_components < maxcompno) { + /* consume the vector from the last element */ + igraph_integer_t grandfather = igraph_vector_int_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_int_clear(&verts); + + /* this node is gone for any future components */ + VECTOR(next_nei)[grandfather] = 1; + + /* add to component */ + IGRAPH_CHECK(igraph_vector_int_push_back(&verts, grandfather)); + IGRAPH_CHECK(igraph_dqueue_int_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_int_empty(&q)) { + /* consume the queue from this node */ + igraph_integer_t act_node = igraph_dqueue_int_pop_back(&q); + tmp = igraph_adjlist_get(&adjlist, act_node); + n = igraph_vector_int_size(tmp); + for (i = 0; i < n; i++) { + igraph_integer_t neighbor = VECTOR(*tmp)[i]; + if (VECTOR(next_nei)[neighbor] != 0) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + VECTOR(next_nei)[neighbor] = 1; + + /* add to component */ + IGRAPH_CHECK(igraph_vector_int_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_int_size(&verts) < minelements) { + continue; + } + + IGRAPH_CHECK(igraph_i_induced_subgraph_map( + graph, &newg, igraph_vss_vector(&verts), + IGRAPH_SUBGRAPH_AUTO, &vids_old2new, + /* invmap = */ 0, /* map_is_prepared = */ 1 + )); + IGRAPH_FINALLY(igraph_destroy, &newg); + IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg)); + IGRAPH_FINALLY_CLEAN(1); /* ownership of newg now taken by 'components' */ + + /* vids_old2new has to be cleaned up here because a vertex may appear + * in multiple strongly connected components. Simply calling + * igraph_vector_int_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_int_size(&verts); + for (i = 0; i < n; i++) { + VECTOR(vids_old2new)[VECTOR(verts)[i]] = 0; + } + + no_of_components++; + } + + IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); + + /* Clean up, return */ + + igraph_vector_int_destroy(&vids_old2new); + igraph_vector_int_destroy(&verts); + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&out); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&next_nei); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; + +} + +/** + * \function igraph_articulation_points + * \brief Finds the articulation points in a graph. + * + * A vertex is an articulation point if its removal increases + * the number of (weakly) connected components in the graph. + * + * + * Note that a graph without any articulation points is not necessarily + * biconnected. Counterexamples are the two-vertex complete graph as well + * as empty graphs. Use \ref igraph_is_biconnected() to check whether + * a graph is biconnected. + * + * \param graph The input graph. It will be treated as undirected. + * \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_is_bipartite(), + * \ref igraph_connected_components(), \ref igraph_bridges() + */ + +igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_int_t *res) { + + return igraph_biconnected_components(graph, NULL, NULL, NULL, NULL, res); +} + +/** + * \function igraph_biconnected_components + * \brief Calculates 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 a 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, while isolated vertices are part of none at all. + * + * + * Note that some authors do not consider the graph consisting of + * two connected vertices as biconnected, however, igraph does. + * + * + * 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. This means that checking whether there is a single + * biconnected component is not sufficient for determining if a graph is + * biconnected. Use \ref igraph_is_biconnected() for this purpose. + * + * \param graph The input graph. It will be treated as undirected. + * \param no If not a \c NULL pointer, the number of biconnected components will + * be stored here. + * \param tree_edges If not a \c 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. + * \param component_edges If not a \c 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 \c 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 \p components and + * \p component_edges. If you calculate \p components, then it is + * quadratic in the number of vertices. If you calculate + * \p component_edges as well, then it is cubic in the number of + * vertices. + * + * \sa \ref igraph_articulation_points(), \ref igraph_is_biconnected(), + * \ref igraph_connected_components(). + * + * \example examples/simple/igraph_biconnected_components.c + */ + +igraph_error_t igraph_biconnected_components(const igraph_t *graph, + igraph_integer_t *no, + igraph_vector_int_list_t *tree_edges, + igraph_vector_int_list_t *component_edges, + igraph_vector_int_list_t *components, + igraph_vector_int_t *articulation_points) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t nextptr; + igraph_vector_int_t num, low; + igraph_bitset_t found; + igraph_vector_int_t *adjedges; + igraph_stack_int_t path; + igraph_stack_int_t edgestack; + igraph_inclist_t inclist; + igraph_integer_t counter, rootdfs = 0; + igraph_vector_int_t vertex_added; + igraph_integer_t comps = 0; + igraph_vector_int_list_t *mycomponents = components, vcomponents; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&found, no_of_nodes); + + IGRAPH_STACK_INT_INIT_FINALLY(&path, 100); + IGRAPH_STACK_INT_INIT_FINALLY(&edgestack, 100); + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_added, no_of_nodes); + + if (no) { + *no = 0; + } + if (tree_edges) { + igraph_vector_int_list_clear(tree_edges); + } + if (components) { + igraph_vector_int_list_clear(components); + } + if (component_edges) { + igraph_vector_int_list_clear(component_edges); + } + if (articulation_points) { + igraph_vector_int_clear(articulation_points); + } + if (component_edges && !components) { + mycomponents = &vcomponents; + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(mycomponents, 0); + } + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + + if (VECTOR(low)[i] != 0) { + continue; /* already visited */ + } + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_stack_int_push(&path, i)); + counter = 1; + rootdfs = 0; + VECTOR(low)[i] = VECTOR(num)[i] = counter++; + while (!igraph_stack_int_empty(&path)) { + igraph_integer_t n; + igraph_integer_t act = igraph_stack_int_top(&path); + igraph_integer_t actnext = VECTOR(nextptr)[act]; + + adjedges = igraph_inclist_get(&inclist, act); + n = igraph_vector_int_size(adjedges); + if (actnext < n) { + /* Step down (maybe) */ + igraph_integer_t edge = VECTOR(*adjedges)[actnext]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, act); + if (VECTOR(low)[nei] == 0) { + if (act == i) { + rootdfs++; + } + IGRAPH_CHECK(igraph_stack_int_push(&edgestack, edge)); + IGRAPH_CHECK(igraph_stack_int_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_int_pop(&path); + if (!igraph_stack_int_empty(&path)) { + igraph_integer_t prev = igraph_stack_int_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 && !IGRAPH_BIT_TEST(found, prev) + && prev != i /* the root */) { + IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, prev)); + IGRAPH_BIT_SET(found, prev); + } + if (no) { + *no += 1; + } + + /*------------------------------------*/ + /* Record the biconnected component just found */ + if (tree_edges || mycomponents) { + igraph_vector_int_t *v, *v2; + comps++; + if (tree_edges) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(tree_edges, &v)); + } + if (mycomponents) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(mycomponents, &v2)); + } + + while (!igraph_stack_int_empty(&edgestack)) { + igraph_integer_t e = igraph_stack_int_pop(&edgestack); + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + if (tree_edges) { + IGRAPH_CHECK(igraph_vector_int_push_back(v, e)); + } + if (mycomponents) { + if (VECTOR(vertex_added)[from] != comps) { + VECTOR(vertex_added)[from] = comps; + IGRAPH_CHECK(igraph_vector_int_push_back(v2, from)); + } + if (VECTOR(vertex_added)[to] != comps) { + VECTOR(vertex_added)[to] = comps; + IGRAPH_CHECK(igraph_vector_int_push_back(v2, to)); + } + } + if (from == prev || to == prev) { + break; + } + } + + if (component_edges) { + igraph_vector_int_t *nodes = igraph_vector_int_list_get_ptr(mycomponents, comps - 1); + igraph_integer_t ii, no_vert = igraph_vector_int_size(nodes); + igraph_vector_int_t *vv; + + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(component_edges, &vv)); + for (ii = 0; ii < no_vert; ii++) { + igraph_integer_t vert = VECTOR(*nodes)[ii]; + igraph_vector_int_t *edges = igraph_inclist_get(&inclist, + vert); + igraph_integer_t j, nn = igraph_vector_int_size(edges); + for (j = 0; j < nn; j++) { + igraph_integer_t e = VECTOR(*edges)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, e, vert); + if (VECTOR(vertex_added)[nei] == comps && nei < vert) { + IGRAPH_CHECK(igraph_vector_int_push_back(vv, e)); + } + } + } + } + } /* record component if requested */ + /*------------------------------------*/ + + } + } /* !igraph_stack_int_empty(&path) */ + } + + } /* !igraph_stack_int_empty(&path) */ + + if (articulation_points && rootdfs >= 2) { + IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, i)); + } + + } /* i < no_of_nodes */ + + if (mycomponents != components) { + igraph_vector_int_list_destroy(mycomponents); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&vertex_added); + igraph_inclist_destroy(&inclist); + igraph_stack_int_destroy(&edgestack); + igraph_stack_int_destroy(&path); + igraph_bitset_destroy(&found); + igraph_vector_int_destroy(&low); + igraph_vector_int_destroy(&num); + igraph_vector_int_destroy(&nextptr); + IGRAPH_FINALLY_CLEAN(8); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_biconnected + * \brief Checks whether a graph is biconnected. + * + * \experimental + * + * A graph is biconnected if the removal of any single vertex (and + * its incident edges) does not disconnect it. + * + * + * igraph does not consider single-vertex graphs biconnected. + * + * + * Note that some authors do not consider the graph consisting of + * two connected vertices as biconnected, however, igraph does. + * + * \param graph The input graph. It will be treated as undirected. + * \param result If not a \c NULL pointer, the result will be returned here. + * \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(). + * + * \example examples/simple/igraph_is_biconnected.c + */ + +igraph_error_t igraph_is_biconnected(const igraph_t *graph, igraph_bool_t *res) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t nextptr; + igraph_vector_int_t num, low; + igraph_stack_int_t path; + igraph_lazy_adjlist_t inclist; + igraph_bool_t is_biconnected = true; + + if (no_of_nodes == 0 || no_of_nodes == 1) { + /* The null graph is not connected, hence it is not biconnected either. + * The singleton graph is not biconnected. */ + is_biconnected = false; + goto exit2; + } + + /* no_of_nodes == 2 is special: if the two nodes are connected, then the + * graph is both biconnected _and_ acyclic, unlike no_of_nodes >= 3, where + * the graph is not acyclic if it is biconnected. */ + + /* We do not touch the cache for graphs with less than three nodes because + * of all the edge cases. */ + if (no_of_nodes >= 3 && ( + (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED)) || + (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) && + igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST)) + )) { + is_biconnected = false; + goto exit2; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); + + IGRAPH_STACK_INT_INIT_FINALLY(&path, 100); + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inclist); + + const igraph_integer_t root = 0; /* start DFS from vertex 0 */ + igraph_integer_t counter = 1; + igraph_integer_t rootdfs = 0; + IGRAPH_CHECK(igraph_stack_int_push(&path, root)); + VECTOR(low)[root] = VECTOR(num)[root] = counter++; + while (!igraph_stack_int_empty(&path)) { + igraph_integer_t act = igraph_stack_int_top(&path); + igraph_integer_t actnext = VECTOR(nextptr)[act]; + + const igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&inclist, act); + const igraph_integer_t n = igraph_vector_int_size(neis); + if (actnext < n) { + /* Step down (maybe) */ + igraph_integer_t nei = VECTOR(*neis)[actnext]; + if (VECTOR(low)[nei] == 0) { + if (act == root) { + rootdfs++; + } + IGRAPH_CHECK(igraph_stack_int_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_int_pop(&path); + if (!igraph_stack_int_empty(&path)) { + igraph_integer_t prev = igraph_stack_int_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 (prev != root /* the root */) { + /* Found an articulation point, the graph is not biconnected */ + is_biconnected = false; + goto exit; + } + } + } /* !igraph_stack_int_empty(&path) */ + } + + } /* !igraph_stack_int_empty(&path) */ + + /* The root is an articulation point, the graph is not biconnected */ + if (rootdfs >= 2) { + is_biconnected = false; + goto exit; + } + + /* We did not reach all vertices, the graph is not connected */ + if (counter <= no_of_nodes) { + is_biconnected = false; + goto exit; + } + +exit: + + igraph_lazy_adjlist_destroy(&inclist); + igraph_stack_int_destroy(&path); + igraph_vector_int_destroy(&low); + igraph_vector_int_destroy(&num); + igraph_vector_int_destroy(&nextptr); + IGRAPH_FINALLY_CLEAN(5); + +exit2: + + if (res) { + *res = is_biconnected; + } + + /* We do not touch the cache for graphs with less than three nodes because + * of all the edge cases. */ + if (is_biconnected && no_of_nodes >= 3) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true); + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, false); + } + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_bridges + * \brief Finds 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. It will be treated as undirected. + * \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_connected_components() + */ + +igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridges) { + + /* 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. + Additionally, we use explicit stacks instead of recursion to avoid + stack overflow. */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_inclist_t il; + igraph_bitset_t visited; + igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */ + igraph_vector_int_t low; /* low[u] is the lowest visit time of vertices reachable from u */ + igraph_vector_int_t incoming_edge; + igraph_stack_int_t su, si; + igraph_integer_t time; + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + + IGRAPH_BITSET_INIT_FINALLY(&visited, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&incoming_edge, no_of_nodes); + igraph_vector_int_fill(&incoming_edge, -1); + + IGRAPH_STACK_INT_INIT_FINALLY(&su, 0); + IGRAPH_STACK_INT_INIT_FINALLY(&si, 0); + + igraph_vector_int_clear(bridges); + + time = 0; + for (igraph_integer_t start = 0; start < no_of_nodes; ++start) { + if (! IGRAPH_BIT_TEST(visited, start)) { + /* Perform a DFS from 'start'. + * The top of the su stack is u, the vertex currently being visited. + * The top of the si stack is i, the index of u's neighbour that will + * be processed next. */ + + IGRAPH_CHECK(igraph_stack_int_push(&su, start)); + IGRAPH_CHECK(igraph_stack_int_push(&si, 0)); + + while (! igraph_stack_int_empty(&su)) { + igraph_integer_t u = igraph_stack_int_pop(&su); + igraph_integer_t i = igraph_stack_int_pop(&si); + + if (i == 0) { + /* We are at the first step of visiting vertex u. */ + + IGRAPH_BIT_SET(visited, u); + + time += 1; + + VECTOR(vis)[u] = time; + VECTOR(low)[u] = time; + } + + igraph_vector_int_t *incedges = igraph_inclist_get(&il, u); + + if (i < igraph_vector_int_size(incedges)) { + IGRAPH_CHECK(igraph_stack_int_push(&su, u)); + IGRAPH_CHECK(igraph_stack_int_push(&si, i+1)); + + igraph_integer_t edge = VECTOR(*incedges)[i]; + igraph_integer_t v = IGRAPH_OTHER(graph, edge, u); + + if (! IGRAPH_BIT_TEST(visited, v)) { + VECTOR(incoming_edge)[v] = edge; + + IGRAPH_CHECK(igraph_stack_int_push(&su, v)); + IGRAPH_CHECK(igraph_stack_int_push(&si, 0)); + } else if (edge != VECTOR(incoming_edge)[u]) { + VECTOR(low)[u] = VECTOR(low)[u] < VECTOR(vis)[v] ? VECTOR(low)[u] : VECTOR(vis)[v]; + } + } else { + /* We are done visiting vertex u, so it won't be put back on the stack. + * We are ready to update the 'low' value of its parent w, and decide + * whether its incoming edge is a bridge. */ + + igraph_integer_t edge = VECTOR(incoming_edge)[u]; + if (edge >= 0) { + igraph_integer_t w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */ + VECTOR(low)[w] = VECTOR(low)[w] < VECTOR(low)[u] ? VECTOR(low)[w] : VECTOR(low)[u]; + if (VECTOR(low)[u] > VECTOR(vis)[w]) { + IGRAPH_CHECK(igraph_vector_int_push_back(bridges, edge)); + } + } + } + } + } + } + + igraph_stack_int_destroy(&si); + igraph_stack_int_destroy(&su); + igraph_vector_int_destroy(&incoming_edge); + igraph_vector_int_destroy(&low); + igraph_vector_int_destroy(&vis); + igraph_bitset_destroy(&visited); + igraph_inclist_destroy(&il); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_subcomponent + * \brief The vertices reachable from a given vertex. + * + * This function returns the set of vertices reachable from a specified + * vertex. In undirected graphs, this is simple the set of vertices within + * the same component. + * + * \param graph The graph object. + * \param res The result, vector with the IDs of the vertices reachable + * from \p vertex. + * \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; + * \ref igraph_reachability() to efficiently compute the reachable set from \em all + * vertices. + */ +igraph_error_t igraph_subcomponent( + const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vertex, + igraph_neimode_t mode +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + igraph_bitset_t already_added; + igraph_integer_t i, vsize; + igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; + + if (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); + } + + igraph_vector_int_clear(res); + + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_dqueue_int_push(&q, vertex)); + IGRAPH_CHECK(igraph_vector_int_push_back(res, vertex)); + IGRAPH_BIT_SET(already_added, vertex); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, mode)); + vsize = igraph_vector_int_size(&tmp); + for (i = 0; i < vsize; i++) { + igraph_integer_t neighbor = VECTOR(tmp)[i]; + + if (IGRAPH_BIT_TEST(already_added, neighbor)) { + continue; + } + IGRAPH_BIT_SET(already_added, neighbor); + IGRAPH_CHECK(igraph_vector_int_push_back(res, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + } + } + + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&tmp); + igraph_bitset_destroy(&already_added); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/connectivity/reachability.c b/src/connectivity/reachability.c new file mode 100644 index 0000000..1b5560f --- /dev/null +++ b/src/connectivity/reachability.c @@ -0,0 +1,263 @@ +/* + IGraph library. + Copyright (C) 2024 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_reachability.h" + +#include "igraph_adjlist.h" +#include "igraph_bitset_list.h" +#include "igraph_components.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" + + +/** + * \ingroup structural + * \function igraph_reachability + * \brief Calculates which vertices are reachable from each vertex in the graph. + * + * \experimental + * + * The resulting list will contain one bitset for each strongly connected component. + * The bitset for component i will have its j-th bit set, if vertex j is reachable + * from some vertex in component i in 0 or more steps. + * In particular, a vertex is always reachable from itself. + * + * \param graph The graph object to analyze. + * \param membership Pointer to an integer vector. For every vertex, + * the ID of its component is given. The vector will be resized as needed. + * This parameter must not be \c NULL. + * \param csize Pointer to an integer vector or \c NULL. For every component, it + * gives its size (vertex count), the order being defined by the component + * IDs. The vector will be resized as needed. + * \param no_of_components Pointer to an integer or \c NULL. The number of + * components will be stored here. + * \param reach A list of bitsets representing the result. It will be resized + * as needed. reach[membership[u]][v] is set to \c true if + * vertex \c v is reachable from vertex \c u. + * \param mode In directed graphs, controls the treatment of edge directions. + * Ignored in undirected graphs. With \c IGRAPH_OUT, reachability is computed + * by traversing edges along their direction. With \c IGRAPH_IN, edges are + * traversed opposite to their direction. With \c IGRAPH_ALL, edge directions + * are ignored and the graph is treated as undirected. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory + * to perform the operation. + * + * \sa \ref igraph_connected_components() to find the connnected components + * of a graph; \ref igraph_count_reachable() to count how many vertices + * are reachable from each vertex; \ref igraph_subcomponent() to find + * which vertices are rechable from a single vertex. + * + * Time complexity: O(|C||V|/w + |V| + |E|), where + * |C| is the number of strongly connected components (at most |V|), + * |V| is the number of vertices, and + * |E| is the number of edges respectively, + * and w is the bit width of \type igraph_integer_t, typically the + * word size of the machine (32 or 64). + */ + +igraph_error_t igraph_reachability( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize, + igraph_integer_t *no_of_components, + igraph_bitset_list_t *reach, + igraph_neimode_t mode) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_comps; + igraph_adjlist_t adjlist, dag; + + if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) { + IGRAPH_ERROR("Invalid mode for reachability.", IGRAPH_EINVMODE); + } + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_CHECK(igraph_connected_components(graph, + membership, csize, &no_of_comps, + mode == IGRAPH_ALL ? IGRAPH_WEAK : IGRAPH_STRONG)); + + if (no_of_components) { + *no_of_components = no_of_comps; + } + + IGRAPH_CHECK(igraph_bitset_list_resize(reach, no_of_comps)); + + for (igraph_integer_t comp = 0; comp < no_of_comps; comp++) { + IGRAPH_CHECK(igraph_bitset_resize(igraph_bitset_list_get_ptr(reach, comp), no_of_nodes)); + } + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + IGRAPH_BIT_SET(*igraph_bitset_list_get_ptr(reach, VECTOR(*membership)[v]), v); + } + + if (mode == IGRAPH_ALL) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_adjlist_init_empty(&dag, no_of_comps)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &dag); + + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + const igraph_vector_int_t *neighbours = igraph_adjlist_get(&adjlist, v); + igraph_vector_int_t *dag_neighbours = igraph_adjlist_get(&dag, VECTOR(*membership)[v]); + const igraph_integer_t n = igraph_vector_int_size(neighbours); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t w = VECTOR(*neighbours)[i]; + if (VECTOR(*membership)[v] != VECTOR(*membership)[w]) { + IGRAPH_CHECK(igraph_vector_int_push_back(dag_neighbours, VECTOR(*membership)[w])); + } + } + } + + /* Iterate through strongly connected components in reverser topological order, + * exploiting the fact that they are indexed in topological order. */ + for (igraph_integer_t i = 0; i < no_of_comps; i++) { + const igraph_integer_t comp = mode == IGRAPH_IN ? i : no_of_comps - i - 1; + const igraph_vector_int_t *dag_neighbours = igraph_adjlist_get(&dag, comp); + igraph_bitset_t *from_bitset = igraph_bitset_list_get_ptr(reach, comp); + const igraph_integer_t n = igraph_vector_int_size(dag_neighbours); + for (igraph_integer_t j = 0; j < n; j++) { + const igraph_bitset_t *to_bitset = igraph_bitset_list_get_ptr(reach, VECTOR(*dag_neighbours)[j]); + igraph_bitset_or(from_bitset, from_bitset, to_bitset); + } + } + + igraph_adjlist_destroy(&adjlist); + igraph_adjlist_destroy(&dag); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_count_reachable + * \brief The number of vertices reachable from each vertex in the graph. + * + * \experimental + * + * \param graph The graph object to analyze. + * \param counts Integer vector. counts[v] will store the number + * of vertices reachable from vertex \c v, including \c v itself. + * \param mode In directed graphs, controls the treatment of edge directions. + * Ignored in undirected graphs. With \c IGRAPH_OUT, reachability is computed + * by traversing edges along their direction. With \c IGRAPH_IN, edges are + * traversed opposite to their direction. With \c IGRAPH_ALL, edge directions + * are ignored and the graph is treated as undirected. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory + * to perform the operation. + * + * \sa \ref igraph_connected_components(), \ref igraph_transitive_closure() + * + * Time complexity: O(|C||V|/w + |V| + |E|), where + * |C| is the number of strongly connected components (at most |V|), + * |V| is the number of vertices, and + * |E| is the number of edges respectively, + * and w is the bit width of \type igraph_integer_t, typically the + * word size of the machine (32 or 64). + */ + +igraph_error_t igraph_count_reachable(const igraph_t *graph, + igraph_vector_int_t *counts, + igraph_neimode_t mode) { + + igraph_vector_int_t membership; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_bitset_list_t reach; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); + IGRAPH_BITSET_LIST_INIT_FINALLY(&reach, 0); + + IGRAPH_CHECK(igraph_reachability(graph, &membership, NULL, NULL, &reach, mode)); + + IGRAPH_CHECK(igraph_vector_int_resize(counts, igraph_vcount(graph))); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + VECTOR(*counts)[i] = igraph_bitset_popcount(igraph_bitset_list_get_ptr(&reach, VECTOR(membership)[i])); + } + + igraph_bitset_list_destroy(&reach); + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_transitive_closure + * \brief Computes the transitive closure of a graph. + * + * \experimental + * + * The resulting graph will have an edge from vertex \c i to vertex \c j + * if \c j is reachable from \c i. + * + * \param graph The graph object to analyze. + * \param closure The resulting graph representing the transitive closure. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory + * to perform the operation. + * + * \sa \ref igraph_connected_components(), \ref igraph_count_reachable() + * + * Time complexity: O(|V|^2 + |E|), where + * |V| is the number of vertices, and + * |E| is the number of edges, respectively. + */ +igraph_error_t igraph_transitive_closure(const igraph_t *graph, igraph_t *closure) { + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_bool_t directed = igraph_is_directed(graph); + igraph_vector_int_t membership, edges; + igraph_bitset_list_t reach; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); + IGRAPH_BITSET_LIST_INIT_FINALLY(&reach, 0); + + IGRAPH_CHECK(igraph_reachability(graph, &membership, NULL, NULL, &reach, IGRAPH_OUT)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + for (igraph_integer_t u = 0; u < no_of_nodes; u++) { + const igraph_bitset_t *row = igraph_bitset_list_get_ptr(&reach, VECTOR(membership)[u]); + for (igraph_integer_t v = directed ? 0 : u + 1; v < no_of_nodes; v++) { + if (u != v && IGRAPH_BIT_TEST(*row, v)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); + } + } + } + + igraph_bitset_list_destroy(&reach); + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(closure, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/connectivity/separators.c b/src/connectivity/separators.c new file mode 100644 index 0000000..ece42ec --- /dev/null +++ b/src/connectivity/separators.c @@ -0,0 +1,885 @@ +/* -*- 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_operators.h" +#include "igraph_structural.h" +#include "igraph_vector.h" + +#include "core/interruption.h" + + +/** + * \function igraph_i_is_separator + * + * Checks if vertex set \c S is a separator. Optionally, also checks if it + * is a _minimal_ separator. + * + * \param graph The graph, edge directions are ignored. + * \param S Candidate vertex set. + * \param is_separator Pointer to boolean, is S a separator? + * \param is_minimal If not \c NULL, it is also checked whether the separator + * is minimal. This takes additional time. If S is not a separator, this is + * set to false + */ +static igraph_error_t igraph_i_is_separator( + const igraph_t *graph, + igraph_vs_t S, + igraph_bool_t *is_separator, + igraph_bool_t *is_minimal +) { + + const igraph_integer_t vcount = igraph_vcount(graph); + + /* mark[v] means: + * + * bit 0: Was v visited? + * bit 1: Is v in S? + * bit 2: Used to keep track of which vertices were reachable in S + * from its neighbourhood in the minimal separator test. + */ + igraph_vector_char_t mark; + + igraph_vector_int_t neis; + igraph_dqueue_int_t Q; + igraph_vit_t vit; + igraph_integer_t S_size = 0, S_visited_count = 0; + + IGRAPH_CHECK(igraph_vit_create(graph, S, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&mark, vcount); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 10); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); + +#define VISITED(x) (VECTOR(mark)[x] & 1) /* Was x visited? */ +#define SET_VISITED(x) (VECTOR(mark)[x] |= 1) /* Mark x as visited. */ +#define IN_S(x) (VECTOR(mark)[x] & 2) /* Is x in S? */ +#define SET_IN_S(x) (VECTOR(mark)[x] |= 2) /* Mark x as being in S. */ + + /* Mark and count vertices in S, taking care not to double-count + * when duplicate vertices were passed in. */ + for (; ! IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + const igraph_integer_t u = IGRAPH_VIT_GET(vit); + if (! IN_S(u)) { + SET_IN_S(u); + S_size++; + } + } + + /* If S contains more than |V| - 2 vertices, it is not a separator. */ + if (S_size > vcount-2) { + *is_separator = false; + goto done; + } + + /* Assume that S is not a separator until proven otherwise. */ + *is_separator = false; + + for (IGRAPH_VIT_RESET(vit); ! IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + + const igraph_integer_t u = IGRAPH_VIT_GET(vit); + if (VISITED(u)) { + continue; + } + + /* For the sake of the following discussion, and counting of degrees, + * let us consider each undirected edge as a pair of directed ones. + */ + + /* We start at a vertex in S, and traverse the graph with the following + * restriction: + * + * Once we exited S through an edge, we may not exit through + * any other edge again (but we may re-enter S). With this traversal, + * we can determine: once we find ourselves outside of S, are there + * any vertices which we can only reach by passing through S again? + * + * Theorem: The visited vertices in S constitute a separating set if and + * only if when the traversal is complete, some of them still have + * untraversed out-edges. + * + * Note that this refers only to the subset of S visited during this + * traversal, not the rest of S. If there are remaining vertices in S, + * they may constitute a separating set as well. We cannot conclude that + * S is NOT a separator until all its vertices have been considered, hence + * the outer for-loop. + */ + + /* Sum of in-degrees of visited vertices in S. */ + igraph_integer_t degsum = 0; + + /* Number of in-edges of vertices in S that were traversed. */ + igraph_integer_t edgecount = 0; + + /* Have we already traversed an edge leaving S? */ + igraph_bool_t exited = false; + + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, u)); + + while (! igraph_dqueue_int_empty(&Q)) { + const igraph_integer_t v = igraph_dqueue_int_pop(&Q); + if (VISITED(v)) { + continue; + } + + SET_VISITED(v); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); + + const igraph_integer_t dv = igraph_vector_int_size(&neis); + + if (IN_S(v)) { + degsum += dv; + S_visited_count++; + } + + for (igraph_integer_t i=0; i < dv; i++) { + const igraph_integer_t w = VECTOR(neis)[i]; + + /* Decide whether to traverse the v -> w edge. */ + if (!exited || !IN_S(v) || IN_S(w)) { + if (IN_S(w)) { + edgecount++; + } + + if (!VISITED(w)) { + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, w)); + } + + if (!exited && IN_S(v) && !IN_S(w)) { + exited = true; + } + } + } + } + + /* If some incident edges of visited vertices in S are + * still untraversed, then S is a separator. We are done. */ + if (degsum > edgecount) { + *is_separator = true; + break; + } + } + +done: + + /* Optionally, check if S is also a _minimal_ separator. */ + if (is_minimal != NULL) { + /* Be optimistic, and assume that if S was found to be a separator, + * it is a minimal separator. */ + *is_minimal = *is_separator; + + /* If S was proven to be a separator before visiting all of its + * vertices, it is not minimal. We are done. */ + if (*is_minimal && S_visited_count < S_size) { + *is_minimal = false; + } + + /* The subset of S with untraversed out-edges forms a separator. + * Therefore, if S contains vertices with no untraversed out-edges, + * S is not minimal. + * + * Suppose it doesn't contain any. + * + * Then for S to be minimal, each of its vertices should be reachable + * from any vertex in the unvisited neighbourhood of S. In order to + * separate a vertex in this neighbourhood, we need to cut precisely + * those vertices in S which are reachable from it. + * + * The check below verifies BOTH of the above conditions by traversing + * the graph from each unvisited neighbour of S with the constraint + * of never entering S. + */ + if (*is_minimal) { + igraph_vector_int_t Sneis; + IGRAPH_VECTOR_INT_INIT_FINALLY(&Sneis, 10); + + /* If the 2nd bit of mark[v] is the same as 'bit', it indicates + * that v in S was reached in the current testing round. + * We flip 'bit' between testing rounds, assuming that the previous + * round reached all of S. */ + igraph_bool_t bit = true; + +#define REACHED(x) (!(VECTOR(mark)[x] & 4) == !bit) /* Was x in S reached already? */ +#define FLIP_REACHED(x) (VECTOR(mark)[x] ^= 4) /* Flip the reachability status of x in S. */ + + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + const igraph_integer_t u = IGRAPH_VIT_GET(vit); + IGRAPH_CHECK(igraph_neighbors(graph, &Sneis, u, IGRAPH_ALL)); + const igraph_integer_t du = igraph_vector_int_size(&Sneis); + for (igraph_integer_t i=0; i < du; i++) { + + igraph_integer_t v = VECTOR(Sneis)[i]; + if (VISITED(v)) { + continue; + } + + /* How many vertices in S were reachable from u? */ + igraph_integer_t S_reached = 0; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, v)); + while (! igraph_dqueue_int_empty(&Q)) { + v = igraph_dqueue_int_pop(&Q); + if (VISITED(v)) { + continue; + } + + SET_VISITED(v); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); + + const igraph_integer_t dv = igraph_vector_int_size(&neis); + + for (igraph_integer_t j=0; j < dv; j++) { + const igraph_integer_t w = VECTOR(neis)[j]; + + if (! VISITED(w)) { + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, w)); + } else if (IN_S(w) && !REACHED(w)) { + S_reached++; + FLIP_REACHED(w); /* set as reachable */ + } + } + } + + bit = !bit; + + if (S_reached < S_size) { + *is_minimal = false; + break; + } + } + + if (! *is_minimal) { + break; + } + } + + igraph_vector_int_destroy(&Sneis); + IGRAPH_FINALLY_CLEAN(1); + } + } + +#undef REACHED +#undef FLIP_REACHED +#undef VISITED +#undef SET_VISITED +#undef IN_S +#undef SET_IN_S + + + igraph_dqueue_int_destroy(&Q); + igraph_vector_int_destroy(&neis); + igraph_vector_char_destroy(&mark); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_separator + * \brief Would removing this set of vertices disconnect the graph? + * + * A vertex set \c S is a separator if there are vertices \c u and \c v + * in the graph such that all paths between \c u and \c v pass through + * some vertices in \c S. + * + * \param graph The input graph. It may be directed, but edge + * directions are ignored. + * \param candidate The candidate separator. + * \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 + */ + +igraph_error_t igraph_is_separator(const igraph_t *graph, + const igraph_vs_t candidate, + igraph_bool_t *res) { + + return igraph_i_is_separator(graph, candidate, res, NULL); +} + +/** + * \function igraph_is_minimal_separator + * \brief Decides whether a set of vertices is a minimal separator. + * + * A vertex separator \c S is minimal is no proper subset of \c S + * is also a separator. + * + * \param graph The input graph. It may be directed, but edge + * directions are ignored. + * \param candidate The candidate minimal separators. + * \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_minimal_separator.c + */ + +igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, + const igraph_vs_t candidate, + igraph_bool_t *res) { + + igraph_bool_t is_separator; + return igraph_i_is_separator(graph, candidate, &is_separator, res); +} + +/* --------------------------------------------------------------------*/ + +#define UPDATEMARK() do { \ + (*mark)++; \ + if (!(*mark)) { \ + igraph_vector_int_null(leaveout); \ + (*mark)=1; \ + } \ + } while (0) + +static igraph_error_t igraph_i_connected_components_leaveout(const igraph_adjlist_t *adjlist, + igraph_vector_int_t *components, + igraph_vector_int_t *leaveout, + igraph_integer_t *mark, + igraph_dqueue_int_t *Q) { + + /* Another trick: we use the same 'leaveout' vector to mark the + * vertices that were already found in the BFS + */ + + igraph_integer_t i, no_of_nodes = igraph_adjlist_size(adjlist); + + igraph_dqueue_int_clear(Q); + igraph_vector_int_clear(components); + + for (i = 0; i < no_of_nodes; i++) { + + if (VECTOR(*leaveout)[i] == *mark) { + continue; + } + + VECTOR(*leaveout)[i] = *mark; + IGRAPH_CHECK(igraph_dqueue_int_push(Q, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(components, i)); + + while (!igraph_dqueue_int_empty(Q)) { + igraph_integer_t act_node = igraph_dqueue_int_pop(Q); + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, act_node); + igraph_integer_t j, n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (VECTOR(*leaveout)[nei] == *mark) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_int_push(Q, nei)); + VECTOR(*leaveout)[nei] = *mark; + IGRAPH_CHECK(igraph_vector_int_push_back(components, nei)); + } + } + + IGRAPH_CHECK(igraph_vector_int_push_back(components, -1)); + } + + UPDATEMARK(); + + return IGRAPH_SUCCESS; +} + +static igraph_bool_t igraph_i_separators_is_not_seen_yet( + const igraph_vector_int_list_t *comps, const igraph_vector_int_t *newc +) { + + igraph_integer_t co, nocomps = igraph_vector_int_list_size(comps); + + for (co = 0; co < nocomps; co++) { + igraph_vector_int_t *act = igraph_vector_int_list_get_ptr(comps, co); + if (igraph_vector_int_all_e(act, newc)) { + return false; + } + } + + /* If not found, then it is new */ + return true; +} + +static igraph_error_t igraph_i_separators_store(igraph_vector_int_list_t *separators, + const igraph_adjlist_t *adjlist, + igraph_vector_int_t *components, + igraph_vector_int_t *leaveout, + igraph_integer_t *mark, + igraph_vector_int_t *sorter) { + + /* We need to store N(C), the neighborhood of C, but only if it is + * not already stored among the separators. + */ + + igraph_integer_t cptr = 0, next, complen = igraph_vector_int_size(components); + + while (cptr < complen) { + igraph_integer_t saved = cptr; + igraph_vector_int_clear(sorter); + + /* Calculate N(C) for the next C */ + + while ( (next = VECTOR(*components)[cptr++]) != -1) { + VECTOR(*leaveout)[next] = *mark; + } + cptr = saved; + + while ( (next = VECTOR(*components)[cptr++]) != -1) { + igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, next); + igraph_integer_t j, nn = igraph_vector_int_size(neis); + for (j = 0; j < nn; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (VECTOR(*leaveout)[nei] != *mark) { + IGRAPH_CHECK(igraph_vector_int_push_back(sorter, nei)); + VECTOR(*leaveout)[nei] = *mark; + } + } + } + igraph_vector_int_sort(sorter); + + UPDATEMARK(); + + /* Add it to the list of separators, if it is new */ + /* TODO: Is there a cleaner way to avoid empty separators, + * or is this an inherent limitation of the algorithm? + * See https://github.com/igraph/igraph/issues/2517 */ + if ( + igraph_vector_int_size(sorter) > 0 && + igraph_i_separators_is_not_seen_yet(separators, sorter) + ) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(separators, sorter)); + } + } /* while cptr < complen */ + + return IGRAPH_SUCCESS; +} + +/** + * \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 Pointer to a list of integer vectors, the separators + * will be stored here. + * \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 + */ + +igraph_error_t igraph_all_minimal_st_separators( + const igraph_t *graph, igraph_vector_int_list_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. + * + * The try_next pointer show the next separator to try as a basis. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t leaveout; + igraph_integer_t try_next = 0; + igraph_integer_t mark = 1; + igraph_integer_t v; + + igraph_adjlist_t adjlist; + igraph_vector_int_t components; + igraph_dqueue_int_t Q; + igraph_vector_int_t sorter; + + igraph_vector_int_list_clear(separators); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&leaveout, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&components, 0); + IGRAPH_CHECK(igraph_vector_int_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_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + IGRAPH_VECTOR_INT_INIT_FINALLY(&sorter, 0); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_integer_t i, n = igraph_vector_int_size(neis); + VECTOR(leaveout)[v] = mark; + for (i = 0; i < n; i++) { + igraph_integer_t nei = VECTOR(*neis)[i]; + VECTOR(leaveout)[nei] = mark; + } + + /* Find the components */ + IGRAPH_CHECK(igraph_i_connected_components_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_int_list_size(separators)) { + /* copy "basis" out of the vector_list because we are going to + * mutate the vector_list later, and this can potentially invalidate + * the pointer */ + igraph_vector_int_t basis = *(igraph_vector_int_list_get_ptr(separators, try_next)); + igraph_integer_t b, basislen = igraph_vector_int_size(&basis); + for (b = 0; b < basislen; b++) { + + /* Remove N(x) U basis */ + igraph_integer_t x = VECTOR(basis)[b]; + igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, x); + igraph_integer_t i, n = igraph_vector_int_size(neis); + for (i = 0; i < basislen; i++) { + igraph_integer_t sn = VECTOR(basis)[i]; + VECTOR(leaveout)[sn] = mark; + } + for (i = 0; i < n; i++) { + igraph_integer_t nei = VECTOR(*neis)[i]; + VECTOR(leaveout)[nei] = mark; + } + + /* Find the components */ + IGRAPH_CHECK(igraph_i_connected_components_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_int_destroy(&sorter); + igraph_dqueue_int_destroy(&Q); + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&components); + igraph_vector_int_destroy(&leaveout); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +#undef UPDATEMARK + +static igraph_error_t igraph_i_minimum_size_separators_append( + igraph_vector_int_list_t *old, igraph_vector_int_list_t *new +) { + + igraph_integer_t olen = igraph_vector_int_list_size(old); + igraph_integer_t j; + + while (!igraph_vector_int_list_empty(new)) { + igraph_vector_int_t *newvec = igraph_vector_int_list_tail_ptr(new); + + /* Check whether the separator is already in `old' */ + for (j = 0; j < olen; j++) { + igraph_vector_int_t *oldvec = igraph_vector_int_list_get_ptr(old, j); + if (igraph_vector_int_all_e(oldvec, newvec)) { + break; + } + } + + if (j == olen) { + /* We have found a new separator, append it to `old'. We do it by + * extending it with an empty vector and then swapping it with + * the new vector to be appended */ + igraph_vector_int_t *oldvec; + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(old, &oldvec)); + igraph_vector_int_swap(oldvec, newvec); + olen++; + } + + igraph_vector_int_list_discard_back(new); + } + + return IGRAPH_SUCCESS; +} + +/** + * Finds the k largest degree vertices. + */ +static igraph_error_t igraph_i_minimum_size_separators_topkdeg( + const igraph_t *graph, igraph_vector_int_t *res, const igraph_integer_t k +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t deg, order; + + IGRAPH_VECTOR_INT_INIT_FINALLY(°, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + + /* It is assumed that this function receives only simple graphs, so we can use the + * faster IGRAPH_LOOPS here instead of the slower IGRAPH_NO_LOOPS. */ + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_vector_int_order1(°, &order, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(res, k)); + for (igraph_integer_t i = 0; i < k; i++) { + VECTOR(*res)[i] = VECTOR(order)[no_of_nodes - 1 - i]; + } + + igraph_vector_int_destroy(&order); + igraph_vector_int_destroy(°); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * https://doi.org/10.1002/net.3230230604 + * + * \param graph The input graph, which must be undirected. + * \param separators An initialized list of integer vectors, the separators + * are stored here. It is a list of pointers to igraph_vector_int_t + * objects. Each vector will contain the IDs of the vertices in + * the separator. The separators are returned in an arbitrary order. + * \return Error code. + * + * Time complexity: TODO. + * + * \example examples/simple/igraph_minimum_size_separators.c + */ + +igraph_error_t igraph_minimum_size_separators( + const igraph_t *graph, igraph_vector_int_list_t *separators +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t conn; + igraph_vector_int_t X; + igraph_integer_t k, n; + 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_int_list_clear(separators); + + /* ---------------------------------------------------------------- */ + /* 1 Find the vertex connectivity of 'graph' */ + IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /* checks= */ true)); + k = conn; + + /* Special cases for low connectivity, two exits here! */ + if (conn == 0) { + /* Nothing to do */ + return IGRAPH_SUCCESS; + } else if (conn == 1) { + igraph_vector_int_t ap; + IGRAPH_VECTOR_INT_INIT_FINALLY(&ap, 0); + IGRAPH_CHECK(igraph_articulation_points(graph, &ap)); + n = igraph_vector_int_size(&ap); + IGRAPH_CHECK(igraph_vector_int_list_resize(separators, n)); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(separators, i); + IGRAPH_CHECK(igraph_vector_int_push_back(v, VECTOR(ap)[i])); + } + igraph_vector_int_destroy(&ap); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + } else if (conn == no_of_nodes - 1) { + IGRAPH_CHECK(igraph_vector_int_list_resize(separators, no_of_nodes)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(separators, i); + IGRAPH_CHECK(igraph_vector_int_resize(v, no_of_nodes - 1)); + for (igraph_integer_t j = 0, k = 0; j < no_of_nodes; j++) { + if (j != i) { + VECTOR(*v)[k++] = j; + } + } + } + return IGRAPH_SUCCESS; + } + + /* 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, /* remove_multiple */ true, /* remove_loops */ true, 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_int_init(&X, conn)); + IGRAPH_FINALLY(igraph_vector_int_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_CHECK(igraph_vector_int_list_push_back_copy(separators, &X)); + } + + /* 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 (igraph_integer_t i = 0; i < k; i++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + for (igraph_integer_t j = 0; j < no_of_nodes; j++) { + igraph_integer_t xi = VECTOR(X)[i]; + igraph_real_t phivalue; + igraph_bool_t conn; + + if (xi == j) { + continue; /* the same vertex */ + } + IGRAPH_CHECK(igraph_are_adjacent(&graph_copy, xi, 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=*/ NULL, + /*partition=*/ NULL, /*partition2=*/ NULL, + /* source= */ xi + no_of_nodes, + /* target= */ j, + &capacity, &stats)); + + if (phivalue == k) { + + /* ------------------------------------------------------------- */ + /* 5-6-7. Find all k-sets separating x[i] and v[j]. */ + igraph_vector_int_list_t stcuts; + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&stcuts, 0); + IGRAPH_CHECK(igraph_all_st_mincuts(&Gbar, /*value=*/ NULL, + /*cuts=*/ &stcuts, + /*partition1s=*/ NULL, + /*source=*/ xi + no_of_nodes, + /*target=*/ j, + /*capacity=*/ &capacity)); + + IGRAPH_CHECK(igraph_i_minimum_size_separators_append(separators, &stcuts)); + igraph_vector_int_list_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, xi, j)); + IGRAPH_CHECK(igraph_add_edge(&Gbar, xi + no_of_nodes, j)); + IGRAPH_CHECK(igraph_add_edge(&Gbar, j + no_of_nodes, xi)); + IGRAPH_CHECK(igraph_vector_push_back(&capacity, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_push_back(&capacity, no_of_nodes)); + + } /* for j>= 1; + break; + case IGRAPH_LOOPS_ONCE: + default: + break; + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_adjacency_directed( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k; + + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < no_of_nodes; j++) { + igraph_integer_t M = MATRIX(*adjmatrix, i, j); + if (i == j) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); + } + for (k = 0; k < M; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + } + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_adjacency_max( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k, M1, M2; + + for (i = 0; i < no_of_nodes; i++) { + /* do the loops first */ + M1 = MATRIX(*adjmatrix, i, i); + if (M1) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M1, loops)); + for (k = 0; k < M1; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M1 = MATRIX(*adjmatrix, i, j); + M2 = MATRIX(*adjmatrix, j, i); + if (M1 < M2) { + M1 = M2; + } + for (k = 0; k < M1; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + } + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_adjacency_undirected( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + if (!igraph_matrix_is_symmetric(adjmatrix)) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + return igraph_i_adjacency_max(adjmatrix, edges, loops); +} + +static igraph_error_t igraph_i_adjacency_upper( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k, M; + + for (i = 0; i < no_of_nodes; i++) { + /* do the loops first */ + M = MATRIX(*adjmatrix, i, i); + if (M) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); + for (k = 0; k < M; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M = MATRIX(*adjmatrix, i, j); + for (k = 0; k < M; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + } + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_adjacency_lower( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k, M; + + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < i; j++) { + M = MATRIX(*adjmatrix, i, j); + for (k = 0; k < M; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + } + } + + /* do the loops as well */ + M = MATRIX(*adjmatrix, i, i); + if (M) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); + for (k = 0; k < M; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + } + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_adjacency_min( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k, M1, M2; + + for (i = 0; i < no_of_nodes; i++) { + /* do the loops first */ + M1 = MATRIX(*adjmatrix, i, i); + if (M1) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M1, loops)); + for (k = 0; k < M1; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M1 = MATRIX(*adjmatrix, i, j); + M2 = MATRIX(*adjmatrix, j, i); + if (M1 > M2) { + M1 = M2; + } + for (k = 0; k < M1; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + } + } + } + + return IGRAPH_SUCCESS; +} + + + +/** + * \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 + * The graph will be undirected and + * an element gives the number of edges between two vertices. + * If the input matrix is not symmetric, an error is thrown. + * \cli IGRAPH_ADJ_MAX + * An 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 + * An undirected graph will be created + * with min(A(i,j), A(j,i)) + * edges between vertices i and j. + * \cli IGRAPH_ADJ_PLUS + * An undirected graph will be created + * with A(i,j)+A(j,i) edges + * between vertices i and j. + * \cli IGRAPH_ADJ_UPPER + * An undirected graph will be created. + * Only the upper right triangle (including the diagonal) is + * used for the number of edges. + * \cli IGRAPH_ADJ_LOWER + * An undirected graph will be created. + * Only the lower left triangle (including the diagonal) is + * used for the number of edges. + * \endclist + * \param loops Constant to specify how the diagonal of the matrix should be + * treated when creating loop edges. + * \clist + * \cli IGRAPH_NO_LOOPS + * Ignore the diagonal of the input matrix and do not create loops. + * \cli IGRAPH_LOOPS_ONCE + * Treat the diagonal entries as the number of loop edges incident on + * the corresponding vertex. + * \cli IGRAPH_LOOPS_TWICE + * Treat the diagonal entries as \em twice the number of loop edges + * incident on the corresponding vertex. Odd numbers in the diagonal + * will return an error code. + * \endclist + * \return Error code, + * \c IGRAPH_NONSQUARE: non-square matrix. + * \c IGRAPH_EINVAL: Negative entry was found in adjacency matrix, or an + * odd number was found in the diagonal with \c IGRAPH_LOOPS_TWICE + * + * Time complexity: O(|V||V|), + * |V| is the number of vertices in the graph. + * + * \example examples/simple/igraph_adjacency.c + */ +igraph_error_t igraph_adjacency( + igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, + igraph_loops_t loops +) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + + /* Some checks */ + if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) { + IGRAPH_ERROR("Adjacency matrices must be square.", IGRAPH_NONSQUARE); + } + + if (no_of_nodes != 0 && igraph_matrix_min(adjmatrix) < 0) { + IGRAPH_ERRORF("Edge counts should be non-negative, found %g.", IGRAPH_EINVAL, + igraph_matrix_min(adjmatrix)); + } + + IGRAPH_VECTOR_INT_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, loops)); + break; + case IGRAPH_ADJ_MAX: + IGRAPH_CHECK(igraph_i_adjacency_max(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_UNDIRECTED: + IGRAPH_CHECK(igraph_i_adjacency_undirected(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_UPPER: + IGRAPH_CHECK(igraph_i_adjacency_upper(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_LOWER: + IGRAPH_CHECK(igraph_i_adjacency_lower(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_MIN: + IGRAPH_CHECK(igraph_i_adjacency_min(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_PLUS: + IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges, loops)); + break; + default: + IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_weighted_adjacency_directed( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_plus( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_max( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_upper( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_lower( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_min( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +); + +static void igraph_i_adjust_loop_edge_weight(igraph_real_t* weight, igraph_loops_t loops) { + /* The compiler should be smart enough to figure out that this can be + * inlined */ + switch (loops) { + case IGRAPH_NO_LOOPS: + *weight = 0.0; + break; + case IGRAPH_LOOPS_TWICE: + *weight /= 2; + break; + case IGRAPH_LOOPS_ONCE: + default: + break; + } +} + +static igraph_error_t igraph_i_weighted_adjacency_directed( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t 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) { + if (i == j) { + igraph_i_adjust_loop_edge_weight(&M, loops); + if (M == 0.0) { + continue; + } + } + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_weighted_adjacency_plus( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M; + + for (i = 0; i < no_of_nodes; i++) { + if (loops != IGRAPH_NO_LOOPS) { + M = MATRIX(*adjmatrix, i, i); + if (M != 0.0) { + igraph_i_adjust_loop_edge_weight(&M, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + + for (j = i + 1; j < no_of_nodes; j++) { + M = MATRIX(*adjmatrix, i, j) + MATRIX(*adjmatrix, j, i); + if (M != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_weighted_adjacency_max( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M1, M2; + + for (i = 0; i < no_of_nodes; i++) { + /* do the loops first */ + if (loops) { + M1 = MATRIX(*adjmatrix, i, i); + if (M1 != 0.0) { + igraph_i_adjust_loop_edge_weight(&M1, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M1 = MATRIX(*adjmatrix, i, j); + M2 = MATRIX(*adjmatrix, j, i); + if (M1 < M2 || isnan(M2)) { + M1 = M2; + } + if (M1 != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + } + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_weighted_adjacency_undirected( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + /* We do not use igraph_matrix_is_symmetric() for this check, as we need to + * allow symmetric matrices with NaN values. igraph_matrix_is_symmetric() + * returns false for these as NaN != NaN. */ + igraph_integer_t n = igraph_matrix_nrow(adjmatrix); + for (igraph_integer_t i=0; i < n; i++) { + for (igraph_integer_t j=0; j < i; j++) { + igraph_real_t a1 = MATRIX(*adjmatrix, i, j); + igraph_real_t a2 = MATRIX(*adjmatrix, j, i); + if (a1 != a2 && ! (isnan(a1) && isnan(a2))) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + } + } + return igraph_i_weighted_adjacency_max(adjmatrix, edges, weights, loops); +} + + +static igraph_error_t igraph_i_weighted_adjacency_upper( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M; + + for (i = 0; i < no_of_nodes; i++) { + /* do the loops first */ + if (loops) { + M = MATRIX(*adjmatrix, i, i); + if (M != 0.0) { + igraph_i_adjust_loop_edge_weight(&M, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + igraph_real_t M = MATRIX(*adjmatrix, i, j); + if (M != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_weighted_adjacency_lower( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M; + + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < i; j++) { + M = MATRIX(*adjmatrix, i, j); + if (M != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + + /* do the loops as well */ + if (loops) { + M = MATRIX(*adjmatrix, i, i); + if (M != 0.0) { + igraph_i_adjust_loop_edge_weight(&M, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + } + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_weighted_adjacency_min( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M1, M2; + + for (i = 0; i < no_of_nodes; i++) { + /* do the loops first */ + if (loops) { + M1 = MATRIX(*adjmatrix, i, i); + if (M1 != 0.0) { + igraph_i_adjust_loop_edge_weight(&M1, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M1 = MATRIX(*adjmatrix, i, j); + M2 = MATRIX(*adjmatrix, j, i); + if (M1 > M2 || isnan(M2)) { + M1 = M2; + } + if (M1 != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + } + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \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 weights Pointer to an initialized vector, the weights will be stored here. + * \param loops Constant to specify how the diagonal of the matrix should be + * treated when creating loop edges. + * \clist + * \cli IGRAPH_NO_LOOPS + * Ignore the diagonal of the input matrix and do not create loops. + * \cli IGRAPH_LOOPS_ONCE + * Treat the diagonal entries as the weight of the loop edge incident + * on the corresponding vertex. + * \cli IGRAPH_LOOPS_TWICE + * Treat the diagonal entries as \em twice the weight of the loop edge + * incident on the corresponding vertex. + * \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_weighted_adjacency.c + */ +igraph_error_t igraph_weighted_adjacency( + igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, + igraph_vector_t *weights, igraph_loops_t loops +) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t no_of_nodes; + + /* Some checks */ + if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) { + IGRAPH_ERROR("Adjacency matrices must be square.", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + igraph_vector_clear(weights); + + /* 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_UNDIRECTED: + IGRAPH_CHECK(igraph_i_weighted_adjacency_undirected(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; + default: + IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); + } + + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); + IGRAPH_FINALLY(igraph_destroy, graph); + if (igraph_vector_int_size(&edges) > 0) { + IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); + } + IGRAPH_FINALLY_CLEAN(1); + + /* Cleanup */ + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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|). + * + */ +igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, + igraph_neimode_t mode, igraph_bool_t duplicate) { + + igraph_integer_t no_of_nodes = igraph_adjlist_size(adjlist); + igraph_integer_t no_of_edges = 0; + igraph_integer_t i; + + igraph_vector_int_t edges; + igraph_integer_t 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_INT_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); + igraph_integer_t j, n = igraph_vector_int_size(neis); + igraph_integer_t loops = 0; + + for (j = 0; j < n; j++) { + igraph_integer_t nei = 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, no_of_nodes, 0)); + else + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, 1)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_directed( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_max( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_real_t other; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + multi = multi > other ? multi : other; + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_min( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_real_t other; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + multi = multi < other ? multi : other; + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_upper( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_lower( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to > from) { + continue; + } + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_undirected( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_bool_t sym; + + IGRAPH_CHECK(igraph_sparsemat_is_symmetric(adjmatrix, &sym)); + if (!sym) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + return igraph_i_sparse_adjacency_upper(adjmatrix, edges, loops); +} + +/** + * \ingroup generators + * \function igraph_sparse_adjacency + * \brief Creates a graph from a sparse adjacency matrix. + * + * This has the same functionality as \ref igraph_adjacency(), but uses + * a column-compressed adjacency matrix. + * + * Time complexity: O(|E|), + * where |E| is the number of edges in the graph. + */ + +igraph_error_t igraph_sparse_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, + igraph_adjacency_t mode, igraph_loops_t loops) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(adjmatrix); + igraph_integer_t no_of_nonzeros = igraph_sparsemat_count_nonzero(adjmatrix); + igraph_integer_t approx_no_of_edges; + + if (!igraph_sparsemat_is_cc(adjmatrix)) { + IGRAPH_ERROR("Sparse adjacency matrix should be in column-compressed " + "form.", IGRAPH_EINVAL); + } + if (no_of_nodes != igraph_sparsemat_ncol(adjmatrix)) { + IGRAPH_ERROR("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); + } + + if (no_of_nodes != 0 && igraph_sparsemat_min(adjmatrix) < 0) { + IGRAPH_ERRORF("Edge counts should be non-negative, found %g.", IGRAPH_EINVAL, + igraph_sparsemat_min(adjmatrix)); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + /* Approximate the number of edges in the graph based on the number of + * nonzero elements in the matrix */ + switch (mode) { + case IGRAPH_ADJ_DIRECTED: + case IGRAPH_ADJ_PLUS: + case IGRAPH_ADJ_UPPER: + case IGRAPH_ADJ_LOWER: + approx_no_of_edges = no_of_nonzeros; + break; + case IGRAPH_ADJ_UNDIRECTED: + case IGRAPH_ADJ_MAX: + case IGRAPH_ADJ_MIN: + approx_no_of_edges = no_of_nonzeros / 2; + break; + default: + approx_no_of_edges = no_of_nonzeros; + break; + } + + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, approx_no_of_edges * 2)); + + /* Collect the edges */ + switch (mode) { + case IGRAPH_ADJ_DIRECTED: + IGRAPH_CHECK(igraph_i_sparse_adjacency_directed(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_MAX: + IGRAPH_CHECK(igraph_i_sparse_adjacency_max(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_UNDIRECTED: + IGRAPH_CHECK(igraph_i_sparse_adjacency_undirected(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_UPPER: + IGRAPH_CHECK(igraph_i_sparse_adjacency_upper(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_LOWER: + IGRAPH_CHECK(igraph_i_sparse_adjacency_lower(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_MIN: + IGRAPH_CHECK(igraph_i_sparse_adjacency_min(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_PLUS: + IGRAPH_CHECK(igraph_i_sparse_adjacency_directed(adjmatrix, &edges, loops)); + break; + default: + IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_max ( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + igraph_integer_t e = 0; + igraph_real_t other; + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + weight = weight > other ? weight : other; + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_min ( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_integer_t e = 0; + igraph_real_t other; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + weight = weight < other ? weight : other; + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_plus ( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_integer_t e = 0; + igraph_real_t other; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + weight += other; + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_upper( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + igraph_integer_t e = 0; + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to < from) { + continue; + } + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_lower( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + igraph_integer_t e = 0; + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to > from) { + continue; + } + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_undirected ( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_bool_t sym; + + IGRAPH_CHECK(igraph_sparsemat_is_symmetric(adjmatrix, &sym)); + if (!sym) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + return igraph_i_sparse_weighted_adjacency_upper(adjmatrix, edges, weights, loops); +} + + +static igraph_error_t igraph_i_sparse_weighted_adjacency_directed( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + igraph_integer_t e = 0; + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_sparse_weighted_adjacency + * \brief Creates a graph from a weighted sparse adjacency matrix. + * + * This has the same functionality as \ref igraph_weighted_adjacency(), but uses + * a column-compressed adjacency matrix. + * + * Time complexity: O(|E|), + * where |E| is the number of edges in the graph. + */ + + +igraph_error_t igraph_sparse_weighted_adjacency( + igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(adjmatrix); + igraph_integer_t no_of_edges = igraph_sparsemat_count_nonzero(adjmatrix); + + if (!igraph_sparsemat_is_cc(adjmatrix)) { + IGRAPH_ERROR("Sparse adjacency matrix should be in column-compressed form.", IGRAPH_EINVAL); + } + if (no_of_nodes != igraph_sparsemat_ncol(adjmatrix)) { + IGRAPH_ERROR("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_vector_resize(weights, no_of_edges)); + + /* Collect the edges */ + switch (mode) { + case IGRAPH_ADJ_DIRECTED: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_directed(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_MAX: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_max(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_UNDIRECTED: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_undirected(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_UPPER: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_upper(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_LOWER: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_lower(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_MIN: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_min(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_PLUS: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_plus(adjmatrix, &edges, + weights, loops)); + break; + default: + IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); + } + + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); + IGRAPH_FINALLY(igraph_destroy, graph); + if (igraph_vector_int_size(&edges) > 0) { + IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); + } + IGRAPH_FINALLY_CLEAN(1); + + /* Cleanup */ + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/atlas-edges.h b/src/constructors/atlas-edges.h new file mode 100644 index 0000000..7038ed6 --- /dev/null +++ b/src/constructors/atlas-edges.h @@ -0,0 +1,1292 @@ +/* -*- 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 + +*/ + +#ifndef IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H +#define IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H + +#include "igraph_decls.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +const igraph_integer_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 igraph_integer_t 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 + +#endif /* IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H */ diff --git a/src/constructors/atlas.c b/src/constructors/atlas.c new file mode 100644 index 0000000..4fe2db8 --- /dev/null +++ b/src/constructors/atlas.c @@ -0,0 +1,80 @@ +/* + IGraph library. + Copyright (C) 2006-2023 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 "constructors/atlas-edges.h" + +/** + * \function igraph_atlas + * \brief Create a small graph from the \quote Graph Atlas \endquote. + * + * + * The graph atlas contains all simple undirected unlabeled graphs on between + * 0 and 7 vertices. The number of the graph is given as a parameter. + * The graphs are listed: + * \olist + * \oli in increasing order of number of vertices; + * \oli for a fixed number of vertices, in increasing order of the + * number of edges; + * \oli for fixed numbers of vertices and edges, in laxicographically + * 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 https://networkx.org/. + * + * + * 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. Must be between 0 and + * 1252 (inclusive). Graphs on 0-7 vertices start at numbers 0, 1, 2, 4, + * 8, 19, 53, and 209, respectively. + * + * 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 + */ +igraph_error_t igraph_atlas(igraph_t *graph, igraph_integer_t number) { + + const igraph_vector_int_t v; + + if (number < 0 || + number >= sizeof(igraph_i_atlas_edges_pos) / sizeof(igraph_i_atlas_edges_pos[0])) { + IGRAPH_ERROR("No such graph in atlas", IGRAPH_EINVAL); + } + + igraph_integer_t pos = igraph_i_atlas_edges_pos[number]; + igraph_integer_t n = igraph_i_atlas_edges[pos]; + igraph_integer_t e = igraph_i_atlas_edges[pos + 1]; + + IGRAPH_CHECK(igraph_create(graph, + igraph_vector_int_view(&v, igraph_i_atlas_edges + pos + 2, e * 2), + n, IGRAPH_UNDIRECTED)); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/basic_constructors.c b/src/constructors/basic_constructors.c new file mode 100644 index 0000000..892c3cd --- /dev/null +++ b/src/constructors/basic_constructors.c @@ -0,0 +1,156 @@ +/* -*- 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 + */ +igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_t *edges, + igraph_integer_t n, igraph_bool_t directed) { + igraph_bool_t has_edges = igraph_vector_int_size(edges) > 0; + igraph_integer_t max; + + if (igraph_vector_int_size(edges) % 2 != 0) { + IGRAPH_ERROR("Invalid (odd) edges vector.", IGRAPH_EINVEVECTOR); + } + if (has_edges && !igraph_vector_int_isininterval(edges, 0, IGRAPH_VCOUNT_MAX-1)) { + IGRAPH_ERROR("Invalid (negative or too large) vertex ID.", IGRAPH_EINVVID); + } + + /* The + 1 here cannot overflow as above we have already + * checked that vertex IDs are within range. */ + max = has_edges ? igraph_vector_int_max(edges) + 1 : 0; + + IGRAPH_CHECK(igraph_empty(graph, n, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + if (has_edges) { + n = igraph_vcount(graph); + if (n < max) { + IGRAPH_CHECK(igraph_add_vertices(graph, (max - n), 0)); + } + IGRAPH_CHECK(igraph_add_edges(graph, edges, 0)); + } + + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * This function is intended to be used with vertex IDs that are entered as + * literal integers. If you use a variable instead of a literal, make sure + * that it is of type int, as this is the type that this function + * assumes for all variadic arguments. Using a different integer type is + * undefined behaviour and likely to cause platform-specific issues. + * + * \param graph Pointer to an uninitialized graph object. The result + * will be stored here. + * \param n The number of vertices in the graph; a non-negative 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, + * and \em must be of type int. Don't forget to supply an + * additional -1 after the last (meaningful) argument. The + * \p first parameter is present for technical reasons and represents + * the first variadic 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 + */ + +igraph_error_t igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + int first, ...) { + igraph_vector_int_t edges; + va_list ap; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + va_start(ap, first); + int num = first; + while (num != -1) { + igraph_vector_int_push_back(&edges, num); + num = va_arg(ap, int); + } + va_end(ap); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/circulant.c b/src/constructors/circulant.c new file mode 100644 index 0000000..6f6a215 --- /dev/null +++ b/src/constructors/circulant.c @@ -0,0 +1,111 @@ +/* + 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 . +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +#include "math/safe_intop.h" + +/** + * \function igraph_circulant + * \brief Creates a circulant graph. + * + * A circulant graph G(n, shifts) consists of \p n vertices v_0, ..., + * v_(n-1) such that for each \c s_i in the list of offsets \p shifts, \c v_j is + * connected to v_((j + s_i) mod n) for all j. + * + * + * The function can generate either directed or undirected graphs. It does not generate + * multi-edges or self-loops. + * + * \param graph Pointer to an uninitialized graph object, the result will + * be stored here. + * \param n Integer, the number of vertices in the circulant graph. + * \param shifts Integer vector, a list of the offsets within the circulant graph. + * \param directed Boolean, whether to create a directed graph. + * \return Error code. + * + * \sa \ref igraph_ring(), \ref igraph_generalized_petersen(), \ref igraph_extended_chordal_ring() + * + * Time complexity: O(|V||shifts|), the number of vertices in the graph times the number + * of shifts. + */ + +igraph_error_t igraph_circulant(igraph_t *graph, igraph_integer_t n, const igraph_vector_int_t *shifts, igraph_bool_t directed) { + + igraph_vector_int_t edges; + igraph_vector_bool_t shift_seen; + igraph_integer_t i, j; + igraph_integer_t limit; + igraph_integer_t shift_size = igraph_vector_int_size(shifts); + + if (n < 0) { + IGRAPH_ERRORF("Number of nodes = %" IGRAPH_PRId " must be non-negative.", IGRAPH_EINVAL, n); + } + if (n == 0) { + return igraph_empty(graph, 0, directed); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + { + igraph_integer_t size; + IGRAPH_SAFE_MULT(n, shift_size, &size); + IGRAPH_SAFE_MULT(size, 2, &size); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size)); + } + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&shift_seen, n); + VECTOR(shift_seen)[0] = true; /* do not allow self loops */ + + for (i = 0; i < shift_size; i++) { + /* simplify the shift */ + igraph_integer_t shift = VECTOR(*shifts)[i] % n; + if (shift < 0) { + shift += n; + } + if (!directed) { + if (shift >= (n + 1) / 2) { + shift = n - shift; + } + } + + /* only use shift if non-zero and we haven't seen it before */ + if (!VECTOR(shift_seen)[shift]) { + if (n % 2 == 0 && shift == n / 2 && !directed) { + limit = n / 2; /* this to avoid doubling up the n/2 shift for even n and undirected graph */ + } else { + limit = n; + } + for (j = 0; j < limit; j++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, (j + shift) % n)); + } + + VECTOR(shift_seen)[shift] = true; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_int_destroy(&edges); + igraph_vector_bool_destroy(&shift_seen); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/de_bruijn.c b/src/constructors/de_bruijn.c new file mode 100644 index 0000000..e7a55d2 --- /dev/null +++ b/src/constructors/de_bruijn.c @@ -0,0 +1,114 @@ +/* -*- 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" +#include "math/safe_intop.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. + */ +igraph_error_t igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { + + /* m - number of symbols */ + /* n - length of strings */ + + igraph_integer_t no_of_nodes, no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t i, j; + int iter = 0; + + 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); + } + + { + igraph_real_t no_of_nodes_real = pow(m, n); + no_of_nodes = no_of_nodes_real; + if (no_of_nodes != no_of_nodes_real) { + IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for De Bruijn graph.", IGRAPH_EINVAL, + m, n); + } + } + /* no_of_edges = m * no_of_nodes */ + IGRAPH_SAFE_MULT(no_of_nodes, m, &no_of_edges); + + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + } + + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t basis = (i * m) % no_of_nodes; + for (j = 0; j < m; j++) { + igraph_vector_int_push_back(&edges, i); + igraph_vector_int_push_back(&edges, basis + j); + } + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 10); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_DIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/famous.c b/src/constructors/famous.c new file mode 100644 index 0000000..31119cd --- /dev/null +++ b/src/constructors/famous.c @@ -0,0 +1,497 @@ +/* -*- 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" /* strcasecmp */ + +const igraph_integer_t igraph_i_famous_bull[] = { + 5, 5, 0, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4 +}; + +const igraph_integer_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_integer_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_integer_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_integer_t igraph_i_famous_diamond[] = { + 4, 5, 0, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 3 +}; + +const igraph_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_t igraph_i_famous_house[] = { + 5, 6, 0, + 0, 1, 0, 2, 1, 3, 2, 3, 2, 4, 3, 4 +}; + +const igraph_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_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_integer_t igraph_i_famous_tetrahedron[] = { + 4, 6, 0, + 0, 3, 1, 3, 2, 3, 0, 1, 1, 2, 0, 2 +}; + +const igraph_integer_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_integer_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_integer_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_integer_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_integer_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 igraph_error_t igraph_i_famous(igraph_t *graph, const igraph_integer_t *data) { + igraph_integer_t no_of_nodes = data[0]; + igraph_integer_t no_of_edges = data[1]; + igraph_bool_t directed = (igraph_bool_t) data[2]; + igraph_vector_int_t edges; + + igraph_vector_int_view(&edges, data + 3, 2 * no_of_edges); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + return IGRAPH_SUCCESS; +} + +/** + * \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_kary_tree(), \ref igraph_square_lattice(), + * \ref igraph_full(). + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number + * of edges in the graph. + */ + +igraph_error_t 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/constructors/full.c b/src/constructors/full.c new file mode 100644 index 0000000..67b7de4 --- /dev/null +++ b/src/constructors/full.c @@ -0,0 +1,377 @@ +/* -*- 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" +#include "math/safe_intop.h" + +/** + * \ingroup generators + * \function igraph_full + * \brief Creates a full graph (complete graph). + * + * In a full graph every possible edge is present: every vertex is + * connected to every other vertex. \a igraph generalizes the usual + * concept of complete graphs in graph theory to graphs with self-loops + * as well as to directed graphs. + * + * \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|^2) = O(|E|), + * where |V| is the number of vertices and |E| is the number of edges. + * + * \sa \ref igraph_square_lattice(), \ref igraph_star(), \ref igraph_kary_tree() + * for creating other regular structures. + * + * \example examples/simple/igraph_full.c + */ +igraph_error_t igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + igraph_bool_t loops) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t no_of_edges2; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + if (directed && loops) { + /* ecount = n * n */ + IGRAPH_SAFE_MULT(n, n, &no_of_edges2); + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + for (igraph_integer_t i = 0; i < n; i++) { + for (igraph_integer_t j = 0; j < n; j++) { + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ + } + IGRAPH_ALLOW_INTERRUPTION(); + } + } else if (directed && !loops) { + /* ecount = n * (n - 1) */ + IGRAPH_SAFE_MULT(n, n - 1, &no_of_edges2); + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + for (igraph_integer_t i = 0; i < n; i++) { + for (igraph_integer_t j = 0; j < i; j++) { + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ + } + for (igraph_integer_t j = i + 1; j < n; j++) { + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ + } + IGRAPH_ALLOW_INTERRUPTION(); + } + } else if (!directed && loops) { + /* ecount = n * (n + 1) / 2 */ + IGRAPH_SAFE_ADD(n, 1, &no_of_edges2); + IGRAPH_SAFE_MULT(n, no_of_edges2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + for (igraph_integer_t i = 0; i < n; i++) { + for (igraph_integer_t j = i; j < n; j++) { + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ + } + IGRAPH_ALLOW_INTERRUPTION(); + } + } else { + /* ecount = n * (n - 1) / 2 */ + IGRAPH_SAFE_MULT(n, n - 1, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + for (igraph_integer_t i = 0; i < n; i++) { + for (igraph_integer_t j = i + 1; j < n; j++) { + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ + } + IGRAPH_ALLOW_INTERRUPTION(); + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_full_multipartite + * \brief Creates a full multipartite graph. + * + * A multipartite graph contains two or more types of vertices and connections + * are only possible between two vertices of different types. This function + * creates a complete multipartite graph. + * + * \param graph Pointer to an uninitialized graph object, the graph will be + * created here. + * \param types Pointer to an integer vector. If not a null pointer, + * the type of each vertex will be stored here. + * \param n Pointer to an integer vector, the number of vertices + * of each type. + * \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 low-index vertices to high-index vertices; if + * \c IGRAPH_IN, then the opposite direction is realized; + * \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_bipartite() for complete bipartite graphs, + * \ref igraph_turan() for Turán graphs. + */ +igraph_error_t igraph_full_multipartite(igraph_t *graph, + igraph_vector_int_t *types, + const igraph_vector_int_t *n, + igraph_bool_t directed, + igraph_neimode_t mode) { + + igraph_vector_int_t edges; + igraph_vector_int_t n_acc; + + igraph_integer_t no_of_types = igraph_vector_int_size(n); + + if (no_of_types == 0) { + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + if (types) { + igraph_vector_int_clear(types); + } + return IGRAPH_SUCCESS; + } + + if (igraph_vector_int_min(n) < 0) { + IGRAPH_ERROR("Number of vertices must not be negative in any partition.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&n_acc, no_of_types+1); + VECTOR(n_acc)[0] = 0; + for (igraph_integer_t i = 1; i < no_of_types+1; i++) { + IGRAPH_SAFE_ADD(VECTOR(n_acc)[i-1], VECTOR(*n)[i-1], + &VECTOR(n_acc)[i]); + } + + igraph_integer_t no_of_edges2 = 0; + + for (igraph_integer_t i = 0; i < no_of_types; i++) { + igraph_integer_t v = VECTOR(*n)[i]; + igraph_integer_t partial_sum = VECTOR(n_acc)[no_of_types] - v; + IGRAPH_SAFE_MULT(partial_sum, v, &partial_sum); + IGRAPH_SAFE_ADD(no_of_edges2, partial_sum, &no_of_edges2); + } + + if (directed && mode == IGRAPH_ALL) { + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + + igraph_integer_t ptr = 0; + + for (igraph_integer_t from_type = 0; from_type < no_of_types-1; from_type++) { + igraph_integer_t edge_from = VECTOR(n_acc)[from_type]; + for (igraph_integer_t i = 0; i < VECTOR(*n)[from_type]; i++) { + for (igraph_integer_t to_type = from_type+1; to_type < no_of_types; to_type++) { + igraph_integer_t edge_to = VECTOR(n_acc)[to_type]; + for (igraph_integer_t j = 0; j < VECTOR(*n)[to_type]; j++) { + if (!directed || mode == IGRAPH_OUT) { + VECTOR(edges)[ptr++] = edge_from; + VECTOR(edges)[ptr++] = edge_to; + } else if (mode == IGRAPH_IN) { + VECTOR(edges)[ptr++] = edge_to; + VECTOR(edges)[ptr++] = edge_from; + } else { + VECTOR(edges)[ptr++] = edge_from; + VECTOR(edges)[ptr++] = edge_to; + VECTOR(edges)[ptr++] = edge_to; + VECTOR(edges)[ptr++] = edge_from; + } + edge_to++; + } + } + edge_from++; + IGRAPH_ALLOW_INTERRUPTION(); + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, VECTOR(n_acc)[no_of_types], directed)); + + if (types) { + IGRAPH_CHECK(igraph_vector_int_resize(types, VECTOR(n_acc)[no_of_types])); + if (VECTOR(n_acc)[no_of_types] > 0) { + igraph_integer_t v = 1; + for (igraph_integer_t i = 0; i < VECTOR(n_acc)[no_of_types]; i++) { + if (i == VECTOR(n_acc)[v]) { + v++; + } + VECTOR(*types)[i] = v-1; + } + } + } + + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&n_acc); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_turan + * \brief Creates a Turán graph. + * + * Turán graphs are complete multipartite graphs with the property + * that the sizes of the partitions are as close to equal as possible. + * + * + * The Turán graph with \p n vertices and \p r partitions is the densest + * graph on \p n vertices that does not contain a clique of size + * r+1. + * + * + * This function generates undirected graphs. The null graph is + * returned when the number of vertices is zero. A complete graph is + * returned if the number of partitions is greater than the number of + * vertices. + * + * \param graph Pointer to an igraph_t object, the graph will be + * created here. + * \param types Pointer to an integer vector. If not a null pointer, + * the type (partition index) of each vertex will be stored here. + * \param n Integer, the number of vertices in the graph. + * \param r Integer, the number of partitions of the graph, must be + * positive. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \sa \ref igraph_full_multipartite() for full multipartite graphs. + */ +igraph_error_t igraph_turan(igraph_t *graph, + igraph_vector_int_t *types, + igraph_integer_t n, + igraph_integer_t r) { + igraph_integer_t quotient; + igraph_integer_t remainder; + igraph_vector_int_t subsets; + + if (n < 0) { + IGRAPH_ERRORF("Number of vertices must not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); + } + + if (r <= 0) { + IGRAPH_ERRORF("Number of partitions must be positive, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, r); + } + + if (n == 0) { + IGRAPH_CHECK(igraph_empty(graph, 0, IGRAPH_UNDIRECTED)); + if (types) { + igraph_vector_int_clear(types); + } + return IGRAPH_SUCCESS; + } + + if (r > n) { + r = n; + } + + quotient = n / r; + remainder = n % r; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&subsets, r); + + igraph_vector_int_fill(&subsets, quotient); + for (igraph_integer_t i = 0; i < remainder; i++) { + VECTOR(subsets)[i]++; + } + + IGRAPH_CHECK(igraph_full_multipartite(graph, types, &subsets, + IGRAPH_UNDIRECTED, IGRAPH_ALL)); + igraph_vector_int_destroy(&subsets); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_full_citation + * \brief Creates a full citation graph (a complete directed acyclic graph). + * + * This is a directed graph, where every i->j edge is + * present if and only if j<i. + * If the \p directed argument is false then an undirected graph is + * created, and it is just a complete 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 false an + * undirected graph is created. + * \return Error code. + * + * \sa \ref igraph_full() + * + * Time complexity: O(|V|^2) = O(|E|), + * where |V| is the number of vertices and |E| is the number of edges. + */ +igraph_error_t igraph_full_citation(igraph_t *graph, igraph_integer_t n, + igraph_bool_t directed) { + igraph_vector_int_t edges; + igraph_integer_t ptr = 0; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + } + + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(n, n-1, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } + + for (igraph_integer_t i = 1; i < n; i++) { + for (igraph_integer_t j = 0; j < i; j++) { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = j; + } + IGRAPH_ALLOW_INTERRUPTION(); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/generalized_petersen.c b/src/constructors/generalized_petersen.c new file mode 100644 index 0000000..0b34f7e --- /dev/null +++ b/src/constructors/generalized_petersen.c @@ -0,0 +1,95 @@ +/* + 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 . +*/ + +#include "igraph_constructors.h" + +#include "math/safe_intop.h" + +/** + * \function igraph_generalized_petersen + * \brief Creates a Generalized Petersen graph. + * + * The generalized Petersen graph G(n, k) consists of \p n vertices + * \c v_0, ..., \c v_n forming an "outer" cycle graph, and \p n additional vertices + * \c u_0, ..., \c u_n forming an "inner" circulant graph where u_i + * is connected to u_(i + k mod n). Additionally, all \c v_i are + * connected to \c u_i. + * + * + * G(n, k) has \c 2n vertices and \c 3n edges. The Petersen graph + * itself is G(5, 2). + * + * + * Reference: + * + * + * M. E. Watkins, + * A Theorem on Tait Colorings with an Application to the Generalized Petersen Graphs, + * Journal of Combinatorial Theory 6, 152-164 (1969). + * https://doi.org/10.1016%2FS0021-9800%2869%2980116-X + * + * \param graph Pointer to an uninitialized graph object, the result will + * be stored here. + * \param n Integer, \c n is the number of vertices in the inner and outer + * cycle/circulant graphs. It must be at least 3. + * \param k Integer, \c k is the shift of the circulant graph. It must be + * positive and less than n/2. + * \return Error code. + * + * \sa \ref igraph_famous() for the original Petersen graph. + * + * Time complexity: O(|V|), the number of vertices in the graph. + */ +igraph_error_t igraph_generalized_petersen(igraph_t *graph, igraph_integer_t n, igraph_integer_t k) { + /* This is a generalized Petersen graph constructor */ + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes, no_of_edges2; + igraph_integer_t i; + + if (n < 3) { + IGRAPH_ERRORF("n = %" IGRAPH_PRId " must be at least 3.", IGRAPH_EINVAL, n); + } + + IGRAPH_SAFE_MULT(n, 2, &no_of_nodes); + + /* The seemingly redundant k < n check avoids integer overflow on 2*k in 2*k < n. + * Note that 2*n has already been checked not to overflow above. */ + if (! (k > 0 && k < n && 2*k < n)) { + IGRAPH_ERRORF("k = %" IGRAPH_PRId " must be positive and less than n/2 with n = %" IGRAPH_PRId ".", IGRAPH_EINVAL, k, n); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_SAFE_MULT(n, 6, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + + for (i = 0; i < n; i++) { + igraph_vector_int_push_back(&edges, i); + igraph_vector_int_push_back(&edges, (i + 1) % n); + igraph_vector_int_push_back(&edges, i); + igraph_vector_int_push_back(&edges, i + n); + igraph_vector_int_push_back(&edges, i + n); + igraph_vector_int_push_back(&edges, ((i + k) % n) + n); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_UNDIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/kautz.c b/src/constructors/kautz.c new file mode 100644 index 0000000..7684847 --- /dev/null +++ b/src/constructors/kautz.c @@ -0,0 +1,211 @@ +/* -*- 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" +#include "math/safe_intop.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. + */ +igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { + + /* m+1 - number of symbols */ + /* n+1 - length of strings */ + + igraph_integer_t no_of_nodes, no_of_edges; + igraph_integer_t allstrings; + igraph_integer_t i, j, idx = 0; + igraph_vector_int_t edges; + igraph_vector_int_t digits, table; + igraph_vector_int_t index1, index2; + igraph_integer_t actb = 0; + igraph_integer_t actvalue = 0; + int iter = 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 = ((m + 1) * pow(m, n)) */ + { + igraph_real_t m_to_pow_n_real = pow(m, n); + igraph_integer_t m_to_pow_n = m_to_pow_n_real; + if (m_to_pow_n != m_to_pow_n_real) { + IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for Kautz graph.", IGRAPH_EINVAL, + m, n); + } + IGRAPH_SAFE_MULT(m+1, m_to_pow_n, &no_of_nodes); + } + /* no_of_edges = m * no_of_nodes */ + IGRAPH_SAFE_MULT(no_of_nodes, m, &no_of_edges); + + { + igraph_real_t allstrings_real = pow(m + 1, n + 1); + allstrings = allstrings_real; + if (allstrings != allstrings_real) { + IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for Kautz graph.", IGRAPH_EINVAL, + m, n); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + IGRAPH_CHECK(igraph_vector_int_init(&table, n + 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &table); + j = 1; + for (i = n; i >= 0; i--) { + VECTOR(table)[i] = j; + j *= (m + 1); + } + + IGRAPH_CHECK(igraph_vector_int_init(&digits, n + 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &digits); + IGRAPH_CHECK(igraph_vector_int_init(&index1, pow(m + 1, n + 1))); + IGRAPH_FINALLY(igraph_vector_int_destroy, &index1); + IGRAPH_CHECK(igraph_vector_int_init(&index2, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &index2); + + /* Fill the index tables*/ + while (true) { + /* at the beginning of the loop, 0:actb contain the valid prefix */ + /* we might need to fill it to get a valid string */ + igraph_integer_t 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 (true) { + /* try to increase digits at position actb */ + igraph_integer_t 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_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + } + + /* Now come the edges at last */ + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t fromvalue = VECTOR(index2)[i]; + igraph_integer_t lastdigit = fromvalue % (m + 1); + igraph_integer_t basis = (fromvalue * (m + 1)) % allstrings; + for (j = 0; j <= m; j++) { + igraph_integer_t tovalue, to; + if (j == lastdigit) { + continue; + } + tovalue = basis + j; + to = VECTOR(index1)[tovalue] - 1; + if (to < 0) { + continue; + } + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + } + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 10); + } + + igraph_vector_int_destroy(&index2); + igraph_vector_int_destroy(&index1); + igraph_vector_int_destroy(&digits); + igraph_vector_int_destroy(&table); + IGRAPH_FINALLY_CLEAN(4); + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_DIRECTED)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/lattices.c b/src/constructors/lattices.c new file mode 100644 index 0000000..5126178 --- /dev/null +++ b/src/constructors/lattices.c @@ -0,0 +1,603 @@ +/* + 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, see . +*/ + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "core/interruption.h" +#include "math/safe_intop.h" + +#define MIN(n, m) (n < m ? n : m) +#define MAX(n, m) (n < m ? m : n) + +#define VERTEX_INDEX(i, j) \ + lex_ordering ? row_count * (i - VECTOR(*row_start_vector)[j]) + j : (VECTOR(row_lengths_prefix_sum_vector)[j] + i - VECTOR(*row_start_vector)[j]) + +#define ROW_END(j) (VECTOR(*row_start_vector)[j] + VECTOR(*row_lengths_vector)[j] - 1) +#define ADD_EDGE_IJ_KL_IF_EXISTS(i, j, k, l) \ + if (VECTOR(*row_start_vector)[l] <= k && k <= ROW_END(l) && 0 <= l && l <= row_count - 1) \ + { \ + igraph_vector_int_push_back(&edges, VERTEX_INDEX((i), (j))); /* reserved */ \ + igraph_vector_int_push_back(&edges, VERTEX_INDEX((k), (l))); /* reserved */ \ + if (directed && mutual) \ + { \ + igraph_vector_int_push_back(&edges, VERTEX_INDEX((k), (l))); /* reserved */ \ + igraph_vector_int_push_back(&edges, VERTEX_INDEX((i), (j))); /* reserved */ \ + } \ + } + +#define COMPUTE_NUMBER_OF_VERTICES() \ + do \ + { \ + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_prefix_sum_vector, row_count + 1); \ + VECTOR(row_lengths_prefix_sum_vector)[0] = 0; \ + for (i = 1; i < row_count + 1; i++) \ + { \ + IGRAPH_SAFE_ADD(VECTOR(row_lengths_prefix_sum_vector)[i - 1], VECTOR(*row_lengths_vector)[i - 1], &(VECTOR(row_lengths_prefix_sum_vector)[i])); \ + } \ + no_of_nodes = VECTOR(row_lengths_prefix_sum_vector)[row_count]; \ + } while (0) + +/** + * Creates a triangular lattice whose vertices have the form (i, j) for non-negative integers i and j + * and (i, j) is connected with (i + 1, j), (i, j + 1), and (i - 1, j + 1) provided a vertex + * exists. Thus, all vertices have degree at most 6. + * + * + * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being + * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) unless + * \c lex_ordering is set to true in which case the roles of the coordinates are reversed. + * + * \param graph An uninitialized graph object. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual argument is 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 lex_ordering Boolean, set to true if the vertices of the resulting graph are ordered + * lexicographically with the 1st coordinate being more significant. Use only when all the + * rows have the number of vertices. + * \param row_lengths_vector Integer vector, defines the number of vertices with + * the second coordinate equal to the index. The length of this vector must match + * the length of \p row_start_vector. All coordinates must be non-negative. + * \param row_start_vector Integer vector, defines the leftmost coordinate of + * the vertex with the second coordinate equal to the index. + * + * \return Error code: + * \c IGRAPH_EINVAL: invalid (negative) length of row_lengths_vector does not match the length of the + * row_start_vector. + * + * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. + */ +static igraph_error_t triangular_lattice( + igraph_t *graph, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t lex_ordering, + const igraph_vector_int_t *row_lengths_vector, const igraph_vector_int_t *row_start_vector) { + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t row_count = igraph_vector_int_size(row_lengths_vector); + igraph_integer_t no_of_nodes; + igraph_vector_int_t row_lengths_prefix_sum_vector; + igraph_integer_t i, j; + + if (igraph_vector_int_size(row_lengths_vector) != igraph_vector_int_size(row_start_vector)) { + IGRAPH_ERRORF( + "Length of row_lengths_vector vector (%" IGRAPH_PRId ") must match the length of the " + "row_start_vector (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_int_size(row_lengths_vector), + igraph_vector_int_size(row_start_vector)); + } + + + if (row_count > 0 && lex_ordering && !igraph_vector_int_isininterval(row_lengths_vector, VECTOR(*row_lengths_vector)[0], VECTOR(*row_lengths_vector)[0])) { + IGRAPH_ERROR( + "row_lengths_vector must have all the coordinates the same", IGRAPH_EINVAL); + } + + for (i = 0; i < row_count; i++) { + if (VECTOR(*row_lengths_vector)[i] < 0) { + IGRAPH_ERRORF( + "row_lengths_vector vector must have non-negative coordinates, " + "was (%" IGRAPH_PRId ") for the (%" IGRAPH_PRId ")-th row.", + IGRAPH_EINVAL, VECTOR(*row_lengths_vector)[i], i); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + COMPUTE_NUMBER_OF_VERTICES(); + + /* computing the number of edges in the constructed triangular lattice */ + igraph_integer_t no_of_edges2 = VECTOR(*row_lengths_vector)[row_count - 1] - 1; + igraph_integer_t multiplier = mutual && directed ? 4 : 2; + for (j = 0; j < row_count - 1; j++) { + IGRAPH_SAFE_ADD(no_of_edges2, VECTOR(*row_lengths_vector)[j] - 1, &no_of_edges2); + IGRAPH_SAFE_ADD(no_of_edges2, MIN(ROW_END(j), ROW_END((j + 1))) - MAX(VECTOR(*row_start_vector)[j], VECTOR(*row_start_vector)[j + 1]) + 1, + &no_of_edges2); + IGRAPH_SAFE_ADD(no_of_edges2, MIN(ROW_END(j), ROW_END((j + 1)) + 1) - MAX(VECTOR(*row_start_vector)[j], VECTOR(*row_start_vector)[j + 1] + 1) + 1, + &no_of_edges2); + } + IGRAPH_SAFE_MULT(no_of_edges2, multiplier, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + + /* constructing the edge array */ + igraph_integer_t k; + for (j = 0; j < row_count; j++) { + IGRAPH_ALLOW_INTERRUPTION(); + for (i = 0; i < VECTOR(*row_lengths_vector)[j]; i++) { + k = VECTOR(*row_start_vector)[j] + i; + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k + 1), j); + if (j < row_count - 1) { + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, k, (j + 1)); + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k - 1), (j + 1)); + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&row_lengths_prefix_sum_vector); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t triangular_lattice_triangle_shape(igraph_t *graph, igraph_integer_t size, igraph_bool_t directed, igraph_bool_t mutual) { + igraph_integer_t row_count = size; + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + for (i = 0; i < row_count; i++) { + VECTOR(row_lengths_vector)[i] = size - i; + VECTOR(row_start_vector)[i] = 0; + } + + IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t triangular_lattice_rectangle_shape( + igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, + igraph_bool_t directed, igraph_bool_t mutual) { + igraph_integer_t row_count = size_x; + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + for (i = 0; i < row_count; i++) { + VECTOR(row_lengths_vector)[i] = size_y; + VECTOR(row_start_vector)[i] = (row_count - i) / 2; + } + + IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t triangular_lattice_hex_shape( + igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, + igraph_integer_t size_z, igraph_bool_t directed, igraph_bool_t mutual) { + igraph_integer_t row_count = size_y + size_z - 1; + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + igraph_integer_t row_length = size_x; + igraph_integer_t row_start = size_y - 1; + igraph_integer_t first_threshold = MIN(size_y - 1, size_z - 1); + igraph_integer_t second_threshold = MAX(size_y - 1, size_z - 1); + igraph_integer_t sgn_flag = size_y < size_z ? 0 : -1; + + for (i = 0; i < row_count; i++) { + VECTOR(row_lengths_vector)[i] = row_length; + VECTOR(row_start_vector)[i] = row_start; + + if (i < first_threshold) { + row_length++; + row_start--; + } else if (i < second_threshold) { + row_start += sgn_flag; + } else { + row_length--; + } + } + + IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_triangular_lattice + * \brief A triangular lattice with the given shape. + * + * \experimental + * + * Creates a triangular lattice whose vertices have the form (i, j) for non-negative integers i and j + * and (i, j) is generally connected with (i + 1, j), (i, j + 1), and (i - 1, j + 1). + * The function constructs a planar dual of the graph constructed by \ref igraph_hexagonal_lattice(). + * In particular, there a one-to-one correspondence between the vertices in the constructed graph + * and the cycles of length 6 in the graph constructed by \ref igraph_hexagonal_lattice() + * with the same \p dims parameter. + * + * + * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being + * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) + * + * \param graph An uninitialized graph object. + * \param dims Integer vector, defines the shape of the lattice. (Below the "edge length"s are in terms of graph theoretical path lengths.) + * If \p dims is of length 1, the resulting lattice has a triangular shape + * where each side of the triangle contains dims[0] vertices. + * If \p dims is of length 2, the resulting lattice has a + * "quasi rectangular" shape with the sides containing dims[0] and + * dims[1] vertices, respectively. + * If \p dims is of length 3, the resulting lattice has a hexagonal shape + * where the sides of the hexagon contain dims[0], dims[1] and + * dims[2] vertices. + * All coordinates must be non-negative. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual argument is 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. + * \return Error code: + * \c IGRAPH_EINVAL: The size of \p dims must be either 1, 2, or 3 with all the components + * at least 1. + * \sa \ref igraph_hexagonal_lattice() for creating a triangular lattice. + * + * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. + * + */ +igraph_error_t igraph_triangular_lattice( + igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, + igraph_bool_t mutual) { + igraph_integer_t num_dims = igraph_vector_int_size(dims); + if (igraph_vector_int_any_smaller(dims, 0)) { + IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); + } + /* If a coordinate of dims is 0 the result is an empty graph. */ + if (igraph_vector_int_contains(dims, 0)) { + return igraph_empty(graph, 0, directed); + } + + switch (num_dims) { + case 1: + IGRAPH_CHECK(triangular_lattice_triangle_shape(graph, VECTOR(*dims)[0], directed, mutual)); + break; + case 2: + IGRAPH_CHECK(triangular_lattice_rectangle_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], directed, mutual)); + break; + case 3: + IGRAPH_CHECK(triangular_lattice_hex_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], VECTOR(*dims)[2], directed, mutual)); + break; + default: + IGRAPH_ERRORF( + "The size of the dimension vector must be 1, 2 or 3, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, num_dims); + } + + return IGRAPH_SUCCESS; +} + + +/** + * Creates a hexagonal lattice whose vertices have the form (i, j) for non-negative integers i and j + * and (i, j) is connected with (i + 1, j), and if i is odd also with (i - 1, j + 1) provided a vertex + * exists. Thus, all vertices have degree at most 3. + * + * + * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being + * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1). + * + * \param graph An uninitialized graph object. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual argument is 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 row_lengths_vector Integer vector, defines the number of vertices with + * the second coordinate equal to the index. The length of this vector must match + * the length of \p row_start_vector. All coordinates must be non-negative. + * \param row_start_vector Integer vector, defines the leftmost coordinate of + * the vertex with the second coordinate equal to the index. + * + * \return Error code: + * \c IGRAPH_EINVAL: invalid (negative) length of row_lengths_vector does not match the length of the + * row_start_vector. + * + * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. + */ +static igraph_error_t hexagonal_lattice( + igraph_t *graph, igraph_bool_t directed, igraph_bool_t mutual, + const igraph_vector_int_t *row_lengths_vector, const igraph_vector_int_t *row_start_vector +) { + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t row_count = igraph_vector_int_size(row_lengths_vector); + igraph_integer_t no_of_nodes; + igraph_vector_int_t row_lengths_prefix_sum_vector; + igraph_integer_t i, j; + igraph_bool_t lex_ordering = false; + + if (igraph_vector_int_size(row_lengths_vector) != igraph_vector_int_size(row_start_vector)) { + IGRAPH_ERRORF( + "Length of row_lengths_vector vector (%" IGRAPH_PRId ") must match the length of the " + "row_start_vector (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_int_size(row_lengths_vector), + igraph_vector_int_size(row_start_vector) + ); + } + + for (i = 0; i < row_count; i++) { + if (VECTOR(*row_lengths_vector)[i] < 0) { + IGRAPH_ERRORF( + "row_lengths_vector vector must have non-negative coordinates, " + "was (%" IGRAPH_PRId ") for the (%" IGRAPH_PRId ")-th row.", + IGRAPH_EINVAL, VECTOR(*row_lengths_vector)[i], i); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + COMPUTE_NUMBER_OF_VERTICES(); + + /* computing the number of edges in the constructed hex lattice */ + igraph_integer_t no_of_edges2 = VECTOR(*row_lengths_vector)[row_count - 1] - 1; + igraph_integer_t multiplier = mutual && directed ? 4 : 2, low, high; + for (j = 0; j < row_count - 1; j++) { + IGRAPH_SAFE_ADD(no_of_edges2, VECTOR(*row_lengths_vector)[j] - 1, &no_of_edges2); + low = MAX((VECTOR(*row_start_vector)[j] - 1), (VECTOR(*row_start_vector)[j + 1])); + low = low % 2 ? low + 1 : low; + high = MIN((ROW_END(j) - 1), (ROW_END(j + 1))); + high = high % 2 ? high - 1 : high; + IGRAPH_SAFE_ADD(no_of_edges2, (high - low) / 2 + 1, &no_of_edges2); + } + IGRAPH_SAFE_MULT(no_of_edges2, multiplier, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + + /* constructing the edge array */ + igraph_integer_t k; + for (j = 0; j < row_count; j++) { + IGRAPH_ALLOW_INTERRUPTION(); + for (i = 0; i < VECTOR(*row_lengths_vector)[j]; i++) { + k = VECTOR(*row_start_vector)[j] + i; + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k + 1), j); + if (j < row_count - 1 && k % 2 == 1) { + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k - 1), (j + 1)); + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&row_lengths_prefix_sum_vector); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t hexagonal_lattice_triangle_shape(igraph_t *graph, igraph_integer_t size, igraph_bool_t directed, igraph_bool_t mutual) { + igraph_integer_t row_count; + IGRAPH_SAFE_ADD(size, 2, &row_count); + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count - 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count - 1); + + for (i = 0; i < row_count - 1; i++) { + VECTOR(row_lengths_vector)[i] = 2 * (row_count - i) - (i ? 1 : 3); + VECTOR(row_start_vector)[i] = (i ? 0 : 1); + } + + IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t hexagonal_lattice_rectangle_shape( + igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, + igraph_bool_t directed, igraph_bool_t mutual +) { + igraph_integer_t row_count; + IGRAPH_SAFE_ADD(size_x, 1, &row_count); + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t actual_size_y; + IGRAPH_SAFE_ADD(size_y, 1, &actual_size_y); + IGRAPH_SAFE_MULT(actual_size_y, 2, &actual_size_y); + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + igraph_bool_t is_first_row, is_last_row, is_start_odd; + + for (i = 0; i < row_count; i++) { + is_first_row = (i == 0); + is_last_row = i == row_count - 1; + is_start_odd = (row_count - i - 1) % 2; + VECTOR(row_lengths_vector)[i] = actual_size_y - (is_first_row || is_last_row ? 1 : 0); + VECTOR(row_start_vector)[i] = row_count - i - 1 + (is_first_row && !is_start_odd ? 1 : 0); + } + + IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t hexagonal_lattice_hex_shape( + igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, + igraph_integer_t size_z, igraph_bool_t directed, igraph_bool_t mutual +) { + igraph_integer_t row_count = size_y + size_z; + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + igraph_integer_t row_length; + IGRAPH_SAFE_MULT(size_x, 2, &row_length); + IGRAPH_SAFE_ADD(row_length, 1, &row_length); + igraph_integer_t row_start; + IGRAPH_SAFE_MULT(size_y, 2, &row_start); + IGRAPH_SAFE_ADD(row_start, -1, &row_start); + igraph_integer_t first_threshold = MIN(size_y - 1, size_z - 1); + igraph_integer_t second_threshold = MAX(size_y - 1, size_z - 1); + igraph_integer_t sgn_flag = size_y < size_z ? 0 : -2; + + for (i = 0; i < row_count; i++) { + VECTOR(row_lengths_vector)[i] = row_length; + VECTOR(row_start_vector)[i] = row_start; + + if (i < first_threshold) { + row_length += 2; + row_start -= 2; + } else if (i < second_threshold) { + row_start += sgn_flag; + } else { + row_length -= 2; + } + if (i == size_y - 1) { + row_start--; + row_length++; + } + if (i == size_z - 1) { + row_length++; + } + } + + IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hexagonal_lattice + * \brief A hexagonal lattice with the given shape. + * + * \experimental + * + * Creates a hexagonal lattice whose vertices have the form (i, j) for non-negative integers i and j + * and (i, j) is generally connected with (i + 1, j), and if i is odd also with (i - 1, j + 1). + * The function constructs a planar dual of the graph constructed by \ref igraph_triangular_lattice(). + * In particular, there a one-to-one correspondence between the cycles of length 6 in the constructed graph + * and the vertices of the graph constructed by \ref igraph_triangular_lattice() function + * with the same \p dims parameter. + * + * + * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being + * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) + * + * \param graph An uninitialized graph object. + * \param dims Integer vector, defines the shape of the lattice. (Below the "edge length"s are in terms of graph theoretical path lengths.) + * If \p dims is of length 1, the resulting lattice has a triangular shape + * where each side of the triangle contains dims[0] vertices. + * If \p dims is of length 2, the resulting lattice has a + * "quasi rectangular" shape with the sides containing dims[0] and + * dims[1] vertices, respectively. + * If \p dims is of length 3, the resulting lattice has a hexagonal shape + * where the sides of the hexagon contain dims[0], dims[1] and + * dims[2] vertices. + * All coordinates must be non-negative. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual argument is 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. + * \return Error code: + * \c IGRAPH_EINVAL: The size of \p dims must be either 1, 2, or 3 with all the components + * at least 1. + * \sa \ref igraph_triangular_lattice() for creating a triangular lattice. + * + * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. + * + */ +igraph_error_t igraph_hexagonal_lattice( + igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, + igraph_bool_t mutual +) { + igraph_integer_t num_dims = igraph_vector_int_size(dims); + if (igraph_vector_int_any_smaller(dims, 0)) { + IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); + } + /* If a coordinate of dims is 0 the result is an empty graph. */ + if (igraph_vector_int_any_smaller(dims, 1)) { + return igraph_empty(graph, 0, directed); + } + + switch (num_dims) { + case 1: + IGRAPH_CHECK(hexagonal_lattice_triangle_shape(graph, VECTOR(*dims)[0], directed, mutual)); + break; + case 2: + IGRAPH_CHECK(hexagonal_lattice_rectangle_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], directed, mutual)); + break; + case 3: + IGRAPH_CHECK(hexagonal_lattice_hex_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], VECTOR(*dims)[2], directed, mutual)); + break; + default: + IGRAPH_ERRORF( + "The size of the dimension vector must be 1, 2 or 3, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, num_dims + ); + } + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/lcf.c b/src/constructors/lcf.c new file mode 100644 index 0000000..329255d --- /dev/null +++ b/src/constructors/lcf.c @@ -0,0 +1,155 @@ +/* -*- 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" + +#include "math/safe_intop.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. + */ +igraph_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, + const igraph_vector_int_t *shifts, + igraph_integer_t repeats) { + + igraph_vector_int_t edges; + igraph_integer_t no_of_shifts = igraph_vector_int_size(shifts); + igraph_integer_t ptr = 0, i, sptr = 0; + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_edges = n + no_of_shifts * repeats; + igraph_integer_t no_of_edges2; + + if (repeats < 0) { + IGRAPH_ERROR("Number of repeats must not be negative.", IGRAPH_EINVAL); + } + + /* no_of_edges = n + no_of_shifts * repeats */ + IGRAPH_SAFE_MULT(no_of_shifts, repeats, &no_of_edges); + IGRAPH_SAFE_ADD(no_of_edges, n, &no_of_edges); + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + + 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) { + igraph_integer_t sh = VECTOR(*shifts)[sptr % no_of_shifts]; + igraph_integer_t from = sptr % no_of_nodes; + igraph_integer_t to = (no_of_nodes + sptr + sh) % no_of_nodes; + VECTOR(edges)[ptr++] = from; + VECTOR(edges)[ptr++] = to; + sptr++; + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_UNDIRECTED)); + IGRAPH_CHECK(igraph_simplify(graph, true, true, NULL)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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 + * https://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 + */ +igraph_error_t igraph_lcf(igraph_t *graph, igraph_integer_t n, ...) { + igraph_vector_int_t shifts; + igraph_integer_t repeats; + va_list ap; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&shifts, 0); + + va_start(ap, n); + while (1) { + igraph_error_t err; + int num = va_arg(ap, int); + if (num == 0) { + break; + } + err = igraph_vector_int_push_back(&shifts, num); + if (err != IGRAPH_SUCCESS) { + va_end(ap); + IGRAPH_ERROR("", err); + } + } + va_end(ap); + if (igraph_vector_int_size(&shifts) == 0) { + repeats = 0; + } else { + repeats = igraph_vector_int_pop_back(&shifts); + } + + IGRAPH_CHECK(igraph_lcf_vector(graph, n, &shifts, repeats)); + igraph_vector_int_destroy(&shifts); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/linegraph.c b/src/constructors/linegraph.c new file mode 100644 index 0000000..cad6070 --- /dev/null +++ b/src/constructors/linegraph.c @@ -0,0 +1,175 @@ +/* -*- 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 igraph_error_t igraph_i_linegraph_undirected(const igraph_t *graph, igraph_t *linegraph) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t adjedges, adjedges2; + igraph_vector_int_t edges; + igraph_integer_t prev = -1; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjedges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjedges2, 0); + + for (igraph_integer_t e1 = 0; e1 < no_of_edges; e1++) { + igraph_integer_t from = IGRAPH_FROM(graph, e1); + igraph_integer_t to = IGRAPH_TO(graph, e1); + igraph_integer_t n; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (from != prev) { + IGRAPH_CHECK(igraph_incident(graph, &adjedges, from, IGRAPH_ALL)); + } + n = igraph_vector_int_size(&adjedges); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t e2 = VECTOR(adjedges)[i]; + if (e2 < e1) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e2)); + } + } + + IGRAPH_CHECK(igraph_incident(graph, &adjedges2, to, IGRAPH_ALL)); + n = igraph_vector_int_size(&adjedges2); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t e2 = VECTOR(adjedges2)[i]; + if (e2 < e1) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e2)); + } + } + + /* Self-loops are considered self-adjacent. */ + if (from == to) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e1)); + } + + prev = from; + } + + igraph_vector_int_destroy(&adjedges); + igraph_vector_int_destroy(&adjedges2); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(linegraph, &edges, no_of_edges, IGRAPH_UNDIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegraph) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, n; + igraph_vector_int_t adjedges; + igraph_vector_int_t edges; + igraph_integer_t prev = -1; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjedges, 0); + + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + + IGRAPH_ALLOW_INTERRUPTION(); + + if (from != prev) { + IGRAPH_CHECK(igraph_incident(graph, &adjedges, from, IGRAPH_IN)); + } + n = igraph_vector_int_size(&adjedges); + for (j = 0; j < n; j++) { + igraph_integer_t e = VECTOR(adjedges)[j]; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + } + + prev = from; + } + + igraph_vector_int_destroy(&adjedges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_create(linegraph, &edges, no_of_edges, igraph_is_directed(graph))); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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 single vertex of an undirected self-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. + * + * + * Self-loops are considered self-adjacent, thus their corresponding vertex + * in the line graph will also a have a single self-loop, in both undirected + * and directed graphs. + * + * + * 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. + */ + +igraph_error_t 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/constructors/prufer.c b/src/constructors/prufer.c new file mode 100644 index 0000000..d2de905 --- /dev/null +++ b/src/constructors/prufer.c @@ -0,0 +1,126 @@ +/* -*- 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 "math/safe_intop.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_kary_tree(), \ref igraph_tree_game() + * + * Time complexity: O(|V|), where |V| is the number of vertices in the tree. + * + */ +igraph_error_t igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer) { + igraph_vector_int_t degree; + igraph_vector_int_t edges; + igraph_integer_t n; + igraph_integer_t i, k; + igraph_integer_t u, v; /* vertices */ + igraph_integer_t ec; + + IGRAPH_SAFE_ADD(igraph_vector_int_size(prufer), 2, &n); + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, n); /* initializes vector to zeros */ + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(n - 1, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } + + /* build out-degree vector (i.e. number of child vertices) and verify Prufer sequence */ + for (i = 0; i < n - 2; ++i) { + igraph_integer_t w = VECTOR(*prufer)[i]; + if (w >= n || w < 0) { + IGRAPH_ERROR("Invalid Prufer sequence.", IGRAPH_EINVAL); + } + VECTOR(degree)[w] += 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, n, IGRAPH_UNDIRECTED)); + + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/regular.c b/src/constructors/regular.c new file mode 100644 index 0000000..858b6d8 --- /dev/null +++ b/src/constructors/regular.c @@ -0,0 +1,1003 @@ +/* -*- 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" +#include "math/safe_intop.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_square_lattice(), \ref igraph_ring(), \ref igraph_kary_tree() + * for creating other regular structures. + * + * \example examples/simple/igraph_star.c + */ +igraph_error_t igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, + igraph_integer_t center) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t 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 star mode.", IGRAPH_EINVMODE); + } + + if (mode != IGRAPH_STAR_MUTUAL) { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(n-1, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } else { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(n-1, 4, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } + + 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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_wheel + * \brief Creates a \em wheel graph, a union of a star and a cycle graph. + * + * A wheel graph on \p n vertices can be thought of as a wheel with + * n - 1 spokes. The cycle graph part makes up the rim, + * while the star graph part adds the spokes. + * + * + * Note that the two and three-vertex wheel graphs are non-simple: + * The two-vertex wheel graph contains a self-loop, while the three-vertex + * wheel graph contains parallel edges (a 1-cycle and a 2-cycle, respectively). + * + * \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_WHEEL_OUT + * directed wheel graph, edges point + * \em from the center to the other vertices. + * \cli IGRAPH_WHEEL_IN + * directed wheel graph, edges point + * \em to the center from the other vertices. + * \cli IGRAPH_WHEEL_MUTUAL + * directed wheel graph with mutual edges. + * \cli IGRAPH_WHEEL_UNDIRECTED + * an undirected wheel 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_square_lattice(), \ref igraph_ring(), \ref igraph_star(), + * \ref igraph_kary_tree() for creating other regular structures. + * + */ + +igraph_error_t igraph_wheel(igraph_t *graph, igraph_integer_t n, igraph_wheel_mode_t mode, + igraph_integer_t center) { + + igraph_star_mode_t star_mode; + igraph_vector_int_t rim_edges = IGRAPH_VECTOR_NULL; + igraph_integer_t i; + + /* Firstly creates a star by the function \ref igraph_star() and makes + * use of its existing input parameter checking ability, it can check + * "Invalid number of vertices" and "Invalid center vertex". */ + switch (mode) + { + case IGRAPH_WHEEL_OUT: + star_mode = IGRAPH_STAR_OUT; + break; + case IGRAPH_WHEEL_IN: + star_mode = IGRAPH_STAR_IN; + break; + case IGRAPH_WHEEL_MUTUAL: + star_mode = IGRAPH_STAR_MUTUAL; + break; + case IGRAPH_WHEEL_UNDIRECTED: + star_mode = IGRAPH_STAR_UNDIRECTED; + break; + default: + IGRAPH_ERROR("Invalid wheel graph mode.", IGRAPH_EINVMODE); + } + + IGRAPH_CHECK(igraph_star(graph, n, star_mode, center)); + + /* If n <= 1, wheel graph is identical with star graph, + * no further processing is needed. */ + if (n <= 1) { + return IGRAPH_SUCCESS; + } + + /* Register the star for deallocation in case of error flow before + * the entire wheel is successfully created. */ + IGRAPH_FINALLY(igraph_destroy, graph); + + /* Add edges to the rim. As the rim (or cycle) has n - 1 vertices, + * it will have n - 1 edges. For MUTUAL mode, number of edges + * will be double. */ + if (mode == IGRAPH_WHEEL_MUTUAL) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&rim_edges, 4 * (n-1)); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&rim_edges, 2 * (n-1)); + } + + /* Assign first n-1 edges (MUTUAL will be handled later). */ + for (i = 0; i < n-2; i++) { + if ( i < center ) { + VECTOR(rim_edges)[2 * i] = i; + if ( i + 1 < center ) { + VECTOR(rim_edges)[2 * i + 1] = i + 1; + } else { + VECTOR(rim_edges)[2 * i + 1] = i + 2; + } + } else { + VECTOR(rim_edges)[2 * i] = i + 1; + VECTOR(rim_edges)[2 * i + 1] = i + 2; + } + } + + /* Assign the last edge (MUTUAL will be handled later). */ + if ( n - 2 < center ) { + VECTOR(rim_edges)[2 * n - 4] = n - 2; + } else { + VECTOR(rim_edges)[2 * n - 4] = n - 1; + } + if ( center > 0 ) { + VECTOR(rim_edges)[2 * n - 3] = 0; + } else { + VECTOR(rim_edges)[2 * n - 3] = 1; + } + + /* For MUTUAL mode, add reverse-direction edges. */ + if (mode == IGRAPH_WHEEL_MUTUAL) { + for (i=0; i < 2 * (n-1); i++) { + VECTOR(rim_edges)[4 * (n-1) - 1 - i] = VECTOR(rim_edges)[i]; + } + } + + /* Combine the rim into the star to make it a wheel graph. */ + IGRAPH_CHECK(igraph_add_edges(graph, &rim_edges, NULL)); + + igraph_vector_int_destroy(&rim_edges); + + /* 2 instead of 1 because the star graph is registered before. */ + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_lattice + * \brief Arbitrary dimensional square lattices (deprecated). + * + * \deprecated-by igraph_square_lattice 0.10.0 + */ +igraph_error_t igraph_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, + igraph_integer_t nei, igraph_bool_t directed, igraph_bool_t mutual, + igraph_bool_t circular) { + igraph_vector_bool_t periodic; + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&periodic, igraph_vector_int_size(dimvector)); + igraph_vector_bool_fill(&periodic, circular); + + IGRAPH_CHECK(igraph_square_lattice(graph, dimvector, nei, directed, mutual, &periodic)); + + igraph_vector_bool_destroy(&periodic); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_square_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_1, i_2, i_3, ..., i_d) + * in a lattice of size (n_1, n_2, ..., n_d) will be + * i_1 + n_1 * i_2 + n_1 * n_2 * i_3 + .... + * + * \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 periodic Boolean vector, defines whether the generated lattice is + * periodic along each dimension. The length of this vector must match + * the length of \p dimvector. This parameter may also be \c NULL, which + * implies that the lattice will not be periodic. + * \return Error code: + * \c IGRAPH_EINVAL: invalid (negative) dimension vector or mismatch + * between the length of the dimension vector and the periodicity 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. + */ +igraph_error_t igraph_square_lattice( + igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, + igraph_bool_t directed, igraph_bool_t mutual, const igraph_vector_bool_t *periodic +) { + + igraph_integer_t dims = igraph_vector_int_size(dimvector); + igraph_integer_t no_of_nodes; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t *coords, *weights; + igraph_integer_t i, j; + int carry, pos; + int iter = 0; + + if (igraph_vector_int_any_smaller(dimvector, 0)) { + IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); + } + + if (periodic && igraph_vector_bool_size(periodic) != dims) { + IGRAPH_ERRORF( + "Length of periodicity vector must match the length of the " + "dimension vector (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, dims + ); + } + + /* compute no. of nodes in overflow-safe manner */ + IGRAPH_CHECK(igraph_i_safe_vector_int_prod(dimvector, &no_of_nodes)); + + /* init coords & weights */ + + coords = IGRAPH_CALLOC(dims, igraph_integer_t); + IGRAPH_CHECK_OOM(coords, "Lattice creation failed."); + IGRAPH_FINALLY(igraph_free, coords); + + weights = IGRAPH_CALLOC(dims, igraph_integer_t); + IGRAPH_CHECK_OOM(weights, "Lattice creation failed."); + IGRAPH_FINALLY(igraph_free, weights); + + if (dims > 0) { + weights[0] = 1; + for (i = 1; i < dims; i++) { + weights[i] = weights[i - 1] * VECTOR(*dimvector)[i - 1]; + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + if (mutual && directed) { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_nodes, dims, &no_of_edges2); + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + } else { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_nodes, dims, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + } + +#define IS_PERIODIC(dim) ((periodic && VECTOR(*periodic)[dim])) + + for (i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 10); + + /* Connect the current node to the "next" node along each dimension */ + for (j = 0; j < dims; j++) { + igraph_bool_t is_periodic = IS_PERIODIC(j); + + if (is_periodic|| coords[j] != VECTOR(*dimvector)[j] - 1) { + igraph_integer_t new_nei; + if (coords[j] != VECTOR(*dimvector)[j] - 1) { + new_nei = i + weights[j] + 1; + } else { + new_nei = i - (VECTOR(*dimvector)[j] - 1) * weights[j] + 1; + } + if (new_nei != i + 1 && + (VECTOR(*dimvector)[j] != 2 || coords[j] != 1 || directed)) { + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, new_nei - 1); /* reserved */ + } + } /* if is_periodic || coords[j] */ + if (mutual && directed && (is_periodic || coords[j] != 0)) { + igraph_integer_t new_nei; + if (coords[j] != 0) { + new_nei = i - weights[j] + 1; + } else { + new_nei = i + (VECTOR(*dimvector)[j] - 1) * weights[j] + 1; + } + if (new_nei != i + 1 && + (VECTOR(*dimvector)[j] != 2 || !is_periodic)) { + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, new_nei - 1); /* reserved */ + } + } /* if is_periodic || coords[0] */ + } /* for j= 2) { + IGRAPH_CHECK(igraph_connect_neighborhood(graph, nei, IGRAPH_ALL)); + } + + /* clean up */ + IGRAPH_FREE(coords); + IGRAPH_FREE(weights); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * When \p n is 1 or 2, the result may not be a simple graph: + * the one-cycle contains a self-loop and the undirected or reciprocally + * connected directed two-cycle contains parallel edges. + * + * \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_square_lattice() for generating more general lattices. + * + * \example examples/simple/igraph_ring.c + */ +igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + igraph_bool_t mutual, igraph_bool_t circular) { + + igraph_vector_int_t edges; + igraph_integer_t no_of_edges, no_of_edges2; + igraph_integer_t i; + + if (n < 0) { + IGRAPH_ERRORF("The number of vertices must be non-negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); + } + + if (n == 0) { + return igraph_empty(graph, 0, directed); + } + + no_of_edges = circular ? n : n-1; + if (directed && mutual) { + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges); + } + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + + if (directed && mutual) { + for (i=0; i < n-1; ++i) { + VECTOR(edges)[4*i] = i; + VECTOR(edges)[4*i+1] = i+1; + VECTOR(edges)[4*i+2] = i+1; + VECTOR(edges)[4*i+3] = i; + } + if (circular) { + /* Now i == n-1 */ + VECTOR(edges)[4*i] = i; + VECTOR(edges)[4*i+1] = 0; + VECTOR(edges)[4*i+2] = 0; + VECTOR(edges)[4*i+3] = i; + } + } else { + for (i=0; i < n-1; ++i) { + VECTOR(edges)[2*i] = i; + VECTOR(edges)[2*i+1] = i+1; + } + if (circular) { + /* Now i == n-1 */ + VECTOR(edges)[2*i] = i; + VECTOR(edges)[2*i+1] = 0; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_kary_tree + * \brief Creates a k-ary tree in which almost all vertices have k 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_regular_tree(), \ref igraph_symmetric_tree() and \ref igraph_star() + * for creating other regular structures; \ref igraph_from_prufer() and + * \ref igraph_tree_from_parent_vector() for creating arbitrary trees; + * \ref igraph_tree_game() for uniform random sampling of trees. + * + * \example examples/simple/igraph_kary_tree.c + */ +igraph_error_t igraph_kary_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, + igraph_tree_mode_t type) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t i, j; + igraph_integer_t idx = 0; + igraph_integer_t 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_integer_t no_of_edges2; + if (n > 0) { + IGRAPH_SAFE_MULT(n-1, 2, &no_of_edges2); + } else { + no_of_edges2 = 0; + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } + + 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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_tree + * \brief Creates a k-ary tree in which almost all vertices have k children (deprecated alias). + * + * \deprecated-by igraph_kary_tree 0.10.0 + */ +igraph_error_t igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, + igraph_tree_mode_t type) { + return igraph_kary_tree(graph, n, children, type); +} + +/** + * \ingroup generators + * \function igraph_symmetric_tree + * \brief Creates a symmetric tree with the specified number of branches at each level. + * + * This function creates a tree in which all vertices at distance \c d from the + * root have \p branching_counts[d] children. + * + * \param graph Pointer to an uninitialized graph object. + * \param branches Vector detailing the number of branches at each level. + * \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_INVMODE: invalid mode argument. + * \c IGRAPH_EINVAL: invalid number of children. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_kary_tree(), \ref igraph_regular_tree() and \ref igraph_star() + * for creating other regular tree structures; + * \ref igraph_from_prufer() for creating arbitrary trees; + * \ref igraph_tree_game() for uniform random sampling of trees. + * + * \example examples/simple/igraph_symmetric_tree.c + */ + +igraph_error_t igraph_symmetric_tree(igraph_t *graph, const igraph_vector_int_t *branches, + igraph_tree_mode_t type) { + + igraph_vector_int_t edges; + igraph_integer_t j, k, temp, no_of_nodes, idx, parent, child, level_end; + igraph_integer_t branching_counts_size = igraph_vector_int_size(branches); + + if (type != IGRAPH_TREE_OUT && type != IGRAPH_TREE_IN && type != IGRAPH_TREE_UNDIRECTED) { + IGRAPH_ERROR("Invalid tree orientation type.", IGRAPH_EINVMODE); + } + if (!igraph_vector_int_empty(branches) && igraph_vector_int_min(branches) <= 0) { + IGRAPH_ERROR("The number of branches must be positive at each level.", IGRAPH_EINVAL); + } + + /* Compute the number of vertices in the tree. */ + no_of_nodes = 1; + temp = 1; + for (j = 0; j < branching_counts_size; ++j) { + IGRAPH_SAFE_MULT(temp, VECTOR(*branches)[j], &temp); + IGRAPH_SAFE_ADD(no_of_nodes, temp, &no_of_nodes); + } + + /* Trees have precisely |E| = |V| - 1 edges. */ + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_nodes - 1, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } + + idx = 0; + + /* Current parent and child vertex ids. + * parent -> child edges will be added. */ + child = 1; + parent = 0; + for (k = 0; k < branching_counts_size; ++k) { + level_end = child; /* points to one past the last vertex of the current level of parents */ + while (parent < level_end) { + IGRAPH_ALLOW_INTERRUPTION(); + for (j = 0; j < VECTOR(*branches)[k]; j++) { + if (type == IGRAPH_TREE_IN) { + VECTOR(edges)[idx++] = child++; + VECTOR(edges)[idx++] = parent; + } else { + VECTOR(edges)[idx++] = parent; + VECTOR(edges)[idx++] = child++; + } + } + parent++; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, type != IGRAPH_TREE_UNDIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_regular_tree + * \brief Creates a regular tree. + * + * All vertices of a regular tree, except its leaves, have the same total degree \p k. + * This is different from a k-ary tree (\ref igraph_kary_tree()), where all + * vertices have the same number of children, thus the degre of the root is + * one less than the degree of the other internal vertices. Regular trees + * are also referred to as Bethe lattices. + * + * \param graph Pointer to an uninitialized graph object. + * \param h The height of the tree, i.e. the distance between the root and the leaves. + * \param k The degree of the regular 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. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_kary_tree() to create k-ary tree where each vertex has the same + * number of children, i.e. out-degree, instead of the same total degree. + * \ref igraph_symmetric_tree() to use a different number of children at each level. + * + * \example examples/simple/igraph_regular_tree.c + */ + +igraph_error_t igraph_regular_tree(igraph_t *graph, igraph_integer_t h, igraph_integer_t k, igraph_tree_mode_t type) { + igraph_vector_int_t branching_counts; + + if (h < 1) { + IGRAPH_ERRORF("Height of regular tree must be positive, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, h); + } + if (k < 2 ) { + IGRAPH_ERRORF("Degree of regular tree must be at least 2, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, k); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&branching_counts, h); + igraph_vector_int_fill(&branching_counts, k-1); + if (h > 0) { + VECTOR(branching_counts)[0] += 1; + } + + IGRAPH_CHECK(igraph_symmetric_tree(graph, &branching_counts, type)); + + igraph_vector_int_destroy(&branching_counts); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t igraph_extended_chordal_ring( + igraph_t *graph, igraph_integer_t nodes, const igraph_matrix_int_t *W, + igraph_bool_t directed) { + igraph_vector_int_t edges; + igraph_integer_t period = igraph_matrix_int_ncol(W); + igraph_integer_t nrow = igraph_matrix_int_nrow(W); + igraph_integer_t i, j, mpos = 0, epos = 0; + + if (nodes < 3) { + IGRAPH_ERROR("An extended chordal ring has at least 3 nodes.", IGRAPH_EINVAL); + } + + if (nodes % period != 0) { + IGRAPH_ERROR("The period (number of columns in W) should divide the number of nodes.", + IGRAPH_EINVAL); + } + + { + /* ecount = nodes + nodes * nrow */ + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(nodes, nrow, &no_of_edges2); + IGRAPH_SAFE_ADD(no_of_edges2, nodes, &no_of_edges2); + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } + + 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++) { + igraph_integer_t offset = MATRIX(*W, j, mpos); + igraph_integer_t 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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hypercube + * \brief The n-dimensional hypercube graph. + * + * \experimental + * + * The hypercube graph \c Q_n has 2^n vertices and + * 2^(n-1) n edges. Two vertices are connected when the binary + * representations of their zero-based vertex IDs differs in precisely one bit. + * + * \param graph An uninitialized graph object. + * \param n The dimension of the hypercube graph. + * \param directed Whether the graph should be directed. Edges will point + * from lower index vertices towards higher index ones. + * \return Error code. + * + * \sa \ref igraph_square_lattice() + * + * Time complexity: O(2^n) + */ +igraph_error_t igraph_hypercube(igraph_t *graph, + igraph_integer_t n, igraph_bool_t directed) { + + /* An n-dimensional hypercube graph has 2^n vertices and 2^(n-1)*n edges. + * The maximum possible dimension is calculated with the assumption that + * the largest possible edge count is no more than half IGRAPH_INTEGER_MAX, + * which is in fact the current limit. */ + + const igraph_integer_t maxn = + (IGRAPH_INTEGER_SIZE - 1) - (igraph_integer_t) ceil(log2(IGRAPH_INTEGER_SIZE)); + + if (n > maxn) { + IGRAPH_ERRORF("The requested hypercube graph dimension (%" IGRAPH_PRId + ") is too high. It must be no greater than %" IGRAPH_PRId ".", + IGRAPH_EINVAL, n, maxn); + } + + /* Integer overflow is no longer a concern after the above check. */ + + const igraph_integer_t vcount = (igraph_integer_t) 1 << n; + const igraph_integer_t ecount = ((igraph_integer_t) 1 << (n-1)) * n; + igraph_vector_int_t edges; + igraph_integer_t p; + int iter = 0; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2*ecount); + + p = 0; + for (igraph_integer_t v=0; v < vcount; v++) { + igraph_integer_t bit = 1; + for (igraph_integer_t i=0; i < n; i++) { + const igraph_integer_t u = v ^ bit; + if (v < u) { + VECTOR(edges)[p++] = v; + VECTOR(edges)[p++] = u; + } + bit <<= 1; + } + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 16); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, vcount, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/trees.c b/src/constructors/trees.c new file mode 100644 index 0000000..16b625c --- /dev/null +++ b/src/constructors/trees.c @@ -0,0 +1,163 @@ +/* + 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 +*/ + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_vector.h" + +/** + * \function igraph_tree_from_parent_vector + * \brief Constructs a tree or forest from a vector encoding the parent of each vertex. + * + * \experimental + * + * Rooted trees and forests are conveniently represented using a \p parents + * vector where the ID of the parent of vertex \c v is stored in parents[v]. + * This function serves to construct an igraph graph from a parent vector representation. + * The result is guaranteed to be a forest or a tree. If the \p parents vector + * is found to encode a cycle or a self-loop, an error is raised. + * + * + * Several igraph functions produce such vectors, such as graph traversal + * functions (\ref igraph_bfs() and \ref igraph_dfs()), shortest path functions + * that construct a shortest path tree, as well as some other specialized + * functions like \ref igraph_dominator_tree() or \ref igraph_cohesive_blocks(). + * Vertices which do not have parents (i.e. roots) get a negative entry in the + * \p parents vector. + * + * + * Use \ref igraph_bfs() or \ref igraph_dfs() to convert a forest into a parent + * vector representation. For trees, i.e. forests with a single root, it is + * more convenient to use \ref igraph_bfs_simple(). + * + * \param graph Pointer to an uninitialized graph object. + * \param parents The parent vector. parents[v] is the ID of + * the parent vertex of \c v. parents[v] < 0 indicates that + * \c v does not have a parent. + * \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. + * + * \sa \ref igraph_bfs(), \ref igraph_bfs_simple() for back-conversion; + * \ref igraph_from_prufer() for creating trees from Prüfer sequences; + * \ref igraph_is_tree() and \ref igraph_is_forest() to check if a graph + * is a tree or forest. + * + * Time complexity: O(n) where n is the length of \p parents. + */ +igraph_error_t igraph_tree_from_parent_vector( + igraph_t *graph, + const igraph_vector_int_t *parents, + igraph_tree_mode_t type) { + + const igraph_integer_t no_of_nodes = igraph_vector_int_size(parents); + igraph_vector_int_t seen; + igraph_vector_int_t edges; + igraph_bool_t directed, intree; + + switch (type) { + case IGRAPH_TREE_OUT: + directed = true; intree = false; break; + case IGRAPH_TREE_IN: + directed = true; intree = true; break; + case IGRAPH_TREE_UNDIRECTED: + directed = false; intree = true; break; + default: + IGRAPH_ERROR("Invalid tree mode.", IGRAPH_EINVAL); + } + + /* Catch null graph case */ + if (no_of_nodes == 0) { + return igraph_empty(graph, 0, directed); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&seen, no_of_nodes); + + /* A tree has no_of_nodes - 1 edges but a forest has fewer. In order to support + * the use case of extracting small sub-trees of large graphs, we only reserve + * the full amount of memory needed for a tree when the graph is small. + * This also eliminates the need to check for integer overflow. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_nodes > 1024 ? 2048 : 2*(no_of_nodes-1)); + igraph_vector_int_clear(&edges); + + igraph_integer_t c=1; + for (igraph_integer_t i=0; i < no_of_nodes; i++) { + igraph_integer_t v = i; + + if (VECTOR(seen)[v]) continue; + + while (true) { + igraph_integer_t u; + + VECTOR(seen)[v] = c; /* mark v as seen in the current round */ + u = VECTOR(*parents)[v]; + + if (u < 0) { + break; /* v is a root, stop traversal */ + } + if (u >= no_of_nodes) { + IGRAPH_ERROR("Invalid vertex ID in parent vector.", IGRAPH_EINVVID); + } + + if (intree) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); + } else { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); + } + + if (VECTOR(seen)[u]) { + if (VECTOR(seen)[u] == c) { + /* u was already seen in the current round, we found a cycle. + * We distinguish between self-loops, i.e. 1-cycles, and longer + * cycles in order to make the error message more useful. */ + IGRAPH_ERROR( + u==v + ? "Found a self-loop while constructing tree from parent vector." + : "Found a cycle while constructing tree from parent vector.", + IGRAPH_EINVAL); + } + break; /* u was seen in a previous round, stop traversal */ + } + + v = u; + } + + c++; + } + + igraph_vector_int_destroy(&seen); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/core/array.c b/src/core/array.c new file mode 100644 index 0000000..07bc67f --- /dev/null +++ b/src/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_INT +#include "igraph_pmt.h" +#include "array.pmt" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#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/core/array.pmt b/src/core/array.pmt new file mode 100644 index 0000000..227061f --- /dev/null +++ b/src/core/array.pmt @@ -0,0 +1,110 @@ +/* -*- 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 "math/safe_intop.h" + +igraph_error_t FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, + igraph_integer_t n1, igraph_integer_t n2, igraph_integer_t n3) { + + igraph_integer_t size, n1n2; + + IGRAPH_ASSERT(n1 >= 0 && n2 >= 0 && n3 >= 0); + + IGRAPH_SAFE_MULT(n1, n2, &n1n2); + IGRAPH_SAFE_MULT(n1n2, n3, &size); + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&a->data, size)); + + a->n1 = n1; + a->n2 = n2; + a->n3 = n3; + a->n1n2 = n1n2; + + return IGRAPH_SUCCESS; +} + +void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a) { + FUNCTION(igraph_vector, destroy)(&a->data); +} + +igraph_integer_t FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a) { + return (a->n1n2) * (a->n3); +} + +igraph_integer_t FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, igraph_integer_t idx) { + switch (idx) { + case 1: return a->n1; + break; + case 2: return a->n2; + break; + case 3: return a->n3; + break; + } + return 0; +} + +igraph_error_t FUNCTION(igraph_array3, resize)( + TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, + igraph_integer_t n3) { + + igraph_integer_t size, n1n2; + + IGRAPH_ASSERT(n1 >= 0 && n2 >= 0 && n3 >= 0); + + IGRAPH_SAFE_MULT(n1, n2, &n1n2); + IGRAPH_SAFE_MULT(n1n2, n3, &size); + + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&a->data, size)); + + a->n1 = n1; + a->n2 = n2; + a->n3 = n3; + a->n1n2 = n1n2; + + return IGRAPH_SUCCESS; +} + +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); +} + +igraph_error_t 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 IGRAPH_SUCCESS; +} diff --git a/src/core/bitset.c b/src/core/bitset.c new file mode 100644 index 0000000..2a94f07 --- /dev/null +++ b/src/core/bitset.c @@ -0,0 +1,833 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2024 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 "string.h" + +#include "igraph_bitset.h" +#include "igraph_memory.h" + +igraph_integer_t igraph_i_ctz32(igraph_uint_t x) { +#ifdef HAVE__BITSCANFORWARD + unsigned long index; + return _BitScanForward(&index, x) ? index : 32; +#else + for (igraph_integer_t i = 0; i < 32; ++i) { + if (IGRAPH_BIT_MASK(i) & x) { + return i; + } + } + return 32; +#endif +} + +igraph_integer_t igraph_i_clz32(igraph_uint_t x) { +#ifdef HAVE_BITSCANREVERSE + unsigned long index; + return _BitScanReverse(&index, x) ? 31 - index : 32; +#else + for (igraph_integer_t i = 31; i >= 0; --i) { + if (IGRAPH_BIT_MASK(i) & x) { + return 31 - i; + } + } + return 32; +#endif +} + +igraph_integer_t igraph_i_popcnt(igraph_uint_t x) { + igraph_integer_t result = 0; + while (x) { + result++; + x = x & (x - 1); + } + return result; +} + +/* Fallbacks for 64-bit word (and igraph_integer_t/igraph_uint_t) size */ +#if IGRAPH_INTEGER_SIZE == 64 +igraph_integer_t igraph_i_ctz64(igraph_uint_t x) { +#ifdef HAVE_BITSCANFORWARD64 + unsigned long index; + return _BitScanForward64(&index, x) ? index : 64; +#else + for (igraph_integer_t i = 0; i < 64; ++i) { + if (IGRAPH_BIT_MASK(i) & x) { + return i; + } + } + return 64; +#endif +} + +igraph_integer_t igraph_i_clz64(igraph_uint_t x) { +#ifdef HAVE_BITSCANREVERSE64 + unsigned long index; + return _BitScanReverse64(&index, x) ? 63 - index : 64; +#else + for (igraph_integer_t i = 63; i >= 0; --i) { + if (IGRAPH_BIT_MASK(i) & x) { + return 63 - i; + } + } + return 64; +#endif +} +#endif /* IGRAPH_INTEGER_SIZE == 64 */ + +/** + * \ingroup bitset + * \section about_igraph_bitset_t_objects About \type igraph_bitset_t objects + * + * The \type igraph_bitset_t data type is a simple and efficient + * interface to arrays containing boolean values. It is similar to + * the \type bitset template in the C++ standard library, although the main + * difference being the C++ version's size is initialized at compile time. + * + * The \type igraph_bitset_t type and use O(n/w) space + * to store n elements, where w is the bit width of \type igraph_integer_t, + * the integer type used throughout the library (either 32 or 64). + * Sometimes they use more, this is because bitsets can + * shrink, but even if they shrink, the current implementation does not free a + * single bit of memory. + * + * The elements in an \type igraph_bitset_t object and its variants are + * indexed from zero, we follow the usual C convention here. Bitsets are indexed + * from right to left, meaning index 0 is the least significant bit and index n-1 + * is the most significant bit. + * + * The elements of a bitset always occupy a single block of + * memory, the starting address of this memory block can be queried + * with the \ref VECTOR() macro. This way, bitset objects can be used + * with standard mathematical libraries, like the GNU Scientific + * Library. + */ + +/** + * \ingroup bitset + * \section igraph_bitset_constructors_and_destructors Constructors and + * destructors + * + * \type igraph_bitset_t objects have to be initialized before using + * them, this is analogous to calling a constructor on them. There are two + * \type igraph_bitset_t constructors, for your convenience. + * \ref igraph_bitset_init() is the basic constructor, it + * creates a bitset of the given length, filled with zeros. + * \ref igraph_bitset_init_copy() creates a new identical copy + * of an already existing and initialized bitset. + * + * If a \type igraph_bitset_t object is not needed any more, it + * should be destroyed to free its allocated memory by calling the + * \type igraph_bitset_t destructor, \ref igraph_bitset_destroy(). + */ + +/** + * \ingroup bitset + * \function igraph_bitset_init + * \brief Initializes a bitset object (constructor). + * + * \experimental + * + * + * Every bitset 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 bitset of the given size and + * initializes each entry to 0. + * + * + * Every bitset 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_bitset_destroy() function is + * responsible for this. + * + * \param bitset Pointer to a not yet initialized bitset object. + * \param size The size of the bitset. + * \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/w) elements, + * n is the number of elements. + * w is the word size of the machine (32 or 64). + */ + +igraph_error_t igraph_bitset_init(igraph_bitset_t *bitset, igraph_integer_t size) { + igraph_integer_t alloc_size = IGRAPH_BIT_NSLOTS(size); + bitset->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_uint_t); + IGRAPH_CHECK_OOM(bitset->stor_begin, "Cannot initialize bitset"); + bitset->size = size; + bitset->stor_end = bitset->stor_begin + alloc_size; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup bitset + * \function igraph_bitset_destroy + * \brief Destroys a bitset object. + * + * \experimental + * + * + * All bitsets initialized by \ref igraph_bitset_init() should be properly + * destroyed by this function. A destroyed bitset needs to be + * reinitialized by \ref igraph_bitset_init() or + * another constructor. + * + * \param bitset Pointer to the (previously initialized) bitset object to + * destroy. + * + * Time complexity: operating system dependent. + */ + +void igraph_bitset_destroy(igraph_bitset_t *bitset) { + IGRAPH_ASSERT(bitset != NULL); + IGRAPH_FREE(bitset->stor_begin); + bitset->size = 0; +} + +/** + * \ingroup bitset + * \function igraph_bitset_init_copy + * \brief Initializes a bitset from another bitset object (constructor). + * + * \experimental + * + * + * + * The contents of the existing bitset object will be copied to + * the new one. + * \param dest Pointer to a not yet initialized bitset object. + * \param src The original bitset object to copy. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system dependent, usually + * O(n/w), + * n is the size of the bitset, + * w is the word size of the machine (32 or 64). + */ + +igraph_error_t igraph_bitset_init_copy(igraph_bitset_t *dest, const igraph_bitset_t *src) { + IGRAPH_ASSERT(src != NULL); + IGRAPH_ASSERT(src->stor_begin != NULL); + IGRAPH_CHECK(igraph_bitset_init(dest, src->size)); + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = VECTOR(*src)[i]; + } + return IGRAPH_SUCCESS; +} + +/** + * \ingroup bitset + * \function igraph_bitset_capacity + * \brief Returns the allocated capacity of the bitset. + * + * \experimental + * + * Note that this might be different from the size of the bitset (as + * queried by \ref igraph_bitset_size()), and specifies how many elements + * the bitset can hold, without reallocation. + * + * \param bitset Pointer to the (previously initialized) bitset object + * to query. + * \return The allocated capacity. + * + * \sa \ref igraph_bitset_size(). + * + * Time complexity: O(1). + */ + +igraph_integer_t igraph_bitset_capacity(const igraph_bitset_t *bitset) { + return IGRAPH_INTEGER_SIZE * (bitset->stor_end - bitset->stor_begin); +} + +/** + * \ingroup bitset + * \function igraph_bitset_size + * \brief Returns the length of the bitset. + * + * \experimental + * + * \param bitset The bitset object + * \return The size of the bitset. + * + * Time complexity: O(1). + */ + +igraph_integer_t igraph_bitset_size(const igraph_bitset_t *bitset) { + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_reserve + * \brief Reserves memory for a bitset. + * + * \experimental + * + * + * \a igraph bitsets are flexible, they can grow and + * shrink. Growing + * however occasionally needs the data in the bitset to be copied. + * In order to avoid this, you can call this function to reserve space for + * future growth of the bitset. + * + * + * Note that this function does \em not change the size of the + * bitset. Let us see a small example to clarify things: if you + * reserve space for 100 elements and the size of your + * bitset was (and still is) 60, then you can surely add additional 40 + * elements to your bitset before it will be copied. + * \param bitset The bitset object. + * \param capacity The new \em allocated size of the bitset. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system dependent, should be around + * O(n/w), + * n is the new allocated size of the bitset, + * w is the word size of the machine (32 or 64). + */ + +igraph_error_t igraph_bitset_reserve(igraph_bitset_t *bitset, igraph_integer_t capacity) { + igraph_integer_t current_capacity; + igraph_uint_t *tmp; + + IGRAPH_ASSERT(bitset != NULL); + IGRAPH_ASSERT(bitset->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); + + current_capacity = igraph_bitset_capacity(bitset); + + if (IGRAPH_BIT_NSLOTS(capacity) <= IGRAPH_BIT_NSLOTS(current_capacity)) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(bitset->stor_begin, IGRAPH_BIT_NSLOTS(capacity), igraph_uint_t); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for bitset."); + + bitset->stor_begin = tmp; + bitset->stor_end = bitset->stor_begin + IGRAPH_BIT_NSLOTS(capacity); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup bitset + * \function igraph_bitset_resize + * \brief Resizes the bitset. + * + * \experimental + * + * + * Note that this function does not free any memory, just sets the + * size of the bitset to the given one. It may, 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 bitset are + * set to zero. + * + * \param bitset The bitset object + * \param new_size The new size of the bitset. + * \return Error code, + * \c IGRAPH_ENOMEM if there is not enough + * memory. Note that this function \em never returns an error + * if the bitset is made smaller. + * \sa \ref igraph_bitset_reserve() for allocating memory for future + * extensions of a bitset. + * + * 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/w), + * n is the new size of the bitset, + * w is the word size of the machine (32 or 64). + */ + +igraph_error_t igraph_bitset_resize(igraph_bitset_t *bitset, igraph_integer_t new_size) { + IGRAPH_ASSERT(bitset != NULL); + IGRAPH_ASSERT(bitset->stor_begin != NULL); + IGRAPH_CHECK(igraph_bitset_reserve(bitset, new_size)); + + if (new_size > bitset->size) { + for (igraph_integer_t i = bitset->size; i % IGRAPH_INTEGER_SIZE != 0; ++i) { + IGRAPH_BIT_CLEAR(*bitset, i); + } + memset(bitset->stor_begin + IGRAPH_BIT_NSLOTS(bitset->size), 0, + sizeof(igraph_uint_t) * (IGRAPH_BIT_NSLOTS(new_size) - IGRAPH_BIT_NSLOTS(bitset->size))); + } + bitset->size = new_size; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup bitset + * \function igraph_bitset_popcount + * \brief The population count of the bitset. + * + * \experimental + * + * Returns the number of set bits, also called the population count, + * of the bitset. + * + * \param bitset The bitset object + * \return The population count of the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_popcount(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? ~zero : ((one << final_block_size) - 1); + igraph_integer_t count = 0; + + for (igraph_integer_t i = 0; i + 1 < slots; ++i) { + count += IGRAPH_POPCOUNT(VECTOR(*bitset)[i]); + } + if (bitset->size) { + count += IGRAPH_POPCOUNT(mask & VECTOR(*bitset)[slots - 1]); + } + + return count; +} + +/** + * \ingroup bitset + * \function igraph_bitset_countl_zero + * \brief The number of leading zeros in the bitset. + * + * \experimental + * + * Returns the number of leading (starting at the most significant bit) + * zeros in the bitset before the first one is encountered. If the bitset + * is all zeros, then its size is returned. + * + * \param bitset The bitset object + * \return The number of leading zeros in the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_countl_zero(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t padding = IGRAPH_INTEGER_SIZE - final_block_size; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? ~zero : ((one << final_block_size) - one); + + if (bitset->size && (mask & VECTOR(*bitset)[slots - 1]) != 0) { + return IGRAPH_CLZ(mask & VECTOR(*bitset)[slots - 1]) - padding; + } + for (igraph_integer_t i = 1; i < slots; ++i) { + if (VECTOR(*bitset)[slots - i - 1] != 0) { + return IGRAPH_INTEGER_SIZE * i + IGRAPH_CLZ(VECTOR(*bitset)[slots - i - 1]) - padding; + } + } + + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_countl_one + * \brief The number of leading ones in the bitset. + * + * \experimental + * + * Returns the number of leading ones (starting at the most significant bit) + * in the bitset before the first zero is encountered. + * If the bitset is all ones, then its size is returned. + * + * \param bitset The bitset object + * \return The number of leading ones in the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_countl_one(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t padding = IGRAPH_INTEGER_SIZE - final_block_size; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? zero : ~((one << final_block_size) - one); + + if (bitset->size && (mask | VECTOR(*bitset)[slots - 1]) != ~zero) { + return IGRAPH_CLO(mask | VECTOR(*bitset)[slots - 1]) - padding; + } + for (igraph_integer_t i = 1; i < slots; ++i) { + if (VECTOR(*bitset)[slots - i - 1] != ~zero) { + return IGRAPH_INTEGER_SIZE * i + IGRAPH_CLO(VECTOR(*bitset)[slots - i - 1]) - padding; + } + } + + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_countr_zero + * \brief The number of trailing zeros in the bitset. + * + * \experimental + * + * Returns the number of trailing (starting at the least significant bit) + * zeros in the bitset before the first one is encountered. + * If the bitset is all zeros, then its size is returned. + * + * \param bitset The bitset object + * \return The number of trailing zeros in the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_countr_zero(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? ~zero : ((one << final_block_size) - one); + + for (igraph_integer_t i = 0; i + 1 < slots; ++i) { + if (VECTOR(*bitset)[i] != zero) { + return IGRAPH_INTEGER_SIZE * i + IGRAPH_CTZ(VECTOR(*bitset)[i]); + } + } + if (bitset->size && (mask & VECTOR(*bitset)[slots - 1]) != zero) { + return IGRAPH_INTEGER_SIZE * (slots - 1) + IGRAPH_CTZ(mask & VECTOR(*bitset)[slots - 1]); + } + + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_countr_one + * \brief The number of trailing ones in the bitset. + * + * \experimental + * + * Returns the number of trailing ones (starting at the least significant bit) + * in the bitset before the first zero is encountered. + * If the bitset is all ones, then its size is returned. + * + * \param bitset The bitset object + * \return The number of trailing ones in the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_countr_one(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? zero : ~((one << final_block_size) - one); + + for (igraph_integer_t i = 0; i + 1 < slots; ++i) { + if (VECTOR(*bitset)[i] != ~zero) { + return IGRAPH_INTEGER_SIZE * i + IGRAPH_CTO(VECTOR(*bitset)[i]); + } + } + if (bitset->size && (mask | VECTOR(*bitset)[slots - 1]) != ~zero) { + return IGRAPH_INTEGER_SIZE * (slots - 1) + IGRAPH_CTO(mask | VECTOR(*bitset)[slots - 1]); + } + + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_is_all_zero + * \brief Are all bits zeros? + * + * \experimental + * + * \param bitset The bitset object to test. + * \return True if none of the bits are set. + * + * Time complexity: O(n/w). + */ + +igraph_bool_t igraph_bitset_is_all_zero(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? ~zero : ((one << final_block_size) - one); + + for (igraph_integer_t i = 0; i < slots - 1; i++) { + if (VECTOR(*bitset)[i] != zero) { + return false; + } + } + if (bitset->size && (mask & VECTOR(*bitset)[slots - 1]) != zero) { + return false; + } + return true; +} + +/** + * \ingroup bitset + * \function igraph_bitset_is_all_one + * \brief Are all bits ones? + * + * \experimental + * + * \param bitset The bitset object to test. + * \return True if all of the bits are set. + * + * Time complexity: O(n/w). + */ + +igraph_bool_t igraph_bitset_is_all_one(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? zero : ~((one << final_block_size) - one); + + for (igraph_integer_t i = 0; i < slots - 1; i++) { + if (VECTOR(*bitset)[i] != ~zero) { + return false; + } + } + if (bitset->size && (mask | VECTOR(*bitset)[slots - 1]) != ~zero) { + return false; + } + return true; +} + +/** + * \ingroup bitset + * \function igraph_bitset_is_any_zero + * \brief Are any bits zeros? + * + * \experimental + * + * \param bitset The bitset object to test. + * \return True if at least one bit is zero. + * + * Time complexity: O(n/w). + */ + +igraph_bool_t igraph_bitset_is_any_zero(const igraph_bitset_t *bitset) { + return ! igraph_bitset_is_all_one(bitset); +} + +/** + * \ingroup bitset + * \function igraph_bitset_is_any_one + * \brief Are any bits ones? + * + * \experimental + * + * \param bitset The bitset object to test. + * \return True if at least one bit is one. + * + * Time complexity: O(n/w). + */ + +igraph_bool_t igraph_bitset_is_any_one(const igraph_bitset_t *bitset) { + return ! igraph_bitset_is_all_zero(bitset); +} + +/** + * \ingroup bitset + * \function igraph_bitset_or + * \brief Bitwise OR of two bitsets. + * + * \experimental + * + * Applies a bitwise or to the contents of two bitsets and stores it in an + * already initialized bitset. The destination bitset may be equal to one + * (or even both) of the sources. When working with bitsets, it is common + * that those created are of the same size fixed size. Therefore, this + * function does not check the sizes of the bitsets passed to it, the caller + * must do so if necessary. + * + * \param dest The bitset object where the result is stored + * \param src1 A bitset + * \param src2 A bitset + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_or(igraph_bitset_t *dest, + const igraph_bitset_t *src1, const igraph_bitset_t *src2) { + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = VECTOR(*src1)[i] | VECTOR(*src2)[i]; + } +} + +/** + * \ingroup bitset + * \function igraph_bitset_and + * \brief Bitwise AND of two bitsets. + * + * \experimental + * + * Applies a bitwise and to the contents of two bitsets and stores it in an + * already initialized bitset. The destination bitset may be equal to one + * (or even both) of the sources. When working with bitsets, it is common + * that those created are of the same size fixed size. Therefore, this + * function does not check the sizes of the bitsets passed to it, the caller + * must do so if necessary. + * + * \param dest The bitset object where the result is stored + * \param src1 A bitset + * \param src2 A bitset + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_and(igraph_bitset_t *dest, const igraph_bitset_t *src1, const igraph_bitset_t *src2) { + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = VECTOR(*src1)[i] & VECTOR(*src2)[i]; + } +} + +/** + * \ingroup bitset + * \function igraph_bitset_xor + * \brief Bitwise XOR of two bitsets. + * + * \experimental + * + * Applies a bitwise xor to the contents of two bitsets and stores it in + * an already initialized bitset. The destination bitset may be equal to + * one (or even both) of the sources. When working with bitsets, it is common + * that those created are of the same size fixed size. Therefore, this + * function does not check the sizes of the bitsets passed to it, the caller + * must do so if necessary. + * + * \param dest The bitset object where the result is stored + * \param src1 A bitset + * \param src2 A bitset + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_xor(igraph_bitset_t *dest, + const igraph_bitset_t *src1, const igraph_bitset_t *src2) { + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = VECTOR(*src1)[i] ^ VECTOR(*src2)[i]; + } +} + +/** + * \ingroup bitset + * \function igraph_bitset_not + * \brief Bitwise negation of a bitset. + * + * \experimental + * + * Applies a bitwise not to the contents of a bitset and stores it in an + * already initialized bitset. The destination bitset may be equal to the + * source. When working with bitsets, it is common that those created are + * of the same size fixed size. Therefore, this function does not check the + * sizes of the bitsets passed to it, the caller must do so if necessary. + * + * \param dest The bitset object where the result is stored + * \param src A bitset + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_not(igraph_bitset_t *dest, const igraph_bitset_t *src) { + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = ~VECTOR(*src)[i]; + } +} + +/** + * \ingroup bitset + * \function igraph_bitset_fill + * \brief Fills a bitset with a constant value. + * + * \experimental + * + * Sets all bits of a bitset to the same value. + * + * \param bitset The bitset object to modify. + * \param value The value to set for all bits. + * +* \sa \ref igraph_bitset_null() + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_fill(igraph_bitset_t *bitset, igraph_bool_t value) { + memset(bitset->stor_begin, + value ? ~ (unsigned char) 0 : 0, + sizeof(igraph_uint_t) * IGRAPH_BIT_NSLOTS(bitset->size)); +} + +/** + * \ingroup bitset + * \function igraph_bitset_null + * \brief Clears all bits in a bitset. + * + * \experimental + * + * \param bitset The bitset object to clear all bits in. + * + * \sa \ref igraph_bitset_fill() + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_null(igraph_bitset_t *bitset) { + igraph_bitset_fill(bitset, false); +} + +/** + * \ingroup bitset + * \function igraph_bitset_fprint + * \brief Prints the bits of a bitset. + * + * \experimental + * + * Outputs the contents of a bitset to a file. + * The bitset is written from index n-1 to index 0, left to right, + * such that index 0 is the least significant bit and index n-1 is + * the most significant bit, where n is the size of the bitset. + * This is the reverse of how sequential structures are usually written, + * such as vectors, but consistent with the bitset being a binary + * representation of an integer and how they are usually written. + * + * + * No newline is printed at the end. + * + * \param bitset The bitset to be printed. + * \param file The file to be written. + * \return Error code. + * + * Time complexity: O(n). + */ +igraph_error_t igraph_bitset_fprint(const igraph_bitset_t *bitset, FILE *file) { + for (igraph_integer_t i = bitset->size - 1; i >= 0; i--) { + fputc(IGRAPH_BIT_TEST(*bitset, i) ? '1' : '0', file); + } + return IGRAPH_SUCCESS; +} + +#ifndef USING_R +igraph_error_t igraph_bitset_print(const igraph_bitset_t *bitset) { + return igraph_bitset_fprint(bitset, stdout); +} +#endif diff --git a/src/core/bitset_list.c b/src/core/bitset_list.c new file mode 100644 index 0000000..a2f783a --- /dev/null +++ b/src/core/bitset_list.c @@ -0,0 +1,54 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2022 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 + +*/ + +#include "igraph_bitset_list.h" + +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_types.h" + +#define BITSET_LIST +#define BASE_BITSET +#define CUSTOM_INIT_DESTROY +#include "igraph_pmt.h" +#include "typed_list.pmt" +#include "igraph_pmt_off.h" +#undef CUSTOM_INIT_DESTROY +#undef BASE_BITSET +#undef BITSET_LIST + +static igraph_error_t igraph_i_bitset_list_init_item( + const igraph_bitset_list_t* list, igraph_bitset_t* item +) { + return igraph_bitset_init(item, 0); +} + +static igraph_error_t igraph_i_bitset_list_copy_item( + igraph_bitset_t* dest, const igraph_bitset_t* source +) { + return igraph_bitset_init_copy(dest, source); +} + +static void igraph_i_bitset_list_destroy_item(igraph_bitset_t* item) { + igraph_bitset_destroy(item); +} diff --git a/src/core/buckets.c b/src/core/buckets.c new file mode 100644 index 0000000..d2204cd --- /dev/null +++ b/src/core/buckets.c @@ -0,0 +1,193 @@ +/* -*- 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/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. + */ + +igraph_error_t igraph_buckets_init(igraph_buckets_t *b, igraph_integer_t bsize, igraph_integer_t size) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->bptr, bsize); + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->buckets, size); + b->max = -1; b->no = 0; + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +void igraph_buckets_destroy(igraph_buckets_t *b) { + igraph_vector_int_destroy(&b->bptr); + igraph_vector_int_destroy(&b->buckets); +} + +igraph_integer_t igraph_buckets_popmax(igraph_buckets_t *b) { + /* Precondition: there is at least a non-empty bucket */ + /* Search for the highest bucket first */ + igraph_integer_t max; + while ( (max = VECTOR(b->bptr)[b->max]) == 0) { + b->max --; + } + VECTOR(b->bptr)[b->max] = VECTOR(b->buckets)[max - 1]; + b->no--; + + return max - 1; +} + +igraph_integer_t igraph_buckets_pop(igraph_buckets_t *b, igraph_integer_t bucket) { + igraph_integer_t 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, + igraph_integer_t bucket) { + return VECTOR(b->bptr)[bucket] == 0; +} + +void igraph_buckets_add(igraph_buckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem) { + + VECTOR(b->buckets)[elem] = VECTOR(b->bptr)[bucket]; + VECTOR(b->bptr)[bucket] = elem + 1; + if (bucket > b->max) { + b->max = bucket; + } + b->no++; +} + +void igraph_buckets_clear(igraph_buckets_t *b) { + igraph_vector_int_null(&b->bptr); + igraph_vector_int_null(&b->buckets); + b->max = -1; + b->no = 0; +} + +igraph_error_t igraph_dbuckets_init(igraph_dbuckets_t *b, igraph_integer_t bsize, igraph_integer_t size) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->bptr, bsize); + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->next, size); + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->prev, size); + b->max = -1; b->no = 0; + IGRAPH_FINALLY_CLEAN(3); + return IGRAPH_SUCCESS; +} + +void igraph_dbuckets_destroy(igraph_dbuckets_t *b) { + igraph_vector_int_destroy(&b->bptr); + igraph_vector_int_destroy(&b->next); + igraph_vector_int_destroy(&b->prev); +} + +void igraph_dbuckets_clear(igraph_dbuckets_t *b) { + igraph_vector_int_null(&b->bptr); + igraph_vector_int_null(&b->next); + igraph_vector_int_null(&b->prev); + b->max = -1; + b->no = 0; +} + +igraph_integer_t igraph_dbuckets_popmax(igraph_dbuckets_t *b) { + while ( VECTOR(b->bptr)[b->max] == 0) { + b->max--; + } + return igraph_dbuckets_pop(b, b->max); +} + +igraph_integer_t igraph_dbuckets_pop(igraph_dbuckets_t *b, igraph_integer_t bucket) { + igraph_integer_t ret = VECTOR(b->bptr)[bucket] - 1; + igraph_integer_t 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, + igraph_integer_t bucket) { + return VECTOR(b->bptr)[bucket] == 0; +} + +void igraph_dbuckets_add(igraph_dbuckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem) { + igraph_integer_t 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 = bucket; + } + b->no++; +} + +/* Remove an arbitrary element */ + +void igraph_dbuckets_delete(igraph_dbuckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem) { + if (VECTOR(b->bptr)[bucket] == elem + 1) { + /* First element in bucket */ + igraph_integer_t next = VECTOR(b->next)[elem]; + if (next != 0) { + VECTOR(b->prev)[next - 1] = 0; + } + VECTOR(b->bptr)[bucket] = next; + } else { + igraph_integer_t next = VECTOR(b->next)[elem]; + igraph_integer_t 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/core/buckets.h b/src/core/buckets.h new file mode 100644 index 0000000..176eddf --- /dev/null +++ b/src/core/buckets.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_BUCKETS_H +#define IGRAPH_CORE_BUCKETS_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* Buckets, needed for the maximum flow algorithm */ + +typedef struct igraph_buckets_t { + igraph_vector_int_t bptr; + igraph_vector_int_t buckets; + igraph_integer_t max, no; +} igraph_buckets_t; + +igraph_error_t igraph_buckets_init(igraph_buckets_t *b, igraph_integer_t bsize, igraph_integer_t size); +void igraph_buckets_destroy(igraph_buckets_t *b); +void igraph_buckets_clear(igraph_buckets_t *b); +igraph_integer_t igraph_buckets_popmax(igraph_buckets_t *b); +igraph_integer_t igraph_buckets_pop(igraph_buckets_t *b, igraph_integer_t bucket); +IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b); +IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b, + igraph_integer_t bucket); +void igraph_buckets_add(igraph_buckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem); + +typedef struct igraph_dbuckets_t { + igraph_vector_int_t bptr; + igraph_vector_int_t next, prev; + igraph_integer_t max, no; +} igraph_dbuckets_t; + +igraph_error_t igraph_dbuckets_init(igraph_dbuckets_t *b, igraph_integer_t bsize, igraph_integer_t size); +void igraph_dbuckets_destroy(igraph_dbuckets_t *b); +void igraph_dbuckets_clear(igraph_dbuckets_t *b); +igraph_integer_t igraph_dbuckets_popmax(igraph_dbuckets_t *b); +igraph_integer_t igraph_dbuckets_pop(igraph_dbuckets_t *b, igraph_integer_t bucket); +IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b); +IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b, + igraph_integer_t bucket); +void igraph_dbuckets_add(igraph_dbuckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem); +void igraph_dbuckets_delete(igraph_dbuckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem); + +__END_DECLS + +#endif diff --git a/src/core/cutheap.c b/src/core/cutheap.c new file mode 100644 index 0000000..96dd1be --- /dev/null +++ b/src/core/cutheap.c @@ -0,0 +1,169 @@ +/* -*- 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, + igraph_integer_t hidx1, igraph_integer_t hidx2) { + if (hidx1 != hidx2) { + igraph_integer_t idx1 = VECTOR(ch->index)[hidx1]; + igraph_integer_t idx2 = 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, igraph_integer_t hidx) { + igraph_integer_t 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, igraph_integer_t 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)); + } +} + +igraph_error_t 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_int_init_range(&ch->index, 0, nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &ch->index); + IGRAPH_CHECK(igraph_vector_init_range(&ch->hptr, INDEXINC, nodes + INDEXINC)); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch) { + igraph_vector_destroy(&ch->hptr); + igraph_vector_int_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_vector_size(&ch->heap); +} + +/* Number of all (defined) vertices */ + +igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch) { + return 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) { + igraph_integer_t size = igraph_vector_size(&ch->heap); + igraph_integer_t maxindex = 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)[ igraph_vector_int_tail(&ch->index)] = INACTIVE; + igraph_vector_pop_back(&ch->heap); + igraph_vector_int_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 */ + +void igraph_i_cutheap_update( + igraph_i_cutheap_t *ch, igraph_integer_t index, igraph_real_t add) { + igraph_real_t hidx = VECTOR(ch->hptr)[index]; + if (hidx != INACTIVE && hidx != UNDEFINED) { + igraph_integer_t hidx2 = (hidx - INDEXINC); + /* printf("updating vertex %li, heap index %li\n", index, hidx2); */ + VECTOR(ch->heap)[hidx2] += add; + igraph_i_cutheap_sink(ch, hidx2); + igraph_i_cutheap_shift_up(ch, hidx2); + } +} + +/* Reset the value of all vertices to zero and make them active */ + +igraph_error_t igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, igraph_integer_t vertex) { + igraph_integer_t 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_int_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 IGRAPH_SUCCESS; +} diff --git a/src/core/cutheap.h b/src/core/cutheap.h new file mode 100644 index 0000000..fcbb4cd --- /dev/null +++ b/src/core/cutheap.h @@ -0,0 +1,53 @@ +/* -*- 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 + +#include "igraph_decls.h" +#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_int_t index; + igraph_vector_t hptr; + igraph_integer_t dnodes; +} igraph_i_cutheap_t; + +IGRAPH_PRIVATE_EXPORT igraph_error_t 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_FUNCATTR_PURE igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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 void igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index, igraph_real_t add); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, igraph_integer_t vertex); + +__END_DECLS + +#endif diff --git a/src/core/dqueue.c b/src/core/dqueue.c new file mode 100644 index 0000000..20e8d95 --- /dev/null +++ b/src/core/dqueue.c @@ -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 + +*/ + +#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_INT +#include "igraph_pmt.h" +#include "dqueue.pmt" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#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 diff --git a/src/core/dqueue.pmt b/src/core/dqueue.pmt new file mode 100644 index 0000000..eb0f4c4 --- /dev/null +++ b/src/core/dqueue.pmt @@ -0,0 +1,443 @@ +/* -*- 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 /* memcpy & co. */ +#include + +/* Notes on the internal representation of dqueue: + * + * 'stor_begin' points at the beginning of the allocated storage. + * 'stor_end' points one past the allocated storage. + * + * 'begin' points at the first element of the queue contents. + * 'end' points one past the last element. + * + * The queue elements are stored "cyclically" within the allocated + * buffer, and arithmetic on 'begin' and 'end' is done modulo + * 'size = stor_end - stor_begin'. Thus the smallest valid value of + * 'begin' and 'end' is 'stor_begin'. Their largest valid value is + * 'stor_end - 1'. + * + * This means that 'begin == end' would be true both when the queue + * is full and when it is empty. To distinguish between these + * two situations, 'end' is set to NULL when the queue is empty. + */ + +/** + * \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 capacity How many elements to allocate memory for. + * \return Error code. + * + * Time complexity: O(\p capacity). + */ + +igraph_error_t FUNCTION(igraph_dqueue, init)(TYPE(igraph_dqueue)* q, igraph_integer_t capacity) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(capacity >= 0); + + if (capacity == 0) capacity = 1; + + q->stor_begin = IGRAPH_CALLOC(capacity, BASE); + IGRAPH_CHECK_OOM(q->stor_begin, "Cannot initialize dqueue."); + q->stor_end = q->stor_begin + capacity; + q->begin = q->stor_begin; + q->end = NULL; + + return IGRAPH_SUCCESS; +} + +/** + * \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 != NULL); + IGRAPH_FREE(q->stor_begin); /* sets to NULL */ +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_empty + * \brief Decide whether the queue is empty. + * + * \param q The queue. + * \return Boolean, true if \p q contains at least one element, + * false otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t FUNCTION(igraph_dqueue, empty)(const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + 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 != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + 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 \ref 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 != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + 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). + */ + +igraph_integer_t FUNCTION(igraph_dqueue, size)(const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + 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 != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ + 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 != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ + 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) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ + BASE tmp = *(q->begin); + (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 Removes 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 != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ + 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 note 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.) + */ + +igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + 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; + igraph_integer_t old_size = q->stor_end - q->stor_begin; + igraph_integer_t new_capacity = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to dqueue, already at maximum size.", IGRAPH_EOVERFLOW); + } + if (new_capacity == 0) { + new_capacity = 1; + } + + bigger = IGRAPH_CALLOC(new_capacity, BASE); + IGRAPH_CHECK_OOM(bigger, "Cannot push to dqueue."); + + if (q->stor_end - q->begin > 0) { + 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 + old_size; + q->stor_end = bigger + new_capacity; + 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 IGRAPH_SUCCESS; +} + +#if defined (OUT_FORMAT) + +#ifndef USING_R +igraph_error_t FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q) { + return FUNCTION(igraph_dqueue, fprint)(q, stdout); +} +#endif + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +#endif + +/** + * \ingroup dqueue + * \function igraph_dqueue_get + * \brief Access an element in a queue. + * + * \param q The queue. + * \param idx The index of the element within the queue. + * \return The desired element. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_dqueue, get)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx) { + IGRAPH_ASSERT(idx >= 0); + IGRAPH_ASSERT(idx < FUNCTION(igraph_dqueue, size)(q)); + 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 { + /* The assertions at the top make it impossible to reach here, + but omitting this branch would cause compiler warnings. */ + IGRAPH_FATAL("Out of bounds access in dqueue."); + } +} + +/** + * \ingroup dqueue + * \function igraph_dqueue_e + * \brief Access an element in a queue (deprecated alias). + * + * \deprecated-by igraph_dqueue_get 0.10.2 + */ + +BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx) { + return FUNCTION(igraph_dqueue, get)(q, idx); +} diff --git a/src/core/error.c b/src/core/error.c new file mode 100644 index 0000000..8436c7f --- /dev/null +++ b/src/core/error.c @@ -0,0 +1,599 @@ +/* -*- 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_FUNCATTR_NORETURN void igraph_abort(void) { +#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; this error should never be visible to the user, " + "please report this error along with the steps to reproduce it.", + /* 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.", + /* 61 */ "Result too large", + /* 62 */ "Input problem has no solution" +}; + + +/** + * \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 igraph_error_t igraph_errno) { + if ((int) igraph_errno < 0 || + (int) igraph_errno >= sizeof(igraph_i_error_strings) / sizeof(igraph_i_error_strings[0])) { + IGRAPH_FATALF("Invalid error code %d; no error string available.", (int) igraph_errno); + } + return igraph_i_error_strings[igraph_errno]; +} + + +/** + * \function igraph_error + * \brief Reports an error. + * + * \a igraph functions usually call this function (most often via the + * \ref IGRAPH_ERROR macro) if they notice an error. + * It calls the currently installed error handler function with the + * supplied arguments. + * + * \param reason Textual description of the error. + * \param file The source file in which the error was noticed. + * \param line The number of line in the source file which triggered the + * error. + * \param igraph_errno The \a igraph error code. + * \return the error code (if it returns) + * + * \sa \ref igraph_errorf() + */ + +igraph_error_t igraph_error(const char *reason, const char *file, int line, + igraph_error_t 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; +} + + +/** + * \function igraph_errorf + * \brief Reports 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 \ref igraph_error() + */ + +igraph_error_t igraph_errorf(const char *reason, const char *file, int line, + igraph_error_t igraph_errno, ...) { + va_list ap; + va_start(ap, igraph_errno); + vsnprintf(igraph_i_errormsg_buffer, + sizeof(igraph_i_errormsg_buffer) / sizeof(char), reason, ap); + va_end(ap); + return igraph_error(igraph_i_errormsg_buffer, file, line, igraph_errno); +} + +igraph_error_t igraph_errorvf(const char *reason, const char *file, int line, + igraph_error_t 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, igraph_error_t 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, igraph_error_t 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, igraph_error_t igraph_errno) { + fprintf(stderr, "Error at %s:%i : %s - %s.\n", + file, line, reason, igraph_strerror(igraph_errno)); + IGRAPH_FINALLY_FREE(); +} +#endif + + +/** + * \function igraph_set_error_handler + * \brief Sets a new error handler. + * + * Installs a new error handler. If called with \c NULL, it installs the + * default error handler (which is currently \ref igraph_error_handler_abort). + * + * \param new_handler The error handler function to install. + * \return The old error handler function. This should be saved and + * restored if \p new_handler is not needed any + * more. + */ + +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]; +IGRAPH_THREAD_LOCAL int igraph_i_finally_stack_size = 0; +IGRAPH_THREAD_LOCAL int igraph_i_finally_stack_level = 0; + +static void igraph_i_reset_finally_stack(void) { + igraph_i_finally_stack_size = 0; + igraph_i_finally_stack_level = 0; +} + +/* + * Adds another element to the free list + */ + +void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr) { + int no = igraph_i_finally_stack_size; + if (no < 0) { + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATALF("Corrupt finally stack: it contains %d elements.", no); + } + if (no >= (int) (sizeof(igraph_i_finally_stack) / sizeof(igraph_i_finally_stack[0]))) { + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATALF("Finally stack too large: it contains %d elements.", no); + } + igraph_i_finally_stack[no].ptr = ptr; + igraph_i_finally_stack[no].func = func; + igraph_i_finally_stack[no].level = igraph_i_finally_stack_level; + igraph_i_finally_stack_size++; +} + +void IGRAPH_FINALLY_CLEAN(int minus) { + igraph_i_finally_stack_size -= minus; + if (igraph_i_finally_stack_size < 0) { + int left = igraph_i_finally_stack_size + minus; + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATALF("Corrupt finally stack: trying to pop %d element(s) when only %d left.", minus, left); + } +} + +void IGRAPH_FINALLY_FREE(void) { + for (; igraph_i_finally_stack_size > 0; igraph_i_finally_stack_size--) { + int p = igraph_i_finally_stack_size - 1; + /* Call destructors only up to the current level */ + if (igraph_i_finally_stack[p].level < igraph_i_finally_stack_level) { + break; + } + igraph_i_finally_stack[p].func(igraph_i_finally_stack[p].ptr); + } +} + +int IGRAPH_FINALLY_STACK_SIZE(void) { + return igraph_i_finally_stack_size; +} + +/** + * \function IGRAPH_FINALLY_ENTER + * + * For internal use only. + * + * Opens a new level in the finally stack. Must have a matching + * IGRAPH_FINALLY_EXIT() call that closes the level and exits it. + * + * The finally stack is divided into "levels". A call to IGRAPH_FINALLY_FREE() + * will only unwind the current level of the finally stack, not any of the lower + * levels. This mechanism is used to allow some functions to pause stack unwinding + * until they can restore their data structures into a consistent state. + * See \ref igraph_add_edges() for an example usage. + */ +void IGRAPH_FINALLY_ENTER(void) { + int no = igraph_i_finally_stack_size; + /* Level indices must always be in increasing order in the finally stack */ + if (no > 0 && igraph_i_finally_stack[no-1].level > igraph_i_finally_stack_level) { + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATAL("Corrupt finally stack: cannot create new finally stack level before last one is freed."); + } + igraph_i_finally_stack_level++; +} + +/** + * \function IGRAPH_FINALLY_EXIT + * + * For internal use only. + * + * Exists the current level of the finally stack, see IGRAPH_FINALLY_ENTER() + * for details. If an error occured inbetween the last pair of + * IGRAPH_FINALLY_ENTER()/EXIT() calls, a call to igraph_error(), typically + * through IGRAPH_ERROR(), is mandatory directly after IGRAPH_FINALLY_EXIT(). + * This ensures that resource cleanup will properly resume. + */ +void IGRAPH_FINALLY_EXIT(void) { + igraph_i_finally_stack_level--; + if (igraph_i_finally_stack_level < 0) { + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATAL("Corrupt finally stack: trying to exit outermost finally stack level."); + } +} + + +/***** 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) { + IGRAPH_UNUSED(reason); + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); +} + +#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) { + fprintf(stderr, "Warning at %s:%i : %s\n", file, line, reason); +} +#endif + + +/** + * \function igraph_warning + * \brief Reports 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, + * but this is currently not used in igraph. + * \return The supplied error code. + */ + +void igraph_warning(const char *reason, const char *file, int line) { + + if (igraph_i_warning_handler) { + igraph_i_warning_handler(reason, file, line); +#ifndef USING_R + } else { + igraph_warning_handler_print(reason, file, line); +#endif + } +} + + +/** + * \function igraph_warningf + * \brief Reports a warning, printf-like version. + * + * This function is similar to \ref igraph_warning(), but + * uses a printf-like syntax. It substitutes the additional arguments + * into the \p reason template string and calls \ref igraph_warning(). + * + * \param reason Textual description of the warning, a template string + * with the same syntax as the standard printf C library function. + * \param file The source file in which the warning was noticed. + * \param line The number of line in the source file which triggered the + * warning. + * \param igraph_errno Warnings could have potentially error codes as well, + * but this is currently not used in igraph. + * \param ... The additional arguments to be substituted into the + * template string. + */ + +void igraph_warningf(const char *reason, const char *file, int line, ...) { + va_list ap; + va_start(ap, line); + vsnprintf(igraph_i_warningmsg_buffer, + sizeof(igraph_i_warningmsg_buffer) / sizeof(char), reason, ap); + va_end(ap); + igraph_warning(igraph_i_warningmsg_buffer, file, line); +} + + +/** + * \function igraph_set_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 + * 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_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; + +/** + * \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_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 + + +/** + * \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. + */ + +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(); +} + + +/** + * \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. + */ + +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/core/estack.c b/src/core/estack.c new file mode 100644 index 0000000..7c45a2a --- /dev/null +++ b/src/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" + +igraph_error_t igraph_estack_init(igraph_estack_t *s, igraph_integer_t setsize, + igraph_integer_t stacksize) { + IGRAPH_CHECK(igraph_bitset_init(&s->isin, setsize)); + IGRAPH_FINALLY(igraph_bitset_destroy, &s->isin); + IGRAPH_CHECK(igraph_stack_int_init(&s->stack, stacksize)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +void igraph_estack_destroy(igraph_estack_t *s) { + igraph_stack_int_destroy(&s->stack); + igraph_bitset_destroy(&s->isin); +} + +igraph_error_t igraph_estack_push(igraph_estack_t *s, igraph_integer_t elem) { + if ( !IGRAPH_BIT_TEST(s->isin, elem)) { + IGRAPH_CHECK(igraph_stack_int_push(&s->stack, elem)); + IGRAPH_BIT_SET(s->isin, elem); + } + return IGRAPH_SUCCESS; +} + +igraph_integer_t igraph_estack_pop(igraph_estack_t *s) { + igraph_integer_t elem = igraph_stack_int_pop(&s->stack); + IGRAPH_BIT_CLEAR(s->isin, elem); + return elem; +} + +igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, + igraph_integer_t elem) { + return IGRAPH_BIT_TEST(s->isin, elem); +} + +igraph_integer_t igraph_estack_size(const igraph_estack_t *s) { + return igraph_stack_int_size(&s->stack); +} + +#ifndef USING_R +igraph_error_t igraph_estack_print(const igraph_estack_t *s) { + return igraph_stack_int_print(&s->stack); +} +#endif diff --git a/src/core/estack.h b/src/core/estack.h new file mode 100644 index 0000000..9a738a6 --- /dev/null +++ b/src/core/estack.h @@ -0,0 +1,52 @@ +/* -*- 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_decls.h" +#include "igraph_bitset.h" +#include "igraph_stack.h" + +__BEGIN_DECLS + +typedef struct igraph_estack_t { + igraph_stack_int_t stack; + igraph_bitset_t isin; +} igraph_estack_t; + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_estack_init( + igraph_estack_t *s, igraph_integer_t setsize, igraph_integer_t stacksize); +IGRAPH_PRIVATE_EXPORT void igraph_estack_destroy(igraph_estack_t *s); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_estack_push(igraph_estack_t *s, igraph_integer_t elem); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_estack_pop(igraph_estack_t *s); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, + igraph_integer_t elem); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_estack_size(const igraph_estack_t *s); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_estack_print(const igraph_estack_t *s); + +__END_DECLS + +#endif diff --git a/src/core/exceptions.h b/src/core/exceptions.h new file mode 100644 index 0000000..d486082 --- /dev/null +++ b/src/core/exceptions.h @@ -0,0 +1,34 @@ +#ifndef IGRAPH_HANDLE_EXCEPTIONS_H +#define IGRAPH_HANDLE_EXCEPTIONS_H + +#include "igraph_error.h" + +#include +#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(). + * + * In some cases IGRAPH_HANDLE_EXCEPTIONS() won't work because the + * C preprocessor gets confused by the code block. In that case one can use + * IGRAPH_HANDLE_EXCEPTIONS_BEGIN; and IGRAPH_HANDLE_EXCEPTIONS_END at the + * beginning and end of the code block. + */ +#define IGRAPH_HANDLE_EXCEPTIONS_BEGIN \ + try { +#define IGRAPH_HANDLE_EXCEPTIONS_END \ + } \ + catch (const std::bad_alloc &e) { IGRAPH_ERROR(e.what(), IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } \ + catch (const std::range_error &e) { IGRAPH_ERROR(e.what(), IGRAPH_EOVERFLOW); /* LCOV_EXCL_LINE */ } \ + catch (const std::exception &e) { IGRAPH_ERROR(e.what(), IGRAPH_FAILURE); /* LCOV_EXCL_LINE */ } \ + catch (...) { IGRAPH_ERROR("Unknown exception caught.", IGRAPH_FAILURE); /* LCOV_EXCL_LINE */ } + +#define IGRAPH_HANDLE_EXCEPTIONS(code) \ + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; \ + code; \ + IGRAPH_HANDLE_EXCEPTIONS_END; + +#endif // IGRAPH_HANDLE_EXCEPTIONS_H diff --git a/src/core/fixed_vectorlist.c b/src/core/fixed_vectorlist.c new file mode 100644 index 0000000..8b247f7 --- /dev/null +++ b/src/core/fixed_vectorlist.c @@ -0,0 +1,59 @@ +/* -*- 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/fixed_vectorlist.h" + +void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l) { + igraph_vector_int_list_destroy(&l->vecs); +} + +igraph_error_t igraph_fixed_vectorlist_convert( + igraph_fixed_vectorlist_t *l, const igraph_vector_int_t *from, + igraph_integer_t size +) { + igraph_vector_int_t sizes; + igraph_integer_t i, no = igraph_vector_int_size(from), to; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&l->vecs, size); + IGRAPH_VECTOR_INT_INIT_FINALLY(&sizes, size); + + for (i = 0; i < no; i++) { + to = VECTOR(*from)[i]; + if (to >= 0) { + VECTOR(sizes)[to] += 1; + } + } + + for (i = 0; i < no; i++) { + to = VECTOR(*from)[i]; + if (to >= 0) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&l->vecs, to); + IGRAPH_CHECK(igraph_vector_int_push_back(v, i)); + } + } + + igraph_vector_int_destroy(&sizes); + IGRAPH_FINALLY_CLEAN(2); /* + l->vecs */ + + return IGRAPH_SUCCESS; +} diff --git a/src/core/fixed_vectorlist.h b/src/core/fixed_vectorlist.h new file mode 100644 index 0000000..34e2437 --- /dev/null +++ b/src/core/fixed_vectorlist.h @@ -0,0 +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 + 02110-1301 USA + +*/ + +#ifndef IGRAPH_TYPES_INTERNAL_H +#define IGRAPH_TYPES_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Vectorlist, fixed length */ +/* -------------------------------------------------- */ + +typedef struct igraph_fixed_vectorlist_t { + igraph_vector_int_list_t vecs; + igraph_integer_t length; +} igraph_fixed_vectorlist_t; + +void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l); +igraph_error_t igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, + const igraph_vector_int_t *from, + igraph_integer_t size); + +__END_DECLS + +#endif diff --git a/src/core/genheap.c b/src/core/genheap.c new file mode 100644 index 0000000..1e4ec3f --- /dev/null +++ b/src/core/genheap.c @@ -0,0 +1,307 @@ +/* + 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, see . + */ + +#include "igraph_memory.h" + +#include "core/genheap.h" + +#include /* memcpy */ + +#if defined(_MSC_VER) && _MSC_VER < 1927 + /* MSVC does not understand restrict before version 19.27 */ + #define restrict __restrict +#endif + +#define PARENT(x) (((x)+1)/2-1) +#define LEFTCHILD(x) (((x)+1)*2-1) +#define RIGHTCHILD(x) (((x)+1)*2) + +#define ELEM(h, x) ((char *) h->data + x * h->item_size) + +/* This is a smart indexed heap that can hold elements of arbitrary type. + * 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 swapfunc(char * restrict a, char * restrict b, size_t es) { + char t; + + do { + t = *a; + *a++ = *b; + *b++ = t; + } while (--es > 0); +} + +static void igraph_i_gen2wheap_switch(igraph_gen2wheap_t *h, + igraph_integer_t e1, igraph_integer_t e2) { + if (e1 != e2) { + igraph_integer_t tmp1, tmp2; + + swapfunc(ELEM(h, e1), ELEM(h, e2), h->item_size); + + 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_gen2wheap_shift_up(igraph_gen2wheap_t *h, + igraph_integer_t elem) { + if (elem == 0 || h->cmp(ELEM(h, elem), ELEM(h, PARENT(elem))) < 0) { + /* at the top */ + } else { + igraph_i_gen2wheap_switch(h, elem, PARENT(elem)); + igraph_i_gen2wheap_shift_up(h, PARENT(elem)); + } +} + +static void igraph_i_gen2wheap_sink(igraph_gen2wheap_t *h, + igraph_integer_t head) { + igraph_integer_t size = igraph_gen2wheap_size(h); + if (LEFTCHILD(head) >= size) { + /* no subtrees */ + } else if (RIGHTCHILD(head) == size || + h->cmp(ELEM(h, LEFTCHILD(head)), ELEM(h, RIGHTCHILD(head))) >= 0) { + /* sink to the left if needed */ + if (h->cmp(ELEM(h, head), ELEM(h, LEFTCHILD(head))) < 0) { + igraph_i_gen2wheap_switch(h, head, LEFTCHILD(head)); + igraph_i_gen2wheap_sink(h, LEFTCHILD(head)); + } + } else { + /* sink to the right */ + if (h->cmp(ELEM(h, head), ELEM(h, RIGHTCHILD(head))) < 0) { + igraph_i_gen2wheap_switch(h, head, RIGHTCHILD(head)); + igraph_i_gen2wheap_sink(h, RIGHTCHILD(head)); + } + } +} + +/* ------------------ */ +/* These are public */ +/* ------------------ */ + +/** + * Initializes a new two-way heap. The max_size parameter defines the maximum + * number of items that the heap can hold. + */ +igraph_error_t igraph_gen2wheap_init( + igraph_gen2wheap_t *h, + int (*cmp)(const void *, const void *), + size_t item_size, igraph_integer_t max_size +) { + /* TODO: Currently, storage is allocated for the maximum number of elements + * right from the start. This is sufficient for the only use case as of this + * writing, the D-SATUR graph colouring algorithm, but it may not be efficcient + * for other use cases. Consider improving this in the future. + */ + h->max_size = max_size; + /* We start with the biggest */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&h->index2, max_size); + h->cmp = cmp; + h->item_size = item_size; + h->data = igraph_calloc(max_size, item_size); + IGRAPH_CHECK_OOM(h->data, "Cannot initialize generic heap."); + IGRAPH_FINALLY(igraph_free, h->data); + IGRAPH_CHECK(igraph_vector_int_init(&h->index, 0)); + + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +/** + * Destroys a two-way heap. + */ +void igraph_gen2wheap_destroy(igraph_gen2wheap_t *h) { + IGRAPH_FREE(h->data); + igraph_vector_int_destroy(&h->index); + igraph_vector_int_destroy(&h->index2); +} + +/** + * Returns the current number of elements in the two-way heap. + */ +igraph_integer_t igraph_gen2wheap_size(const igraph_gen2wheap_t *h) { + return igraph_vector_int_size(&h->index); +} + +/** + * Clears a two-way heap, i.e. removes all the elements from the heap. + */ +void igraph_gen2wheap_clear(igraph_gen2wheap_t *h) { + igraph_vector_int_clear(&h->index); + igraph_vector_int_null(&h->index2); +} + +/** + * Returns whether the two-way heap is empty. + */ +igraph_bool_t igraph_gen2wheap_empty(const igraph_gen2wheap_t *h) { + return igraph_vector_int_empty(&h->index); +} + +/** + * Pushes a new element into the two-way heap, with the given associated index. + * The index must be between 0 and the size of the heap minus 1, inclusive. It + * is assumed (and not checked) that the heap does not have another item with + * the same index. + */ +igraph_error_t igraph_gen2wheap_push_with_index(igraph_gen2wheap_t *h, + igraph_integer_t idx, const void *elem) { + + igraph_integer_t size = igraph_vector_int_size(&h->index); + + if (size > IGRAPH_INTEGER_MAX - 2) { + /* to allow size+2 below */ + IGRAPH_ERROR("Cannot push to gen2wheap, already at maximum size.", IGRAPH_EOVERFLOW); + } + + memcpy(ELEM(h, size), elem, h->item_size); + IGRAPH_CHECK(igraph_vector_int_push_back(&h->index, idx)); + VECTOR(h->index2)[idx] = size + 2; + + /* maintain heap */ + igraph_i_gen2wheap_shift_up(h, size); + + return IGRAPH_SUCCESS; +} + +/** + * Returns the maximum number of elements that the two-way heap can hold. This + * is also one larger than the maximum allowed index that can be passed to + * \c igraph_gen2wheap_push_with_index . + */ +igraph_integer_t igraph_gen2wheap_max_size(const igraph_gen2wheap_t *h) { + return h->max_size; +} + +/** + * Returns a pointer to the largest element in the heap. + */ +const void *igraph_gen2wheap_max(const igraph_gen2wheap_t *h) { + return ELEM(h, 0); +} + +/** + * Returns the index that was associated to the largest element in the heap + * when it was pushed to the heap. + */ +igraph_integer_t igraph_gen2wheap_max_index(const igraph_gen2wheap_t *h) { + return VECTOR(h->index)[0]; +} + +/** + * Returns whether the heap contains an element with the given index, even if + * it was deactivated earlier. + */ +igraph_bool_t igraph_gen2wheap_has_elem(const igraph_gen2wheap_t *h, igraph_integer_t idx) { + return VECTOR(h->index2)[idx] != 0; +} + +/** + * Returns whether the heap contains an element with the given index \em and it + * has not been deactivated yet. + */ +igraph_bool_t igraph_gen2wheap_has_active(const igraph_gen2wheap_t *h, igraph_integer_t idx) { + return VECTOR(h->index2)[idx] > 1; +} + +/** + * Returns a pointer to the item at the given index in the two-way heap. + */ +const void *igraph_gen2wheap_get(const igraph_gen2wheap_t *h, igraph_integer_t idx) { + igraph_integer_t i = VECTOR(h->index2)[idx] - 2; + return ELEM(h, i); +} + +/** + * Deletes the largest element from the two-way heap. + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ +void igraph_gen2wheap_delete_max(igraph_gen2wheap_t *h) { + igraph_integer_t tmpidx = VECTOR(h->index)[0]; + igraph_i_gen2wheap_switch(h, 0, igraph_gen2wheap_size(h) - 1); + igraph_vector_int_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 0; + igraph_i_gen2wheap_sink(h, 0); +} + +/** + * Deactivates the largest element from the two-way heap. + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ +void igraph_gen2wheap_deactivate_max(igraph_gen2wheap_t *h) { + igraph_integer_t tmpidx = VECTOR(h->index)[0]; + igraph_i_gen2wheap_switch(h, 0, igraph_gen2wheap_size(h) - 1); + igraph_vector_int_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 1; + igraph_i_gen2wheap_sink(h, 0); +} + +/** + * Modifies the value associated to the given index in the two-way heap. + */ +void igraph_gen2wheap_modify(igraph_gen2wheap_t *h, igraph_integer_t idx, const void *elem) { + + igraph_integer_t pos = VECTOR(h->index2)[idx] - 2; + + memcpy(ELEM(h, pos), elem, h->item_size); + igraph_i_gen2wheap_sink(h, pos); + igraph_i_gen2wheap_shift_up(h, pos); +} + +/** + * Checks that the heap is in a consistent state + */ +igraph_error_t igraph_gen2wheap_check(const igraph_gen2wheap_t *h) { + igraph_integer_t size = igraph_gen2wheap_size(h); + igraph_integer_t i; + igraph_bool_t error = false; + + /* Check the heap property */ + for (i = 0; i < size; i++) { + if (LEFTCHILD(i) >= size) { + break; + } + if (h->cmp(ELEM(h, LEFTCHILD(i)), ELEM(h, i)) > 0) { + error = true; break; + } + if (RIGHTCHILD(i) >= size) { + break; + } + if (h->cmp(ELEM(h, RIGHTCHILD(i)), ELEM(h, i)) > 0) { + error = true; break; + } + } + + if (error) { + IGRAPH_ERROR("Inconsistent heap.", IGRAPH_EINTERNAL); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/core/genheap.h b/src/core/genheap.h new file mode 100644 index 0000000..e711b43 --- /dev/null +++ b/src/core/genheap.h @@ -0,0 +1,73 @@ +/* + 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, see . + */ + +#include "igraph_types.h" +#include "igraph_vector.h" + +typedef struct igraph_gen2wheap_t { + /** Maximum number of items in the heap */ + igraph_integer_t max_size; + + /** The size of an individual item */ + size_t item_size; + + /** The items themselves in the heap */ + /* TODO: currently this is always allocated to have max_size */ + void *data; + + /** qsort-style comparison function used to order items */ + int (*cmp)(const void *, const void *); + + /** An integer index associated to each item in the heap; this vector is + * always modified in tandem with \c data . Values in this vector are + * between 0 and size-1 */ + igraph_vector_int_t index; + + /** + * A _reverse_ index that allows O(1) lookup of the position of a given + * value within the \c index member. Note that it uses two special values: + * index2[i] == 0 means that \c i is not in \c index at all, while + * index2[i] == 1 means that \c i is in \c index but it was _deactivated_. + * The semantics of deactivation is up to the user of the data structure + * to decide. Other than these two special values, index2[i] == j means + * that index[j-2] == i and data[j-2] is the corresponding item in the heap + */ + igraph_vector_int_t index2; +} igraph_gen2wheap_t; + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_init( + igraph_gen2wheap_t *h, + int (*cmp)(const void *, const void *), + size_t item_size, igraph_integer_t max_size +); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_destroy(igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gen2wheap_size(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_clear(igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_gen2wheap_empty(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_push_with_index(igraph_gen2wheap_t *h, + igraph_integer_t idx, const void *elem); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gen2wheap_max_size(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE const void *igraph_gen2wheap_max(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gen2wheap_max_index(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_gen2wheap_has_elem(const igraph_gen2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_gen2wheap_has_active(const igraph_gen2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE const void *igraph_gen2wheap_get(const igraph_gen2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_delete_max(igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_deactivate_max(igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_modify(igraph_gen2wheap_t *h, igraph_integer_t idx, const void *elem); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_check(const igraph_gen2wheap_t *h); diff --git a/src/core/grid.c b/src/core/grid.c new file mode 100644 index 0000000..1c62034 --- /dev/null +++ b/src/core/grid.c @@ -0,0 +1,275 @@ +/* + IGraph library. + Copyright (C) 2006-2023 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_types.h" + +#include "core/grid.h" + +#include + +/* internal function */ + +static void igraph_i_2dgrid_which( + igraph_2dgrid_t *grid, igraph_real_t xc, igraph_real_t yc, + igraph_integer_t *x, igraph_integer_t *y +) { + if (xc <= grid->minx) { + *x = 0; + } else if (xc >= grid->maxx) { + *x = grid->stepsx - 1; + } else { + *x = floor((xc - (grid->minx)) / (grid->deltax)); + } + + if (yc <= grid->miny) { + *y = 0; + } else if (yc >= grid->maxy) { + *y = grid->stepsy - 1; + } else { + *y = floor((yc - (grid->miny)) / (grid->deltay)); + } +} + +igraph_error_t 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) { + igraph_integer_t no_of_points; + + IGRAPH_ASSERT(minx <= maxx); + IGRAPH_ASSERT(miny <= maxy); + IGRAPH_ASSERT(deltax > 0 && deltay > 0); + IGRAPH_ASSERT(isfinite(minx) && isfinite(maxx) && isfinite(miny) && isfinite(maxy)); + IGRAPH_ASSERT(isfinite(deltax) && isfinite(deltay)); + + grid->coords = coords; + grid->minx = minx; + grid->maxx = maxx; + grid->deltax = deltax; + grid->miny = miny; + grid->maxy = maxy; + grid->deltay = deltay; + + grid->stepsx = ceil((maxx - minx) / deltax); + grid->stepsy = ceil((maxy - miny) / deltay); + + no_of_points = igraph_matrix_nrow(coords); + + IGRAPH_CHECK(igraph_matrix_int_init(&grid->startidx, grid->stepsx, grid->stepsy)); + IGRAPH_FINALLY(igraph_matrix_int_destroy, &grid->startidx); + IGRAPH_VECTOR_INT_INIT_FINALLY(&grid->next, no_of_points); + IGRAPH_VECTOR_INT_INIT_FINALLY(&grid->prev, no_of_points); + + igraph_vector_int_null(&grid->prev); + igraph_vector_int_null(&grid->next); + + grid->massx = 0; + grid->massy = 0; + grid->vertices = 0; + + IGRAPH_FINALLY_CLEAN(3); + return IGRAPH_SUCCESS; +} + +void igraph_2dgrid_destroy(igraph_2dgrid_t *grid) { + igraph_matrix_int_destroy(&grid->startidx); + igraph_vector_int_destroy(&grid->next); + igraph_vector_int_destroy(&grid->prev); +} + +void igraph_2dgrid_add(igraph_2dgrid_t *grid, igraph_integer_t elem, + igraph_real_t xc, igraph_real_t yc) { + igraph_integer_t x, y; + igraph_integer_t first; + + MATRIX(*grid->coords, elem, 0) = xc; + MATRIX(*grid->coords, elem, 1) = yc; + + /* add to cell */ + igraph_i_2dgrid_which(grid, xc, yc, &x, &y); + first = 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, igraph_integer_t elem) { + igraph_integer_t x, y; + igraph_integer_t first; + igraph_real_t xc, yc; + + xc = MATRIX(*grid->coords, elem, 0); + yc = MATRIX(*grid->coords, elem, 1); + + /* add to cell */ + igraph_i_2dgrid_which(grid, xc, yc, &x, &y); + first = 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, igraph_integer_t elem, + igraph_real_t xc, igraph_real_t yc) { + igraph_integer_t oldx, oldy; + igraph_integer_t newx, newy; + igraph_real_t oldxc = MATRIX(*grid->coords, elem, 0); + igraph_real_t oldyc = MATRIX(*grid->coords, elem, 1); + igraph_integer_t first; + + xc = oldxc + xc; yc = oldyc + yc; + + igraph_i_2dgrid_which(grid, oldxc, oldyc, &oldx, &oldy); + igraph_i_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) [ 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)[ VECTOR(grid->next)[elem] - 1 ] = + VECTOR(grid->prev)[elem]; + } + + /* add to this cell */ + first = 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, igraph_integer_t elem) { + return VECTOR(grid->next)[elem] != -1; +} + +igraph_real_t igraph_2dgrid_sq_dist(const igraph_2dgrid_t *grid, + igraph_integer_t e1, igraph_integer_t 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; +} + +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 = 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 = MATRIX(grid->startidx, it->x, it->y); + } +} + +igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid, + igraph_2dgrid_iterator_t *it) { + igraph_integer_t 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 = VECTOR(grid->next) [ ret - 1 ]; + while (it->ncells > 0 && it->nei == 0 ) { + it->ncells -= 1; + it->nei = MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); + } + + /* Next vertex */ + it->vid = 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 = MATRIX(grid->startidx, it->x, it->y); + } + + return ret; +} + +igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid, + igraph_2dgrid_iterator_t *it) { + igraph_integer_t ret = it->nei; + + if (it->nei != 0) { + it->nei = VECTOR(grid->next) [ ret - 1 ]; + } + while (it->ncells > 0 && it->nei == 0 ) { + it->ncells -= 1; + it->nei = MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); + } + + return ret; +} diff --git a/src/core/grid.h b/src/core/grid.h new file mode 100644 index 0000000..3043359 --- /dev/null +++ b/src/core/grid.h @@ -0,0 +1,78 @@ +/* -*- 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; /* The current coordinates in the grid */ + igraph_real_t minx, maxx, deltax; /* Minimum and maximum X coordinates and X spacing */ + igraph_real_t miny, maxy, deltay; /* Minimum and maximum Y coordinates and Y spacing */ + igraph_integer_t stepsx, stepsy; /* Number of cells in the X and Y directions */ + igraph_matrix_int_t startidx; /* startidx[i, j] is the index of an arbitrary point in that grid cell, plus one; zero means "empty cell" */ + igraph_vector_int_t next; /* next[i] is the index of the point following point i in the same cell, plus one; zero means "last point" */ + igraph_vector_int_t prev; /* prev[i] is the index of the point preceding point i in the same cell, plus one; zero means "first point" */ + igraph_real_t massx, massy; /* The sum of the coordinates */ + igraph_integer_t vertices; /* Number of active vertices */ +} igraph_2dgrid_t; + +igraph_error_t 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, igraph_integer_t elem, + igraph_real_t xc, igraph_real_t yc); +void igraph_2dgrid_add2(igraph_2dgrid_t *grid, igraph_integer_t elem); +void igraph_2dgrid_move(igraph_2dgrid_t *grid, igraph_integer_t 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_FUNCATTR_PURE igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, igraph_integer_t elem); +IGRAPH_FUNCATTR_PURE igraph_real_t igraph_2dgrid_sq_dist(const igraph_2dgrid_t *grid, + igraph_integer_t e1, igraph_integer_t e2); + +typedef struct igraph_2dgrid_iterator_t { + igraph_integer_t vid, x, y; + igraph_integer_t nei; + igraph_integer_t 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/core/heap.c b/src/core/heap.c new file mode 100644 index 0000000..a202ea6 --- /dev/null +++ b/src/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_INT +#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_INT + +#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/core/heap.pmt b/src/core/heap.pmt new file mode 100644 index 0000000..0aa14c9 --- /dev/null +++ b/src/core/heap.pmt @@ -0,0 +1,382 @@ +/* -*- 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 /* 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, igraph_integer_t size, igraph_integer_t head); +static void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, igraph_integer_t size, igraph_integer_t elem); +static void FUNCTION(igraph_heap, i_sink)(BASE* arr, igraph_integer_t size, igraph_integer_t head); +static void FUNCTION(igraph_heap, i_switch)(BASE* arr, igraph_integer_t e1, igraph_integer_t e2); + +/** + * \ingroup heap + * \function igraph_heap_init + * \brief Initializes an empty heap object. + * + * Creates an \em empty heap, and also allocates memory + * for some elements. + * + * \param h Pointer to an uninitialized heap object. + * \param capacity Number of elements to allocate memory for. + * \return Error code. + * + * Time complexity: O(\p alloc_size), assuming memory allocation is a + * linear operation. + */ + +igraph_error_t FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, igraph_integer_t capacity) { + IGRAPH_ASSERT(capacity >= 0); + if (capacity == 0 ) { + capacity = 1; + } + h->stor_begin = IGRAPH_CALLOC(capacity, BASE); + IGRAPH_CHECK_OOM(h->stor_begin, "Cannot initialize heap."); + h->stor_end = h->stor_begin + capacity; + h->end = h->stor_begin; + h->destroy = true; + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, const BASE *data, igraph_integer_t len) { + h->stor_begin = IGRAPH_CALLOC(len, BASE); + IGRAPH_CHECK_OOM(h->stor_begin, "Cannot initialize heap from array."); + h->stor_end = h->stor_begin + len; + h->end = h->stor_end; + h->destroy = true; + + memcpy(h->stor_begin, data, (size_t) len * sizeof(BASE)); + + FUNCTION(igraph_heap, i_build) (h->stor_begin, h->end - h->stor_begin, 0); + + return IGRAPH_SUCCESS; +} + +/** + * \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 != NULL) { + IGRAPH_FREE(h->stor_begin); /* sets to NULL */ + } + } +} + +/** + * \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)(const 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_clear + * \brief Removes all elements from a heap. + * + * This function simply sets the size of the heap to zero, it does + * not free any allocated memory. For that you have to call + * \ref igraph_heap_destroy(). + * + * \param h The heap object. + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_heap, clear)(TYPE(igraph_heap)* h) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + h->end = h->stor_begin; +} + +/** + * \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. + */ + +igraph_error_t 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) { + igraph_integer_t old_size = FUNCTION(igraph_heap, size)(h); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to heap, already at maximum size.", IGRAPH_EOVERFLOW); + } + 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 IGRAPH_SUCCESS; +} + +/** + * \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)(const 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 Removes and returns 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 in the heap. + * + * 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). + */ + +igraph_integer_t FUNCTION(igraph_heap, size)(const 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 Reserves memory for a heap. + * + * Allocates memory for future use. The size of the heap is + * unchanged. If the heap is larger than the \p capacity parameter then + * nothing happens. + * + * \param h The heap object. + * \param capacity The number of elements to allocate memory for. + * \return Error code. + * + * Time complexity: O(\p capacity) if \p capacity is larger than the current + * number of elements. O(1) otherwise. + */ + +igraph_error_t FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, igraph_integer_t capacity) { + igraph_integer_t actual_size = FUNCTION(igraph_heap, size)(h); + BASE *tmp; + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); + + if (capacity <= actual_size) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(h->stor_begin, (size_t) capacity, BASE); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for heap."); + + h->stor_begin = tmp; + h->stor_end = h->stor_begin + capacity; + h->end = h->stor_begin + actual_size; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup heap + * \brief Build a heap, this should not be called directly. + */ + +void FUNCTION(igraph_heap, i_build)(BASE* arr, + igraph_integer_t size, igraph_integer_t 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, igraph_integer_t size, igraph_integer_t 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, igraph_integer_t size, igraph_integer_t 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, igraph_integer_t e1, igraph_integer_t e2) { + if (e1 != e2) { + BASE tmp = arr[e1]; + arr[e1] = arr[e2]; + arr[e2] = tmp; + } +} diff --git a/src/core/indheap.c b/src/core/indheap.c new file mode 100644 index 0000000..b4ee00e --- /dev/null +++ b/src/core/indheap.c @@ -0,0 +1,1038 @@ +/* -*- 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, igraph_integer_t head); +static void igraph_indheap_i_shift_up(igraph_indheap_t* h, igraph_integer_t elem); +static void igraph_indheap_i_sink(igraph_indheap_t* h, igraph_integer_t head); +static void igraph_indheap_i_switch(igraph_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2); + +/** + * \ingroup indheap + * \brief Initializes an indexed heap (constructor). + * + * \return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +igraph_error_t igraph_indheap_init(igraph_indheap_t* h, igraph_integer_t alloc_size) { + IGRAPH_ASSERT(alloc_size >= 0); + if (alloc_size == 0 ) { + alloc_size = 1; + } + h->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_real_t); + if (! h->stor_begin) { + h->index_begin = NULL; + IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + h->index_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + if (! h->index_begin) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = NULL; + IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + h->stor_end = h->stor_begin + alloc_size; + h->end = h->stor_begin; + h->destroy = true; + + return IGRAPH_SUCCESS; +} + +void igraph_indheap_clear(igraph_indheap_t *h) { + h->end = h->stor_begin; +} + +/** + * \ingroup indheap + * \brief Initializes and build an indexed heap from a C array (constructor). + * + * \return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +igraph_error_t igraph_indheap_init_array(igraph_indheap_t *h, const igraph_real_t *data, igraph_integer_t len) { + igraph_integer_t i; + igraph_integer_t alloc_size; + + IGRAPH_ASSERT(len >= 0); + alloc_size = (len <= 0) ? 1 : len; + + h->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_real_t); + if (! h->stor_begin) { + h->index_begin = 0; + IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + h->index_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + if (! h->index_begin) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = 0; + IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + h->stor_end = h->stor_begin + alloc_size; + h->end = h->stor_begin + len; + h->destroy = true; + + 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 IGRAPH_SUCCESS; +} + +/** + * \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(const 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. + */ + +igraph_error_t 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) { + igraph_integer_t old_size = igraph_indheap_size(h); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); + } + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup indheap + * \brief Adds an element to an indexed heap with a given index. + */ + +igraph_error_t igraph_indheap_push_with_index(igraph_indheap_t* h, igraph_integer_t 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) { + igraph_integer_t old_size = igraph_indheap_size(h); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); + } + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup indheap + * \brief Modifies an element in an indexed heap. + */ + +void igraph_indheap_modify(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem) { + igraph_integer_t 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; + } + + /* maintain indheap */ + igraph_indheap_i_build(h, 0); +} + +/** + * \ingroup indheap + * \brief Returns the largest element in an indexed heap. + */ + +igraph_real_t igraph_indheap_max(const 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. + */ + +igraph_integer_t igraph_indheap_size(const 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 + */ + +igraph_error_t igraph_indheap_reserve(igraph_indheap_t* h, igraph_integer_t size) { + igraph_integer_t actual_size = igraph_indheap_size(h); + igraph_real_t *tmp1; + igraph_integer_t *tmp2; + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + if (size <= actual_size) { + return IGRAPH_SUCCESS; + } + + tmp1 = IGRAPH_CALLOC(size, igraph_real_t); + if (tmp1 == 0) { + IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, tmp1); + tmp2 = IGRAPH_CALLOC(size, igraph_integer_t); + if (tmp2 == 0) { + IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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(igraph_integer_t)); + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup indheap + * \brief Returns the index of the largest element in an indexed heap. + */ + +igraph_integer_t igraph_indheap_max_index(const 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, igraph_integer_t head) { + + igraph_integer_t 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, igraph_integer_t 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, igraph_integer_t head) { + + igraph_integer_t 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, igraph_integer_t e1, igraph_integer_t 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] = tmp; + } +} + +/*************************************************/ + +/* -------------------------------------------------- */ +/* Doubly indexed heap */ +/* -------------------------------------------------- */ + +/* static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, igraph_integer_t head); */ /* Unused function */ +static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t* h, igraph_integer_t elem); +static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, igraph_integer_t head); +static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2); + +/** + * \ingroup doubleindheap + * \brief Initializes an empty doubly indexed heap object (constructor). + * + * \return Error code: + * - IGRAPH_ENOMEM: out of memory + */ + +igraph_error_t igraph_d_indheap_init(igraph_d_indheap_t* h, igraph_integer_t alloc_size) { + IGRAPH_ASSERT(alloc_size >= 0); + 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); /* LCOV_EXCL_LINE */ + } + h->stor_end = h->stor_begin + alloc_size; + h->end = h->stor_begin; + h->destroy = true; + h->index_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + 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); /* LCOV_EXCL_LINE */ + } + h->index2_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + 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); /* LCOV_EXCL_LINE */ + } + + return IGRAPH_SUCCESS; +} + +/** + * \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(const 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. + */ + +igraph_error_t igraph_d_indheap_push(igraph_d_indheap_t* h, igraph_real_t elem, + igraph_integer_t idx, igraph_integer_t idx2) { + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + /* full, allocate more storage */ + if (h->stor_end == h->end) { + igraph_integer_t old_size = igraph_d_indheap_size(h); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); + } + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup doubleindheap + * \brief Returns the largest element in the heap. + */ + +igraph_real_t igraph_d_indheap_max(const 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. + */ + +igraph_integer_t igraph_d_indheap_size(const 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 + */ + +igraph_error_t igraph_d_indheap_reserve(igraph_d_indheap_t* h, igraph_integer_t size) { + igraph_integer_t actual_size = igraph_d_indheap_size(h); + igraph_real_t *tmp1; + igraph_integer_t *tmp2, *tmp3; + IGRAPH_ASSERT(h != 0); + IGRAPH_ASSERT(h->stor_begin != 0); + + if (size <= actual_size) { + return IGRAPH_SUCCESS; + } + + tmp1 = IGRAPH_CALLOC(size, igraph_real_t); + if (tmp1 == 0) { + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, tmp1); + tmp2 = IGRAPH_CALLOC(size, igraph_integer_t); + if (tmp2 == 0) { + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, tmp2); + tmp3 = IGRAPH_CALLOC(size, igraph_integer_t); + if (tmp3 == 0) { + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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(igraph_integer_t)); + memcpy(tmp3, h->index2_begin, (size_t) actual_size * sizeof(igraph_integer_t)); + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup doubleindheap + * \brief Gives the indices of the maximal element in the heap. + */ + +void igraph_d_indheap_max_index(igraph_d_indheap_t *h, igraph_integer_t *idx, igraph_integer_t *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, igraph_integer_t head) { + + igraph_integer_t 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, igraph_integer_t 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, igraph_integer_t head) { + + igraph_integer_t 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, igraph_integer_t e1, igraph_integer_t e2) { + if (e1 != e2) { + igraph_integer_t 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, + igraph_integer_t e1, igraph_integer_t e2) { + if (e1 != e2) { + igraph_integer_t 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, + igraph_integer_t 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, + igraph_integer_t head) { + igraph_integer_t 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 */ +/* ------------------ */ + +/** + * Initializes a new two-way heap. The max_size parameter defines the maximum + * number of items that the heap can hold. + */ +igraph_error_t igraph_2wheap_init(igraph_2wheap_t *h, igraph_integer_t max_size) { + h->max_size = max_size; + /* We start with the biggest */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&h->index2, max_size); + IGRAPH_VECTOR_INIT_FINALLY(&h->data, 0); + IGRAPH_CHECK(igraph_vector_int_init(&h->index, 0)); + /* IGRAPH_FINALLY(igraph_vector_int_destroy, &h->index); */ + + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +/** + * Destroys a two-way heap. + */ +void igraph_2wheap_destroy(igraph_2wheap_t *h) { + igraph_vector_destroy(&h->data); + igraph_vector_int_destroy(&h->index); + igraph_vector_int_destroy(&h->index2); +} + +/** + * Clears a two-way heap, i.e. removes all the elements from the heap. + */ +void igraph_2wheap_clear(igraph_2wheap_t *h) { + igraph_vector_clear(&h->data); + igraph_vector_int_clear(&h->index); + igraph_vector_int_null(&h->index2); +} + +/** + * Returns whether the two-way heap is empty. + */ +igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h) { + return igraph_vector_empty(&h->data); +} + +/** + * Pushes a new element into the two-way heap, with the given associated index. + * The index must be between 0 and the size of the heap minus 1, inclusive. It + * is assumed (and not checked) that the heap does not have another item with + * the same index. + */ +igraph_error_t igraph_2wheap_push_with_index(igraph_2wheap_t *h, + igraph_integer_t idx, igraph_real_t elem) { + + /* printf("-> %.2g [%li]\n", elem, idx); */ + + igraph_integer_t size = igraph_vector_size(&h->data); + + if (size > IGRAPH_INTEGER_MAX - 2) { + /* to allow size+2 below */ + IGRAPH_ERROR("Cannot push to 2wheap, already at maximum size.", IGRAPH_EOVERFLOW); + } + + IGRAPH_CHECK(igraph_vector_push_back(&h->data, elem)); + IGRAPH_CHECK(igraph_vector_int_push_back(&h->index, idx)); + VECTOR(h->index2)[idx] = size + 2; + + /* maintain heap */ + igraph_i_2wheap_shift_up(h, size); + return IGRAPH_SUCCESS; +} + +/** + * Returns the current number of elements in the two-way heap. + */ +igraph_integer_t igraph_2wheap_size(const igraph_2wheap_t *h) { + return igraph_vector_size(&h->data); +} + +/** + * Returns the maximum number of elements that the two-way heap can hold. This + * is also one larger than the maximum allowed index that can be passed to + * \c igraph_2wheap_push_with_index . + */ +igraph_integer_t igraph_2wheap_max_size(const igraph_2wheap_t *h) { + return h->max_size; +} + +/** + * Returns the largest element in the heap. + */ +igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h) { + return VECTOR(h->data)[0]; +} + +/** + * Returns the index that was associated to the largest element in the heap + * when it was pushed to the heap. + */ +igraph_integer_t igraph_2wheap_max_index(const igraph_2wheap_t *h) { + return VECTOR(h->index)[0]; +} + +/** + * Returns whether the heap contains an element with the given index, even if + * it was deactivated earlier. + */ +igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, igraph_integer_t idx) { + return VECTOR(h->index2)[idx] != 0; +} + +/** + * Returns whether the heap contains an element with the given index \em and it + * has not been deactivated yet. + */ +igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, igraph_integer_t idx) { + return VECTOR(h->index2)[idx] > 1; +} + +/** + * Returns the item at the given index in the two-way heap. + */ +igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, igraph_integer_t idx) { + igraph_integer_t i = VECTOR(h->index2)[idx] - 2; + return VECTOR(h->data)[i]; +} + +/** + * Deletes and returns the largest element from the two-way heap. + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ +igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h) { + + igraph_real_t tmp = VECTOR(h->data)[0]; + igraph_integer_t tmpidx = VECTOR(h->index)[0]; + igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1); + igraph_vector_pop_back(&h->data); + igraph_vector_int_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 0; + igraph_i_2wheap_sink(h, 0); + + /* printf("<-max %.2g\n", tmp); */ + + return tmp; +} + +/** + * Deactivates and returns the largest element from the two-way heap. + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ +igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h) { + + igraph_real_t tmp = VECTOR(h->data)[0]; + igraph_integer_t tmpidx = VECTOR(h->index)[0]; + igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1); + igraph_vector_pop_back(&h->data); + igraph_vector_int_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 1; + igraph_i_2wheap_sink(h, 0); + + return tmp; +} + +/** + * Deletes the largest element from the heap and returns it along with its + * associated index (the latter being returned in an output argument). + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ +igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, igraph_integer_t *idx) { + + igraph_real_t tmp = VECTOR(h->data)[0]; + igraph_integer_t tmpidx = VECTOR(h->index)[0]; + igraph_i_2wheap_switch(h, 0, igraph_2wheap_size(h) - 1); + igraph_vector_pop_back(&h->data); + igraph_vector_int_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 0; + igraph_i_2wheap_sink(h, 0); + + if (idx) { + *idx = tmpidx; + } + return tmp; +} + +/** + * Modifies the value associated to the given index in the two-way heap. + */ +void igraph_2wheap_modify(igraph_2wheap_t *h, igraph_integer_t idx, igraph_real_t elem) { + + igraph_integer_t 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); +} + +/** + * Checks that the heap is in a consistent state + */ +igraph_error_t igraph_2wheap_check(const igraph_2wheap_t *h) { + igraph_integer_t size = igraph_2wheap_size(h); + igraph_integer_t i; + igraph_bool_t error = false; + + /* 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 = true; break; + } + if (RIGHTCHILD(i) >= size) { + break; + } + if (VECTOR(h->data)[RIGHTCHILD(i)] > VECTOR(h->data)[i]) { + error = true; break; + } + } + + if (error) { + IGRAPH_ERROR("Inconsistent heap.", IGRAPH_EINTERNAL); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/core/indheap.h b/src/core/indheap.h new file mode 100644 index 0000000..93194cb --- /dev/null +++ b/src/core/indheap.h @@ -0,0 +1,157 @@ +/* -*- 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; + igraph_bool_t destroy; + igraph_integer_t* index_begin; +} igraph_indheap_t; + +#define IGRAPH_INDHEAP_NULL { 0,0,0,0,0 } + +igraph_error_t igraph_indheap_init(igraph_indheap_t* h, igraph_integer_t size); +igraph_error_t igraph_indheap_init_array(igraph_indheap_t *t, const igraph_real_t *data, igraph_integer_t len); +void igraph_indheap_destroy(igraph_indheap_t* h); +void igraph_indheap_clear(igraph_indheap_t *h); +IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_indheap_empty(const igraph_indheap_t* h); +igraph_error_t igraph_indheap_push(igraph_indheap_t* h, igraph_real_t elem); +igraph_error_t igraph_indheap_push_with_index(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem); +void igraph_indheap_modify(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem); +IGRAPH_FUNCATTR_PURE igraph_real_t igraph_indheap_max(const igraph_indheap_t* h); +igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_indheap_size(const igraph_indheap_t *h); +igraph_error_t igraph_indheap_reserve(igraph_indheap_t* h, igraph_integer_t size); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_indheap_max_index(const 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; + igraph_bool_t destroy; + igraph_integer_t* index_begin; + igraph_integer_t* index2_begin; +} igraph_d_indheap_t; + + +#define IGRAPH_D_INDHEAP_NULL { 0,0,0,0,0,0 } + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_d_indheap_init(igraph_d_indheap_t *h, igraph_integer_t size); +IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_destroy(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_d_indheap_empty(const igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_d_indheap_push(igraph_d_indheap_t *h, igraph_real_t elem, + igraph_integer_t idx, igraph_integer_t idx2); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t igraph_d_indheap_max(const igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_d_indheap_size(const igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_d_indheap_reserve(igraph_d_indheap_t *h, igraph_integer_t size); +IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_max_index(igraph_d_indheap_t *h, igraph_integer_t *idx, igraph_integer_t *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 { + /** Number of items in the heap */ + igraph_integer_t max_size; + + /** The items themselves in the heap */ + igraph_vector_t data; + + /** An integer index associated to each item in the heap; this vector is + * always modified in tandem with \c data . Values in this vector are + * between 0 and size-1 */ + igraph_vector_int_t index; + + /** + * A _reverse_ index that allows O(1) lookup of the position of a given + * value within the \c index member. Note that it uses two special values: + * index2[i] == 0 means that \c i is not in \c index at all, while + * index2[i] == 1 means that \c i is in \c index but it was _deactivated_. + * The semantics of deactivation is up to the user of the data structure + * to decide. Other than these two special values, index2[i] == j means + * that index[j-2] == i and data[j-2] is the corresponding item in the heap + */ + igraph_vector_int_t index2; +} igraph_2wheap_t; + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_init(igraph_2wheap_t *h, igraph_integer_t size); +IGRAPH_PRIVATE_EXPORT void igraph_2wheap_destroy(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT void igraph_2wheap_clear(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_push_with_index(igraph_2wheap_t *h, + igraph_integer_t idx, igraph_real_t elem); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_2wheap_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_2wheap_max_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t 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_FUNCATTR_PURE igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, igraph_integer_t 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, igraph_integer_t *idx); +IGRAPH_PRIVATE_EXPORT void igraph_2wheap_modify(igraph_2wheap_t *h, igraph_integer_t idx, igraph_real_t elem); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_check(const igraph_2wheap_t *h); + +__END_DECLS + +#endif diff --git a/src/core/interruption.c b/src/core/interruption.c new file mode 100644 index 0000000..7942203 --- /dev/null +++ b/src/core/interruption.c @@ -0,0 +1,40 @@ +/* -*- 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; + +igraph_error_t 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/core/interruption.h b/src/core/interruption.h new file mode 100644 index 0000000..98cbafb --- /dev/null +++ b/src/core/interruption.h @@ -0,0 +1,81 @@ +/* -*- 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_LIMITED + * + * This is a variant of IGRAPH_ALLOW_INTERRUPTION() that checks for interruption + * only on every 'skips' call. The 'iter' macro parameter is the name of a variable, + * usually of type 'int', that is used to count calls to this macto. It must be declared + * separately, outside of the loop where IGRAPH_ALLOW_INTERRUPTION_LIMITED() is called, + * and initialized to 0. Example: + * + * int myiter = 0; + * for (igraph_integer_t i=0; i < n; i++) { + * // Allow for interruption every 1000th iteration + * IGRAPH_ALLOW_INTERRUPTION_LIMITED(myiter, 1000); + * } + * + */ +#define IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, skips) \ + do { \ + if (++iter >= skips) { \ + IGRAPH_ALLOW_INTERRUPTION(); \ + iter = 0; \ + } \ + } while (0) + +__END_DECLS + +#endif diff --git a/src/core/marked_queue.c b/src/core/marked_queue.c new file mode 100644 index 0000000..29fe411 --- /dev/null +++ b/src/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 + +igraph_error_t igraph_marked_queue_int_init(igraph_marked_queue_int_t *q, + igraph_integer_t size) { + IGRAPH_CHECK(igraph_dqueue_int_init(&q->Q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q->Q); + IGRAPH_CHECK(igraph_vector_int_init(&q->set, size)); + q->mark = 1; + q->size = 0; + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +void igraph_marked_queue_int_destroy(igraph_marked_queue_int_t *q) { + igraph_vector_int_destroy(&q->set); + igraph_dqueue_int_destroy(&q->Q); +} + +void igraph_marked_queue_int_reset(igraph_marked_queue_int_t *q) { + igraph_dqueue_int_clear(&q->Q); + q->size = 0; + q->mark += 1; + if (q->mark == 0) { + igraph_vector_int_null(&q->set); + q->mark += 1; + } +} + +igraph_bool_t igraph_marked_queue_int_empty(const igraph_marked_queue_int_t *q) { + return q->size == 0; +} + +igraph_integer_t igraph_marked_queue_int_size(const igraph_marked_queue_int_t *q) { + return q->size; +} + +igraph_bool_t igraph_marked_queue_int_iselement(const igraph_marked_queue_int_t *q, + igraph_integer_t elem) { + return (VECTOR(q->set)[elem] == q->mark); +} + +igraph_error_t igraph_marked_queue_int_push(igraph_marked_queue_int_t *q, igraph_integer_t elem) { + if (VECTOR(q->set)[elem] != q->mark) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q->Q, elem)); + VECTOR(q->set)[elem] = q->mark; + q->size += 1; + } + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_marked_queue_int_start_batch(igraph_marked_queue_int_t *q) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q->Q, BATCH_MARKER)); + return IGRAPH_SUCCESS; +} + +void igraph_marked_queue_int_pop_back_batch(igraph_marked_queue_int_t *q) { + igraph_integer_t size = igraph_dqueue_int_size(&q->Q); + igraph_integer_t elem; + while (size > 0 && + (elem = igraph_dqueue_int_pop_back(&q->Q)) != BATCH_MARKER) { + VECTOR(q->set)[elem] = 0; + size--; + q->size--; + } +} + +#ifndef USING_R +igraph_error_t igraph_marked_queue_int_print(const igraph_marked_queue_int_t *q) { + IGRAPH_CHECK(igraph_dqueue_int_print(&q->Q)); + return IGRAPH_SUCCESS; +} +#endif + +igraph_error_t igraph_marked_queue_int_fprint(const igraph_marked_queue_int_t *q, FILE *file) { + IGRAPH_CHECK(igraph_dqueue_int_fprint(&q->Q, file)); + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_marked_queue_int_as_vector(const igraph_marked_queue_int_t *q, + igraph_vector_int_t *vec) { + igraph_integer_t i, p, n = igraph_dqueue_int_size(&q->Q); + IGRAPH_CHECK(igraph_vector_int_resize(vec, q->size)); + for (i = 0, p = 0; i < n; i++) { + igraph_integer_t e = igraph_dqueue_int_get(&q->Q, i); + if (e != BATCH_MARKER) { + VECTOR(*vec)[p++] = e; + } + } + return IGRAPH_SUCCESS; +} diff --git a/src/core/marked_queue.h b/src/core/marked_queue.h new file mode 100644 index 0000000..70a2fdc --- /dev/null +++ b/src/core/marked_queue.h @@ -0,0 +1,75 @@ +/* -*- 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_decls.h" +#include "igraph_vector.h" +#include "igraph_dqueue.h" + +#include + +__BEGIN_DECLS + +/* 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_int_t { + igraph_dqueue_int_t Q; + igraph_vector_int_t set; + igraph_integer_t mark; + igraph_integer_t size; +} igraph_marked_queue_int_t; + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_init(igraph_marked_queue_int_t *q, + igraph_integer_t size); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_destroy(igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_reset(igraph_marked_queue_int_t *q); + +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_marked_queue_int_empty(const igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_marked_queue_int_size(const igraph_marked_queue_int_t *q); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_print(const igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_fprint(const igraph_marked_queue_int_t *q, FILE *file); + +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_marked_queue_int_iselement(const igraph_marked_queue_int_t *q, + igraph_integer_t elem); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_push(igraph_marked_queue_int_t *q, igraph_integer_t elem); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_start_batch(igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_pop_back_batch(igraph_marked_queue_int_t *q); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_as_vector(const igraph_marked_queue_int_t *q, + igraph_vector_int_t *vec); + +__END_DECLS + +#endif diff --git a/src/core/math.h b/src/core/math.h new file mode 100644 index 0000000..9c406c2 --- /dev/null +++ b/src/core/math.h @@ -0,0 +1,81 @@ +/* + 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, see . +*/ + +#ifndef IGRAPH_CORE_MATH_H +#define IGRAPH_CORE_MATH_H + +#include + +/* Math constants are not part of standard C */ + +/* The following definitions contain enough precision for + * an IEEE-754 quadruple-precision floating point format. */ + +#ifndef M_E +#define M_E 2.71828182845904523536028747135266250 +#endif + +#ifndef M_LOG2E +#define M_LOG2E 1.44269504088896340735992468100189214 +#endif + +#ifndef M_LOG10E +#define M_LOG10E 0.434294481903251827651128918916605082 +#endif + +#ifndef M_LN2 +#define M_LN2 0.693147180559945309417232121458176568 +#endif + +#ifndef M_LN10 +#define M_LN10 2.30258509299404568401799145468436421 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950288 +#endif + +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923132169163975144 +#endif + +#ifndef M_PI_4 +#define M_PI_4 0.785398163397448309615660845819875721 +#endif + +#ifndef M_1_PI +#define M_1_PI 0.318309886183790671537767526745028724 +#endif + +#ifndef M_2_PI +#define M_2_PI 0.636619772367581343075535053490057448 +#endif + +#ifndef M_2_SQRTPI +#define M_2_SQRTPI 1.12837916709551257389615890312154517 +#endif + +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880168872420969808 +#endif + +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.707106781186547524400844362104849039 +#endif + +#endif /* IGRAPH_CORE_MATH_H */ diff --git a/src/core/matrix.c b/src/core/matrix.c new file mode 100644 index 0000000..62a1c3c --- /dev/null +++ b/src/core/matrix.c @@ -0,0 +1,304 @@ +/* -*- 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_matrix.h" +#include "igraph_types.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_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 + +/** + * \ingroup matrix + * \function igraph_matrix_complex_real + * \brief Gives the real part of a complex matrix. + * + * \param m Pointer to a complex matrix. + * \param real Pointer to an initialized matrix. The result will be stored here. + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_real(const igraph_matrix_complex_t *m, + igraph_matrix_t *real) { + igraph_integer_t nrow = igraph_matrix_complex_nrow(m); + igraph_integer_t ncol = igraph_matrix_complex_ncol(m); + IGRAPH_CHECK(igraph_matrix_resize(real, nrow, ncol)); + IGRAPH_CHECK(igraph_vector_complex_real(&m->data, &real->data)); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup matrix + * \function igraph_matrix_complex_imag + * \brief Gives the imaginary part of a complex matrix. + * + * \param m Pointer to a complex matrix. + * \param imag Pointer to an initialized matrix. The result will be stored here. + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_imag(const igraph_matrix_complex_t *m, + igraph_matrix_t *imag) { + igraph_integer_t nrow = igraph_matrix_complex_nrow(m); + igraph_integer_t ncol = igraph_matrix_complex_ncol(m); + IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol)); + IGRAPH_CHECK(igraph_vector_complex_imag(&m->data, &imag->data)); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup matrix + * \function igraph_matrix_complex_realimag + * \brief Gives the real and imaginary parts of a complex matrix. + * + * \param m Pointer to a complex matrix. + * \param real Pointer to an initialized matrix. The real part will be stored here. + * \param imag Pointer to an initialized matrix. The imaginary part will be stored here. + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_realimag(const igraph_matrix_complex_t *m, + igraph_matrix_t *real, + igraph_matrix_t *imag) { + igraph_integer_t nrow = igraph_matrix_complex_nrow(m); + igraph_integer_t ncol = igraph_matrix_complex_ncol(m); + IGRAPH_CHECK(igraph_matrix_resize(real, nrow, ncol)); + IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol)); + IGRAPH_CHECK(igraph_vector_complex_realimag(&m->data, &real->data, + &imag->data)); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup matrix + * \function igraph_matrix_complex_create + * \brief Creates a complex matrix from a real and imaginary part. + * + * \param m Pointer to an uninitialized complex matrix. + * \param real Pointer to the real part of the complex matrix. + * \param imag Pointer to the imaginary part of the complex matrix. + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_create(igraph_matrix_complex_t *m, + const igraph_matrix_t *real, + const igraph_matrix_t *imag) { + igraph_integer_t nrowr = igraph_matrix_nrow(real); + igraph_integer_t ncolr = igraph_matrix_ncol(real); + igraph_integer_t nrowi = igraph_matrix_nrow(imag); + igraph_integer_t ncoli = igraph_matrix_ncol(imag); + + if (nrowr != nrowi || ncolr != ncoli) { + IGRAPH_ERRORF("Dimensions of real (%" IGRAPH_PRId " by %" IGRAPH_PRId ") and " + "imaginary (%" IGRAPH_PRId " by %" IGRAPH_PRId ") matrices must match.", + IGRAPH_EINVAL, nrowr, ncolr, nrowi, ncoli); + } + + IGRAPH_CHECK(igraph_matrix_complex_init(m, nrowr, ncolr)); + + for (igraph_integer_t i = 0; i < nrowr * ncolr; i++) { + VECTOR(m->data)[i] = igraph_complex(VECTOR(real->data)[i], VECTOR(imag->data)[i]); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup matrix + * \function igraph_matrix_complex_create_polar + * \brief Creates a complex matrix from a magnitude and an angle. + * + * \param m Pointer to an uninitialized complex matrix. + * \param r Pointer to a real matrix containing magnitudes. + * \param theta Pointer to a real matrix containing arguments (phase angles). + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_create_polar(igraph_matrix_complex_t *m, + const igraph_matrix_t *r, + const igraph_matrix_t *theta) { + igraph_integer_t nrowr = igraph_matrix_nrow(r); + igraph_integer_t ncolr = igraph_matrix_ncol(r); + igraph_integer_t nrowt = igraph_matrix_nrow(theta); + igraph_integer_t ncolt = igraph_matrix_ncol(theta); + + if (nrowr != nrowt || ncolr != ncolt) { + IGRAPH_ERRORF("Dimensions of magnitude (%" IGRAPH_PRId " by %" IGRAPH_PRId ") and " + "angle (%" IGRAPH_PRId " by %" IGRAPH_PRId ") matrices must match.", + IGRAPH_EINVAL, nrowr, ncolr, nrowt, ncolt); + } + + IGRAPH_CHECK(igraph_matrix_complex_init(m, nrowr, ncolr)); + + for (igraph_integer_t i = 0; i < nrowr * ncolr; i++) { + VECTOR(m->data)[i] = igraph_complex_polar(VECTOR(r->data)[i], VECTOR(theta->data)[i]); + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_matrix_complex_all_almost_e + * \brief Are all elements almost equal? + * + * Checks if the elements of two complex matrices are equal within a relative tolerance. + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \param eps Relative tolerance, see \ref igraph_complex_almost_equals() for details. + * \return True if the two matrices are almost equal, false if there is at least + * one differing element or if the matrices are not of the same dimensions. + */ +igraph_bool_t igraph_matrix_complex_all_almost_e(igraph_matrix_complex_t *lhs, + igraph_matrix_complex_t *rhs, + igraph_real_t eps) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + igraph_vector_complex_all_almost_e(&lhs->data, &rhs->data, eps); +} + +/** + * Deprecated in favour of \ref igraph_matrix_all_almost_e() which uses + * relative tolerances. Will be removed in 0.11. + * + * Checks if two matrices are equal within an absolute tolerance. + */ +igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t tol) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + igraph_vector_e_tol(&lhs->data, &rhs->data, tol); +} + + +/** + * \function igraph_matrix_all_almost_e + * \brief Are all elements almost equal? + * + * Checks if the elements of two matrices are equal within a relative tolerance. + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \param eps Relative tolerance, see \ref igraph_almost_equals() for details. + * \return True if the two matrices are almost equal, false if there is at least + * one differing element or if the matrices are not of the same dimensions. + */ +igraph_bool_t igraph_matrix_all_almost_e(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t eps) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + igraph_vector_all_almost_e(&lhs->data, &rhs->data, eps); +} + + +/** + * \function igraph_matrix_zapsmall + * \brief Replaces small elements of a matrix by exact zeros. + * + * Matrix elements which are smaller in magnitude than the given absolute + * tolerance will be replaced by exact zeros. The default tolerance + * corresponds to two-thirds of the representable digits of \type igraph_real_t, + * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. + * + * \param m The matrix to process, it will be changed in-place. + * \param tol Tolerance value. Numbers smaller than this in magnitude will + * be replaced by zeros. Pass in zero to use the default tolerance. + * Must not be negative. + * \return Error code. + * + * \sa \ref igraph_matrix_all_almost_e() and \ref igraph_almost_equals() to + * perform comparisons with relative tolerances. + */ +igraph_error_t igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol) { + return igraph_vector_zapsmall(&m->data, tol); +} + +/** + * \function igraph_matrix_complex_zapsmall + * \brief Replaces small elements of a complex matrix by exact zeros. + * + * Similarly to \ref igraph_matrix_zapsmall(), small elements will be replaced + * by zeros. The operation is performed separately on the real and imaginary + * parts of the numbers. This way, complex numbers with a large real part and + * tiny imaginary part will effectively be transformed to real numbers. + * The default tolerance + * corresponds to two-thirds of the representable digits of \type igraph_real_t, + * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. + * + * \param m The matrix to process, it will be changed in-place. + * \param tol Tolerance value. Real and imaginary parts smaller than this in + * magnitude will be replaced by zeros. Pass in zero to use the default + * tolerance. Must not be negative. + * \return Error code. + * + * \sa \ref igraph_matrix_complex_all_almost_e() and + * \ref igraph_complex_almost_equals() to perform comparisons with relative + * tolerances. + */ +igraph_error_t igraph_matrix_complex_zapsmall(igraph_matrix_complex_t *m, igraph_real_t tol) { + return igraph_vector_complex_zapsmall(&m->data, tol); +} diff --git a/src/core/matrix.pmt b/src/core/matrix.pmt new file mode 100644 index 0000000..fbdf918 --- /dev/null +++ b/src/core/matrix.pmt @@ -0,0 +1,1877 @@ +/* -*- 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_error.h" + +#include "math/safe_intop.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. + */ + +igraph_error_t FUNCTION(igraph_matrix, init)( + TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol) { + igraph_integer_t size; + IGRAPH_ASSERT(nrow >= 0 && ncol >= 0); + IGRAPH_SAFE_MULT(nrow, ncol, &size); + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&m->data, size)); + m->nrow = nrow; + m->ncol = ncol; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup matrix + * \function igraph_matrix_view + * \brief Creates a matrix view into an existing array. + * + * + * This function lets you treat an existing C array as a matrix. The elements + * of the matrix are assumed to be stored in column-major order in the array, + * i.e. the elements of the first column are stored first, followed by the + * second column and so on. + * + * + * Since this function creates a view into an existing array, you must \em not + * destroy the \c igraph_matrix_t object when you are done with it. Similarly, + * you must \em not call any function on it that may attempt to modify the size + * of the matrix. Modifying an element in the matrix will modify the underlying + * array as the two share the same memory block. + * + * \param m Pointer to a not yet initialized matrix object where the view will + * be created. + * \param data The array that the matrix provides a view into. + * \param nrow The number of rows in the matrix. + * \param ncol The number of columns in the matrix. + * \return Pointer to the matrix object, the same as the \p m parameter, for + * convenience. + * + * Time complexity: O(1). + */ + +const TYPE(igraph_matrix)* FUNCTION(igraph_matrix, view)( + const TYPE(igraph_matrix) *m, const BASE *data, + igraph_integer_t nrow, igraph_integer_t ncol) { + /* temporarily cast away the constness */ + TYPE(igraph_matrix) *m2 = (TYPE(igraph_matrix)*)m; + + /* No overflow checking, as this function does not return igraph_error_t. + * It is the caller's resposibility to ensure that the size of 'data' + * matches nrow*ncol, which also implies that nrow*ncol does not overflow. */ + + FUNCTION(igraph_vector, view)(&m2->data, data, ncol * nrow); + m2->nrow = nrow; + m2->ncol = ncol; + + return m; +} + +/** + * \ingroup matrix + * \function igraph_matrix_view_from_vector + * \brief Creates a matrix view that treats an existing vector as a matrix. + * + * + * This function lets you treat an existing igraph vector as a matrix. The + * elements of the matrix are assumed to be stored in column-major order in the + * vector, i.e. the elements of the first column are stored first, followed by + * the second column and so on. + * + * + * Since this function creates a view into an existing vector, you must \em not + * destroy the \c igraph_matrix_t object when you are done with it. Similarly, + * you must \em not call any function on it that may attempt to modify the size + * of the vector. Modifying an element in the matrix will modify the underlying + * vector as the two share the same memory block. + * + * + * Additionally, you must \em not attempt to grow the underlying vector by any + * vector operation as that may result in a re-allocation of the backing memory + * block of the vector, and the matrix view will not be informed about the + * re-allocation so it will point to an invalid memory area afterwards. + * + * \param m Pointer to a not yet initialized matrix object where the view will + * be created. + * \param v The vector that the matrix will provide a view into. + * \param nrow The number of rows in the matrix. The number of columns will be + * derived implicitly from the size of the vector. If the number of + * items in the vector is not divisible by the number of rows, the + * last few elements of the vector will not be covered by the view. + * \return Error code. + * + * Time complexity: O(1). + */ + +const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view_from_vector)( + const TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, + igraph_integer_t nrow +) { + /* temporarily cast away the constness */ + TYPE(igraph_matrix) *m2 = (TYPE(igraph_matrix)*)m; + + igraph_integer_t size = FUNCTION(igraph_vector, size)(v); + igraph_integer_t ncol = nrow > 0 ? size / nrow : 0; + + FUNCTION(igraph_vector, view)(&m2->data, VECTOR(*v), ncol * nrow); + 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). + */ + +igraph_integer_t 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. + */ + +igraph_error_t FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol) { + igraph_integer_t size; + IGRAPH_ASSERT(nrow >= 0 && ncol >= 0); + IGRAPH_SAFE_MULT(nrow, ncol, &size); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, size)); + m->nrow = nrow; + m->ncol = ncol; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup matrix + * \function igraph_matrix_resize_min + * \brief Deallocates unused memory for a matrix. + * + * This function attempts to deallocate the unused reserved storage + * of a matrix. + * + * \param m Pointer to an initialized matrix. + * + * \sa \ref igraph_matrix_resize(). + * + * Time complexity: operating system dependent, O(n) at worst. + */ + +void FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m) { + FUNCTION(igraph_vector, resize_min)(&m->data); +} + + +/** + * \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). + */ + +igraph_integer_t 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). + */ + +igraph_integer_t 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). + */ + +igraph_integer_t 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. + */ + +igraph_error_t FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, igraph_integer_t n) { + igraph_integer_t new_ncol; + IGRAPH_SAFE_ADD(m->ncol, n, &new_ncol); + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow, new_ncol)); + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, igraph_integer_t n) { + igraph_integer_t new_nrow, new_size; + IGRAPH_SAFE_ADD(m->nrow, n, &new_nrow); + IGRAPH_SAFE_MULT(m->ncol, new_nrow, &new_size); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, new_size)); + for (igraph_integer_t i = m->ncol - 1; i >= 0; i--) { + FUNCTION(igraph_vector, move_interval)(&m->data, (m->nrow)*i, (m->nrow) * (i + 1), + new_nrow * i); + } + m->nrow = new_nrow; + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, igraph_integer_t col) { + FUNCTION(igraph_vector, remove_section)(&m->data, (m->nrow)*col, (m->nrow) * (col + 1)); + m->ncol--; + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, permdelete_rows)( + TYPE(igraph_matrix) *m, igraph_integer_t *index, igraph_integer_t nremove) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * Copies matrix data stored contiguously while transposing. Applications include implementing + * matrix transposition as well as changing between row-major and column-major storage formats. + * + * \param dst Data will be copied into this vector. It must have length m-by-n. It will not be resized. + * \param src Vector containing the data to be copied. It is assumed to have length m-by-n. + * Must not be the same as \p dst . + * \param m The size of contiguous blocks. This is the number of columns when using column-major + * storage format, or the number of rows when using row-major format. + * \param n The number of blocks. The is the number of rows when using column-major format, + * or the number of column when using row-major format. + */ +static void FUNCTION(igraph_i, transpose_copy)( + TYPE(igraph_vector) *dst, const TYPE(igraph_vector) *src, + size_t m, size_t n) { + + IGRAPH_ASSERT(dst != src); + + /* Block size of 4 was found to yield the best performance when benchmarking with: + * - Intel Core i7-7920HQ on macOS. + * - AMD Ryzen Threadripper 3990X on Linux. + */ + const size_t blocksize = 4; + for (size_t i=0; i < m; i += blocksize) { + for (size_t j=0; j < n; j++) { + for (size_t k=0; k < blocksize && i+k < m; k++) { + VECTOR(*dst)[j + (i+k)*n] = VECTOR(*src)[i+k + j*m]; + } + } + } +} + +/** + * \ingroup matrix + * \function igraph_matrix_init_array + * \brief Initializes a matrix from an ordinary C array (constructor). + * + * The array is assumed to store the matrix data contiguously, either in + * a column-major or row-major format. In other words, \p data may + * store concatenated matrix columns or concatenated matrix rows. + * Constructing a matrix from column-major data is faster, as this is + * igraph's native storage format. + * + * \param v Pointer to an uninitialized matrix object. + * \param data A regular C array, storing the elements of the matrix in + * column-major order, i.e. the elements of the first column are stored + * first, followed by the second column and so on. + * \param nrow The number of rows in the matrix. + * \param ncol The number of columns in the matrix. + * \param storage \c IGRAPH_ROW_MAJOR if the array is in row-major format, + \c IGRAPH_COLUMN_MAJOR if the array is in column-major format. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system specific, usually + * O(\p nrow \p ncol). + */ + +igraph_error_t FUNCTION(igraph_matrix, init_array)( + TYPE(igraph_matrix) *m, const BASE *data, + igraph_integer_t nrow, igraph_integer_t ncol, + igraph_matrix_storage_t storage) { + igraph_integer_t length; + TYPE(igraph_vector) v; + + IGRAPH_SAFE_MULT(nrow, ncol, &length); + IGRAPH_CHECK(FUNCTION(igraph_matrix, init)(m, nrow, ncol)); + FUNCTION(igraph_vector, view)(&v, data, length); + if (storage == IGRAPH_COLUMN_MAJOR) { + IGRAPH_CHECK(FUNCTION(igraph_vector, update)(&m->data, &v)); + } else if (storage == IGRAPH_ROW_MAJOR) { + FUNCTION(igraph_i, transpose_copy)(&m->data, &v, ncol, nrow); + } else { + IGRAPH_ERROR("Invalid storage type argument", IGRAPH_EINVAL); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup matrix + * \function igraph_matrix_init_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. + */ + +igraph_error_t FUNCTION(igraph_matrix, init_copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { + IGRAPH_CHECK(FUNCTION(igraph_vector, init_copy)(&to->data, &from->data)); + to->nrow = from->nrow; + to->ncol = from->ncol; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup matrix + * \function igraph_matrix_copy + * \brief Copies a matrix (deprecated alias). + * + * \deprecated-by igraph_matrix_init_copy 0.10 + */ + +igraph_error_t FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { + return FUNCTION(igraph_matrix, init_copy)(to, from); +} + +#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. + */ + +igraph_error_t FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_int_t *rows) { + igraph_integer_t norows = igraph_vector_int_size(rows); + igraph_integer_t 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, VECTOR(*rows)[i], j); + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_int_t *rows, + const igraph_vector_int_t *cols) { + igraph_integer_t nrows = igraph_vector_int_size(rows); + igraph_integer_t ncols = igraph_vector_int_size(cols); + igraph_integer_t 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, VECTOR(*rows)[i], VECTOR(*cols)[j]); + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, + igraph_integer_t index) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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? + * + * Checks element-wise equality of two matrices. For matrices containing floating + * point values, consider using \ref igraph_matrix_all_almost_e(). + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \return True if the elements in the \p lhs are all + * equal to the corresponding elements in \p rhs. Returns 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 True if the elements in the \p lhs are all + * less than the corresponding elements in \p rhs. Returns 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 True if the elements in the \p lhs are all + * greater than the corresponding elements in \p rhs. Returns 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 True if the elements in the \p lhs are all + * less than or equal to the corresponding elements in \p + * rhs. Returns 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 True if the elements in the \p lhs are all + * greater than or equal to the corresponding elements in \p + * rhs. Returns 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) { + if (m1->ncol != m2->ncol || m1->nrow != m2->nrow) { + IGRAPH_WARNING("Comparing non-conformant matrices."); + } + return FUNCTION(igraph_vector, maxdifference)(&m1->data, &m2->data); +} + +#endif + +#define SWAP(TYPE,a,b) do { TYPE igraph_i_tmp = (a); (a) = (b); (b) = igraph_i_tmp; } while (0) + +/** + * \function igraph_matrix_transpose + * \brief Transpose of a matrix. + * + * Calculates the transpose of a matrix. When the matrix is non-square, + * this function reallocates the storage 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. + */ + +igraph_error_t FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m) { + if (m->nrow > 1 && m->ncol > 1) { + if (m->nrow == m->ncol) { + /* In-place transpose for square matrices. */ + + /* Block size of 4 was found to yield the best performance during benchmarking, + * see igraph_i_transpose_copy() */ + const size_t blocksize = 4; + const size_t n = m->nrow; + size_t k=0; + for (k=0; k + blocksize - 1 < n; k += blocksize) { + for (size_t i = k; i < k + blocksize; ++i) { + for (size_t j = i + 1; j < k + blocksize; ++j) { + SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); + } + } + for (size_t i = k + blocksize; i < n; ++i) { + for (size_t j = k; j < k + blocksize; ++j) { + SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); + } + } + } + for (size_t i = k; i < n; ++i) { + for (size_t j = i + 1; j < n; ++j) { + SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); + } + } + } else { + /* Allocate new storage for non-square matrices. */ + TYPE(igraph_vector) newdata; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&newdata, m->nrow * m->ncol)); + FUNCTION(igraph_i, transpose_copy)(&newdata, &m->data, m->nrow, m->ncol); + FUNCTION(igraph_vector, destroy)(&m->data); + m->data = newdata; + } + } + + SWAP(igraph_integer_t, m->nrow, m->ncol); + + return IGRAPH_SUCCESS; +} + +#undef SWAP + +/** + * \function igraph_matrix_get + * 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, get)(const TYPE(igraph_matrix) *m, + igraph_integer_t row, igraph_integer_t col) { + return MATRIX(*m, row, col); +} + +/** + * \function igraph_matrix_get_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, get_ptr)(const TYPE(igraph_matrix) *m, + igraph_integer_t row, igraph_integer_t col) { + return &MATRIX(*m, row, col); +} + +/** + * \function igraph_matrix_e + * Extract an element from a matrix (deprecated alias). + * + * \deprecated-by igraph_matrix_get 0.10.0 + */ + +BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, + igraph_integer_t row, igraph_integer_t col) { + return FUNCTION(igraph_matrix, get)(m, row, col); +} + +/** + * \function igraph_matrix_e_ptr + * Pointer to an element of a matrix. + * + * \deprecated-by igraph_matrix_get_ptr 0.10.0 + */ + +BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, + igraph_integer_t row, igraph_integer_t col) { + return FUNCTION(igraph_matrix, get_ptr)(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, igraph_integer_t row, igraph_integer_t 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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from) { + igraph_integer_t tocols = to->ncol, fromcols = from->ncol; + igraph_integer_t torows = to->nrow, fromrows = from->nrow; + igraph_integer_t offset, c, r, index, offset2; + if (tocols != fromcols) { + IGRAPH_ERROR("Cannot do rbind, number of columns do not match", IGRAPH_EINVAL); + } + + igraph_integer_t new_size; /* new_size = tocols * (fromrows + torows) */ + IGRAPH_SAFE_ADD(fromrows, torows, &new_size); + IGRAPH_SAFE_MULT(tocols, new_size, &new_size); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&to->data, new_size)); + 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) * fromrows); + offset += fromrows + torows; + offset2 += fromrows; + } + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from) { + + igraph_integer_t tocols = to->ncol, fromcols = from->ncol; + igraph_integer_t torows = to->nrow, fromrows = from->nrow; + if (torows != fromrows) { + IGRAPH_ERROR("Cannot do rbind, number of rows do not match", IGRAPH_EINVAL); + } + + igraph_integer_t new_tocols; + IGRAPH_SAFE_ADD(tocols, fromcols, &new_tocols); + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(to, torows, new_tocols)); + + FUNCTION(igraph_vector, copy_to)(&from->data, VECTOR(to->data) + tocols * torows); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_matrix_swap + * \brief 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). + */ + +igraph_error_t FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2) { + igraph_integer_t tmp; + + tmp = m1->nrow; + m1->nrow = m2->nrow; + m2->nrow = tmp; + + tmp = m1->ncol; + m1->ncol = m2->ncol; + m2->ncol = tmp; + + IGRAPH_CHECK(FUNCTION(igraph_vector, swap)(&m1->data, &m2->data)); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, igraph_integer_t index) { + igraph_integer_t rows = m->nrow, cols = m->ncol; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, igraph_integer_t index) { + const igraph_integer_t rows = m->nrow, cols = m->ncol; + + 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 (igraph_integer_t i = index, j = 0; j < cols; i += rows, j++) { + VECTOR(m->data)[i] = VECTOR(*v)[j]; + } + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, igraph_integer_t index) { + const igraph_integer_t rows = m->nrow, cols = m->ncol; + + 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 (igraph_integer_t i = index * rows, j = 0; j < rows; i++, j++) { + VECTOR(m->data)[i] = VECTOR(*v)[j]; + } + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, + igraph_integer_t i, igraph_integer_t j) { + const igraph_integer_t ncol = m->ncol, nrow = m->nrow; + const igraph_integer_t n = nrow * ncol; + + if (i >= nrow || j >= nrow) { + IGRAPH_ERROR("Cannot swap rows, index out of range", IGRAPH_EINVAL); + } + if (i == j) { + return IGRAPH_SUCCESS; + } + for (igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m, + igraph_integer_t i, igraph_integer_t j) { + const igraph_integer_t ncol = m->ncol, nrow = m->nrow; + + if (i >= ncol || j >= ncol) { + IGRAPH_ERROR("Cannot swap columns, index out of range.", IGRAPH_EINVAL); + } + if (i == j) { + return IGRAPH_SUCCESS; + } + for (igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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. + */ + +igraph_error_t 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 + * \brief Elementwise matrix 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. + */ + +igraph_error_t 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 elements of 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. + */ + +igraph_error_t 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 an igraph_integer_t. The row index of the + * minimum is stored here. + * \param j Pointer to an igraph_integer_t. The column index of + * the minimum is stored here. + * + * Time complexity: O(mn), the number of elements. + */ + +void FUNCTION(igraph_matrix, which_min)( + const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j) { + igraph_integer_t vmin = FUNCTION(igraph_vector, which_min)(&m->data); + *i = vmin % m->nrow; + *j = vmin / m->nrow; +} + +/** + * \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 an igraph_integer_t. The row index of the + * maximum is stored here. + * \param j Pointer to an igraph_integer_t. The column index of + * the maximum is stored here. + * + * Time complexity: O(mn), the number of elements. + */ + +void FUNCTION(igraph_matrix, which_max)( + const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j) { + igraph_integer_t vmax = FUNCTION(igraph_vector, which_max)(&m->data); + *i = vmax % m->nrow; + *j = vmax / m->nrow; +} + +/** + * \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. + * + * Time complexity: O(mn), the number of elements. + */ + +void FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m, + BASE *min, BASE *max) { + 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 an igraph_integer_t, the row index of + * the minimum is stored here. + * \param jmin Pointer to an igraph_integer_t, the column index of + * the minimum is stored here. + * \param imax Pointer to an igraph_integer_t, the row index of + * the maximum is stored here. + * \param jmax Pointer to an igraph_integer_t, the column index of + * the maximum is stored here. + * + * Time complexity: O(mn), the number of elements. + */ + +void FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, + igraph_integer_t *imin, igraph_integer_t *jmin, + igraph_integer_t *imax, igraph_integer_t *jmax) { + igraph_integer_t 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; +} + +#endif + +/** + * \function igraph_matrix_isnull + * \brief Checks 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 + * \brief Is the matrix empty? + * + * 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 + * \brief Is the matrix symmetric? + * + * 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) { + + const igraph_integer_t n = m->nrow; + if (m->ncol != n) { + return false; + } + for (igraph_integer_t r = 1; r < n; r++) { + for (igraph_integer_t c = 0; c < r; c++) { + BASE a1 = MATRIX(*m, r, c); + BASE a2 = MATRIX(*m, c, r); +#ifdef EQ + if (!EQ(a1, a2)) { + return false; + } +#else + if (a1 != a2) { + return false; + } +#endif + } + } + return true; +} + +/** + * \function igraph_matrix_prod + * \brief Product of all matrix elements. + * + * Note that this function can result in overflow easily, even for not too + * big matrices. Overflow is not checked. + * + * \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 + * \brief 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. + */ + +igraph_error_t FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res) { + const igraph_integer_t nrow = m->nrow, ncol = m->ncol; + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, nrow)); + for (igraph_integer_t r = 0; r < nrow; r++) { + BASE sum = ZERO; + for (igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_matrix_colsum + * \brief 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. + */ + +igraph_error_t FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res) { + const igraph_integer_t nrow = m->nrow, ncol = m->ncol; + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, ncol)); + for (igraph_integer_t c = 0; c < ncol; c++) { + BASE sum = ZERO; + for (igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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 an igraph_integer_t. If the element is + * found, then this is set to the position of its first appearance. + * \param row Pointer to an igraph_integer_t. If the element is + * found, then this is set to its row index. + * \param col Pointer to an igraph_integer_t. 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, + igraph_integer_t from, BASE what, igraph_integer_t *pos, + igraph_integer_t *row, igraph_integer_t *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. + */ + +igraph_error_t FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, igraph_integer_t row) { + + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_int_t *cols) { + igraph_integer_t ncols = igraph_vector_int_size(cols); + igraph_integer_t nrows = m->nrow; + igraph_integer_t 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, VECTOR(*cols)[j]); + } + } + return IGRAPH_SUCCESS; +} + +#ifdef OUT_FORMAT +#ifndef USING_R +igraph_error_t FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, + const char *format) { + igraph_integer_t nr = FUNCTION(igraph_matrix, nrow)(m); + igraph_integer_t nc = FUNCTION(igraph_matrix, ncol)(m); + igraph_integer_t 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 IGRAPH_SUCCESS; +} +#endif /* USING_R */ +#endif /* OUT_FORMAT */ + +#if defined(OUT_FORMAT) || defined(FPRINTFUNC) + +#ifndef USING_R + +igraph_error_t FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_matrix, fprint)(m, stdout); +} + +#endif /* USING_R */ + +igraph_error_t FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, FILE *file) { + igraph_integer_t nr = FUNCTION(igraph_matrix, nrow)(m); + igraph_integer_t nc = FUNCTION(igraph_matrix, ncol)(m); + igraph_integer_t i, j; + igraph_vector_int_t column_width; + +#ifdef OUT_FORMAT + /* Insert dynamic width specifier '*' in format string. */ + char format[ sizeof(OUT_FORMAT) + 1 ] = "%*"; + strcpy(format + 2, (const char *) OUT_FORMAT + 1); +#endif + + IGRAPH_VECTOR_INT_INIT_FINALLY(&column_width, nc); + + /* First we detect the width needed for each matrix column. + * snprintf() may be passed a NULL pointer with a buffer size of 0. + * It will then return the number of characters that would have been written, + * without writing anything. */ + for (j = 0; j < nc; j++) { + for (i = 0; i < nr; i++) { + const int min_width = 1; /* minimum field width */ + int width; +#ifdef SNPRINTFUNC + width = SNPRINTFUNC(NULL, 0, MATRIX(*m, i, j)); +#else + width = snprintf(NULL, 0, OUT_FORMAT, MATRIX(*m, i, j)); +#endif + if (width < min_width) { + width = min_width; + } + if (width > VECTOR(column_width)[j]) { + VECTOR(column_width)[j] = width; + } + } + } + + for (i = 0; i < nr; i++) { + for (j = 0; j < nc; j++) { + if (j != 0) { + fputc(' ', file); + } +#ifdef FPRINTFUNC_ALIGNED + FPRINTFUNC_ALIGNED(file, (int) VECTOR(column_width)[j], MATRIX(*m, i, j)); +#else + fprintf(file, format, (int) VECTOR(column_width)[j], MATRIX(*m, i, j)); +#endif + } + fprintf(file, "\n"); + } + + igraph_vector_int_destroy(&column_width); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ diff --git a/src/core/matrix_list.c b/src/core/matrix_list.c new file mode 100644 index 0000000..f00e5d7 --- /dev/null +++ b/src/core/matrix_list.c @@ -0,0 +1,54 @@ +/* -*- 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 + +*/ + +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_matrix_list.h" + +#define MATRIX_LIST + +#define BASE_IGRAPH_REAL +#define CUSTOM_INIT_DESTROY +#include "igraph_pmt.h" +#include "typed_list.pmt" +#include "igraph_pmt_off.h" +#undef CUSTOM_INIT_DESTROY +#undef BASE_IGRAPH_REAL + +static igraph_error_t igraph_i_matrix_list_init_item( + const igraph_matrix_list_t* list, igraph_matrix_t* item +) { + IGRAPH_UNUSED(list); + return igraph_matrix_init(item, 0, 0); +} + +static igraph_error_t igraph_i_matrix_list_copy_item( + igraph_matrix_t* dest, const igraph_matrix_t* source +) { + return igraph_matrix_init_copy(dest, source); +} + +static void igraph_i_matrix_list_destroy_item(igraph_matrix_t* item) { + igraph_matrix_destroy(item); +} + +#undef MATRIX_LIST diff --git a/src/core/memory.c b/src/core/memory.c new file mode 100644 index 0000000..490db99 --- /dev/null +++ b/src/core/memory.c @@ -0,0 +1,124 @@ +/* -*- 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" + +/** + * \section about-alloc-funcs About allocation 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. \c igraph_vector_ptr_t + * has functions to deallocate the contained pointers on its own, but in this + * case it has to be ensured that these pointers are allocated by a function + * that corresponding to the deallocator function that igraph uses. + * + * + * To this end, igraph exports the memory allocation functions that are used + * internally so the user of the library can ensure that the proper functions + * are used when pointers are moved between the code written by the user and + * the code of the igraph library. + * + * + * Additionally, the memory allocator functions used by igraph work around the + * quirk of classical \c malloc(), \c realloc() and \c calloc() implementations + * where the behaviour of allocating zero bytes is undefined. igraph allocator + * functions will always allocate at least one byte. + */ + +/** + * \function igraph_free + * \brief Deallocate memory that was allocated by igraph functions. + * + * This function exposes the \c free() function used internally by igraph. + * + * \param ptr Pointer to the piece of memory to be deallocated. + * + * Time complexity: platform dependent, ideally it should be O(1). + * + * \sa \ref igraph_calloc(), \ref igraph_malloc(), \ref igraph_realloc() + */ + +void igraph_free(void *ptr) { + IGRAPH_FREE(ptr); +} + + +/** + * \function igraph_calloc + * \brief Allocate memory that can be safely deallocated by igraph functions. + * + * This function behaves like \c calloc(), but it ensures that at least one + * byte is allocated even when the caller asks for zero bytes. + * + * \param count Number of items to be allocated. + * \param size Size of a single item to be allocated. + * \return Pointer to the piece of allocated memory; \c NULL if the allocation + * failed. + * + * \sa \ref igraph_malloc(), \ref igraph_realloc(), \ref igraph_free() + */ + +void *igraph_calloc(size_t count, size_t size) { + return (void *) IGRAPH_CALLOC(count * size, char); +} + + +/** + * \function igraph_malloc + * \brief Allocate memory that can be safely deallocated by igraph functions. + * + * This function behaves like \c malloc(), but it ensures that at least one + * byte is allocated even when the caller asks for zero bytes. + * + * \param size Number of bytes to be allocated. Zero is treated as one byte. + * \return Pointer to the piece of allocated memory; \c NULL if the allocation + * failed. + * + * \sa \ref igraph_calloc(), \ref igraph_realloc(), \ref igraph_free() + */ + +void *igraph_malloc(size_t size) { + return IGRAPH_MALLOC(size); +} + + +/** + * \function igraph_realloc + * \brief Reallocate memory that can be safely deallocated by igraph functions. + * + * This function behaves like \c realloc(), but it ensures that at least one + * byte is allocated even when the caller asks for zero bytes. + * + * \param ptr The pointer to reallocate. + * \param size Number of bytes to be allocated. + * \return Pointer to the piece of allocated memory; \c NULL if the allocation + * failed. + * + * \sa \ref igraph_free(), \ref igraph_malloc() + */ + +void *igraph_realloc(void* ptr, size_t size) { + return (void*) IGRAPH_REALLOC(ptr, size, char); +} diff --git a/src/core/printing.c b/src/core/printing.c new file mode 100644 index 0000000..85635f5 --- /dev/null +++ b/src/core/printing.c @@ -0,0 +1,231 @@ +/* -*- 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_complex.h" +#include "igraph_error.h" +#include "igraph_types.h" + +#include + +/* The number of digits chosen here will be used in all places where + * igraph_real_fprintf_precise() is used, including all textual graph + * formats such as GML, GraphML, Pajek, etc. DBL_DIG digits are sufficient + * to preserve the decimal representation during a + * decimal (textual) -> binary -> decimal (textual) round-trip conversion. + * This many digits are however not sufficient for a lossless + * binary -> decimal -> binary conversion. Thus, writing numerical attributes + * to a file and reading them back in may cause a tiny change in the last + * binary digit of numbers. This change is minute, always smaller than 10^-15 + * times the original number, thus acceptable. + * + * We could output more digits, but that would come with its own problem: + * It would sometimes cause a change in the decimal representation originally + * input by users, which is surprising and confusing. For example, + * + * printf("%.17g\n", 100.1) + * + * outputs 100.09999999999999 instead of 100.1. We can prevent this by + * using DBL_DIG == 15 digits instead of 17, which would be required + * for a lossless binary -> decimal -> binary round-tripping. + * + * This justifies using DBL_DIG digits, and not more, in all places. + */ +#ifdef DBL_DIG + /* Use DBL_DIG to determine the maximum precision used for %g */ + #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%." IGRAPH_I_STRINGIFY(DBL_DIG) "g" +#else + /* Assume a precision of 15 digits for %g, which is what IEEE-754 doubles require. */ + #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%.15g" +#endif + +int igraph_real_fprintf(FILE *file, igraph_real_t val) { + if (isfinite(val)) { + return fprintf(file, "%g", val); + } else if (isnan(val)) { + return fprintf(file, "NaN"); + } else if (isinf(val)) { + if (val < 0) { + return fprintf(file, "-Inf"); + } else { + return fprintf(file, "Inf"); + } + } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ +} + +#ifndef USING_R +int igraph_real_printf(igraph_real_t val) { + return igraph_real_fprintf(stdout, val); +} +#endif + +int igraph_real_fprintf_aligned(FILE *file, int width, igraph_real_t val) { + if (isfinite(val)) { + return fprintf(file, "%*g", width, val); + } else if (isnan(val)) { + return fprintf(file, "%*s", width, "NaN"); + } else if (isinf(val)) { + if (val < 0) { + return fprintf(file, "%*s", width, "-Inf"); + } else { + return fprintf(file, "%*s", width, "Inf"); + } + } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ +} + +#ifndef USING_R +int igraph_real_printf_aligned(int width, igraph_real_t val) { + return igraph_real_fprintf_aligned(stdout, width, val); +} +#endif + +int igraph_real_snprintf(char *str, size_t size, igraph_real_t val) { + if (isfinite(val)) { + return snprintf(str, size, "%g", val); + } else if (isnan(val)) { + return snprintf(str, size, "NaN"); + } else if (isinf(val)) { + if (val < 0) { + return snprintf(str, size, "-Inf"); + } else { + return snprintf(str, size, "Inf"); + } + } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ +} + +int igraph_real_fprintf_precise(FILE *file, igraph_real_t val) { + if (isfinite(val)) { + return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); + } else if (isnan(val)) { + return fprintf(file, "NaN"); + } else if (isinf(val)) { + if (val < 0) { + return fprintf(file, "-Inf"); + } else { + return fprintf(file, "Inf"); + } + } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ +} + +#ifndef USING_R +int igraph_real_printf_precise(igraph_real_t val) { + return igraph_real_fprintf_precise(stdout, val); +} +#endif + +int igraph_real_snprintf_precise(char *str, size_t size, igraph_real_t val) { + if (isfinite(val)) { + return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); + } else if (isnan(val)) { + return snprintf(str, size, "NaN"); + } else if (isinf(val)) { + if (val < 0) { + return snprintf(str, size, "-Inf"); + } else { + return snprintf(str, size, "Inf"); + } + } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ +} + +#define PROPAGATE() \ + do { \ + if (res < 0) { \ + return -1; \ + } \ + cnt += res; \ + } while (0) + +int igraph_complex_fprintf(FILE *file, igraph_complex_t val) { + int res, cnt = 0; + igraph_real_t re = IGRAPH_REAL(val), im = IGRAPH_IMAG(val); + res = igraph_real_fprintf(file, re); + PROPAGATE(); + if (! signbit(im)) { + res = fprintf(file, "+"); + PROPAGATE(); + } + res = igraph_real_fprintf(file, im); + PROPAGATE(); + res = fprintf(file, "i"); + PROPAGATE(); + return cnt; +} + +#undef PROPAGATE + +#ifndef USING_R +int igraph_complex_printf(igraph_complex_t val) { + return igraph_complex_fprintf(stdout, val); +} +#endif + +#define PROPAGATE() \ + do { \ + if (res < 0) { \ + return -1; \ + } \ + cnt += res; \ + /* remember that 'size' is unsigned, can't check if size - res < 0! */ \ + if (size > res) size -= res; \ + else size = 0; \ + if (size == 0) str = NULL; else str += res; \ + } while (0) + +int igraph_complex_snprintf(char *str, size_t size, igraph_complex_t val) { + int res, cnt = 0; + igraph_real_t re = IGRAPH_REAL(val), im = IGRAPH_IMAG(val); + res = igraph_real_snprintf(str, size, re); + PROPAGATE(); + if (! signbit(im)) { + res = snprintf(str, size, "+"); + PROPAGATE(); + } + res = igraph_real_snprintf(str, size, im); + PROPAGATE(); + res = snprintf(str, size, "i"); + PROPAGATE(); + return cnt; +} + +int igraph_complex_fprintf_aligned(FILE *file, int width, igraph_complex_t val) { + /* Most characters produces by %g is 13, so including 'i' and null terminator we + * need up to 13 + 13 + 1 + 1 = 28 characters in total. */ + char buf[28]; + + if (igraph_complex_snprintf(buf, sizeof(buf) / sizeof(buf[0]), val) < 0) { + return -1; + } + return fprintf(file, "%*s", width, buf); +} + +#ifndef USING_R +int igraph_complex_printf_aligned(int width, igraph_complex_t val) { + return igraph_complex_fprintf_aligned(stdout, width, val); +} +#endif + +#undef PROPAGATE diff --git a/src/core/progress.c b/src/core/progress.c new file mode 100644 index 0000000..8cf35a8 --- /dev/null +++ b/src/core/progress.c @@ -0,0 +1,155 @@ +/* -*- 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). + */ + +igraph_error_t 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 + */ + +igraph_error_t 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); + va_end(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). + */ + +igraph_error_t 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/core/psumtree.c b/src/core/psumtree.c new file mode 100644 index 0000000..f0bfc73 --- /dev/null +++ b/src/core/psumtree.c @@ -0,0 +1,268 @@ +/* -*- 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 "math/safe_intop.h" + +#include + +/** + * \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. It must be at least one. + * \return Error code, typically \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: O(n) for a tree containing n elements + */ +igraph_error_t igraph_psumtree_init(igraph_psumtree_t *t, igraph_integer_t size) { + igraph_integer_t vecsize; + + IGRAPH_ASSERT(size > 0); + + t->size = size; + + /* offset = 2^ceiling(log2(size)) - 1 */ + IGRAPH_CHECK(igraph_i_safe_next_pow_2(size, &t->offset)); + t->offset -= 1; + + IGRAPH_SAFE_ADD(t->offset, t->size, &vecsize); + IGRAPH_CHECK(igraph_vector_init(&t->v, vecsize)); + + return IGRAPH_SUCCESS; +} + +/** + * \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_null(&t->v); +} + +/** + * \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, igraph_integer_t 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. Must be in the interval + * [0, sum), where \c sum is the sum of all elements + * (leaves) in the tree. + * \return Error code; currently the search always succeeds. + * + * Time complexity: O(log n), where n is the number of items in the tree. + */ +igraph_error_t igraph_psumtree_search(const igraph_psumtree_t *t, igraph_integer_t *idx, + igraph_real_t search) { + const igraph_vector_t *tree = &t->v; + igraph_integer_t i = 1; + igraph_integer_t size = igraph_vector_size(tree); + + IGRAPH_ASSERT(search >= 0); + IGRAPH_ASSERT(search < igraph_psumtree_sum(t)); + + 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. + */ +igraph_error_t igraph_psumtree_update(igraph_psumtree_t *t, igraph_integer_t idx, + igraph_real_t new_value) { + const igraph_vector_t *tree = &t->v; + igraph_real_t difference; + + if (new_value >= 0 && isfinite(new_value)) { + 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, infinity and NaN. */ + IGRAPH_ERRORF("Trying to use negative or non-finite weight (%g) when " + "sampling from discrete distribution using prefix sum trees.", + IGRAPH_EINVAL, new_value); + } +} + +/** + * \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). + */ +igraph_integer_t 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/core/set.c b/src/core/set.c new file mode 100644 index 0000000..5ee8667 --- /dev/null +++ b/src/core/set.c @@ -0,0 +1,326 @@ +/* -*- 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_memory.h" + +#include "core/set.h" + +#include /* memmove */ + +#define SET(s) ((s).stor_begin) + +/** + * \ingroup set + * \function igraph_set_init + * \brief Initializes a set. + * + * Initializes an empty set (with zero elements). Allocates memory for + * the requested capacity. No re-allocation will be necessary until the + * number of elements exceeds this initial capacity. + * + * \param set Pointer to the set to be initialized. + * \param capacity 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. + */ +igraph_error_t igraph_set_init(igraph_set_t *set, igraph_integer_t capacity) { + igraph_integer_t alloc_size; + + IGRAPH_ASSERT(capacity >= 0); + alloc_size = capacity > 0 ? capacity : 1; + set->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + if (! set->stor_begin) { + IGRAPH_ERROR("Cannot initialize set.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + set->stor_end = set->stor_begin + alloc_size; + set->end = set->stor_begin; + + return IGRAPH_SUCCESS; +} + +/** + * \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 != NULL); + if (set->stor_begin != NULL) { + IGRAPH_FREE(set->stor_begin); /* sets to 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 != NULL); +} + +/** + * \ingroup set + * \function igraph_set_reserve + * \brief Reserves memory for a set. + * + * \param set The set object. + * \param capacity the new \em allocated capacity of the set. + * + * Time complexity: operating system dependent, should be around + * O(n), n is the new allocated size of the set. + */ +igraph_error_t igraph_set_reserve(igraph_set_t* set, igraph_integer_t capacity) { + igraph_integer_t actual_size = igraph_set_size(set); + igraph_integer_t *tmp; + IGRAPH_ASSERT(set != NULL); + IGRAPH_ASSERT(set->stor_begin != NULL); + if (capacity <= actual_size) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(set->stor_begin, capacity, igraph_integer_t); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for set."); + + set->stor_begin = tmp; + set->stor_end = set->stor_begin + capacity; + set->end = set->stor_begin + actual_size; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup set + * \function igraph_set_empty + * \brief Decides whether the size of the set is zero. + * + * \param set The set object. + * \return True if the size of the set is not zero and false 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 the 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 set 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 of the set. + * + * The number of elements in the set. + * + * \param set The set object + * \return The size of the set. + * + * Time complexity: O(1). + */ + +igraph_integer_t 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. + */ +igraph_error_t igraph_set_add(igraph_set_t* set, igraph_integer_t e) { + igraph_integer_t left, right, middle; + igraph_integer_t 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) { + igraph_integer_t new_size = size < IGRAPH_INTEGER_MAX/2 ? size * 2 : IGRAPH_INTEGER_MAX; + if (size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot add to set, already at maximum size.", IGRAPH_EOVERFLOW); + } + 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 - left) * sizeof(set->stor_begin[0])); + + set->stor_begin[left] = e; + set->end += 1; + } + + return IGRAPH_SUCCESS; +} + +/** + * \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 True if \p e is found, false otherwise. + * + * Time complexity: O(log(n)), n is the number of elements in \p set. + */ +igraph_bool_t igraph_set_contains(const igraph_set_t* set, igraph_integer_t e) { + igraph_integer_t 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 false; /* 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 true; + } + } + + return SET(*set)[left] == e || SET(*set)[right] == e; +} + +/** + * \ingroup set + * \function igraph_set_iterate + * \brief Iterates through the element of 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 an \c igraph_integer_t variable + * which must be zero for the first invocation. + * The object must not be adjusted and its value should + * not be used for anything during the iteration. + * \param element The next element or 0 (if the iteration + * has ended) is returned here. + * + * \return True if there are more elements, false otherwise. + */ +igraph_bool_t igraph_set_iterate(const igraph_set_t *set, igraph_integer_t *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 true; + } else { + *element = 0; + return false; + } +} diff --git a/src/core/set.h b/src/core/set.h new file mode 100644 index 0000000..8f5b3e3 --- /dev/null +++ b/src/core/set.h @@ -0,0 +1,66 @@ +/* -*- 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_error.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 igraph_error_t igraph_set_init(igraph_set_t* set, igraph_integer_t size); +IGRAPH_PRIVATE_EXPORT void igraph_set_destroy(igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_set_inited(igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_set_reserve(igraph_set_t* set, igraph_integer_t size); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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 IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_set_size(const igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_set_add(igraph_set_t* v, igraph_integer_t e); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_set_contains(const igraph_set_t *set, igraph_integer_t e); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_iterate(const igraph_set_t *set, igraph_integer_t* state, + igraph_integer_t* element); + +__END_DECLS + +#endif diff --git a/src/core/sparsemat.c b/src/core/sparsemat.c new file mode 100644 index 0000000..524c109 --- /dev/null +++ b/src/core/sparsemat.c @@ -0,0 +1,3561 @@ +/* -*- 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_sparsemat.h" + +#include "igraph_attributes.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_types.h" +#include "igraph_vector_ptr.h" + +#include "internal/hacks.h" /* IGRAPH_STATIC_ASSERT */ + +#include +#include + +#include +#undef cs /* because otherwise it messes up the name of the 'cs' member in igraph_sparsemat_t */ + +/* Returns the number of potential nonzero elements in the given sparse matrix. + * The returned value can be used to iterate over A->cs->x no matter whether the + * matrix is in triplet or column-compressed form */ +static CS_INT igraph_i_sparsemat_count_elements(const igraph_sparsemat_t* A) { + return A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_init(igraph_sparsemat_t *A, igraph_integer_t rows, + igraph_integer_t cols, igraph_integer_t nzmax) { + IGRAPH_STATIC_ASSERT(sizeof(igraph_integer_t) == sizeof(CS_INT)); + IGRAPH_STATIC_ASSERT(sizeof(igraph_real_t) == sizeof(CS_ENTRY)); + + 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); /* LCOV_EXCL_LINE */ + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_init_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. + */ + +igraph_error_t igraph_sparsemat_init_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(CS_INT) * (size_t) ne); + memcpy(to->cs->i, from->cs->i, sizeof(CS_INT) * (size_t) (from->cs->nzmax)); + memcpy(to->cs->x, from->cs->x, sizeof(CS_ENTRY) * (size_t) (from->cs->nzmax)); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_copy + * \brief Copies a sparse matrix (deprecated alias). + * + * \deprecated-by igraph_sparsemat_init_copy 0.10 + */ + +igraph_error_t igraph_sparsemat_copy( + igraph_sparsemat_t *to, const igraph_sparsemat_t *from +) { + return igraph_sparsemat_init_copy(to, from); +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_realloc(igraph_sparsemat_t *A, igraph_integer_t nzmax) { + if (!cs_sprealloc(A->cs, nzmax)) { + IGRAPH_ERROR("Could not allocate more memory for sparse matrix.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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). + */ + +igraph_integer_t 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). + */ + +igraph_integer_t 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 igraph_sparsemat_is_cc(A) ? 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. + */ + +igraph_error_t 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; + CS_INT* 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 */ + pinv = IGRAPH_CALLOC(nrow, CS_INT); + if (pinv == 0) { + IGRAPH_ERROR("Cannot allocate index vector for permutation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, pinv); + for (i = 0; i < nrow; i++) { + pinv[ VECTOR(*p)[i] ] = i; + } + + /* And call the permutation routine */ + res->cs = cs_permute(A->cs, pinv, (const CS_INT*) VECTOR(*q), /*values=*/ 1); + if (!res->cs) { + IGRAPH_ERROR("Cannot index sparse matrix", IGRAPH_FAILURE); + } + + IGRAPH_FREE(pinv); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t idx_rows = igraph_vector_int_size(p); + igraph_integer_t k; + + /* Create index matrix */ + IGRAPH_CHECK(igraph_sparsemat_init(&II2, idx_rows, nrow, idx_rows)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2); + for (k = 0; k < idx_rows; k++) { + IGRAPH_CHECK(igraph_sparsemat_entry(&II2, 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t idx_cols = igraph_vector_int_size(q); + igraph_integer_t k; + + /* Create index matrix */ + IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, ncol, idx_cols, idx_cols)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2); + for (k = 0; k < idx_cols; k++) { + IGRAPH_CHECK(igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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; + igraph_integer_t idx_rows = p ? igraph_vector_int_size(p) : -1; + igraph_integer_t idx_cols = q ? igraph_vector_int_size(q) : -1; + igraph_integer_t 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, idx_rows, nrow, idx_rows)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2); + for (k = 0; k < idx_rows; k++) { + IGRAPH_CHECK(igraph_sparsemat_entry(&II2, 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, ncol, idx_cols, idx_cols)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2); + for (k = 0; k < idx_cols; k++) { + IGRAPH_CHECK(igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_entry(igraph_sparsemat_t *A, + igraph_integer_t row, igraph_integer_t 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. + */ + +igraph_error_t 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; +} + +static igraph_real_t igraph_i_sparsemat_get_cc( + const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col +) { + /* elements in column 'col' are at indices + * A->cs->p[col] .. A->cs->p[col+1] (open from right) in + * A->cs->x . + * + * Their corresponding row indices are in A->cs->i . + */ + + CS_INT lo = A->cs->p[col]; + CS_INT hi = A->cs->p[col + 1]; + igraph_real_t result = 0.0; + + /* TODO: this could be faster with binary search if A->cs->i + * is sorted, which I think should be */ + for (; lo < hi; lo++) { + if (A->cs->i[lo] == row) { + result += A->cs->x[lo]; + } + } + + return result; +} + +static igraph_real_t igraph_i_sparsemat_get_triplet( + const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col +) { + igraph_sparsemat_iterator_t it; + igraph_real_t result = 0.0; + + igraph_sparsemat_iterator_init(&it, A); + while (!igraph_sparsemat_iterator_end(&it)) { + if ( + igraph_sparsemat_iterator_row(&it) == row && + igraph_sparsemat_iterator_col(&it) == col + ) { + result += igraph_sparsemat_iterator_get(&it); + } + igraph_sparsemat_iterator_next(&it); + } + + return result; +} + +/** + * \function igraph_sparsemat_get + * \brief Return the value of a single element from a sparse matrix. + * + * \param A The input matrix, in triplet or column-compressed format. + * \param row The row index + * \param col The column index + * \return The value of the cell with the given row and column indices in the + * matrix; zero if the indices are out of bounds. + * + * Time complexity: TODO. + */ +igraph_real_t igraph_sparsemat_get( + const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col +) { + if (row < 0 || col < 0 || row >= A->cs->m || col >= A->cs->n) { + return 0.0; + } else if (igraph_sparsemat_is_cc(A)) { + return igraph_i_sparsemat_get_cc(A, row, col); + } else { + return igraph_i_sparsemat_get_triplet(A, row, col); + } +} + +/** + * \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. + * \return Error code. + * + * Time complexity: TODO. + */ + +igraph_error_t igraph_sparsemat_transpose( + const igraph_sparsemat_t *A, igraph_sparsemat_t *res +) { + + if (igraph_sparsemat_is_cc(A)) { + /* column-compressed */ + res->cs = cs_transpose(A->cs, /* values = */ 1); + if (!res->cs) { + IGRAPH_ERROR("Cannot transpose sparse matrix", IGRAPH_FAILURE); + } + } else { + /* triplets */ + CS_INT *tmp; + IGRAPH_CHECK(igraph_sparsemat_init_copy(res, A)); + tmp = res->cs->p; + res->cs->p = res->cs->i; + res->cs->i = tmp; + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A, igraph_bool_t *result) { + igraph_sparsemat_t t, tt; + igraph_bool_t res; + igraph_integer_t nz; + + IGRAPH_CHECK(igraph_sparsemat_transpose(A, &t)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &t); + IGRAPH_CHECK(igraph_sparsemat_dupl(&t)); + IGRAPH_CHECK(igraph_sparsemat_transpose(&t, &tt)); + igraph_sparsemat_destroy(&t); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tt); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tt, &t)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &t); + + nz = t.cs->p[t.cs->n]; + res = memcmp(t.cs->i, tt.cs->i, sizeof(CS_INT) * (size_t) nz) == 0; + res = res && memcmp(t.cs->p, tt.cs->p, sizeof(CS_INT) * + (size_t)(t.cs->n + 1)) == 0; + res = res && memcmp(t.cs->x, tt.cs->x, sizeof(CS_ENTRY) * (size_t)nz) == 0; + + igraph_sparsemat_destroy(&t); + igraph_sparsemat_destroy(&tt); + IGRAPH_FINALLY_CLEAN(2); + + *result = res; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; +} + +/** + * \function igraph_sparsemat_is_symmetric + * \brief Returns whether a sparse matrix is symmetric. + * + * \param A The input matrix + * \param result Pointer to an \c igraph_bool_t ; the result is provided here. + * \return Error code. + */ + +igraph_error_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A, igraph_bool_t *result) { + if (A->cs->m != A->cs->n) { + *result = false; + } else if (igraph_sparsemat_is_cc(A)) { + IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_cc(A, result)); + } else { + IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_triplet(A, result)); + } + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_dupl(igraph_sparsemat_t *A) { + + if (! igraph_sparsemat_is_cc(A)) { + IGRAPH_ERROR("Sparse matrix must be in compressed format in order to remove duplicates.", IGRAPH_EINVAL); + } + + if (!cs_dupl(A->cs)) { + IGRAPH_ERROR("Cannot remove duplicates from sparse matrix.", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +struct fkeep_wrapper_data { + igraph_integer_t (*fkeep) (igraph_integer_t, igraph_integer_t, igraph_real_t, void*); + void* data; +}; + +static CS_INT fkeep_wrapper(CS_INT row, CS_INT col, double value, void* data) { + return ((struct fkeep_wrapper_data*)data)->fkeep( + row, col, value, ((struct fkeep_wrapper_data*)data)->data + ); +} + +/** + * \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 igraph_integer_t, the row index of the entry, the second is + * another \c igraph_integer_t, 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. + */ + +igraph_error_t igraph_sparsemat_fkeep( + igraph_sparsemat_t *A, + igraph_integer_t (*fkeep)(igraph_integer_t, igraph_integer_t, igraph_real_t, void*), + void *other +) { + struct fkeep_wrapper_data wrapper_data = { + /* .fkeep = */ fkeep, + /* .data = */ 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_wrapper, &wrapper_data) < 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. + */ + +igraph_error_t igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { + + if (!cs_dropzeros(A->cs)) { + IGRAPH_ERROR("Cannot drop zeros from sparse matrix", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res, + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_lusol(const igraph_sparsemat_t *A, + const igraph_vector_t *b, + igraph_vector_t *res, + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed) { + + igraph_vector_int_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; + igraph_integer_t from = 0; + igraph_integer_t to = 0; + igraph_integer_t e = 0; + + if (no_of_nodes != A->cs->n) { + IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INT_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_int_resize(&edges, e); + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed) { + + igraph_vector_int_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; + igraph_integer_t e; + + if (no_of_nodes != A->cs->n) { + IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INT_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_int_resize(&edges, e); + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed) { + + if (igraph_sparsemat_is_cc(A)) { + return (igraph_i_sparsemat_cc(graph, A, directed)); + } else { + return (igraph_i_sparsemat_triplet(graph, A, directed)); + } +} + +static igraph_error_t igraph_i_weighted_sparsemat_cc(const igraph_sparsemat_t *A, + igraph_bool_t directed, const char *attr, + igraph_bool_t loops, + igraph_vector_int_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; + igraph_integer_t from = 0; + igraph_integer_t to = 0; + igraph_integer_t e = 0, w = 0; + + IGRAPH_UNUSED(attr); + + IGRAPH_CHECK(igraph_vector_int_resize(edges, no_of_edges * 2)); + IGRAPH_CHECK(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_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, w); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_weighted_sparsemat_triplet(const igraph_sparsemat_t *A, + igraph_bool_t directed, + const char *attr, + igraph_bool_t loops, + igraph_vector_int_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); +} + +igraph_error_t igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, + igraph_bool_t directed, const char *attr, + igraph_bool_t loops) { + + igraph_vector_int_t edges; + igraph_vector_t weights; + CS_INT pot_edges = igraph_i_sparsemat_count_elements(A); + 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_INT_INIT_FINALLY(&edges, pot_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&weights, pot_edges); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attr_vec, 1); + + if (igraph_sparsemat_is_cc(A)) { + 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, no_of_nodes, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + if (igraph_vector_int_size(&edges) > 0) { + IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec)); + } + IGRAPH_FINALLY_CLEAN(1); + + /* Cleanup */ + igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&weights); + igraph_vector_ptr_destroy(&attr_vec); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +#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. + */ + +igraph_error_t igraph_sparsemat_print(const igraph_sparsemat_t *A, + FILE *outstream) { + + if (igraph_sparsemat_is_cc(A)) { + /* 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 IGRAPH_SUCCESS; +} + +#undef CHECK + +static igraph_error_t igraph_i_sparsemat_eye_triplet( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value +) { + igraph_integer_t i; + + IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax)); + + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_sparsemat_entry(A, i, i, value)); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparsemat_eye_cc( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_real_t value +) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_init_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). + */ + +igraph_error_t igraph_sparsemat_init_eye( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t 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); + } +} + +/** + * \function igraph_sparsemat_eye + * \brief Creates a sparse identity matrix (deprecated alias). + * + * \deprecated-by igraph_sparsemat_init_eye 0.10 + */ + +igraph_error_t igraph_sparsemat_eye( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value, igraph_bool_t compress +) { + return igraph_sparsemat_init_eye(A, n, nzmax, value, compress); +} + +static igraph_error_t igraph_i_sparsemat_init_diag_triplet( + igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values +) { + + CS_INT i, n = igraph_vector_size(values); + + IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax)); + + for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_sparsemat_entry(A, i, i, VECTOR(*values)[i])); + } + + return IGRAPH_SUCCESS; + +} + +static igraph_error_t igraph_i_sparsemat_init_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 IGRAPH_SUCCESS; + +} + +/** + * \function igraph_sparsemat_init_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. + */ + +igraph_error_t igraph_sparsemat_init_diag( + igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, + igraph_bool_t compress +) { + if (compress) { + return (igraph_i_sparsemat_init_diag_cc(A, values)); + } else { + return (igraph_i_sparsemat_init_diag_triplet(A, nzmax, values)); + } +} + +/** + * \function igraph_sparsemat_diag + * \brief Creates a sparse diagonal matrix (deprecated alias). + * + * \deprecated-by igraph_sparsemat_init_diag 0.10 + */ + +igraph_error_t igraph_sparsemat_diag( + igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, + igraph_bool_t compress +) { + return igraph_sparsemat_init_diag(A, nzmax, values, compress); +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +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 igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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(). Supply + * \c NULL here to use the defaults. 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. + */ + +igraph_error_t 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_integer_t n = igraph_sparsemat_nrow(A); + + if (n != igraph_sparsemat_ncol(A)) { + IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); + } + + if (n > INT_MAX) { + IGRAPH_ERROR("Matrix too large for ARPACK", IGRAPH_EOVERFLOW); + } + + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + + options->n = (int) 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_init_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 IGRAPH_SUCCESS; +} + +/** + * \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(). Supply \c NULL here to use the defaults. + * 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. + */ + +igraph_error_t 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_integer_t n = igraph_sparsemat_nrow(A); + + if (n > INT_MAX) { + IGRAPH_ERROR("Matrix too large for ARPACK", IGRAPH_EOVERFLOW); + } + + if (n != igraph_sparsemat_ncol(A)) { + IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); + } + + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + + options->n = (int) 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. + */ + +igraph_error_t igraph_sparsemat_symbqr(igraph_integer_t order, const igraph_sparsemat_t *A, + igraph_sparsemat_symbolic_t *dis) { + + dis->symbolic = cs_sqr(order, A->cs, /*qr=*/ 1); + if (!dis->symbolic) { + IGRAPH_ERROR("Cannot do symbolic QR decomposition", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_symblu(igraph_integer_t order, const igraph_sparsemat_t *A, + igraph_sparsemat_symbolic_t *dis) { + + dis->symbolic = cs_sqr(order, A->cs, /*qr=*/ 0); + if (!dis->symbolic) { + IGRAPH_ERROR("Cannot do symbolic LU decomposition", IGRAPH_FAILURE); + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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_integer_t 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); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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_integer_t n = din->numeric->L->n; + igraph_real_t *workspace; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + * + * \sa \ref igraph_sparsemat_as_matrix() for the reverse conversion. + * + * Time complexity: O(mn), the number of elements in the dense + * matrix. + */ + +igraph_error_t igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, + const igraph_matrix_t *mat, + igraph_real_t tol) { + igraph_integer_t nrow = igraph_matrix_nrow(mat); + igraph_integer_t ncol = igraph_matrix_ncol(mat); + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, + const igraph_sparsemat_t *spmat) { + + igraph_integer_t nrow = igraph_sparsemat_nrow(spmat); + igraph_integer_t 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 elem_count = spmat->cs->p[ spmat->cs->n ]; + + IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol)); + igraph_matrix_null(res); + + while (*p < elem_count) { + while (to < *(p + 1)) { + MATRIX(*res, *i, from) += *x; + to++; + i++; + x++; + } + from++; + p++; + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, + const igraph_sparsemat_t *spmat) { + igraph_integer_t nrow = igraph_sparsemat_nrow(spmat); + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + * + * \sa \ref igraph_matrix_as_sparsemat() for the reverse conversion. + * + * Time complexity: O(mn), the number of elements in the dense + * matrix. + */ + +igraph_error_t 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 = igraph_i_sparsemat_count_elements(A); + 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 = igraph_i_sparsemat_count_elements(A); + 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. + */ + + +igraph_error_t 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 = igraph_i_sparsemat_count_elements(A); + if (n == 0) { + *min = IGRAPH_POSINFINITY; + *max = IGRAPH_NEGINFINITY; + return IGRAPH_SUCCESS; + } + *min = *max = *ptr; + for (i = 1; i < n; i++, ptr++) { + if (*ptr > *max) { + *max = *ptr; + } else if (*ptr < *min) { + *min = *ptr; + } + } + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_integer_t igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { + CS_INT i, n; + CS_ENTRY *ptr; + igraph_integer_t res = 0; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ptr = A->cs->x; + n = igraph_i_sparsemat_count_elements(A); + 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. + */ + +igraph_integer_t igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, + igraph_real_t tol) { + CS_INT i, n; + CS_ENTRY *ptr; + igraph_integer_t res = 0; + + IGRAPH_CHECK(igraph_sparsemat_dupl(A)); + + ptr = A->cs->x; + n = igraph_i_sparsemat_count_elements(A); + if (n == 0) { + return 0; + } + for (i = 0; i < n; i++, ptr++) { + if (*ptr < - tol || *ptr > tol) { + res++; + } + } + return res; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 igraph_error_t 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; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + igraph_vector_fill(res, IGRAPH_INFINITY); + + for (i = 0; i < A->cs->nz; i++, pi++, px++) { + if (*px < VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT ne; + CS_ENTRY *px; + CS_INT *pi; + + 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, IGRAPH_INFINITY); + + for (; pi < A->cs->i + ne; pi++, px++) { + if (*px < VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + } + } + + return IGRAPH_SUCCESS; +} + +igraph_error_t 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 igraph_error_t 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; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + igraph_vector_fill(res, -IGRAPH_INFINITY); + + for (i = 0; i < A->cs->nz; i++, pi++, px++) { + if (*px > VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, + igraph_vector_t *res) { + CS_INT ne; + CS_ENTRY *px; + CS_INT *pi; + + 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, -IGRAPH_INFINITY); + + for (; pi < A->cs->i + ne; pi++, px++) { + if (*px > VECTOR(*res)[ *pi ]) { + VECTOR(*res)[ *pi ] = *px; + } + } + + return IGRAPH_SUCCESS; +} + +igraph_error_t 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 igraph_error_t 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; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n)); + igraph_vector_fill(res, IGRAPH_INFINITY); + + for (i = 0; i < A->cs->nz; i++, pp++, px++) { + if (*px < VECTOR(*res)[ *pp ]) { + VECTOR(*res)[ *pp ] = *px; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + + 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, IGRAPH_INFINITY); + 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 IGRAPH_SUCCESS; +} + +igraph_error_t 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 igraph_error_t 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; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n)); + igraph_vector_fill(res, -IGRAPH_INFINITY); + + for (i = 0; i < A->cs->nz; i++, pp++, px++) { + if (*px > VECTOR(*res)[ *pp ]) { + VECTOR(*res)[ *pp ] = *px; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + + 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, -IGRAPH_INFINITY); + 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 IGRAPH_SUCCESS; +} + +igraph_error_t 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 igraph_error_t 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; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); + IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->m)); + igraph_vector_fill(res, IGRAPH_INFINITY); + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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, IGRAPH_INFINITY); + 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 IGRAPH_SUCCESS; +} + +igraph_error_t 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 igraph_error_t 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; + + IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n)); + IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->n)); + igraph_vector_fill(res, IGRAPH_INFINITY); + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + + 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, IGRAPH_INFINITY); + 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 IGRAPH_SUCCESS; +} + +igraph_error_t 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 igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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. + */ + +igraph_error_t igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) { + + CS_ENTRY *px = A->cs->x; + CS_ENTRY *stop = px + igraph_i_sparsemat_count_elements(A); + + for (; px < stop; px++) { + *px *= by; + } + + return IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_error_t igraph_sparsemat_add_rows(igraph_sparsemat_t *A, igraph_integer_t n) { + A->cs->m += n; + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_integer_t 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(CS_INT), &realloc_ok); + if (!realloc_ok) { + IGRAPH_ERROR("Cannot add columns to sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_resize + * \brief Resizes a sparse matrix and clears all the elements. + * + * This function resizes a sparse matrix. The resized sparse matrix + * will become empty, even if it contained nonzero entries. + * + * \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. + */ + +igraph_error_t igraph_sparsemat_resize(igraph_sparsemat_t *A, igraph_integer_t nrow, + igraph_integer_t ncol, igraph_integer_t nzmax) { + + if (igraph_sparsemat_is_cc(A)) { + igraph_sparsemat_t tmp; + IGRAPH_CHECK(igraph_sparsemat_init(&tmp, nrow, ncol, nzmax)); + igraph_sparsemat_destroy(A); + *A = tmp; + } else { + IGRAPH_CHECK(igraph_sparsemat_realloc(A, nzmax)); + A->cs->m = nrow; + A->cs->n = ncol; + A->cs->nz = 0; + } + return IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_integer_t igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A) { + return igraph_i_sparsemat_count_elements(A); +} + + +/** + * \function igraph_sparsemat_getelements + * \brief Returns all elements of a sparse matrix. + * + * This function will return the elements of a sparse matrix in three vectors. + * Two vectors will indicate where the elements are located, and one will + * specify the elements themselves. + * + * \param A A sparse matrix in either triplet or compressed form. + * \param i An initialized integer vector. This will store the rows of the + * returned elements. + * \param j An initialized integer 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. + */ + +igraph_error_t 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(CS_INT)); + memcpy(VECTOR(*j), A->cs->p, (size_t) (A->cs->n + 1) * sizeof(CS_INT)); + memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(CS_ENTRY)); + } 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(CS_INT)); + memcpy(VECTOR(*j), A->cs->p, (size_t) nz * sizeof(CS_INT)); + memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(CS_ENTRY)); + } + return IGRAPH_SUCCESS; +} + +igraph_error_t 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 = igraph_i_sparsemat_count_elements(A); + CS_INT e; + + for (e = 0; e < no_of_edges; e++, x++, i++) { + igraph_real_t f = VECTOR(*fact)[*i]; + (*x) *= f; + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +igraph_error_t igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, + const igraph_vector_t *fact) { + if (igraph_sparsemat_is_cc(A)) { + return igraph_i_sparsemat_scale_cols_cc(A, fact); + } else { + return igraph_i_sparsemat_scale_cols_triplet(A, fact); + } +} + +igraph_error_t igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, + const igraph_matrix_t *B, + igraph_matrix_t *res) { + + igraph_integer_t m = igraph_sparsemat_nrow(A); + igraph_integer_t n = igraph_sparsemat_ncol(A); + igraph_integer_t p = igraph_matrix_ncol(B); + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, + const igraph_sparsemat_t *B, + igraph_matrix_t *res) { + igraph_integer_t m = igraph_matrix_nrow(A); + igraph_integer_t n = igraph_matrix_ncol(A); + igraph_integer_t p = igraph_sparsemat_ncol(B); + igraph_integer_t 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++) { + igraph_integer_t idx = *Bp; + while (idx < * (Bp + 1)) { + MATRIX(*res, r, c) += MATRIX(*A, r, B->cs->i[idx]) * B->cs->x[idx]; + idx++; + } + } + Bp++; + } + + return IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_error_t igraph_sparsemat_view(igraph_sparsemat_t *A, igraph_integer_t nzmax, igraph_integer_t m, igraph_integer_t n, + igraph_integer_t *p, igraph_integer_t *i, igraph_real_t *x, igraph_integer_t nz) { + + A->cs = IGRAPH_CALLOC(1, cs_igraph); + A->cs->nzmax = nzmax; + A->cs->m = m; + A->cs->n = n; + A->cs->p = (CS_INT*) p; + A->cs->i = (CS_INT*) i; + A->cs->x = x; + A->cs->nz = nz; + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_sparsemat_sort + * \brief Sorts all elements of a sparse matrix by row and column indices. + * + * This function will sort the elements of a sparse matrix such that iterating + * over the entries will return them sorted by column indices; elements in the + * same column are then sorted by row indices. + * + * \param A A sparse matrix in either triplet or compressed form. + * \param sorted An uninitialized sparse matrix; the result will be returned + * here. The result will be in triplet form if the input was in triplet + * form, otherwise it will be in compressed form. Note that sorting is + * more efficient when the matrix is already in compressed form. + * \return Error code. + * + * Time complexity: TODO + */ + +igraph_error_t igraph_sparsemat_sort(const igraph_sparsemat_t *A, + igraph_sparsemat_t *sorted) { + igraph_sparsemat_t tmp; + igraph_sparsemat_t tmp2; + + if (igraph_sparsemat_is_cc(A)) { + /* for column-compressed matrices, we will transpose the matrix twice, + * which will sort the indices as a side effect */ + IGRAPH_CHECK(igraph_sparsemat_transpose(A, &tmp)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tmp, sorted)); + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_sparsemat_iterator_t it; + + /* for triplet matrices, we convert it to compressed column representation, + * sort it, then we convert back */ + IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_sort(&tmp, &tmp2)); + + igraph_sparsemat_destroy(&tmp); + tmp = tmp2; /* tmp is still protected in the FINALLY stack */ + + IGRAPH_CHECK(igraph_sparsemat_init( + sorted, + igraph_sparsemat_nrow(&tmp), + igraph_sparsemat_ncol(&tmp), + igraph_i_sparsemat_count_elements(&tmp) + )); + IGRAPH_FINALLY(igraph_sparsemat_destroy, sorted); + + IGRAPH_CHECK(igraph_sparsemat_iterator_init(&it, &tmp)); + while (!igraph_sparsemat_iterator_end(&it)) { + IGRAPH_CHECK(igraph_sparsemat_entry( + sorted, + igraph_sparsemat_iterator_row(&it), + igraph_sparsemat_iterator_col(&it), + igraph_sparsemat_iterator_get(&it) + )); + igraph_sparsemat_iterator_next(&it); + } + + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); /* tmp + sorted */ + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_getelements_sorted + * \brief Returns all elements of a sparse matrix, sorted by row and column indices. + * + * This function will sort a sparse matrix and return the elements in three + * vectors. Two vectors will indicate where the elements are located, + * and one will specify the elements themselves. + * + * + * Sorting is done based on the \em indices of the elements, not their + * numeric values. The returned entries will be sorted by column indices; + * entries in the same column are then sorted by row indices. + * + * \param A A sparse matrix in either triplet or compressed form. + * \param i An initialized integer vector. This will store the rows of the + * returned elements. + * \param j An initialized integer 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: TODO. + */ + +igraph_error_t igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, + igraph_vector_int_t *i, + igraph_vector_int_t *j, + igraph_vector_t *x) { + 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); + + /* TODO: in triplets format, we could in theory sort the entries without + * going through an extra sorting step (which temporarily converts the + * matrix into compressed format). This is not implemented yet. */ + + return IGRAPH_SUCCESS; +} + +igraph_integer_t igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) { + return A->cs->nzmax; +} + +igraph_error_t igraph_sparsemat_neg(igraph_sparsemat_t *A) { + CS_INT i; + CS_INT nz = igraph_i_sparsemat_count_elements(A); + CS_ENTRY *px = A->cs->x; + + for (i = 0; i < nz; i++, px++) { + *px = - (*px); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_normalize_cols + * \brief Normalizes the column sums of a sparse matrix to a given value. + * + * \param sparsemat The sparse matrix to normalize + * \param allow_zeros If false, zero-sum columns will be rejected with an error. + * \return \c IGRAPH_SUCCESS if everything was successful, + * \c IGRAPH_EINVAL if there is at least one column with zero sum and it + * is disallowed, + * \c IGRAPH_ENOMEM for out-of-memory conditions + */ + +igraph_error_t igraph_sparsemat_normalize_cols( + igraph_sparsemat_t *sparsemat, igraph_bool_t allow_zeros +) { + igraph_vector_t sum; + const igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(sparsemat); + + IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes); + + IGRAPH_CHECK(igraph_sparsemat_colsums(sparsemat, &sum)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (VECTOR(sum)[i] != 0.0) { + VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; + } else if (!allow_zeros) { + IGRAPH_ERROR("Columns with zero sum are not allowed.", IGRAPH_EINVAL); + } + } + IGRAPH_CHECK(igraph_sparsemat_scale_cols(sparsemat, &sum)); + + igraph_vector_destroy(&sum); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_normalize_rows + * \brief Normalizes the row sums of a sparse matrix to a given value. + * + * \param sparsemat The sparse matrix to normalize + * \param allow_zeros If false, zero-sum rows will be rejected with an error. + * \return \c IGRAPH_SUCCESS if everything was successful, + * \c IGRAPH_EINVAL if there is at least one row with zero sum and it + * is disallowed, + * \c IGRAPH_ENOMEM for out-of-memory conditions + */ + +igraph_error_t igraph_sparsemat_normalize_rows( + igraph_sparsemat_t *sparsemat, igraph_bool_t allow_zeros +) { + igraph_vector_t sum; + const igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(sparsemat); + + IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes); + + IGRAPH_CHECK(igraph_sparsemat_rowsums(sparsemat, &sum)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (VECTOR(sum)[i] != 0.0) { + VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; + } else if (!allow_zeros) { + IGRAPH_ERROR("Rows with zero sum are not allowed.", IGRAPH_EINVAL); + } + } + IGRAPH_CHECK(igraph_sparsemat_scale_rows(sparsemat, &sum)); + + igraph_vector_destroy(&sum); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_sparsemat_iterator_init( + igraph_sparsemat_iterator_t *it, const 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. + */ + +igraph_error_t 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). + */ + +igraph_integer_t 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). + */ + +igraph_integer_t 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. + */ + +igraph_integer_t 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). + */ + +igraph_integer_t igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) { + return it->pos; +} diff --git a/src/core/stack.c b/src/core/stack.c new file mode 100644 index 0000000..69b3b3e --- /dev/null +++ b/src/core/stack.c @@ -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 + +*/ + +#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_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 diff --git a/src/core/stack.pmt b/src/core/stack.pmt new file mode 100644 index 0000000..4be8cff --- /dev/null +++ b/src/core/stack.pmt @@ -0,0 +1,306 @@ +/* -*- 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 /* 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 capacity The number of elements to allocate memory for. + * \return Error code. + * + * Time complexity: O(\p size). + */ + +igraph_error_t FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, igraph_integer_t capacity) { + igraph_integer_t alloc_size; + IGRAPH_ASSERT(capacity >= 0); + alloc_size = capacity > 0 ? capacity : 1; + IGRAPH_ASSERT(s != NULL); + s->stor_begin = IGRAPH_CALLOC(alloc_size, BASE); + if (s->stor_begin == NULL) { + IGRAPH_ERROR("Cannot initialize stack.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + s->stor_end = s->stor_begin + alloc_size; + s->end = s->stor_begin; + + return IGRAPH_SUCCESS; +} + +/** + * \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 != NULL) { + IGRAPH_FREE(s->stor_begin); + s->stor_begin = NULL; + } +} + +/** + * \ingroup stack + * \function igraph_stack_capacity + * \brief Returns the allocated capacity of the stack. + * + * Note that this might be different from the size of the stack (as + * queried by \ref igraph_stack_size()), and specifies how many elements + * the stack can hold, without reallocation. + * + * \param v Pointer to the (previously initialized) stack object + * to query. + * \return The allocated capacity. + * + * \sa \ref igraph_stack_size(). + * + * Time complexity: O(1). + */ + +igraph_integer_t FUNCTION(igraph_stack, capacity)(const TYPE(igraph_stack) *s) { + return s->stor_end - s->stor_begin; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, igraph_integer_t capacity) { + igraph_integer_t current_capacity; + BASE *tmp; + + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); + + current_capacity = FUNCTION(igraph_stack, capacity)(s); + + if (capacity <= current_capacity) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(s->stor_begin, capacity, BASE); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for stack."); + + s->end = tmp + (s->end - s->stor_begin); + s->stor_begin = tmp; + s->stor_end = s->stor_begin + capacity; + + return IGRAPH_SUCCESS; +} + +/** + * \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); + 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). + */ + +igraph_integer_t 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. + */ + +igraph_error_t FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem) { + IGRAPH_ASSERT(s != NULL); + IGRAPH_ASSERT(s->stor_begin != NULL); + + if (s->stor_end == s->end) { + /* full, allocate more storage */ + igraph_integer_t old_size = FUNCTION(igraph_stack, size)(s); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to stack, already at maximum size.", IGRAPH_EOVERFLOW); + } + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(FUNCTION(igraph_stack, reserve)(s, new_size)); + } + + *(s->end) = elem; + s->end += 1; + + return IGRAPH_SUCCESS; +} + +/** + * \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) || defined(FPRINTFUNC) + +#ifndef USING_R +igraph_error_t FUNCTION(igraph_stack, print)(const TYPE(igraph_stack) *s) { + return FUNCTION(igraph_stack, fprint)(s, stdout); +} +#endif /* USING_R */ + +igraph_error_t FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack) *s, FILE *file) { + igraph_integer_t i, n = FUNCTION(igraph_stack, size)(s); + if (n != 0) { +#ifdef FPRINTFUNC + FPRINTFUNC(file, s->stor_begin[0]); +#else + fprintf(file, OUT_FORMAT, s->stor_begin[0]); +#endif + } + for (i = 1; i < n; i++) { +#ifdef FPRINTFUNC + fputc(' ', file); fprintf(file, OUT_FORMAT, s->stor_begin[i]); +#else + fprintf(file, " " OUT_FORMAT, s->stor_begin[i]); +#endif + } + fprintf(file, "\n"); + return IGRAPH_SUCCESS; +} + +#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ diff --git a/src/core/statusbar.c b/src/core/statusbar.c new file mode 100644 index 0000000..48e47b9 --- /dev/null +++ b/src/core/statusbar.c @@ -0,0 +1,133 @@ +/* -*- 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 + * \brief Reports 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). + */ + +igraph_error_t 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 + * \brief 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 \ref igraph_status(). + */ + +igraph_error_t igraph_statusf(const char *message, void *data, ...) { + char buffer[300]; + va_list ap; + va_start(ap, data); + vsnprintf(buffer, sizeof(buffer) - 1, message, ap); + va_end(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 error. + * + * \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). + */ + +igraph_error_t igraph_status_handler_stderr(const char *message, void *data) { + IGRAPH_UNUSED(data); + fputs(message, stderr); + return IGRAPH_SUCCESS; +} +#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/core/strvector.c b/src/core/strvector.c new file mode 100644 index 0000000..eb4bc2e --- /dev/null +++ b/src/core/strvector.c @@ -0,0 +1,699 @@ +/* -*- 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 "internal/hacks.h" /* strdup */ +#include "math/safe_intop.h" + +#include /* memcpy & co. */ +#include + +/** + * \section igraph_strvector_t + * + * The igraph_strvector_t type is a vector of null-terminated + * strings. It is used internally for storing graph attribute names as well as + * string attributes in the C attribute handler. + * + * + * + * This container automatically manages the memory of its elements. + * The strings within an igraph_strvector_t should be considered + * constant, and not modified directly. Functions that add new elements + * always make copies of the string passed to them. + * + * + * + * \example examples/simple/igraph_strvector.c + * + */ + +/** + * \ingroup strvector + * \function igraph_strvector_init + * \brief Initializes a string vector. + * + * 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). + */ + +igraph_error_t igraph_strvector_init(igraph_strvector_t *sv, igraph_integer_t size) { + + sv->stor_begin = IGRAPH_CALLOC(size, char*); + IGRAPH_CHECK_OOM(sv->stor_begin, "Cannot initialize string vector."); + + sv->stor_end = sv->stor_begin + size; + sv->end = sv->stor_end; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_destroy + * \brief Frees the memory allocated for the string vector. + * + * 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) { + char **ptr; + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + for (ptr = sv->stor_begin; ptr < sv->end; ptr++) { + IGRAPH_FREE(*ptr); + } + IGRAPH_FREE(sv->stor_begin); +} + +/** + * \ingroup strvector + * \function igraph_strvector_get + * \brief Retrieves an element of a string vector. + * + * Query an element of a string vector. The returned string must not be modified. + * + * \param sv The input string vector. + * \param idx The index of the element to query. + * + * Time complexity: O(1). + */ + +const char *igraph_strvector_get(const igraph_strvector_t *sv, igraph_integer_t idx) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + return sv->stor_begin[idx] ? sv->stor_begin[idx] : ""; +} + +/** + * \ingroup strvector + * \function igraph_strvector_set + * \brief Sets an element of the string vector from a string. + * + * 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. + */ + +igraph_error_t igraph_strvector_set(igraph_strvector_t *sv, igraph_integer_t idx, + const char *value) { + return igraph_strvector_set_len(sv, idx, value, strlen(value)); +} + +/** + * \ingroup strvector + * \function igraph_strvector_set_len + * \brief Sets an element of the string vector given a buffer and its size. + * + * 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. + */ +igraph_error_t igraph_strvector_set_len(igraph_strvector_t *sv, igraph_integer_t idx, + const char *value, size_t len) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + + if (sv->stor_begin[idx] == NULL) { + sv->stor_begin[idx] = strndup(value, len); + IGRAPH_CHECK_OOM(sv->stor_begin[idx], "Cannot reserve space for new item in string vector."); + } else { + char *tmp = IGRAPH_REALLOC(sv->stor_begin[idx], len + 1, char); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for new item in string vector."); + + sv->stor_begin[idx] = tmp; + memcpy(sv->stor_begin[idx], value, len * sizeof(char)); + sv->stor_begin[idx][len] = '\0'; + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_remove_section + * \brief Removes a section from a string vector. + * + * This function removes the range [from, to) from the string vector. + * + * \param sv The string vector. + * \param from The position of the first element to remove. + * \param to The position of the first element \em not to remove. + */ + +void igraph_strvector_remove_section( + igraph_strvector_t *sv, igraph_integer_t from, igraph_integer_t to) { + igraph_integer_t size = igraph_strvector_size(sv); + igraph_integer_t i; + + if (from < 0) { + from = 0; + } + + if (to > size) { + to = size; + } + + if (to > from) { + for (i = from; i < to; i++) { + IGRAPH_FREE(sv->stor_begin[i]); + } + + memmove(sv->stor_begin + from, sv->stor_begin + to, + sizeof(char*) * (sv->end - sv->stor_begin - to)); + sv->end -= (to - from); + } +} + +/** + * \ingroup strvector + * \function igraph_strvector_remove + * \brief Removes a single element from a string vector. + * + * The string will be one shorter. + * \param sv 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 *sv, igraph_integer_t elem) { + igraph_strvector_remove_section(sv, elem, elem + 1); +} + +/** + * \ingroup strvector + * \function igraph_strvector_init_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. + */ + +igraph_error_t igraph_strvector_init_copy(igraph_strvector_t *to, + const igraph_strvector_t *from) { + igraph_integer_t from_size = igraph_strvector_size(from); + + to->stor_begin = IGRAPH_CALLOC(from_size, char*); + IGRAPH_CHECK_OOM(to->stor_begin, "Cannot copy string vector."); + + for (igraph_integer_t i = 0; i < from_size; i++) { + /* If the string in the 'from' vector is empty, we represent it as NULL. + * The NULL value was already set by IGRAPH_CALLOC(). */ + if (from->stor_begin[i] == NULL || from->stor_begin[i][0] == '\0') { + continue; + } + to->stor_begin[i] = strdup(from->stor_begin[i]); + if (to->stor_begin[i] == NULL) { + /* LCOV_EXCL_START */ + for (igraph_integer_t j = 0; j < i; j++) { + IGRAPH_FREE(to->stor_begin[j]); + } + IGRAPH_FREE(to->stor_begin); + IGRAPH_ERROR("Cannot copy string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + /* LCOV_EXCL_STOP */ + } + } + + to->stor_end = to->stor_begin + from_size; + to->end = to->stor_end; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_copy + * \brief Initialization by copying (deprecated alias). + * + * \deprecated-by igraph_strvector_init_copy 0.10.0 + */ + +igraph_error_t igraph_strvector_copy(igraph_strvector_t *to, + const igraph_strvector_t *from) { + return igraph_strvector_init_copy(to, from); +} + +/** + * \function igraph_strvector_append + * \brief Concatenates two string vectors. + * + * Appends the contents of the \p from vector to the \p to vector. + * If the \p from vector is no longer needed after this operation, + * use \ref igraph_strvector_merge() for better performance. + * + * \param to The first string vector, the result is stored here. + * \param from The second string vector, it is kept unchanged. + * \return Error code. + * + * \sa \ref igraph_strvector_merge() + * + * 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. + */ + +igraph_error_t igraph_strvector_append(igraph_strvector_t *to, + const igraph_strvector_t *from) { + igraph_integer_t len1 = igraph_strvector_size(to), len2 = igraph_strvector_size(from); + igraph_integer_t newlen; + igraph_bool_t error = false; + char *tmp; + + IGRAPH_SAFE_ADD(len1, len2, &newlen); + IGRAPH_CHECK(igraph_strvector_reserve(to, newlen)); + + for (igraph_integer_t i = 0; i < len2; i++) { + if (from->stor_begin[i] == NULL || from->stor_begin[i][0] == '\0') { + /* Represent empty strings as NULL. */ + tmp = NULL; + } else { + tmp = strdup(from->stor_begin[i]); + if (tmp == NULL) { + error = true; + break; + } + } + *(to->end) = tmp; + to->end++; + } + + if (error) { + igraph_strvector_resize(to, len1); /* always shrinks */ + IGRAPH_ERROR("Cannot append string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_merge + * \brief Moves the contents of a string vector to the end of another. + * + * Transfers the contents of the \p from vector to the end of \p to, clearing + * \p from in the process. If this operation fails, both vectors are left intact. + * This function does not copy or reallocate individual strings, therefore it + * performs better than \ref igraph_strvector_append(). + * + * \param to The target vector. The contents of \p from will be appended to it. + * \param from The source vector. It will be cleared. + * \return Error code. + * + * \sa \ref igraph_strvector_append() + * + * Time complexity: O(l2) if \p to has sufficient capacity, O(2*l1+l2) otherwise, + * where l1 and l2 are the lengths of \p to and \from respectively. + */ +igraph_error_t igraph_strvector_merge(igraph_strvector_t *to, igraph_strvector_t *from) { + char **p1, **p2, **pe; + igraph_integer_t newlen; + + IGRAPH_SAFE_ADD(igraph_strvector_size(to), igraph_strvector_size(from), &newlen); + IGRAPH_CHECK(igraph_strvector_reserve(to, newlen)); + + /* transfer contents of 'from' to 'to */ + for (p1 = to->end, p2 = from->stor_begin, pe = to->stor_begin + newlen; + p1 < pe; ++p1, ++p2) + { + *p1 = *p2; + } + to->end = pe; + + /* clear 'from' */ + from->end = from->stor_begin; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_strvector_clear + * \brief Removes all elements from a string vector. + * + * 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) { + igraph_integer_t n = igraph_strvector_size(sv); + + for (igraph_integer_t i = 0; i < n; i++) { + IGRAPH_FREE(sv->stor_begin[i]); + } + sv->end = sv->stor_begin; +} + +/** + * \ingroup strvector + * \function igraph_strvector_resize + * \brief Resizes a string vector. + * + * If the new size is bigger then empty strings are added, if it is + * smaller then the unneeded elements are removed. + * + * \param sv 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. + */ + +igraph_error_t igraph_strvector_resize(igraph_strvector_t *sv, igraph_integer_t newsize) { + igraph_integer_t toadd = newsize - igraph_strvector_size(sv); + igraph_integer_t oldsize = igraph_strvector_size(sv); + + if (newsize < oldsize) { + for (igraph_integer_t i = newsize; i < oldsize; i++) { + IGRAPH_FREE(sv->stor_begin[i]); + } + sv->end = sv->stor_begin + newsize; + } else if (newsize > oldsize) { + IGRAPH_CHECK(igraph_strvector_reserve(sv, newsize)); + memset(sv->stor_begin + oldsize, 0, toadd * sizeof(char *)); + sv->end = sv->stor_begin + newsize; + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_capacity + * \brief Returns the capacity of a string vector. + * + * \param sv The string vector. + * \return The capacity of the string vector. + * + * Time complexity: O(1). + */ + +igraph_integer_t igraph_strvector_capacity(const igraph_strvector_t *sv) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + return sv->stor_end - sv->stor_begin; +} + +/** + * \ingroup strvector + * \function igraph_strvector_reserve + * \brief Reserves memory for a string vector. + * + * + * \a igraph string 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 + * string vector. Let us see a small example to clarify things: if you + * reserve space for 100 strings and the size of your + * vector was (and still is) 60, then you can surely add additional 40 + * strings to your vector before it will be copied. + * + * \param sv The string vector object. + * \param capacity The new \em allocated size of the string 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. + */ + +igraph_error_t igraph_strvector_reserve(igraph_strvector_t *sv, igraph_integer_t capacity) { + igraph_integer_t current_capacity = igraph_strvector_capacity(sv); + char **tmp; + + if (capacity <= current_capacity) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(sv->stor_begin, capacity, char *); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for new items in string vector."); + + sv->end = tmp + (sv->end - sv->stor_begin); + sv->stor_begin = tmp; + sv->stor_end = sv->stor_begin + capacity; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_resize_min + * \brief Deallocates the unused memory of a string vector. + * + * This function attempts to deallocate the unused reserved storage + * of a string vector. If it succeeds, \ref igraph_strvector_size() and + * \ref igraph_strvector_capacity() will be the same. The data in the + * string vector is always preserved, even if deallocation is not successful. + * + * \param sv The string vector. + * + * Time complexity: Operating system dependent, at most O(n). + */ + +void igraph_strvector_resize_min(igraph_strvector_t *sv) { + igraph_integer_t size; + char **tmp; + if (sv->stor_end == sv->end) { + return; + } + + size = (sv->end - sv->stor_begin); + tmp = IGRAPH_REALLOC(sv->stor_begin, size, char *); + + if (tmp != NULL) { + sv->stor_begin = tmp; + sv->stor_end = sv->end = sv->stor_begin + size; + } +} + +/** + * \ingroup strvector + * \function igraph_strvector_size + * \brief Returns the size of a string vector. + * + * \param sv The string vector. + * \return The length of the string vector. + * + * Time complexity: O(1). + */ + +igraph_integer_t igraph_strvector_size(const igraph_strvector_t *sv) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + return sv->end - sv->stor_begin; +} + +/** + * Ensures that the vector has at least one extra slot at the end of its + * allocated storage area. + */ +static igraph_error_t igraph_i_strvector_expand_if_full(igraph_strvector_t *sv) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + + if (sv->stor_end == sv->end) { + igraph_integer_t old_size = igraph_strvector_size(sv); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot add new item to string vector, already at maximum size.", IGRAPH_EOVERFLOW); + } + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(igraph_strvector_reserve(sv, new_size)); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_push_back + * \brief Adds an element to the back of a string vector. + * + * \param sv 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. + */ + +igraph_error_t igraph_strvector_push_back(igraph_strvector_t *sv, const char *value) { + IGRAPH_CHECK(igraph_i_strvector_expand_if_full(sv)); + char *tmp = strdup(value); + IGRAPH_CHECK_OOM(tmp, "Cannot push new string to string vector."); + *sv->end = tmp; + sv->end++; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_push_back_len + * \brief Adds a string of the given length to the back of a string vector. + * + * \param sv The string vector. + * \param value The start of the string to add. At most \p len characters will be copied. + * \param len The length of the string. + * \return Error code. + * + * Time complexity: O(n+l), n is the total number of strings, l is the + * length of the new string. + */ + +igraph_error_t igraph_strvector_push_back_len( + igraph_strvector_t *sv, + const char *value, igraph_integer_t len) { + + IGRAPH_CHECK(igraph_i_strvector_expand_if_full(sv)); + char *tmp = strndup(value, len); + if (! tmp) { + IGRAPH_ERROR("Cannot add string to string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + *sv->end = tmp; + sv->end++; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_add + * \brief Adds an element to the back of a string vector (deprecated alias). + * + * \deprecated-by igraph_strvector_push_back 0.10.0 + */ + +igraph_error_t igraph_strvector_add(igraph_strvector_t *sv, const char *value) { + return igraph_strvector_push_back(sv, value); +} + +/** + * \ingroup strvector + * \function igraph_strvector_set2 + * \brief Sets an element of the string vector given a buffer and its size (deprecated alias). + * + * \deprecated-by igraph_strvector_set_len 0.10.0 + */ + +igraph_error_t igraph_strvector_set2( + igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len +) { + return igraph_strvector_set_len(sv, idx, value, len); +} + +/** + * \ingroup strvector + * \function igraph_strvector_print + * \brief Prints a string vector. + * + * \param sv The string vector. + * \param file The file to write to. + * \param sep The separator to print between strings. + * \return Error code. + */ +igraph_error_t igraph_strvector_print(const igraph_strvector_t *sv, FILE *file, + const char *sep) { + + igraph_integer_t n = igraph_strvector_size(sv); + if (n != 0) { + fprintf(file, "%s", igraph_strvector_get(sv, 0)); + } + for (igraph_integer_t i = 1; i < n; i++) { + fprintf(file, "%s%s", sep, igraph_strvector_get(sv, i)); + } + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_index + * \brief Takes elements at given positions from a string vector. + * + * \param sv The string vector. + * \param newv An initialized string vector, it will be resized as needed. + * \param idx An integer vector of indices to take from \p sv. + * \return Error code. + */ +igraph_error_t igraph_strvector_index(const igraph_strvector_t *sv, + igraph_strvector_t *newv, + const igraph_vector_int_t *idx) { + + igraph_integer_t newlen = igraph_vector_int_size(idx); + IGRAPH_CHECK(igraph_strvector_resize(newv, newlen)); + + for (igraph_integer_t i = 0; i < newlen; i++) { + igraph_integer_t j = VECTOR(*idx)[i]; + const char *str = igraph_strvector_get(sv, j); + IGRAPH_CHECK(igraph_strvector_set(newv, i, str)); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/core/trie.c b/src/core/trie.c new file mode 100644 index 0000000..395b120 --- /dev/null +++ b/src/core/trie.c @@ -0,0 +1,435 @@ +/* -*- 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 "core/trie.h" +#include "internal/hacks.h" /* strdup */ + +#include +#include + + +/* + * igraph_trie_t is a data structures that stores an ordered list of strings. + * It allows an efficient lookup of the index of a string. It has the capability + * to also store the list of strings directly for reverse lookup of strings + * by index. + */ + +/* Allocates memory for a trie node. */ +static igraph_error_t 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_INT_INIT_FINALLY(&t->values, 0); + IGRAPH_FINALLY_CLEAN(3); + return IGRAPH_SUCCESS; +} + +static void igraph_i_trie_destroy_node(igraph_trie_node_t *t); + +/** + * \ingroup igraphtrie + * \brief Creates a trie. + * + * \param t An uninitialized trie. + * \param storekeys Specifies whether keys are stored for reverse lookup. + * \return Error code: Errors by \ref igraph_strvector_init(), + * \ref igraph_vector_ptr_init() and \ref igraph_vector_init() might be returned. + */ + +igraph_error_t igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys) { + t->maxvalue = -1; + t->storekeys = storekeys; + IGRAPH_CHECK(igraph_i_trie_init_node(&t->node)); + IGRAPH_FINALLY(igraph_i_trie_destroy_node, &t->node); + if (storekeys) { + IGRAPH_CHECK(igraph_strvector_init(&t->keys, 0)); + } + + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +static void igraph_i_trie_destroy_node_helper(igraph_trie_node_t *t, igraph_bool_t sfree) { + igraph_strvector_destroy(&t->strs); + igraph_integer_t children_size = igraph_vector_ptr_size(&t->children); + for (igraph_integer_t i = 0; i < children_size; i++) { + igraph_trie_node_t *child = VECTOR(t->children)[i]; + if (child != NULL) { + igraph_i_trie_destroy_node_helper(child, true); + } + } + igraph_vector_ptr_destroy(&t->children); + igraph_vector_int_destroy(&t->values); + if (sfree) { + IGRAPH_FREE(t); + } +} + +/* Deallocates a trie node. */ +static void igraph_i_trie_destroy_node(igraph_trie_node_t *t) { + igraph_i_trie_destroy_node_helper(t, false); +} + +/** + * \ingroup igraphtrie + * \brief Destroys a trie (frees allocated memory). + * + * \param t The trie. + */ + +void igraph_trie_destroy(igraph_trie_t *t) { + if (t->storekeys) { + igraph_strvector_destroy(&t->keys); + } + igraph_i_trie_destroy_node(&t->node); +} + + +/* Computes the location (index) of the first difference between 'str' and 'key' */ +static size_t igraph_i_strdiff(const char *str, const char *key) { + size_t 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, usually \c IGRAPH_ENOMEM. + */ + +static igraph_error_t igraph_i_trie_get_node( + igraph_trie_node_t *t, const char *key, igraph_integer_t newvalue, + igraph_integer_t *id +) { + assert(key != NULL); + + /* If newvalue is negative, we don't add the node if nonexistent, only check + * for its existence */ + igraph_bool_t add = (newvalue >= 0); + + igraph_integer_t strs_size = igraph_strvector_size(&t->strs); + for (igraph_integer_t i = 0; i < strs_size; i++) { + size_t diff; + const char *str = igraph_strvector_get(&t->strs, i); + 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 = VECTOR(t->values)[i]; + return IGRAPH_SUCCESS; + } else { + VECTOR(t->values)[i] = newvalue; + *id = newvalue; + return IGRAPH_SUCCESS; + } + + } 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 != NULL) { + return igraph_i_trie_get_node(node, key + diff, newvalue, id); + } else if (add) { + igraph_trie_node_t *new_node = IGRAPH_CALLOC(1, igraph_trie_node_t); + IGRAPH_CHECK_OOM(new_node, "Cannot add to trie."); + IGRAPH_FINALLY(igraph_free, new_node); + + IGRAPH_STRVECTOR_INIT_FINALLY(&new_node->strs, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&new_node->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_node->values, 1); + IGRAPH_CHECK(igraph_strvector_set(&new_node->strs, 0, key + diff)); + IGRAPH_FINALLY_CLEAN(4); + + VECTOR(new_node->children)[0] = 0; + VECTOR(new_node->values)[0] = newvalue; + + VECTOR(t->children)[i] = new_node; + + *id = newvalue; + return IGRAPH_SUCCESS; + } else { + *id = -1; + return IGRAPH_SUCCESS; + } + + } 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); + IGRAPH_CHECK_OOM(node, "Cannot add to trie."); + IGRAPH_FINALLY(igraph_free, node); + + IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK_OOM(str2, "Cannot add to trie."); + IGRAPH_FINALLY(igraph_free, str2); + str2[diff] = '\0'; + + IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); + + IGRAPH_FREE(str2); + IGRAPH_FINALLY_CLEAN(5); + + VECTOR(t->values)[i] = newvalue; + VECTOR(t->children)[i] = node; + + *id = newvalue; + return IGRAPH_SUCCESS; + + } else if (add) { + + /* ------------------------------------ */ + /* the first diff characters match */ + char *str2; + + igraph_trie_node_t *node = IGRAPH_CALLOC(1, igraph_trie_node_t); + IGRAPH_CHECK_OOM(node, "Cannot add to trie."); + IGRAPH_FINALLY(igraph_free, node); + + IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 2); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 2); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK_OOM(str2, "Cannot add to trie."); + + str2[diff] = '\0'; + IGRAPH_FINALLY(igraph_free, str2); + + IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); + + IGRAPH_FREE(str2); + IGRAPH_FINALLY_CLEAN(5); + + VECTOR(t->values)[i] = -1; + VECTOR(t->children)[i] = node; + + *id = newvalue; + return IGRAPH_SUCCESS; + } else { + + /* ------------------------------------------------- */ + /* No match, but we requested not to add the new key */ + *id = -1; + return IGRAPH_SUCCESS; + } + } + + /* ------------------------------------ */ + /* Nothing matches */ + + if (add) { + /* Memory saving at the cost of performance may be possible by using the pattern + * CHECK(reserve(vec, size(vec) + 1)); + * push_back(vec, value); + * This was the original pattern used before igraph 0.10. */ + IGRAPH_CHECK(igraph_strvector_push_back(&t->strs, key)); + IGRAPH_CHECK(igraph_vector_ptr_push_back(&t->children, NULL)); + IGRAPH_CHECK(igraph_vector_int_push_back(&t->values, newvalue)); + *id = newvalue; + } else { + *id = -1; + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup igraphtrie + * \brief Search/insert a null-terminated string in a trie. + * + * \param t The trie. + * \param key The string to search for. If not found, it will be inserted. + * \param id The index of the string is stored here. + * \return Error code, usually \c IGRAPH_ENOMEM. + */ + +igraph_error_t igraph_trie_get(igraph_trie_t *t, const char *key, igraph_integer_t *id) { + assert(key != NULL); + + if (*key == '\0') { + IGRAPH_ERROR("Keys in a trie cannot be empty.", IGRAPH_EINVAL); + } + + if (!t->storekeys) { + IGRAPH_CHECK(igraph_i_trie_get_node(&t->node, key, t->maxvalue + 1, id)); + if (*id > t->maxvalue) { + t->maxvalue = *id; + } + } else { + igraph_error_t ret; + + IGRAPH_FINALLY_ENTER(); + /* Add it to the string vector first, we can undo this later */ + ret = igraph_strvector_push_back(&t->keys, key); + if (ret != IGRAPH_SUCCESS) { + IGRAPH_FINALLY_EXIT(); + IGRAPH_ERROR("Cannot get element from trie.", ret); + } + ret = igraph_i_trie_get_node(&t->node, key, t->maxvalue + 1, id); + if (ret != IGRAPH_SUCCESS) { + igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); /* shrinks, error safe */ + IGRAPH_FINALLY_EXIT(); + 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); /* shrinks, error safe */ + } + IGRAPH_FINALLY_EXIT(); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup igraphtrie + * \brief Search/insert a string of given length in a trie. + * + * This function is identical to \ref igraph_trie_get(), except that + * it takes a string of a given length as input instead of a null-terminated + * string. + * + * \param t The trie. + * \param key The string to search for. If not found, it will be inserted. + * \param length The length of \p key. + * \param id The index of the string is stored here. + * \return Error code, usually \c IGRAPH_ENOMEM. + */ + +igraph_error_t igraph_trie_get_len( + igraph_trie_t *t, const char *key, + igraph_integer_t length, + igraph_integer_t *id) { + + char *tmp = strndup(key, length); + IGRAPH_CHECK_OOM(tmp, "Cannot get from trie."); + IGRAPH_FINALLY(igraph_free, tmp); + IGRAPH_CHECK(igraph_trie_get(t, tmp, id)); + IGRAPH_FREE(tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup igraphtrie + * \brief Search in a trie. + * + * This variant does not add \p key to the trie if it does not exist. + * In this case, a negative \p id is returned. + * + * \param t The trie. + * \param key The string to search for. + * \param id If \p key is found, its index is stored here. Otherwise, + * a negative value is returned. + * \param Error code. + */ + +igraph_error_t igraph_trie_check(igraph_trie_t *t, const char *key, igraph_integer_t *id) { + IGRAPH_CHECK(igraph_i_trie_get_node(&t->node, key, -1, id)); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup igraphtrie + * \brief Get an element of a trie based on its index. + * + * \param t The trie. + * \param idx The index of the string. It is not checked that it is within range. + * \return The string with the given index. If the trie does not store the keys for + * reverse lookup, \c NULL is returned. + */ + +const char* igraph_trie_idx(igraph_trie_t *t, igraph_integer_t idx) { + if (! t->storekeys) { + return NULL; + } + return igraph_strvector_get(&t->keys, idx); +} + +/** + * \ingroup igraphtrie + * \brief Returns the size of a trie. + * + * \param t The trie. + * \return The size of the trie, i.e. one larger than the maximum index. + */ + +igraph_integer_t igraph_trie_size(igraph_trie_t *t) { + return t->maxvalue + 1; +} + +/* Hmmm, very dirty.... */ + +/** + * \ingroup igraphtrie + * \brief Retrieves all the keys from the trie. + * + * + * Note that the returned pointer is a \em borrowed reference into the internal + * string vector of the trie. Do \em not modify it and do \em not use it after + * the trie was destroyed. + * + * \param t The trie. + * \return The borrowed reference. + */ + +const igraph_strvector_t* igraph_i_trie_borrow_keys(igraph_trie_t *t) { + return &t->keys; +} diff --git a/src/core/trie.h b/src/core/trie.h new file mode 100644 index 0000000..3bfcb47 --- /dev/null +++ b/src/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_int_t values; +} igraph_trie_node_t; + +typedef struct s_igraph_trie { + igraph_trie_node_t node; + igraph_integer_t 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 igraph_error_t 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 igraph_error_t igraph_trie_get(igraph_trie_t *t, const char *key, igraph_integer_t *id); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_trie_check(igraph_trie_t *t, const char *key, igraph_integer_t *id); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_trie_get_len(igraph_trie_t *t, const char *key, igraph_integer_t length, + igraph_integer_t *id); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE const char* igraph_trie_idx(igraph_trie_t *t, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_trie_size(igraph_trie_t *t); + +const igraph_strvector_t* igraph_i_trie_borrow_keys(igraph_trie_t *t); + +__END_DECLS + +#endif diff --git a/src/core/typed_list.pmt b/src/core/typed_list.pmt new file mode 100644 index 0000000..2ec3a41 --- /dev/null +++ b/src/core/typed_list.pmt @@ -0,0 +1,1101 @@ +/* -*- 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 + +*/ + +#include /* memmove */ + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#if defined(VECTOR_LIST) + /* It was indicated that every item in a list is a vector of the base type + * so let's define ITEM_TYPE appropriately */ + #define ITEM_TYPE BASE_VECTOR + + /* Define the macro that creates the name of a function that refers to a single + * _item_ in the vector */ + #if defined(BASE_IGRAPH_REAL) + #define ITEM_FUNCTION(f) CONCAT2x(igraph_vector,f) + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define ITEM_FUNCTION(f) CONCAT2x(igraph_vector_bool,f) + #else + #define ITEM_FUNCTION(f) CONCAT3(igraph_vector,SHORT,f) + #endif +#elif defined(MATRIX_LIST) + /* It was indicated that every item in a list is a matrix of the base type + * so let's define ITEM_TYPE appropriately */ + #define ITEM_TYPE BASE_MATRIX + + /* Define the macro that creates the name of a function that refers to a single + * _item_ in the matrix */ + #if defined(BASE_IGRAPH_REAL) + #define ITEM_FUNCTION(f) CONCAT2x(igraph_matrix,f) + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define ITEM_FUNCTION(f) CONCAT2x(igraph_matrix_bool,f) + #else + #define ITEM_FUNCTION(f) CONCAT3(igraph_matrix,SHORT,f) + #endif +#else + #define ITEM_TYPE BASE + + /* Define the macro that creates the name of a function that refers to a single + * _item_ in the vector */ + #if defined(BASE_GRAPH) + #define ITEM_FUNCTION(f) CONCAT2x(igraph,f) + #endif + #if defined(BASE_BITSET) + #define ITEM_FUNCTION(f) CONCAT2x(igraph_bitset,f) + #endif +#endif + +static igraph_error_t INTERNAL_FUNCTION(init_item)(const TYPE* list, ITEM_TYPE* item); +static igraph_error_t INTERNAL_FUNCTION(copy_item)(ITEM_TYPE* dest, const ITEM_TYPE* source); +static void INTERNAL_FUNCTION(destroy_item)(ITEM_TYPE* item); + +static igraph_error_t INTERNAL_FUNCTION(init_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end); +static void INTERNAL_FUNCTION(destroy_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end); +static igraph_error_t INTERNAL_FUNCTION(expand_if_full)(TYPE* list); +static int INTERNAL_FUNCTION(sort_ind_cmp)(void *thunk, const void *p1, const void *p2); + +/** + * \ingroup vector_list + * \function igraph_vector_list_init + * \brief Initializes a list of vectors (constructor). + * + * + * This function constructs a list of vectors of the given size, and initializes + * each vector in the newly created list to become an empty vector. + * + * + * Vector objects initialized by this function are \em owned by the list, and + * they will be destroyed automatically when the list is destroyed with + * \ref igraph_vector_list_destroy(). + * + * \param v Pointer to a not yet initialized list of vectors. + * \param size The size of the list. + * \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 and initialize the corresponding vectors; + * n is the number of elements. + */ + +igraph_error_t FUNCTION(init)(TYPE* v, igraph_integer_t size) { + igraph_integer_t alloc_size = size > 0 ? size : 1; + IGRAPH_ASSERT(size >= 0); + v->stor_begin = IGRAPH_CALLOC(alloc_size, ITEM_TYPE); + if (v->stor_begin == 0) { + IGRAPH_ERROR("Cannot initialize list.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + v->stor_end = v->stor_begin + alloc_size; + v->end = v->stor_begin + size; + + IGRAPH_CHECK(INTERNAL_FUNCTION(init_slice)(v, v->stor_begin, v->end)); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_destroy + * \brief Destroys a list of vectors object. + * + * + * All lists initialized by \ref igraph_vector_list_init() should be properly + * destroyed by this function. A destroyed list of vectors needs to be + * reinitialized by \ref igraph_vector_list_init() if you want to use it again. + * + * + * Vectors that are in the list when it is destroyed are also destroyed + * implicitly. + * + * \param v Pointer to the (previously initialized) list object to + * destroy. + * + * Time complexity: operating system dependent. + */ + +void FUNCTION(destroy)(TYPE* v) { + IGRAPH_ASSERT(v != 0); + + if (v->stor_begin != 0) { + FUNCTION(clear)(v); + IGRAPH_FREE(v->stor_begin); + v->stor_begin = NULL; + } +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_capacity + * \brief Returns the allocated capacity of the list. + * + * Note that this might be different from the size of the list (as + * queried by \ref igraph_vector_list_size()), and specifies how many vectors + * the list can hold, without reallocation. + * + * \param v Pointer to the (previously initialized) list object to query. + * \return The allocated capacity. + * + * \sa \ref igraph_vector_list_size(). + * + * Time complexity: O(1). + */ + +igraph_integer_t FUNCTION(capacity)(const TYPE* v) { + return v->stor_end - v->stor_begin; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_reserve + * \brief Reserves memory for a list. + * + * + * \a igraph lists are flexible, they can grow and shrink. Growing + * however occasionally needs the data in the list to be copied. + * In order to avoid this, you can call this function to reserve space for + * future growth of the list. + * + * + * Note that this function does \em not change the size of the list, neither + * does it initialize any new vectors. Let us see a small example to clarify + * things: if you reserve space for 100 elements and the size of your + * list was (and still is) 60, then you can surely add additional 40 + * new vectors to your list before it will be copied. + * \param v The list object. + * \param capacity The new \em allocated size of the list. + * \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 list. + */ + +igraph_error_t FUNCTION(reserve)(TYPE* v, igraph_integer_t capacity) { + igraph_integer_t current_capacity; + ITEM_TYPE *tmp; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); + + current_capacity = FUNCTION(capacity)(v); + + if (capacity <= current_capacity) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(v->stor_begin, capacity, ITEM_TYPE); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for list."); + + v->end = tmp + (v->end - v->stor_begin); + v->stor_begin = tmp; + v->stor_end = v->stor_begin + capacity; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_empty + * \brief Decides whether the size of the list is zero. + * + * \param v The list object. + * \return True if the size of the list is zero and false otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t FUNCTION(empty)(const TYPE* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin == v->end; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_size + * \brief Returns the size (=length) of the vector. + * + * \param v The list object + * \return The size of the list. + * + * Time complexity: O(1). + */ + +igraph_integer_t FUNCTION(size)(const TYPE* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->end - v->stor_begin; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_resize + * \brief Resize the list of vectors. + * + * + * Note that this function does not free any memory, just sets the + * size of the list to the given one. It can on the other hand + * allocate more memory if the new size is larger than the previous + * one. + * + * + * When the new size is larger than the current size, the newly added + * vectors in the list are initialized to empty vectors. When the new + * size is smaller than the current size, the vectors that were removed + * from the end of the list are destroyed automatically. + * + * \param v The list object + * \param new_size The new size of the list. + * \return Error code, + * \c IGRAPH_ENOMEM if there is not enough + * memory. Note that this function \em never returns an error + * if the list is made smaller. + * \sa \ref igraph_vector_list_reserve() for allocating memory for future + * extensions of a list. + * + * Time complexity: O(m) if the new size is smaller (m is the number of items + * that were removed from the list), operating system dependent if the new + * size is larger. In the latter case it is usually around O(n), where n is the + * new size of the vector. + */ +igraph_error_t FUNCTION(resize)(TYPE* v, igraph_integer_t new_size) { + igraph_integer_t old_size; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + IGRAPH_CHECK(FUNCTION(reserve)(v, new_size)); + + old_size = FUNCTION(size)(v); + + if (old_size < new_size) { + IGRAPH_CHECK(INTERNAL_FUNCTION(init_slice)(v, v->stor_begin + old_size, v->stor_begin + new_size)); + } else if (old_size > new_size) { + INTERNAL_FUNCTION(destroy_slice)(v, v->stor_begin + new_size, v->stor_begin + old_size); + } + + v->end = v->stor_begin + new_size; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_list_clear + * \brief Removes all elements from a list of vectors. + * + * + * This function sets the size of the list to zero, and it also destroys all + * the vectors that were placed in the list before clearing it. + * + * \param v The list object. + * + * Time complexity: O(n), n is the number of items being deleted. + */ +void FUNCTION(clear)(TYPE* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + INTERNAL_FUNCTION(destroy_slice)(v, v->stor_begin, v->end); + v->end = v->stor_begin; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_get_ptr + * \brief Retrieve the address of a vector in the vector list. + * \param v The list object. + * \param pos The position of the vector in the list. The position of the first + * vector is zero. + * \return A pointer to the vector. It remains valid as long as the underlying + * list of vectors is not modified. + * + * Time complexity: O(1). + */ +ITEM_TYPE* FUNCTION(get_ptr)(const TYPE* v, igraph_integer_t pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin + pos; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_set + * \brief Sets the vector at the given index in the list. + * + * + * This function destroys the vector that is already at the given index \p pos + * in the list, and replaces it with the vector pointed to by \p e. + * The ownership of the vector pointed to by \p e is taken by the list so + * the user is not responsible for destroying \p e any more; it will be + * destroyed when the list itself is destroyed or if \p e gets removed from the + * list without passing on the ownership to somewhere else. + * + * \param v The list object. + * \param pos The index to modify in the list. + * \param e The vector to set in the list. + * + * Time complexity: O(1). + */ +void FUNCTION(set)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { + INTERNAL_FUNCTION(destroy_item)(v->stor_begin + pos); + v->stor_begin[pos] = *e; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_replace + * \brief Replaces the vector at the given index in the list with another one. + * + * + * This function replaces the vector that is already at the given index \p pos + * in the list with the vector pointed to by \p e. The ownership of the vector + * pointed to by \p e is taken by the list so the user is not responsible for + * destroying \p e any more. At the same time, the ownership of the vector that + * \em was in the list at position \p pos will be transferred to the caller and + * \p e will be updated to point to it, so the caller becomes responsible for + * destroying it when it does not need the vector any more. + * + * \param v The list object. + * \param pos The index to modify in the list. + * \param e The vector to swap with the one already in the list. + * + * Time complexity: O(1). + */ +void FUNCTION(replace)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { + ITEM_TYPE old_value = *(FUNCTION(get_ptr)(v, pos)); + v->stor_begin[pos] = *e; + *e = old_value; +} + +/** + * \function igraph_vector_list_swap + * \brief Swaps all elements of two vector lists. + * + * \param v1 The first list. + * \param v2 The second list. + * \return Error code. + * + * Time complexity: O(1). + */ + +igraph_error_t FUNCTION(swap)(TYPE *v1, TYPE *v2) { + TYPE tmp; + + tmp = *v1; + *v1 = *v2; + *v2 = tmp; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_list_swap_elements + * \brief Swap two elements in a vector list. + * + * Note that currently no range checking is performed. + * \param v The input list. + * \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). + */ + +igraph_error_t FUNCTION(swap_elements)(TYPE *v1, igraph_integer_t i, igraph_integer_t j) { + ITEM_TYPE tmp = v1->stor_begin[i]; + v1->stor_begin[i] = v1->stor_begin[j]; + v1->stor_begin[j] = tmp; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_tail_ptr + * \brief Retrieve the address of the last vector in the vector list. + * \param v The list object. + * \return A pointer to the last vector in the list, or \c NULL if the list + * is empty. + * + * Time complexity: O(1). + */ +ITEM_TYPE* FUNCTION(tail_ptr)(const TYPE *v) { + igraph_integer_t size = FUNCTION(size)(v); + return size > 0 ? FUNCTION(get_ptr)(v, size - 1) : 0; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_discard + * \brief Discard the item at the given index in the vector list. + * + * + * This function removes the vector at the given index from the list, and + * moves all subsequent items in the list by one slot to the left to fill + * the gap. The vector that was removed from the list is destroyed automatically. + * + * \param v The list object. + * \param index Index of the item to be discarded and destroyed. + * \sa \ref igraph_vector_list_discard_fast() if you do not care about the + * order of the items in the list, \ref igraph_vector_list_remove() if you + * want to gain ownership of the item that was removed instead of destroying it. + * + * Time complexity: O(n), where n is the number of items in the list. + */ +void FUNCTION(discard)(TYPE* v, igraph_integer_t index) { + igraph_integer_t size = FUNCTION(size)(v); + + if (size > 0) { + INTERNAL_FUNCTION(destroy_item)(v->stor_begin + index); + memmove(v->stor_begin + index, v->stor_begin + index + 1, sizeof(ITEM_TYPE) * (size - index - 1)); + v->end -= 1; + } +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_discard_back + * \brief Discard the last item in the vector list. + * + * + * This function removes the last vector from the list and destroys it. + * + * \param v The list object. + * + * Time complexity: O(1). + */ +void FUNCTION(discard_back)(TYPE* v) { + igraph_integer_t size = FUNCTION(size)(v); + if (size > 0) { + INTERNAL_FUNCTION(destroy_item)(v->end - 1); + v->end -= 1; + } +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_discard_fast + * \brief Discard the item at the given index in the vector list and move the last item to its place. + * + * + * This function removes the vector at the given index from the list, and + * moves the last item in the list to \p index to fill the gap. The vector that + * was removed from the list is destroyed automatically. + * + * \param v The list object. + * \param index Index of the item to be discarded and destroyed. + * \sa \ref igraph_vector_list_discard() if you want to preserve the order of the + * items in the list, \ref igraph_vector_list_remove_fast() if you want to gain + * ownership of the item that was removed instead of destroying it. + * + * Time complexity: O(1). + */ +void FUNCTION(discard_fast)(TYPE* v, igraph_integer_t index) { + igraph_integer_t size = FUNCTION(size)(v); + + if (size > 0) { + INTERNAL_FUNCTION(destroy_item)(v->stor_begin + index); + v->end -= 1; + v->stor_begin[index] = *(v->end); + } +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_push_back + * \brief Append an existing vector to the list, transferring ownership. + * + * + * This function resizes the list to be one element longer, and sets the very last + * element in the list to the specified vector \p e . The list takes ownership + * of the vector so the user is not responsible for freeing \p e any more; + * the vector will be destroyed when the list itself is destroyed or if \p e gets + * removed from the list without passing on the ownership to somewhere else. + * + * \param v The list object. + * \param e Pointer to the vector to append to the list. + * \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_list_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). + */ +igraph_error_t FUNCTION(push_back)(TYPE* v, ITEM_TYPE* e) { + IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); + *(v->end) = *e; + v->end += 1; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_push_back_copy + * \brief Append the copy of a vector to the list. + * + * + * This function resizes the list to be one element longer, and copies the + * specified vector given as an argument to the last element. The newly added + * element is owned by the list, but the ownership of the original vector is + * retained at the caller. + * + * \param v The list object. + * \param e Pointer to the vector to copy to the end of the list. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: same as \ref igraph_vector_list_push_back() plus the time + * needed to copy the vector (which is O(n) for n elements in the vector). + */ +igraph_error_t FUNCTION(push_back_copy)(TYPE* v, const ITEM_TYPE* e) { + ITEM_TYPE copy; + IGRAPH_CHECK(INTERNAL_FUNCTION(copy_item)(©, e)); + IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); + IGRAPH_CHECK(FUNCTION(push_back)(v, ©)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_push_back_new + * \brief Append a new vector to the list. + * + * + * This function resizes the list to be one element longer. The newly added + * element will be an empty vector that is owned by the list. A pointer to + * the newly added element is returned in the last argument if it is not + * \c NULL . + * + * \param v The list object. + * \param result Pointer to a vector pointer; this will be updated to point to + * the newly added vector. May be \c NULL if you do not need a pointer + * to the newly added vector. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: same as \ref igraph_vector_list_push_back(). + */ +igraph_error_t FUNCTION(push_back_new)(TYPE* v, ITEM_TYPE** e) { + IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); + IGRAPH_CHECK(INTERNAL_FUNCTION(init_item)(v, v->end)); + if (e) { + *e = v->end; + } + v->end += 1; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_insert + * \brief Insert an existing vector into the list, transferring ownership. + * + * + * This function inserts \p e into the list at the given index, moving other + * items towards the end of the list as needed. The list takes ownership + * of the vector so the user is not responsible for freeing \p e any more; + * the vector will be destroyed when the list itself is destroyed or if \p e gets + * removed from the list without passing on the ownership to somewhere else. + * + * \param v The list object. + * \param pos The position where the new element is to be inserted. + * \param e Pointer to the vector to insert into the list. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: O(n). + */ +igraph_error_t FUNCTION(insert)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { + igraph_integer_t size = FUNCTION(size)(v); + IGRAPH_ASSERT(0 <= pos && pos <= size); + IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); + if (pos < size) { + memmove(v->stor_begin + pos + 1, v->stor_begin + pos, sizeof(ITEM_TYPE) * (size - pos)); + } + v->end += 1; + v->stor_begin[pos] = *e; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_insert_copy + * \brief Insert the copy of a vector to the list. + * + * + * This function inserts a copy of \p e into the list at the given index, moving + * other items towards the end of the list as needed. The newly added + * element is owned by the list, but the ownership of the original vector is + * retained at the caller. + * + * \param v The list object. + * \param pos The position where the new element is to be inserted. + * \param e Pointer to the vector to copy to the end of the list. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: same as \ref igraph_vector_list_insert() plus the time + * needed to copy the vector (which is O(n) for n elements in the vector). + */ +igraph_error_t FUNCTION(insert_copy)(TYPE* v, igraph_integer_t pos, const ITEM_TYPE* e) { + ITEM_TYPE copy; + IGRAPH_CHECK(INTERNAL_FUNCTION(copy_item)(©, e)); + IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); + IGRAPH_CHECK(FUNCTION(insert)(v, pos, ©)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_insert_new + * \brief Insert a new vector into the list. + * + * + * This function inserts a newly created empty vector into the list at the given + * index, moving other items towards the end of the list as needed. The newly + * added vector is owned by the list. A pointer to the new element is returned + * in the last argument if it is not \c NULL . + * + * \param v The list object. + * \param pos The position where the new element is to be inserted. + * \param result Pointer to a vector pointer; this will be updated to point to + * the newly added vector. May be \c NULL if you do not need a pointer + * to the newly added vector. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: same as \ref igraph_vector_list_push_back(). + */ +igraph_error_t FUNCTION(insert_new)(TYPE* v, igraph_integer_t pos, ITEM_TYPE** e) { + ITEM_TYPE copy; + IGRAPH_CHECK(INTERNAL_FUNCTION(init_item)(v, ©)); + IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); + IGRAPH_CHECK(FUNCTION(insert)(v, pos, ©)); + IGRAPH_FINALLY_CLEAN(1); + if (e) { + *e = FUNCTION(get_ptr)(v, pos); + } + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_remove + * \brief Remove the item at the given index from the vector list and transfer ownership to the caller. + * + * + * This function removes the vector at the given index from the list, and + * moves all subsequent items in the list by one slot to the left to fill + * the gap. The vector that was removed from the list is returned in \p e + * and its ownership is passed back to the caller; in other words, the caller + * becomes responsible for destroying the vector when it is not needed any more. + * + * \param v The list object. + * \param index Index of the item to be removed. + * \param result Pointer to an \c igraph_vector_t object; it will be updated to the + * item that was removed from the list. Ownership of this vector is + * passed on to the caller. It is an error to supply a null pointer here. + * \sa \ref igraph_vector_list_discard() if you are not interested in the item + * that was removed, \ref igraph_vector_list_remove_fast() if you do not care + * about the order of the items in the list. + * + * Time complexity: O(n), where n is the number of items in the list. + */ +igraph_error_t FUNCTION(remove)(TYPE* v, igraph_integer_t index, ITEM_TYPE* result) { + igraph_integer_t size = FUNCTION(size)(v); + + IGRAPH_ASSERT(result != 0); + + if (index < 0 || index >= size) { + IGRAPH_ERROR("invalid index when removing item", IGRAPH_EINVAL); + } + + *result = *(FUNCTION(get_ptr)(v, index)); + + memmove(v->stor_begin + index, v->stor_begin + index + 1, sizeof(ITEM_TYPE) * (size - index - 1)); + v->end -= 1; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_pop_back + * \brief Remove the last item from the vector list and transfer ownership to the caller. + * + * + * This function removes the last vector from the list. The vector that was + * removed from the list is returned and its ownership is passed back to the + * caller; in other words, the caller becomes responsible for destroying + * the vector when it is not needed any more. + * + * + * It is an error to call this function with an empty vector. + * + * \param v The list object. + * \param result Pointer to an \c igraph_vector_t object; it will be updated to the + * item that was removed from the list. Ownership of this vector is + * passed on to the caller. + * + * Time complexity: O(1). + */ +ITEM_TYPE FUNCTION(pop_back)(TYPE* v) { + IGRAPH_ASSERT(!FUNCTION(empty)(v)); + v->end -= 1; + return *(v->end); +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_remove_fast + * \brief Remove the item at the given index in the vector list, move the last item to its place and transfer ownership to the caller. + * + * + * This function removes the vector at the given index from the list, + * moves the last item in the list to \p index to fill the gap, and then + * transfers ownership of the removed vector back to the caller; in other words, + * the caller becomes responsible for destroying the vector when it is not + * needed any more. + * + * \param v The list object. + * \param index Index of the item to be removed. + * \param result Pointer to an \c igraph_vector_t object; it will be updated to the + * item that was removed from the list. Ownership of this vector is + * passed on to the caller. It is an error to supply a null pointer here. + * \sa \ref igraph_vector_list_remove() if you want to preserve the order of the + * items in the list, \ref igraph_vector_list_discard_fast() if you are not + * interested in the item that was removed. + * + * Time complexity: O(1). + */ +igraph_error_t FUNCTION(remove_fast)(TYPE* v, igraph_integer_t index, ITEM_TYPE* result) { + igraph_integer_t size = FUNCTION(size)(v); + + IGRAPH_ASSERT(result != 0); + + if (index < 0 || index >= size) { + IGRAPH_ERROR("invalid index when removing item", IGRAPH_EINVAL); + } + + *result = *(FUNCTION(get_ptr)(v, index)); + + v->end -= 1; + v->stor_begin[index] = *(v->end); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_permute + * \brief Permutes the elements of a list in place according to an index vector. + * + * + * This function takes a list \c v and a corresponding index vector \c index, + * and permutes the elements of \c v such that \c v[index[i]] is moved to become + * \c v[i] after the function is executed. + * + * + * It is an error to call this function with an index vector that does not + * represent a valid permutation. Each element in the index vector must be + * between 0 and the length of the list minus one (inclusive), and each such + * element must appear only once. The function does not attempt to validate the + * index vector. Memory may be leaked if the index vector does not satisfy these + * conditions. + * + * + * The index vector that this function takes is compatible with the index vector + * returned from \ref igraph_vector_list_sort_ind(); passing in the index vector + * from \ref igraph_vector_list_sort_ind() will sort the original vector. + * + * \param v the list to permute + * \param index the index vector + * + * Time complexity: O(n), the number of items in the list. + */ +igraph_error_t FUNCTION(permute)(TYPE* v, const igraph_vector_int_t* index) { + ITEM_TYPE* work; + igraph_integer_t i, size; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(index != NULL); + IGRAPH_ASSERT(index->stor_begin != NULL); + + size = igraph_vector_int_size(index); + IGRAPH_ASSERT(FUNCTION(size)(v) == size); + + work = IGRAPH_CALLOC(size, ITEM_TYPE); + if (work == 0) { + IGRAPH_ERROR("Cannot permute list.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + for (i = 0; i < size; i++) { + work[i] = v->stor_begin[VECTOR(*index)[i]]; + } + + memcpy(v->stor_begin, work, sizeof(ITEM_TYPE) * size); + + IGRAPH_FREE(work); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_sort + * \brief Sorts the elements of the list into ascending order. + * + * \param v Pointer to an initialized list object. + * \param cmp A comparison function that takes pointers to two vectors and + * returns zero if the two vectors are considered equal, any negative + * number if the first vector is smaller and any positive number if the + * second vector is smaller. + * \return Error code. + * + * Time complexity: O(n log n) for n elements. + */ +void FUNCTION(sort)(TYPE *v, int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*)) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + igraph_qsort( + v->stor_begin, FUNCTION(size)(v), sizeof(ITEM_TYPE), + (int(*)(const void*, const void*))cmp + ); +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_sort_ind + * \brief Returns a permutation of indices that sorts the list. + * + * Takes an unsorted list \p v as input and computes an array of + * indices \p inds such that v[ inds[i] ], with i increasing from 0, is + * an ordered array according to the comparison function \p cmp. The order of + * indices for identical elements is not defined. + * + * \param v the list to be sorted + * \param inds the output array of indices. This must be initialized, + * but will be resized + * \param cmp A comparison function that takes pointers to two vectors and + * returns zero if the two vectors are considered equal, any negative + * number if the first vector is smaller and any positive number if the + * second vector is smaller. + * \return Error code. + * + * Time complexity: O(n log n) for n elements. + */ +igraph_error_t FUNCTION(sort_ind)( + TYPE *v, igraph_vector_int_t *inds, + int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) +) { + igraph_integer_t i, n = FUNCTION(size)(v); + ITEM_TYPE **vind, *first; + + IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); + if (n == 0) { + return IGRAPH_SUCCESS; + } + + vind = IGRAPH_CALLOC(n, ITEM_TYPE*); + if (vind == 0) { + IGRAPH_ERROR("igraph_vector_list_sort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + for (i = 0; i < n; i++) { + vind[i] = v->stor_begin + i; + } + first = vind[0]; + igraph_qsort_r( + vind, n, sizeof(ITEM_TYPE*), (void*) cmp, + (int(*)(void*, const void*, const void*)) INTERNAL_FUNCTION(sort_ind_cmp) + ); + for (i = 0; i < n; i++) { + VECTOR(*inds)[i] = vind[i] - first; + } + IGRAPH_FREE(vind); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_remove_consecutive_duplicates + * \brief Removes consecutive duplicates from a vector list. + * + * Removes consecutive duplicate vectors from the list. Optionally, a custom + * equivalence relation may be used to determine when two vectors are + * considered to be the same. + * + * + * An efficient way to remove all duplicates, not just consecutive ones, + * is to first sort the vector list using \ref igraph_vector_list_sort(), + * then use this function. This will of course re-order the list. + * + * \param v The list to remove consecutive duplicates from. + * \param eq A comparison function that takes pointers to two vectors and + * returns true if they are equivalent. It is assumed that it implements + * a transitive, but not necessarily symmetric relation. + * Use \ref igraph_vector_all_e() to consider vector equivalent only + * when their contents are identical. + * + * \sa \ref igraph_vector_list_sort() + * + * Time complexity: O(n), the number of items in the list. + */ +void FUNCTION(remove_consecutive_duplicates)( + TYPE *v, igraph_bool_t (*eq)(const ITEM_TYPE*, const ITEM_TYPE*) +) { + igraph_integer_t i, j, n = FUNCTION(size)(v); + ITEM_TYPE *p = v->stor_begin; + + if (n < 2) { + return; + } + + for (i=0, j=0; i < n-1; ++i) { + if (eq(&p[i], &p[i+1])) { + INTERNAL_FUNCTION(destroy_item)(&p[i]); + } else { + p[j++] = p[i]; + } + } + p[j++] = p[n-1]; + + v->end = p + j; +} + +/** + * \function igraph_vector_list_reverse + * \brief Reverses the elements of a vector list. + * + * The first element will be last, the last element will be + * first, etc. + * \param v The input vector list. + * \return Error code, currently always \c IGRAPH_SUCCESS. + * + * Time complexity: O(n), the number of elements. + */ +igraph_error_t FUNCTION(reverse)(TYPE *v) { + igraph_integer_t n = FUNCTION(size)(v), n2 = n / 2; + igraph_integer_t i, j; + for (i = 0, j = n - 1; i < n2; i++, j--) { + ITEM_TYPE tmp; + tmp = VECTOR(*v)[i]; + VECTOR(*v)[i] = VECTOR(*v)[j]; + VECTOR(*v)[j] = tmp; + } + return IGRAPH_SUCCESS; +} + +/* ************************************************************************ */ + +#ifndef CUSTOM_INIT_DESTROY + +static igraph_error_t INTERNAL_FUNCTION(init_item)(const TYPE* list, ITEM_TYPE* item) { + IGRAPH_UNUSED(list); + return ITEM_FUNCTION(init)(item, 0); +} + +static igraph_error_t INTERNAL_FUNCTION(copy_item)(ITEM_TYPE* dest, const ITEM_TYPE* source) { + return ITEM_FUNCTION(init_copy)(dest, source); +} + +static void INTERNAL_FUNCTION(destroy_item)(ITEM_TYPE* item) { + ITEM_FUNCTION(destroy)(item); +} + +#endif + +/* ************************************************************************ */ + +static igraph_error_t INTERNAL_FUNCTION(init_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end) { + ITEM_TYPE* current; + igraph_error_t retval; + + for (current = start; current < end; current++) { + retval = INTERNAL_FUNCTION(init_item)(list, current); + if (retval) { + INTERNAL_FUNCTION(destroy_slice)(list, start, current); + IGRAPH_CHECK(retval); + } + } + + return IGRAPH_SUCCESS; +} + +static void INTERNAL_FUNCTION(destroy_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end) { + IGRAPH_UNUSED(list); + for (; start < end; start++) { + INTERNAL_FUNCTION(destroy_item)(start); + } +} + +/** + * Ensures that the vector has at least one extra slot at the end of its + * allocated storage area. + */ +static igraph_error_t INTERNAL_FUNCTION(expand_if_full)(TYPE* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + if (v->stor_end == v->end) { + igraph_integer_t old_size = FUNCTION(size)(v); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot add new item to list, already at maximum size.", IGRAPH_EOVERFLOW); + } + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(FUNCTION(reserve)(v, new_size)); + } + + return IGRAPH_SUCCESS; +} + +/** + * Helper function passed to qsort from igraph_vector_qsort_ind + */ +static int INTERNAL_FUNCTION(sort_ind_cmp)(void *thunk, const void *p1, const void *p2) { + int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) = (int (*)(const ITEM_TYPE*, const ITEM_TYPE*)) thunk; + ITEM_TYPE **pa = (ITEM_TYPE **) p1; + ITEM_TYPE **pb = (ITEM_TYPE **) p2; + return cmp(*pa, *pb); +} + +#undef ITEM_FUNCTION diff --git a/src/core/vector.c b/src/core/vector.c new file mode 100644 index 0000000..678c844 --- /dev/null +++ b/src/core/vector.c @@ -0,0 +1,708 @@ +/* -*- 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_vector.h" + +#include "igraph_complex.h" +#include "igraph_types.h" +#include "igraph_nongraph.h" + +#include + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#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/indheap.h" + +/** + * \ingroup vector + * \function igraph_vector_floor + * \brief Transform a real vector to an integer 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 integer 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. + */ +igraph_error_t igraph_vector_floor(const igraph_vector_t *from, igraph_vector_int_t *to) { + const igraph_integer_t n = igraph_vector_size(from); + + IGRAPH_CHECK(igraph_vector_int_resize(to, n)); + for (igraph_integer_t i = 0; i < n; i++) { + VECTOR(*to)[i] = floor(VECTOR(*from)[i]); + } + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_vector_round(const igraph_vector_t *from, igraph_vector_int_t *to) { + const igraph_integer_t n = igraph_vector_size(from); + + IGRAPH_CHECK(igraph_vector_int_resize(to, n)); + for (igraph_integer_t i = 0; i < n; i++) { + VECTOR(*to)[i] = round(VECTOR(*from)[i]); + } + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_vector_order2(igraph_vector_t *v) { + igraph_indheap_t heap; + + IGRAPH_CHECK(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 IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_int_pair_order + * \brief Calculates the order of the elements in a pair of integer vectors of equal length. + * + * The smallest element will have order zero, the second smallest + * order one, etc. + * + * \param v The original \ref igraph_vector_int_t object. + * \param v2 A secondary key, another \ref igraph_vector_int_t object. + * \param res An initialized \ref igraph_vector_int_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() + */ + +igraph_error_t igraph_vector_int_pair_order(const igraph_vector_int_t* v, + const igraph_vector_int_t* v2, + igraph_vector_int_t* res, igraph_integer_t nodes) { + igraph_integer_t edges = igraph_vector_int_size(v); + igraph_vector_int_t ptr; + igraph_vector_int_t rad; + igraph_integer_t i, j; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, nodes + 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, edges); + IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); + + for (i = 0; i < edges; i++) { + igraph_integer_t radix = VECTOR(*v2)[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) { + igraph_integer_t next = VECTOR(ptr)[i] - 1; + VECTOR(*res)[j++] = next; + while (VECTOR(rad)[next] != 0) { + next = VECTOR(rad)[next] - 1; + VECTOR(*res)[j++] = next; + } + } + } + + igraph_vector_int_null(&ptr); + igraph_vector_int_null(&rad); + + for (i = 0; i < edges; i++) { + igraph_integer_t edge = VECTOR(*res)[edges - i - 1]; + igraph_integer_t radix = 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) { + igraph_integer_t next = VECTOR(ptr)[i] - 1; + VECTOR(*res)[j++] = next; + while (VECTOR(rad)[next] != 0) { + next = VECTOR(rad)[next] - 1; + VECTOR(*res)[j++] = next; + } + } + } + + igraph_vector_int_destroy(&ptr); + igraph_vector_int_destroy(&rad); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_vector_int_order1(const igraph_vector_int_t* v, + igraph_vector_int_t* res, + igraph_integer_t nodes) { + igraph_integer_t edges = igraph_vector_int_size(v); + igraph_vector_int_t ptr; + igraph_vector_int_t rad; + igraph_integer_t i, j; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, nodes + 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, edges); + IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); + + for (i = 0; i < edges; i++) { + igraph_integer_t radix = 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) { + igraph_integer_t next = VECTOR(ptr)[i] - 1; + res->stor_begin[j++] = next; + while (VECTOR(rad)[next] != 0) { + next = VECTOR(rad)[next] - 1; + res->stor_begin[j++] = next; + } + } + } + + igraph_vector_int_destroy(&ptr); + igraph_vector_int_destroy(&rad); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_vector_rank( + const igraph_vector_t *v, igraph_vector_int_t *res, igraph_integer_t nodes) { + + igraph_vector_int_t rad; + igraph_vector_int_t ptr; + igraph_integer_t edges = igraph_vector_size(v); + igraph_integer_t i, c = 0; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, edges); + IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); + + for (i = 0; i < edges; i++) { + igraph_integer_t elem = VECTOR(*v)[i]; + VECTOR(ptr)[i] = VECTOR(rad)[elem]; + VECTOR(rad)[elem] = i + 1; + } + + for (i = 0; i < nodes; i++) { + igraph_integer_t p = VECTOR(rad)[i]; + while (p != 0) { + VECTOR(*res)[p - 1] = c++; + p = VECTOR(ptr)[p - 1]; + } + } + + igraph_vector_int_destroy(&ptr); + igraph_vector_int_destroy(&rad); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_vector_int_rank( + const igraph_vector_int_t *v, igraph_vector_int_t *res, igraph_integer_t nodes) { + + igraph_vector_int_t rad; + igraph_vector_int_t ptr; + igraph_integer_t edges = igraph_vector_int_size(v); + igraph_integer_t i, c = 0; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, edges); + IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); + + for (i = 0; i < edges; i++) { + igraph_integer_t elem = VECTOR(*v)[i]; + VECTOR(ptr)[i] = VECTOR(rad)[elem]; + VECTOR(rad)[elem] = i + 1; + } + + for (i = 0; i < nodes; i++) { + igraph_integer_t p = VECTOR(rad)[i]; + while (p != 0) { + VECTOR(*res)[p - 1] = c++; + p = VECTOR(ptr)[p - 1]; + } + } + + igraph_vector_int_destroy(&ptr); + igraph_vector_int_destroy(&rad); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_complex_real + * \brief Gives the real part of a complex vector. + * + * \param v Pointer to a complex vector. + * \param real Pointer to an initialized vector. The result will be stored here. + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t igraph_vector_complex_real(const igraph_vector_complex_t *v, + igraph_vector_t *real) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_complex_imag + * \brief Gives the imaginary part of a complex vector. + * + * \param v Pointer to a complex vector. + * \param real Pointer to an initialized vector. The result will be stored here. + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t igraph_vector_complex_imag(const igraph_vector_complex_t *v, + igraph_vector_t *imag) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_complex_realimag + * \brief Gives the real and imaginary parts of a complex vector. + * + * \param v Pointer to a complex vector. + * \param real Pointer to an initialized vector. The real part will be stored here. + * \param imag Pointer to an initialized vector. The imaginary part will be stored here. + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t igraph_vector_complex_realimag(const igraph_vector_complex_t *v, + igraph_vector_t *real, + igraph_vector_t *imag) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_complex_create + * \brief Creates a complex vector from a real and imaginary part. + * + * \param v Pointer to an uninitialized complex vector. + * \param real Pointer to the real part of the complex vector. + * \param imag Pointer to the imaginary part of the complex vector. + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t igraph_vector_complex_create(igraph_vector_complex_t *v, + const igraph_vector_t *real, + const igraph_vector_t *imag) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_complex_create_polar + * \brief Creates a complex matrix from a magnitude and an angle. + * + * \param m Pointer to an uninitialized complex vector. + * \param r Pointer to a real vector containing magnitudes. + * \param theta Pointer to a real vector containing arguments (phase angles). + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t igraph_vector_complex_create_polar(igraph_vector_complex_t *v, + const igraph_vector_t *r, + const igraph_vector_t *theta) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_complex_all_almost_e + * \brief Are all elements almost equal? + * + * Checks if the elements of two complex vectors are equal within a relative tolerance. + * + * \param lhs The first vector. + * \param rhs The second vector. + * \param eps Relative tolerance, see \ref igraph_complex_almost_equals() for details. + * \return True if the two vectors are almost equal, false if there is at least + * one differing element or if the vectors are not of the same size. + */ +igraph_bool_t igraph_vector_complex_all_almost_e(const igraph_vector_complex_t *lhs, + const igraph_vector_complex_t *rhs, + igraph_real_t eps) { + + igraph_integer_t n = igraph_vector_complex_size(lhs); + + if (lhs == rhs) { + return true; + } + + if (igraph_vector_complex_size(rhs) != n) { + return false; + } + + for (igraph_integer_t i=0; i < n; i++) { + if (! igraph_complex_almost_equals(VECTOR(*lhs)[i], VECTOR(*rhs)[i], eps)) + return false; + } + + return true; +} + +/** + * Deprecated in favour of \ref igraph_vector_all_almost_e() which uses + * relative tolerances. Will be removed in 0.11. + * + * Checks if two vectors are equal within an absolute tolerance. + */ +igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t tol) { + igraph_integer_t 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 false; + } 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 false; + } + } + return true; + } +} + +/** + * \function igraph_vector_all_almost_e + * \brief Are all elements almost equal? + * + * Checks if the elements of two vectors are equal within a relative tolerance. + * + * \param lhs The first vector. + * \param rhs The second vector. + * \param eps Relative tolerance, see \ref igraph_almost_equals() for details. + * \return True if the two vectors are almost equal, false if there is at least + * one differing element or if the vectors are not of the same size. + */ +igraph_bool_t igraph_vector_all_almost_e(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t eps) { + + igraph_integer_t n = igraph_vector_size(lhs); + + if (lhs == rhs) { + return true; + } + + if (igraph_vector_size(rhs) != n) { + return false; + } + + for (igraph_integer_t i=0; i < n; i++) { + if (! igraph_almost_equals(VECTOR(*lhs)[i], VECTOR(*rhs)[i], eps)) + return false; + } + + return true; +} + +/** + * \function igraph_vector_zapsmall + * \brief Replaces small elements of a vector by exact zeros. + * + * Vector elements which are smaller in magnitude than the given absolute + * tolerance will be replaced by exact zeros. The default tolerance + * corresponds to two-thirds of the representable digits of \type igraph_real_t, + * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. + * + * \param v The vector to process, it will be changed in-place. + * \param tol Tolerance value. Numbers smaller than this in magnitude will + * be replaced by zeros. Pass in zero to use the default tolerance. + * Must not be negative. + * \return Error code. + * + * \sa \ref igraph_vector_all_almost_e() and \ref igraph_almost_equals() to + * perform comparisons with relative tolerances. + */ +igraph_error_t igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) { + igraph_integer_t i, n = igraph_vector_size(v); + if (tol < 0.0) { + IGRAPH_ERROR("Tolerance must be positive or zero.", IGRAPH_EINVAL); + } + if (tol == 0.0) { + tol = pow(DBL_EPSILON, 2.0/3); + } + for (i = 0; i < n; i++) { + igraph_real_t val = VECTOR(*v)[i]; + if (val < tol && val > -tol) { + VECTOR(*v)[i] = 0.0; + } + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_complex_zapsmall + * \brief Replaces small elements of a complex vector by exact zeros. + * + * Similarly to \ref igraph_vector_zapsmall(), small elements will be replaced + * by zeros. The operation is performed separately on the real and imaginary + * parts of the numbers. This way, complex numbers with a large real part and + * tiny imaginary part will effectively be transformed to real numbers. + * The default tolerance + * corresponds to two-thirds of the representable digits of \type igraph_real_t, + * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. + * + * \param v The vector to process, it will be changed in-place. + * \param tol Tolerance value. Real and imaginary parts smaller than this in + * magnitude will be replaced by zeros. Pass in zero to use the default + * tolerance. Must not be negative. + * \return Error code. + * + * \sa \ref igraph_vector_complex_all_almost_e() and + * \ref igraph_complex_almost_equals() to perform comparisons with relative + * tolerances. + */ +igraph_error_t igraph_vector_complex_zapsmall(igraph_vector_complex_t *v, igraph_real_t tol) { + igraph_integer_t i, n = igraph_vector_complex_size(v); + if (tol < 0.0) { + IGRAPH_ERROR("Tolerance must be positive or zero.", IGRAPH_EINVAL); + } + if (tol == 0.0) { + tol = pow(DBL_EPSILON, 2.0/3); + } + for (i = 0; i < n; i++) { + igraph_complex_t val = VECTOR(*v)[i]; + igraph_bool_t zapped = false; + if (IGRAPH_REAL(val) < tol && IGRAPH_REAL(val) > -tol) { + IGRAPH_REAL(val) = 0.0; + zapped = true; + } + if (IGRAPH_IMAG(val) < tol && IGRAPH_IMAG(val) > -tol) { + IGRAPH_IMAG(val) = 0.0; + zapped = true; + } + if (zapped) { + VECTOR(*v)[i] = val; + } + } + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t 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 = isnan(*ptr); + } + 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 True if any element is NaN, false 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 (isnan(*ptr)) { + return true; + } + ptr++; + } + return false; +} + + +/** + * \ingroup vector + * \function igraph_vector_is_all_finite + * \brief Check if all elements are finite. + * + * \param v The \type igraph_vector_t object to check. + * \return True if none of the elements are infinite or NaN. + * + * Time complexity: O(n), the number of elements. + */ +igraph_bool_t igraph_vector_is_all_finite(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 (!isfinite(*ptr)) { + return false; + } + ptr++; + } + return true; +} diff --git a/src/core/vector.pmt b/src/core/vector.pmt new file mode 100644 index 0000000..8bc0dd0 --- /dev/null +++ b/src/core/vector.pmt @@ -0,0 +1,3344 @@ +/* -*- 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 "math/safe_intop.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 to (but much + * simpler than) the \type vector template in the C++ standard library. + * + * There are multiple variants of \type igraph_vector_t; the basic variant + * stores doubles, but there is also \type igraph_vector_int_t for integers (of + * type \type igraph_integer_t), \c igraph_vector_bool_t for booleans (of type + * \type igraph_bool_t) and so on. Vectors are used extensively in \a igraph; all + * functions that expect or return a list of numbers use \type igraph_vector_t or + * \type igraph_vector_int_t to achieve this. Integer vectors are typically used + * when the vector is supposed to hold vertex or edge identifiers, while + * \type igraph_vector_t is used when the vector is expected to hold fractional + * numbers or infinities. + * + * The \type igraph_vector_t type and its variants usually use O(n) space + * to store n elements. Sometimes they use 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 and its variants 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. + * + * Almost all of the functions described below for \type igraph_vector_t + * also exist for all the other vector type variants. These variants are not + * documented separately; you can simply replace \c vector with \c vector_int, + * \c vector_bool or something similar if you need a function for another + * variant. For instance, to initialize a vector of type \type igraph_vector_int_t, + * you need to use \ref igraph_vector_int_init() and not \ref igraph_vector_init(). + */ + +/** + * \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_init_copy() creates a new identical copy + * of an already existing and initialized vector. \ref + * igraph_vector_init_array() creates a vector by copying a regular C array. + * \ref igraph_vector_init_range() 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 must not 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. + */ + +igraph_error_t FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, igraph_integer_t size) { + igraph_integer_t alloc_size; + IGRAPH_ASSERT(size >= 0); + alloc_size = size > 0 ? size : 1; + + /* When this function fails, it should leave stor_begin set to NULL, + * so that vector_destroy() is still safe to call on the vector. + * This simplifies freeing partially initialized data structures, + * such as adjacency lists, when an error occurs mid-initialization. */ + v->stor_begin = IGRAPH_CALLOC(alloc_size, BASE); + if (v->stor_begin == NULL) { + IGRAPH_ERROR("Cannot initialize vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + v->stor_end = v->stor_begin + alloc_size; + v->end = v->stor_begin + size; + + return IGRAPH_SUCCESS; +} + +/** + * \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, except + * when \p length is zero. + * \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, igraph_integer_t length) { + static const BASE dummy = ZERO; + TYPE(igraph_vector) *v2 = (TYPE(igraph_vector)*)v; + + /* When the length is zero, we allow 'data' to be NULL. + * An igraph_vector_t may never contain a NULL pointer, + * thus we use a pointer to a dummy variable in this case. */ + if (length == 0) { + data = &dummy; + } else { + IGRAPH_ASSERT(data != NULL); + } + + 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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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 \ref 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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +#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_array() 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 != NULL); + /* vector_init() will leave stor_begin set to NULL when it fails. + * We handle these cases gracefully. */ + if (v->stor_begin != NULL) { + 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). + */ + +igraph_integer_t 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 capacity 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. + */ + +igraph_error_t FUNCTION(igraph_vector, reserve)(TYPE(igraph_vector)* v, igraph_integer_t capacity) { + igraph_integer_t current_capacity; + BASE *tmp; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); + + current_capacity = FUNCTION(igraph_vector, capacity)(v); + + if (capacity <= current_capacity) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(v->stor_begin, capacity, BASE); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for vector."); + + v->end = tmp + (v->end - v->stor_begin); + v->stor_begin = tmp; + v->stor_end = v->stor_begin + capacity; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_empty + * \brief Decides whether the size of the vector is zero. + * + * \param v The vector object. + * \return True if the size of the vector is zero and 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). + */ + +igraph_integer_t 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.) + */ + +igraph_error_t FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE e) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + if (v->stor_end == v->end) { + /* full, allocate more storage */ + igraph_integer_t old_size = FUNCTION(igraph_vector, size)(v); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to vector, already at maximum size.", IGRAPH_EOVERFLOW); + } + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(FUNCTION(igraph_vector, reserve)(v, new_size)); + } + + *(v->end) = e; + v->end += 1; + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t FUNCTION(igraph_vector, insert)( + TYPE(igraph_vector) *v, igraph_integer_t pos, BASE value) { + igraph_integer_t size = FUNCTION(igraph_vector, size)(v); + IGRAPH_ASSERT(0 <= pos && pos <= size); + if (size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot insert to vector, already at maximum size.", IGRAPH_EOVERFLOW); + } + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, size + 1)); + if (pos < size) { + memmove(v->stor_begin + pos + 1, v->stor_begin + pos, + sizeof(BASE) * (size - pos)); + } + v->stor_begin[pos] = value; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \section igraph_vector_accessing_elements Accessing elements + * + * The simplest and most performant 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_get() queries and \ref igraph_vector_set() sets an element of a + * vector. \ref igraph_vector_get_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_get + * \brief Access an element of a vector. + * + * Unless you need a function, consider using the \ref VECTOR + * macro instead for better performance. + * + * \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_get_ptr() and the \ref VECTOR macro. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_vector, get)(const TYPE(igraph_vector)* v, igraph_integer_t pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return * (v->stor_begin + pos); +} + +/** + * \ingroup vector + * \function igraph_vector_get_ptr + * \brief Get the address of an element of a vector. + * + * Unless you need a function, consider using the \ref VECTOR + * macro instead for better performance. + * + * \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_get() and the \ref VECTOR macro. + * + * Time complexity: O(1). + */ + +BASE* FUNCTION(igraph_vector, get_ptr) (const TYPE(igraph_vector)* v, igraph_integer_t pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin + pos; +} + +/** + * \ingroup vector + * \function igraph_vector_e + * \brief Access an element of a vector (deprecated alias). + * + * \deprecated-by igraph_vector_get 0.10.0 + */ + +BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, igraph_integer_t pos) { + return FUNCTION(igraph_vector, get)(v, 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_get() and the \ref VECTOR macro. + * + * Time complexity: O(1). + */ + +BASE* FUNCTION(igraph_vector, e_ptr) (const TYPE(igraph_vector)* v, igraph_integer_t pos) { + return FUNCTION(igraph_vector, get_ptr)(v, pos); +} + +/** + * \ingroup vector + * \function igraph_vector_set + * \brief Assignment to an element of a vector. + * + * Unless you need a function, consider using the \ref VECTOR + * macro instead for better performance. + * + * \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_get(). + */ + +void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, igraph_integer_t 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) * 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; + } +} + +#ifndef NOTORDERED + +/** + * \ingroup vector + * \function igraph_vector_range + * \brief Updates a vector to store a range. + * + * Sets the elements of the vector to contain the numbers \p start, \p start+1, + * ..., \p end-1. Note that the range is closed from the left and open from the + * right, according to C conventions. The vector will be resized to fit the range. + * + * \param v The vector to update. + * \param start The lower limit in the range (inclusive). + * \param end The upper limit in the range (exclusive). + * \return Error code: + * \c IGRAPH_ENOMEM: out of memory. + * + * Time complexity: O(n), the number of elements in the vector. + */ + +igraph_error_t FUNCTION(igraph_vector, range)(TYPE(igraph_vector) *v, BASE from, BASE to) { + BASE *p; + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, (to - from))); + + for (p = v->stor_begin; p < v->end; p++) { + *p = from; + from = from + ONE; + } + + return IGRAPH_SUCCESS; +} + +#endif + +/** + * \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) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(v->end != NULL); + IGRAPH_ASSERT(v->end != v->stor_begin); + + (v->end)--; + + return *(v->end); +} + +#ifndef NOTORDERED + +/** + * \ingroup vector + * \function igraph_vector_permute + * \brief Permutes the elements of a vector in place according to an index vector. + * + * This function takes a vector \c v and a corresponding index vector \c ind, + * and permutes the elements of \c v such that \c v[ind[i]] is moved to become + * \c v[i] after the function is executed. + * + * + * It is an error to call this function with an index vector that does not + * represent a valid permutation. Each element in the index vector must be + * between 0 and the length of the vector minus one (inclusive), and each such + * element must appear only once. The function does not attempt to validate the + * index vector. + * + * + * The index vector that this function takes is compatible with the index vector + * returned from \ref igraph_vector_qsort_ind(); passing in the index vector + * from \ref igraph_vector_qsort_ind() will sort the original vector. + * + * + * As a special case, this function allows the index vector to be \em shorter + * than the vector being permuted, in which case the elements whose indices do + * not occur in the index vector will be removed from the vector. + * + * \param v the vector to permute + * \param ind the index vector + * + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: O(n), the size of the vector. + */ +igraph_error_t FUNCTION(igraph_vector, permute)(TYPE(igraph_vector)* v, const igraph_vector_int_t* index) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(index != NULL); + IGRAPH_ASSERT(index->stor_begin != NULL); + IGRAPH_ASSERT(FUNCTION(igraph_vector, size)(v) >= igraph_vector_int_size(index)); + + TYPE(igraph_vector) v_copy; + BASE *v_ptr; + igraph_integer_t *ind_ptr; + + /* There is a more space-efficient algorithm that needs O(1) space only, + * but it messes up the index vector, which we don't want */ + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&v_copy, igraph_vector_int_size(index))); + IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), &v_copy); + + for ( + v_ptr = v_copy.stor_begin, ind_ptr = index->stor_begin; + ind_ptr < index->end; + v_ptr++, ind_ptr++ + ) { + *v_ptr = VECTOR(*v)[*ind_ptr]; + } + + IGRAPH_CHECK(FUNCTION(igraph_vector, update)(v, &v_copy)); + + FUNCTION(igraph_vector, destroy)(&v_copy); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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, 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, 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 Returns a permutation of indices that sorts a vector. + * + * Takes an unsorted array \c v as input and computes an array of indices + * \p inds such that v[ inds[i] ], with \c i increasing from 0, + * is an ordered array (either ascending or descending, depending on + * \p 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 order whether the output array should be sorted in ascending + * or descending order. Use \c IGRAPH_ASCENDING for ascending and + * \c IGRAPH_DESCENDING for 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. + */ + +igraph_error_t FUNCTION(igraph_vector, qsort_ind)(const TYPE(igraph_vector) *v, + igraph_vector_int_t *inds, igraph_order_t order) { + igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); + BASE **vind, *first; + IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); + if (n == 0) { + return IGRAPH_SUCCESS; + } + vind = IGRAPH_CALLOC(n, BASE*); + if (vind == 0) { + IGRAPH_ERROR("igraph_vector_qsort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + for (i = 0; i < n; i++) { + vind[i] = &VECTOR(*v)[i]; + } + first = vind[0]; + if (order == IGRAPH_ASCENDING) { + igraph_qsort(vind, n, sizeof(BASE*), FUNCTION(igraph_vector, i_qsort_ind_cmp_asc)); + } else { + igraph_qsort(vind, n, sizeof(BASE*), FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)); + } + for (i = 0; i < n; i++) { + VECTOR(*inds)[i] = vind[i] - first; + } + IGRAPH_FREE(vind); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_lex_cmp + * \brief Lexicographical comparison of two vectors (type-safe variant). + * + * 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_list_sort(). + * + * \param lhs Pointer to the first vector. + * \param rhs Pointer to the second vector. + * \return -1 if \p lhs is lexicographically smaller, + * 0 if \p lhs and \p rhs are equal, else 1. + * \sa \ref igraph_vector_lex_cmp_untyped() for an untyped variant of this + * function, or \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_int_list_sort.c + */ +int FUNCTION(igraph_vector, lex_cmp)( + const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs +) { + igraph_integer_t i, sa, sb; + const TYPE(igraph_vector) *a = lhs, *b = rhs; + + 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_lex_cmp_untyped + * \brief Lexicographical comparison of two vectors (non-type-safe). + * + * + * 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_lex_cmp() for a type-safe variant of this + * function, or \ref igraph_vector_colex_cmp_untyped() to compare vectors starting from + * the last element. + * + * Time complexity: O(n), the number of elements in the smaller vector. + */ +int FUNCTION(igraph_vector, lex_cmp_untyped)(const void *lhs, const void *rhs) { + const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; + const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; + return FUNCTION(igraph_vector, lex_cmp)(a, b); +} + +/** + * \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_list_sort(). + * + * \param lhs Pointer to a pointer to the first vector. + * \param rhs Pointer to a pointer to the second vector. + * \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_colex_cmp_untyped() for an untyped variant of this + * function, or \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_int_list_sort.c + */ +int FUNCTION(igraph_vector, colex_cmp)( + const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs +) { + igraph_integer_t i, sa, sb, rai, rbi; + const TYPE(igraph_vector) *a = lhs, *b = rhs; + + 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; +} + +/** + * \function igraph_vector_colex_cmp_untyped + * \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_colex_cmp() for a type-safe variant of this + * function, \ref igraph_vector_lex_cmp_untyped() to compare vectors starting from + * the first element. + * + * Time complexity: O(n), the number of elements in the smaller vector. + */ +int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, const void *rhs) { + const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; + const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; + return FUNCTION(igraph_vector, colex_cmp)(a, b); +} + +#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 new_size 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. + */ + +igraph_error_t FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, igraph_integer_t new_size) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_CHECK(FUNCTION(igraph_vector, reserve)(v, new_size)); + v->end = v->stor_begin + new_size; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_resize_min + * \brief Deallocate the unused memory of a vector. + * + * This function attempts to deallocate the unused reserved storage + * of a vector. If it succeeds, \ref igraph_vector_size() and + * \ref igraph_vector_capacity() will be the same. The data in the + * vector is always preserved, even if deallocation is not successful. + * + * \param v Pointer to an initialized vector. + * + * \sa \ref igraph_vector_resize(), \ref igraph_vector_reserve(). + * + * Time complexity: operating system dependent, O(n) at worst. + */ + +void FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v) { + igraph_integer_t size; + BASE *tmp; + if (v->stor_end == v->end) { + return; + } + + size = (v->end - v->stor_begin); + tmp = IGRAPH_REALLOC(v->stor_begin, size, BASE); + + if (tmp != NULL) { + v->stor_begin = tmp; + v->stor_end = v->end = v->stor_begin + size; + } +} + +#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. + * + * The vector must not be empty. + * + * \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(!FUNCTION(igraph_vector, empty)(v)); + max = *(v->stor_begin); +#if defined(BASE_IGRAPH_REAL) + if (isnan(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) + else if (isnan(*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. + * + * The vector must not be empty. 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. + */ +igraph_integer_t FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v) { + BASE *max; + BASE *ptr; + IGRAPH_ASSERT(!FUNCTION(igraph_vector, empty)(v)); + max = ptr = v->stor_begin; +#if defined(BASE_IGRAPH_REAL) + if (isnan(*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) + else if (isnan(*ptr)) { + return ptr - v->stor_begin; /* Result is NaN */ + } +#endif + ptr++; + } + return max - v->stor_begin; +} + +/** + * \ingroup vector + * \function igraph_vector_min + * \brief Smallest element of a vector. + * + * The vector must not be 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(!FUNCTION(igraph_vector, empty)(v)); + min = *(v->stor_begin); +#if defined(BASE_IGRAPH_REAL) + if (isnan(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) + else if (isnan(*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 not be 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. + */ +igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { + BASE *min; + BASE *ptr; + IGRAPH_ASSERT(!FUNCTION(igraph_vector, empty)(v)); + min = ptr = v->stor_begin; +#if defined(BASE_IGRAPH_REAL) + if (isnan(*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) + else if (isnan(*ptr)) { + return ptr - v->stor_begin; /* Result is NaN */ + } +#endif + ptr++; + } + return min - v->stor_begin; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +/** + * \ingroup vector + * \function igraph_vector_init_array + * \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). + */ + +igraph_error_t FUNCTION(igraph_vector, init_array)( + TYPE(igraph_vector) *v, const BASE *data, igraph_integer_t length) { + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, length)); + + /* Note: memcpy() behaviour is undefined if data==NULL, even if length==0. */ + if (length > 0) { + memcpy(v->stor_begin, data, length * sizeof(BASE)); + } + + return IGRAPH_SUCCESS; +} + +/** + * \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) * (v->end - v->stor_begin)); + } +} + +/** + * \ingroup vector + * \function igraph_vector_init_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. + */ + +igraph_error_t FUNCTION(igraph_vector, init_copy)( + TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from +) { + igraph_integer_t from_size; + + IGRAPH_ASSERT(from != NULL); + IGRAPH_ASSERT(from->stor_begin != NULL); + + from_size = FUNCTION(igraph_vector, size)(from); + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(to, from_size)); + + memcpy(to->stor_begin, from->stor_begin, from_size * sizeof(BASE)); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_copy + * \brief Initializes a vector from another vector object (deprecated alias). + * + * \deprecated-by igraph_vector_init_copy 0.10 + */ + +igraph_error_t FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from) { + return FUNCTION(igraph_vector, init_copy)(to, from); +} + +/** + * \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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +#ifndef NOTORDERED + +/** + * \ingroup vector + * \function igraph_vector_init_seq + * \brief Initializes a vector with a sequence, inclusive endpoints (deprecated). + * + * + * The vector will contain the numbers \p from, \p from+1, ..., \p to. Note that + * both endpoints are \em inclusive, contrary to typical usage of ranges in C. + * + * \deprecated-by igraph_vector_init_range 0.10.0 + * + * \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. + */ + +igraph_error_t FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector) *v, BASE from, BASE to) { + BASE *p; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (to - from + 1))); + + for (p = v->stor_begin; p < v->end; p++) { + *p = from++; + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_init_range + * \brief Initializes a vector with a range. + * + * + * The vector will contain the numbers \p start, \p start+1, ..., \p end-1. Note + * that the range is closed from the left and open from the right, according to + * C conventions. + * + * \param v Pointer to an uninitialized vector object. + * \param start The lower limit in the range (inclusive). + * \param end The upper limit in the range (exclusive). + * \return Error code: + * \c IGRAPH_ENOMEM: out of memory. + * + * Time complexity: O(n), the number of elements in the vector. + */ + +igraph_error_t FUNCTION(igraph_vector, init_range)(TYPE(igraph_vector) *v, BASE from, BASE to) { + BASE *p; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (to - from))); + + for (p = v->stor_begin; p < v->end; p++) { + *p = from; + from = from + ONE; + } + + return IGRAPH_SUCCESS; +} + +#endif + +/** + * \ingroup vector + * \function igraph_vector_remove_section + * \brief Deletes a section from a vector. + * + * + * \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, igraph_integer_t from, igraph_integer_t to) { + igraph_integer_t size = FUNCTION(igraph_vector, size)(v); + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + if (from < 0) { + from = 0; + } + + if (to > size) { + to = size; + } + + if (to > from) { + memmove(v->stor_begin + from, v->stor_begin + to, + sizeof(BASE) * (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, igraph_integer_t 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_remove_fast + * \brief Removes a single element from a vector, \em not keeping the order of the remaining elements. + * + * This function removes the element with the given element from the vector by + * swapping it with the last element and then popping it off. You can use this + * function instead of \ref igraph_vector_remove() to gain some speed if the + * order of elements does not matter. + * + * + * 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(1). + */ + +void FUNCTION(igraph_vector, remove_fast)(TYPE(igraph_vector) *v, igraph_integer_t elem) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + igraph_integer_t len = FUNCTION(igraph_vector, size)(v); + VECTOR(*v)[elem] = VECTOR(*v)[len - 1]; + FUNCTION(igraph_vector, pop_back)(v); +} + +/** + * \ingroup vector + * \function igraph_vector_move_interval + * \brief Copies a section of a vector. + * + * + * The source and the destination sections are allowed to overlap; this will + * be handled internally by the function. + * \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). + */ + +igraph_error_t FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v, + igraph_integer_t begin, igraph_integer_t end, igraph_integer_t to) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + memmove(v->stor_begin + to, v->stor_begin + begin, + sizeof(BASE) * (end - begin)); + + return IGRAPH_SUCCESS; +} + +/** + * \deprecated-by igraph_vector_move_interval 0.10.0 + */ +igraph_error_t FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v, + igraph_integer_t begin, igraph_integer_t end, igraph_integer_t to) { + return FUNCTION(igraph_vector, move_interval)(v, begin, end, to); +} + +#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 if the vector is empty or all vector elements + * are in the interval, false otherwise. If any element is NaN, it will + * return 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++) { + /* Note that the following is not equivalent to *ptr < low || *ptr > high + * when *ptr is NaN! */ + 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 if the vector contains at least one + * smaller element than \p limit, false 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? + * + * Checks element-wise equality of two vectors. For vectors containing floating + * point values, consider using \ref igraph_matrix_all_almost_e(). + * + * \param lhs The first vector. + * \param rhs The second vector. + * \return True if the elements in the \p lhs are all + * equal to the corresponding elements in \p rhs. Returns + * 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) { + igraph_integer_t 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 false; + } 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 false; + } + } + return true; + } +} + +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 True if the elements in the \p lhs are all + * less than the corresponding elements in \p rhs. Returns false + * if the lengths of the vectors don't match. If any element + * is NaN, it will return 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) { + igraph_integer_t 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 false; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; + if (l >= r) { + return false; + } + } + return true; + } +} + +/** + * \ingroup vector + * \function igraph_vector_all_g + * \brief Are all elements greater? + * + * \param lhs The first vector. + * \param rhs The second vector. + * \return True if the elements in the \p lhs are all + * greater than the corresponding elements in \p rhs. Returns false + * if the lengths of the vectors don't match. If any element + * is NaN, it will return 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) { + + igraph_integer_t 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 false; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; + if (l <= r) { + return false; + } + } + return true; + } +} + +/** + * \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 True if the elements in the \p lhs are all + * less than or equal to the corresponding elements in \p + * rhs. Returns false if the lengths of the vectors don't + * match. If any element is NaN, it will return 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) { + igraph_integer_t 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 false; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; + if (l > r) { + return false; + } + } + return true; + } +} + +/** + * \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 True if the elements in the \p lhs are all + * greater than or equal to the corresponding elements in \p + * rhs. Returns false if the lengths of the vectors don't + * match. If any element is NaN, it will return 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) { + igraph_integer_t 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 false; + } else { + for (i = 0; i < s; i++) { + BASE l = VECTOR(*lhs)[i]; + BASE r = VECTOR(*rhs)[i]; + if (l < r) { + return false; + } + } + return true; + } +} + +#endif + + +#ifndef NOTORDERED + +static igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v, + BASE what, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t 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 an \type igraph_integer_t. 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 True if \p what is found in the vector, 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, igraph_integer_t *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 \em slice + * sorted) is returned. Note that this means that the returned index will point + * \em inside the slice (including its endpoints), but will not evaluate values + * \em outside the slice. 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 an \type igraph_integer_t. 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 True if \p what is found in the vector, 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, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t end) { + igraph_integer_t left = start; + igraph_integer_t 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); +} + +static igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v, + BASE what, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t end) { + igraph_integer_t left = start; + igraph_integer_t right = end - 1; + + while (left <= right) { + /* (right + left) / 2 could theoretically overflow for long vectors */ + igraph_integer_t 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 true; + } + } + + /* if we are here, the element was not found */ + if (pos != 0) { + *pos = left; + } + + return false; +} + +/** + * \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 True if \p what is found in the vector, 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) { + igraph_integer_t left = 0; + igraph_integer_t right = FUNCTION(igraph_vector, size)(v) - 1; + + while (left <= right) { + /* (right + left) / 2 could theoretically overflow for long vectors */ + igraph_integer_t middle = left + ((right - left) >> 1); + if (what < VECTOR(*v)[middle]) { + right = middle - 1; + } else if (what > VECTOR(*v)[middle]) { + left = middle + 1; + } else { + return true; + } + } + + return false; +} + +#endif + +/** + * \function igraph_vector_scale + * \brief Multiplies 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) { + igraph_integer_t 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) { + igraph_integer_t 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 true; + } + p++; + } + return false; +} + +/** + * \function igraph_vector_search + * \brief Searches in a vector 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, + igraph_integer_t from, BASE what, igraph_integer_t *pos) { + igraph_integer_t 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 true; + } else { + return false; + } +} + +#ifndef NOTORDERED + +/** + * \function igraph_vector_filter_smaller + * \ingroup internal + */ + +igraph_error_t FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, + BASE elem) { + igraph_integer_t i = 0, n = FUNCTION(igraph_vector, size)(v); + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +#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. + */ + +igraph_error_t FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from) { + igraph_integer_t tosize, fromsize; + igraph_integer_t newsize; + + tosize = FUNCTION(igraph_vector, size)(to); + fromsize = FUNCTION(igraph_vector, size)(from); + IGRAPH_SAFE_ADD(tosize, fromsize, &newsize); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, newsize)); + memcpy(to->stor_begin + tosize, from->stor_begin, + sizeof(BASE) * fromsize); + to->end = to->stor_begin + tosize + fromsize; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_get_interval + */ + +igraph_error_t FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *res, igraph_integer_t from, igraph_integer_t to) { + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, to - from)); + memcpy(res->stor_begin, v->stor_begin + from, + (to - from) * sizeof(BASE)); + return IGRAPH_SUCCESS; +} + +#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) { + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(m1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(m2); + igraph_integer_t n = n1 < n2 ? n1 : n2; + igraph_integer_t 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) + else if (isnan(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. + */ + +igraph_error_t FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from) { + igraph_integer_t n = FUNCTION(igraph_vector, size)(from); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, n)); + memcpy(to->stor_begin, from->stor_begin, sizeof(BASE)*n); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_swap + * \brief Swap all elements of two vectors. + * + * \param v1 The first vector. + * \param v2 The second vector. + * \return Error code. + * + * Time complexity: O(1). + */ + +igraph_error_t 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). + */ + +igraph_error_t FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, + igraph_integer_t i, igraph_integer_t j) { + BASE tmp = VECTOR(*v)[i]; + VECTOR(*v)[i] = VECTOR(*v)[j]; + VECTOR(*v)[j] = tmp; + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) { + + igraph_integer_t n = FUNCTION(igraph_vector, size)(v), n2 = n / 2; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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 + */ + +igraph_error_t FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v) { + igraph_integer_t n = FUNCTION(igraph_vector, size)(v); + igraph_integer_t 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. + */ + +igraph_error_t FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); + igraph_integer_t i; + if (n1 != n2) { + IGRAPH_ERROR("Vectors to be added must have the same sizes.", + 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); + igraph_integer_t i; + if (n1 != n2) { + IGRAPH_ERROR("Vectors to be subtracted must have the same sizes.", + 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); + igraph_integer_t i; + if (n1 != n2) { + IGRAPH_ERROR("Vectors to be multiplied must have the same sizes.", + 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); + igraph_integer_t i; + if (n1 != n2) { + IGRAPH_ERROR("Vectors to be divided must have the same sizes.", + 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 IGRAPH_SUCCESS; +} + +#ifndef NOABS + +igraph_error_t FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v) { +#ifdef UNSIGNED + /* Nothing do to, unsigned type */ + IGRAPH_UNUSED(v); +#else + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +#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. + * + * Time complexity: O(n), the number of elements. + */ + +void FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, + BASE *min, BASE *max) { + BASE* ptr; + IGRAPH_ASSERT(!FUNCTION(igraph_vector, empty)(v)); + *min = *max = *(v->stor_begin); + #if defined(BASE_IGRAPH_REAL) + if (isnan(*min)) { return; }; /* 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) + else if (isnan(*ptr)) { /* Result is NaN */ + *min = *max = *ptr; + return; + }; + #endif + ptr++; + } +} + +/** + * \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. + * + * Time complexity: O(n), the number of elements. + */ + +void FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, + igraph_integer_t *which_min, igraph_integer_t *which_max) { + BASE *min, *max; + BASE *ptr; + IGRAPH_ASSERT(!FUNCTION(igraph_vector, empty)(v)); + ptr = v->stor_begin; + min = max = ptr; + #if defined(BASE_IGRAPH_REAL) + if (isnan(*ptr)) { /* Result is NaN */ + *which_min = *which_max = 0; + return; + } + #endif + while (ptr < v->end) { + if (*ptr > *max) { + max = ptr; + } else if (*ptr < *min) { + min = ptr; + } +#if defined(BASE_IGRAPH_REAL) + else if (isnan(*ptr)) { /* Result is NaN */ + *which_min = *which_max = ptr - v->stor_begin; + return; + } +#endif + ptr++; + } + *which_min = min - v->stor_begin; + *which_max = max - v->stor_begin; +} + +#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) { + + igraph_integer_t n = FUNCTION(igraph_vector, size)(v); + igraph_integer_t i = 0; + +#ifdef EQ + while (i < n && EQ(VECTOR(*v)[i], (BASE) ZERO)) { +#else + while (i < n && VECTOR(*v)[i] == (BASE) ZERO) { +#endif + i++; + } + + return i == n; +} + +#ifndef NOTORDERED + + +/* + * Sorted vector intersection + */ + +/* Recursive Baeza-Yates intersection algorithm for sorted vector intersection. */ +static igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( + const TYPE(igraph_vector) *v1, igraph_integer_t begin1, igraph_integer_t end1, + const TYPE(igraph_vector) *v2, igraph_integer_t begin2, igraph_integer_t end2, + TYPE(igraph_vector) *result) { + igraph_integer_t size1, size2, probe1, probe2; + + if (begin1 == end1 || begin2 == end2) { + return IGRAPH_SUCCESS; + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_intersect_sorted + * \brief Set 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. + * + * + * For similar-size vectors, this function uses a straightforward linear scan. + * When the vector sizes differ substantially, it uses the set intersection + * method of Ricardo Baeza-Yates, which takes logarithmic time in the length + * of the larger vector. + * + * + * The algorithm keeps the multiplicities of the elements: if an element appears + * \c k1 times in the first vector and \c 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. + * https://doi.org/10.1007/978-3-540-27801-6_30 + * + * \param v1 The first vector + * \param v2 The second vector + * \param result The result vector, which will also be sorted. + * \return Error code. + * + * Time complexity: O(m log(n)) where m is the size of the smaller vector + * and n is the size of the larger one. + */ +igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { + igraph_integer_t size1, size2; + igraph_real_t r; + + size1 = FUNCTION(igraph_vector, size)(v1); + size2 = FUNCTION(igraph_vector, size)(v2); + + FUNCTION(igraph_vector, clear)(result); + + if (size1 == 0 || size2 == 0) { + return IGRAPH_SUCCESS; + } + + r = size1 > size2 ? (igraph_real_t) size1 / size2 : (igraph_real_t) size2 / size1; + + /* When the set size ratio is small, use a simple linear scan. See comments in + * igraph_vector_intersection_size_sorted() for the justification of this r threshold. */ + if (r < 10) { + igraph_integer_t i1 = 0, i2 = 0; + while (i1 < size1 && i2 < size2) { + BASE e1 = VECTOR(*v1)[i1], e2 = VECTOR(*v2)[i2]; + if (e1 < e2) { + i1++; + } else if (e1 > e2) { + i2++; + } else { + i1++; i2++; + IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, e1)); + } + } + } else { + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, 0, size1, v2, 0, size2, result)); + } + return IGRAPH_SUCCESS; +} + +/* Recursive Baeza-Yates intersection algorithm for sorted vector intersection size. */ +static void FUNCTION(igraph_i_vector, intersection_size_sorted)( + const TYPE(igraph_vector) *v1, igraph_integer_t begin1, igraph_integer_t end1, + const TYPE(igraph_vector) *v2, igraph_integer_t begin2, igraph_integer_t end2, + igraph_integer_t *result) { + + igraph_integer_t size1, size2, probe1, probe2; + + if (begin1 == end1 || begin2 == end2) { + return; + } + + 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); + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, begin1, probe1, v2, begin2, probe2, result + ); + if (!(probe2 == end2 || VECTOR(*v1)[probe1] < VECTOR(*v2)[probe2])) { + (*result)++; + probe2++; + } + FUNCTION(igraph_i_vector, intersection_size_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); + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, begin1, probe1, v2, begin2, probe2, result + ); + if (!(probe1 == end1 || VECTOR(*v2)[probe2] < VECTOR(*v1)[probe1])) { + (*result)++; + probe1++; + } + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, probe1, end1, v2, probe2 + 1, end2, result + ); + } +} + +/** + * \function igraph_vector_intersection_size_sorted + * \brief Intersection size of two sorted vectors. + * + * \experimental + * + * Counts elements that are present in both vectors. This is particularly + * useful for counting common neighbours of two vertices. + * + * + * For similar-size vectors, this function uses a straightforward linear scan. + * When the vector sizes differ substantially, it uses the set intersection + * method of Ricardo Baeza-Yates, which takes logarithmic time in the length + * of the larger vector. + * + * + * The algorithm keeps the multiplicities of the elements: if an element appears + * \c k1 times in the first vector and \c 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. + * https://doi.org/10.1007/978-3-540-27801-6_30 + * + * \param v1 The first vector + * \param v2 The second vector + * \return The number of common elements. + * + * Time complexity: O(m log(n)) where m is the size of the smaller vector + * and n is the size of the larger one. + */ + +igraph_integer_t FUNCTION(igraph_vector, intersection_size_sorted)( + const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + igraph_integer_t size1, size2, count; + igraph_real_t r; + + size1 = FUNCTION(igraph_vector, size)(v1); + size2 = FUNCTION(igraph_vector, size)(v2); + + count = 0; + + if (size1 == 0 || size2 == 0) { + return count; + } + + r = size1 > size2 ? (igraph_real_t) size1 / size2 : (igraph_real_t) size2 / size1; + + /* When the set size ratio is small, use a simple linear scan. The ideal ratio + * seems to be affected by processor cache effects. It depends on the machine, + * as well as on the input sizes. The threshold of 10 was determined empirically + * by using the intersection.c (direct) and igraph_ecc.c (indirect) benchmarks. + * See https://github.com/igraph/igraph/pull/2618 */ + if (r < 10) { + /* This is a fast branchless implementation that uses arithmetic + * instead of conditionals. */ + igraph_integer_t i1 = 0, i2 = 0; + while (i1 < size1 && i2 < size2) { + BASE e1 = VECTOR(*v1)[i1], e2 = VECTOR(*v2)[i2]; + igraph_integer_t d1 = (e1 <= e2), d2 = (e1 >= e2); + i1 += d1; i2 += d2; + count += (d1 == d2); + } + } else { + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, 0, size1, v2, 0, size2, &count); + } + + return count; +} + +/** + * \function igraph_vector_difference_sorted + * \brief Set difference of two sorted vectors. + * + * 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 + */ +igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { + igraph_integer_t 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) * 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) * 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) { + igraph_integer_t 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) * (i0 - i)); + } + + return IGRAPH_SUCCESS; +} + +#endif + +#ifdef OUT_FORMAT +#ifndef USING_R +igraph_error_t FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, const char *format) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} +#endif /* USING_R */ +#endif /* OUT_FORMAT */ + +#if defined(OUT_FORMAT) || defined(FPRINTFUNC) + +#ifndef USING_R +igraph_error_t FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v) { + return FUNCTION(igraph_vector, fprint)(v, stdout); +} +#endif + +igraph_error_t FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ + +igraph_error_t FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *newv, + const igraph_vector_int_t *idx) { + + igraph_integer_t i, j, newlen = igraph_vector_int_size(idx); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(newv, newlen)); + + for (i = 0; i < newlen; i++) { + j = VECTOR(*idx)[i]; + VECTOR(*newv)[i] = VECTOR(*v)[j]; + } + + return IGRAPH_SUCCESS; +} + +igraph_error_t FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, + const igraph_vector_int_t *idx) { + BASE *tmp; + igraph_integer_t i, n = igraph_vector_int_size(idx); + + tmp = IGRAPH_CALLOC(n, BASE); + if (!tmp) { + IGRAPH_ERROR("Cannot index vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + 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 IGRAPH_SUCCESS; +} diff --git a/src/core/vector_list.c b/src/core/vector_list.c new file mode 100644 index 0000000..5f761b1 --- /dev/null +++ b/src/core/vector_list.c @@ -0,0 +1,171 @@ +/* -*- 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 + +*/ + +#include "igraph_vector_list.h" + +#define VECTOR_LIST + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "typed_list.pmt" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#include "igraph_pmt.h" +#include "typed_list.pmt" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#undef VECTOR_LIST + +/** + * \ingroup vector_list + * \section about_igraph_vector_list_t_objects About \type igraph_vector_list_t objects + * + * The \type igraph_vector_list_t data type is essentially a list of + * \type igraph_vector_t objects with automatic memory management. It is something + * similar to (but much simpler than) the \type vector template in the C++ + * standard library where the elements are vectors themselves. + * + * There are multiple variants of \type igraph_vector_list_t; the basic variant + * stores vectors of doubles (i.e. each item is an \ref igraph_vector_t), but + * there is also \type igraph_vector_int_list_t for integers (where each item is + * an \type igraph_vector_int_t), \type igraph_matrix_list_t for matrices of + * doubles and so on. The following list summarizes the variants that are + * currently available in the library: + * + * \ilist + * \ili \type igraph_vector_list_t for lists of vectors of floating-point numbers + * (\type igraph_vector_t) + * \ili \type igraph_vector_int_list_t for lists of integer vectors + * (\type igraph_vector_int_t) + * \ili \type igraph_matrix_list_t for lists of matrices of floating-point numbers + * (\type igraph_matrix_t) + * \ili \type igraph_graph_list_t for lists of graphs (\type igraph_t) + * \endilist + * + * Lists of vectors are used in \a igraph in many + * cases, e.g., when returning lists of paths, cliques or vertex sets. + * Functions that expect or return a list of numeric vectors typically use + * \type igraph_vector_list_t or \type igraph_vector_int_list_t to achieve this. + * Lists of integer vectors are used when the vectors in the list are supposed + * to hold vertex or edge identifiers, while lists of floating-point vectors + * are used when the vectors are expected to hold fractional numbers or + * infinities. + * + * The elements in an \type igraph_vector_list_t object and its variants are + * indexed from zero, we follow the usual C convention here. + * + * Almost all of the functions described below for \type igraph_vector_list_t + * also exist for all the other vector list variants. These variants are not + * documented separately; you can simply replace \c vector_list with, say, + * \c vector_int_list if you need a function for another variant. For instance, + * to initialize a list of integer vectors, you need to use + * \c igraph_vector_int_list_init() and not \ref igraph_vector_list_init(). + * + * Before diving into a detailed description of the functions related to + * lists of vectors, we must also talk about the \em ownership rules of these + * objects. The most important rule is that the vectors in the list are + * owned by the list itself, meaning that the user is \em not responsible + * for allocating memory for the vectors or for freeing the memory associated + * to the vectors. It is the responsibility of the list to allocate and initialize + * the vectors when new items are created in the list, and it is also the + * responsibility of the list to destroy the items when they are removed from + * the list without passing on their ownership to the user. As a consequence, + * the list may not contain "uninitialized" or "null" items; each item is + * initialized when it comes to existence. If you create a list containing + * one million vectors, you are not only allocating memory for one million + * \ref igraph_vector_t object but you are also initializing one million + * vectors. Also, if you have a list containing one million vectors and you + * clear the list by calling \ref igraph_vector_list_clear(), the list will + * implicitly destroy these lists, and any pointers that you may hold to the + * items become invalid at once. + * + * Speaking about pointers, the typical way of working with vectors in + * a list is to obtain a pointer to one of the items via the + * \ref igraph_vector_list_get_ptr() method and then passing this pointer + * onwards to functions that manipulate \ref igraph_vector_t objects. However, + * note that the pointers are \em ephemeral in the sense that they may be + * invalidated any time when the list is modified because a modification may + * involve the re-allocation of the internal storage of the list if more space + * is needed, and the pointers that you obtained will not follow the + * reallocation. This limitation does not appear often in real-world usage of + * \c igraph_vector_list_t and in general, the advantages of the automatic + * memory management outweigh this limitation. + */ + +/** + * \ingroup vector_list + * \section igraph_vector_list_constructors_and_destructors Constructors and + * destructors + * + * \type igraph_vector_list_t objects have to be initialized before using + * them, this is analogous to calling a constructor on them. + * \ref igraph_vector_list_init() is the basic constructor; it creates a list + * of the given length and also initializes each vector in the newly created + * list to zero length. + * + * If an \type igraph_vector_list_t object is not needed any more, it + * should be destroyed to free its allocated memory by calling the + * \type igraph_vector_list_t destructor, \ref igraph_vector_list_destroy(). + * Calling the destructor also destroys all the vectors inside the vector + * list due to the ownership rules. If you want to keep a few of the vectors + * in the vector list, you need to copy them with \ref igraph_vector_init_copy() or + * \ref igraph_vector_update(), or you need to remove them from the list and + * take ownership by calling \ref igraph_vector_list_pop_back(), + * \ref igraph_vector_list_remove() or \ref igraph_vector_list_remove_fast() . + */ + + +/** + * \ingroup vector_list + * \section igraph_vector_list_accessing_elements Accessing elements + * + * Elements of a vector list may be accessed with the + * \ref igraph_vector_list_get_ptr() function. The function returns a \em pointer + * to the vector with a given index inside the list, and you may then pass + * this pointer onwards to other functions that can query or manipulate + * vectors. The pointer itself is guaranteed to stay valid as long as the + * list itself is not modified; however, \em any modification to the list + * will invalidate the pointer, even modifications that are seemingly unrelated + * to the vector that the pointer points to (such as adding a new vector at + * the end of the list). This is because the list data structure may be forced + * to re-allocate its internal storage if a new element does not fit into the + * already allocated space, and there are no guarantees that the re-allocated + * block remains at the same memory location (typically it gets moved elsewhere). + * + * + * Note that the standard \ref VECTOR macro that works for ordinary vectors + * does not work for lists of vectors to access the i-th element (but of course + * you can use it to index into an existing vector that you retrieved from the + * vector list with \ref igraph_vector_list_get_ptr() ). This is because the + * macro notation would allow one to overwrite the vector in the list with + * another one without the list knowing about it, so the list would not be able + * to destroy the vector that was overwritten by a new one. + * + * + * \ref igraph_vector_list_tail_ptr() returns a pointer to the last + * vector in the list, or \c NULL if the list is empty. There is no + * igraph_vector_list_head_ptr(), however, as it is easy to + * write igraph_vector_list_get_ptr(v, 0) instead. + */ diff --git a/src/core/vector_ptr.c b/src/core/vector_ptr.c new file mode 100644 index 0000000..ce063fd --- /dev/null +++ b/src/core/vector_ptr.c @@ -0,0 +1,802 @@ +/* -*- 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_vector_ptr.h" + +#include "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#include /* memcpy & co. */ +#include /* uintptr_t */ +#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. + * + * 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 some function 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. + */ + +igraph_error_t igraph_vector_ptr_init(igraph_vector_ptr_t* v, igraph_integer_t size) { + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + v->stor_end = v->stor_begin + alloc_size; + v->end = v->stor_begin + size; + v->item_destructor = 0; + + return IGRAPH_SUCCESS; +} + +/** + */ + +const igraph_vector_ptr_t *igraph_vector_ptr_view( + const igraph_vector_ptr_t *v, void *const *data, igraph_integer_t 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 + */ + +igraph_error_t igraph_vector_ptr_reserve(igraph_vector_ptr_t* v, igraph_integer_t capacity) { + igraph_integer_t actual_size = igraph_vector_ptr_size(v); + void **tmp; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); + + if (capacity <= igraph_vector_ptr_size(v)) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(v->stor_begin, (size_t) capacity, void*); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for pointer vector."); + + v->stor_begin = tmp; + v->stor_end = v->stor_begin + capacity; + v->end = v->stor_begin + actual_size; + + return IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_integer_t 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 \ref 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. + */ + +igraph_error_t 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) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_pop_back + * \brief Removes and returns the last element of a pointer vector. + * + * + * It is an error to call this function with an empty vector. + * + * \param v The pointer vector. + * \return The removed last element. + * + * Time complexity: O(1). + */ + +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 + */ +igraph_error_t igraph_vector_ptr_insert(igraph_vector_ptr_t* v, igraph_integer_t pos, void* e) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_get + * \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_get(const igraph_vector_ptr_t* v, igraph_integer_t pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return *(v->stor_begin + pos); +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_e + * \brief Access an element of a pointer vector (deprecated alias). + * + * \deprecated-by igraph_vector_ptr_get 0.10.0 + */ + +void *igraph_vector_ptr_e(const igraph_vector_ptr_t* v, igraph_integer_t pos) { + return igraph_vector_ptr_get(v, 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, igraph_integer_t 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. + */ + +igraph_error_t igraph_vector_ptr_resize(igraph_vector_ptr_t* v, igraph_integer_t newsize) { + IGRAPH_CHECK(igraph_vector_ptr_reserve(v, newsize)); + v->end = v->stor_begin + newsize; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vectorptr + * \brief Initializes a pointer vector from an array (constructor). + * + * \param v Pointer to an uninitialized + * igraph_vector_ptr_t object to be initialized. + * \param data The array of pointers that serves as the initial contents of the + * pointer vector. + * \param length Integer, the length of the array. + * \return Error code: + * \c IGRAPH_ENOMEM if out of memory + */ + +igraph_error_t igraph_vector_ptr_init_array(igraph_vector_ptr_t *v, void *const *data, igraph_integer_t length) { + v->stor_begin = IGRAPH_CALLOC(length, void*); + if (v->stor_begin == 0) { + IGRAPH_ERROR("Cannot initialize pointer vector from array", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +/** + * \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_init_copy + * \brief Initializes a pointer vector from another one (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. + */ + +igraph_error_t igraph_vector_ptr_init_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { + igraph_integer_t 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 pointer vector", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_copy + * \brief Initializes a pointer vector from another one (deprecated alias). + * + * \deprecated-by igraph_vector_ptr_init_copy 0.10 + */ + +igraph_error_t igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { + return igraph_vector_ptr_init_copy(to, from); +} + +/** + * \ingroup vectorptr + * \brief Remove an element from a pointer vector. + */ + +void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, igraph_integer_t 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 **. + */ +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); +} + +igraph_error_t igraph_vector_ptr_append(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { + igraph_integer_t origsize = igraph_vector_ptr_size(to); + igraph_integer_t othersize = igraph_vector_ptr_size(from); + igraph_integer_t 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 IGRAPH_SUCCESS; +} + + +/** + * \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; +} + +typedef int cmp_t (const void *, const void *); + +/** + * Comparison function passed to qsort_r from igraph_vector_ptr_sort_ind + */ +static int igraph_vector_ptr_i_sort_ind_cmp(void *thunk, const void *p1, const void *p2) { + cmp_t *cmp = (cmp_t *) thunk; + uintptr_t *pa = (uintptr_t*) p1; + uintptr_t *pb = (uintptr_t*) p2; + void **item_a_ptr = (void**) *pa; + void **item_b_ptr = (void**) *pb; + return cmp(*item_a_ptr, *item_b_ptr); +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_sort_ind + * \brief Returns a permutation of indices that sorts a vector of pointers. + * + * Takes an unsorted array \c v as input and computes an array of + * indices inds such that v[ inds[i] ], with i increasing from 0, is + * an ordered array (either ascending or descending, depending on + * \v order). The order of indices for identical elements is not + * defined. + * + * \param v the array to be sorted + * \param inds the output array of indices. This must be initialized, + * but will be resized + * \param cmp a comparator function that takes two elements of the pointer + * vector being sorted (these are constant pointers on their own) + * and returns a negative value if the item \em "pointed to" by the + * first pointer is smaller than the item \em "pointed to" by the + * second pointer, a positive value if it is larger, or zero if the + * two items are equal + * \return Error code. + * + * This routine uses the C library qsort routine. + * Algorithm: 1) create an array of pointers to the elements of v. 2) + * Pass this array to qsort. 3) after sorting the difference between + * the pointer value and the first pointer value gives its original + * position in the array. Use this to set the values of inds. + */ + +igraph_error_t igraph_vector_ptr_sort_ind(igraph_vector_ptr_t *v, + igraph_vector_int_t *inds, cmp_t *cmp) { + igraph_integer_t i; + uintptr_t *vind, first; + igraph_integer_t n = igraph_vector_ptr_size(v); + + IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); + if (n == 0) { + return IGRAPH_SUCCESS; + } + + vind = IGRAPH_CALLOC(n, uintptr_t); + if (vind == 0) { + IGRAPH_ERROR("igraph_vector_ptr_sort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + for (i = 0; i < n; i++) { + vind[i] = (uintptr_t) &VECTOR(*v)[i]; + } + + first = vind[0]; + + igraph_qsort_r(vind, n, sizeof(vind[0]), (void*)cmp, igraph_vector_ptr_i_sort_ind_cmp); + + for (i = 0; i < n; i++) { + VECTOR(*inds)[i] = (vind[i] - first) / sizeof(uintptr_t); + } + + IGRAPH_FREE(vind); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_permute + * \brief Permutes the elements of a pointer vector in place according to an index vector. + * + * + * This function takes a vector \c v and a corresponding index vector \c ind, + * and permutes the elements of \c v such that \c v[ind[i]] is moved to become + * \c v[i] after the function is executed. + * + * + * It is an error to call this function with an index vector that does not + * represent a valid permutation. Each element in the index vector must be + * between 0 and the length of the vector minus one (inclusive), and each such + * element must appear only once. The function does not attempt to validate the + * index vector. + * + * + * The index vector that this function takes is compatible with the index vector + * returned from \ref igraph_vector_ptr_sort_ind(); passing in the index vector + * from \ref igraph_vector_ptr_sort_ind() will sort the original vector. + * + * + * As a special case, this function allows the index vector to be \em shorter + * than the vector being permuted, in which case the elements whose indices do + * not occur in the index vector will be removed from the vector. + * + * \param v the vector to permute + * \param ind the index vector + * + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: O(n), the size of the vector. + */ +igraph_error_t igraph_vector_ptr_permute(igraph_vector_ptr_t* v, const igraph_vector_int_t* index) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(index != NULL); + IGRAPH_ASSERT(index->stor_begin != NULL); + IGRAPH_ASSERT(igraph_vector_ptr_size(v) >= igraph_vector_int_size(index)); + + igraph_vector_ptr_t v_copy; + void** v_ptr; + igraph_integer_t *ind_ptr; + + /* There is a more space-efficient algorithm that needs O(1) space only, + * but it messes up the index vector, which we don't want */ + + IGRAPH_CHECK(igraph_vector_ptr_init(&v_copy, igraph_vector_int_size(index))); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &v_copy); + + for ( + v_ptr = v_copy.stor_begin, ind_ptr = index->stor_begin; + ind_ptr < index->end; + v_ptr++, ind_ptr++ + ) { + *v_ptr = VECTOR(*v)[*ind_ptr]; + } + + IGRAPH_CHECK(igraph_vector_ptr_resize(v, igraph_vector_int_size(index))); + igraph_vector_ptr_copy_to(&v_copy, VECTOR(*v)); + + igraph_vector_ptr_destroy(&v_copy); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/flow/flow.c b/src/flow/flow.c new file mode 100644 index 0000000..af257db --- /dev/null +++ b/src/flow/flow.c @@ -0,0 +1,2620 @@ +/* -*- 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 "flow/flow_internal.h" +#include "math/safe_intop.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 igraph_error_t igraph_i_maxflow_undirected( + const igraph_t *graph, + igraph_real_t *value, + igraph_vector_t *flow, + igraph_vector_int_t *cut, + igraph_vector_int_t *partition, + igraph_vector_int_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_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t edges; + igraph_vector_t newcapacity; + igraph_t newgraph; + igraph_integer_t size; + + /* 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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges * 2); + IGRAPH_SAFE_MULT(no_of_edges, 4, &size); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size)); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_int_resize(&edges, size)); + for (igraph_integer_t 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) { + igraph_integer_t cs = igraph_vector_int_size(cut); + for (igraph_integer_t 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) { + for (igraph_integer_t 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_int_destroy(&edges); + igraph_vector_destroy(&newcapacity); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +#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(igraph_integer_t b, igraph_maxflow_stats_t *stats, + igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, + igraph_integer_t no_of_nodes, + igraph_vector_int_t *distance) { + + IGRAPH_UNUSED(buckets); + + igraph_integer_t bo; + (stats->nogap)++; + for (bo = b + 1; bo <= no_of_nodes; bo++) { + while (!igraph_dbuckets_empty_bucket(ibuckets, bo)) { + igraph_integer_t n = igraph_dbuckets_pop(ibuckets, bo); + (stats->nogapnodes)++; + DIST(n) = no_of_nodes; + } + } +} + +static void igraph_i_mf_relabel(igraph_integer_t v, igraph_integer_t no_of_nodes, + igraph_vector_int_t *distance, + igraph_vector_int_t *first, + igraph_vector_t *rescap, igraph_vector_int_t *to, + igraph_vector_int_t *current, + igraph_maxflow_stats_t *stats, igraph_integer_t *nrelabelsince) { + + igraph_integer_t min = no_of_nodes; + igraph_integer_t 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(igraph_integer_t v, igraph_integer_t e, igraph_integer_t n, + igraph_vector_int_t *current, + igraph_vector_t *rescap, igraph_vector_t *excess, + igraph_integer_t target, igraph_integer_t source, + igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, + igraph_vector_int_t *distance, + igraph_vector_int_t *rev, igraph_maxflow_stats_t *stats, + igraph_integer_t *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, DIST(n), n); + } + RESCAP(e) -= delta; + RESCAP(REV(e)) += delta; + EXCESS(n) += delta; + EXCESS(v) -= delta; +} + +static void igraph_i_mf_discharge(igraph_integer_t v, + igraph_vector_int_t *current, + igraph_vector_int_t *first, + igraph_vector_t *rescap, + igraph_vector_int_t *to, + igraph_vector_int_t *distance, + igraph_vector_t *excess, + igraph_integer_t no_of_nodes, igraph_integer_t source, + igraph_integer_t target, igraph_buckets_t *buckets, + igraph_dbuckets_t *ibuckets, + igraph_vector_int_t *rev, + igraph_maxflow_stats_t *stats, + igraph_integer_t *npushsince, igraph_integer_t *nrelabelsince) { + + do { + igraph_integer_t i; + igraph_integer_t start = CURRENT(v); + igraph_integer_t stop = LAST(v); + for (i = start; i < stop; i++) { + if (RESCAP(i) > 0) { + igraph_integer_t nei = HEAD(i); + if (DIST(v) == DIST(nei) + 1) { + PUSH((v), i, nei); + if (EXCESS(v) == 0) { + break; + } + } + } + } + if (i == stop) { + igraph_integer_t 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 igraph_error_t igraph_i_mf_bfs(igraph_dqueue_int_t *bfsq, + igraph_integer_t source, igraph_integer_t target, + igraph_integer_t no_of_nodes, igraph_buckets_t *buckets, + igraph_dbuckets_t *ibuckets, + igraph_vector_int_t *distance, + igraph_vector_int_t *first, igraph_vector_int_t *current, + igraph_vector_int_t *to, igraph_vector_t *excess, + igraph_vector_t *rescap, igraph_vector_int_t *rev) { + + igraph_integer_t k, l; + + IGRAPH_UNUSED(source); + + igraph_buckets_clear(buckets); + igraph_dbuckets_clear(ibuckets); + igraph_vector_int_fill(distance, no_of_nodes); + DIST(target) = 0; + + IGRAPH_CHECK(igraph_dqueue_int_push(bfsq, target)); + while (!igraph_dqueue_int_empty(bfsq)) { + igraph_integer_t node = igraph_dqueue_int_pop(bfsq); + igraph_integer_t ndist = DIST(node) + 1; + for (k = FIRST(node), l = LAST(node); k < l; k++) { + if (RESCAP(REV(k)) > 0) { + igraph_integer_t 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_CHECK(igraph_dqueue_int_push(bfsq, nei)); + } + } + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_maxflow + * \brief 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 + * https://doi.org/10.1145/48014.61051. + * + * + * 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 + */ + +igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, + igraph_vector_t *flow, igraph_vector_int_t *cut, + igraph_vector_int_t *partition, igraph_vector_int_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_vcount(graph); + igraph_integer_t no_of_orig_edges = igraph_ecount(graph); + igraph_integer_t no_of_edges = 2 * no_of_orig_edges; + + igraph_vector_t rescap, excess; + igraph_vector_int_t from, to, rev, distance; + igraph_vector_int_t edges, rank; + igraph_vector_int_t current, first; + igraph_buckets_t buckets; + igraph_dbuckets_t ibuckets; + + igraph_dqueue_int_t bfsq; + + igraph_integer_t i, j, idx; + igraph_integer_t 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 IGRAPH_SUCCESS; + } + + if (capacity && igraph_vector_size(capacity) != no_of_orig_edges) { + IGRAPH_ERROR("Capacity vector must match number of edges in length.", IGRAPH_EINVAL); + } + if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) { + IGRAPH_ERROR("Invalid source or target vertex.", IGRAPH_EINVVID); + } + if (source == target) { + IGRAPH_ERROR("Source and target vertices are the same.", 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_CHECK(igraph_dqueue_int_init(&bfsq, no_of_nodes)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &bfsq); + IGRAPH_VECTOR_INT_INIT_FINALLY(&to, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rev, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&rescap, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&excess, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&distance, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&first, no_of_nodes + 1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&from, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges); + + /* Create the basic data structure */ + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_int_rank(&edges, &rank, no_of_nodes)); + + for (i = 0; i < no_of_edges; i += 2) { + igraph_integer_t pos = VECTOR(rank)[i]; + igraph_integer_t pos2 = 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++) { + igraph_integer_t n = (VECTOR(from)[i] - + VECTOR(from)[ 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_int_destroy(&from); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + if (!flow) { + igraph_vector_int_destroy(&rank); + IGRAPH_FINALLY_CLEAN(1); + } + + /* And the current pointers, initially the same as the first */ + IGRAPH_VECTOR_INT_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; + } + } + + IGRAPH_CHECK(BFS()); + (stats->nobfs)++; + + while (!igraph_buckets_empty(&buckets)) { + igraph_integer_t 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_int_t Q; + igraph_vector_bool_t added; + igraph_integer_t marked = 0; + + IGRAPH_CHECK(igraph_vector_bool_init(&added, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &added); + + IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); + VECTOR(added)[target] = true; + marked++; + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); + for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) { + igraph_integer_t nei = HEAD(i); + if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) { + VECTOR(added)[nei] = true; + marked++; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + } + } + } + igraph_dqueue_int_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_int_clear(cut); + for (i = 0; i < no_of_orig_edges; i++) { + igraph_integer_t f = IGRAPH_FROM(graph, i); + igraph_integer_t t = IGRAPH_TO(graph, i); + if (!VECTOR(added)[f] && VECTOR(added)[t]) { + IGRAPH_CHECK(igraph_vector_int_push_back(cut, i)); + } + } + } + + if (partition2) { + igraph_integer_t x = 0; + IGRAPH_CHECK(igraph_vector_int_resize(partition2, marked)); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(added)[i]) { + VECTOR(*partition2)[x++] = i; + } + } + } + + if (partition) { + igraph_integer_t x = 0; + IGRAPH_CHECK(igraph_vector_int_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_int_t Q; + igraph_vector_int_t added; /* uses more than two values, cannot be bool */ + igraph_integer_t j, k, l; + igraph_t flow_graph; + igraph_vector_int_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_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, source)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); + VECTOR(added)[source] = 1; + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&Q); + DIST(actnode) = actdist; + + for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) { + igraph_integer_t nei = HEAD(i); + if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) { + VECTOR(added)[nei] = 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actdist + 1)); + } + } + } /* !igraph_dqueue_int_empty(&Q) */ + + igraph_vector_int_destroy(&added); + igraph_dqueue_int_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, DIST(i), i); + } + } + + /* Now we return the flow to the source */ + while (!igraph_buckets_empty(&buckets)) { + igraph_integer_t vertex = igraph_buckets_popmax(&buckets); + + /* DISCHARGE(vertex) comes here */ + do { + for (i = CURRENT(vertex), j = LAST(vertex); i < j; i++) { + if (RESCAP(i) > 0) { + igraph_integer_t 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, DIST(nei), nei); + } + + EXCESS(nei) += delta; + EXCESS(vertex) -= delta; + + if (EXCESS(vertex) == 0) { + break; + } + + } + } + } + + if (i == j) { + + /* RELABEL(vertex) comes here */ + igraph_integer_t min; + igraph_integer_t 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, DIST(vertex), vertex); + } + + /* TODO: gap heuristics here ??? */ + + } else { + CURRENT(vertex) = FIRST(vertex); + } + + break; + + } while (true); + } + + /* 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_INT_INIT_FINALLY(&flow_edges, 0); + for (i = 0, j = 0; i < no_of_edges; i += 2, j++) { + igraph_integer_t pos = VECTOR(rank)[i]; + if ((capacity ? VECTOR(*capacity)[j] : 1.0) > RESCAP(pos)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&flow_edges, + IGRAPH_FROM(graph, j))); + IGRAPH_CHECK(igraph_vector_int_push_back(&flow_edges, + IGRAPH_TO(graph, j))); + } + } + IGRAPH_CHECK(igraph_create(&flow_graph, &flow_edges, no_of_nodes, + IGRAPH_DIRECTED)); + igraph_vector_int_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_int_t stack; + igraph_vector_t mycap; + + IGRAPH_CHECK(igraph_vector_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_vector_int_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) { + igraph_integer_t pos = VECTOR(rank)[i]; + igraph_integer_t pos2 = VECTOR(rank)[i + 1]; + MYCAP(pos) = (capacity ? VECTOR(*capacity)[i / 2] : 1.0) - RESCAP(pos); + MYCAP(pos2) = 0.0; + } + + do { + igraph_vector_int_null(¤t); + igraph_vector_int_clear(&stack); + igraph_vector_int_null(&added); + + IGRAPH_CHECK(igraph_vector_int_push_back(&stack, -1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&stack, source)); + VECTOR(added)[source] = 1; + while (!igraph_vector_int_empty(&stack) && + igraph_vector_int_tail(&stack) != target) { + igraph_integer_t actnode = igraph_vector_int_tail(&stack); + igraph_integer_t edge = FIRST(actnode) + CURRENT(actnode); + igraph_integer_t 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_int_push_back(&stack, edge)); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_integer_t idx; + for (idx = igraph_vector_int_size(&stack) - 2; + idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) { + igraph_integer_t 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_int_size(&stack) - 2; + idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) { + igraph_integer_t 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_int_pop_back(&stack); + igraph_vector_int_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_int_empty(&stack)) { + igraph_integer_t pl = igraph_vector_int_size(&stack); + igraph_real_t thisflow = EXCESS(target); + for (i = 2; i < pl; i += 2) { + igraph_integer_t edge = VECTOR(stack)[i]; + igraph_real_t rcap = MYCAP(edge); + if (rcap < thisflow) { + thisflow = rcap; + } + } + for (i = 2; i < pl; i += 2) { + igraph_integer_t edge = VECTOR(stack)[i]; + MYCAP(edge) -= thisflow; + } + } + + } while (!igraph_vector_int_empty(&stack)); + + igraph_vector_destroy(&mycap); + igraph_vector_int_destroy(&added); + igraph_vector_int_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++) { + igraph_integer_t pos = VECTOR(rank)[i]; + VECTOR(*flow)[j] = (capacity ? VECTOR(*capacity)[j] : 1.0) - + RESCAP(pos); + } + + igraph_vector_int_destroy(&rank); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_dbuckets_destroy(&ibuckets); + igraph_buckets_destroy(&buckets); + igraph_vector_int_destroy(¤t); + igraph_vector_int_destroy(&first); + igraph_vector_int_destroy(&distance); + igraph_vector_destroy(&excess); + igraph_vector_destroy(&rescap); + igraph_vector_int_destroy(&rev); + igraph_vector_int_destroy(&to); + igraph_dqueue_int_destroy(&bfsq); + IGRAPH_FINALLY_CLEAN(10); + + return IGRAPH_SUCCESS; +} + +/** + * \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 + * https://doi.org/10.1145/48014.61051. + * + * + * 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 \ref + * 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. + */ + +igraph_error_t 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=*/ NULL, /*cut=*/ NULL, + /*partition=*/ NULL, /*partition1=*/ NULL, + 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 (\p source) to + * another vertex (\p 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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_st_mincut + * \brief 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 an initialized 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 an initialized 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 an initialized 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(). + */ + +igraph_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, + igraph_vector_int_t *cut, igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity) { + + return igraph_maxflow(graph, value, /*flow=*/ NULL, + cut, partition, partition2, + source, target, capacity, NULL); +} + +/* + * This is the Stoer-Wagner algorithm, it works for calculating the + * minimum cut for undirected graphs, for the whole graph. + * I.e. this is basically the edge-connectivity of the graph. + * It can also calculate the cut itself, not just the cut value. + */ + +static igraph_error_t igraph_i_mincut_undirected( + const igraph_t *graph, + igraph_real_t *res, + igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_vector_int_t *cut, + const igraph_vector_t *capacity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + igraph_i_cutheap_t heap; + igraph_real_t mincut = IGRAPH_INFINITY; /* infinity */ + igraph_integer_t i; + + igraph_adjlist_t adjlist; + igraph_inclist_t inclist; + + igraph_vector_int_t mergehist; + igraph_bool_t calc_cut = partition || partition2 || cut; + igraph_integer_t act_step = 0, mincut_step = 0; + + if (capacity && igraph_vector_size(capacity) != no_of_edges) { + IGRAPH_ERROR("Invalid capacity vector size", IGRAPH_EINVAL); + } + + /* Check if the graph is connected at all */ + { + igraph_vector_int_t memb; + igraph_vector_int_t csize; + igraph_integer_t no; + IGRAPH_VECTOR_INT_INIT_FINALLY(&memb, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&csize, 0); + IGRAPH_CHECK(igraph_connected_components(graph, &memb, &csize, &no, IGRAPH_WEAK)); + if (no != 1) { + if (res) { + *res = 0; + } + if (cut) { + igraph_vector_int_clear(cut); + } + if (partition) { + igraph_integer_t j = 0; + IGRAPH_CHECK(igraph_vector_int_resize(partition, + VECTOR(csize)[0])); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(memb)[i] == 0) { + VECTOR(*partition)[j++] = i; + } + } + } + if (partition2) { + igraph_integer_t j = 0; + IGRAPH_CHECK(igraph_vector_int_resize(partition2, no_of_nodes - + VECTOR(csize)[0])); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(memb)[i] != 0) { + VECTOR(*partition2)[j++] = i; + } + } + } + } + igraph_vector_int_destroy(&csize); + igraph_vector_int_destroy(&memb); + IGRAPH_FINALLY_CLEAN(2); + + if (no != 1) { + return IGRAPH_SUCCESS; + } + } + + if (calc_cut) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&mergehist, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&mergehist, no_of_nodes * 2)); + } + + IGRAPH_CHECK(igraph_i_cutheap_init(&heap, no_of_nodes)); + IGRAPH_FINALLY(igraph_i_cutheap_destroy, &heap); + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + while (igraph_i_cutheap_size(&heap) >= 2) { + + igraph_integer_t last; + igraph_real_t acut; + igraph_integer_t 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 = VECTOR(*edges)[i]; + igraph_integer_t to = VECTOR(*neis)[i]; + igraph_real_t weight = capacity ? VECTOR(*capacity)[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_int_push_back(&mergehist, a)); + IGRAPH_CHECK(igraph_vector_int_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 = VECTOR(*neis)[i]; + igraph_integer_t 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) { + igraph_integer_t bignode = VECTOR(mergehist)[2 * mincut_step + 1]; + igraph_integer_t i, idx; + igraph_integer_t size = 1; + bool *mark; + + mark = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(mark, "Not enough memory for minimum cut."); + IGRAPH_FINALLY(igraph_free, mark); + + /* first count the vertices in the partition */ + mark[bignode] = true; + for (i = mincut_step - 1; i >= 0; i--) { + if ( mark[ VECTOR(mergehist)[2 * i] ] ) { + size++; + mark [ VECTOR(mergehist)[2 * i + 1] ] = true; + } + } + + /* now store them, if requested */ + if (partition) { + IGRAPH_CHECK(igraph_vector_int_resize(partition, size)); + idx = 0; + VECTOR(*partition)[idx++] = bignode; + for (i = mincut_step - 1; i >= 0; i--) { + if (mark[ VECTOR(mergehist)[2 * i] ]) { + VECTOR(*partition)[idx++] = VECTOR(mergehist)[2 * i + 1]; + } + } + } + + /* The other partition too? */ + if (partition2) { + IGRAPH_CHECK(igraph_vector_int_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_int_clear(&mergehist); + for (i = 0; i < no_of_edges; i++) { + igraph_edge(graph, i, &from, &to); + if ((mark[from] && !mark[to]) || + (mark[to] && !mark[from])) { + IGRAPH_CHECK(igraph_vector_int_push_back(&mergehist, i)); + } + } + igraph_vector_int_clear(cut); + IGRAPH_CHECK(igraph_vector_int_append(cut, &mergehist)); + } + + IGRAPH_FREE(mark); + igraph_vector_int_destroy(&mergehist); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_mincut_directed( + const igraph_t *graph, + igraph_real_t *value, + igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_vector_int_t *cut, + const igraph_vector_t *capacity) { + + igraph_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_real_t flow; + igraph_real_t minmaxflow = IGRAPH_INFINITY; + igraph_vector_int_t mypartition, mypartition2, mycut; + igraph_vector_int_t *ppartition = 0, *ppartition2 = 0, *pcut = 0; + igraph_vector_int_t bestpartition, bestpartition2, bestcut; + + if (partition) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&bestpartition, 0); + } + if (partition2) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&bestpartition2, 0); + } + if (cut) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&bestcut, 0); + } + + if (partition) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&mypartition, 0); + ppartition = &mypartition; + } + if (partition2) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&mypartition2, 0); + ppartition2 = &mypartition2; + } + if (cut) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&mycut, 0); + pcut = &mycut; + } + + for (i = 1; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_maxflow(graph, /*value=*/ &flow, /*flow=*/ NULL, + pcut, ppartition, ppartition2, /*source=*/ 0, + /*target=*/ i, capacity, NULL)); + if (flow < minmaxflow) { + minmaxflow = flow; + if (cut) { + IGRAPH_CHECK(igraph_vector_int_update(&bestcut, &mycut)); + } + if (partition) { + IGRAPH_CHECK(igraph_vector_int_update(&bestpartition, &mypartition)); + } + if (partition2) { + IGRAPH_CHECK(igraph_vector_int_update(&bestpartition2, &mypartition2)); + } + + if (minmaxflow == 0) { + break; + } + } + IGRAPH_CHECK(igraph_maxflow(graph, /*value=*/ &flow, /*flow=*/ NULL, + pcut, ppartition, ppartition2, + /*source=*/ i, + /*target=*/ 0, capacity, NULL)); + if (flow < minmaxflow) { + minmaxflow = flow; + if (cut) { + IGRAPH_CHECK(igraph_vector_int_update(&bestcut, &mycut)); + } + if (partition) { + IGRAPH_CHECK(igraph_vector_int_update(&bestpartition, &mypartition)); + } + if (partition2) { + IGRAPH_CHECK(igraph_vector_int_update(&bestpartition2, &mypartition2)); + } + + if (minmaxflow == 0) { + break; + } + } + } + + if (value) { + *value = minmaxflow; + } + + if (cut) { + igraph_vector_int_destroy(&mycut); + IGRAPH_FINALLY_CLEAN(1); + } + if (partition) { + igraph_vector_int_destroy(&mypartition); + IGRAPH_FINALLY_CLEAN(1); + } + if (partition2) { + igraph_vector_int_destroy(&mypartition2); + IGRAPH_FINALLY_CLEAN(1); + } + if (cut) { + IGRAPH_CHECK(igraph_vector_int_update(cut, &bestcut)); + igraph_vector_int_destroy(&bestcut); + IGRAPH_FINALLY_CLEAN(1); + } + if (partition2) { + IGRAPH_CHECK(igraph_vector_int_update(partition2, &bestpartition2)); + igraph_vector_int_destroy(&bestpartition2); + IGRAPH_FINALLY_CLEAN(1); + } + if (partition) { + IGRAPH_CHECK(igraph_vector_int_update(partition, &bestpartition)); + igraph_vector_int_destroy(&bestpartition); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \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 + */ + +igraph_error_t igraph_mincut(const igraph_t *graph, + igraph_real_t *value, + igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_vector_int_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 IGRAPH_SUCCESS; +} + + +static igraph_error_t 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(). + */ + +igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, + const igraph_vector_t *capacity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_real_t minmaxflow, flow; + igraph_integer_t i; + + minmaxflow = IGRAPH_INFINITY; + + if (!igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_mincut_value_undirected(graph, res, capacity)); + return IGRAPH_SUCCESS; + } + + for (i = 1; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, 0, i, + capacity, 0)); + if (flow < minmaxflow) { + minmaxflow = flow; + if (flow == 0) { + break; + } + } + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, i, 0, + capacity, 0)); + if (flow < minmaxflow) { + minmaxflow = flow; + if (flow == 0) { + break; + } + } + } + + if (res) { + *res = minmaxflow; + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_st_vertex_connectivity_check_errors( + const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target, + igraph_vconn_nei_t neighbors, + igraph_bool_t *done, + igraph_integer_t *no_conn) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t eid; + igraph_bool_t conn; + *done = true; + *no_conn = 0; + + if (source == target) { + IGRAPH_ERROR("Source and target vertices are the same.", IGRAPH_EINVAL); + } + + 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_adjacent(graph, source, target, &conn)); + if (conn) { + IGRAPH_ERROR("Source and target vertices connected.", IGRAPH_EINVAL); + } + break; + case IGRAPH_VCONN_NEI_NEGATIVE: + IGRAPH_CHECK(igraph_are_adjacent(graph, source, target, &conn)); + if (conn) { + *res = -1; + return IGRAPH_SUCCESS; + } + break; + case IGRAPH_VCONN_NEI_NUMBER_OF_NODES: + IGRAPH_CHECK(igraph_are_adjacent(graph, source, target, &conn)); + if (conn) { + *res = no_of_nodes; + return IGRAPH_SUCCESS; + } + break; + case IGRAPH_VCONN_NEI_IGNORE: + IGRAPH_CHECK(igraph_get_eid(graph, &eid, source, target, IGRAPH_DIRECTED, /*error=*/ false)); + if (eid >= 0) { + IGRAPH_CHECK(igraph_count_multiple_1(graph, no_conn, eid)); + } + break; + default: + IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'.", IGRAPH_EINVAL); + break; + } + *done = false; + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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_vcount(graph); + igraph_integer_t no_of_edges; + igraph_real_t real_res; + igraph_t newgraph; + igraph_integer_t i, len; + igraph_bool_t done; + igraph_integer_t no_conn; + igraph_vector_int_t incs; + igraph_vector_t capacity; + + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_check_errors(graph, res, source, target, neighbors, &done, &no_conn)); + if (done) { + return IGRAPH_SUCCESS; + } + + /* Create the new graph */ + IGRAPH_CHECK(igraph_i_split_vertices(graph, &newgraph)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + + /* Create the capacity vector, fill it with ones */ + no_of_edges = igraph_ecount(&newgraph); + IGRAPH_VECTOR_INIT_FINALLY(&capacity, no_of_edges); + igraph_vector_fill(&capacity, 1); + + /* "Disable" the edges incident on the input half of the source vertex + * and the output half of the target vertex */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&incs, 0); + IGRAPH_CHECK(igraph_incident(&newgraph, &incs, source + no_of_nodes, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (i = 0; i < len; i++) { + VECTOR(capacity)[VECTOR(incs)[i]] = 0; + } + IGRAPH_CHECK(igraph_incident(&newgraph, &incs, target, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (i = 0; i < len; i++) { + VECTOR(capacity)[VECTOR(incs)[i]] = 0; + } + igraph_vector_int_destroy(&incs); + IGRAPH_FINALLY_CLEAN(1); + + /* Do the maximum flow */ + IGRAPH_CHECK(igraph_maxflow_value(&newgraph, &real_res, + source, target + no_of_nodes, &capacity, 0)); + *res = (igraph_integer_t) real_res; + + *res -= no_conn; + + igraph_vector_destroy(&capacity); + igraph_destroy(&newgraph); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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_t newgraph; + igraph_bool_t done; + igraph_integer_t no_conn; + + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_check_errors(graph, res, source, target, neighbors, &done, &no_conn)); + if (done) { + return IGRAPH_SUCCESS; + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_st_vertex_connectivity + * \brief The vertex connectivity of a pair of vertices. + * + * The vertex connectivity of two vertices (\p source and + * \p target) is the minimum number of vertices that must be + * deleted to eliminate all paths from \p source to \p + * 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, assuming no direct edges between them. + * + * + * 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_NEI_NEGATIVE, return -1. + * \c IGRAPH_VCONN_NEI_NUMBER_OF_NODES, return the number of nodes. + * \c IGRAPH_VCONN_NEI_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. + * \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(). + */ + +igraph_error_t 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 (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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_vertex_connectivity_directed( + const igraph_t *graph, + igraph_integer_t *res, + igraph_bool_t all_edges_are_mutual) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges; + igraph_integer_t i, j, k, len; + igraph_integer_t minconn = no_of_nodes - 1, conn = 0; + igraph_t split_graph; + igraph_vector_t capacity; + igraph_bool_t done; + igraph_integer_t dummy_num_connections; + igraph_vector_int_t incs; + igraph_real_t real_res; + + /* Create the new graph */ + IGRAPH_CHECK(igraph_i_split_vertices(graph, &split_graph)); + IGRAPH_FINALLY(igraph_destroy, &split_graph); + + /* Create the capacity vector, fill it with ones */ + no_of_edges = igraph_ecount(&split_graph); + IGRAPH_VECTOR_INIT_FINALLY(&capacity, no_of_edges); + igraph_vector_fill(&capacity, 1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&incs, 0); + + for (i = 0; i < no_of_nodes; i++) { + for (j = all_edges_are_mutual ? i + 1 : 0; j < no_of_nodes; j++) { + if (i == j) { + continue; + } + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Check for easy cases */ + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_check_errors( + graph, &conn, i, j, IGRAPH_VCONN_NEI_NUMBER_OF_NODES, &done, + &dummy_num_connections + )); + + /* 'done' will be set to true if the two vertices are already + * connected, and in this case 'res' will be set to the number of + * nodes-1. + * + * Also, since we used IGRAPH_VCONN_NEI_NUMBER_OF_NODES, + * dummy_num_connections will always be zero, no need to deal with + * it */ + IGRAPH_ASSERT(dummy_num_connections == 0); + + if (!done) { + /* "Disable" the edges incident on the input half of the source vertex + * and the output half of the target vertex */ + IGRAPH_CHECK(igraph_incident(&split_graph, &incs, i + no_of_nodes, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (k = 0; k < len; k++) { + VECTOR(capacity)[VECTOR(incs)[k]] = 0; + } + IGRAPH_CHECK(igraph_incident(&split_graph, &incs, j, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (k = 0; k < len; k++) { + VECTOR(capacity)[VECTOR(incs)[k]] = 0; + } + + /* Do the maximum flow */ + IGRAPH_CHECK(igraph_maxflow_value( + &split_graph, &real_res, i, j + no_of_nodes, &capacity, 0 + )); + + /* Restore the capacities */ + IGRAPH_CHECK(igraph_incident(&split_graph, &incs, i + no_of_nodes, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (k = 0; k < len; k++) { + VECTOR(capacity)[VECTOR(incs)[k]] = 1; + } + IGRAPH_CHECK(igraph_incident(&split_graph, &incs, j, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (k = 0; k < len; k++) { + VECTOR(capacity)[VECTOR(incs)[k]] = 1; + } + + conn = (igraph_integer_t) real_res; + } + + if (conn < minconn) { + minconn = conn; + if (conn == 0) { + break; + } + } + } + + if (minconn == 0) { + break; + } + } + + if (res) { + *res = minconn; + } + + igraph_vector_int_destroy(&incs); + igraph_vector_destroy(&capacity); + igraph_destroy(&split_graph); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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, /* all_edges_are_mutual = */ true)); + + igraph_destroy(&newgraph); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) + * + * Check if the graph is connected, and if its minimum degree is 1. + * This is sufficient to determine both the vertex and edge connectivity, + * which are returned in 'res'. 'found' will indicate if the connectivity + * could be determined. + */ +static igraph_error_t igraph_i_connectivity_checks( + const igraph_t *graph, + igraph_integer_t *res, + igraph_bool_t *found) { + + igraph_bool_t conn; + *found = false; + + if (igraph_vcount(graph) == 0) { + *res = 0; + *found = true; + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_STRONG)); + if (!conn) { + *res = 0; + *found = true; + } else { + igraph_vector_int_t degree; + IGRAPH_VECTOR_INT_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_int_min(°ree) == 1) { + *res = 1; + *found = true; + } + } else { + /* directed, check both in- & out-degree */ + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_LOOPS)); + if (igraph_vector_int_min(°ree) == 1) { + *res = 1; + *found = true; + } else { + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS)); + if (igraph_vector_int_min(°ree) == 1) { + *res = 1; + *found = true; + } + } + } + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vertex_connectivity + * \brief 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 + * https://doi.org/10.1111/0081-1750.00098. + * + * \param graph The input graph. + * \param res Pointer to an integer, the result will be stored here. + * \param checks Logical constant. Whether to check if the graph is + * connected or complete 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 1 then the vertex connectivity is also + * 1. If the graph is complete, the connectivity is the vertex count + * minus 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(). + */ + +igraph_error_t igraph_vertex_connectivity( + const igraph_t *graph, igraph_integer_t *res, + igraph_bool_t checks) { + + igraph_bool_t ret = false; + + if (checks) { + IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret)); + } + + if (checks && !ret) { + /* The vertex connectivity of a complete graph is vcount-1 by definition. + * This check cannot be performed within igraph_i_connectivity_checks(), + * as that function is used both for vertex and edge connectivities. + * Checking if the graph is complete does not suffice to determine the + * edge connectivity of a complete multigraph. */ + igraph_bool_t complete; + IGRAPH_CHECK(igraph_is_complete(graph, &complete)); + if (complete) { + *res = igraph_vcount(graph) - 1; + ret = true; + } + } + + /* Are we done yet? */ + if (!ret) { + if (igraph_is_directed(graph)) { + IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(graph, res, /* all_edges_are_mutual = */ false)); + } else { + IGRAPH_CHECK(igraph_i_vertex_connectivity_undirected(graph, res)); + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_st_edge_connectivity + * \brief Edge connectivity of a pair of vertices. + * + * The edge connectivity of two vertices (\p source and \p target) is the + * minimum number of edges that have to be deleted from the graph to eliminate + * all paths from \p source to \p 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_disjoint_paths(), + * \ref igraph_edge_connectivity(), + * \ref igraph_st_vertex_connectivity(), \ref + * igraph_vertex_connectivity(). + */ + +igraph_error_t 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("The source and target vertices must be different.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0)); + *res = (igraph_integer_t) flow; + + return IGRAPH_SUCCESS; +} + + +/** + * \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 + * https://doi.org/10.1111/0081-1750.00098. + * + * \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(). + */ + +igraph_error_t igraph_edge_connectivity(const igraph_t *graph, + igraph_integer_t *res, + igraph_bool_t checks) { + + igraph_bool_t ret = false; + 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 IGRAPH_SUCCESS; + } + + /* 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 IGRAPH_SUCCESS; +} + +/** + * \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(). + */ + +igraph_error_t 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 when the source and target are the same.", + IGRAPH_UNIMPLEMENTED); + } + + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0)); + + *res = (igraph_integer_t) flow; + + return IGRAPH_SUCCESS; +} + +/** + * \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, other than the endpoints. This function computes + * the largest number of such paths that can be constructed between + * a source and a target vertex. The calculation is performed by using maximum + * flow techniques. + * + * + * When there are no edges from the source to the target, the number of + * vertex-disjoint paths is the same as the vertex connectivity of + * the two vertices. When some edges are present, each one of them + * contributes one extra path. + * + * \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_st_vertex_connectivity(), \ref igraph_maxflow_value(). + */ + +igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target) { + + igraph_vector_int_t eids; + + if (source == target) { + IGRAPH_ERROR("Not implemented when the source and target are the same.", + IGRAPH_UNIMPLEMENTED); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 4); + IGRAPH_CHECK(igraph_get_all_eids_between(graph, &eids, source, target, /*directed*/ true)); + + 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)); + } + + *res += igraph_vector_int_size(&eids); + + igraph_vector_int_destroy(&eids); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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(). + */ + +igraph_error_t 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(). + */ + +igraph_error_t igraph_cohesion(const igraph_t *graph, + igraph_integer_t *res, + igraph_bool_t checks) { + + IGRAPH_CHECK(igraph_vertex_connectivity(graph, res, checks)); + return IGRAPH_SUCCESS; +} + +/** + * \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: + * + * + * Reference: + * + * + * Gusfield D: Very simple methods for all pairs network flow analysis. SIAM J + * Comput 19(1):143-155, 1990 + * https://doi.org/10.1137/0219009. + * + * \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() + */ +igraph_error_t 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_int_t neighbors; + igraph_vector_t flow_values; + igraph_vector_int_t partition; + igraph_vector_int_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_INT_INIT_FINALLY(&neighbors, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&flow_values, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&partition, 0); + IGRAPH_VECTOR_INT_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)[source]; + + /* Find the maximum flow between source and target */ + IGRAPH_CHECK(igraph_maxflow(graph, &flow_value, NULL, NULL, &partition, &partition2, + source, target, capacity, 0)); + + /* Store the maximum flow */ + VECTOR(flow_values)[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_int_size(&partition); + for (i = 0; i < n; i++) { + mid = VECTOR(partition)[i]; + if (mid != source) { + if (VECTOR(neighbors)[mid] == target) { + VECTOR(neighbors)[mid] = source; + } else if (VECTOR(neighbors)[target] == mid) { + VECTOR(neighbors)[target] = source; + VECTOR(neighbors)[source] = mid; + VECTOR(flow_values)[source] = VECTOR(flow_values)[target]; + VECTOR(flow_values)[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_int_resize(&partition, no_of_nodes > 0 ? 2 * (no_of_nodes - 1) : 0)); + for (i = 1, mid = 0; i < no_of_nodes; i++, mid += 2) { + VECTOR(partition)[mid] = i; + VECTOR(partition)[mid + 1] = VECTOR(neighbors)[i]; + } + + /* Create the tree graph; we use igraph_subgraph_from_edges here to keep the + * graph and vertex attributes */ + IGRAPH_CHECK(igraph_subgraph_from_edges(graph, tree, igraph_ess_none(), 0)); + IGRAPH_CHECK(igraph_add_edges(tree, &partition, 0)); + + /* Free the allocated memory */ + igraph_vector_int_destroy(&partition2); + igraph_vector_int_destroy(&partition); + igraph_vector_int_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/flow/flow_conversion.c b/src/flow/flow_conversion.c new file mode 100644 index 0000000..01b20a5 --- /dev/null +++ b/src/flow/flow_conversion.c @@ -0,0 +1,93 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2023 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_conversion.h" +#include "igraph_interface.h" + +#include "flow/flow_internal.h" + +/** + * \function igraph_i_split_vertices + * \brief Splits each vertex in the graph into an input and an output vertex. + * + * This function implements a transformation that allows us to calculate the + * vertex connectivity of a directed graph either for a specific s-t pair or + * for all s-t pairs using flows. The transformation splits each vertex into + * an input vertex and an output vertex. All inbound edges of the original + * vertex are rewired to point to the input vertex, and all outbound edges of + * the original vertex are rewired to original from the output vertex, while + * adding a single directed edge from the input vertex to the output vertex. + * + * + * s-t vertex connectivities can then be calculated on this modified graph by + * setting the capacity of each edge to 1, \em except for the following edges: + * the edges incident of the input half of the source vertex and the edges + * incident on the output half of the target vertex. The max flow on this + * modified graph will be equal to the s-t vertex connectivity of the original + * graph. + * + * + * This function prepares the graph only but does not supply a capacity vector; + * it is the responsibility of the caller to provide the capacities. + * + * + * If the original graph had \em n vertices, he function guarantees that the + * first \em n vertices of the result graph will correspond to the \em output + * halves of the vertices and the remaining \em n vertices will correspond to + * the \em input halves, in the same order as in the original graph. + * + * \param graph the input graph + * \param result an uninitialized graph object; the result will be returned here + */ +igraph_error_t igraph_i_split_vertices(const igraph_t* graph, igraph_t* result) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i; + igraph_vector_int_t edges; + + if (!igraph_is_directed(graph)) { + IGRAPH_ERROR("Input graph must be directed.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2 * (no_of_edges + no_of_nodes))); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_int_resize(&edges, 2 * (no_of_edges + no_of_nodes))); + + for (i = 0; i < 2 * no_of_edges; i += 2) { + igraph_integer_t to = VECTOR(edges)[i + 1]; + 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(result, &edges, 2 * no_of_nodes, IGRAPH_DIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/flow/flow_internal.h b/src/flow/flow_internal.h new file mode 100644 index 0000000..a269d00 --- /dev/null +++ b/src/flow/flow_internal.h @@ -0,0 +1,44 @@ +/* -*- 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_datatype.h" +#include "igraph_decls.h" +#include "igraph_types.h" + +#include "core/estack.h" +#include "core/marked_queue.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_all_st_cuts_pivot( + const igraph_t *graph, const igraph_marked_queue_int_t *S, + const igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, + igraph_integer_t *v, igraph_vector_int_t *Isv, void *arg); + +igraph_error_t igraph_i_split_vertices(const igraph_t* graph, igraph_t* result); + +__END_DECLS + +#endif diff --git a/src/flow/st-cuts.c b/src/flow/st-cuts.c new file mode 100644 index 0000000..6823b0d --- /dev/null +++ b/src/flow/st-cuts.c @@ -0,0 +1,1575 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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_flow.h" + +#include "igraph_adjlist.h" +#include "igraph_bitset.h" +#include "igraph_constants.h" +#include "igraph_constructors.h" +#include "igraph_components.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_operators.h" +#include "igraph_stack.h" +#include "igraph_visitor.h" +#include "igraph_topology.h" + +#include "core/estack.h" +#include "core/marked_queue.h" +#include "flow/flow_internal.h" +#include "graph/attributes.h" +#include "math/safe_intop.h" + +typedef igraph_error_t igraph_provan_shier_pivot_t(const igraph_t *graph, + const igraph_marked_queue_int_t *S, + const igraph_estack_t *T, + igraph_integer_t source, + igraph_integer_t target, + igraph_integer_t *v, + igraph_vector_int_t *Isv, + void *arg); + +/** + * \function igraph_even_tarjan_reduction + * \brief Even-Tarjan reduction of a graph. + * + * A digraph is created with twice as many vertices and edges. For each + * original vertex \c 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 \c i to \c 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. + * https://doi.org/10.1137/0204043 + * + * \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 indicate infinity). + * \return Error code. + * + * Time complexity: O(|E|+|V|). + * + * \example examples/simple/even_tarjan.c + */ + +igraph_error_t igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, + igraph_vector_t *capacity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + igraph_integer_t new_no_of_nodes; + igraph_integer_t new_no_of_edges = no_of_edges * 2; + + igraph_vector_int_t edges; + igraph_integer_t edgeptr = 0, capptr = 0; + igraph_integer_t i; + + IGRAPH_SAFE_MULT(no_of_nodes, 2, &new_no_of_nodes); + IGRAPH_SAFE_ADD(new_no_of_edges, no_of_nodes, &new_no_of_edges); + + /* To ensure the size of the edges vector will not overflow. */ + if (new_no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + + IGRAPH_VECTOR_INT_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++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t 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, new_no_of_nodes, + IGRAPH_DIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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_int_t *tmp) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, no_new_edges = 0; + igraph_integer_t 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_int_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) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t 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, no_of_nodes, + IGRAPH_DIRECTED)); + + return IGRAPH_SUCCESS; +} + +igraph_error_t 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_int_t tmp; + igraph_integer_t 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_INT_INIT_FINALLY(&tmp, 0); + + IGRAPH_CHECK(igraph_i_residual_graph(graph, capacity, residual, + residual_capacity, flow, &tmp)); + + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_reverse_residual_graph(const igraph_t *graph, + const igraph_vector_t *capacity, + igraph_t *residual, + const igraph_vector_t *flow, + igraph_vector_int_t *tmp) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, no_new_edges = 0; + igraph_integer_t 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_int_resize(tmp, no_new_edges * 2)); + + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t 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, no_of_nodes, + IGRAPH_DIRECTED)); + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_reverse_residual_graph(const igraph_t *graph, + const igraph_vector_t *capacity, + igraph_t *residual, + const igraph_vector_t *flow) { + igraph_vector_int_t tmp; + igraph_integer_t 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_INT_INIT_FINALLY(&tmp, 0); + + IGRAPH_CHECK(igraph_i_reverse_residual_graph(graph, capacity, residual, + flow, &tmp)); + + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +typedef struct igraph_i_dbucket_t { + igraph_vector_int_t head; + igraph_vector_int_t next; +} igraph_i_dbucket_t; + +static igraph_error_t igraph_i_dbucket_init(igraph_i_dbucket_t *buck, igraph_integer_t size) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&buck->head, size); + IGRAPH_CHECK(igraph_vector_int_init(&buck->next, size)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +static void igraph_i_dbucket_destroy(igraph_i_dbucket_t *buck) { + igraph_vector_int_destroy(&buck->head); + igraph_vector_int_destroy(&buck->next); +} + +static igraph_error_t igraph_i_dbucket_insert(igraph_i_dbucket_t *buck, igraph_integer_t bid, + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +static igraph_bool_t igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck, + igraph_integer_t bid) { + return VECTOR(buck->head)[bid] == 0; +} + +static igraph_integer_t igraph_i_dbucket_delete(igraph_i_dbucket_t *buck, igraph_integer_t bid) { + igraph_integer_t elem = VECTOR(buck->head)[bid] - 1; + VECTOR(buck->head)[bid] = VECTOR(buck->next)[elem]; + return elem; +} + +static igraph_error_t igraph_i_dominator_LINK(igraph_integer_t v, igraph_integer_t w, + igraph_vector_int_t *ancestor) { + VECTOR(*ancestor)[w] = v + 1; + return IGRAPH_SUCCESS; +} + +/* TODO: don't always reallocate path */ + +static igraph_error_t igraph_i_dominator_COMPRESS(igraph_integer_t v, + igraph_vector_int_t *ancestor, + igraph_vector_int_t *label, + igraph_vector_int_t *semi) { + igraph_stack_int_t path; + igraph_integer_t w = v; + igraph_integer_t top, pretop; + + IGRAPH_CHECK(igraph_stack_int_init(&path, 10)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &path); + + while (VECTOR(*ancestor)[w] != 0) { + IGRAPH_CHECK(igraph_stack_int_push(&path, w)); + w = VECTOR(*ancestor)[w] - 1; + } + + top = igraph_stack_int_pop(&path); + while (!igraph_stack_int_empty(&path)) { + pretop = igraph_stack_int_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_int_destroy(&path); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_integer_t igraph_i_dominator_EVAL(igraph_integer_t v, + igraph_vector_int_t *ancestor, + igraph_vector_int_t *label, + igraph_vector_int_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 + * \brief 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. + * https://doi.org/10.1145/357062.357071 + * + * \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 \p 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, -2 is stored here. For + * the root vertex itself, -1 is added. + * \param domtree Pointer to an \em uninitialized igraph_t, + * or \c 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. + * Graph and vertex attributes are preserved, but edge attributes + * are discarded. + * \param leftout Pointer to an initialized vector object, or \c NULL. If + * not \c 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 + */ + +igraph_error_t igraph_dominator_tree(const igraph_t *graph, + igraph_integer_t root, + igraph_vector_int_t *dom, + igraph_t *domtree, + igraph_vector_int_t *leftout, + igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + igraph_adjlist_t succ, pred; + igraph_vector_int_t parent; + igraph_vector_int_t semi; /* +1 always */ + igraph_vector_int_t vertex; /* +1 always */ + igraph_i_dbucket_t bucket; + igraph_vector_int_t ancestor; + igraph_vector_int_t label; + + igraph_neimode_t invmode = IGRAPH_REVERSE_MODE(mode); + + igraph_vector_int_t vdom, *mydom = dom; + + igraph_integer_t component_size = 0; + + if (root < 0 || root >= no_of_nodes) { + IGRAPH_ERROR("Invalid root vertex ID for dominator tree.", + IGRAPH_EINVVID); + } + + 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_int_resize(dom, no_of_nodes)); + } else { + mydom = &vdom; + IGRAPH_VECTOR_INT_INIT_FINALLY(mydom, no_of_nodes); + } + igraph_vector_int_fill(mydom, -2); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&parent, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&semi, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ancestor, no_of_nodes); + IGRAPH_CHECK(igraph_vector_int_init_range(&label, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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=*/ false, + /*order=*/ &vertex, + /*order_out=*/ NULL, /*parents=*/ &parent, + /*dist=*/ NULL, /*in_callback=*/ NULL, + /*out_callback=*/ NULL, /*extra=*/ NULL)); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (VECTOR(vertex)[i] >= 0) { + igraph_integer_t t = VECTOR(vertex)[i]; + VECTOR(semi)[t] = component_size + 1; + VECTOR(vertex)[component_size] = t + 1; + component_size++; + } + } + if (leftout) { + igraph_integer_t n = no_of_nodes - component_size; + igraph_integer_t p = 0, j; + IGRAPH_CHECK(igraph_vector_int_resize(leftout, n)); + for (j = 0; j < no_of_nodes && p < n; j++) { + if (VECTOR(parent)[j] < -1) { + VECTOR(*leftout)[p++] = j; + } + } + } + + /* We need to go over 'pred' because it should contain only the + edges towards the target vertex. */ + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *v = igraph_adjlist_get(&pred, i); + igraph_integer_t n = igraph_vector_int_size(v); + for (igraph_integer_t j = 0; j < n; ) { + igraph_integer_t v2 = VECTOR(*v)[j]; + if (VECTOR(parent)[v2] >= -1) { + 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 (igraph_integer_t i = component_size - 1; i > 0; i--) { + igraph_integer_t w = VECTOR(vertex)[i] - 1; + igraph_vector_int_t *predw = igraph_adjlist_get(&pred, w); + igraph_integer_t j, n = igraph_vector_int_size(predw); + for (j = 0; j < n; j++) { + igraph_integer_t v = VECTOR(*predw)[j]; + igraph_integer_t 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, VECTOR(vertex)[ VECTOR(semi)[w] - 1 ] - 1, w); + igraph_i_dominator_LINK(VECTOR(parent)[w], w, &ancestor); + while (!igraph_i_dbucket_empty(&bucket, VECTOR(parent)[w])) { + igraph_integer_t v = igraph_i_dbucket_delete(&bucket, VECTOR(parent)[w]); + igraph_integer_t 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 (igraph_integer_t i = 1; i < component_size; i++) { + igraph_integer_t w = VECTOR(vertex)[i] - 1; + if (VECTOR(*mydom)[w] != VECTOR(vertex)[VECTOR(semi)[w] - 1] - 1) { + VECTOR(*mydom)[w] = VECTOR(*mydom)[VECTOR(*mydom)[w]]; + } + } + VECTOR(*mydom)[root] = -1; + + igraph_i_dbucket_destroy(&bucket); + igraph_adjlist_destroy(&pred); + igraph_adjlist_destroy(&succ); + igraph_vector_int_destroy(&label); + igraph_vector_int_destroy(&ancestor); + igraph_vector_int_destroy(&vertex); + igraph_vector_int_destroy(&semi); + igraph_vector_int_destroy(&parent); + IGRAPH_FINALLY_CLEAN(8); + + if (domtree) { + igraph_vector_int_t edges; + igraph_integer_t ptr = 0; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, component_size * 2 - 2); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (i != root && VECTOR(*mydom)[i] >= 0) { + 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, no_of_nodes, + IGRAPH_DIRECTED)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_I_ATTRIBUTE_DESTROY(domtree); + IGRAPH_I_ATTRIBUTE_COPY(domtree, graph, + /*graph=*/ true, /*vertex=*/ true, /*edge=*/ false); + } + + if (!dom) { + igraph_vector_int_destroy(&vdom); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +typedef struct igraph_i_all_st_cuts_minimal_dfs_data_t { + igraph_stack_int_t *stack; + igraph_bitset_t *nomark; + const igraph_bitset_t *GammaX; + igraph_integer_t root; + const igraph_vector_int_t *map; +} igraph_i_all_st_cuts_minimal_dfs_data_t; + +static igraph_error_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_int_t *stack = data->stack; + igraph_bitset_t *nomark = data->nomark; + const igraph_bitset_t *GammaX = data->GammaX; + const igraph_vector_int_t *map = data->map; + igraph_integer_t realvid = VECTOR(*map)[vid]; + + IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist); + + if (IGRAPH_BIT_TEST(*GammaX, realvid)) { + if (!igraph_stack_int_empty(stack)) { + igraph_integer_t top = igraph_stack_int_top(stack); + IGRAPH_BIT_SET(*nomark, top); /* we just found a smaller one */ + } + IGRAPH_CHECK(igraph_stack_int_push(stack, realvid)); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_all_st_cuts_minimal_dfs_outcb( + 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_int_t *stack = data->stack; + const igraph_vector_int_t *map = data->map; + igraph_integer_t realvid = VECTOR(*map)[vid]; + + IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist); + + if (!igraph_stack_int_empty(stack) && + igraph_stack_int_top(stack) == realvid) { + igraph_stack_int_pop(stack); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_all_st_cuts_minimal(const igraph_t *graph, + const igraph_t *domtree, + igraph_integer_t root, + const igraph_marked_queue_int_t *X, + const igraph_bitset_t *GammaX, + const igraph_vector_int_t *invmap, + igraph_vector_int_t *minimal) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_stack_int_t stack; + igraph_bitset_t nomark; + igraph_i_all_st_cuts_minimal_dfs_data_t data; + igraph_integer_t i; + + IGRAPH_UNUSED(X); + + IGRAPH_STACK_INT_INIT_FINALLY(&stack, 10); + IGRAPH_BITSET_INIT_FINALLY(&nomark, no_of_nodes); + + 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. */ + igraph_bitset_not(&nomark, GammaX); + + /* 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, root, IGRAPH_IN, + /*unreachable=*/ false, /*order=*/ NULL, + /*order_out=*/ NULL, /*parents=*/ NULL, + /*dist=*/ NULL, + /*in_callback=*/ igraph_i_all_st_cuts_minimal_dfs_incb, + /*out_callback=*/ igraph_i_all_st_cuts_minimal_dfs_outcb, + /*extra=*/ &data)); + + igraph_vector_int_clear(minimal); + for (i = 0; i < no_of_nodes; i++) { + if (!IGRAPH_BIT_TEST(nomark, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(minimal, i)); + } + } + + igraph_bitset_destroy(&nomark); + igraph_stack_int_destroy(&stack); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/* not 'static' because used in igraph_all_st_cuts.c test program */ +igraph_error_t igraph_i_all_st_cuts_pivot( + const igraph_t *graph, const igraph_marked_queue_int_t *S, + const igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, + igraph_integer_t *v, igraph_vector_int_t *Isv, void *arg +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_t Sbar; + igraph_vector_int_t Sbar_map, Sbar_invmap; + igraph_vector_int_t keep; + igraph_t domtree; + igraph_vector_int_t leftout; + igraph_integer_t i, nomin, n; + igraph_integer_t root; + igraph_vector_int_t M; + igraph_bitset_t GammaS; + igraph_vector_int_t Nuv; + igraph_vector_int_t Isv_min; + igraph_vector_int_t GammaS_vec; + igraph_integer_t Sbar_size; + + IGRAPH_UNUSED(arg); + + /* We need to create the graph induced by Sbar */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_map, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_invmap, 0); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&keep, 0); + for (i = 0; i < no_of_nodes; i++) { + if (!igraph_marked_queue_int_iselement(S, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&keep, i)); + } + } + Sbar_size = igraph_vector_int_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_int_destroy(&keep); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_destroy, &Sbar); + + root = VECTOR(Sbar_map)[target] - 1; + + /* -------------------------------------------------------------*/ + /* Construct the dominator tree of Sbar */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&leftout, 0); + IGRAPH_CHECK(igraph_dominator_tree(&Sbar, 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_BITSET_INIT_FINALLY(&GammaS, no_of_nodes); + if (igraph_marked_queue_int_size(S) == 0) { + IGRAPH_BIT_SET(GammaS, VECTOR(Sbar_map)[source] - 1); + } else { + for (i = 0; i < no_of_nodes; i++) { + if (igraph_marked_queue_int_iselement(S, i)) { + igraph_vector_int_t neis; + igraph_integer_t j; + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, + IGRAPH_OUT)); + n = igraph_vector_int_size(&neis); + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (!igraph_marked_queue_int_iselement(S, nei)) { + IGRAPH_BIT_SET(GammaS, nei); + } + } + igraph_vector_int_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_int_size(&leftout); + for (i = 0; i < n; i++) { + VECTOR(leftout)[i] = VECTOR(Sbar_invmap)[VECTOR(leftout)[i]]; + IGRAPH_BIT_CLEAR(GammaS, VECTOR(leftout)[i]); + } + + IGRAPH_VECTOR_INT_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_int_clear(Isv); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Nuv, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&GammaS_vec, 0); + for (i = 0; i < no_of_nodes; i++) { + if (IGRAPH_BIT_TEST(GammaS, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&GammaS_vec, i)); + } + } + + nomin = igraph_vector_int_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. */ + igraph_integer_t min = VECTOR(Sbar_map)[ VECTOR(M)[i] ] - 1; + igraph_integer_t nuvsize, isvlen, j; + IGRAPH_CHECK(igraph_dfs(&domtree, min, IGRAPH_IN, + /*unreachable=*/ false, /*order=*/ &Nuv, + /*order_out=*/ NULL, /*parents=*/ NULL, /*dist=*/ NULL, + /*in_callback=*/ NULL, /*out_callback=*/ NULL, + /*extra=*/ NULL)); + /* Remove the negative values from the end of the vector */ + for (nuvsize = 0; nuvsize < Sbar_size; nuvsize++) { + igraph_integer_t t = VECTOR(Nuv)[nuvsize]; + if (t >= 0) { + VECTOR(Nuv)[nuvsize] = VECTOR(Sbar_invmap)[t]; + } else { + break; + } + } + igraph_vector_int_resize(&Nuv, nuvsize); /* shrinks, error safe */ + + /* -------------------------------------------------------------*/ + /* 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=*/ false, + /*restricted=*/ &Nuv, + /*order=*/ &Isv_min, /*rank=*/ NULL, + /*parents=*/ NULL, /*pred=*/ NULL, /*succ=*/ NULL, + /*dist=*/ NULL, /*callback=*/ NULL, /*extra=*/ NULL)); + for (isvlen = 0; isvlen < no_of_nodes; isvlen++) { + if (VECTOR(Isv_min)[isvlen] < 0) { + break; + } + } + igraph_vector_int_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++) { + igraph_integer_t u = VECTOR(Isv_min)[j]; + if (igraph_estack_iselement(T, u) || u == target) { + break; + } + } + /* We might have found one */ + if (j == isvlen) { + *v = VECTOR(M)[i]; + /* Calculate real Isv */ + IGRAPH_CHECK(igraph_vector_int_append(&Nuv, &leftout)); + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v, + /*roots=*/ NULL, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ false, /*restricted=*/ &Nuv, + /*order=*/ &Isv_min, /*rank=*/ NULL, + /*parents=*/ NULL, /*pred=*/ NULL, /*succ=*/ NULL, + /*dist=*/ NULL, /*callback=*/ NULL, /*extra=*/ NULL)); + for (isvlen = 0; isvlen < no_of_nodes; isvlen++) { + if (VECTOR(Isv_min)[isvlen] < 0) { + break; + } + } + igraph_vector_int_resize(&Isv_min, isvlen); + igraph_vector_int_update(Isv, &Isv_min); + + break; + } + } + + igraph_vector_int_destroy(&GammaS_vec); + igraph_vector_int_destroy(&Isv_min); + igraph_vector_int_destroy(&Nuv); + IGRAPH_FINALLY_CLEAN(3); + + igraph_vector_int_destroy(&M); + igraph_bitset_destroy(&GammaS); + igraph_destroy(&domtree); + igraph_vector_int_destroy(&leftout); + igraph_destroy(&Sbar); + igraph_vector_int_destroy(&Sbar_map); + igraph_vector_int_destroy(&Sbar_invmap); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/* TODO: This is a temporary recursive version */ + +static igraph_error_t igraph_i_provan_shier_list_recursive( + const igraph_t *graph, igraph_marked_queue_int_t *S, + igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, + igraph_vector_int_list_t *result, igraph_provan_shier_pivot_t *pivot, + igraph_vector_int_t *Isv, void *pivot_arg +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t v = 0; + igraph_integer_t i, n; + + pivot(graph, S, T, source, target, &v, Isv, pivot_arg); + + if (igraph_vector_int_empty(Isv)) { + if (igraph_marked_queue_int_size(S) != 0 && igraph_marked_queue_int_size(S) != no_of_nodes) { + igraph_vector_int_t *vec; + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(result, &vec)); + IGRAPH_CHECK(igraph_marked_queue_int_as_vector(S, vec)); + } + } else { + /* Add Isv to S */ + IGRAPH_CHECK(igraph_marked_queue_int_start_batch(S)); + n = igraph_vector_int_size(Isv); + for (i = 0; i < n; i++) { + if (!igraph_marked_queue_int_iselement(S, VECTOR(*Isv)[i])) { + IGRAPH_CHECK(igraph_marked_queue_int_push(S, VECTOR(*Isv)[i])); + } + } + igraph_vector_int_clear(Isv); + + /* Go down right in the search tree */ + IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( + graph, S, T, source, target, result, pivot, Isv, pivot_arg)); + + /* Take out Isv from S */ + igraph_marked_queue_int_pop_back_batch(S); + + /* Put v into T */ + IGRAPH_CHECK(igraph_estack_push(T, v)); + + /* Go down left in the search tree */ + IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( + graph, S, T, source, target, result, pivot, Isv, pivot_arg)); + + /* Take out v from T */ + igraph_estack_pop(T); + + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_provan_shier_list( + const igraph_t *graph, igraph_marked_queue_int_t *S, + igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, + igraph_vector_int_list_t *result, igraph_provan_shier_pivot_t *pivot, + void *pivot_arg +) { + igraph_vector_int_t Isv; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv, 0); + + IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( + graph, S, T, source, target, result, pivot, &Isv, pivot_arg + )); + + /* Reverse the result to stay compatible with versions before 0.10.3 */ + IGRAPH_CHECK(igraph_vector_int_list_reverse(result)); + + igraph_vector_int_destroy(&Isv); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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 list of integer vectors, the cuts are stored + * here. Each vector will contain the IDs of the edges in + * the cut. This argument is ignored if it is a null pointer. + * \param partition1s An initialized list of integer vectors, 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. + * \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. + */ + +igraph_error_t igraph_all_st_cuts(const igraph_t *graph, + igraph_vector_int_list_t *cuts, + igraph_vector_int_list_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. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_marked_queue_int_t S; + igraph_estack_t T; + igraph_vector_int_list_t *mypartition1s = partition1s, vpartition1s; + igraph_vector_int_t cut; + igraph_integer_t 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_int_list_init(mypartition1s, 0)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, mypartition1s); + } else { + igraph_vector_int_list_clear(mypartition1s); + } + + IGRAPH_CHECK(igraph_marked_queue_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_marked_queue_int_destroy, &S); + IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0)); + IGRAPH_FINALLY(igraph_estack_destroy, &T); + IGRAPH_VECTOR_INT_INIT_FINALLY(&cut, 0); + + /* 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_int_list_size(mypartition1s); + + if (cuts) { + igraph_vector_int_t inS; + IGRAPH_CHECK(igraph_vector_int_init(&inS, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &inS); + igraph_vector_int_list_clear(cuts); + IGRAPH_CHECK(igraph_vector_int_list_reserve(cuts, nocuts)); + for (i = 0; i < nocuts; i++) { + igraph_vector_int_t *part = igraph_vector_int_list_get_ptr(mypartition1s, i); + igraph_integer_t cutsize = 0; + igraph_integer_t j, partlen = igraph_vector_int_size(part); + /* Mark elements */ + for (j = 0; j < partlen; j++) { + igraph_integer_t v = VECTOR(*part)[j]; + VECTOR(inS)[v] = i + 1; + } + /* Check how many edges */ + for (j = 0; j < no_of_edges; j++) { + igraph_integer_t from = IGRAPH_FROM(graph, j); + igraph_integer_t to = IGRAPH_TO(graph, j); + igraph_integer_t pfrom = VECTOR(inS)[from]; + igraph_integer_t pto = VECTOR(inS)[to]; + if (pfrom == i + 1 && pto != i + 1) { + cutsize++; + } + } + /* Add the edges */ + IGRAPH_CHECK(igraph_vector_int_resize(&cut, cutsize)); + cutsize = 0; + for (j = 0; j < no_of_edges; j++) { + igraph_integer_t from = IGRAPH_FROM(graph, j); + igraph_integer_t to = IGRAPH_TO(graph, j); + igraph_integer_t pfrom = VECTOR(inS)[from]; + igraph_integer_t pto = VECTOR(inS)[to]; + if ((pfrom == i + 1 && pto != i + 1)) { + VECTOR(cut)[cutsize++] = j; + } + } + /* Add the vector to 'cuts' */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(cuts, &cut)); + } + + igraph_vector_int_destroy(&inS); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&cut); + igraph_estack_destroy(&T); + igraph_marked_queue_int_destroy(&S); + IGRAPH_FINALLY_CLEAN(3); + + if (!partition1s) { + igraph_vector_int_list_destroy(mypartition1s); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/* 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 do so by traversing the nodes in topological sort order. The nodes that + are processed first and are not yet connected to any minimal nodes are then + marked as minimal (if active). Any subsequent nodes that are connected to + minimal nodes are then not marked as minimal. +*/ + +static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *residual, + const igraph_marked_queue_int_t *S, + const igraph_bitset_t *active, + igraph_vector_int_t *minimal) { + + igraph_integer_t no_of_nodes = igraph_vcount(residual); + igraph_integer_t i; + igraph_vector_int_t neis; + igraph_bitset_t connected_to_minimal; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_BITSET_INIT_FINALLY(&connected_to_minimal, no_of_nodes); + + // Clear minimal nodes, we will push back to vector as required. + igraph_vector_int_clear(minimal); + + /* We will loop over the nodes in topological sort order, where residual is + * assumed to already be in topological sort order. This way, any node that + * we encounter that is not yet connected to a minimal node and that is + * active, should be marked as minimal. Any node that is connected to a + * minimal node should not be considered minimal, irrespective of it being + * active or not. + */ + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t j, n; + IGRAPH_CHECK(igraph_neighbors(residual, &neis, i, IGRAPH_IN)); + n = igraph_vector_int_size(&neis); + + // Only consider nodes that are not in S. + if (!igraph_marked_queue_int_iselement(S, i)) { + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + /* If connected to node that is connected to node that is minimal, + * this node is also connected to node that is minimal. + */ + if (IGRAPH_BIT_TEST(connected_to_minimal, nei)) { + IGRAPH_BIT_SET(connected_to_minimal, i); + break; + } + } + + /* If this node is not connected to node that is minimal, and this node + * is minimal and active itself, set it as a minimal node. + */ + if (!IGRAPH_BIT_TEST(connected_to_minimal, i) && IGRAPH_BIT_TEST(*active, i)) { + + igraph_vector_int_push_back(minimal, i); + /* Also mark this node as connected to minimal, to make sure that + * any descendants will be marked as being connected to a minimal + * node. + */ + IGRAPH_BIT_SET(connected_to_minimal, i); + } + } + } + + igraph_bitset_destroy(&connected_to_minimal); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +typedef struct igraph_i_all_st_mincuts_data_t { + const igraph_bitset_t *active; +} igraph_i_all_st_mincuts_data_t; + +static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, + const igraph_marked_queue_int_t *S, + const igraph_estack_t *T, + igraph_integer_t source, + igraph_integer_t target, + igraph_integer_t *v, + igraph_vector_int_t *Isv, + void *arg) { + + igraph_i_all_st_mincuts_data_t *data = arg; + const igraph_bitset_t *active = data->active; + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j; + igraph_vector_int_t keep; + igraph_vector_int_t M; + igraph_integer_t nomin; + + IGRAPH_UNUSED(source); IGRAPH_UNUSED(target); + + if (igraph_marked_queue_int_size(S) == no_of_nodes) { + igraph_vector_int_clear(Isv); + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&keep, 0); + for (i = 0; i < no_of_nodes; i++) { + if (!igraph_marked_queue_int_iselement(S, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&keep, i)); + } + } + + /* ------------------------------------------------------------- */ + /* Identify the set M of minimal elements that are active */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&M, 0); + IGRAPH_CHECK(igraph_i_all_st_mincuts_minimal(graph, S, active, &M)); + + /* ------------------------------------------------------------- */ + /* Now find a minimal element that is not in T */ + igraph_vector_int_clear(Isv); + nomin = igraph_vector_int_size(&M); + for (i = 0; i < nomin; i++) { + igraph_integer_t min = 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_int_t Isv_min; + IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0); + *v = VECTOR(M)[i]; + /* TODO: restricted == keep ? */ + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v, /*roots=*/ NULL, + /*mode=*/ IGRAPH_IN, /*unreachable=*/ false, + /*restricted=*/ &keep, /*order=*/ &Isv_min, + /*rank=*/ NULL, /*parents=*/ NULL, /*pred=*/ NULL, + /*succ=*/ NULL, /*dist=*/ NULL, /*callback=*/ NULL, + /*extra=*/ NULL)); + for (j = 0; j < no_of_nodes; j++) { + igraph_integer_t u = VECTOR(Isv_min)[j]; + if (u < 0) { + break; + } + if (!igraph_marked_queue_int_iselement(S, u)) { + IGRAPH_CHECK(igraph_vector_int_push_back(Isv, u)); + } + } + igraph_vector_int_destroy(&Isv_min); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&M); + igraph_vector_int_destroy(&keep); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_all_st_mincuts + * \brief 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 or \c NULL. The value of the minimum cut + * is stored here, unless it is a null pointer. + * \param cuts Pointer to initialized list of integer vectors or \c NULL. + * The cuts are stored here as lists of vertex IDs. + * \param partition1s Pointer to an initialized list of integer vectors or \c NULL. + * 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. + * \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 + */ + +igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, + igraph_vector_int_list_t *cuts, + igraph_vector_int_list_t *partition1s, + igraph_integer_t source, + igraph_integer_t target, + const igraph_vector_t *capacity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_t flow; + igraph_t residual; + igraph_vector_int_t NtoL; + igraph_vector_int_t cut; + igraph_integer_t newsource, newtarget; + igraph_marked_queue_int_t S; + igraph_estack_t T; + igraph_i_all_st_mincuts_data_t pivot_data; + igraph_bitset_t VE1bool; + igraph_integer_t i, nocuts; + igraph_integer_t proj_nodes; + igraph_vector_int_t revmap_ptr, revmap_next; + igraph_vector_int_list_t closedsets; + igraph_vector_int_list_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_EINVVID); + } + if (target < 0 || target >= no_of_nodes) { + IGRAPH_ERROR("Invalid target vertex.", IGRAPH_EINVVID); + } + if (source == target) { + IGRAPH_ERROR("Source and target vertices are the same.", IGRAPH_EINVAL); + } + if (capacity && igraph_vector_size(capacity) != no_of_edges) { + IGRAPH_ERROR("Capacity vector length must agree with number of edges.", IGRAPH_EINVAL); + } + if (capacity && no_of_edges > 0 && igraph_vector_min(capacity) <= 0) { + IGRAPH_ERROR("Not all capacities are strictly positive.", IGRAPH_EINVAL); + } + + if (!partition1s) { + mypartition1s = &vpartition1s; + IGRAPH_CHECK(igraph_vector_int_list_init(mypartition1s, 0)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, mypartition1s); + } + + /* -------------------------------------------------------------------- */ + /* We need to calculate the maximum flow first */ + IGRAPH_VECTOR_INIT_FINALLY(&flow, 0); + IGRAPH_CHECK(igraph_maxflow(graph, value, &flow, /*cut=*/ NULL, + /*partition1=*/ NULL, /*partition2=*/ NULL, + /*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_INT_INIT_FINALLY(&NtoL, 0); + IGRAPH_CHECK(igraph_connected_components( + &residual, /*membership=*/ &NtoL, /*csize=*/ NULL, + /*no=*/ &proj_nodes, IGRAPH_STRONG + )); + IGRAPH_CHECK(igraph_contract_vertices(&residual, /*mapping=*/ &NtoL, /*vertex_comb=*/ NULL)); + IGRAPH_CHECK(igraph_simplify(&residual, /*remove_multiple=*/ true, /*remove_loops=*/ true, /*edge_comb=*/ NULL)); + + /* We relabel the residual graph so that it is in topological sort order. */ + igraph_integer_t no_of_nodes_residual = igraph_vcount(&residual); + igraph_vector_int_t order; + igraph_t tmpgraph; + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes_residual); + IGRAPH_CHECK(igraph_topological_sorting(&residual, &order, IGRAPH_OUT)); + + /* Invert order to get permutation to ensure vertices follow topological order. */ + igraph_vector_int_t inv_order; + IGRAPH_VECTOR_INT_INIT_FINALLY(&inv_order, no_of_nodes_residual); + for (i = 0; i < no_of_nodes_residual; i++) { + VECTOR(inv_order)[VECTOR(order)[i]] = i; + } + + // Relabel mapping + for (i = 0; i < no_of_nodes; i++) { + VECTOR(NtoL)[i] = VECTOR(inv_order)[VECTOR(NtoL)[i]]; + } + + /* Permute vertices and replace residual with tmpgraph that is topologically + * sorted. + */ + IGRAPH_CHECK(igraph_permute_vertices(&residual, &tmpgraph, &inv_order)); + + igraph_destroy(&residual); // We first free memory from original residual graph + residual = tmpgraph; // Then we replace it by allocated memory from tmpgraph + + igraph_vector_int_destroy(&inv_order); + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(2); + + newsource = VECTOR(NtoL)[source]; + newtarget = VECTOR(NtoL)[target]; + + /* TODO: handle the newsource == newtarget case */ + + /* -------------------------------------------------------------------- */ + /* Determine the active vertices in the projection */ + IGRAPH_BITSET_INIT_FINALLY(&VE1bool, proj_nodes); + for (i = 0; i < no_of_edges; i++) { + if (VECTOR(flow)[i] > 0) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_integer_t pfrom = VECTOR(NtoL)[from]; + igraph_integer_t pto = VECTOR(NtoL)[to]; + if (!IGRAPH_BIT_TEST(VE1bool, pfrom)) { + IGRAPH_BIT_SET(VE1bool, pfrom); + } + if (!IGRAPH_BIT_TEST(VE1bool, pto)) { + IGRAPH_BIT_SET(VE1bool, pto); + } + } + } + + if (cuts) { + igraph_vector_int_list_clear(cuts); + } + if (partition1s) { + igraph_vector_int_list_clear(partition1s); + } + + /* -------------------------------------------------------------------- */ + /* Everything is ready, list the cuts, using the right PIVOT + function */ + IGRAPH_CHECK(igraph_marked_queue_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_marked_queue_int_destroy, &S); + IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0)); + IGRAPH_FINALLY(igraph_estack_destroy, &T); + IGRAPH_VECTOR_INT_INIT_FINALLY(&cut, 0); + + pivot_data.active = &VE1bool; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&closedsets, 0); + 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_INT_INIT_FINALLY(&revmap_ptr, igraph_vcount(&residual)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&revmap_next, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t id = 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_int_list_size(&closedsets); + igraph_vector_int_list_clear(mypartition1s); + IGRAPH_CHECK(igraph_vector_int_list_reserve(mypartition1s, nocuts)); + for (i = 0; i < nocuts; i++) { + igraph_vector_int_t *supercut = igraph_vector_int_list_get_ptr(&closedsets, i); + igraph_integer_t j, supercutsize = igraph_vector_int_size(supercut); + + igraph_vector_int_clear(&cut); + for (j = 0; j < supercutsize; j++) { + igraph_integer_t vtx = VECTOR(*supercut)[j]; + igraph_integer_t ovtx = VECTOR(revmap_ptr)[vtx]; + while (ovtx != 0) { + ovtx--; + IGRAPH_CHECK(igraph_vector_int_push_back(&cut, ovtx)); + ovtx = VECTOR(revmap_next)[ovtx]; + } + } + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(mypartition1s, &cut)); + + /* TODO: we could already reclaim the memory taken by 'supercut' here */ + } + + igraph_vector_int_destroy(&revmap_next); + igraph_vector_int_destroy(&revmap_ptr); + igraph_vector_int_list_destroy(&closedsets); + IGRAPH_FINALLY_CLEAN(3); + + /* Create cuts in original graph */ + if (cuts) { + igraph_vector_int_t memb; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&memb, no_of_nodes); + IGRAPH_CHECK(igraph_vector_int_list_reserve(cuts, nocuts)); + + for (i = 0; i < nocuts; i++) { + igraph_vector_int_t *part = igraph_vector_int_list_get_ptr(mypartition1s, i); + igraph_integer_t j, n = igraph_vector_int_size(part); + + igraph_vector_int_clear(&cut); + for (j = 0; j < n; j++) { + igraph_integer_t vtx = VECTOR(*part)[j]; + VECTOR(memb)[vtx] = i + 1; + } + for (j = 0; j < no_of_edges; j++) { + if (VECTOR(flow)[j] > 0) { + igraph_integer_t from = IGRAPH_FROM(graph, j); + igraph_integer_t to = IGRAPH_TO(graph, j); + if (VECTOR(memb)[from] == i + 1 && VECTOR(memb)[to] != i + 1) { + IGRAPH_CHECK(igraph_vector_int_push_back(&cut, j)); + } + } + } + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(cuts, &cut)); + } + + igraph_vector_int_destroy(&memb); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&cut); + igraph_estack_destroy(&T); + igraph_marked_queue_int_destroy(&S); + igraph_bitset_destroy(&VE1bool); + igraph_vector_int_destroy(&NtoL); + igraph_destroy(&residual); + igraph_vector_destroy(&flow); + IGRAPH_FINALLY_CLEAN(7); + + if (!partition1s) { + igraph_vector_int_list_destroy(mypartition1s); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/games/barabasi.c b/src/games/barabasi.c new file mode 100644 index 0000000..c8ff2ed --- /dev/null +++ b/src/games/barabasi.c @@ -0,0 +1,859 @@ +/* -*- 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" +#include "math/safe_intop.h" + +/* Attraction function for barabasi_game. + * We special-case power == 0 to ensure that 0^0 is computed as 1 instead of NaN. */ +static igraph_real_t attraction(igraph_real_t degree, igraph_real_t power, igraph_real_t A) { + return ( power == 0 ? 1.0 : pow(degree, power) ) + A; +} + +static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, + igraph_integer_t m, + const igraph_vector_int_t *outseq, + igraph_bool_t outpref, + igraph_bool_t directed, + const igraph_t *start_from); + +static igraph_error_t igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, + igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_int_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + const igraph_t *start_from); + +static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, + igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_int_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + const igraph_t *start_from); + +static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, + igraph_integer_t m, + const igraph_vector_int_t *outseq, + igraph_bool_t outpref, + igraph_bool_t directed, + const igraph_t *start_from) { + + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_neighbors = m; + igraph_integer_t *bag; + igraph_integer_t bagp = 0; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t resp; + igraph_integer_t i, j, k; + igraph_integer_t bagsize, start_nodes, start_edges, new_edges, no_of_edges; + + if (!directed) { + outpref = true; + } + + start_nodes = start_from ? igraph_vcount(start_from) : 1; + start_edges = start_from ? igraph_ecount(start_from) : 0; + if (outseq) { + if (igraph_vector_int_size(outseq) > 1) { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); + new_edges -= VECTOR(*outseq)[0]; + } else { + new_edges = 0; + } + } else { + IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); + } + IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + resp = start_edges * 2; + bagsize = no_of_nodes; + IGRAPH_SAFE_ADD(bagsize, no_of_edges, &bagsize); + if (outpref) { + IGRAPH_SAFE_ADD(bagsize, no_of_edges, &bagsize); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + + bag = IGRAPH_CALLOC(bagsize, igraph_integer_t); + if (bag == 0) { + IGRAPH_ERROR("barabasi_game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, bag); + + /* The first node(s) in the bag */ + if (start_from) { + igraph_vector_int_t deg; + igraph_integer_t ii, jj, sn = igraph_vcount(start_from); + igraph_neimode_t mm = outpref ? IGRAPH_ALL : IGRAPH_IN; + + IGRAPH_VECTOR_INT_INIT_FINALLY(°, sn); + IGRAPH_CHECK(igraph_degree(start_from, °, igraph_vss_all(), mm, + IGRAPH_LOOPS)); + for (ii = 0; ii < sn; ii++) { + igraph_integer_t d = VECTOR(deg)[ii]; + for (jj = 0; jj <= d; jj++) { + bag[bagp++] = ii; + } + } + + igraph_vector_int_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= */ false)); + igraph_vector_int_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 = VECTOR(*outseq)[k]; + } + for (j = 0; j < no_of_neighbors; j++) { + igraph_integer_t 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++] = VECTOR(edges)[resp - 2 * j - 1]; + if (outpref) { + bag[bagp++] = i; + } + } + } + + RNG_END(); + + IGRAPH_FREE(bag); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, + igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_int_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + const igraph_t *start_from) { + + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_neighbors = m; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; + igraph_psumtree_t sumtree; + igraph_integer_t edgeptr = 0; + igraph_vector_int_t degree; + igraph_integer_t start_nodes, start_edges, new_edges, no_of_edges; + + if (!directed) { + outpref = true; + } + + start_nodes = start_from ? igraph_vcount(start_from) : 1; + start_edges = start_from ? igraph_ecount(start_from) : 0; + if (outseq) { + if (igraph_vector_int_size(outseq) > 1) { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); + new_edges -= VECTOR(*outseq)[0]; + } else { + new_edges = 0; + } + } else { + IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); + } + IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + edgeptr = start_edges * 2; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + + /* First node(s): */ + if (start_from) { + igraph_integer_t 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_int_resize(°ree, no_of_nodes)); + for (ii = 0; ii < sn; ii++) { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, attraction(VECTOR(degree)[ii], power, A))); + } + } else { + /* Any weight may be used for the first node. In the first step, it will be connected to + * with certainty, after which its weight will be set appropriately. */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.0)); + } + + /* Initialize the edges vector */ + if (start_from) { + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ false)); + igraph_vector_int_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); + igraph_integer_t to; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (outseq) { + no_of_neighbors = VECTOR(*outseq)[k]; + } + for (j = 0; j < no_of_neighbors; j++) { + if (sum == 0) { + /* If none of the so-far added nodes have positive weights, + * we choose one uniformly to connect to. */ + to = RNG_INTEGER(0, i-1); + } else { + 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++) { + igraph_integer_t nn = VECTOR(edges)[edgeptr - 2 * j - 1]; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, attraction(VECTOR(degree)[nn], power, A))); + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(VECTOR(degree)[i], power, A))); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(0, power, A))); + } + } + + RNG_END(); + + igraph_psumtree_destroy(&sumtree); + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, + igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_int_t *outseq, + igraph_bool_t outpref, + igraph_real_t A, + igraph_bool_t directed, + const igraph_t *start_from) { + + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_neighbors = m; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; + igraph_psumtree_t sumtree; + igraph_integer_t edgeptr = 0; + igraph_vector_int_t degree; + igraph_integer_t start_nodes, start_edges, new_edges, no_of_edges; + + if (!directed) { + outpref = true; + } + + start_nodes = start_from ? igraph_vcount(start_from) : 1; + start_edges = start_from ? igraph_ecount(start_from) : 0; + if (outseq) { + if (igraph_vector_int_size(outseq) > 1) { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); + new_edges -= VECTOR(*outseq)[0]; + } else { + new_edges = 0; + } + } else { + IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); + } + IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + edgeptr = start_edges * 2; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + + RNG_BEGIN(); + + /* First node(s): */ + if (start_from) { + igraph_integer_t 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_int_resize(°ree, no_of_nodes)); + for (ii = 0; ii < sn; ii++) { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, attraction(VECTOR(degree)[ii], power, A))); + } + } else { + /* Any weight may be used for the first node. In the first step, it will be connected to + * with certainty, after which its weight will be set appropriately. */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.0)); + } + + /* Initialize the edges vector */ + if (start_from) { + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ false)); + } + + /* 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_integer_t to; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (outseq) { + no_of_neighbors = 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_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + edgeptr += 2; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, to, attraction(VECTOR(degree)[to], power, A))); + } + } else { + for (j = 0; j < no_of_neighbors; j++) { + sum = igraph_psumtree_sum(&sumtree); + if (sum == 0) { + /* If none of the so-far added nodes have positive weights, + * we choose one uniformly to connect to. */ + to = RNG_INTEGER(0, i-1); + } else { + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + } + VECTOR(degree)[to]++; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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++) { + igraph_integer_t nn = VECTOR(edges)[edgeptr - 2 * j - 1]; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, attraction(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, attraction(VECTOR(degree)[i], power, A))); + } else { + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(0, power, A))); + } + } + + RNG_END(); + + igraph_psumtree_destroy(&sumtree); + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_barabasi_game + * \brief Generates a graph based on the Barabási-Albert model. + * + * This function implements several variants of the preferential attachment + * process, including linear and non-linear varieties of the Barabási-Albert + * and Price models. The graph construction starts with a single vertex, + * or an existing graph given by the \p start_from parameter. Then new vertices + * are added one at a time. Each new vertex connects to \p m existing vertices, + * choosing them with probabilities proportional to + * + * + * d^power + A, + * + * + * where \c d is the in- or total degree of the existing vertex (controlled + * by the \p outpref argument), while \p power and \p A are given by + * parameters. The constant attractiveness \p A + * is used to ensure that vertices with zero in-degree can also be + * connected to with non-zero probability. + * + * + * Barabási, A.-L. and Albert R. 1999. Emergence of scaling in + * random networks, Science, 286 509--512. + * https://doi.org/10.1126/science.286.5439.509 + * + * + * de Solla Price, D. J. 1965. Networks of Scientific Papers, Science, + * 149 510--515. + * https://doi.org/10.1126/science.149.3683.510 + * + * \param graph An uninitialized graph object. + * \param n The number of vertices in the graph. + * \param power Power of the preferential attachment. In the classic preferential + * attachment model power=1. Other values allow for + * sampling from a non-linear preferential attachment model. + * Negative values are only allowed when no zero-degree vertices + * are present during the construction process, i.e. when + * the starting graph has no isolated vertices and \p outpref + * is set to \c true. + * \param m The number of outgoing edges generated for each + * vertex. Only used when \p outseq is \c NULL. + * \param outseq Gives the (out-)degrees of the vertices. If this is + * constant, this can be a \c NULL pointer or an empty 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 constant attractiveness of vertices. When \p outpref + * is set to \c false, it should be positive to ensure that + * zero in-degree vertices can be connected to as well. + * \param directed Boolean, whether to generate a directed graph. + * When set to \c false, outpref is assumed to be \c true. + * \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 \c 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, \p A 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 + */ +igraph_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, + igraph_real_t power, + igraph_integer_t m, + const igraph_vector_int_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_integer_t start_nodes = start_from ? igraph_vcount(start_from) : 0; + igraph_integer_t newn = start_from ? n - start_nodes : n; + + /* Fix obscure parameterizations */ + if (outseq && igraph_vector_int_empty(outseq)) { + outseq = NULL; + } + if (!directed) { + outpref = true; + } + + /* Check arguments */ + 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 && igraph_vector_int_size(outseq) != newn) { + IGRAPH_ERROR("Invalid out-degree sequence length.", IGRAPH_EINVAL); + } + if (!outseq && m < 0) { + IGRAPH_ERROR("Number of edges added per step must not be negative.", IGRAPH_EINVAL); + } + if (outseq && igraph_vector_int_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); + } + + switch (algo) { + case IGRAPH_BARABASI_BAG: + return igraph_i_barabasi_game_bag(graph, n, m, outseq, outpref, directed, start_from); + + case IGRAPH_BARABASI_PSUMTREE: + return igraph_i_barabasi_game_psumtree(graph, n, power, m, outseq, + outpref, A, directed, start_from); + case IGRAPH_BARABASI_PSUMTREE_MULTIPLE: + return igraph_i_barabasi_game_psumtree_multiple(graph, n, power, m, + outseq, outpref, A, + directed, start_from); + default: + IGRAPH_ERROR("Invalid algorithm for Barabasi game.", IGRAPH_EINVAL); + } +} + +/* Attraction function for barabasi_aging_game. + * We special-case deg_exp == 0 to ensure that 0^0 is computed as 1 instead of NaN. */ +static igraph_real_t attraction_aging( + igraph_real_t deg, igraph_real_t age, + igraph_real_t deg_exp, igraph_real_t age_exp, + igraph_real_t deg_A, igraph_real_t age_A, + igraph_real_t deg_coef, igraph_real_t age_coef) { + + igraph_real_t dp = deg_exp == 0 ? 1.0 : pow(deg, deg_exp); + igraph_real_t ap = pow(age, age_exp); + return (deg_coef * dp + deg_A) * (age_coef * ap + age_A); +} + +/** + * \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 multiplied 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. + */ +igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, + igraph_integer_t nodes, + igraph_integer_t m, + const igraph_vector_int_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) { + igraph_integer_t no_of_nodes = nodes; + igraph_integer_t no_of_neighbors = m; + igraph_integer_t binwidth; + igraph_integer_t no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; + igraph_psumtree_t sumtree; + igraph_integer_t edgeptr = 0; + igraph_vector_int_t degree; + + if (no_of_nodes < 0) { + IGRAPH_ERRORF("Number of nodes must not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_nodes); + } + if (outseq != 0 && igraph_vector_int_size(outseq) != 0 && igraph_vector_int_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("The length of the out-degree sequence (%" IGRAPH_PRId ") does not agree with the number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_int_size(outseq), no_of_nodes); + } + if ( (outseq == 0 || igraph_vector_int_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_int_size(outseq) == 0) { + no_of_neighbors = m; + IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); + } else { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); + no_of_edges -= VECTOR(*outseq)[0]; + } + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, no_of_nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + + RNG_BEGIN(); + + /* First node: */ + /* Any weight may be used for the first node. In the first step, it will be connected to + * with certainty, after which its weight will be set appropriately. */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.0)); + + /* And the rest: */ + for (i = 1; i < no_of_nodes; i++) { + igraph_real_t sum; + igraph_integer_t to; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (outseq != 0 && igraph_vector_int_size(outseq) != 0) { + no_of_neighbors = VECTOR(*outseq)[i]; + } + sum = igraph_psumtree_sum(&sumtree); + for (j = 0; j < no_of_neighbors; j++) { + if (sum == 0) { + /* If none of the so-far added nodes have positive weights, + * we choose one uniformly to connect to. */ + to = RNG_INTEGER(0, i-1); + } else { + 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++) { + igraph_integer_t n = VECTOR(edges)[edgeptr - 2 * j - 1]; + igraph_integer_t age = (i - n) / binwidth; + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, n, + attraction_aging(VECTOR(degree)[n], age+1, + pa_exp, aging_exp, + zero_deg_appeal, zero_age_appeal, + deg_coef, age_coef) + )); + } + if (outpref) { + VECTOR(degree)[i] += no_of_neighbors; + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, i, + attraction_aging(VECTOR(degree)[i], 1, + pa_exp, aging_exp, + zero_deg_appeal, zero_age_appeal, + deg_coef, age_coef) + )); + } else { + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, i, + attraction_aging(0, 1, + pa_exp, aging_exp, + zero_deg_appeal, zero_age_appeal, + deg_coef, age_coef) + )); + } + + /* aging */ + for (k = 1; binwidth * k <= i; k++) { + igraph_integer_t shnode = i - binwidth * k; + igraph_integer_t deg = VECTOR(degree)[shnode]; + igraph_integer_t age = (i - shnode) / binwidth; + /* igraph_real_t old=igraph_psumtree_get(&sumtree, shnode); */ + IGRAPH_CHECK(igraph_psumtree_update( + &sumtree, shnode, + attraction_aging(deg, age + 2, + pa_exp, aging_exp, + zero_deg_appeal, zero_age_appeal, + deg_coef, age_coef) + )); + } + } + + RNG_END(); + + igraph_vector_int_destroy(°ree); + igraph_psumtree_destroy(&sumtree); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/callaway_traits.c b/src/games/callaway_traits.c new file mode 100644 index 0000000..a1edfe9 --- /dev/null +++ b/src/games/callaway_traits.c @@ -0,0 +1,203 @@ +/* -*- 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. + */ +igraph_error_t 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_int_t *node_type_vec) { + igraph_integer_t i, j; + igraph_vector_int_t edges; + igraph_vector_t cumdist; + igraph_real_t maxcum; + igraph_vector_int_t *nodetypes; + + /* Argument contracts */ + if (nodes < 0) { + IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); + } + + if (edges_per_step < 0) { + IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + edges_per_step); + } + + 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 (isnan(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); /* matrix size is at least 1x1, safe to call minmax */ + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (isnan(lo) || isnan(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_INT_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_int_resize(nodetypes, nodes)); + } else { + nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(nodetypes, "Insufficient memory for Callaway traits game."); + IGRAPH_FINALLY(igraph_free, nodetypes); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); + } + + RNG_BEGIN(); + + for (i = 0; i < nodes; i++) { + igraph_real_t uni = RNG_UNIF(0, maxcum); + igraph_integer_t 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++) { + igraph_integer_t node1 = RNG_INTEGER(0, i); + igraph_integer_t node2 = RNG_INTEGER(0, i); + igraph_integer_t type1 = VECTOR(*nodetypes)[node1]; + igraph_integer_t type2 = 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_int_push_back(&edges, node1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, node2)); + } + } + } + + RNG_END(); + + if (! node_type_vec) { + igraph_vector_int_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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/chung_lu.c b/src/games/chung_lu.c new file mode 100644 index 0000000..c3af93d --- /dev/null +++ b/src/games/chung_lu.c @@ -0,0 +1,323 @@ +/* + IGraph library. + Copyright (C) 2024 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_games.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_vector.h" + +#include "core/interruption.h" +#include "math/safe_intop.h" + +#include /* isfinite() */ + +/* This implementation follows the ideas in: + * + * Joel Miller and Aric Hagberg, + * Efficient generation of networks with given expected degrees + * (2011) + * + * It is analogous to the method used in igraph_erdos_renyi_game_gnp() + * and has linear complexity in the number of edges. + */ + +static igraph_error_t check_expected_degrees(const igraph_vector_t *weights) { + igraph_real_t minw, maxw; + + igraph_vector_minmax(weights, &minw, &maxw); + + if (minw < 0) { + IGRAPH_ERRORF("Vertex weights must not be negative in Chung-Lu model, got %g.", IGRAPH_EINVAL, minw); + } + + /* Catches both NaN and +Inf. */ + if (! isfinite(maxw)) { + IGRAPH_ERRORF("Vertex weights must be finite, got %g.", IGRAPH_EINVAL, maxw); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_chung_lu_game + * \brief Samples graphs from the Chung-Lu model. + * + * \experimental + * + * The Chung-Lu model is useful for generating random graphs with fixed + * expected degrees. This function implements both the original model of Chung + * and Lu, as well as some additional variants with useful properties. + * + * + * In the original Chung-Lu model, each pair of vertices \c i and \c j is + * connected with independent probability p_ij = w_i w_j / S, + * where \c w_i is a weight associated with vertex \c i and + * S = sum_k w_k is the sum of weights. In the directed variant, + * vertices have both out-weights, w^out, and in-weights, + * w^in, with equal sums, + * S = sum_k w^out_k = sum_k w^in_k. + * The connection probability between \c i and \c j is + * p_ij = w^out_i w^in_j / S. + * + * + * This model is commonly used to create random graphs with a fixed \em expected + * degree sequence. The expected degree of vertex \c i is approximately equal + * to the weight \c w_i. Specifically, if the graph is directed and self-loops + * are allowed, then the expected out- and in-degrees are precisely + * w^out and w^in. If self-loops are disallowed, + * then the expected out- and in-degrees are w^out (S - w^in) / S + * and w^in (S - w^out) / S, respectively. If the graph is + * undirected, then the expected degrees with and without self-loops are + * w (S + w) / S and w (S - w) / S, respectively. + * + * + * A limitation of the original Chung-Lu model is that when some of the + * weights are large, the formula for \c p_ij yields values larger than 1. + * Chung and Lu's original paper exludes the use of such weights. When + * p_ij > 1, this function simply issues a warning and creates + * a connection between \c i and \c j. However, in this case the expected degrees + * will no longer relate to the weights in the manner stated above. Thus the + * original Chung-Lu model cannot produce certain (large) expected degrees. + * + * + * The overcome this limitation, this function implements additional variants of + * the model, with modified expressions for the connection probability \c p_ij + * between vertices \c i and \c j. Let q_ij = w_i w_j / S, or + * q_ij = w^out_i w^in_j / S in the directed case. All model + * variants become equivalent in the limit of sparse graphs where \c q_ij + * approaches zero. In the original Chung-Lu model, selectable by setting + * \p variant to \c IGRAPH_CHUNG_LU_ORIGINAL, p_ij = min(q_ij, 1). + * The \c IGRAPH_CHUNG_LU_MAXENT variant, sometiems referred to a the generalized + * random graph, uses p_ij = q_ij / (1 + q_ij), and is equivalent + * to a maximum entropy model (i.e. exponential random graph model) with + * a constraint on expected degrees; see Park and Newman (2004), Section B, + * setting exp(-Theta_ij) = w_i w_j / S. This model is also + * discussed by Britton, Deijfen and Martin-Löf (2006). By virtue of being + * a degree-constrained maximum entropy model, it graphs with the same degree + * sequence are produced with the same probability. + * A third variant can be requested with \c IGRAPH_CHUNG_LU_NR, and uses + * p_ij = 1 - exp(-q_ij). This is the underlying simple graph + * of a multigraph model introduced by Norros and Reittu (2006). + * For a discussion of these three model variants, see Section 16.4 of + * Bollobás, Janson, Riordan (2007), as well as Van Der Hofstad (2013). + * + * + * References: + * + * + * Chung F and Lu L: Connected components in a random graph with given + * degree sequences. Annals of Combinatorics 6, 125-145 (2002). + * https://doi.org/10.1007/PL00012580 + * + * + * Miller JC and Hagberg A: + * Efficient Generation of Networks with Given Expected Degrees (2011). + * https://doi.org/10.1007/978-3-642-21286-4_10 + * + * + * Park J and Newman MEJ: Statistical mechanics of networks. + * Physical Review E 70, 066117 (2004). + * https://doi.org/10.1103/PhysRevE.70.066117 + * + * + * Britton T, Deijfen M, Martin-Löf A: + * Generating Simple Random Graphs with Prescribed Degree Distribution. + * J Stat Phys 124, 1377–1397 (2006). + * https://doi.org/10.1007/s10955-006-9168-x + * + * + * Norros I and Reittu H: On a conditionally Poissonian graph process. + * Advances in Applied Probability 38, 59–75 (2006). + * https://doi.org/10.1239/aap/1143936140 + * + * + * Bollobás B, Janson S, Riordan O: + * The phase transition in inhomogeneous random graphs. + * Random Struct Algorithms 31, 3–122 (2007). + * https://doi.org/10.1002/rsa.20168 + * + * + * Van Der Hofstad R: Critical behavior in inhomogeneous random graphs. + * Random Struct Algorithms 42, 480–508 (2013). + * https://doi.org/10.1002/rsa.20450 + * + * \param graph Pointer to an uninitialized graph object. + * \param out_weights A vector of non-negative vertex weights (or out-weights). + * In sparse graphs these will be approximately equal to the expected + * (out-)degrees. + * \param in_weights A vector of non-negative in-weights, approximately equal + * to the expected in-degrees in sparse graphs. May be set to \c NULL, + * in which case undirected graphs are generated. + * \param loops Whether to allow the creation of self-loops. Since vertex + * pairs are connected independently, setting this to false is equivalent + * to simply discarding self-loops from an existing loopy Chung-Lu graph. + * \param variant The model variant to sample from, with different definitions + * of the connection probability between vertices \c i and \c j. Given + * q_ij = w_i w_j / S, the following formulations are available: + * \clist + * \cli IGRAPH_CHUNG_LU_ORIGINAL + * the original Chung-Lu model, p_ij = min(q_ij, 1). + * \cli IGRAPH_CHUNG_LU_MAXENT + * maximum entropy model with fixed expected degrees, + * p_ij = q_ij / (1 + q_ij). + * \cli IGRAPH_CHUNG_LU_NR + * Norros and Reittu's model, p_ij = 1 - exp(-q_ij). + * \endclist + * \return Error code. + * + * \sa \ref igraph_static_fitness_game() implements a similar model with + * a sharp constraint on the number of edges; + * \ref igraph_degree_sequence_game() samples random graphs with sharply + * specified degrees; \ref igraph_erdos_renyi_game_gnp() creates random + * graphs with a fixed connection probability \c p between all vertex pairs. + * + * Time complexity: O(|E| + |V|), linear in the number of edges. + */ +igraph_error_t igraph_chung_lu_game(igraph_t *graph, + const igraph_vector_t *out_weights, + const igraph_vector_t *in_weights, + igraph_bool_t loops, + igraph_chung_lu_t variant) { + + const igraph_integer_t no_of_nodes = igraph_vector_size(out_weights); + const igraph_bool_t directed = in_weights != NULL; + igraph_vector_int_t edges, idx; + igraph_real_t wsum = igraph_vector_sum(out_weights); + igraph_bool_t warned = false; + int iter = 0; + + /* Necessitated by floating point arithmetic used in the implementation. */ + if (no_of_nodes >= IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Number of vertices is too large.", IGRAPH_EOVERFLOW); + } + + if (! directed) { + in_weights = out_weights; + } else if (igraph_vector_size(in_weights) != no_of_nodes) { + IGRAPH_ERROR("Vertex out- and in-weight vectors must have the same length.", IGRAPH_EINVAL); + } + + if (no_of_nodes == 0) { + return igraph_empty(graph, 0, directed); + } + + IGRAPH_CHECK(check_expected_degrees(out_weights)); + + if (directed) { + IGRAPH_CHECK(check_expected_degrees(in_weights)); + + if (igraph_vector_sum(in_weights) != wsum) { + IGRAPH_ERRORF("Sum of out- and in-weights must be the same, got %g and %g, respectively.", + IGRAPH_EINVAL, wsum, igraph_vector_sum(in_weights)); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, 0); + + igraph_vector_qsort_ind(in_weights, &idx, IGRAPH_DESCENDING); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < no_of_nodes; i++) { + igraph_integer_t vi, vj; + igraph_real_t wi, wj; + igraph_real_t p, q; + + igraph_integer_t j = directed ? 0 : i; + + vi = VECTOR(idx)[i]; + wi = VECTOR(*out_weights)[vi]; + + if (wi == 0) { + if (directed) continue; else break; + } + + p = 1; + + while (true) { + igraph_real_t gap = RNG_GEOM(p); + + /* This formulation not only terminates the loop when necessary, + * but also protects against overflow when 'p' is very small + * and 'gap' becomes very large, perhaps larger than representable + * in an igraph_integer_t. */ + if (gap >= no_of_nodes-j) { + break; + } + + j += gap; + + vj = VECTOR(idx)[j]; + wj = VECTOR(*in_weights)[vj]; + + q = wi * wj / wsum; + switch (variant) { + case IGRAPH_CHUNG_LU_ORIGINAL: + if (q > 1) { + q = 1; + if (! warned && (loops || vi != vj)) { + IGRAPH_WARNINGF( + "Expected degrees %g and %g lead to a calculated connection probability " + "larger than 1 in Chung-Lu model. The degrees of the resulting graph will " + "not be consistent with the given input.", wi, wj); + warned = true; + } + } + break; + case IGRAPH_CHUNG_LU_MAXENT: + q = q / (1 + q); + break; + case IGRAPH_CHUNG_LU_NR: + q = 1 - exp(-q); + break; + default: + IGRAPH_ERROR("Invalid Chung-Lu variant.", IGRAPH_EINVAL); + } + + /* A probability of zero must not be passed to RNG_GEOM(), + * so we catch this case here. */ + if (q == 0) { + break; + } + + if (RNG_UNIF01() < q/p && (loops || vi != vj)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, vi)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, vj)); + } + + p = q; + + j++; + + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 16); + } + } + RNG_END(); + + igraph_vector_int_destroy(&idx); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/citations.c b/src/games/citations.c new file mode 100644 index 0000000..4c55b23 --- /dev/null +++ b/src/games/citations.c @@ -0,0 +1,515 @@ +/* -*- 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" + +#include "math/safe_intop.h" + +typedef struct { + igraph_integer_t 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 nodes 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. + */ +igraph_error_t 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_integer_t no_of_nodes = nodes; + igraph_psumtree_t sumtree; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; + igraph_integer_t *lastcit; + igraph_integer_t *index; + igraph_integer_t 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 %" IGRAPH_PRId ".", + 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 (edges_per_node < 0) { + IGRAPH_ERRORF("Number of edges per node should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + edges_per_node); + } + 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_INT_INIT_FINALLY(&edges, 0); + + lastcit = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + if (!lastcit) { + IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, lastcit); + + index = IGRAPH_CALLOC(no_of_nodes + 1, igraph_integer_t); + if (!index) { + IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, index); + + IGRAPH_CHECK(igraph_psumtree_init(&sumtree, nodes)); + IGRAPH_FINALLY(igraph_psumtree_destroy, &sumtree); + IGRAPH_CHECK(igraph_vector_int_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++) { + igraph_integer_t to; + igraph_real_t sum = igraph_psumtree_sum(&sumtree); + if (sum == 0) { + /* If none of the so-far added nodes have positive weight, + * we choose one uniformly to connect to. */ + to = RNG_INTEGER(0, i-1); + } else { + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + } + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, to); /* reserved */ + 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++) { + igraph_integer_t shnode = i - binwidth * k; + igraph_integer_t m = index[shnode], n = index[shnode + 1]; + for (j = 2 * m; j < 2 * n; j += 2) { + igraph_integer_t cnode = 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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_int_t *types, + const igraph_vector_t *pref, + igraph_integer_t edges_per_step, + igraph_bool_t directed) { + + igraph_vector_int_t edges; + igraph_vector_t cumsum; + igraph_real_t sum, nnval; + igraph_integer_t i, j, type; + igraph_integer_t pref_len = igraph_vector_size(pref); + + if (igraph_vector_int_size(types) != nodes) { + IGRAPH_ERRORF("Length of types vector (%" IGRAPH_PRId ") must match number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(types), nodes); + } + if (edges_per_step < 0) { + IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + edges_per_step); + } + + 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_int_min(types) < 0) { + IGRAPH_ERRORF("Types should be non-negative, but found %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_vector_int_min(types)); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + IGRAPH_VECTOR_INIT_FINALLY(&cumsum, 2); + IGRAPH_CHECK(igraph_vector_reserve(&cumsum, nodes + 1)); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nodes * edges_per_step)); + + /* first node */ + VECTOR(cumsum)[0] = 0; + type = 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++) { + igraph_integer_t to; + if (sum > 0) { + igraph_vector_binsearch(&cumsum, RNG_UNIF(0, sum), &to); + } else { + to = i + 1; + } + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, to - 1); /* reserved */ + } + type = 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); /* reserved */ + } + + RNG_END(); + + igraph_vector_destroy(&cumsum); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + +err_pref_too_short: + IGRAPH_ERRORF("Preference vector should have length at least %" IGRAPH_PRId " with the given types.", IGRAPH_EINVAL, + igraph_vector_int_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) { + if (!s->sumtrees) { + return; + } + for (igraph_integer_t 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 vector 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 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. + * + * Time complexity: O((|V|+|E|)log|V|), |V| and |E| are number of + * vertices and edges, respectively. + */ + +igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_int_t *types, + const igraph_matrix_t *pref, + igraph_integer_t edges_per_step, + igraph_bool_t directed) { + + igraph_vector_int_t edges; + igraph_i_citing_cited_type_game_struct_t str = { 0, NULL }; + igraph_psumtree_t *sumtrees; + igraph_vector_t sums; + igraph_integer_t no_of_types; + igraph_integer_t i, j, no_of_edges, no_of_edge_endpoints; + + if (igraph_vector_int_size(types) != nodes) { + IGRAPH_ERRORF("Length of types vector (%" IGRAPH_PRId ") not equal to number" + " of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(types), nodes); + } + if (edges_per_step < 0 ) { + IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + edges_per_step); + } + + /* avoid calling vector_max on empty vector */ + no_of_types = nodes == 0 ? 0 : igraph_vector_int_max(types) + 1; + + if (igraph_matrix_ncol(pref) != no_of_types) { + IGRAPH_ERRORF("Number of preference matrix columns (%" IGRAPH_PRId ") not " + "equal to number of types (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_matrix_ncol(pref), + no_of_types); + } + if (igraph_matrix_nrow(pref) != no_of_types) { + IGRAPH_ERRORF("Number of preference matrix rows (%" IGRAPH_PRId ") not " + "equal to number of types (%" IGRAPH_PRId ").", + 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_INT_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); /* LCOV_EXCL_LINE */ + } + 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_SAFE_MULT(nodes, edges_per_step, &no_of_edges); + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edge_endpoints); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edge_endpoints)); + + /* First node */ + for (i = 0; i < no_of_types; i++) { + igraph_integer_t type = 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++) { + igraph_integer_t type = VECTOR(*types)[i]; + igraph_real_t sum = VECTOR(sums)[type]; + for (j = 0; j < edges_per_step; j++) { + igraph_integer_t to; + if (sum == 0) { + /* If none of the so-far added nodes have positive weight, + * we choose one uniformly to connect to. */ + to = RNG_INTEGER(0, i-1); + } else { + igraph_psumtree_search(&sumtrees[type], &to, RNG_UNIF(0, sum)); + } + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, to); /* reserved */ + } + + /* 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_CHECK(igraph_create(graph, &edges, nodes, directed)); + + igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&sums); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/correlated.c b/src/games/correlated.c new file mode 100644 index 0000000..20f0c95 --- /dev/null +++ b/src/games/correlated.c @@ -0,0 +1,324 @@ +/* -*- 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 undirected) 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_integer_t *a = (const igraph_integer_t *) va; + const igraph_integer_t *b = (const igraph_integer_t *) vb; + const igraph_integer_t 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_int_t *edges, const igraph_t *graph) { + igraph_qsort_r(VECTOR(*edges), igraph_vector_int_size(edges) / 2, 2*sizeof(igraph_integer_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. + */ +igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, + igraph_real_t corr, igraph_real_t p, + const igraph_vector_int_t *permutation) { + + igraph_integer_t no_of_nodes = igraph_vcount(old_graph); + igraph_integer_t no_of_edges = igraph_ecount(old_graph); + igraph_bool_t directed = igraph_is_directed(old_graph); + igraph_real_t no_of_all = directed ? ((igraph_real_t) no_of_nodes) * (no_of_nodes - 1) : + ((igraph_real_t) 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; + igraph_vector_int_t edges, newedges; + igraph_real_t last; + igraph_integer_t p_e = 0, p_a = 0, p_d = 0; + igraph_integer_t no_add, no_del; + igraph_real_t next_e, next_a, next_d; + igraph_integer_t i, newec; + 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_int_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_gnp(new_graph, no_of_nodes, p, directed, IGRAPH_NO_LOOPS); + } + if (corr == 1) { + /* We don't copy, because we don't need the attributes.... */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0)); + if (permutation) { + newec = igraph_vector_int_size(&edges); + for (i = 0; i < newec; i++) { + igraph_integer_t tmp = VECTOR(edges)[i]; + VECTOR(edges)[i] = VECTOR(*permutation)[tmp]; + } + } + IGRAPH_CHECK(igraph_create(new_graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&newedges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&add, 0); + IGRAPH_VECTOR_INIT_FINALLY(&delete, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + + IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0)); + /* The sampling 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++) { + igraph_integer_t td = VECTOR(delete)[i]; + igraph_integer_t from = VECTOR(edges)[2 * td]; + igraph_integer_t to = VECTOR(edges)[2 * td + 1]; + VECTOR(delete)[i] = CODE(from, to); + } + + IGRAPH_CHECK(igraph_vector_int_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 = IGRAPH_INFINITY; } } +#define UPD_A() \ + { if (p_a < no_add) { \ + next_a = VECTOR(add)[p_a] + p_e; } else { next_a = IGRAPH_INFINITY; } } +#define UPD_D() \ + { if (p_d < no_del) { \ + next_d = VECTOR(delete)[p_d]; } else { next_d = IGRAPH_INFINITY; } } + + UPD_E(); UPD_A(); UPD_D(); + + while (next_e != IGRAPH_INFINITY || next_a != IGRAPH_INFINITY || next_d != IGRAPH_INFINITY) { + IGRAPH_ALLOW_INTERRUPTION(); + if (next_e <= next_a && next_e < next_d) { + + /* keep an edge */ + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(edges)[2 * p_e])); + IGRAPH_CHECK(igraph_vector_int_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 */ + igraph_integer_t to, from; + IGRAPH_ASSERT(isfinite(next_a)); + if (directed) { + to = floor(next_a / no_of_nodes); + from = next_a - ((igraph_real_t)to) * no_of_nodes; + if (from == to) { + to = no_of_nodes - 1; + } + } else { + to = floor((sqrt(8 * next_a + 1) + 1) / 2); + from = next_a - (((igraph_real_t)to) * (to - 1)) / 2; + } + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, to)); + p_a++; UPD_A(); + + } + } + + igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&add); + igraph_vector_destroy(&delete); + IGRAPH_FINALLY_CLEAN(3); + + if (permutation) { + newec = igraph_vector_int_size(&newedges); + for (i = 0; i < newec; i++) { + igraph_integer_t tmp = VECTOR(newedges)[i]; + VECTOR(newedges)[i] = VECTOR(*permutation)[tmp]; + } + } + + IGRAPH_CHECK(igraph_create(new_graph, &newedges, no_of_nodes, directed)); + + igraph_vector_int_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. + */ +igraph_error_t 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_int_t *permutation) { + + IGRAPH_CHECK(igraph_erdos_renyi_game_gnp(graph1, n, p, directed, IGRAPH_NO_LOOPS)); + IGRAPH_CHECK(igraph_correlated_game(graph1, graph2, corr, p, permutation)); + return IGRAPH_SUCCESS; +} diff --git a/src/games/degree_sequence.c b/src/games/degree_sequence.c new file mode 100644 index 0000000..9ed6562 --- /dev/null +++ b/src/games/degree_sequence.c @@ -0,0 +1,813 @@ +/* -*- 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_interface.h" +#include "igraph_memory.h" +#include "igraph_operators.h" +#include "igraph_random.h" +#include "igraph_vector_ptr.h" + +#include "core/interruption.h" +#include "core/set.h" +#include "games/degree_sequence_vl/degree_sequence_vl.h" +#include "math/safe_intop.h" + +static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *graph, + const igraph_vector_int_t *out_seq, + const igraph_vector_int_t *in_seq) { + + igraph_integer_t outsum = 0, insum = 0; + igraph_bool_t directed = (in_seq != NULL && igraph_vector_int_size(in_seq) != 0); + igraph_bool_t degseq_ok; + igraph_integer_t no_of_nodes, no_of_edges; + igraph_integer_t *bag1, *bag2; + igraph_integer_t bagp1 = 0, bagp2 = 0; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + + 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); + } + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(out_seq, &outsum)); + if (directed) { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(in_seq, &insum)); + } + + no_of_nodes = igraph_vector_int_size(out_seq); + no_of_edges = directed ? outsum : outsum / 2; + + bag1 = IGRAPH_CALLOC(outsum, igraph_integer_t); + IGRAPH_CHECK_OOM(bag1, "Insufficient memory for sampling from configuration model."); + IGRAPH_FINALLY(igraph_free, bag1); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t j = 0; j < VECTOR(*out_seq)[i]; j++) { + bag1[bagp1++] = i; + } + } + if (directed) { + bag2 = IGRAPH_CALLOC(insum, igraph_integer_t); + IGRAPH_CHECK_OOM(bag2, "Insufficient memory for sampling from configuration model."); + IGRAPH_FINALLY(igraph_free, bag2); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t j = 0; j < VECTOR(*in_seq)[i]; j++) { + bag2[bagp2++] = i; + } + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + + RNG_BEGIN(); + + if (directed) { + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + igraph_integer_t from = RNG_INTEGER(0, bagp1 - 1); + igraph_integer_t to = RNG_INTEGER(0, bagp2 - 1); + igraph_vector_int_push_back(&edges, bag1[from]); /* safe, already reserved */ + igraph_vector_int_push_back(&edges, bag2[to]); /* ditto */ + bag1[from] = bag1[bagp1 - 1]; + bag2[to] = bag2[bagp2 - 1]; + bagp1--; bagp2--; + } + } else { + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + igraph_integer_t from = RNG_INTEGER(0, bagp1 - 1); + igraph_integer_t to; + igraph_vector_int_push_back(&edges, bag1[from]); /* safe, already reserved */ + bag1[from] = bag1[bagp1 - 1]; + bagp1--; + to = RNG_INTEGER(0, bagp1 - 1); + igraph_vector_int_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, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( + igraph_t *graph, const igraph_vector_int_t *seq) { + + igraph_vector_int_t stubs; + igraph_vector_int_t *neis; + igraph_vector_int_t residual_degrees; + igraph_set_t incomplete_vertices; + igraph_adjlist_t al; + igraph_bool_t finished, failed; + igraph_integer_t from, to, dummy; + igraph_integer_t i, j, k; + igraph_integer_t 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); + } + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(seq, &outsum)); + no_of_nodes = igraph_vector_int_size(seq); + + /* Allocate required data structures */ + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + IGRAPH_VECTOR_INT_INIT_FINALLY(&stubs, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&stubs, outsum)); + IGRAPH_VECTOR_INT_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 = false; + while (!finished) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Be optimistic :) */ + failed = false; + + /* 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_int_update(&residual_degrees, seq)); + + /* While there are some unconnected stubs left... */ + while (!finished && !failed) { + /* Construct the initial stub vector */ + igraph_vector_int_clear(&stubs); + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < VECTOR(residual_degrees)[i]; j++) { + igraph_vector_int_push_back(&stubs, i); + } + } + + /* Clear the skipped stub counters and the set of incomplete vertices */ + igraph_vector_int_null(&residual_degrees); + igraph_set_clear(&incomplete_vertices); + + /* Shuffle the stubs in-place */ + igraph_vector_int_shuffle(&stubs); + + /* Connect the stubs where possible */ + k = igraph_vector_int_size(&stubs); + for (i = 0; i < k; ) { + from = VECTOR(stubs)[i++]; + to = 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 = true; + 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 = false; + break; + } + } + } + } + } + } + + /* Finish the RNG */ + RNG_END(); + + /* Clean up */ + igraph_set_destroy(&incomplete_vertices); + igraph_vector_int_destroy(&residual_degrees); + igraph_vector_int_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 igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t *graph, + const igraph_vector_int_t *out_seq, const igraph_vector_int_t *in_seq) { + igraph_adjlist_t al; + igraph_bool_t deg_seq_ok, failed, finished; + igraph_vector_int_t in_stubs; + igraph_vector_int_t out_stubs; + igraph_vector_int_t *neis; + igraph_vector_int_t residual_in_degrees = IGRAPH_VECTOR_NULL; + igraph_vector_int_t residual_out_degrees = IGRAPH_VECTOR_NULL; + igraph_set_t incomplete_in_vertices; + igraph_set_t incomplete_out_vertices; + igraph_integer_t from, to; + igraph_integer_t i, j, k; + igraph_integer_t 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); + } + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(out_seq, &outsum)); + no_of_nodes = igraph_vector_int_size(out_seq); + + /* Allocate required data structures */ + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_stubs, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&out_stubs, outsum)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_stubs, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&in_stubs, outsum)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&residual_out_degrees, no_of_nodes); + IGRAPH_VECTOR_INT_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 = false; + while (!finished) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Be optimistic :) */ + failed = false; + + /* 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_int_update(&residual_out_degrees, out_seq)); + IGRAPH_CHECK(igraph_vector_int_update(&residual_in_degrees, in_seq)); + + /* While there are some unconnected stubs left... */ + while (!finished && !failed) { + /* Construct the initial stub vectors */ + igraph_vector_int_clear(&out_stubs); + igraph_vector_int_clear(&in_stubs); + for (i = 0; i < no_of_nodes; i++) { + for (j = 0; j < VECTOR(residual_out_degrees)[i]; j++) { + igraph_vector_int_push_back(&out_stubs, i); + } + for (j = 0; j < VECTOR(residual_in_degrees)[i]; j++) { + igraph_vector_int_push_back(&in_stubs, i); + } + } + + /* Clear the skipped stub counters and the set of incomplete vertices */ + igraph_vector_int_null(&residual_out_degrees); + igraph_vector_int_null(&residual_in_degrees); + igraph_set_clear(&incomplete_out_vertices); + igraph_set_clear(&incomplete_in_vertices); + + /* Shuffle the out-stubs in-place */ + igraph_vector_int_shuffle(&out_stubs); + + /* Connect the stubs where possible */ + k = igraph_vector_int_size(&out_stubs); + for (i = 0; i < k; i++) { + from = VECTOR(out_stubs)[i]; + to = 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 = true; + 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 = false; + break; + } + } + } + } + } + } + + /* Finish the RNG */ + RNG_END(); + + /* Clean up */ + igraph_set_destroy(&incomplete_in_vertices); + igraph_set_destroy(&incomplete_out_vertices); + igraph_vector_int_destroy(&residual_in_degrees); + igraph_vector_int_destroy(&residual_out_degrees); + igraph_vector_int_destroy(&in_stubs); + igraph_vector_int_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 igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirected(igraph_t *graph, const igraph_vector_int_t *degseq) { + igraph_vector_int_t stubs; + igraph_vector_int_t edges; + igraph_bool_t degseq_ok; + igraph_vector_ptr_t adjlist; + igraph_integer_t i, j; + igraph_integer_t 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 = igraph_vector_int_sum(degseq); + ecount = stub_count / 2; + vcount = igraph_vector_int_size(degseq); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&stubs, stub_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, stub_count); + + /* Fill stubs vector. */ + { + igraph_integer_t k = 0; + for (i = 0; i < vcount; ++i) { + igraph_integer_t deg = 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("Cannot sample from configuration model (simple graphs).", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_CHECK(igraph_set_init(set, 0)); + VECTOR(adjlist)[i] = set; + IGRAPH_CHECK(igraph_set_reserve(set, VECTOR(*degseq)[i])); + } + + RNG_BEGIN(); + + for (;;) { + igraph_bool_t success = true; + + /* Shuffle stubs vector with Fisher-Yates and check for self-loops and multi-edges as we go. */ + for (i = 0; i < ecount; ++i) { + igraph_integer_t 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 = false; + break; + } + + /* multi-edge, fail */ + if (igraph_set_contains((igraph_set_t *) VECTOR(adjlist)[to], from)) { + success = false; + 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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directed( + igraph_t *graph, const igraph_vector_int_t *out_deg, const igraph_vector_int_t *in_deg) { + igraph_vector_int_t out_stubs, in_stubs; + igraph_vector_int_t edges; + igraph_bool_t degseq_ok; + igraph_vector_ptr_t adjlist; + igraph_integer_t i, j; + igraph_integer_t 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 = igraph_vector_int_sum(out_deg); + vcount = igraph_vector_int_size(out_deg); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_stubs, ecount); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_stubs, ecount); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2 * ecount); + + /* Fill in- and out-stubs vectors. */ + { + igraph_integer_t k = 0, l = 0; + for (i = 0; i < vcount; ++i) { + igraph_integer_t dout, din; + + dout = VECTOR(*out_deg)[i]; + for (j = 0; j < dout; ++j) { + VECTOR(out_stubs)[k++] = i; + } + + din = 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_CHECK(igraph_set_init(set, 0)); + VECTOR(adjlist)[i] = set; + IGRAPH_CHECK(igraph_set_reserve(set, VECTOR(*out_deg)[i])); + } + + RNG_BEGIN(); + + for (;;) { + igraph_bool_t success = true; + + /* Shuffle out-stubs vector with Fisher-Yates and check for self-loops and multi-edges as we go. */ + for (i = 0; i < ecount; ++i) { + igraph_integer_t 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 = false; + break; + } + + /* multi-edge, fail */ + set = (igraph_set_t *) VECTOR(adjlist)[from]; + if (igraph_set_contains(set, to)) { + success = false; + 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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +#undef SWAP_INT_ELEM + +igraph_error_t igraph_i_degree_sequence_game_edge_switching( + igraph_t *graph, + const igraph_vector_int_t *out_seq, const igraph_vector_int_t *in_seq) { + + IGRAPH_CHECK(igraph_realize_degree_sequence(graph, out_seq, in_seq, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_rewire(graph, 10 * igraph_ecount(graph), IGRAPH_REWIRING_SIMPLE)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup generators + * \function igraph_degree_sequence_game + * \brief Generates a random graph with a given degree sequence. + * + * This function generates random graphs with a prescribed degree sequence. + * Several sampling methods are available, which respect different constraints + * (simple graph or multigraphs, connected graphs, etc.), and provide different + * tradeoffs between performance and unbiased sampling. See Section 2.1 of + * Horvát and Modes (2021) for an overview of sampling techniques for graphs + * with fixed degrees. + * + * + * References: + * + * + * Fabien Viger, and Matthieu Latapy: + * Efficient and Simple Generation of Random Simple Connected Graphs with Prescribed Degree Sequence, + * Journal of Complex Networks 4, no. 1, pp. 15–37 (2015). + * https://doi.org/10.1093/comnet/cnv013. + * + * + * Szabolcs Horvát, and Carl D Modes: + * Connectedness Matters: Construction and Exact Random Sampling of Connected Networks, + * Journal of Physics: Complexity 2, no. 1, pp. 015008 (2021). + * https://doi.org/10.1088/2632-072x/abced5. + * + * \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_CONFIGURATION + * 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_CONFIGURATION_SIMPLE + * This method is identical to \c IGRAPH_DEGSEQ_CONFIGURATION, 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_FAST_HEUR_SIMPLE + * This method generates simple graphs. + * It is similar to \c IGRAPH_DEGSEQ_CONFIGURATION + * 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_EDGE_SWITCHING_SIMPLE + * This is an MCMC sampler based on degree-preserving edge switches. + * It generates simple undirected or directed graphs. + * It uses \ref igraph_realize_degree_sequence() to construct an initial + * graph, then rewires it using \ref igraph_rewire(). + * \cli IGRAPH_DEGSEQ_VL + * This method samples undirected \em connected graphs approximately + * uniformly. It is a Monte Carlo method based on degree-preserving + * edge switches. + * 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_CONFIGURATION and \c IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE. + * The time complexity of the other modes is not known. + * + * \sa \ref igraph_is_graphical() to determine if there exist graphs with a certain + * degree sequence; \ref igraph_erdos_renyi_game_gnm() to generate graphs with a + * fixed number of edges, without any degree constraints; \ref igraph_chung_lu_game() + * and \ref igraph_static_fitness_game() to sample random graphs with a prescribed + * \em expected degree sequence (but variable actual degrees); + * \ref igraph_realize_degree_sequence() and \ref igraph_realize_bipartite_degree_sequence() + * to generate a single (non-random) graph with given degrees. + * + * \example examples/simple/igraph_degree_sequence_game.c + */ + +igraph_error_t igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_int_t *out_deg, + const igraph_vector_int_t *in_deg, + igraph_degseq_t method) { + if (in_deg && igraph_vector_int_empty(in_deg) && !igraph_vector_int_empty(out_deg)) { + in_deg = 0; + } + + switch (method) { + case IGRAPH_DEGSEQ_CONFIGURATION: + return igraph_i_degree_sequence_game_configuration(graph, out_deg, in_deg); + + case IGRAPH_DEGSEQ_VL: + return igraph_degree_sequence_game_vl(graph, out_deg, in_deg); + + case IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE: + if (in_deg == 0) { + return igraph_i_degree_sequence_game_fast_heur_undirected(graph, out_deg); + } else { + return igraph_i_degree_sequence_game_fast_heur_directed(graph, out_deg, in_deg); + } + + case IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE: + if (in_deg == 0) { + return igraph_i_degree_sequence_game_configuration_simple_undirected(graph, out_deg); + } else { + return igraph_i_degree_sequence_game_configuration_simple_directed(graph, out_deg, in_deg); + } + + case IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE: + return igraph_i_degree_sequence_game_edge_switching(graph, out_deg, in_deg); + + default: + IGRAPH_ERROR("Invalid degree sequence game method.", IGRAPH_EINVAL); + } +} diff --git a/src/games/degree_sequence_vl/degree_sequence_vl.h b/src/games/degree_sequence_vl/degree_sequence_vl.h new file mode 100644 index 0000000..c4e9eeb --- /dev/null +++ b/src/games/degree_sequence_vl/degree_sequence_vl.h @@ -0,0 +1,34 @@ +/* + 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, see . +*/ + +#ifndef IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H +#define IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +igraph_error_t igraph_degree_sequence_game_vl(igraph_t *graph, + const igraph_vector_int_t *out_seq, + const igraph_vector_int_t *in_seq); + +__END_DECLS + +#endif /* IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H */ diff --git a/src/games/degree_sequence_vl/gengraph_definitions.h b/src/games/degree_sequence_vl/gengraph_definitions.h new file mode 100644 index 0000000..c96e24b --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_definitions.h @@ -0,0 +1,195 @@ +/* + * + * 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 "igraph_types.h" +#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); +long my_random(); +int my_binomial(double pp, int n); +double my_random01(); // (0,1] + +#define MY_RAND_MAX 0x7FFFFFFF + +//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) + defmax(long long) +#endif //max + +// Debug definitions +//#define PERFORMANCE_MONITOR +//#define OPT_ISOLATED + +// Max Int +#ifndef MAX_INT + #define MAX_INT 0x7FFFFFFF +#endif //MAX_INT + +//Edge type +typedef struct { + igraph_integer_t from; + igraph_integer_t 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 igraph_integer_t* fast_rpl(igraph_integer_t *m, igraph_integer_t a, igraph_integer_t b) { + while (*m != a) { + m++; + } + *m = b; + return m; +} +inline igraph_integer_t* fast_search(igraph_integer_t *m, igraph_integer_t size, igraph_integer_t a) { + igraph_integer_t *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() { + long 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 long _random_bits = 0; + +inline int random_bit() { + long 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/games/degree_sequence_vl/gengraph_degree_sequence.cpp b/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp new file mode 100644 index 0000000..2db0a08 --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp @@ -0,0 +1,140 @@ +/* + * + * 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_degree_sequence.h" + +#include +#include +#include +#include +#include +#include + +// using namespace __gnu_cxx; +using namespace std; + +namespace gengraph { + +// shuffle an igraph_integer_t[] randomly +void random_permute(igraph_integer_t *a, igraph_integer_t n); + +// sort an array of positive integers in time & place O(n + max) +void cumul_sort(igraph_integer_t *q, igraph_integer_t n); + + +degree_sequence::~degree_sequence() { + deg = NULL; +} + +void degree_sequence::compute_total() { + total = 0; + for (igraph_integer_t i = 0; i < n; i++) { + total += deg[i]; + } +} + +degree_sequence:: +degree_sequence(igraph_integer_t n0, igraph_integer_t *degs) { + deg = degs; + n = n0; + compute_total(); +} + +degree_sequence:: +degree_sequence(const igraph_vector_int_t *out_seq) { + n = igraph_vector_int_size(out_seq); + deg = &VECTOR(*out_seq)[0]; + compute_total(); +} + +#ifndef FBUFF_SIZE + #define FBUFF_SIZE 999 +#endif //FBUFF_SIZE + +bool degree_sequence::havelhakimi() { + + igraph_integer_t i; + igraph_integer_t dm = dmax() + 1; + // Sort vertices using basket-sort, in descending degrees + igraph_integer_t *nb = new igraph_integer_t[dm]; + igraph_integer_t *sorted = new igraph_integer_t[n]; + // init basket + for (i = 0; i < dm; i++) { + nb[i] = 0; + } + // count basket + for (i = 0; i < n; i++) { + nb[deg[i]]++; + } + // cumul + igraph_integer_t c = 0; + for (i = dm - 1; i >= 0; i--) { + igraph_integer_t t = nb[i]; + nb[i] = c; + c += t; + } + // sort + for (i = 0; i < n; i++) { + sorted[nb[deg[i]]++] = i; + } + +// Binding process starts + igraph_integer_t first = 0; // vertex with biggest residual degree + igraph_integer_t 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 + igraph_integer_t dv = d; + // bind it ! + c -= dv; + igraph_integer_t dc = d; // residual degree of vertices we bind to + igraph_integer_t fc = ++first; // position of the first vertex with degree dc + + while (dv > 0 && dc > 0) { + igraph_integer_t 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; +} + +} // namespace gengraph diff --git a/src/games/degree_sequence_vl/gengraph_degree_sequence.h b/src/games/degree_sequence_vl/gengraph_degree_sequence.h new file mode 100644 index 0000000..ef286bf --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_degree_sequence.h @@ -0,0 +1,88 @@ +/* + * + * 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_vector.h" + +namespace gengraph { + +class degree_sequence { + +private: + igraph_integer_t n; + igraph_integer_t *deg; + igraph_integer_t total; + +public : + // #vertices + inline igraph_integer_t size() { + return n; + }; + inline igraph_integer_t sum() { + return total; + }; + inline igraph_integer_t operator[](igraph_integer_t i) { + return deg[i]; + }; + inline igraph_integer_t *seq() { + return deg; + }; + inline void assign(igraph_integer_t n0, igraph_integer_t* d0) { + n = n0; + deg = d0; + }; + inline igraph_integer_t dmax() { + igraph_integer_t dm = deg[0]; + for (igraph_integer_t i = 1; i < n; i++) if (deg[i] > dm) { + dm = deg[i]; + } + return dm; + } + + degree_sequence(igraph_integer_t n, igraph_integer_t *degs); + + // igraph constructor + degree_sequence(const igraph_vector_int_t *out_seq); + + // destructor + ~degree_sequence(); + + // compute total number of arcs + void compute_total(); + +#if 0 + // raw print (vertex by vertex) + void print(); + + // distribution print (degree frequency) + void print_cumul(); +#endif + + // is degree sequence realizable ? + bool havelhakimi(); + +}; + +} // namespace gengraph + +#endif //DEGREE_SEQUENCE_H diff --git a/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp b/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp new file mode 100644 index 0000000..586d944 --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp @@ -0,0 +1,732 @@ +/* + * + * 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_qsort.h" +#include "gengraph_hash.h" +#include "gengraph_degree_sequence.h" +#include "gengraph_graph_molloy_hash.h" + +#include "igraph_constructors.h" +#include "igraph_error.h" +#include "igraph_progress.h" + +#include +#include +#include +#include +#include + +namespace gengraph { + +//_________________________________________________________________________ +void graph_molloy_hash::compute_neigh() { + igraph_integer_t *p = links; + for (igraph_integer_t i = 0; i < n; i++) { + neigh[i] = p; + p += HASH_SIZE(deg[i]); + } +} + +//_________________________________________________________________________ +void graph_molloy_hash::compute_size() { + size = 0; + for (igraph_integer_t i = 0; i < n; i++) { + size += HASH_SIZE(deg[i]); + } +} + +//_________________________________________________________________________ +void graph_molloy_hash::init() { + for (igraph_integer_t i = 0; i < size; i++) { + links[i] = HASH_NONE; + } +} + +//_________________________________________________________________________ +graph_molloy_hash::graph_molloy_hash(degree_sequence °s) { + alloc(degs); +} + +//_________________________________________________________________________ +igraph_integer_t graph_molloy_hash::alloc(degree_sequence °s) { + n = degs.size(); + a = degs.sum(); + assert(a % 2 == 0); + + deg = degs.seq(); + compute_size(); + deg = new igraph_integer_t[n + size]; + if (deg == NULL) { + return 0; + } + igraph_integer_t i; + for (i = 0; i < n; i++) { + deg[i] = degs[i]; + } + links = deg + n; + init(); + neigh = new igraph_integer_t*[n]; + if (neigh == NULL) { + return 0; + } + compute_neigh(); + return sizeof(igraph_integer_t *)*n + sizeof(igraph_integer_t) * (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(igraph_integer_t *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); + // Read links[] + restore(svg + n); +} + +//_________________________________________________________________________ +igraph_integer_t *graph_molloy_hash::hard_copy() { + igraph_integer_t *hc = new igraph_integer_t[2 + n + a / 2]; // to store n,a,deg[] and links[] + hc[0] = n; + hc[1] = a; + memcpy(hc + 2, deg, sizeof(igraph_integer_t)*n); + igraph_integer_t *p = hc + 2 + n; + igraph_integer_t *l = links; + for (igraph_integer_t i = 0; i < n; i++) for (igraph_integer_t j = HASH_SIZE(deg[i]); j--; l++) { + igraph_integer_t 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]; + igraph_integer_t *buff = new igraph_integer_t[n]; + igraph_integer_t comp_size = depth_search(visited, buff); + delete[] visited; + delete[] buff; + return (comp_size == n); +} + +//_________________________________________________________________________ +igraph_integer_t* graph_molloy_hash::backup() { + igraph_integer_t *b = new igraph_integer_t[a / 2]; + igraph_integer_t *c = b; + igraph_integer_t *p = links; + for (igraph_integer_t i = 0; i < n; i++) + for (igraph_integer_t 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(igraph_integer_t* b) { + init(); + igraph_integer_t i; + igraph_integer_t *dd = new igraph_integer_t[n]; + memcpy(dd, deg, sizeof(igraph_integer_t)*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(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { + if (K < 2) { + return false; + } +#ifdef OPT_ISOLATED + if (K <= deg[v] + 1) { + return false; + } +#endif //OPT_ISOLATED + igraph_integer_t *seen = Kbuff; + igraph_integer_t *known = Kbuff; + igraph_integer_t *max = Kbuff + K; + *(known++) = v; + visited[v] = true; + bool is_isolated = true; + + while (known != seen) { + v = *(seen++); + igraph_integer_t *ww = neigh[v]; + igraph_integer_t w; + for (igraph_integer_t 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(igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { + // Pick two random vertices a and c + igraph_integer_t f1 = pick_random_vertex(); + igraph_integer_t f2 = pick_random_vertex(); + // Check that f1 != f2 + if (f1 == f2) { + return 0; + } + // Get two random edges (f1,*f1t1) and (f2,*f2t2) + igraph_integer_t *f1t1 = random_neighbour(f1); + igraph_integer_t t1 = *f1t1; + igraph_integer_t *f2t2 = random_neighbour(f2); + igraph_integer_t 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 + igraph_integer_t *f1t2 = H_rpl(neigh[f1], deg[f1], f1t1, t2); + igraph_integer_t *f2t1 = H_rpl(neigh[f2], deg[f2], f2t2, t1); + igraph_integer_t *t1f2 = H_rpl(neigh[t1], deg[t1], f1, f2); + igraph_integer_t *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; +} + +//_________________________________________________________________________ +igraph_integer_t graph_molloy_hash::shuffle(igraph_integer_t times, + igraph_integer_t maxtimes, int type) { + igraph_progress("Shuffle", 0, 0); + // assert(verify()); + // counters + igraph_integer_t nb_swaps = 0; + igraph_integer_t all_swaps = 0; + unsigned long cost = 0; + // window + double T = double(((a < times) ? 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; + igraph_integer_t *Kbuff = new igraph_integer_t[int(K) + 1]; + bool *visited = new bool[n]; + for (igraph_integer_t i = 0; i < n; i++) { + visited[i] = false; + } + // Used for monitoring , active only if VERBOSE() + igraph_integer_t failures = 0; + igraph_integer_t 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 + igraph_integer_t *save = backup(); + // Prepare counters, K, T + igraph_integer_t swaps = 0; + igraph_integer_t K_int = 0; + if (type == FINAL_HEURISTICS || type == BRUTE_FORCE_HEURISTICS) { + K_int = igraph_integer_t(K); + } + igraph_integer_t T_int = (igraph_integer_t)(floor(T)); + if (T_int < 1) { + T_int = 1; + } + // compute cost + cost += T_int; + if (K_int > 2) { + cost += K_int + T_int; + } + // Perform T edge swap attempts + for (igraph_integer_t i = T_int; i > 0; i--) { + // try one swap + swaps += random_edge_swap(K_int, Kbuff, visited); + all_swaps++; + // Verbose + if (nb_swaps + swaps > next) { + next = (nb_swaps + swaps) + (times / 1000 > 100 ? times / 1000 : 100); + int progress = int(double(nb_swaps + swaps) / double(times)); + igraph_progress("Shuffle", progress, 0); + } + } + // test connectivity + cost += (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 igraph_integer_t[igraph_integer_t(K) + 1]; + } + break; + case OPTIMAL_HEURISTICS: + if (ok) { + T = double(optimal_window()); + } + break; + case BRUTE_FORCE_HEURISTICS: + K *= 2; delete[] Kbuff; Kbuff = new igraph_integer_t[igraph_integer_t(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?"); + } + + return nb_swaps; +} + +//_________________________________________________________________________ + +/* +void graph_molloy_hash::print(FILE *f) { + igraph_integer_t i, j; + for (i = 0; i < n; i++) { + fprintf(f, "%" IGRAPH_PRId, i); + for (j = 0; j < HASH_SIZE(deg[i]); j++) if (neigh[i][j] != HASH_NONE) { + fprintf(f, " %" IGRAPH_PRId, neigh[i][j]); + } + fprintf(f, "\n"); + } +} +*/ + +igraph_error_t graph_molloy_hash::print(igraph_t *graph) { + igraph_integer_t i, j; + igraph_integer_t ptr = 0; + igraph_vector_int_t edges; + + IGRAPH_VECTOR_INT_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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +//_________________________________________________________________________ +bool graph_molloy_hash::try_shuffle(igraph_integer_t T, igraph_integer_t K, igraph_integer_t *backup_graph) { + // init all + igraph_integer_t *Kbuff = NULL; + bool *visited = NULL; + if (K > 2) { + Kbuff = new igraph_integer_t[K]; + visited = new bool[n]; + for (igraph_integer_t i = 0; i < n; i++) { + visited[i] = false; + } + } + igraph_integer_t *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(igraph_integer_t T, igraph_integer_t *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; + } +} + +//_________________________________________________________________________ +igraph_integer_t graph_molloy_hash::optimal_window() { + igraph_integer_t Tmax; + igraph_integer_t optimal_T = 1; + double min_cost = 1e+99; + igraph_integer_t *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; + } + } + // on cherche autour + double span = 2.0; + int try_again = 4; + while (span > 1.05 && optimal_T <= 5 * a) { + igraph_integer_t T_low = igraph_integer_t(double(optimal_T) / span); + igraph_integer_t T_high = igraph_integer_t(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; + } + 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(igraph_integer_t K, int quality) { + if (K < 3) { + return 0.0; + } + long sum_K = 0; + igraph_integer_t *Kbuff = new igraph_integer_t[K]; + bool *visited = new bool[n]; + igraph_integer_t i; + for (i = 0; i < n; i++) { + visited[i] = false; + } + for (i = 0; i < quality; i++) { + // assert(verify()); + igraph_integer_t f1, f2, t1, t2; + igraph_integer_t *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); +} + +//_________________________________________________________________________ +igraph_integer_t graph_molloy_hash::effective_isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { + igraph_integer_t i; + for (i = 0; i < K; i++) { + Kbuff[i] = -1; + } + igraph_integer_t count = 0; + igraph_integer_t left = K; + igraph_integer_t *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(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &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++; + igraph_integer_t *copy = NULL; + igraph_integer_t *w = neigh[v]; + if (IS_HASH(deg[v])) { + copy = new igraph_integer_t[deg[v]]; + H_copy(copy, w, deg[v]); + w = copy; + } + qsort(deg, w, deg[v]); + w += deg[v]; + for (igraph_integer_t 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; + } +} + +//_________________________________________________________________________ +igraph_integer_t graph_molloy_hash::depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t v0) { + for (igraph_integer_t i = 0; i < n; i++) { + visited[i] = false; + } + igraph_integer_t *to_visit = buff; + igraph_integer_t nb_visited = 1; + visited[v0] = true; + *(to_visit++) = v0; + while (to_visit != buff && nb_visited < n) { + igraph_integer_t v = *(--to_visit); + igraph_integer_t *ww = neigh[v]; + igraph_integer_t w; + for (igraph_integer_t 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; +// } + +} // namespace gengraph diff --git a/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.h b/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.h new file mode 100644 index 0000000..086bcd9 --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.h @@ -0,0 +1,188 @@ +/* + * + * 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 GRAPH_MOLLOY_HASH_H +#define GRAPH_MOLLOY_HASH_H + +#include "gengraph_definitions.h" +#include "gengraph_hash.h" +#include "gengraph_degree_sequence.h" + +#include "igraph_datatype.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 + igraph_integer_t n; + //Number of arcs ( = #edges * 2 ) + igraph_integer_t a; + //Total size of links[] + igraph_integer_t size; + // The degree sequence of the graph + igraph_integer_t *deg; + // The array containing all links + igraph_integer_t *links; + // The array containing pointers to adjacency list of every vertices + igraph_integer_t **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!!) + igraph_integer_t 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(igraph_integer_t u, igraph_integer_t v, igraph_integer_t *realdeg) { + igraph_integer_t deg_u = realdeg[u]; + if (deg_u == deg[u]) { + return false; + } + // Check that edge was not already inserted + assert(fast_search(neigh[u], (u == n - 1 ? links + size : neigh[u + 1]) - neigh[u], v) == NULL); + assert(fast_search(neigh[v], (v == n - 1 ? links + size : neigh[v + 1]) - neigh[v], u) == NULL); + assert(deg[u] < deg_u); + igraph_integer_t 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(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t 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(igraph_integer_t) bytes per edge] + igraph_integer_t* backup(); + // Test if vertex is in an isolated component of size dmax. + void depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited); + + +public: + //degree of v + inline igraph_integer_t degree(igraph_integer_t 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(igraph_integer_t *); + // Create hard copy of graph + igraph_integer_t *hard_copy(); + // Restore from backup + void restore(igraph_integer_t* back); + //Clear hash tables + void init(); + // nb arcs + inline igraph_integer_t nbarcs() { + return a; + }; + // nb vertices + inline igraph_integer_t nbvertices() { + return n; + }; + // print graph in SUCC_LIST mode, in stdout + /* void print(FILE *f = stdout); */ + igraph_error_t print(igraph_t *graph); + // Test if graph is connected + bool is_connected(); + // is edge ? + inline bool is_edge(igraph_integer_t u, igraph_integer_t 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(igraph_integer_t K = 0, igraph_integer_t *Kbuff = NULL, bool *visited = NULL); + // Connected Shuffle + igraph_integer_t shuffle(igraph_integer_t, igraph_integer_t, int type); + // Optimal window for the gkantsidis heuristics + igraph_integer_t optimal_window(); + // Average unitary cost per post-validated edge swap, for some window + double average_cost(igraph_integer_t T, igraph_integer_t *back, double min_cost); + // Get caracteristic K + double eval_K(int quality = 100); + // Get effective K + double effective_K(igraph_integer_t K, int quality = 10000); + // Try to shuffle T times. Return true if at the end, the graph was still connected. + bool try_shuffle(igraph_integer_t T, igraph_integer_t K, igraph_integer_t *back = NULL); +}; + +} // namespace gengraph + +#endif //GRAPH_MOLLOY_HASH_H diff --git a/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp b/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp new file mode 100644 index 0000000..dc45a2a --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp @@ -0,0 +1,1212 @@ +/* + * + * 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_degree_sequence.h" +#include "gengraph_graph_molloy_optimized.h" + +#include "igraph_error.h" +#include "igraph_progress.h" + + +using namespace std; + +namespace gengraph { + +igraph_integer_t graph_molloy_opt::max_degree() { + igraph_integer_t m = 0; + for (igraph_integer_t k = 0; k < n; k++) if (deg[k] > m) { + m = deg[k]; + } + return m; +} + +void graph_molloy_opt::compute_neigh() { + igraph_integer_t *p = links; + for (igraph_integer_t 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 igraph_integer_t[n + a]; + for (igraph_integer_t i = 0; i < n; i++) { + deg[i] = degs[i]; + } + links = deg + n; + neigh = new igraph_integer_t*[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; +} + +igraph_integer_t *graph_molloy_opt::hard_copy() { + igraph_integer_t *hc = new igraph_integer_t[2 + n + a / 2]; // to store n,a,deg[] and links[] + hc[0] = n; + hc[1] = a; + memcpy(hc + 2, deg, sizeof(igraph_integer_t)*n); + igraph_integer_t *c = hc + 2 + n; + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t *p = neigh[i]; + for (igraph_integer_t 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(igraph_integer_t* b) { + igraph_integer_t i; + for (i = 0; i < n; i++) { + deg[i] = 0; + } + igraph_integer_t *p = links; + for (i = 0; i < n - 1; i++) { + p += deg[i]; + deg[i] = igraph_integer_t(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++); + } + } +} + +igraph_integer_t* graph_molloy_opt::backup_degs(igraph_integer_t *b) { + if (b == NULL) { + b = new igraph_integer_t[n]; + } + memcpy(b, deg, sizeof(igraph_integer_t)*n); + return b; +} + +void graph_molloy_opt::restore_degs_only(igraph_integer_t *b) { + memcpy(deg, b, sizeof(igraph_integer_t)*n); + refresh_nbarcs(); +} + +void graph_molloy_opt::restore_degs_and_neigh(igraph_integer_t *b) { + restore_degs_only(b); + compute_neigh(); +} + +void graph_molloy_opt::restore_degs(igraph_integer_t last_degree) { + a = last_degree; + deg[n - 1] = last_degree; + for (igraph_integer_t i = n - 2; i >= 0; i--) { + a += (deg[i] = igraph_integer_t(neigh[i + 1] - neigh[i])); + } + refresh_nbarcs(); +} + +void graph_molloy_opt::clean() { + igraph_integer_t *b = hard_copy(); + replace(b); + delete[] b; +} + +void graph_molloy_opt::replace(igraph_integer_t *_hardcopy) { + delete[] deg; + n = *(_hardcopy++); + a = *(_hardcopy++); + deg = new igraph_integer_t[a + n]; + memcpy(deg, _hardcopy, sizeof(igraph_integer_t)*n); + links = deg + n; + compute_neigh(); + restore(_hardcopy + n); +} + +igraph_integer_t* graph_molloy_opt::components(igraph_integer_t *comp) { + igraph_integer_t i; + // breadth-first search buffer + igraph_integer_t *buff = new igraph_integer_t[n]; + // comp[i] will contain the index of the component that contains vertex i + if (comp == NULL) { + comp = new igraph_integer_t[n]; + } + memset(comp, 0, sizeof(igraph_integer_t)*n); + // current component index + igraph_integer_t curr_comp = 0; + // loop over all non-visited vertices... + for (igraph_integer_t v0 = 0; v0 < n; v0++) if (comp[v0] == 0) { + curr_comp++; + // initiate breadth-first search + igraph_integer_t *to_visit = buff; + igraph_integer_t *visited = buff; + *(to_visit++) = v0; + comp[v0] = curr_comp; + // breadth-first search + while (visited != to_visit) { + igraph_integer_t v = *(visited++); + igraph_integer_t d = deg[v]; + for (igraph_integer_t *w = neigh[v]; d--; w++) if (comp[*w] == 0) { + comp[*w] = curr_comp; + *(to_visit++) = *w; + } + } + } + // compute component sizes and store them in buff[] + igraph_integer_t nb_comp = 0; + memset(buff, 0, sizeof(igraph_integer_t)*n); + for (i = 0; i < n; i++) + if (buff[comp[i] - 1]++ == 0 && comp[i] > nb_comp) { + nb_comp = comp[i]; + } + // box-sort sizes + igraph_integer_t offset = 0; + igraph_integer_t *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 (igraph_integer_t *c = comp + n; comp != c--; *c = buff[*c - 1]) { } + // clean.. at last! + delete[] buff; + return comp; +} + +bool graph_molloy_opt::havelhakimi() { + + igraph_integer_t i; + igraph_integer_t dmax = max_degree() + 1; + // Sort vertices using basket-sort, in descending degrees + igraph_integer_t *nb = new igraph_integer_t[dmax]; + igraph_integer_t *sorted = new igraph_integer_t[n]; + // init basket + for (i = 0; i < dmax; i++) { + nb[i] = 0; + } + // count basket + for (i = 0; i < n; i++) { + nb[deg[i]]++; + } + // cumul + igraph_integer_t 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 + igraph_integer_t first = 0; // vertex with biggest residual degree + igraph_integer_t 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 + igraph_integer_t v = sorted[first]; + // look for current degree of v + while (nb[d] <= first) { + d--; + } + // store it in dv + igraph_integer_t dv = d; + // bind it ! + c -= dv; + igraph_integer_t dc = d; // residual degree of vertices we bind to + igraph_integer_t fc = ++first; // position of the first vertex with degree dc + + while (dv > 0 && dc > 0) { + igraph_integer_t lc = nb[dc]; + if (lc != fc) { + while (dv > 0 && lc > fc) { + // binds v with sorted[--lc] + dv--; + igraph_integer_t 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 use IGRAPH_FATAL(), as triggering this + * indicates a bug. */ + IGRAPH_FATALF("Error in graph_molloy_opt::havelhakimi(): " + "Couldn't bind vertex %" IGRAPH_PRId " entirely (%" IGRAPH_PRId " edges remaining)", + 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 (igraph_integer_t i = n; i > 0; visited[--i] = false) { } + igraph_integer_t *to_visit = new igraph_integer_t[n]; + igraph_integer_t *stop = to_visit; + igraph_integer_t left = n - 1; + *(to_visit++) = 0; + visited[0] = true; + while (left > 0 && to_visit != stop) { + igraph_integer_t v = *(--to_visit); + igraph_integer_t *w = neigh[v]; + for (igraph_integer_t 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; + } + igraph_integer_t 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) + igraph_integer_t *buff = new igraph_integer_t[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 + igraph_integer_t *ffub = buff + MC_BUFF_SIZE; + edge *edges = (edge *) ffub; + igraph_integer_t *trees = ffub; + igraph_integer_t *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 (igraph_integer_t v0 = 0; v0 < n; v0++) if (dist[v0] == NOT_VISITED) { + // is v0 an isolated vertex? + if (deg[v0] == 0) { + delete[] dist; + delete[] buff; + // 0-degree vertex found, cannot create connected graph + return false; + } + dist[v0] = 0; // root + igraph_integer_t *to_visit = buff; + igraph_integer_t *current = buff; + *(to_visit++) = v0; + + // explore component connected to v0 + bool is_a_tree = true; + while (current != to_visit) { + igraph_integer_t v = *(current++); + unsigned char current_dist = dist[v]; + unsigned char next_dist = (current_dist + 1) & 0x03; + //unsigned char prev_dist = (current_dist-1) & 0x03; + igraph_integer_t* ww = neigh[v]; + igraph_integer_t w; + for (igraph_integer_t 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(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t 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; +} + +void graph_molloy_opt::print(FILE *f, bool NOZERO) { + igraph_integer_t i, j; + for (i = 0; i < n; i++) { + if (!NOZERO || deg[i] > 0) { + fprintf(f, "%" IGRAPH_PRId, i); + for (j = 0; j < deg[i]; j++) { + fprintf(f, " %" IGRAPH_PRId, neigh[i][j]); + } + fprintf(f, "\n"); + } + } +} + +igraph_integer_t graph_molloy_opt::effective_isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { + igraph_integer_t i; + for (i = 0; i < K; i++) { + Kbuff[i] = -1; + } + igraph_integer_t count = 0; + igraph_integer_t left = K; + igraph_integer_t *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(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &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++; + igraph_integer_t *w = neigh[v]; + qsort(deg, w, deg[v]); + w += deg[v]; + for (igraph_integer_t 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; + } + } +} + +igraph_integer_t graph_molloy_opt::depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t v0) { + for (igraph_integer_t i = 0; i < n; i++) { + visited[i] = false; + } + igraph_integer_t *to_visit = buff; + igraph_integer_t nb_visited = 1; + visited[v0] = true; + *(to_visit++) = v0; + while (to_visit != buff && nb_visited < n) { + igraph_integer_t v = *(--to_visit); + igraph_integer_t *ww = neigh[v]; + igraph_integer_t w; + for (igraph_integer_t k = deg[v]; k--; ww++) if (!visited[w = *ww]) { + visited[w] = true; + nb_visited++; + *(to_visit++) = w; + } + } + return nb_visited; +} + +igraph_integer_t graph_molloy_opt::width_search(unsigned char *dist, igraph_integer_t *buff, igraph_integer_t v0, igraph_integer_t toclear) { + if (toclear >= 0) for (igraph_integer_t i = 0; i < toclear; i++) { + dist[buff[i]] = 0; + } else for (igraph_integer_t i = 0; i < n; i++) { + dist[i] = 0; + } + igraph_integer_t *to_visit = buff; + igraph_integer_t *to_add = buff; + igraph_integer_t nb_visited = 1; + dist[v0] = 1; + *(to_add++) = v0; + while (to_visit != to_add && nb_visited < n) { + igraph_integer_t v = *(to_visit++); + igraph_integer_t *ww = neigh[v]; + igraph_integer_t w; + unsigned char d = next_dist(dist[v]); + for (igraph_integer_t k = deg[v]; k--; ww++) if (dist[w = *ww] == 0) { + dist[w] = d; + nb_visited++; + *(to_add++) = w; + } + } + return nb_visited; +} + +// dist[] MUST be full of zeros !!!! +igraph_integer_t graph_molloy_opt::breadth_path_search(igraph_integer_t src, igraph_integer_t *buff, double *paths, unsigned char *dist) { + unsigned char last_dist = 0; + unsigned char curr_dist = 1; + igraph_integer_t *to_visit = buff; + igraph_integer_t *visited = buff; + *(to_visit++) = src; + paths[src] = 1.0; + dist[src] = curr_dist; + igraph_integer_t nb_visited = 1; + while (visited != to_visit) { + igraph_integer_t v = *(visited++); + if (last_dist == (curr_dist = dist[v])) { + break; + } + unsigned char nd = next_dist(curr_dist); + igraph_integer_t *ww = neigh[v]; + double p = paths[v]; + for (igraph_integer_t k = deg[v]; k--;) { + igraph_integer_t 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()) { + throw std::runtime_error("Fatal error: too many (>MAX_DOUBLE) possible paths in graph."); + } + } + } + } + assert(to_visit == buff + nb_visited); + return nb_visited; +} + +igraph_integer_t *graph_molloy_opt::vertices_real(igraph_integer_t &nb_v) { + igraph_integer_t *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; + } + igraph_integer_t *buff = new igraph_integer_t[nb_v]; + yo = buff; + for (igraph_integer_t 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(%" IGRAPH_PRId ")", nb_v); + delete[] buff; + return NULL; + } else { + return buff; + } +} + +bool graph_molloy_opt::isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { + if (K < 2) { + return false; + } +#ifdef OPT_ISOLATED + if (K <= deg[v] + 1) { + return false; + } +#endif //OPT_ISOLATED + igraph_integer_t *seen = Kbuff; + igraph_integer_t *known = Kbuff; + igraph_integer_t *max = Kbuff + (K - 1); + *(known++) = v; + visited[v] = true; + bool is_isolated = true; + + while (known != seen) { + v = *(seen++); + igraph_integer_t *w = neigh[v]; + for (igraph_integer_t 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; +} + +void graph_molloy_opt::sort() { + for (int v = 0; v < n; v++) { + qsort(neigh[v], deg[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 + igraph_integer_t 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 +#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 + igraph_integer_t n; + //Number of arcs ( = #edges * 2 ) + igraph_integer_t a; + // The degree sequence of the graph + igraph_integer_t *deg; + // The array containing all links + igraph_integer_t *links; + // The array containing pointers to adjacency list of every vertices + igraph_integer_t **neigh; + // Allocate memory according to degree_sequence (for constructor use only!!) + void alloc(degree_sequence &); + // Compute #edges + inline void refresh_nbarcs() { + a = 0; + for (igraph_integer_t* 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(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t 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(igraph_integer_t, igraph_integer_t, igraph_integer_t, igraph_integer_t); + // Test if vertex is in an isolated component of size dmax. + void depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited); + // breadth-first search. Store the distance (modulo 3) in dist[]. Returns eplorated component size. + igraph_integer_t width_search(unsigned char *dist, igraph_integer_t *buff, igraph_integer_t v0 = 0, igraph_integer_t toclear = -1); + // depth-first search. + igraph_integer_t depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t v0 = 0); + // breadth-first search that count the number of shortest paths going from src to each vertex + igraph_integer_t breadth_path_search(igraph_integer_t src, igraph_integer_t *buff, double *paths, unsigned char *dist); + // Return component indexes where vertices belong to, starting from 0, + // sorted by size (biggest component has index 0) + igraph_integer_t *components(igraph_integer_t *comp = NULL); + +public: + // neigh[] + inline igraph_integer_t** neighbors() { + return neigh; + }; + // deg[] + inline igraph_integer_t* degrees() { + return deg; + }; + //adjacency list of v + inline igraph_integer_t* operator[](const igraph_integer_t v) { + return neigh[v]; + }; + //degree of v + inline igraph_integer_t degree(const igraph_integer_t v) { + return deg[v]; + }; + // 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(igraph_integer_t *); + // Create hard copy of graph + igraph_integer_t *hard_copy(); + // Remove unused edges, updates neigh[], recreate links[] + void clean(); + // nb arcs + inline igraph_integer_t nbarcs() { + return a; + }; + // last degree + inline igraph_integer_t last_degree() { + return deg[n - 1]; + }; + // nb vertices + inline igraph_integer_t nbvertices() { + return n; + }; + // nb vertices having degree > 0 + inline igraph_integer_t nbvertices_real() { + igraph_integer_t s = 0; + for (igraph_integer_t *d = deg + n; d-- != deg; ) { + if (*d) { + s++; + } + } + return s; + }; + // return list of vertices with degree > 0. Compute #vertices, if not given. + igraph_integer_t *vertices_real(igraph_integer_t &nb_v); + // 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 + igraph_integer_t max_degree(); + // is edge ? + inline bool is_edge(const igraph_integer_t u, const igraph_integer_t 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(igraph_integer_t) bytes per edge] + igraph_integer_t* backup(igraph_integer_t *here = NULL); + // Restore from backup. Assume that degrees haven't changed + void restore(igraph_integer_t* back); + // Resplace with hard backup. + void replace(igraph_integer_t* _hardbackup); + // Backup degs of graph + igraph_integer_t* backup_degs(igraph_integer_t *here = NULL); + // Restore degs from neigh[]. Need last degree, though + void restore_degs(igraph_integer_t last_degree); + // Restore degs[] from backup. Assume that links[] has only been permuted + void restore_degs_only(igraph_integer_t* backup_degs); + // Restore degs[] and neigh[]. Assume that links[] has only been permuted + void restore_degs_and_neigh(igraph_integer_t* backup_degs); + // sort adjacency lists + void sort(); + // 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); + + // 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/games/degree_sequence_vl/gengraph_hash.h b/src/games/degree_sequence_vl/gengraph_hash.h new file mode 100644 index 0000000..9689c0a --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_hash.h @@ -0,0 +1,315 @@ +/* + * + * 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 igraph_integer_t HASH_EXPAND(igraph_integer_t x) { + /* Returns pow(2, floor(log2(x)) + 2) if x > 0, 1 otherwise. Works up to + * x == 2^64, starts to break down afterwards */ + _HASH_EXP_CALL(); + x += x; + x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; +#if IGRAPH_INTEGER_SIZE == 64 + x |= x >> 32; +#endif + 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 igraph_integer_t HASH_KEY(igraph_integer_t x, igraph_integer_t size) { + assert(x >= 0); + return x % size; +}; +inline igraph_integer_t HASH_EXPAND(igraph_integer_t x) { + _HASH_EXP_CALL(); + return x + (x >> 1); +}; +inline int HASH_UNEXPAND(igraph_integer_t x) { + return ((x << 1) + 1) / 3; +}; +#endif //HASH_SIZE_IS_POWER2 +inline int HASH_REKEY(igraph_integer_t k, igraph_integer_t s) { + assert(k >= 0); + if (k == 0) { + return s - 1; + } else { + return k - 1; + } +}; +inline int HASH_SIZE(igraph_integer_t x) { + if (IS_HASH(x)) { + return HASH_EXPAND(x); + } else { + return x; + } +}; +#endif //MACRO_RATHER_THAN_INLINE + +inline igraph_integer_t HASH_PAIR_KEY(igraph_integer_t x, igraph_integer_t y, igraph_integer_t 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(igraph_integer_t *mem, igraph_integer_t *h, igraph_integer_t size) { + for (igraph_integer_t 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 igraph_integer_t* H_add(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t a) { + _HASH_ADD_CALL(); + _HASH_ADD_ITER(); + igraph_integer_t 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(igraph_integer_t a, igraph_integer_t size, igraph_integer_t currentk, igraph_integer_t newk) { + igraph_integer_t 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(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t k) { + _HASH_RM_CALL(); + igraph_integer_t lasthole = k; + do { + _HASH_RM_ITER(); + k = HASH_REKEY(k, size); + igraph_integer_t 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 igraph_integer_t* H_put(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t a) { + assert(H_add(h, size, a) != NULL); + _HASH_PUT_CALL(); + _HASH_PUT_ITER(); + igraph_integer_t 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 igraph_integer_t H_find(igraph_integer_t *h, igraph_integer_t size, igraph_integer_t a) { + assert(H_add(h, size, a) == NULL); + _HASH_FIND_CALL(); + _HASH_FIND_ITER(); + igraph_integer_t 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(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t a, igraph_integer_t b) { + _HASH_ADD_CALL(); + _HASH_ADD_ITER(); + igraph_integer_t 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(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t 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 igraph_integer_t* H_random(igraph_integer_t* mem, igraph_integer_t size) { + if (!IS_HASH(size)) { + return mem + (my_random() % size); + } + _HASH_RAND_CALL(); + size = HASH_EXPAND(size); + igraph_integer_t* yo; + do { + yo = mem + HASH_KEY(my_random(), size); + _HASH_RAND_ITER(); + } while (*yo == HASH_NONE); + return yo; +} + +// replace *k by b +inline igraph_integer_t* H_rpl(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t* k, igraph_integer_t 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 igraph_integer_t* H_rpl(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t a, igraph_integer_t 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/games/degree_sequence_vl/gengraph_header.h b/src/games/degree_sequence_vl/gengraph_header.h new file mode 100644 index 0000000..99e7e14 --- /dev/null +++ b/src/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; +long 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/games/degree_sequence_vl/gengraph_mr-connected.cpp b/src/games/degree_sequence_vl/gengraph_mr-connected.cpp new file mode 100644 index 0000000..4147117 --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_mr-connected.cpp @@ -0,0 +1,188 @@ +/* + * + * 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 "igraph_datatype.h" +#include "igraph_graphicality.h" +#include "igraph_types.h" +#include "igraph_error.h" + +#include "core/exceptions.h" +#include "games/degree_sequence_vl/degree_sequence_vl.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; + +igraph_error_t igraph_degree_sequence_game_vl(igraph_t *graph, + const igraph_vector_int_t *out_seq, + const igraph_vector_int_t *in_seq) { + IGRAPH_HANDLE_EXCEPTIONS( + igraph_bool_t is_graphical; + + if (in_seq && igraph_vector_int_size(in_seq) != 0) { + IGRAPH_ERROR("The Viger-Latapy sampler support only undirected graphs.", 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); + } + + igraph_integer_t *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/games/degree_sequence_vl/gengraph_qsort.h b/src/games/degree_sequence_vl/gengraph_qsort.h new file mode 100644 index 0000000..85df8bc --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_qsort.h @@ -0,0 +1,307 @@ +/* + * + * 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 "igraph_types.h" + +#include +#include + +namespace gengraph { + +//___________________________________________________________________________ +// check if every element is zero +inline bool check_zero(igraph_integer_t *mem, igraph_integer_t n) { + for (igraph_integer_t *v = mem + n; v != mem; ) { + if (*(--v) != 0) { + return false; + } + } + return true; +} + +//___________________________________________________________________________ +// Sort simple integer arrays in ASCENDING order +//___________________________________________________________________________ +inline igraph_integer_t med3(igraph_integer_t a, igraph_integer_t b, igraph_integer_t 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(igraph_integer_t *v, igraph_integer_t t) { + if (t < 2) { + return; + } + for (igraph_integer_t i = 1; i < t; i++) { + igraph_integer_t *w = v + i; + igraph_integer_t tmp = *w; + while (w != v && *(w - 1) > tmp) { + *w = *(w - 1); + w--; + } + *w = tmp; + } +} + +inline igraph_integer_t partitionne(igraph_integer_t *v, igraph_integer_t t, igraph_integer_t p) { + igraph_integer_t i = 0; + igraph_integer_t j = t - 1; + while (i < j) { + while (i <= j && v[i] < p) { + i++; + } + while (i <= j && v[j] > p) { + j--; + } + if (i < j) { + igraph_integer_t 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(igraph_integer_t *v, igraph_integer_t t) { + if (t < 15) { + isort(v, t); + } else { + igraph_integer_t 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 igraph_integer_t qsort_median(igraph_integer_t *v, igraph_integer_t t, igraph_integer_t pos) { + if (t < 10) { + isort(v, t); + return v[pos]; + } + igraph_integer_t 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 igraph_integer_t qsort_median(igraph_integer_t *v, igraph_integer_t 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, igraph_integer_t t) { + if (t < 2) { + return; + } + for (igraph_integer_t 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 igraph_integer_t partitionne(double *v, igraph_integer_t t, double p) { + igraph_integer_t i = 0; + igraph_integer_t 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, igraph_integer_t t) { + if (t < 15) { + isort(v, t); + } else { + igraph_integer_t 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, igraph_integer_t t, igraph_integer_t pos) { + if (t < 10) { + isort(v, t); + return v[pos]; + } + igraph_integer_t 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, igraph_integer_t t) { + return qsort_median(v, t, t / 2); +} + +//___________________________________________________________________________ +// Sort integer arrays according to value stored in mem[], in ASCENDING order +inline void isort(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t t) { + if (t < 2) { + return; + } + for (igraph_integer_t i = 1; i < t; i++) { + igraph_integer_t vtmp = v[i]; + igraph_integer_t tmp = mem[vtmp]; + igraph_integer_t j; + for (j = i; j > 0 && tmp < mem[v[j - 1]]; j--) { + v[j] = v[j - 1]; + } + v[j] = vtmp; + } +} + +inline void qsort(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t t) { + if (t < 15) { + isort(mem, v, t); + } else { + igraph_integer_t p = med3(mem[v[t >> 1]], mem[v[(t >> 2) + 3]], mem[v[t - (t >> 1) - 3]]); + igraph_integer_t i = 0; + igraph_integer_t 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) { + igraph_integer_t 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 igraph_integer_t *pre_boxsort(igraph_integer_t *mem, igraph_integer_t n, igraph_integer_t &offset) { + igraph_integer_t *yo; + // maximum and minimum + igraph_integer_t mx = mem[0]; + igraph_integer_t mn = mem[0]; + for (yo = mem + n - 1; yo != mem; yo--) { + igraph_integer_t x = *yo; + if (x > mx) { + mx = x; + } + if (x < mn) { + mn = x; + } + } + // box + igraph_integer_t c = mx - mn + 1; + igraph_integer_t *box = new igraph_integer_t[c]; + for (yo = box + c; yo != box; * (--yo) = 0) { } + for (yo = mem + n; yo != mem; box[*(--yo) - mn]++) { } + // cumul sum + igraph_integer_t sum = 0; + for (yo = box + c; yo != box; ) { + sum += *(--yo); + *yo = sum; + } + offset = mn; + return box; +} + +inline igraph_integer_t *boxsort(igraph_integer_t *mem, igraph_integer_t n, igraph_integer_t *buff = NULL) { + igraph_integer_t i; + if (n <= 0) { + return buff; + } + igraph_integer_t offset = 0; + igraph_integer_t *box = pre_boxsort(mem, n, offset); + // sort + if (buff == NULL) { + buff = new igraph_integer_t[n]; + } + for (i = 0; i < n; i++) { + buff[--box[mem[i] - offset]] = i; + } + // clean + delete[] box; + return buff; +} + +} // namespace gengraph + +#endif //QSORT_H diff --git a/src/games/degree_sequence_vl/gengraph_random.cpp b/src/games/degree_sequence_vl/gengraph_random.cpp new file mode 100644 index 0000000..67fc0a7 --- /dev/null +++ b/src/games/degree_sequence_vl/gengraph_random.cpp @@ -0,0 +1,275 @@ +/* + * + * 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 + +using namespace std; + +//________________________________________________________________________ +// 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/games/degree_sequence_vl/gengraph_random.h b/src/games/degree_sequence_vl/gengraph_random.h new file mode 100644 index 0000000..db77c3a --- /dev/null +++ b/src/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_INTEGER(0, 0x7fffffff); + } + 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/games/dotproduct.c b/src/games/dotproduct.c new file mode 100644 index 0000000..88587af --- /dev/null +++ b/src/games/dotproduct.c @@ -0,0 +1,281 @@ +/* -*- 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_blas.h" +#include "igraph_constructors.h" +#include "igraph_random.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. + */ + +igraph_error_t 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); + igraph_integer_t i, j; + igraph_vector_int_t edges; + igraph_bool_t warned_neg = false, warned_big = false; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + for (i = 0; i < ncol; i++) { + igraph_integer_t 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 = true; + IGRAPH_WARNING("Negative connection probability in dot-product graph."); + } else if (prob > 1 && ! warned_big) { + warned_big = true; + IGRAPH_WARNING("Greater than 1 connection probability in dot-product graph."); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + } else if (RNG_UNIF01() < prob) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + } + } + } + + RNG_END(); + + IGRAPH_CHECK(igraph_create(graph, &edges, ncol, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sample_sphere_surface + * \brief 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. + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_sample_sphere_volume + * \brief 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. + */ + + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_sample_dirichlet + * \brief 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. + */ + +igraph_error_t 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_ERRORF("Number of samples should be non-negative, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, n); + } + if (len < 2) { + IGRAPH_ERRORF("Dirichlet parameter vector too short, must " + "have at least two entries, got %" IGRAPH_PRId + ".", IGRAPH_EINVAL, len); + } + if (igraph_vector_min(alpha) <= 0) { + IGRAPH_ERRORF("Dirichlet concentration parameters must be positive, got %g.", + IGRAPH_EINVAL, igraph_vector_min(alpha)); + } + + 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 IGRAPH_SUCCESS; +} diff --git a/src/games/erdos_renyi.c b/src/games/erdos_renyi.c new file mode 100644 index 0000000..c0e0e34 --- /dev/null +++ b/src/games/erdos_renyi.c @@ -0,0 +1,451 @@ +/* -*- 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" + +#include "core/interruption.h" +#include "math/safe_intop.h" +#include "random/random_internal.h" + +/** + * \section about_games + * + * Games are random graph generators, i.e. they generate a different + * graph every time they are called. igraph includes many such generators. + * Some implement stochastic graph construction processes inspired by real-world + * mechanics, such as preferential attachment, while others are designed to + * produce graphs with certain used properties (e.g. fixed number of edges, + * fixed degrees, etc.) + */ + +/* This implementation is used only with very large vertex counts, above + * sqrt(MAX_EXACT_REAL) ~ 100 million, when the default implementation would + * fail due to overflow. While this version avoids overflow and uses less memory, + * it is also slower than the default implementation. */ +static igraph_error_t gnp_large( + igraph_t *graph, igraph_integer_t n, igraph_real_t p, + igraph_bool_t directed, igraph_bool_t loops, igraph_integer_t ecount_estimate +) { + + igraph_vector_int_t edges; + int iter = 0; + + /* Necessitated by floating point arithmetic used in the implementation. */ + if (n >= IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Number of vertices is too large.", IGRAPH_EOVERFLOW); + } + + if (ecount_estimate > IGRAPH_ECOUNT_MAX) { + ecount_estimate = IGRAPH_ECOUNT_MAX; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2*ecount_estimate)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < n; i++) { + igraph_integer_t j = directed ? 0 : i; + + while (true) { + igraph_real_t gap = RNG_GEOM(p); + + /* This formulation not only terminates the loop when necessary, + * but also protects against overflow when 'p' is very small + * and 'gap' becomes very large, perhaps larger than representable + * in an igraph_integer_t. */ + if (gap >= n - j) { + break; + } + + j += gap; + + if (loops || i != j) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + } + + j++; + + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } + RNG_END(); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_erdos_renyi_game_gnp + * \brief Generates a random (Erdős-Rényi) graph with fixed edge probabilities. + * + * In the G(n, p) Erdős-Rényi model, also known as the Gilbert model, + * or Bernoulli random graph, a graph with \p n vertices is generated such that + * every possible edge is included in the graph independently with probability + * \p p. This is equivalent to a maximum entropy random graph model model with + * a constraint on the \em expected edge count. Setting p = 1/2 + * generates all graphs on \p n vertices with the same probability. + * + * + * The expected mean degree of the graph is approximately p n; + * set p = k/n when a mean degree of approximately \c k is + * desired. More precisely, the expected mean degree is p(n-1) + * in (undirected or directed) graphs without self-loops, + * p(n+1) in undirected graphs with self-loops, and + * p n in directed graphs with self-loops. + * + * \param graph Pointer to an uninitialized graph object. + * \param n The number of vertices in the graph. + * \param p The probability of the existence of an edge in the graph. + * \param directed Logical, whether to generate a directed graph. + * \param loops Logical, whether to generate self-loops. + * \return Error code: + * \c IGRAPH_EINVAL: invalid \p n or \p p 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_erdos_renyi_game_gnm() to generate random graphs with + * a sharply fixed edge count; \ref igraph_chung_lu_game() and + * \ref igraph_static_fitness_game() to generate random graphs with a + * fixed expected degree sequence; \ref igraph_bipartite_game_gnm() for the + * bipartite version of this model; \ref igraph_barabasi_game() and + * \ref igraph_growing_random_game() for other commonly used random graph models. + * + * \example examples/simple/igraph_erdos_renyi_game_gnp.c + */ +igraph_error_t igraph_erdos_renyi_game_gnp( + igraph_t *graph, igraph_integer_t n, igraph_real_t p, + igraph_bool_t directed, igraph_bool_t loops +) { + /* This function uses doubles in its `s` vector, and for `maxedges` and `last`. + * This is because on a system with 32-bit ints, maxedges will be larger than + * IGRAPH_INTEGER_MAX and this will cause overflows when calculating `from` and `to` + * for tests on large graphs. + */ + igraph_integer_t no_of_nodes = n; + igraph_real_t no_of_nodes_real = (igraph_real_t) no_of_nodes; /* for divisions below */ + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t s = IGRAPH_VECTOR_NULL; + int iter = 0; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + } + if (p < 0.0 || p > 1.0) { + IGRAPH_ERROR("Invalid probability given.", IGRAPH_EINVAL); + } + + if (p == 0.0 || no_of_nodes == 0) { + IGRAPH_CHECK(igraph_empty(graph, n, directed)); + } else if (p == 1.0) { + IGRAPH_CHECK(igraph_full(graph, n, directed, loops)); + } else { + igraph_real_t maxedges = n, last; + igraph_integer_t ecount_estimate, ecount; + + 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_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &ecount_estimate)); + + if (maxedges > IGRAPH_MAX_EXACT_REAL) { + /* Use a slightly slower, but overflow-free implementation. */ + return gnp_large(graph, n, p, directed, loops, ecount_estimate); + } + + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_vector_reserve(&s, ecount_estimate)); + + RNG_BEGIN(); + + last = RNG_GEOM(p); + while (last < maxedges) { + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + last += RNG_GEOM(p); + last += 1; + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + + RNG_END(); + + ecount = igraph_vector_size(&s); + if (ecount > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2*ecount)); + + iter = 0; + if (directed && loops) { + for (igraph_integer_t i = 0; i < ecount; i++) { + igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); + igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } else if (directed && !loops) { + for (igraph_integer_t i = 0; i < ecount; i++) { + igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); + igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; + if (from == to) { + to = no_of_nodes - 1; + } + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } else if (!directed && loops) { + for (igraph_integer_t i = 0; i < ecount; i++) { + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2); + igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } else { /* !directed && !loops */ + for (igraph_integer_t i = 0; i < ecount; i++) { + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); + igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_erdos_renyi_game_gnm + * \brief Generates a random (Erdős-Rényi) graph with a fixed number of edges. + * + * In the G(n, m) Erdős-Rényi model, a graph with \p n vertices + * and \p m edges is generated uniformly at random. + * + * \param graph Pointer to an uninitialized graph object. + * \param n The number of vertices in the graph. + * \param m The number of edges in the graph. + * \param directed Logical, whether to generate a directed graph. + * \param loops Logical, whether to generate self-loops. + * \return Error code: + * \c IGRAPH_EINVAL: invalid \p n 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_erdos_renyi_game_gnp() to sample from the related + * G(n, p) model, which constrains the \em expected edge count; + * \ref igraph_degree_sequence_game() to constrain the degree sequence; + * \ref igraph_bipartite_game_gnm() for the bipartite version of this model; + * \ref igraph_barabasi_game() and \ref igraph_growing_random_game() for other + * commonly used random graph models. + * + * \example examples/simple/igraph_erdos_renyi_game_gnm.c + */ +igraph_error_t igraph_erdos_renyi_game_gnm( + igraph_t *graph, igraph_integer_t n, igraph_integer_t m, + igraph_bool_t directed, igraph_bool_t loops +) { + + /* This function uses doubles in its `s` vector, and for `maxedges` and `last`. + * This is because on a system with 32-bit ints, maxedges will be larger than + * IGRAPH_INTEGER_MAX and this will cause overflows when calculating `from` and `to` + * for tests on large graphs. This is also why we need a 'real' version of random_sample. + */ + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_edges = m; + igraph_real_t no_of_nodes_real = (igraph_real_t) no_of_nodes; /* for divisions below */ + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t s = IGRAPH_VECTOR_NULL; + int iter = 0; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + } + if (m < 0 || m > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Invalid number of edges.", IGRAPH_EINVAL); + } + + if (m == 0.0 || no_of_nodes == 0) { + IGRAPH_CHECK(igraph_empty(graph, n, directed)); + } else { + + igraph_integer_t i; + igraph_real_t 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("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); + } + + if (maxedges == no_of_edges) { + IGRAPH_CHECK(igraph_full(graph, n, directed, loops)); + } else { + + igraph_integer_t slen; + + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_random_sample_real(&s, 0, maxedges - 1, no_of_edges)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, igraph_vector_size(&s) * 2)); + + slen = igraph_vector_size(&s); + if (directed && loops) { + for (i = 0; i < slen; i++) { + igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); + igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } else if (directed && !loops) { + for (i = 0; i < slen; i++) { + igraph_integer_t from = floor(VECTOR(s)[i] / (no_of_nodes_real - 1)); + igraph_integer_t to = VECTOR(s)[i] - from * (no_of_nodes_real - 1); + if (from == to) { + to = no_of_nodes - 1; + } + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } else if (!directed && loops) { + for (i = 0; i < slen; i++) { + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2); + igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } else { /* !directed && !loops */ + for (i = 0; i < slen; i++) { + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); + igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_erdos_renyi_game + * \brief Generates a random (Erdős-Rényi) graph. + * + * This function is deprecated; use \ref igraph_erdos_renyi_game_gnm() or + * \ref igraph_erdos_renyi_game_gnp() instead. + * + * \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(), + * \ref igraph_erdos_renyi_game_gnm(), \ref igraph_erdos_renyi_game_gnp() + */ +igraph_error_t 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) { + + if (type == IGRAPH_ERDOS_RENYI_GNP) { + return igraph_erdos_renyi_game_gnp(graph, n, p_or_m, directed, loops); + } else if (type == IGRAPH_ERDOS_RENYI_GNM) { + return igraph_erdos_renyi_game_gnm(graph, n, (igraph_integer_t) p_or_m, directed, loops); + } else { + IGRAPH_ERROR("Invalid type", IGRAPH_EINVAL); + } +} diff --git a/src/games/establishment.c b/src/games/establishment.c new file mode 100644 index 0000000..9f8cf4e --- /dev/null +++ b/src/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. + */ +igraph_error_t 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_int_t *node_type_vec) { + igraph_integer_t i, j; + igraph_vector_int_t edges; + igraph_vector_t cumdist; + igraph_vector_int_t potneis; + igraph_real_t maxcum; + igraph_vector_int_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 (isnan(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); /* matrix size is at least 1x1, safe to call minmax */ + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (isnan(lo) || isnan(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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); + IGRAPH_VECTOR_INT_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_int_resize(nodetypes, nodes)); + } else { + nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); + if (! nodetypes) { + IGRAPH_ERROR("Insufficient memory for establishment_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, nodetypes); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); + } + + RNG_BEGIN(); + + for (i = 0; i < nodes; i++) { + igraph_real_t uni = RNG_UNIF(0, maxcum); + igraph_integer_t type; + igraph_vector_binsearch(&cumdist, uni, &type); + VECTOR(*nodetypes)[i] = type - 1; + } + + for (i = k; i < nodes; i++) { + igraph_integer_t type1 = VECTOR(*nodetypes)[i]; + igraph_random_sample(&potneis, 0, i - 1, k); + for (j = 0; j < k; j++) { + igraph_integer_t type2 = VECTOR(*nodetypes)[VECTOR(potneis)[j]]; + if (RNG_UNIF01() < MATRIX(*pref_matrix, type1, type2)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, VECTOR(potneis)[j])); + } + } + } + + RNG_END(); + + if (! node_type_vec) { + igraph_vector_int_destroy(nodetypes); + IGRAPH_FREE(nodetypes); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_int_destroy(&potneis); + igraph_vector_destroy(&cumdist); + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/forestfire.c b/src/games/forestfire.c new file mode 100644 index 0000000..a5cb2ae --- /dev/null +++ b/src/games/forestfire.c @@ -0,0 +1,262 @@ +/* -*- 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_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_progress.h" + +#include "core/interruption.h" + +typedef struct igraph_i_forest_fire_data_t { + igraph_vector_int_t *inneis; + igraph_vector_int_t *outneis; + igraph_integer_t no_of_nodes; +} igraph_i_forest_fire_data_t; + + +static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { + for (igraph_integer_t i = 0; i < data->no_of_nodes; i++) { + igraph_vector_int_destroy(data->inneis + i); + igraph_vector_int_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- and out-degree distributions. + * \ili Community structure. + * \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. + */ + +igraph_error_t 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_int_t visited; + igraph_integer_t no_of_nodes = nodes, actnode, i; + igraph_vector_int_t edges; + igraph_vector_int_t *inneis, *outneis; + igraph_i_forest_fire_data_t data; + igraph_dqueue_int_t neiq; + igraph_integer_t 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 IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + inneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_int_t); + IGRAPH_CHECK_OOM(inneis, "Insufficient memory for forest fire model."); + IGRAPH_FINALLY(igraph_free, inneis); + + outneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_int_t); + IGRAPH_CHECK_OOM(outneis, "Insufficient memory for forest fire model."); + 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_int_init(inneis + i, 0)); + IGRAPH_CHECK(igraph_vector_int_init(outneis + i, 0)); + } + + IGRAPH_CHECK(igraph_vector_int_init(&visited, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &visited); + IGRAPH_DQUEUE_INT_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_int_push(&neiq, (nei))); \ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, actnode)); \ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, (nei))); \ + IGRAPH_CHECK(igraph_vector_int_push_back(outneis+actnode, (nei))); \ + IGRAPH_CHECK(igraph_vector_int_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++) { + igraph_integer_t a = RNG_INTEGER(0, actnode - 1); + ADD_EDGE_TO(a); + } + + while (!igraph_dqueue_int_empty(&neiq)) { + igraph_integer_t actamb = igraph_dqueue_int_pop(&neiq); + igraph_vector_int_t *outv = outneis + actamb; + igraph_vector_int_t *inv = inneis + actamb; + igraph_integer_t no_in = igraph_vector_int_size(inv); + igraph_integer_t no_out = igraph_vector_int_size(outv); + igraph_integer_t neis_out = RNG_GEOM(param_geom_out); + igraph_integer_t neis_in = RNG_GEOM(param_geom_in); + /* outgoing neighbors */ + if (neis_out >= no_out) { + for (i = 0; i < no_out; i++) { + igraph_integer_t nei = VECTOR(*outv)[i]; + ADD_EDGE_TO(nei); + } + } else { + igraph_integer_t oleft = no_out; + for (i = 0; i < neis_out && oleft > 0; ) { + igraph_integer_t which = RNG_INTEGER(0, oleft - 1); + igraph_integer_t nei = 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++) { + igraph_integer_t nei = VECTOR(*inv)[i]; + ADD_EDGE_TO(nei); + } + } else { + igraph_integer_t ileft = no_in; + for (i = 0; i < neis_in && ileft > 0; ) { + igraph_integer_t which = RNG_INTEGER(0, ileft - 1); + igraph_integer_t nei = 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_int_destroy(&neiq); + igraph_vector_int_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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/grg.c b/src/games/grg.c new file mode 100644 index 0000000..dbd6348 --- /dev/null +++ b/src/games/grg.c @@ -0,0 +1,180 @@ +/* -*- 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 strictly 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 + */ +igraph_error_t 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_integer_t i; + igraph_vector_t myx, myy, *xx = &myx, *yy = &myy; + igraph_vector_int_t edges; + igraph_real_t r2; + + if (nodes < 0) { + IGRAPH_ERROR("Number of vertices must not be negative.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nodes)); + + /* since we only connect nodes strictly closer than radius, + * radius < 0 is equivalent to radius == 0 */ + if (radius < 0) { + radius = 0; + } + r2 = radius*radius; + + 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]; + igraph_integer_t j = i + 1; + igraph_real_t dx, dy; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* dx is always positive due to xx being sorted */ + while ( j < nodes && (dx = VECTOR(*xx)[j] - xx1) < radius) { + dy = VECTOR(*yy)[j] - yy1; + if (dx * dx + dy * dy < r2) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_integer_t j = i + 1; + igraph_real_t dx, dy; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* dx is always positive due to xx being sorted */ + 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_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/growing_random.c b/src/games/growing_random.c new file mode 100644 index 0000000..79e5231 --- /dev/null +++ b/src/games/growing_random.c @@ -0,0 +1,111 @@ +/* -*- 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 "math/safe_intop.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. + */ +igraph_error_t igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, + igraph_integer_t m, igraph_bool_t directed, + igraph_bool_t citation) { + + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_neighbors = m; + igraph_integer_t no_of_edges; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + + igraph_integer_t resp = 0; + + 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); + } + + if (no_of_nodes == 0) { + no_of_edges = 0; + } else { + IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Number of edges overflows.", IGRAPH_EOVERFLOW); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + + RNG_BEGIN(); + + for (igraph_integer_t i = 1; i < no_of_nodes; i++) { + for (igraph_integer_t j = 0; j < no_of_neighbors; j++) { + if (citation) { + igraph_integer_t to = RNG_INTEGER(0, i - 1); + VECTOR(edges)[resp++] = i; + VECTOR(edges)[resp++] = to; + } else { + igraph_integer_t from = RNG_INTEGER(0, i); + igraph_integer_t 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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/islands.c b/src/games/islands.c new file mode 100644 index 0000000..edececa --- /dev/null +++ b/src/games/islands.c @@ -0,0 +1,176 @@ +/* -*- 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 "math/safe_intop.h" +#include "random/random_internal.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. + * + * All islands are of the same size. Within an island, each edge is generated + * with the same probability. A fixed number of additional edges are then + * generated for each unordered pair of islands to connect them. The generated + * graph is guaranteed to be simple. + * + * \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 within islands. + * \param n_inter The number of edges to create between two islands. It may be + * larger than \p islands_size squared, but in this case it is assumed + * to be \p islands_size squared. + * + * \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. + * + */ +igraph_error_t 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_int_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t s = IGRAPH_VECTOR_NULL; + igraph_integer_t number_of_nodes; + igraph_real_t max_possible_edges_per_island; + igraph_real_t avg_edges_per_island; + igraph_integer_t number_of_inter_island_edges; + igraph_integer_t start_index_of_island, start_index_of_other_island; + igraph_integer_t i, j, is, from, to; + igraph_real_t last; + igraph_integer_t island_ecount; + igraph_real_t nr_edges_reserved; + + 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); + } + + number_of_inter_island_edges = islands_size * islands_size; + if (n_inter > number_of_inter_island_edges) { + IGRAPH_ERRORF( + "Too many edges requested between islands, maximum possible " + "is %" IGRAPH_PRId ", got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, number_of_inter_island_edges, n_inter + ); + } + + /* how much memory ? */ + number_of_nodes = islands_n * islands_size; + max_possible_edges_per_island = ((igraph_real_t)islands_size * ((igraph_real_t)islands_size - 1.0)) / 2.0; + avg_edges_per_island = islands_pin * max_possible_edges_per_island; + number_of_inter_island_edges = n_inter * (islands_n * (islands_n - 1)) / 2; + + nr_edges_reserved = 1.1 * avg_edges_per_island * islands_n + number_of_inter_island_edges; + /* The cast of ECOUNT_MAX to double could change its value, which means in theory the size of + the edges vector could still overflow, but only for very rare cases. */ + if (nr_edges_reserved > (double) (IGRAPH_ECOUNT_MAX ) || nr_edges_reserved > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nr_edges_reserved * 2)); + + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_vector_reserve(&s, 1.1 * avg_edges_per_island)); + + 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, both inclusive */ + start_index_of_island = islands_size * is; + + igraph_vector_clear(&s); + + last = RNG_GEOM(islands_pin); + while (last < max_possible_edges_per_island) { /* avg_edges_per_island */ + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + last += RNG_GEOM(islands_pin); + last += 1; + } + + island_ecount = igraph_vector_size(&s); + for (i = 0; i < island_ecount; i++) { + to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2.0); + from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2.0; + to += start_index_of_island; + from += start_index_of_island; + + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + } + + /* create the links with other islands */ + island_ecount = islands_size * islands_size; + number_of_inter_island_edges = n_inter; + for (i = is + 1; i < islands_n; i++) { /* for each other island (not the previous ones) */ + IGRAPH_CHECK(igraph_random_sample_real(&s, 0, island_ecount - 1, n_inter)); + + start_index_of_other_island = i * islands_size; + for (j = 0; j < n_inter; j++) { /* for each link between islands */ + from = VECTOR(s)[j] / islands_size; + to = VECTOR(s)[j] - from * islands_size; + from += start_index_of_island; + to += start_index_of_other_island; + + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + } + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + + RNG_END(); + + /* actually fill the graph object */ + IGRAPH_CHECK(igraph_create(graph, &edges, number_of_nodes, 0)); + + /* clean remaining things */ + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/k_regular.c b/src/games/k_regular.c new file mode 100644 index 0000000..0cebc21 --- /dev/null +++ b/src/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" + +/** + * \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 IGRAPH_DEGSEQ_CONFIGURATION or the \c IGRAPH_DEGSEQ_FAST_SIMPLE + * 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. + */ +igraph_error_t 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_int_t degseq; + igraph_degseq_t mode = multiple ? IGRAPH_DEGSEQ_CONFIGURATION : IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE; + + /* 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, + * and it produces only connected graphs. + * Actually, that's why we have added FAST_HEUR_SIMPLE. */ + + 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_INT_INIT_FINALLY(°seq, no_of_nodes); + igraph_vector_int_fill(°seq, k); + IGRAPH_CHECK(igraph_degree_sequence_game(graph, °seq, directed ? °seq : 0, mode)); + + igraph_vector_int_destroy(°seq); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/preference.c b/src/games/preference.c new file mode 100644 index 0000000..1a537ef --- /dev/null +++ b/src/games/preference.c @@ -0,0 +1,621 @@ +/* -*- 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 "igraph_vector_list.h" + +#include "core/interruption.h" +#include "math/safe_intop.h" + +#include /* for sqrt and floor */ + +/** + * \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() + */ + +igraph_error_t 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_int_t *node_type_vec, + igraph_bool_t directed, + igraph_bool_t loops) { + + igraph_integer_t i, j, no_reserved_edges; + igraph_vector_int_t edges; + igraph_vector_t s; + igraph_vector_int_t* nodetypes; + igraph_vector_int_list_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 (isnan(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); /* matrix size is at least 1x1, safe to call minmax */ + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (isnan(lo) || isnan(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_int_resize(node_type_vec, nodes)); + nodetypes = node_type_vec; + } else { + nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(nodetypes, "Insufficient memory for preference_game."); + IGRAPH_FINALLY(igraph_free, nodetypes); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); + } + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_type, types); + + 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++) { + igraph_integer_t type1; + igraph_real_t uni1 = RNG_UNIF(0, maxcum); + igraph_vector_binsearch(&cumdist, uni1, &type1); + VECTOR(*nodetypes)[i] = type1 - 1; + IGRAPH_CHECK(igraph_vector_int_push_back( + igraph_vector_int_list_get_ptr(&vids_by_type, type1 - 1), i + )); + } + + igraph_vector_destroy(&cumdist); + IGRAPH_FINALLY_CLEAN(1); + + } else { + igraph_integer_t an = 0; + if (type_dist) { + for (i = 0; i < types; i++) { + igraph_integer_t no = VECTOR(*type_dist)[i]; + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&vids_by_type, i); + for (j = 0; j < no && an < nodes; j++) { + VECTOR(*nodetypes)[an] = i; + IGRAPH_CHECK(igraph_vector_int_push_back(v, an)); + an++; + } + } + } else { + igraph_integer_t size_of_one_group = nodes / types; + igraph_integer_t num_groups_with_one_extra_node = nodes - size_of_one_group * types; + for (i = 0; i < types; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&vids_by_type, i); + for (j = 0; j < size_of_one_group; j++) { + VECTOR(*nodetypes)[an] = i; + IGRAPH_CHECK(igraph_vector_int_push_back(v, an)); + an++; + } + if (i < num_groups_with_one_extra_node) { + VECTOR(*nodetypes)[an] = i; + IGRAPH_CHECK(igraph_vector_int_push_back(v, an)); + an++; + } + } + } + } + + IGRAPH_VECTOR_INT_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 */ + igraph_integer_t k, l, l_x2; + igraph_real_t p, last; + igraph_vector_int_t *v1, *v2; + igraph_integer_t v1_size, v2_size; + + IGRAPH_ALLOW_INTERRUPTION(); + + v1 = igraph_vector_int_list_get_ptr(&vids_by_type, i); + v2 = igraph_vector_int_list_get_ptr(&vids_by_type, j); + v1_size = igraph_vector_int_size(v1); + v2_size = igraph_vector_int_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 = ((igraph_real_t) v1_size) * v2_size; + } else { + if (directed && loops) { + maxedges = ((igraph_real_t) v1_size) * v1_size; + } else if (directed && !loops) { + maxedges = ((igraph_real_t) v1_size) * (v1_size - 1); + } else if (!directed && loops) { + maxedges = ((igraph_real_t) v1_size) * (v1_size + 1) / 2; + } else { + maxedges = ((igraph_real_t) v1_size) * (v1_size - 1) / 2; + } + } + + if (maxedges > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } + + IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &no_reserved_edges)); + IGRAPH_CHECK(igraph_vector_reserve(&s, no_reserved_edges)); + + 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_SAFE_MULT(l, 2, &l_x2); + IGRAPH_SAFE_ADD(igraph_vector_int_size(&edges), l_x2, &no_reserved_edges); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_reserved_edges)); + + if (i != j) { + /* Generating the subgraph between vertices of type i and j */ + for (k = 0; k < l; k++) { + igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); + igraph_integer_t from = (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v2)[to]); + } + } else { + /* Generating the subgraph among vertices of type i */ + if (directed && loops) { + for (k = 0; k < l; k++) { + igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); + igraph_integer_t from = (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); + } + } else if (directed && !loops) { + for (k = 0; k < l; k++) { + igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); + igraph_integer_t from = (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + if (from == to) { + to = v1_size - 1; + } + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); + } + } else if (!directed && loops) { + for (k = 0; k < l; k++) { + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[k] + 1) - 1) / 2); + igraph_integer_t from = (VECTOR(s)[k] - (((igraph_real_t)to) * (to + 1)) / 2); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); + } + } else { + for (k = 0; k < l; k++) { + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[k] + 1) + 1) / 2); + igraph_integer_t from = (VECTOR(s)[k] - (((igraph_real_t)to) * (to - 1)) / 2); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); + } + } + } + } + } + + RNG_END(); + + igraph_vector_destroy(&s); + igraph_vector_int_list_destroy(&vids_by_type); + IGRAPH_FINALLY_CLEAN(2); + + if (node_type_vec == 0) { + igraph_vector_int_destroy(nodetypes); + IGRAPH_FREE(nodetypes); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_int_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 no_out_types The number of vertex out-types. + * \param no_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() + */ + +igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, + igraph_integer_t no_out_types, + igraph_integer_t no_in_types, + const igraph_matrix_t *type_dist_matrix, + const igraph_matrix_t *pref_matrix, + igraph_vector_int_t *node_type_out_vec, + igraph_vector_int_t *node_type_in_vec, + igraph_bool_t loops) { + + igraph_integer_t i, j, k, no_reserved_edges; + igraph_vector_int_t edges; + igraph_vector_t s; + igraph_vector_t cumdist; + igraph_vector_int_t intersect; + igraph_vector_int_t *nodetypes_in; + igraph_vector_int_t *nodetypes_out; + igraph_vector_int_list_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 (no_in_types < 1) { + IGRAPH_ERROR("The number of vertex in-types must be at least 1.", IGRAPH_EINVAL); + } + + if (no_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) != no_out_types || + igraph_matrix_ncol(type_dist_matrix) != no_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 (isnan(lo)) { + IGRAPH_ERROR("The type distribution matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + if (igraph_matrix_nrow(pref_matrix) != no_out_types || + igraph_matrix_ncol(pref_matrix) != no_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); /* matrix size is at least 1x1, safe to call minmax */ + + if (lo < 0 || hi > 1) { + IGRAPH_ERROR("The preference matrix must contain probabilities in [0, 1].", IGRAPH_EINVAL); + } + if (isnan(lo) || isnan(hi)) { + IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&cumdist, no_in_types * no_out_types + 1); + + if (node_type_in_vec) { + nodetypes_in = node_type_in_vec; + IGRAPH_CHECK(igraph_vector_int_resize(nodetypes_in, nodes)); + } else { + nodetypes_in = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(nodetypes_in, "Insufficient memory for asymmetric preference game."); + IGRAPH_FINALLY(igraph_free, &nodetypes_in); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes_in, nodes); + } + + if (node_type_out_vec) { + nodetypes_out = node_type_out_vec; + IGRAPH_CHECK(igraph_vector_int_resize(nodetypes_out, nodes)); + } else { + nodetypes_out = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(nodetypes_out, "Insufficient memory for asymmetric preference game."); + IGRAPH_FINALLY(igraph_free, &nodetypes_out); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes_out, nodes); + } + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_intype, no_in_types); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_outtype, no_out_types); + + VECTOR(cumdist)[0] = 0; + k = 0; + if (type_dist_matrix) { + for (j = 0; j < no_in_types; j++) { + for (i = 0; i < no_out_types; i++) { + VECTOR(cumdist)[k + 1] = VECTOR(cumdist)[k] + MATRIX(*type_dist_matrix, i, j); + k++; + } + } + } else { + for (i = 0; i < no_out_types * no_in_types; i++) { + VECTOR(cumdist)[i + 1] = i + 1; + } + } + maxcum = igraph_vector_tail(&cumdist); + + RNG_BEGIN(); + + for (i = 0; i < nodes; i++) { + igraph_integer_t in_type, out_type; + igraph_real_t uni1 = RNG_UNIF(0, maxcum); + igraph_vector_binsearch(&cumdist, uni1, &in_type); + out_type = (in_type - 1) % no_out_types; + in_type = (in_type - 1) / no_out_types; + VECTOR(*nodetypes_in)[i] = in_type; + VECTOR(*nodetypes_out)[i] = out_type; + IGRAPH_CHECK(igraph_vector_int_push_back( + igraph_vector_int_list_get_ptr(&vids_by_intype, in_type), i + )); + IGRAPH_CHECK(igraph_vector_int_push_back( + igraph_vector_int_list_get_ptr(&vids_by_outtype, out_type), i + )); + } + + igraph_vector_destroy(&cumdist); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&intersect, 0); + for (i = 0; i < no_out_types; i++) { + for (j = 0; j < no_in_types; j++) { + igraph_integer_t kk, l, l_x2; + igraph_integer_t c = 0; + igraph_real_t p, last; + igraph_vector_int_t *v1, *v2; + igraph_integer_t v1_size, v2_size; + + IGRAPH_ALLOW_INTERRUPTION(); + + v1 = igraph_vector_int_list_get_ptr(&vids_by_outtype, i); + v2 = igraph_vector_int_list_get_ptr(&vids_by_intype, j); + v1_size = igraph_vector_int_size(v1); + v2_size = igraph_vector_int_size(v2); + + maxedges = ((igraph_real_t) v1_size) * v2_size; + + if (maxedges > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } + + if (!loops) { + IGRAPH_CHECK(igraph_vector_int_intersect_sorted(v1, v2, &intersect)); + c = igraph_vector_int_size(&intersect); + maxedges -= c; + } + + p = MATRIX(*pref_matrix, i, j); + igraph_vector_clear(&s); + + IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &no_reserved_edges)); + IGRAPH_CHECK(igraph_vector_reserve(&s, no_reserved_edges)); + + 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_SAFE_MULT(l, 2, &l_x2); + IGRAPH_SAFE_ADD(igraph_vector_int_size(&edges), l_x2, &no_reserved_edges); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_reserved_edges)); + + + if (!loops && c > 0) { + for (kk = 0; kk < l; kk++) { + igraph_integer_t to = floor(VECTOR(s)[kk] / v1_size); + igraph_integer_t from = (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_int_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_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v2)[to]); + } + } else { + for (kk = 0; kk < l; kk++) { + igraph_integer_t to = floor(VECTOR(s)[kk] / v1_size); + igraph_integer_t from = (VECTOR(s)[kk] - ((igraph_real_t)to) * v1_size); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v2)[to]); + } + } + } + } + + RNG_END(); + + igraph_vector_destroy(&s); + igraph_vector_int_destroy(&intersect); + igraph_vector_int_list_destroy(&vids_by_intype); + igraph_vector_int_list_destroy(&vids_by_outtype); + IGRAPH_FINALLY_CLEAN(4); + + if (node_type_out_vec == 0) { + igraph_vector_int_destroy(nodetypes_out); + IGRAPH_FREE(nodetypes_out); + IGRAPH_FINALLY_CLEAN(2); + } + + if (node_type_in_vec == 0) { + igraph_vector_int_destroy(nodetypes_in); + IGRAPH_FREE(nodetypes_in); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, 1)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/recent_degree.c b/src/games/recent_degree.c new file mode 100644 index 0000000..c4d101a --- /dev/null +++ b/src/games/recent_degree.c @@ -0,0 +1,388 @@ +/* -*- 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" + +#include "math/safe_intop.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. + * + */ +igraph_error_t 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_int_t *outseq, + igraph_bool_t outpref, + igraph_real_t zero_appeal, + igraph_bool_t directed) { + + igraph_integer_t no_of_nodes = nodes; + igraph_integer_t no_of_neighbors = 0; + igraph_integer_t no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t i, j; + igraph_psumtree_t sumtree; + igraph_integer_t edgeptr = 0; + igraph_vector_t degree; + igraph_dqueue_int_t history; + igraph_bool_t have_outseq = outseq && igraph_vector_int_size(outseq) > 0; + + if (no_of_nodes < 0) { + IGRAPH_ERRORF("Number of vertices cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_nodes); + } + if (have_outseq && igraph_vector_int_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%" IGRAPH_PRId ") does not equal the number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(outseq), no_of_nodes); + } + if (!have_outseq && m < 0) { + IGRAPH_ERRORF("Number 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; + IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); + } else { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); + no_of_edges -= VECTOR(*outseq)[0]; + } + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + + IGRAPH_VECTOR_INT_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_int_init(&history, + 1.5 * time_window * no_of_edges / no_of_nodes + 10)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &history); + + RNG_BEGIN(); + + /* first node */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_appeal)); + IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); + + /* and the rest */ + for (i = 1; i < no_of_nodes; i++) { + igraph_real_t sum; + igraph_integer_t to; + if (have_outseq) { + no_of_neighbors = VECTOR(*outseq)[i]; + } + + if (i >= time_window) { + while ((j = igraph_dqueue_int_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++) { + if (sum == 0) { + /* If none of the so-far added nodes have positive weight, + * we choose one uniformly to connect to. */ + to = RNG_INTEGER(0, i-1); + } else { + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + } + VECTOR(degree)[to]++; + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = to; + IGRAPH_CHECK(igraph_dqueue_int_push(&history, to)); + } + IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); + + /* update probabilities */ + for (j = 0; j < no_of_neighbors; j++) { + igraph_integer_t nn = 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_int_destroy(&history); + igraph_psumtree_destroy(&sumtree); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, + igraph_integer_t nodes, + igraph_integer_t m, + const igraph_vector_int_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) { + + igraph_integer_t no_of_nodes = nodes; + igraph_integer_t no_of_neighbors; + igraph_integer_t binwidth; + igraph_integer_t no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; + igraph_psumtree_t sumtree; + igraph_integer_t edgeptr = 0; + igraph_vector_t degree; + igraph_dqueue_int_t history; + igraph_bool_t have_outseq = outseq && igraph_vector_int_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 %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_nodes); + } + if (have_outseq && igraph_vector_int_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%" IGRAPH_PRId ") does not equal the number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_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; + IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); + } else { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); + no_of_edges -= VECTOR(*outseq)[0]; + } + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + + binwidth = nodes / aging_bins + 1; + + IGRAPH_VECTOR_INT_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_int_init(&history, + 1.5 * time_window * no_of_edges / no_of_nodes + 10)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &history); + + RNG_BEGIN(); + + /* first node */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_appeal)); + IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); + + /* and the rest */ + for (i = 1; i < no_of_nodes; i++) { + igraph_real_t sum; + igraph_integer_t to; + + if (have_outseq) { + no_of_neighbors = VECTOR(*outseq)[i]; + } + + if (i >= time_window) { + while ((j = igraph_dqueue_int_pop(&history)) != -1) { + igraph_integer_t 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++) { + if (sum == 0) { + /* If none of the so-far added nodes have positive weight, + * we choose one uniformly to connect to. */ + to = RNG_INTEGER(0, i-1); + } else { + igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); + } + VECTOR(degree)[to]++; + VECTOR(edges)[edgeptr++] = i; + VECTOR(edges)[edgeptr++] = to; + IGRAPH_CHECK(igraph_dqueue_int_push(&history, to)); + } + IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); + + /* update probabilities */ + for (j = 0; j < no_of_neighbors; j++) { + igraph_integer_t n = VECTOR(edges)[edgeptr - 2 * j - 1]; + igraph_integer_t 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++) { + igraph_integer_t shnode = i - binwidth * k; + igraph_integer_t deg = VECTOR(degree)[shnode]; + igraph_integer_t 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_int_destroy(&history); + igraph_vector_destroy(°ree); + igraph_psumtree_destroy(&sumtree); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/sbm.c b/src/games/sbm.c new file mode 100644 index 0000000..12cf663 --- /dev/null +++ b/src/games/sbm.c @@ -0,0 +1,643 @@ +/* + IGraph library. + Copyright (C) 2003-2023 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_games.h" + +#include "igraph_constructors.h" +#include "igraph_matrix.h" +#include "igraph_random.h" +#include "igraph_vector.h" + +#include "core/interruption.h" +#include "math/safe_intop.h" + +#include /* for DBL_EPSILON */ +#include /* for sqrt and floor */ + +/** + * \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_gnp() for a simple Bernoulli graph. + * + */ + +igraph_error_t 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) { + +#define IGRAPH_CHECK_MAXEDGES() \ + do {if (maxedges > IGRAPH_MAX_EXACT_REAL) { \ + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); \ + }} while (0) + + igraph_integer_t no_blocks = igraph_matrix_nrow(pref_matrix); + igraph_integer_t from, to, fromoff = 0; + igraph_real_t minp, maxp; + igraph_vector_int_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 (%" IGRAPH_PRId ") does not agree with " + "preference matrix size (%" IGRAPH_PRId ").", 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_INT_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + for (from = 0; from < no_blocks; from++) { + igraph_integer_t fromsize = VECTOR(*block_sizes)[from]; + igraph_integer_t start = directed ? 0 : from; + igraph_integer_t i, tooff = 0; + + IGRAPH_ALLOW_INTERRUPTION(); + + for (i = 0; i < start; i++) { + tooff += VECTOR(*block_sizes)[i]; + } + for (to = start; to < no_blocks; to++) { + igraph_integer_t tosize = VECTOR(*block_sizes)[to]; + igraph_real_t prob = MATRIX(*pref_matrix, from, to); + igraph_real_t maxedges; + igraph_real_t last = RNG_GEOM(prob); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + igraph_integer_t vfrom, vto; + + if (directed && loops) { + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (directed && !loops && from != to) { + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (directed && !loops && from == to) { + maxedges = ((igraph_real_t) fromsize) * (fromsize - 1.0); + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + if (vfrom == vto) { + vto = fromsize - 1; + } + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (!directed && loops && from != to) { + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (!directed && loops && from == to) { + maxedges = ((igraph_real_t) fromsize) * (fromsize + 1.0) / 2.0; + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + vto = floor((sqrt(8 * last + 1) - 1) / 2); + vfrom = last - (((igraph_real_t) vto) * (vto + 1.0)) / 2.0; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else if (!directed && !loops && from != to) { + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } else { /*!directed && !loops && from==to */ + maxedges = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 2.0; + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + vto = floor((sqrt(8 * last + 1) + 1) / 2); + vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); + last += RNG_GEOM(prob); + last += 1; + } + } + + tooff += tosize; + } + fromoff += fromsize; + } + + RNG_END(); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +#undef IGRAPH_CHECK_MAXEDGES +} + +/** + * \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 + * \p 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. + */ + +igraph_error_t 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) { + +#define IGRAPH_CHECK_MAXEDGES() \ + do {if (maxedges > IGRAPH_MAX_EXACT_REAL) { \ + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); \ + }} while (0) + igraph_integer_t b, i, k = igraph_vector_size(rho); + igraph_vector_t csizes; + igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON); + igraph_integer_t no_blocks = n / m; + igraph_vector_int_t edges; + igraph_integer_t 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 (n % 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_INT_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + /* Block models first */ + + for (b = 0; b < no_blocks; b++) { + igraph_integer_t from, to, fromoff = 0; + + for (from = 0; from < k; from++) { + igraph_integer_t fromsize = VECTOR(csizes)[from]; + igraph_integer_t i, tooff = 0; + for (i = 0; i < from; i++) { + tooff += VECTOR(csizes)[i]; + } + for (to = from; to < k; to++) { + igraph_integer_t tosize = VECTOR(csizes)[to]; + igraph_real_t prob = MATRIX(*C, from, to); + igraph_real_t maxedges; + igraph_real_t last = RNG_GEOM(prob); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + if (from != to) { + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + igraph_integer_t vto = floor(last / fromsize); + igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); + last += RNG_GEOM(prob); + last += 1; + } + } else { /* from==to */ + maxedges = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 2.0; + IGRAPH_CHECK_MAXEDGES(); + while (last < maxedges) { + igraph_integer_t vto = floor((sqrt(8 * last + 1) + 1) / 2); + igraph_integer_t vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_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) { + igraph_integer_t fromoff = 0, tooff = m; + for (b = 0; b < no_blocks; b++) { + igraph_integer_t fromsize = m; + igraph_integer_t tosize = n - tooff; + igraph_integer_t from, to; + for (from = 0; from < fromsize; from++) { + for (to = 0; to < tosize; to++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + to)); + } + } + fromoff += m; + tooff += m; + } + } else if (p > 0) { + igraph_integer_t fromoff = 0, tooff = m; + for (b = 0; b < no_blocks; b++) { + igraph_integer_t fromsize = m; + igraph_integer_t tosize = n - tooff; + igraph_real_t maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); + igraph_real_t last = RNG_GEOM(p); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + while (last < maxedges) { + igraph_integer_t vto = floor(last / fromsize); + igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + vto)); + last += RNG_GEOM(p); + last += 1; + } + + fromoff += m; + tooff += m; + } + } + + RNG_END(); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, /*directed=*/ 0)); + + igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&csizes); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +#undef IGRAPH_CHECK_MAXEDGES +} + +/** + * \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, specifying 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. + */ + +igraph_error_t igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, + const igraph_vector_int_t *mlist, + const igraph_vector_list_t *rholist, + const igraph_matrix_list_t *Clist, + igraph_real_t p) { + + igraph_integer_t i, no_blocks = igraph_vector_list_size(rholist); + igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON); + igraph_vector_int_t edges; + igraph_vector_t csizes; + igraph_integer_t 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_matrix_list_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 = igraph_vector_list_get_ptr(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 = igraph_matrix_list_get_ptr(Clist, i); + if (igraph_matrix_min(C) < 0 || igraph_matrix_max(C) > 1) { + IGRAPH_ERROR("Bernoulli rates must be between zero and one for HSBM.", + IGRAPH_EINVAL); + } + if (!igraph_matrix_is_symmetric(C)) { + IGRAPH_ERROR("Bernoulli rate matrices must be symmetric.", IGRAPH_EINVAL); + } + } + /* Check that C and rho sizes match */ + for (i = 0; i < no_blocks; i++) { + const igraph_vector_t *rho = igraph_vector_list_get_ptr(rholist, i); + const igraph_matrix_t *C = igraph_matrix_list_get_ptr(Clist, i); + igraph_integer_t k = igraph_vector_size(rho); + if (igraph_matrix_nrow(C) != k || igraph_matrix_ncol(C) != k) { + IGRAPH_ERROR("All Bernoulli rate matrix 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 = igraph_vector_list_get_ptr(rholist, i); + igraph_real_t m = VECTOR(*mlist)[i]; + igraph_integer_t 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_INT_INIT_FINALLY(&edges, 0); + + RNG_BEGIN(); + + /* Block models first */ + + for (b = 0; b < no_blocks; b++) { + igraph_integer_t from, to, fromoff = 0; + const igraph_vector_t *rho = igraph_vector_list_get_ptr(rholist, b); + const igraph_matrix_t *C = igraph_matrix_list_get_ptr(Clist, b); + igraph_integer_t m = VECTOR(*mlist)[b]; + igraph_integer_t k = igraph_vector_size(rho); + + IGRAPH_CHECK(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++) { + igraph_integer_t fromsize = VECTOR(csizes)[from]; + igraph_integer_t i, tooff = 0; + for (i = 0; i < from; i++) { + tooff += VECTOR(csizes)[i]; + } + for (to = from; to < k; to++) { + igraph_integer_t tosize = VECTOR(csizes)[to]; + igraph_real_t prob = MATRIX(*C, from, to); + igraph_real_t maxedges; + igraph_real_t last = RNG_GEOM(prob); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + if (from != to) { + maxedges = ((igraph_real_t) fromsize) * tosize; + while (last < maxedges) { + igraph_integer_t vto = floor(last / fromsize); + igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); + last += RNG_GEOM(prob); + last += 1; + } + } else { /* from==to */ + maxedges = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 2.0; + while (last < maxedges) { + igraph_integer_t vto = floor((sqrt(8 * last + 1) + 1) / 2); + igraph_integer_t vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_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) { + igraph_integer_t fromoff = 0, tooff = VECTOR(*mlist)[0]; + for (b = 0; b < no_blocks; b++) { + igraph_integer_t fromsize = VECTOR(*mlist)[b]; + igraph_integer_t tosize = n - tooff; + igraph_integer_t from, to; + for (from = 0; from < fromsize; from++) { + for (to = 0; to < tosize; to++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + to)); + } + } + fromoff += fromsize; + if (b + 1 < no_blocks) { + tooff += VECTOR(*mlist)[b + 1]; + } + } + } else if (p > 0) { + igraph_integer_t fromoff = 0, tooff = VECTOR(*mlist)[0]; + for (b = 0; b < no_blocks; b++) { + igraph_integer_t fromsize = VECTOR(*mlist)[b]; + igraph_integer_t tosize = n - tooff; + igraph_real_t maxedges = ((igraph_real_t) fromsize) * tosize; + igraph_real_t last = RNG_GEOM(p); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + while (last < maxedges) { + igraph_integer_t vto = floor(last / fromsize); + igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_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_CHECK(igraph_create(graph, &edges, n, /*directed=*/ 0)); + + igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&csizes); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/static_fitness.c b/src/games/static_fitness.c new file mode 100644 index 0000000..e24e4ec --- /dev/null +++ b/src/games/static_fitness.c @@ -0,0 +1,464 @@ +/* -*- 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" +#include "core/math.h" /* M_SQRT2 */ + +/** + * \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 \c i and \c 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 \c i to \c j depends on + * the out-fitness of vertex \c i and the in-fitness of vertex \c j. + * + * + * The generation process goes as follows. We start from \c N disconnected nodes + * (where \c N is given by the length of the fitness vector). Then we randomly + * select two vertices \c i and \c j, with probabilities proportional to their + * fitnesses. (When the generated graph is directed, \c i is selected according to + * the out-fitnesses and \c 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. + * + * + * The \em expected degree (though not the actual degree) of each vertex will be + * proportional to its fitness. This is exactly true when self-loops and multi-edges + * are allowed, and approximately true otherwise. If you need to generate a graph + * with an exact degree sequence, consider \ref igraph_degree_sequence_game() and + * \ref igraph_realize_degree_sequence() instead. + * + * + * To generate random undirected graphs with a given expected degree sequence, set + * \p fitness_out (and in the directed case \p fitness_out) to the desired expected + * degrees, and \p no_of_edges to the corresponding edge count, i.e. half the sum of + * expected degrees in the undirected case, and the sum of out- or in-degrees in the + * directed case. + * + * + * This model is similar to the better-known Chung-Lu model, implemented in igraph + * as \ref igraph_chung_lu_game(), but with a sharply fixed edge count. + * + * + * 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 + * https://doi.org/10.1103/PhysRevLett.87.278701. + * + * \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. + * + * \sa \ref igraph_static_power_law_game(), \ref igraph_chung_lu_game(), + * \ref igraph_degree_sequence_game() + * + * Time complexity: O(|V| + |E| log |E|). + */ +igraph_error_t 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) { + + const igraph_integer_t no_of_nodes = igraph_vector_size(fitness_out); + const igraph_bool_t directed = (fitness_in != NULL); + igraph_vector_int_t edges; + 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_real_t num_steps; + igraph_integer_t from, to, pos; + int iter = 0; + + IGRAPH_ASSERT(fitness_out != NULL); + + if (no_of_edges < 0) { + IGRAPH_ERRORF("Number of edges cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_edges); + } + + if (no_of_edges > IGRAPH_ECOUNT_MAX && (no_of_nodes == 0 && no_of_edges > 0)) { + IGRAPH_ERROR("Too many edges requested.", IGRAPH_EINVAL); + } + + if (no_of_nodes == 0) { + return igraph_empty(graph, no_of_nodes, directed); + } + + if (directed && igraph_vector_size(fitness_in) != no_of_nodes) { + IGRAPH_ERROR("The in-fitness vector must have the same length as the out-fitness vector.", IGRAPH_EINVAL); + } + + /* Sanity checks for the fitnesses */ + if (igraph_vector_min(fitness_out) < 0 && (!directed || igraph_vector_min(fitness_in) < 0)) { + IGRAPH_ERROR("Fitness scores must not be negative.", IGRAPH_EINVAL); + } + + /* Avoid getting into an infinite loop when too many edges are requested. */ + { + igraph_integer_t nodes; + if (directed) { + igraph_integer_t outnodes, innodes; + outnodes = innodes = nodes = 0; + for (igraph_integer_t 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 (igraph_integer_t 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 (max_no_of_edges == 0 && no_of_edges > 0) { + /* This check is necessary even when multiple edges are allowed, + * for example when all fitnesses are zero or only one is nonzero + * not self-loops are disallowed. */ + IGRAPH_ERRORF("No edges are possible with the given fitness scores, " + "but %" IGRAPH_PRId " edges were requested.", IGRAPH_EINVAL, no_of_edges); + } + if (!multiple && 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 (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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2 * no_of_edges)); + + while (no_of_edges > 0) { + /* Report progress after every 10000 edges */ + if ((iter++) % 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_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + + no_of_edges--; + } + + /* Create the graph */ + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + /* Clear the edge list */ + igraph_vector_int_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 ((iter++) % 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 (!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 (!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 (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 \c i + * is i^(-alpha), where alpha = 1/(gamma-1) + * and \c 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 \c i is + * (i+i0-1)^(-alpha), + * where \c 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. + * https://doi.org/10.1103/PhysRevLett.87.278701 + * + * + * Chung F and Lu L: Connected components in a random graph with given + * degree sequences. Annals of Combinatorics 6, 125-145, 2002. + * https://doi.org/10.1007/PL00012580 + * + * + * 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. + * https://doi.org/10.1103/PhysRevLett.103.135702 + * + * \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 Erdős-Rényi 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|). + */ +igraph_error_t 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, alpha_in; + 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 (isfinite(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 * M_SQRT2 * (1 + alpha_out), -1.0 / alpha_out) - 1; + } + if (j < no_of_nodes) { + j = no_of_nodes; + } + for (igraph_integer_t 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 (isfinite(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 * M_SQRT2 * (1 + alpha_in), -1.0 / alpha_in) - 1; + } + if (j < no_of_nodes) { + j = no_of_nodes; + } + for (igraph_integer_t 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, NULL, loops, multiple)); + } + + igraph_vector_destroy(&fitness_out); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/games/tree.c b/src/games/tree.c new file mode 100644 index 0000000..1c24619 --- /dev/null +++ b/src/games/tree.c @@ -0,0 +1,202 @@ +/* -*- 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_bitset.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "math/safe_intop.h" + +/* Uniform sampling of labelled trees (igraph_tree_game) */ + +/* The following implementation uniformly samples Prufer trees and converts + * them to trees. + */ + +static igraph_error_t igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { + igraph_vector_int_t prufer; + igraph_integer_t 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 igraph_error_t igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { + igraph_vector_int_t edges; + igraph_vector_int_t vertices; + igraph_bitset_t visited; + igraph_integer_t i, j; + igraph_integer_t no_edges; + + IGRAPH_SAFE_MULT(n - 1, 2, &no_edges); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_edges); + + IGRAPH_CHECK(igraph_bitset_init(&visited, n)); + IGRAPH_FINALLY(igraph_bitset_destroy, &visited); + + /* The vertices vector contains visited vertices between 0..k-1, unvisited ones between k..n-1. */ + IGRAPH_CHECK(igraph_vector_int_init_range(&vertices, 0, n)); + 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); + IGRAPH_BIT_SET(visited, i); + SWAP_INT_ELEM(vertices, 0, i); + + for (igraph_integer_t k = 1; k < n; ++k) { + j = RNG_INTEGER(0, n - 1); + if (IGRAPH_BIT_TEST(visited, VECTOR(vertices)[j])) { + i = VECTOR(vertices)[j]; + j = RNG_INTEGER(k, n - 1); + } + IGRAPH_BIT_SET(visited, VECTOR(vertices)[j]); + 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_bitset_destroy(&visited); + igraph_vector_int_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() + * + */ + +igraph_error_t 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/games/watts_strogatz.c b/src/games/watts_strogatz.c new file mode 100644 index 0000000..6647296 --- /dev/null +++ b/src/games/watts_strogatz.c @@ -0,0 +1,119 @@ +/* -*- 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 networks with the small-world property + * based on a variant of the Watts-Strogatz model. The network is obtained + * by first creating a periodic undirected lattice, then rewiring both + * endpoints of each edge with probability \p p, while avoiding the + * creation of multi-edges. + * + * + * This process differs from the original model of Watts and Strogatz + * (see reference) in that it rewires \em both endpoints of edges. Thus in + * the limit of p=1, we obtain a G(n,m) random graph with the + * same number of vertices and edges as the original lattice. In comparison, + * the original Watts-Strogatz model only rewires a single endpoint of each edge, + * thus the network does not become fully random even for p=1. + * For appropriate choices of \p p, both models exhibit the property of + * simultaneously having short path lengths and high clustering. + * + * + * Reference: + * + * + * 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_square_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. + */ +igraph_error_t 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_int_t dimvector; + igraph_vector_bool_t periodic; + + 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_INT_INIT_FINALLY(&dimvector, dim); + igraph_vector_int_fill(&dimvector, size); + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&periodic, dim); + igraph_vector_bool_fill(&periodic, true); + + IGRAPH_CHECK(igraph_square_lattice(graph, &dimvector, nei, IGRAPH_UNDIRECTED, + /* mutual */ false, &periodic)); + + igraph_vector_bool_destroy(&periodic); + igraph_vector_int_destroy(&dimvector); + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY(igraph_destroy, graph); + + /* Rewire the edges then */ + + IGRAPH_CHECK(igraph_rewire_edges(graph, p, loops, multiple)); + + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/graph/adjlist.c b/src/graph/adjlist.c new file mode 100644 index 0000000..f027558 --- /dev/null +++ b/src/graph/adjlist.c @@ -0,0 +1,1342 @@ +/* -*- 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_bitset.h" +#include "igraph_memory.h" +#include "igraph_interface.h" + +#include "core/interruption.h" + +#include + +/** + * Helper function that simplifies a sorted adjacency vector by removing + * duplicate elements and optionally self-loops. + * + * has_loops and has_multiple are pointers to booleans that will be updated + * to \c true if the function \em finds a loop or a multiple edge. These values will + * \em never be set back to zero by this function. The usage pattern for these + * arguments is that the caller should set them to zero, followed by one or + * multiple calls to this function; at the end of such a sequence the booleans + * will contain whether the function found at least one loop or multiple edge + * in the set of vertices that were investigated. + * + * Note the usage of the word "found" -- it might be the case that the + * function is not interested in loop or multiple edges due to how it is + * parameterized; in this case, we don't spend extra time in investigating the + * existence of loop or multiple edges, so the values of the has_loops and + * has_multiple arguments will stay as is. Therefore, upon exiting the + * function, finding \c false in one of these variables does \em not mean that + * there is no loop or multiple edge, only that the function hasn't found one. + */ +static igraph_error_t 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, igraph_bool_t *has_loops, + igraph_bool_t *has_multiple +); + +/** + * Helper function that removes loops from an incidence vector (either both + * occurrences or only one of them). + */ +static igraph_error_t 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. + * + * + * This function returns each neighbor list in sorted order, just + * like \ref igraph_neighbors(). + * + * + * As of igraph 0.10, there is a small performance cost to setting \p loops + * to a different value than \c IGRAPH_LOOPS_TWICE or setting \p multiple to a + * different value from \c IGRAPH_MULTIPLE. + * + * \param graph The input graph. + * \param al Pointer to an uninitialized igraph_adjlist_t object. + * \param mode Constant specifying whether to include only outgoing + * (\c IGRAPH_OUT), only incoming (\c IGRAPH_IN), + * or both (\c IGRAPH_ALL) types of neighbors + * in the adjacency list. It is ignored for undirected graphs. + * \param loops Specifies how to treat loop edges. \c IGRAPH_NO_LOOPS + * removes loop edges from the adjacency list. \c IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the adjacency list of the + * corresponding vertex. \c 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 \p mode is set to + * \c IGRAPH_ALL. + * \param multiple Specifies how to treat multiple (parallel) edges. + * \c IGRAPH_NO_MULTIPLE collapses parallel edges into a single one; + * \c 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. + * + * \sa \ref igraph_neighbors() for getting the neighbor lists of individual + * vertices. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +igraph_error_t 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 no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degrees; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create adjacency list view.", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + /* igraph_degree() is fast when loops=true */ + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode, /* loops= */ true)); + + al->length = no_of_nodes; + al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); + IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating adjacency list view."); + IGRAPH_FINALLY(igraph_adjlist_destroy, al); + + /* if we already know there are no multi-edges, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI)) { + multiple = IGRAPH_MULTIPLE; + } + + /* if we already know there are no loops, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + if (mode == IGRAPH_ALL) { + loops = IGRAPH_LOOPS_TWICE; + } else { + loops = IGRAPH_LOOPS_ONCE; + } + } + + igraph_bool_t has_loops = false; + igraph_bool_t has_multiple = false; + for (igraph_integer_t i = 0; i < al->length; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], VECTOR(degrees)[i])); + IGRAPH_CHECK(igraph_neighbors(graph, &al->adjs[i], i, mode)); + + /* Attention: This function will only set values for has_loops and has_multiple + * if it finds loops/multi-edges. Otherwise they are left at their original value. */ + IGRAPH_CHECK(igraph_i_simplify_sorted_int_adjacency_vector_in_place( + &al->adjs[i], i, mode, loops, multiple, &has_loops, &has_multiple + )); + } + if (has_loops) { + /* If we have found at least one loop above, set the cache to true */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, true); + } else if (loops == IGRAPH_NO_LOOPS) { + /* If we explicitly _checked_ for loops (to remove them) and haven't + * found one, set the cache to false. This is the only case when a + * definite "no" from has_loops really means that there are no loops at + * all */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, false); + } + if (has_multiple) { + /* If we have found at least one multiedge above, set the cache to true */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MULTI, true); + } else if (multiple == IGRAPH_NO_MULTIPLE) { + /* If we explicitly _checked_ for multi-edges (to remove them) and + * haven't found one, set the cache to false. This is the only case + * when a definite "no" from has_multiple really means that there are + * no multi-edges at all all */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MULTI, false); + } + + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(2); /* + igraph_adjlist_destroy */ + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes) { + + al->length = no_of_nodes; + al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); + IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating adjlist."); + IGRAPH_FINALLY(igraph_adjlist_destroy, al); + + for (igraph_integer_t i = 0; i < al->length; i++) { + IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], 0)); + } + + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * This function returns each neighbor list in sorted order. + * + * \param graph The input graph. + * \param al Pointer to a not yet initialized adjacency list. + * \param mode Constant specifying whether outgoing + * (\c IGRAPH_OUT), incoming (\c IGRAPH_IN), + * or both (\c 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. + * + * \sa \ref igraph_adjlist_init(), \ref igraph_complementer() + * + * Time complexity: O(|V|^2+|E|), quadratic in the number of vertices. + */ +igraph_error_t igraph_adjlist_init_complementer(const igraph_t *graph, + igraph_adjlist_t *al, + igraph_neimode_t mode, + igraph_bool_t loops) { + + igraph_bitset_t seen; + igraph_vector_int_t neis; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid neighbor mode specified for 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); + IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating complementer adjlist view."); + IGRAPH_FINALLY(igraph_adjlist_destroy, al); + + IGRAPH_BITSET_INIT_FINALLY(&seen, al->length); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + for (igraph_integer_t i = 0; i < al->length; i++) { + /* For each vertex, we mark neighbors within the 'seen' bitset. + * Then we iterate over 'seen' and record non-marked vertices in + * the adjacency list. */ + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Reset neighbor counter and 'seen' vector. */ + igraph_bitset_null(&seen); + igraph_integer_t n = al->length; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, mode)); + + if (!loops) { + IGRAPH_BIT_SET(seen, i); + n--; + } + + igraph_integer_t neis_size = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < neis_size; j++) { + if (! IGRAPH_BIT_TEST(seen, VECTOR(neis)[j]) ) { + n--; + IGRAPH_BIT_SET(seen, VECTOR(neis)[j]); + } + } + + /* Produce "non-neighbor" list in sorted order. */ + IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], n)); + for (igraph_integer_t j = 0, k = 0; k < n; j++) { + if (!IGRAPH_BIT_TEST(seen, j)) { + VECTOR(al->adjs[i])[k++] = j; + } + } + } + + igraph_bitset_destroy(&seen); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); /* +1 for the adjlist itself */ + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_adjlist_init_from_inclist + * \brief Constructs an adjacency list of vertices from an incidence list. + * + * In some algorithms it is useful to have an adjacency list \em and an incidence + * list representation of the same graph, and in many cases it is the most useful + * if they are consistent with each other, i.e. if can be guaranteed that the + * vertex ID in the i-th entry of the adjacency list of vertex v is the + * \em other endpoint of the edge in the i-th entry of the incidence list of the + * same vertex. This function creates such an adjacency list from the corresponding + * incidence list by looking up the endpoints of each edge in the incidence + * list and constructing the corresponding adjacenecy vectors. + * + * + * The adjacency list is independent of the graph or the incidence list after + * creation; in other words, modifications that are made to the graph or the + * incidence list are not reflected in the adjacency list. + * + * \param graph The input graph. + * \param al Pointer to an uninitialized igraph_adjlist_t object. + * \param il Pointer to an \em initialized igraph_inclist_t object + * that will be converted into an adjacency list. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +igraph_error_t igraph_adjlist_init_from_inclist( + const igraph_t *graph, igraph_adjlist_t *al, const igraph_inclist_t *il) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, num_neis; + + igraph_vector_int_t *neis; + igraph_vector_int_t *incs; + + if (igraph_inclist_size(il) != no_of_nodes) { + IGRAPH_ERRORF( + "Incidence list has %" IGRAPH_PRId " entries but the graph has %" IGRAPH_PRId " vertices.", + IGRAPH_EINVAL, + igraph_inclist_size(il), + no_of_nodes + ); + } + + IGRAPH_CHECK(igraph_adjlist_init_empty(al, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + neis = igraph_adjlist_get(al, i); + incs = igraph_inclist_get(il, i); + + num_neis = igraph_vector_int_size(incs); + IGRAPH_CHECK(igraph_vector_int_resize(neis, num_neis)); + + for (j = 0; j < num_neis; j++) { + VECTOR(*neis)[j] = IGRAPH_OTHER(graph, VECTOR(*incs)[j], i); + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \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) { + igraph_integer_t i; + for (i = 0; i < al->length; i++) { + /* This works if some igraph_vector_int_t's contain NULL, + because igraph_vector_int_destroy can handle this. */ + 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) { + igraph_integer_t 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. Note that + * \ref igraph_adjlist_init() already produces sorted neighbor lists. + * This function is useful when the adjacency list is produced in + * a different manner, or is modified in a way that does not preserve + * the sorted order. + * + * \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) { + igraph_integer_t 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. + * + * + * When the adjacency list is created with \ref igraph_adjlist_init(), + * use the \c loops and \c multiple parameters of that function instead. + * + * \param al The adjacency list. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of edges and + * vertices. + */ +igraph_error_t igraph_adjlist_simplify(igraph_adjlist_t *al) { + igraph_integer_t i; + igraph_integer_t n = al->length; + igraph_vector_int_t mark; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, n); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->adjs[i]; + igraph_integer_t j, l = igraph_vector_int_size(v); + VECTOR(mark)[i] = i + 1; + for (j = 0; j < l; /* nothing */) { + igraph_integer_t e = 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 IGRAPH_SUCCESS; +} + +#ifndef USING_R +igraph_error_t igraph_adjlist_print(const igraph_adjlist_t *al) { + igraph_integer_t i; + igraph_integer_t n = al->length; + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->adjs[i]; + igraph_vector_int_print(v); + } + return IGRAPH_SUCCESS; +} +#endif + +igraph_error_t igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile) { + igraph_integer_t i; + igraph_integer_t n = al->length; + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->adjs[i]; + igraph_vector_int_fprint(v, outfile); + } + return IGRAPH_SUCCESS; +} + +#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); + +} + +igraph_error_t 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; + igraph_bool_t found_old, found_new; + igraph_integer_t 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); + + /* oldfrom -> oldto should exist; newfrom -> newto should not. */ + found_old = igraph_vector_int_binsearch(oldfromvec, oldto, &oldpos); + if (! found_old) { + IGRAPH_ERROR("Edge to replace does not exist.", IGRAPH_EINVAL); + } + found_new = igraph_vector_int_binsearch(newfromvec, newto, &newpos); + if (found_new) { + IGRAPH_ERROR("New edge already exists.", IGRAPH_EINVAL); + } + + if (oldfromvec != newfromvec) { + /* grow the new vector first and then remove the item from the old one + * to ensure that we don't end up in a situation where the removal + * succeeds but the addition does not */ + IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); + igraph_vector_int_remove(oldfromvec, oldpos); + } else { + /* moving item within the same vector; here we can safely remove first + * and insert afterwards because there is no need to re-allocate memory */ + igraph_vector_int_remove(oldfromvec, oldpos); + if (oldpos < newpos) { + --newpos; + } + IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); + } + + return IGRAPH_SUCCESS; + +} + +static igraph_error_t igraph_i_remove_loops_from_incidence_vector_in_place( + igraph_vector_int_t *v, const igraph_t *graph, igraph_loops_t loops +) { + igraph_integer_t 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; +} + +#ifndef USING_R +igraph_error_t igraph_inclist_print(const igraph_inclist_t *al) { + igraph_integer_t i; + igraph_integer_t n = al->length; + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->incs[i]; + igraph_vector_int_print(v); + } + return IGRAPH_SUCCESS; +} +#endif + +igraph_error_t igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) { + igraph_integer_t i; + igraph_integer_t n = al->length; + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &al->incs[i]; + igraph_vector_int_fprint(v, outfile); + } + return IGRAPH_SUCCESS; +} + +/** + * \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). + * + * + * As of igraph 0.10, there is a small performance cost to setting \p loops + * to a different value than \c IGRAPH_LOOPS_TWICE. + * + * \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. + */ +igraph_error_t igraph_inclist_init(const igraph_t *graph, + igraph_inclist_t *il, + igraph_neimode_t mode, + igraph_loops_t loops) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degrees; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create incidence list view.", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + /* igraph_degrees() is fast when loops=true */ + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode, /* loops= */ 1)); + + il->length = no_of_nodes; + il->incs = IGRAPH_CALLOC(il->length, igraph_vector_int_t); + if (il->incs == 0) { + IGRAPH_ERROR("Cannot create incidence list view.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + IGRAPH_FINALLY(igraph_inclist_destroy, il); + for (igraph_integer_t i = 0; i < il->length; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_vector_int_init(&il->incs[i], VECTOR(degrees)[i])); + IGRAPH_CHECK(igraph_incident(graph, &il->incs[i], i, mode)); + + if (loops != IGRAPH_LOOPS_TWICE) { + IGRAPH_CHECK( + igraph_i_remove_loops_from_incidence_vector_in_place(&il->incs[i], graph, loops) + ); + } + } + + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(2); /* + igraph_inclist_destroy */ + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n) { + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \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) { + igraph_integer_t i; + for (i = 0; i < il->length; i++) { + /* This works if some igraph_vector_int_t's contain NULL, + because igraph_vector_int_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) { + igraph_integer_t 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; +} + +/* See the prototype above for a description of this function. */ +static igraph_error_t 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, igraph_bool_t *has_loops, + igraph_bool_t *has_multiple + +) { + igraph_bool_t dummy1 = true, dummy2 = true; /* set dummies to avoid uninitialized read */ + if (has_loops == NULL) { + has_loops = &dummy1; + } + if (has_multiple == NULL) { + has_multiple = &dummy2; + } + igraph_integer_t i, p = 0; + igraph_integer_t 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 { + if (VECTOR(*v)[i] == index) { + *has_loops = true; + /* If we haven't found multi-edges yet, we also need to check + * if this is a multi-loop, to set 'has_multiple' correctly. */ + if (! *has_multiple) { + if (mode == IGRAPH_ALL) { + /* Undirected loops appear twice in the neighbour list, + * so we check two following items instead of one. */ + if (i < n - 2 && + VECTOR(*v)[i + 1] == VECTOR(*v)[i] && + VECTOR(*v)[i + 2] == VECTOR(*v)[i]) { + *has_multiple = true; + } + } else { + if (i != n - 1 && VECTOR(*v)[i + 1] == VECTOR(*v)[i]) { + *has_multiple = true; + } + } + } + } else if (i != n - 1 && VECTOR(*v)[i + 1] == VECTOR(*v)[i]) { + *has_multiple = true; + } + } + } + } 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 { + *has_loops = true; + } + } + } + } 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 */ + 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 if ( + /* If this is not a loop then we have a multigraph. + Else we have at least two loops. + The v vector comes from a call to igraph_neighbors. + This will count loops twice if mode == IGRAPH_ALL. + So if mode != IGRAPH_ALL, + then we have a multigraph. + If mode == IGRAPH_ALL and we have three loops + then we also have a multigraph + */ + (VECTOR(*v)[i] != index) || + (mode != IGRAPH_ALL) || + (mode == IGRAPH_ALL && i < n - 2 && VECTOR(*v)[i + 2] == VECTOR(*v)[i]) + ){ + *has_multiple = true; + } + } + } 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) { + *has_loops = true; + /* 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 */ + 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 { + *has_multiple = true; + /* 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 Initializes 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. Neighbor lists will be returned + * in sorted order. + * + * + * As of igraph 0.10, there is a small performance cost to setting \p loops + * to a different value than \c IGRAPH_LOOPS_TWICE or setting \p multiple to a + * different value from \c IGRAPH_MULTIPLE. + * + * \param graph The input graph. + * \param al Pointer to an uninitialized adjacency list object. + * \param mode Constant specifying whether to include only outgoing + * (\c IGRAPH_OUT), only incoming (\c IGRAPH_IN), + * or both (\c IGRAPH_ALL) types of neighbors + * in the adjacency list. It is ignored for undirected graphs. + * \param loops Specifies how to treat loop edges. \c IGRAPH_NO_LOOPS + * removes loop edges from the adjacency list. \c IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the adjacency list of the + * corresponding vertex. \c 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 \p mode is set to + * \c IGRAPH_ALL. + * \param multiple Specifies how to treat multiple (parallel) edges. + * \c IGRAPH_NO_MULTIPLE collapses parallel edges into a single one; + * \c 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. + * + * \sa \ref igraph_neighbors() for getting the neighbor lists of individual + * vertices. + * + * Time complexity: O(|V|), the number of vertices, possibly, but + * depends on the underlying memory management too. + */ + +igraph_error_t 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("Cannot create lazy adjacency list view.", IGRAPH_EINVMODE); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + /* if we already know there are no multi-edges, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI)) { + multiple = IGRAPH_MULTIPLE; + } + + /* if we already know there are no loops, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + if (mode == IGRAPH_ALL) { + loops = IGRAPH_LOOPS_TWICE; + } else { + loops = IGRAPH_LOOPS_ONCE; + } + } + + 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*); + IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating lazy adjacency list view."); + + return IGRAPH_SUCCESS; +} + +/** + * \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_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) { + igraph_integer_t 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 no) { + igraph_error_t ret; + + if (al->adjs[no] == NULL) { + al->adjs[no] = IGRAPH_CALLOC(1, igraph_vector_int_t); + if (al->adjs[no] == NULL) { + return NULL; + } + + ret = igraph_vector_int_init(al->adjs[no], 0); + if (ret != IGRAPH_SUCCESS) { + IGRAPH_FREE(al->adjs[no]); + return NULL; + } + + ret = igraph_neighbors(al->graph, al->adjs[no], no, al->mode); + if (ret != IGRAPH_SUCCESS) { + igraph_vector_int_destroy(al->adjs[no]); + IGRAPH_FREE(al->adjs[no]); + return NULL; + } + + ret = igraph_i_simplify_sorted_int_adjacency_vector_in_place( + al->adjs[no], no, al->mode, al->loops, al->multiple, NULL, + NULL + ); + if (ret != IGRAPH_SUCCESS) { + igraph_vector_int_destroy(al->adjs[no]); + IGRAPH_FREE(al->adjs[no]); + return NULL; + } + } + + 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. + * + * + * As of igraph 0.10, there is a small performance cost to setting \p loops + * to a different value than \c IGRAPH_LOOPS_TWICE. + * + * \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. + */ + +igraph_error_t 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); /* LCOV_EXCL_LINE */ + } + + return IGRAPH_SUCCESS; + +} + +/** + * \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_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) { + igraph_integer_t 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 no) { + igraph_error_t ret; + + if (il->incs[no] == NULL) { + il->incs[no] = IGRAPH_CALLOC(1, igraph_vector_int_t); + if (il->incs[no] == NULL) { + return NULL; + } + + ret = igraph_vector_int_init(il->incs[no], 0); + if (ret != IGRAPH_SUCCESS) { + IGRAPH_FREE(il->incs[no]); + return NULL; + } + + ret = igraph_incident(il->graph, il->incs[no], no, il->mode); + if (ret != IGRAPH_SUCCESS) { + igraph_vector_int_destroy(il->incs[no]); + IGRAPH_FREE(il->incs[no]); + return NULL; + } + + 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 != IGRAPH_SUCCESS) { + igraph_vector_int_destroy(il->incs[no]); + IGRAPH_FREE(il->incs[no]); + return NULL; + } + } + } + + return il->incs[no]; +} diff --git a/src/graph/attributes.c b/src/graph/attributes.c new file mode 100644 index 0000000..731d628 --- /dev/null +++ b/src/graph/attributes.c @@ -0,0 +1,557 @@ +/* -*- 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 "internal/hacks.h" /* strdup */ + +#include +#include + +/* Should you ever want to have a thread-local attribute handler table, prepend + * IGRAPH_THREAD_LOCAL to the following declaration and #include "config.h". */ +igraph_attribute_table_t *igraph_i_attribute_table = NULL; + +igraph_error_t igraph_i_attribute_init(igraph_t *graph, void *attr) { + graph->attr = NULL; + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->init(graph, attr); + } else { + return IGRAPH_SUCCESS; + } +} + +void igraph_i_attribute_destroy(igraph_t *graph) { + if (igraph_i_attribute_table) { + igraph_i_attribute_table->destroy(graph); + } + graph->attr = NULL; +} + +igraph_error_t igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, + igraph_bool_t va, igraph_bool_t ea) { + to->attr = NULL; + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->copy(to, from, ga, va, ea); + } else { + return IGRAPH_SUCCESS; + } +} + +igraph_error_t igraph_i_attribute_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->add_vertices(graph, nv, attr); + } else { + return IGRAPH_SUCCESS; + } +} + +igraph_error_t igraph_i_attribute_permute_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_t *idx) { + /* graph and newgraph may be the same, in which case we need to support + * in-place operations. If they are _not_ the same, it is assumed that the + * new graph has no vertex attributes yet */ + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx); + } else { + return IGRAPH_SUCCESS; + } +} + +igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_t *merges, + const igraph_attribute_combination_t *comb) { + /* It is assumed that the two graphs are not the same and that the new + * graph has no vertex attributes yet. We cannot assert the latter but we + * can assert the former */ + IGRAPH_ASSERT(graph != newgraph); + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->combine_vertices(graph, newgraph, + merges, + comb); + } else { + return IGRAPH_SUCCESS; + } +} + +igraph_error_t igraph_i_attribute_add_edges(igraph_t *graph, + const igraph_vector_int_t *edges, void *attr) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->add_edges(graph, edges, attr); + } else { + return IGRAPH_SUCCESS; + } +} + +igraph_error_t igraph_i_attribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_t *idx) { + /* graph and newgraph may be the same, in which case we need to support + * in-place operations. If they are _not_ the same, it is assumed that the + * new graph has no edge attributes yet */ + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->permute_edges(graph, newgraph, idx); + } else { + return IGRAPH_SUCCESS; + } +} + +igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_t *merges, + const igraph_attribute_combination_t *comb) { + /* It is assumed that the two graphs are not the same and that the new + * graph has no eedge attributes yet. We cannot assert the latter but we + * can assert the former */ + IGRAPH_ASSERT(graph != newgraph); + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->combine_edges(graph, newgraph, + merges, + comb); + } else { + return IGRAPH_SUCCESS; + } +} + +igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph, + igraph_strvector_t *gnames, + igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, + igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, + igraph_vector_int_t *etypes) { + if (igraph_i_attribute_table) { + return igraph_i_attribute_table->get_info(graph, gnames, gtypes, + vnames, vtypes, + enames, etypes); + } else { + return IGRAPH_SUCCESS; + } +} + +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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } + +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +/** + * \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. + * + * + * Attribute handlers are normally attached on program startup, and are + * left active for the program's lifetime. This is because a graph object + * created with a given attribute handler must not be manipulated while + * a different attribute handler is active. + * + * \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(void) { + return igraph_i_attribute_table != NULL; +} + + +/** + * \function igraph_attribute_combination_init + * \brief Initialize attribute combination list. + * + * \param comb The uninitialized attribute combination list. + * \return Error code. + * + * Time complexity: O(1) + */ +igraph_error_t 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) { + igraph_integer_t 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. This function is called + * by the concrete attribute handler attached to igraph, and its + * calling signature depends completely on the attribute handler. + * For instance, if you are using attributes from C and you have + * attached the C attribute handler, you need to follow the + * documentation of the C attribute handler + * for more details. + * \return Error code. + * + * Time complexity: O(n), where n is the number of current attribute + * combinations. + */ +igraph_error_t igraph_attribute_combination_add(igraph_attribute_combination_t *comb, + const char *name, + igraph_attribute_combination_type_t type, + igraph_function_pointer_t func) { + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + if (! name) { + rec->name = NULL; + } else { + rec->name = strdup(name); + if (! rec->name) { + IGRAPH_ERROR("Cannot create attribute combination data.", + IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + } + IGRAPH_FINALLY(igraph_free, (char *) rec->name); /* free() is safe on NULL */ + rec->type = type; + rec->func = func; + + IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec)); + IGRAPH_FINALLY_CLEAN(2); /* ownership of 'rec' transferred to 'comb->list' */ + + } + + 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. + */ +igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, + const char *name) { + igraph_integer_t 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; +} + +igraph_error_t igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, + const char *name, + igraph_attribute_combination_type_t *type, + igraph_function_pointer_t *func) { + igraph_integer_t 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 IGRAPH_SUCCESS; + } + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_attribute_combination + * \brief Initialize attribute combination list and add records. + * + * \param comb The uninitialized attribute combination list. + * \param ... A list of 'name, type[, func]', where: + * \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. + * The list is closed by setting the name to \c IGRAPH_NO_MORE_ATTRIBUTES. + * \return Error code. + * + * Time complexity: O(n^2), where n is the number attribute + * combinations records to add. + * + * \example examples/simple/igraph_attribute_combination.c + */ +igraph_error_t igraph_attribute_combination( + igraph_attribute_combination_t *comb, ...) { + + va_list ap; + + IGRAPH_CHECK(igraph_attribute_combination_init(comb)); + + va_start(ap, comb); + while (true) { + igraph_function_pointer_t func = NULL; + 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_error_t ret = igraph_attribute_combination_add(comb, name, type, func); + if (ret != IGRAPH_SUCCESS) { + va_end(ap); + return ret; + } + } + + va_end(ap); + + return IGRAPH_SUCCESS; +} diff --git a/src/graph/attributes.h b/src/graph/attributes.h new file mode 100644 index 0000000..c1ceb3e --- /dev/null +++ b/src/graph/attributes.h @@ -0,0 +1,115 @@ +/* + 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_decls.h" +#include "igraph_strvector.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +#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 { \ + igraph_error_t igraph_i_ret2=IGRAPH_SUCCESS; \ + (to)->attr = NULL; \ + if ((from)->attr) { \ + IGRAPH_CHECK(igraph_i_ret2=igraph_i_attribute_copy((to),(from),(ga),(va),(ea))); \ + } \ + if (igraph_i_ret2 != IGRAPH_SUCCESS) { \ + IGRAPH_ERROR("", igraph_i_ret2); \ + } \ + } while(0) + +igraph_error_t igraph_i_attribute_init(igraph_t *graph, void *attr); +void igraph_i_attribute_destroy(igraph_t *graph); +igraph_error_t igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, + igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea); +igraph_error_t igraph_i_attribute_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr); +igraph_error_t igraph_i_attribute_permute_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_t *idx); +igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_t *merges, + const igraph_attribute_combination_t *comb); +igraph_error_t igraph_i_attribute_add_edges(igraph_t *graph, + const igraph_vector_int_t *edges, void *attr); +igraph_error_t igraph_i_attribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_t *idx); +igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_t *merges, + const igraph_attribute_combination_t *comb); + +igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph, + igraph_strvector_t *gnames, + igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, + igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, + igraph_vector_int_t *etypes); +igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name); +igraph_error_t igraph_i_attribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name); + +igraph_error_t igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_t *value); +igraph_error_t igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_t *value); +igraph_error_t igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_t *value); +igraph_error_t igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, + const char *name, + igraph_strvector_t *value); +igraph_error_t igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_strvector_t *value); +igraph_error_t igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_strvector_t *value); +igraph_error_t igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_bool_t *value); +igraph_error_t igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value); +igraph_error_t igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_bool_t *value); + +__END_DECLS + +#endif /* IGRAPH_GRAPH_ATTRIBUTES_H */ diff --git a/src/graph/basic_query.c b/src/graph/basic_query.c new file mode 100644 index 0000000..10dc66c --- /dev/null +++ b/src/graph/basic_query.c @@ -0,0 +1,93 @@ +/* -*- 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_adjacent + * \brief Decides whether two vertices are adjacent. + * + * Decides whether there are any edges that have \p v1 and \p v2 + * as endpoints. This function is of course symmetric for undirected + * graphs. + * + * \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. + * + * 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. + */ +igraph_error_t igraph_are_adjacent(const igraph_t *graph, + igraph_integer_t v1, igraph_integer_t v2, + igraph_bool_t *res) { + + igraph_integer_t nov = igraph_vcount(graph); + igraph_integer_t eid = -1; + + if (v1 < 0 || v2 < 0 || v1 > nov - 1 || v2 > nov - 1) { + IGRAPH_ERROR("Invalid vertex ID when checking if two vertices are connected.", IGRAPH_EINVVID); + } + + igraph_get_eid(graph, &eid, v1, v2, IGRAPH_DIRECTED, /*error=*/ false); + *res = (eid >= 0); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_are_connected + * \brief Decides whether two vertices are adjacent (deprecated alias). + * + * \deprecated-by igraph_are_adjacent 0.10.10 + * + * Decides whether there are any edges that have \p v1 and \p v2 + * as endpoints. This function is of course symmetric for undirected + * graphs. + * + * \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. + * + * 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. + */ +igraph_error_t igraph_are_connected(const igraph_t *graph, + igraph_integer_t v1, igraph_integer_t v2, + igraph_bool_t *res) { + return igraph_are_adjacent(graph, v1, v2, res); +} diff --git a/src/graph/caching.c b/src/graph/caching.c new file mode 100644 index 0000000..2f9781c --- /dev/null +++ b/src/graph/caching.c @@ -0,0 +1,214 @@ +/* + 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, see . +*/ + +#include "igraph_interface.h" + +#include "graph/caching.h" + +#include + +/****** Strictly internal functions ******/ + +/** + * \brief Initializes a property cache, ensuring that all values are unknown. + */ +igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache) { + IGRAPH_STATIC_ASSERT(IGRAPH_PROP_I_SIZE <= 32); + + memset(cache->value, 0, sizeof(cache->value)); + cache->known = 0; + return IGRAPH_SUCCESS; +} + +/** + * \brief Copies a property cache. + */ +igraph_error_t igraph_i_property_cache_copy( + igraph_i_property_cache_t *cache, + const igraph_i_property_cache_t *other_cache) { + *cache = *other_cache; + return IGRAPH_SUCCESS; +} + +/** + * \brief Destroys a property cache. + */ +void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache) { + IGRAPH_UNUSED(cache); + /* Nothing to do */ +} + +/***** Developer fuctions, exposed *****/ + +/** + * \brief Returns the value of a cached boolean property. + * + * This function provides valid results only when the property is already + * cached. Use \ref igraph_i_property_cache_has() to retrieve whether the + * property is cached. + * + * \param graph the graph whose cache is to be checked + * \param prop the property to retrieve from the cache + * \return the cached value of the property if the value is in the cache, or + * an undefined value otherwise + */ +igraph_bool_t igraph_i_property_cache_get_bool(const igraph_t *graph, igraph_cached_property_t prop) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + return graph->cache->value[prop]; +} + +/** + * \brief Returns whether the cache contains a value for the given cached property. + * + * \param graph the graph whose cache is to be checked + * \param prop the property to check in the cache + */ +igraph_bool_t igraph_i_property_cache_has(const igraph_t *graph, igraph_cached_property_t prop) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + return graph->cache->known & (1 << prop); +} + +/** + * \brief Stores a property value in the cache. + * + * \param graph the graph whose cache is to be modified + * \param prop the property to update in the cache + * \param value the value of the property to add to the cache + */ +void igraph_i_property_cache_set_bool(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + /* Even though graph is const, updating the cache is not considered modification. + * Functions that merely compute graph properties, and thus leave the graph structure + * intact, will often update the cache. */ + graph->cache->value[prop] = value; + graph->cache->known |= (1 << prop); +} + +/** + * \brief Stores a property value in the cache. + * + * This function asserts that if the value of \p prop was already known, + * then \p value is consistent with the previously stored value. + * If this is not the case, a fatal error is triggered, with the reasoning + * that the cache must have become invalid/inconsistent due to a bug. + * + * Therefore, this function cannot be used to change an already stored + * property to a different value. If this is your intention, invalidate + * the cache explicitly first. + * + * \param graph the graph whose cache is to be modified + * \param prop the property to update in the cache + * \param value the value of the property to add to the cache + */ +void igraph_i_property_cache_set_bool_checked(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + /* Even though graph is const, updating the cache is not considered modification. + * Functions that merely compute graph properties, and thus leave the graph structure + * intact, will often update the cache. */ + if (graph->cache->known & (1 << prop)) { + IGRAPH_ASSERT(graph->cache->value[prop] == value); + } else { + igraph_i_property_cache_set_bool(graph, prop, value); + } +} + +/** + * \brief Invalidates the cached value of a property in a graph. + * + * \param graph the graph whose cache is to be modified + * \param prop the property to invalidate in the cache + */ +void igraph_i_property_cache_invalidate(const igraph_t *graph, igraph_cached_property_t prop) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + graph->cache->known &= ~(1 << prop); +} + +/** + * \brief Invalidates all cached properties of the graph. + * + * This function is typically called after the graph is modified. + * + * \param graph the graph whose cache is to be invalidated + */ +void igraph_i_property_cache_invalidate_all(const igraph_t *graph) { + assert(graph->cache != NULL); + graph->cache->known = 0; +} + +/** + * \brief Invalidates all but a few cached properties of the graph, subject to specific conditions. + * + * This function is typically called after the graph is modified if we know that + * the modification does not affect certain cached properties in certain cases. + * For instance, adding more vertices does not make a connected graph disconnected, + * so we can keep the cached properties related to graph connectivity if they + * were already cached as true, but we need to invalidate them if they were + * cached as false. + * + * + * Use 1 << IGRAPH_PROP_SOMETHING to encode an individual property + * in the bits of the bitmask used in the arguments of this function. + * + * \param graph the graph whose cache is to be invalidated + * \param keep_always bitmask where the i-th bit corresponds to cached property \em i + * and it should be set to 1 if the property should be \em kept , + * irrespectively of its current cached value. + */ +void igraph_i_property_cache_invalidate_conditionally( + const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false, + uint32_t keep_when_true +) { + uint32_t invalidate = ~keep_always; + uint32_t mask; + uint32_t maybe_keep; + igraph_bool_t cached_value; + + assert(graph->cache != NULL); + + /* The bits of maybe_keep are set to 1 for those properties that are: + * + * - currently cached + * - should _probably_ be invalidated + * - _but_ the current cached value of the property may change the decision + */ + maybe_keep = graph->cache->known & invalidate & (keep_when_false | keep_when_true); + + if (maybe_keep) { + for (igraph_cached_property_t prop = (igraph_cached_property_t ) 0; prop < IGRAPH_PROP_I_SIZE; ++prop) { + mask = 1 << prop; + if (maybe_keep & mask) { + /* if we get here, we know that the property is cached; we have + * masked maybe_keep with graph->cache->known */ + cached_value = igraph_i_property_cache_get_bool(graph, prop); + if ( + ((keep_when_false & mask) && !cached_value) || + ((keep_when_true & mask) && cached_value) + ) { + invalidate &= ~mask; + } + } + } + } + + graph->cache->known &= ~invalidate; +} diff --git a/src/graph/caching.h b/src/graph/caching.h new file mode 100644 index 0000000..3a3e149 --- /dev/null +++ b/src/graph/caching.h @@ -0,0 +1,52 @@ +/* + 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, see . +*/ + +#ifndef IGRAPH_CACHING_H +#define IGRAPH_CACHING_H + +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" + +#include "internal/hacks.h" + +#include /* memset */ + +__BEGIN_DECLS + +struct igraph_i_property_cache_t { + igraph_bool_t value[IGRAPH_PROP_I_SIZE]; + + /** Bit field that stores which of the properties are cached at the moment */ + uint32_t known; +}; + +igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache); +igraph_error_t igraph_i_property_cache_copy( + igraph_i_property_cache_t *cache, + const igraph_i_property_cache_t *other_cache); +void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache); + +void igraph_i_property_cache_invalidate_conditionally( + const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false, uint32_t keep_when_true +); + +__END_DECLS + +#endif /* IGRAPH_CACHING_H */ diff --git a/src/graph/cattributes.c b/src/graph/cattributes.c new file mode 100644 index 0000000..21bf872 --- /dev/null +++ b/src/graph/cattributes.c @@ -0,0 +1,4524 @@ +/* -*- 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 "igraph_interface.h" +#include "igraph_random.h" + +#include "internal/hacks.h" /* strdup */ + +#include + +/* An attribute is either a numeric vector (vector_t), a boolean vector + * (vector_bool_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 igraph_i_cattribute_t, which contains three vector_ptr_t's, each + * holding pointers to igraph_attribute_record_t objects. */ + +/* This function is used for producing better error messages. */ +static const char *attribute_type_name(igraph_attribute_type_t type) { + switch (type) { + case IGRAPH_ATTRIBUTE_UNSPECIFIED: + return "unspecified"; /* TODO: should probably trigger a fatal error */ + case IGRAPH_ATTRIBUTE_NUMERIC: + return "numeric"; + case IGRAPH_ATTRIBUTE_BOOLEAN: + return "boolean"; + case IGRAPH_ATTRIBUTE_STRING: + return "string"; + case IGRAPH_ATTRIBUTE_OBJECT: + return "object"; + } + /* The following line is intentionally not in a default switch label + * so that the compiler can warn about unhandled enum values, + * should additional attribute types ever be added in the future. */ + IGRAPH_FATALF("Invalid attribute type %d found.", (int) type); +} + +static igraph_bool_t igraph_i_cattribute_find(const igraph_vector_ptr_t *ptrvec, + const char *name, igraph_integer_t *idx) { + igraph_integer_t i, n = igraph_vector_ptr_size(ptrvec); + igraph_bool_t l = false; + 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; +} + +/* + * Restores attribute vector lengths to their original size after a failure. + * This function assumes that none of the attribute vectors are shorter than origlen. + * Some may be longer due to a partially completed size extension: these will be + * shrunk to their original size. + */ +static void igraph_i_cattribute_revert_attribute_vector_sizes( + igraph_vector_ptr_t *attrlist, igraph_integer_t origlen) { + + igraph_integer_t no_of_attrs = igraph_vector_ptr_size(attrlist); + for (igraph_integer_t i = 0; i < no_of_attrs; i++) { + igraph_attribute_record_t *rec = VECTOR(*attrlist)[i]; + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *nvec = (igraph_vector_t *) rec->value; + IGRAPH_ASSERT(igraph_vector_capacity(nvec) >= origlen); + igraph_vector_resize(nvec, origlen); /* shrinks */ + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *bvec = (igraph_vector_bool_t *) rec->value; + IGRAPH_ASSERT(igraph_vector_bool_capacity(bvec) >= origlen); + igraph_vector_bool_resize(bvec, origlen); /* shrinks */ + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *svec = (igraph_strvector_t *) rec->value; + IGRAPH_ASSERT(igraph_strvector_capacity(svec) >= origlen); + igraph_strvector_resize(svec, origlen); /* shrinks */ + } else { + /* Must never reach here */ + IGRAPH_FATAL("Unknown attribute type encountered."); + } + } +} + +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 igraph_error_t 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, *newrec); + (*newrec)->type = rec->type; + (*newrec)->name = strdup(rec->name); + if (!(*newrec)->name) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newnum); + IGRAPH_CHECK(igraph_vector_init_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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_CHECK(igraph_strvector_init_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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newlog); + IGRAPH_CHECK(igraph_vector_bool_init_copy(newlog, log)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newlog); + (*newrec)->value = newlog; + } + + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; +} + +static void igraph_i_attribute_list_destroy(igraph_vector_ptr_t *attrlist) { + igraph_integer_t i; + igraph_integer_t n = igraph_vector_ptr_size(attrlist); + for (i = 0; i < n; i++) { + igraph_attribute_record_t *rec = VECTOR(*attrlist)[i]; + if (rec) { + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *num = (igraph_vector_t *) rec->value; + igraph_vector_destroy(num); + IGRAPH_FREE(num); + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *str = (igraph_strvector_t *) rec->value; + igraph_strvector_destroy(str); + IGRAPH_FREE(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(boolvec); + } + IGRAPH_FREE(rec->name); + IGRAPH_FREE(rec); + } + } + igraph_vector_ptr_destroy(attrlist); +} + +static igraph_error_t igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { + igraph_attribute_record_t *attr_rec; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, nattr); + + IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->gal, n)); + IGRAPH_FINALLY(igraph_i_attribute_list_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(igraph_vector_ptr_destroy, &nattr->eal); + + 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; + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +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 }; + for (size_t a = 0; a < 3; a++) { + igraph_i_attribute_list_destroy(als[a]); + } + IGRAPH_FREE(graph->attr); /* sets to NULL */ +} + +/* 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 }; + igraph_integer_t i, n; + igraph_vector_t *num; + igraph_strvector_t *str; + igraph_vector_bool_t *boolvec; + igraph_attribute_record_t *rec; + for (size_t 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(rec->name); + IGRAPH_FREE(rec); + } + } +} + +/* No reference counting here. If you use attributes in C you should + know what you're doing. */ + +static igraph_error_t 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 + }; + igraph_integer_t i, n; + 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); /* LCOV_EXCL_LINE */ + } + 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 (size_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, igraph_integer_t nv, + igraph_vector_ptr_t *nattr) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + igraph_integer_t length = igraph_vector_ptr_size(val); + igraph_integer_t nattrno = nattr == NULL ? 0 : igraph_vector_ptr_size(nattr); + igraph_integer_t origlen = igraph_vcount(graph) - nv; + igraph_integer_t newattrs = 0, i; + igraph_vector_int_t news; + + /* First add the new attributes if any */ + newattrs = 0; + IGRAPH_VECTOR_INT_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; + igraph_integer_t j; + igraph_bool_t l = igraph_i_cattribute_find(val, nname, &j); + if (!l) { + newattrs++; + IGRAPH_CHECK(igraph_vector_int_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)[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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newrec); + newrec->type = type; + newrec->name = strdup(tmp->name); + if (!newrec->name) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newbool); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, origlen); + newrec->value = newbool; + igraph_vector_bool_fill(newbool, false); + } + 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; + igraph_integer_t j = -1; + igraph_bool_t l = false; + 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_int_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_add_vertices(igraph_t *graph, igraph_integer_t nv, + igraph_vector_ptr_t *nattr) { + /* Record information needed to restore attribute vector sizes */ + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + igraph_integer_t origlen = igraph_vcount(graph) - nv; + + /* Attempt adding attributes */ + igraph_error_t err = igraph_i_cattribute_add_vertices_inner(graph, nv, nattr); + if (err != IGRAPH_SUCCESS) { + /* If unsuccessful, revert attribute vector sizes. + * The following function assumes that all attributes vectors that + * are present have a length at least as great as origlen. + * This is true at the moment because any new attributes that are + * added to the graph are created directly at 'origlen' instead of + * being created at smaller sizes and resized later. + * + * NOTE: While this ensures that all attribute vector lengths are + * correct, it does not ensure that no extra attributes have + * been added to the graph. However, the presence of extra + * attributes does not make the attribute table inconsistent + * like the incorrect attribute vector lengths would. + */ + igraph_i_cattribute_revert_attribute_vector_sizes(val, origlen); + } + return err; +} + +static void igraph_i_cattribute_clear_attribute_container(igraph_vector_ptr_t *v) { + igraph_integer_t 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); +} + +typedef struct { + igraph_vector_t *numeric; + igraph_vector_bool_t *boolean; + igraph_vector_ptr_t *strings; + igraph_integer_t length; +} igraph_i_attribute_permutation_work_area_t; + +static igraph_error_t igraph_i_attribute_permutation_work_area_init( + igraph_i_attribute_permutation_work_area_t *work_area, igraph_integer_t length +) { + work_area->length = length; + work_area->numeric = NULL; + work_area->boolean = NULL; + work_area->strings = NULL; + return IGRAPH_SUCCESS; +} + +static void igraph_i_attribute_permutation_work_area_release_stored_strvectors( + igraph_i_attribute_permutation_work_area_t *work_area +) { + if (work_area->strings != NULL) { + igraph_vector_ptr_destroy_all(work_area->strings); + IGRAPH_FREE(work_area->strings); + work_area->strings = NULL; + } +} + +static void igraph_i_attribute_permutation_work_area_destroy( + igraph_i_attribute_permutation_work_area_t *work_area +) { + igraph_i_attribute_permutation_work_area_release_stored_strvectors(work_area); + if (work_area->numeric != NULL) { + igraph_vector_destroy(work_area->numeric); + IGRAPH_FREE(work_area->numeric); + work_area->numeric = NULL; + } + if (work_area->boolean != NULL) { + igraph_vector_bool_destroy(work_area->boolean); + IGRAPH_FREE(work_area->boolean); + work_area->boolean = NULL; + } +} + +static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_numeric( + igraph_i_attribute_permutation_work_area_t *work_area +) { + igraph_vector_t* vec = work_area->numeric; + + if (vec == NULL) { + vec = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_init(vec, work_area->length)); + work_area->numeric = vec; + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_boolean( + igraph_i_attribute_permutation_work_area_t *work_area +) { + igraph_vector_bool_t* vec = work_area->boolean; + + if (vec == NULL) { + vec = IGRAPH_CALLOC(1, igraph_vector_bool_t); + IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_bool_init(vec, work_area->length)); + work_area->boolean = vec; + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_strings( + igraph_i_attribute_permutation_work_area_t *work_area +) { + igraph_vector_ptr_t* vec = work_area->strings; + + if (vec == NULL) { + vec = IGRAPH_CALLOC(1, igraph_vector_ptr_t); + IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_ptr_init(vec, 0)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(vec, igraph_strvector_destroy); + work_area->strings = vec; + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_attribute_permutation_work_area_permute_and_store_strvector( + igraph_i_attribute_permutation_work_area_t *work_area, + const igraph_strvector_t *vec, + const igraph_vector_int_t *idx +) { + igraph_strvector_t *new_vec; + + new_vec = IGRAPH_CALLOC(1, igraph_strvector_t); + IGRAPH_CHECK_OOM(new_vec, "Cannot permute attributes"); + IGRAPH_FINALLY(igraph_free, new_vec); + IGRAPH_CHECK(igraph_strvector_init(new_vec, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, new_vec); + IGRAPH_CHECK(igraph_vector_ptr_push_back(work_area->strings, new_vec)); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_strvector_index(vec, new_vec, idx)); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_permute_vertices_in_place( + igraph_t *graph, const igraph_vector_int_t *idx +) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + igraph_integer_t valno = igraph_vector_ptr_size(val); + igraph_integer_t i, j; + igraph_attribute_record_t *oldrec; + igraph_vector_t *num, *num_work; + igraph_strvector_t *str, str_work; + igraph_vector_bool_t *oldbool, *bool_work; + igraph_i_attribute_permutation_work_area_t work_area; + igraph_integer_t idx_size = igraph_vector_int_size(idx); + + /* shortcut: don't allocate anything if there are no attributes */ + if (valno == 0) { + return IGRAPH_SUCCESS; + } + + /* do all the allocations that can potentially fail before we actually + * start to permute the vertices to ensure that we will not ever need to + * back out from a permutation once we've started it */ + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_init(&work_area, idx_size)); + IGRAPH_FINALLY(igraph_i_attribute_permutation_work_area_destroy, &work_area); + for (i = 0; i < valno; i++) { + oldrec = VECTOR(*val)[i]; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + IGRAPH_CHECK(igraph_vector_reserve(num, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_numeric(&work_area)); + break; + + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + IGRAPH_CHECK(igraph_vector_bool_reserve(oldbool, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_boolean(&work_area)); + break; + + case IGRAPH_ATTRIBUTE_STRING: + str = (igraph_strvector_t*) oldrec->value; + IGRAPH_CHECK(igraph_strvector_reserve(str, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_strings(&work_area)); + break; + + default: + IGRAPH_WARNING("Unknown vertex attribute ignored"); + } + } + + /* let's do string attributes first because these might need extra + * allocations that can fail. The strategy is to build new igraph_strvector_t + * instances for the permuted attributes and store them in an + * igraph_vector_ptr_t until we are done with all of them. If any of the + * allocations fail, we can destroy the igraph_vector_ptr_t safely */ + for (i = 0; i < valno; i++) { + oldrec = VECTOR(*val)[i]; + if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { + continue; + } + + str = (igraph_strvector_t*) oldrec->value; + IGRAPH_CHECK( + igraph_i_attribute_permutation_work_area_permute_and_store_strvector( + &work_area, str, idx + ) + ); + } + + /* strings are done, and now all vectors involved in the process are + * as large as they should be (or larger) so the operations below are not + * supposed to fail. We can safely replace the original string attribute + * vectors with the permuted ones, and then proceed to the remaining + * attributes */ + for (i = 0, j = 0; i < valno; i++) { + oldrec = VECTOR(*val)[i]; + if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { + continue; + } + + str = (igraph_strvector_t*) oldrec->value; + str_work = *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]); + *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]) = *str; + *str = str_work; + j++; + } + igraph_i_attribute_permutation_work_area_release_stored_strvectors(&work_area); + + for (i = 0; i < valno; i++) { + oldrec = VECTOR(*val)[i]; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + num_work = work_area.numeric; + IGRAPH_ASSERT(num_work != NULL); + IGRAPH_CHECK(igraph_vector_index(num, num_work, idx)); + work_area.numeric = num; + oldrec->value = num_work; + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + bool_work = work_area.boolean; + IGRAPH_ASSERT(bool_work != NULL); + IGRAPH_CHECK(igraph_vector_bool_index(oldbool, bool_work, idx)); + work_area.boolean = oldbool; + oldrec->value = bool_work; + break; + case IGRAPH_ATTRIBUTE_STRING: + /* nothing to do */ + break; + default: + /* already warned */ + break; + } + } + + igraph_i_attribute_permutation_work_area_destroy(&work_area); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_permute_vertices( + const igraph_t *graph, igraph_t *newgraph, const igraph_vector_int_t *idx +) { + igraph_i_cattributes_t *attr = graph->attr, *new_attr = newgraph->attr; + igraph_vector_ptr_t *val = &attr->val, *new_val = &new_attr->val; + igraph_integer_t i, valno; + + IGRAPH_ASSERT(graph == newgraph || igraph_vector_ptr_empty(new_val)); + + /* Handle in-place permutation separately */ + if (graph == newgraph) { + return igraph_i_cattribute_permute_vertices_in_place(newgraph, idx); + } + + /* New vertex attributes */ + valno = igraph_vector_ptr_size(val); + IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno)); + IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, new_rec); + new_rec->name = strdup(oldrec->name); + if (! new_rec->name) { + IGRAPH_ERROR("Cannot create vertex attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) new_rec->name); + new_rec->type = oldrec->type; + + /* 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newnum); + IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); + IGRAPH_CHECK(igraph_vector_index(num, newnum, idx)); + new_rec->value = newnum; + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newbool); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, 0); + IGRAPH_CHECK(igraph_vector_bool_index(oldbool, newbool, idx)); + new_rec->value = newbool; + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_STRVECTOR_INIT_FINALLY(newstr, 0); + IGRAPH_CHECK(igraph_strvector_index(str, newstr, idx)); + new_rec->value = newstr; + break; + default: + IGRAPH_WARNING("Unknown vertex attribute ignored"); + } + + VECTOR(*new_val)[i] = new_rec; + IGRAPH_FINALLY_CLEAN(4); + } + + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +typedef igraph_error_t igraph_cattributes_combine_num_t(const igraph_vector_t *input, + igraph_real_t *output); + +typedef igraph_error_t igraph_cattributes_combine_str_t(const igraph_strvector_t *input, + char **output); + +typedef igraph_error_t igraph_cattributes_combine_bool_t(const igraph_vector_bool_t *input, + igraph_bool_t *output); + +static igraph_error_t igraph_i_cattributes_cn_sum(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); + for (j = 0; j < n; j++) { + igraph_integer_t x = VECTOR(*idx)[j]; + s += VECTOR(*oldv)[x]; + } + VECTOR(*newv)[i] = s; + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); + for (j = 0; j < n; j++) { + igraph_integer_t x = VECTOR(*idx)[j]; + s *= VECTOR(*oldv)[x]; + } + VECTOR(*newv)[i] = s; + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); + igraph_real_t m = n > 0 ? VECTOR(*oldv)[ VECTOR(*idx)[0] ] : IGRAPH_NAN; + for (j = 1; j < n; j++) { + igraph_integer_t x = 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); + igraph_real_t m = n > 0 ? VECTOR(*oldv)[ VECTOR(*idx)[0] ] : IGRAPH_NAN; + for (j = 1; j < n; j++) { + igraph_integer_t x = 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + RNG_BEGIN(); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = IGRAPH_NAN; + } else if (n == 1) { + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ]; + } else { + igraph_integer_t r = RNG_INTEGER(0, n - 1); + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[r] ]; + } + } + + RNG_END(); + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cn_first(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = IGRAPH_NAN; + } else { + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ]; + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cn_last(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = IGRAPH_NAN; + } else { + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[n - 1] ]; + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + const igraph_vector_t *oldv = oldrec->value; + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); + igraph_real_t s = n > 0 ? 0.0 : IGRAPH_NAN; + for (j = 0; j < n; j++) { + igraph_integer_t x = 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_int_list_t *merges, + igraph_cattributes_combine_num_t *func) { + + const igraph_vector_t *oldv = oldrec->value; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + + igraph_vector_t values; + IGRAPH_VECTOR_INIT_FINALLY(&values, 0); + + for (igraph_integer_t i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + + igraph_integer_t n = igraph_vector_int_size(idx); + IGRAPH_CHECK(igraph_vector_resize(&values, n)); + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t x = VECTOR(*idx)[j]; + VECTOR(values)[j] = VECTOR(*oldv)[x]; + } + + igraph_real_t res; + IGRAPH_CHECK(func(&values, &res)); + VECTOR(*newv)[i] = res; + } + + igraph_vector_destroy(&values); + IGRAPH_FINALLY_CLEAN(3); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cb_random(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + + RNG_BEGIN(); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = 0; + } else if (n == 1) { + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ]; + } else { + igraph_integer_t r = RNG_INTEGER(0, n - 1); + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[r] ]; + } + } + + RNG_END(); + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cb_first(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = 0; + } else { + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ]; + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cb_last(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + if (n == 0) { + VECTOR(*newv)[i] = 0; + } else { + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[n - 1] ]; + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cb_all_is_true(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i, j, n, x; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + n = igraph_vector_int_size(idx); + VECTOR(*newv)[i] = 1; + for (j = 0; j < n; j++) { + x = VECTOR(*idx)[j]; + if (!VECTOR(*oldv)[x]) { + VECTOR(*newv)[i] = 0; + break; + } + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cb_any_is_true(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i, j, n, x; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + n = igraph_vector_int_size(idx); + VECTOR(*newv)[i] = 0; + for (j = 0; j < n; j++) { + x = VECTOR(*idx)[j]; + if (VECTOR(*oldv)[x]) { + VECTOR(*newv)[i] = 1; + break; + } + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cb_majority(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t * newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i, j, n, x, num_trues; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + + RNG_BEGIN(); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + + n = igraph_vector_int_size(idx); + + num_trues = 0; + for (j = 0; j < n; j++) { + x = 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_int_list_t *merges, + igraph_cattributes_combine_bool_t *func) { + + const igraph_vector_bool_t *oldv = oldrec->value; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + + igraph_vector_bool_t values; + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, 0); + + for (igraph_integer_t i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + + igraph_integer_t n = igraph_vector_int_size(idx); + IGRAPH_CHECK(igraph_vector_bool_resize(&values, n)); + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t x = VECTOR(*idx)[j]; + VECTOR(values)[j] = VECTOR(*oldv)[x]; + } + + igraph_bool_t res; + IGRAPH_CHECK(func(&values, &res)); + VECTOR(*newv)[i] = res; + } + + igraph_vector_bool_destroy(&values); + IGRAPH_FINALLY_CLEAN(3); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_strvector_t *oldv = oldrec->value; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + + RNG_BEGIN(); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + const char *tmp; + if (n == 0) { + IGRAPH_CHECK(igraph_strvector_set(newv, i, "")); + } else if (n == 1) { + tmp = igraph_strvector_get(oldv, 0); + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); + } else { + igraph_integer_t r = RNG_INTEGER(0, n - 1); + tmp = igraph_strvector_get(oldv, r); + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); + } + } + + RNG_END(); + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_strvector_t *oldv = oldrec->value; + igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + if (n == 0) { + IGRAPH_CHECK(igraph_strvector_set(newv, i, "")); + } else { + const char *tmp = igraph_strvector_get(oldv, VECTOR(*idx)[0]); + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_strvector_t *oldv = oldrec->value; + igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + if (n == 0) { + IGRAPH_CHECK(igraph_strvector_set(newv, i, "")); + } else { + const char *tmp = igraph_strvector_get(oldv, VECTOR(*idx)[n - 1]); + IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); + } + } + + IGRAPH_FINALLY_CLEAN(2); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_int_list_t *merges) { + + const igraph_strvector_t *oldv = oldrec->value; + igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + + for (i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); + size_t len = 0; + const char *tmp; + char *tmp2; + for (j = 0; j < n; j++) { + tmp = igraph_strvector_get(oldv, j); + len += strlen(tmp); + } + tmp2 = IGRAPH_CALLOC(len + 1, char); + if (!tmp2) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, tmp2); + len = 0; + for (j = 0; j < n; j++) { + tmp = igraph_strvector_get(oldv, j); + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattributes_sn_func(const igraph_attribute_record_t *oldrec, + igraph_attribute_record_t *newrec, + const igraph_vector_int_list_t *merges, + igraph_cattributes_combine_str_t *func) { + + const igraph_strvector_t *oldv = oldrec->value; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); + IGRAPH_FINALLY(igraph_free, newv); + IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + + igraph_strvector_t values; + IGRAPH_STRVECTOR_INIT_FINALLY(&values, 0); + + for (igraph_integer_t i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + + igraph_integer_t n = igraph_vector_int_size(idx); + IGRAPH_CHECK(igraph_strvector_resize(&values, n)); + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t x = VECTOR(*idx)[j]; + const char *elem = igraph_strvector_get(oldv, x); + IGRAPH_CHECK(igraph_strvector_set(newv, j, elem)); + } + + char *res; + IGRAPH_CHECK(func(&values, &res)); + IGRAPH_FINALLY(igraph_free, res); + + IGRAPH_CHECK(igraph_strvector_set(newv, i, res)); + + IGRAPH_FREE(res); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_strvector_destroy(&values); + IGRAPH_FINALLY_CLEAN(3); + newrec->value = newv; + + return IGRAPH_SUCCESS; +} + +/** + * \section c_attribute_combination_functions + * + * + * The C attribute handler supports combining the attributes of multiple + * vertices of edges into a single attribute during a vertex or edge contraction + * operation via a user-defined function. This is achieved by setting the + * type of the attribute combination to \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION + * and passing in a pointer to the custom combination function when specifying + * attribute combinations in \ref igraph_attribute_combination() or + * \ref igraph_attribute_combination_add() . For the C attribute handler, the + * signature of the function depends on the type of the underlying attribute. + * For numeric attributes, use: + * \verbatim igraph_error_t function(const igraph_vector_t *input, igraph_real_t *output); \endverbatim + * where \p input will receive a vector containing the value of the attribute + * for all the vertices or edges being combined, and \p output must be filled + * by the function to the combined value. Similarly, for Boolean attributes, the + * function takes a boolean vector in \p input and must return the combined Boolean + * value in \p output: + * \verbatim igraph_error_t function(const igraph_vector_bool_t *input, igraph_bool_t *output); \endverbatim + * For string attributes, the signature is slightly different: + * \verbatim igraph_error_t function(const igraph_strvector_t *input, char **output); \endverbatim + * In case of strings, all strings in the input vector are \em owned by igraph + * and must not be modified or freed in the combination handler. The string + * returned to the caller in \p output remains owned by the caller; igraph will + * make a copy it and store the copy in the appropriate part of the data + * structure holding the vertex or edge attributes. + * + */ + +typedef struct { + igraph_attribute_combination_type_t type; + union { + igraph_function_pointer_t as_void; + igraph_cattributes_combine_num_t *as_num; + igraph_cattributes_combine_str_t *as_str; + igraph_cattributes_combine_bool_t *as_bool; + } func; +} igraph_attribute_combination_todo_item_t; + +static igraph_error_t igraph_i_cattribute_combine_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_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; + igraph_integer_t valno = igraph_vector_ptr_size(val); + igraph_integer_t i, j, keepno = 0; + igraph_attribute_combination_todo_item_t *todo_items; + + IGRAPH_ASSERT(graph != newgraph); + IGRAPH_ASSERT(igraph_vector_ptr_empty(new_val)); + + todo_items = IGRAPH_CALLOC(valno, igraph_attribute_combination_todo_item_t); + if (!todo_items) { + IGRAPH_ERROR("Cannot combine vertex attributes", + IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, todo_items); + + for (i = 0; i < valno; i++) { + igraph_attribute_record_t *oldrec = VECTOR(*val)[i]; + const char *name = oldrec->name; + igraph_attribute_combination_type_t type; + igraph_function_pointer_t voidfunc; + IGRAPH_CHECK(igraph_attribute_combination_query(comb, name, &type, &voidfunc)); + todo_items[i].type = type; + todo_items[i].func.as_void = voidfunc; + if (type != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + keepno++; + } + } + + IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, keepno)); + IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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_todo_item_t todo_item = todo_items[i]; + igraph_attribute_type_t attr_type = oldrec->type; + + if (todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || + todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + continue; + } + + newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (!newrec) { + IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newrec); + newrec->name = strdup(name); + if (!newrec->name) { + IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) newrec->name); + newrec->type = attr_type; + + if (attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { + switch (todo_item.type) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, + todo_item.func.as_num)); + 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 (attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + switch (todo_item.type) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges, + todo_item.func.as_bool)); + 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 (attr_type == IGRAPH_ATTRIBUTE_STRING) { + switch (todo_item.type) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, + todo_item.func.as_str)); + 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); + } + + VECTOR(*new_val)[j] = newrec; + IGRAPH_FINALLY_CLEAN(2); /* newrec->name and newrec */ + + j++; + } + + IGRAPH_FREE(todo_items); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_add_edges_inner(igraph_t *graph, const igraph_vector_int_t *edges, + igraph_vector_ptr_t *nattr) { + + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + igraph_integer_t ealno = igraph_vector_ptr_size(eal); + igraph_integer_t ne = igraph_vector_int_size(edges) / 2; + igraph_integer_t origlen = igraph_ecount(graph) - ne; + igraph_integer_t nattrno = nattr == 0 ? 0 : igraph_vector_ptr_size(nattr); + igraph_vector_int_t news; + igraph_integer_t newattrs, i; + + /* First add the new attributes if any */ + newattrs = 0; + IGRAPH_VECTOR_INT_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; + igraph_integer_t j; + igraph_bool_t l = igraph_i_cattribute_find(eal, nname, &j); + if (!l) { + newattrs++; + IGRAPH_CHECK(igraph_vector_int_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 NaN/false/"" for the existing vertices for numeric, boolean and string attributes. */ + if (newattrs != 0) { + for (i = 0; i < newattrs; i++) { + igraph_attribute_record_t *tmp = VECTOR(*nattr)[ 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newrec); + newrec->type = type; + newrec->name = strdup(tmp->name); + if (!newrec->name) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newbool); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, origlen); + newrec->value = newbool; + igraph_vector_bool_fill(newbool, false); + } 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); /* LCOV_EXCL_LINE */ + } + 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 = NULL; + const char *name = oldrec->name; + igraph_integer_t j = -1; + igraph_bool_t l = false; + 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 NaN/false/"". */ + 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_int_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_int_t *edges, + igraph_vector_ptr_t *nattr) { + /* Record information needed to restore attribute vector sizes */ + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + igraph_integer_t ne = igraph_vector_int_size(edges) / 2; + igraph_integer_t origlen = igraph_ecount(graph) - ne; + + /* Attempt adding attributes */ + igraph_error_t err = igraph_i_cattribute_add_edges_inner(graph, edges, nattr); + if (err != IGRAPH_SUCCESS) { + /* If unsuccessful, revert attribute vector sizes. + * The following function assumes that all attributes vectors that + * are present have a length at least as great as origlen. + * This is true at the moment because any new attributes that are + * added to the graph are created directly at 'origlen' instead of + * being created at smaller sizes and resized later. + * + * NOTE: While this ensures that all attribute vector lengths are + * correct, it does not ensure that no extra attributes have + * been added to the graph. However, the presence of extra + * attributes does not make the attribute table inconsistent + * like the incorrect attribute vector lengths would. + */ + igraph_i_cattribute_revert_attribute_vector_sizes(eal, origlen); + } + return err; +} + +static igraph_error_t igraph_i_cattribute_permute_edges_in_place( + igraph_t *graph, const igraph_vector_int_t *idx +) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + igraph_integer_t ealno = igraph_vector_ptr_size(eal); + igraph_integer_t i, j; + igraph_attribute_record_t *oldrec; + igraph_vector_t *num, *num_work; + igraph_strvector_t *str, str_work; + igraph_vector_bool_t *oldbool, *bool_work; + igraph_i_attribute_permutation_work_area_t work_area; + igraph_integer_t idx_size = igraph_vector_int_size(idx); + + /* shortcut: don't allocate anything if there are no attributes */ + if (ealno == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_init(&work_area, idx_size)); + IGRAPH_FINALLY(igraph_i_attribute_permutation_work_area_destroy, &work_area); + for (i = 0; i < ealno; i++) { + oldrec = VECTOR(*eal)[i]; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + IGRAPH_CHECK(igraph_vector_reserve(num, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_numeric(&work_area)); + break; + + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + IGRAPH_CHECK(igraph_vector_bool_reserve(oldbool, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_boolean(&work_area)); + break; + + case IGRAPH_ATTRIBUTE_STRING: + str = (igraph_strvector_t*) oldrec->value; + IGRAPH_CHECK(igraph_strvector_reserve(str, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_strings(&work_area)); + break; + + default: + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + } + + /* let's do string attributes first because these might need extra + * allocations that can fail. The strategy is to build new igraph_strvector_t + * instances for the permuted attributes and store them in an + * igraph_vector_ptr_t until we are done with all of them. If any of the + * allocations fail, we can destroy the igraph_vector_ptr_t safely */ + for (i = 0; i < ealno; i++) { + oldrec = VECTOR(*eal)[i]; + if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { + continue; + } + + str = (igraph_strvector_t*) oldrec->value; + IGRAPH_CHECK( + igraph_i_attribute_permutation_work_area_permute_and_store_strvector( + &work_area, str, idx + ) + ); + } + + /* strings are done, and now all vectors involved in the process are + * as large as they should be (or larger) so the operations below are not + * supposed to fail. We can safely replace the original string attribute + * vectors with the permuted ones, and then proceed to the remaining + * attributes */ + for (i = 0, j = 0; i < ealno; i++) { + oldrec = VECTOR(*eal)[i]; + if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { + continue; + } + + str = (igraph_strvector_t*) oldrec->value; + str_work = *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]); + *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]) = *str; + *str = str_work; + j++; + } + igraph_i_attribute_permutation_work_area_release_stored_strvectors(&work_area); + + /* now all vectors involved in the process are as large as they should be + * (or larger) so the operations below are not supposed to fail -- except + * for string operations that still do some extra allocations and we are + * not prepared for the failures of those. This must still be fixed. */ + for (i = 0; i < ealno; i++) { + oldrec = VECTOR(*eal)[i]; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + num_work = work_area.numeric; + IGRAPH_ASSERT(num_work != NULL); + IGRAPH_CHECK(igraph_vector_index(num, num_work, idx)); + work_area.numeric = num; + oldrec->value = num_work; + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + bool_work = work_area.boolean; + IGRAPH_ASSERT(bool_work != NULL); + IGRAPH_CHECK(igraph_vector_bool_index(oldbool, bool_work, idx)); + work_area.boolean = oldbool; + oldrec->value = bool_work; + break; + case IGRAPH_ATTRIBUTE_STRING: + /* nothing to do */ + break; + default: + /* already warned */ + break; + } + } + + igraph_i_attribute_permutation_work_area_destroy(&work_area); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_t *idx) { + + igraph_i_cattributes_t *attr = graph->attr, *new_attr = newgraph->attr; + igraph_vector_ptr_t *eal = &attr->eal, *new_eal = &new_attr->eal; + igraph_integer_t i, ealno; + + IGRAPH_ASSERT(graph == newgraph || igraph_vector_ptr_empty(new_eal)); + + if (graph == newgraph) { + return igraph_i_cattribute_permute_edges_in_place(newgraph, idx); + } + + /* New edge attributes */ + ealno = igraph_vector_ptr_size(eal); + IGRAPH_ASSERT(igraph_vector_ptr_empty(new_eal)); + IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, ealno)); + IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, new_rec); + new_rec->name = strdup(oldrec->name); + if (! new_rec->name) { + IGRAPH_ERROR("Cannot create edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) new_rec->name); + new_rec->type = oldrec->type; + + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newnum); + IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); + IGRAPH_CHECK(igraph_vector_index(num, newnum, idx)); + new_rec->value = newnum; + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_STRVECTOR_INIT_FINALLY(newstr, 0); + IGRAPH_CHECK(igraph_strvector_index(str, newstr, idx)); + new_rec->value = newstr; + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newbool); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, 0); + IGRAPH_CHECK(igraph_vector_bool_index(oldbool, newbool, idx)); + new_rec->value = newbool; + break; + default: + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + VECTOR(*new_eal)[i] = new_rec; + IGRAPH_FINALLY_CLEAN(4); + } + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_combine_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_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; + igraph_integer_t ealno = igraph_vector_ptr_size(eal); + igraph_integer_t i, j, keepno = 0; + igraph_attribute_combination_todo_item_t *todo_items; + + IGRAPH_ASSERT(graph != newgraph); + IGRAPH_ASSERT(igraph_vector_ptr_empty(new_eal)); + + todo_items = IGRAPH_CALLOC(ealno, igraph_attribute_combination_todo_item_t); + if (!todo_items) { + IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, todo_items); + + 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_CHECK(igraph_attribute_combination_query(comb, name, &todo, &voidfunc)); + todo_items[i].type = todo; + todo_items[i].func.as_void = voidfunc; + if (todo != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + keepno++; + } + } + + IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, keepno)); + IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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_todo_item_t todo_item = todo_items[i]; + igraph_attribute_type_t attr_type = oldrec->type; + + if (todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || + todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + continue; + } + + newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (!newrec) { + IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newrec); + newrec->name = strdup(name); + if (! newrec->name) { + IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) newrec->name); + newrec->type = attr_type; + + if (attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { + switch (todo_item.type) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, + todo_item.func.as_num)); + 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 (attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + switch (todo_item.type) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges, + todo_item.func.as_bool)); + 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 (attr_type == IGRAPH_ATTRIBUTE_STRING) { + switch (todo_item.type) { + case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: + IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, + todo_item.func.as_str)); + 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); + } + + VECTOR(*new_eal)[j] = newrec; + IGRAPH_FINALLY_CLEAN(2); /* newrec and newrc->name */ + + j++; + } + + IGRAPH_FREE(todo_items); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_get_info(const igraph_t *graph, + igraph_strvector_t *gnames, + igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, + igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, + igraph_vector_int_t *etypes) { + + igraph_strvector_t *names[3] = { gnames, vnames, enames }; + igraph_vector_int_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 }; + igraph_integer_t i, j; + + for (i = 0; i < 3; i++) { + igraph_strvector_t *n = names[i]; + igraph_vector_int_t *t = types[i]; + igraph_vector_ptr_t *al = attr[i]; + igraph_integer_t len = igraph_vector_ptr_size(al); + + if (n) { + IGRAPH_CHECK(igraph_strvector_resize(n, len)); + } + if (t) { + IGRAPH_CHECK(igraph_vector_int_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 IGRAPH_SUCCESS; +} + +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 }; + igraph_integer_t 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 igraph_error_t igraph_i_cattribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name) { + igraph_integer_t 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; + igraph_integer_t j; + igraph_bool_t l = false; + + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + IGRAPH_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERRORF("Numeric graph attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + num = (igraph_vector_t*)rec->value; + IGRAPH_CHECK(igraph_vector_resize(value, 1)); + VECTOR(*value)[0] = VECTOR(*num)[0]; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERRORF("Boolean graph attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + log = (igraph_vector_bool_t*)rec->value; + IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); + VECTOR(*value)[0] = VECTOR(*log)[0]; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + IGRAPH_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERRORF("String graph attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + str = (igraph_strvector_t*)rec->value; + IGRAPH_CHECK(igraph_strvector_resize(value, 1)); + IGRAPH_CHECK(igraph_strvector_set(value, 0, igraph_strvector_get(str, 0))); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + IGRAPH_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERRORF("Numeric vertex attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + 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; + igraph_integer_t 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++) { + igraph_integer_t v = IGRAPH_VIT_GET(it); + VECTOR(*value)[i] = VECTOR(*num)[v]; + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERRORF("Boolean vertex attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + IGRAPH_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERRORF("String vertex attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + str = (igraph_strvector_t*)rec->value; + if (igraph_vs_is_all(&vs)) { + igraph_strvector_clear(value); + IGRAPH_CHECK(igraph_strvector_append(value, str)); + } else { + igraph_vit_t it; + igraph_integer_t 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++) { + igraph_integer_t v = IGRAPH_VIT_GET(it); + const char *s = igraph_strvector_get(str, v); + IGRAPH_CHECK(igraph_strvector_set(value, i, s)); + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + IGRAPH_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_ERRORF("Numeric edge attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + 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; + igraph_integer_t 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++) { + igraph_integer_t e = IGRAPH_EIT_GET(it); + VECTOR(*value)[i] = VECTOR(*num)[e]; + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + IGRAPH_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERRORF("String edge attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + str = (igraph_strvector_t*)rec->value; + if (igraph_es_is_all(&es)) { + igraph_strvector_clear(value); + IGRAPH_CHECK(igraph_strvector_append(value, str)); + } else { + igraph_eit_t it; + igraph_integer_t 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++) { + igraph_integer_t e = IGRAPH_EIT_GET(it); + const char *s = igraph_strvector_get(str, e); + IGRAPH_CHECK(igraph_strvector_set(value, i, s)); + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); + } + + rec = VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_ERRORF("Boolean edge attribute '%s' expected, got %s.", IGRAPH_EINVAL, name, attribute_type_name(rec->type)); + } + 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; + igraph_integer_t 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++) { + igraph_integer_t e = IGRAPH_EIT_GET(it); + VECTOR(*value)[i] = VECTOR(*log)[e]; + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/* -------------------------------------- */ + +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. This also applies to the default case + * when there is no attribute handler at all. + * + * The C attribute handler supports attaching real numbers, boolean + * values and character strings as attributes. No vector values are allowed. + * For example, vertices have a name attribute holding a single + * string value for each vertex, but it is not possible to have a coords + * attribute which is a vector of numbers per vertex. + * + * The functions documented in this section are specific to the C + * attribute handler. Code using these functions will not function when + * a different attribute handler is attached. + * + * \example examples/simple/cattributes.c + * \example examples/simple/cattributes2.c + * \example examples/simple/cattributes3.c + * \example examples/simple/cattributes4.c + */ + +/** + * \function igraph_cattribute_GAN + * \brief Query a numeric graph attribute. + * + * Returns the value of the given numeric graph attribute. + * If the attribute does not exist, a warning is issued + * and NaN is returned. + * + * \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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + IGRAPH_WARNINGF("Graph attribute '%s' does not exist, returning default numeric attribute value.", name); + return IGRAPH_NAN; + } + + rec = VECTOR(*gal)[j]; + num = (igraph_vector_t*)rec->value; + return VECTOR(*num)[0]; +} + +/** + * \function igraph_cattribute_GAB + * \brief Query a boolean graph attribute. + * + * Returns the value of the given boolean graph attribute. + * If the attribute does not exist, a warning is issued + * and false is returned. + * + * \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; + igraph_integer_t 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_WARNINGF("Graph attribute '%s' does not exist, returning default boolean attribute value.", name); + return false; + } + + rec = VECTOR(*gal)[j]; + log = (igraph_vector_bool_t*)rec->value; + return VECTOR(*log)[0]; +} + +/** + * \function igraph_cattribute_GAS + * \brief Query a string graph attribute. + * + * Returns a const pointer to the string graph attribute + * specified in \p name. The value must not be modified. + * If the attribute does not exist, a warning is issued and + * an empty string is returned. + * + * \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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); + + if (!l) { + IGRAPH_WARNINGF("Graph attribute '%s' does not exist, returning default string attribute value.", name); + return ""; + } + + rec = VECTOR(*gal)[j]; + str = (igraph_strvector_t*)rec->value; + return igraph_strvector_get(str, 0); +} + +/** + * \function igraph_cattribute_VAN + * \brief Query a numeric vertex attribute. + * + * If the attribute does not exist, a warning is issued and + * NaN is returned. See \ref igraph_cattribute_VANV() for + * an error-checked version. + * + * \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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + IGRAPH_WARNINGF("Vertex attribute '%s' does not exist, returning default numeric attribute value.", name); + return IGRAPH_NAN; + } + + rec = VECTOR(*val)[j]; + num = (igraph_vector_t*)rec->value; + return VECTOR(*num)[vid]; +} + +/** + * \function igraph_cattribute_VAB + * \brief Query a boolean vertex attribute. + * + * If the vertex attribute does not exist, a warning is issued + * and false is returned. See \ref igraph_cattribute_VABV() for + * an error-checked version. + * + * \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; + igraph_integer_t 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_WARNINGF("Vertex attribute '%s' does not exist, returning default boolean attribute value.", name); + return false; + } + + rec = VECTOR(*val)[j]; + log = (igraph_vector_bool_t*)rec->value; + return VECTOR(*log)[vid]; +} + +/** + * \function igraph_cattribute_VAS + * \brief Query a string vertex attribute. + * + * Returns a const pointer to the string vertex attribute + * specified in \p name. The value must not be modified. + * If the vertex attribute does not exist, a warning is issued and + * an empty string is returned. See \ref igraph_cattribute_VASV() + * for an error-checked version. + * + * \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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); + + if (!l) { + IGRAPH_WARNINGF("Vertex attribute '%s' does not exist, returning default string attribute value.", name); + return ""; + } + + rec = VECTOR(*val)[j]; + str = (igraph_strvector_t*)rec->value; + return igraph_strvector_get(str, vid); +} + +/** + * \function igraph_cattribute_EAN + * \brief Query a numeric edge attribute. + * + * If the attribute does not exist, a warning is issued and + * NaN is returned. See \ref igraph_cattribute_EANV() for + * an error-checked version. + * + * \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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_vector_t *num; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + IGRAPH_WARNINGF("Edge attribute '%s' does not exist, returning default numeric attribute value.", name); + return IGRAPH_NAN; + } + + rec = VECTOR(*eal)[j]; + num = (igraph_vector_t*)rec->value; + return VECTOR(*num)[eid]; +} + +/** + * \function igraph_cattribute_EAB + * \brief Query a boolean edge attribute. + * + * If the edge attribute does not exist, a warning is issued and + * false is returned. See \ref igraph_cattribute_EABV() for + * an error-checked version. + * + * \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; + igraph_integer_t 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_WARNINGF("Edge attribute '%s' does not exist, returning default boolean attribute value.", name); + return false; + } + + rec = VECTOR(*eal)[j]; + log = (igraph_vector_bool_t*)rec->value; + return VECTOR(*log)[eid]; +} + +/** + * \function igraph_cattribute_EAS + * \brief Query a string edge attribute. + * + * Returns a const pointer to the string edge attribute + * specified in \p name. The value must not be modified. + * If the edge attribute does not exist, a warning is issued and + * an empty string is returned. See \ref igraph_cattribute_EASV() for + * an error-checked version. + * + * \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; + igraph_integer_t j; + igraph_attribute_record_t *rec; + igraph_strvector_t *str; + igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); + + if (!l) { + IGRAPH_WARNINGF("Edge attribute '%s' does not exist, returning default string attribute value.", name); + return ""; + } + + rec = VECTOR(*eal)[j]; + str = (igraph_strvector_t*)rec->value; + return igraph_strvector_get(str, eid); +} + +/** + * \function igraph_cattribute_VANV + * \brief 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'. + */ + +igraph_error_t 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 + * \brief 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'. + */ + +igraph_error_t 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 + * \brief 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'. + */ + +igraph_error_t 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 + * \brief 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'. + */ + +igraph_error_t 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 + * \brief 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.) + */ + +igraph_error_t 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 + * \brief 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.) + */ + +igraph_error_t 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 + * \brief 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. + */ +igraph_error_t igraph_cattribute_list(const igraph_t *graph, + igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, igraph_vector_int_t *etypes) { + return igraph_i_cattribute_get_info(graph, gnames, gtypes, vnames, vtypes, + enames, etypes); +} + +/** + * \function igraph_cattribute_has_attr + * \brief 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, \c true if the attribute exists, \c 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 + * \brief 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). + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_GAB_set + * \brief 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). + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, log); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, 1); + VECTOR(*log)[0] = value; + rec->value = log; + IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_GAS_set + * \brief 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). + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_VAN_set + * \brief 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. + */ +igraph_error_t 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; + igraph_integer_t 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)[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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, num); + IGRAPH_VECTOR_INIT_FINALLY(num, igraph_vcount(graph)); + igraph_vector_fill(num, IGRAPH_NAN); + VECTOR(*num)[vid] = value; + rec->value = num; + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_VAB_set + * \brief 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. + */ +igraph_error_t 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; + igraph_integer_t 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)[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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, log); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, igraph_vcount(graph)); + igraph_vector_bool_fill(log, false); + VECTOR(*log)[vid] = value; + rec->value = log; + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_VAS_set + * \brief 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). + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_EAN_set + * \brief 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. + */ +igraph_error_t 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; + igraph_integer_t 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)[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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, num); + IGRAPH_VECTOR_INIT_FINALLY(num, igraph_ecount(graph)); + igraph_vector_fill(num, IGRAPH_NAN); + VECTOR(*num)[eid] = value; + rec->value = num; + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_EAB_set + * \brief 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. + */ +igraph_error_t 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; + igraph_integer_t 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)[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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, log); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, igraph_ecount(graph)); + igraph_vector_bool_fill(log, false); + VECTOR(*log)[eid] = value; + rec->value = log; + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_EAS_set + * \brief 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). + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name = strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_VAN_setv + * \brief 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. + */ + +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + num = IGRAPH_CALLOC(1, igraph_vector_t); + if (!num) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, num); + rec->value = num; + IGRAPH_CHECK(igraph_vector_init_copy(num, v)); + IGRAPH_FINALLY(igraph_vector_destroy, num); + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} +/** + * \function igraph_cattribute_VAB_setv + * \brief 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. + */ + +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, log); + rec->value = log; + IGRAPH_CHECK(igraph_vector_bool_init_copy(log, v)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, log); + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_VAS_setv + * \brief 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. + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + str = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, str); + rec->value = str; + IGRAPH_CHECK(igraph_strvector_init_copy(str, sv)); + IGRAPH_FINALLY(igraph_strvector_destroy, str); + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_EAN_setv + * \brief 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. + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + num = IGRAPH_CALLOC(1, igraph_vector_t); + if (!num) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, num); + rec->value = num; + IGRAPH_CHECK(igraph_vector_init_copy(num, v)); + IGRAPH_FINALLY(igraph_vector_destroy, num); + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_EAB_setv + * \brief 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. + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, log); + rec->value = log; + IGRAPH_CHECK(igraph_vector_bool_init_copy(log, v)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, log); + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_cattribute_EAS_setv + * \brief 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. + */ +igraph_error_t 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; + igraph_integer_t 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); /* LCOV_EXCL_LINE */ + } + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + str = IGRAPH_CALLOC(1, igraph_strvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, str); + rec->value = str; + IGRAPH_CHECK(igraph_strvector_init_copy(str, sv)); + IGRAPH_FINALLY(igraph_strvector_destroy, str); + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return IGRAPH_SUCCESS; +} + +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 + * \brief 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; + igraph_integer_t 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 + * \brief 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; + igraph_integer_t 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 + * \brief 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; + igraph_integer_t 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 + * \brief 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; + igraph_integer_t 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; + igraph_integer_t 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; + igraph_integer_t 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/graph/graph_list.c b/src/graph/graph_list.c new file mode 100644 index 0000000..9d82ec2 --- /dev/null +++ b/src/graph/graph_list.c @@ -0,0 +1,61 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2022 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 + +*/ + +#include "igraph_graph_list.h" + +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_types.h" + +#define GRAPH_LIST +#define BASE_GRAPH +#define CUSTOM_INIT_DESTROY +#include "igraph_pmt.h" +#include "../core/typed_list.pmt" +#include "igraph_pmt_off.h" +#undef CUSTOM_INIT_DESTROY +#undef BASE_GRAPH +#undef GRAPH_LIST + +void igraph_graph_list_set_directed( + igraph_graph_list_t* list, igraph_bool_t directed +) { + IGRAPH_ASSERT(list != 0); + list->directed = directed; +} + +static igraph_error_t igraph_i_graph_list_init_item( + const igraph_graph_list_t* list, igraph_t* item +) { + return igraph_empty(item, 0, list->directed); +} + +static igraph_error_t igraph_i_graph_list_copy_item( + igraph_t* dest, const igraph_t* source +) { + return igraph_copy(dest, source); +} + +static void igraph_i_graph_list_destroy_item(igraph_t* item) { + igraph_destroy(item); +} diff --git a/src/graph/internal.h b/src/graph/internal.h new file mode 100644 index 0000000..1e5d262 --- /dev/null +++ b/src/graph/internal.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_GRAPH_INTERNAL_H +#define IGRAPH_GRAPH_INTERNAL_H + +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_constants.h" +#include "igraph_error.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_neighbors( + const igraph_t *graph, igraph_vector_int_t *neis, igraph_integer_t pnode, + igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_incident( + const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t pnode, + igraph_neimode_t mode, igraph_loops_t loops); + +igraph_error_t igraph_i_reverse(igraph_t *graph); + +__END_DECLS + +#endif /* IGRAPH_GRAPH_INTERNAL_H */ diff --git a/src/graph/iterators.c b/src/graph/iterators.c new file mode 100644 index 0000000..4e81855 --- /dev/null +++ b/src/graph/iterators.c @@ -0,0 +1,2085 @@ +/* -*- 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_error.h" +#include "igraph_iterators.h" +#include "igraph_memory.h" +#include "igraph_interface.h" +#include "igraph_types.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). + */ + +igraph_error_t igraph_vs_all(igraph_vs_t *vs) { + vs->type = IGRAPH_VS_ALL; + return IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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 + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_error_t igraph_vs_none(igraph_vs_t *vs) { + vs->type = IGRAPH_VS_NONE; + return IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_error_t igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid) { + vs->type = IGRAPH_VS_1; + vs->data.vid = vid; + return IGRAPH_SUCCESS; +} + +/** + * \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 an \type igraph_vector_int_t + * temporarily as a vertex selector. The vertex selector should be + * thought of as a \em view into the vector. If you make changes to + * the vector that also affects the vertex selector. Destroying the + * vertex selector does not destroy the vector. Do not destroy the + * vector before destroying the vertex selector, or you might get + * strange behavior. Since selectors are not tied to any specific + * graph, this function does not check whether the vertex IDs in + * the vector are valid. + * + * \param vs Pointer to an uninitialized vertex selector. + * \param v Pointer to a \type igraph_vector_int_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 + */ + +igraph_error_t igraph_vs_vector(igraph_vs_t *vs, + const igraph_vector_int_t *v) { + vs->type = IGRAPH_VS_VECTORPTR; + vs->data.vecptr = v; + return IGRAPH_SUCCESS; +} + +/** + * \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_int_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_int_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 few + * 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 value of type + * \type int so you cannot supply arbitrarily large (too + * large for \type 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. + */ + +igraph_error_t igraph_vs_vector_small(igraph_vs_t *vs, ...) { + va_list ap; + igraph_integer_t i, n = 0; + igraph_vector_int_t* vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create vertex selector."); + IGRAPH_FINALLY(igraph_free, vec); + + va_start(ap, vs); + while (1) { + int num = va_arg(ap, int); + if (num == -1) { + break; + } + n++; + } + va_end(ap); + + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); + + va_start(ap, vs); + for (i = 0; i < n; i++) { + VECTOR(*vec)[i] = va_arg(ap, int); + } + va_end(ap); + + IGRAPH_FINALLY_CLEAN(2); + + vs->type = IGRAPH_VS_VECTOR; + vs->data.vecptr = vec; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vs_vector_copy + * \brief Vertex set based on a vector, with copying. + * + * This function makes it possible to handle an \type igraph_vector_int_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. Since selectors + * are not tied to any specific graph, this function does not check whether + * the vertex IDs in the vector are valid. + * + * \param vs Pointer to an uninitialized vertex selector. + * \param v Pointer to a \type igraph_vector_int_t object. + * \return Error code. + * \sa \ref igraph_vs_destroy() + * + * Time complexity: O(1). + */ + +igraph_error_t igraph_vs_vector_copy(igraph_vs_t *vs, const igraph_vector_int_t *v) { + igraph_vector_int_t* vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create vertex selector."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); + IGRAPH_FINALLY_CLEAN(1); + + vs->type = IGRAPH_VS_VECTOR; + vs->data.vecptr = vec; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vs_range + * \brief Vertex set, an interval of vertices. + * + * Creates a vertex selector containing all vertices with vertex ID + * equal to or bigger than \p from and smaller than \p to. Note that the + * interval is closed from the left and open from the right, following C + * conventions. + * + * \param vs Pointer to an uninitialized vertex selector object. + * \param start The first vertex ID to be included in the vertex selector. + * \param end The first vertex ID \em not to be included in the vertex selector. + * \return Error code. + * \sa \ref igraph_vss_range(), \ref igraph_vs_destroy() + * + * Time complexity: O(1). + * + * \example examples/simple/igraph_vs_range.c + */ + +igraph_error_t igraph_vs_range(igraph_vs_t *vs, igraph_integer_t start, igraph_integer_t end) { + *vs = igraph_vss_range(start, end); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vss_range + * \brief An interval of vertices (immediate version). + * + * The immediate version of \ref igraph_vs_range(). + * + * \param start The first vertex ID to be included in the vertex selector. + * \param end The first vertex ID \em not to be included in the vertex selector. + * \return Error code. + * \sa \ref igraph_vs_range() + * + * Time complexity: O(1). + */ + +igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end) { + igraph_vs_t vs; + vs.type = IGRAPH_VS_RANGE; + vs.data.range.start = start; + vs.data.range.end = end; + return vs; +} + +/** + * \function igraph_vs_seq + * \brief Vertex set, an interval of vertices with inclusive endpoints (deprecated). + * + * Creates a vertex selector containing all vertices with vertex ID + * equal to or bigger than \p from and equal to or smaller than \p to. + * Note that both endpoints are inclusive, contrary to C conventions. + * + * \deprecated-by igraph_vs_range 0.10.0 + * + * \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_vs_range(), \ref igraph_vss_seq(), \ref igraph_vs_destroy() + * + * Time complexity: O(1). + * + * \example examples/simple/igraph_vs_range.c + */ + +igraph_error_t igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to) { + *vs = igraph_vss_range(from, to + 1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vss_seq + * \brief An interval of vertices with inclusive endpoints (immediate version, deprecated). + * + * The immediate version of \ref igraph_vs_seq(). + * + * \deprecated-by igraph_vss_range 0.10.0 + * + * \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_range(), \ref igraph_vs_seq() + * + * Time complexity: O(1). + */ + +igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to) { + return igraph_vss_range(from, to + 1); +} + +/** + * \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_RANGE: + case IGRAPH_VS_NONADJ: + break; + case IGRAPH_VS_VECTOR: + igraph_vector_int_destroy((igraph_vector_int_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 \c false. + * + * \param vs Pointer to a vertex selector object. + * \return \c true if the vertex selector contains all vertices and + * \c false otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs) { + return vs->type == IGRAPH_VS_ALL; +} + +igraph_error_t igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, + igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src) { + igraph_vector_int_t *vec; + + memcpy(dest, src, sizeof(igraph_vs_t)); + + switch (dest->type) { + case IGRAPH_VS_VECTOR: + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot copy vertex selector."); + IGRAPH_FINALLY(igraph_free, &vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.vecptr)); + dest->data.vecptr = vec; + IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ + break; + default: + break; + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vs_type + * \brief Returns the type of the vertex selector. + */ +igraph_vs_type_t 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. + */ +igraph_error_t igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, + igraph_integer_t *result) { + igraph_vector_int_t vec; + igraph_bool_t *seen; + igraph_integer_t i; + igraph_integer_t vec_len; + + switch (vs->type) { + case IGRAPH_VS_NONE: + *result = 0; return IGRAPH_SUCCESS; + + case IGRAPH_VS_1: + *result = 0; + if (vs->data.vid < igraph_vcount(graph) && vs->data.vid >= 0) { + *result = 1; + } + return IGRAPH_SUCCESS; + + case IGRAPH_VS_RANGE: + *result = vs->data.range.end - vs->data.range.start; + return IGRAPH_SUCCESS; + + case IGRAPH_VS_ALL: + *result = igraph_vcount(graph); return IGRAPH_SUCCESS; + + case IGRAPH_VS_ADJ: + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode)); + *result = igraph_vector_int_size(&vec); + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + + case IGRAPH_VS_NONADJ: + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode)); + vec_len = igraph_vector_int_size(&vec); + *result = igraph_vcount(graph); + seen = IGRAPH_CALLOC(*result, igraph_bool_t); + IGRAPH_CHECK_OOM(seen, "Cannot calculate vertex selector length."); + IGRAPH_FINALLY(igraph_free, seen); + for (i = 0; i < vec_len; i++) { + if (!seen[ VECTOR(vec)[i] ]) { + (*result)--; + seen[ VECTOR(vec)[i] ] = true; + } + } + igraph_free(seen); + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; + + case IGRAPH_VS_VECTOR: + case IGRAPH_VS_VECTORPTR: + *result = igraph_vector_int_size(vs->data.vecptr); + return IGRAPH_SUCCESS; + } + + 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_range(), \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. + */ + +igraph_error_t igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_vit_t *vit) { + igraph_vector_int_t vec; + igraph_vector_int_t *vec_int; + igraph_bool_t *seen; + igraph_integer_t i, j, n; + igraph_integer_t vec_len; + + switch (vs.type) { + case IGRAPH_VS_ALL: + vit->type = IGRAPH_VIT_RANGE; + vit->pos = 0; + vit->start = 0; + vit->end = igraph_vcount(graph); + break; + case IGRAPH_VS_ADJ: + vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec_int, "Cannot create vertex iterator."); + IGRAPH_FINALLY(igraph_free, vec_int); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs.data.adj.vid, vs.data.adj.mode)); + n = igraph_vector_int_size(&vec); + IGRAPH_CHECK(igraph_vector_int_resize(vec_int, n)); + for (i = 0; i < n; i++) { + VECTOR(*vec_int)[i] = VECTOR(vec)[i]; + } + + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(3); + + vit->type = IGRAPH_VIT_VECTOR; + vit->pos = 0; + vit->start = 0; + vit->vec = vec_int; + vit->end = n; + + break; + case IGRAPH_VS_NONADJ: + vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec_int, "Cannot create vertex iterator."); + IGRAPH_FINALLY(igraph_free, vec_int); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs.data.adj.vid, vs.data.adj.mode)); + vec_len = igraph_vector_int_size(&vec); + n = igraph_vcount(graph); + seen = IGRAPH_CALLOC(n, igraph_bool_t); + IGRAPH_CHECK_OOM(seen, "Cannot create vertex iterator."); + IGRAPH_FINALLY(igraph_free, seen); + for (i = 0; i < vec_len; i++) { + if (! seen [ VECTOR(vec)[i] ] ) { + n--; + seen[ VECTOR(vec)[i] ] = true; + } + } + IGRAPH_CHECK(igraph_vector_int_resize(vec_int, n)); + for (i = 0, j = 0; j < n; i++) { + if (!seen[i]) { + VECTOR(*vec_int)[j++] = i; + } + } + + IGRAPH_FREE(seen); + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(4); + + vit->type = IGRAPH_VIT_VECTOR; + vit->pos = 0; + vit->start = 0; + vit->vec = vec_int; + vit->end = n; + break; + case IGRAPH_VS_NONE: + vit->type = IGRAPH_VIT_RANGE; + vit->pos = 0; + vit->start = 0; + vit->end = 0; + break; + case IGRAPH_VS_1: + vit->type = IGRAPH_VIT_RANGE; + 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_int_size(vit->vec); + if (!igraph_vector_int_isininterval(vit->vec, 0, igraph_vcount(graph) - 1)) { + IGRAPH_ERROR("Cannot create iterator, invalid vertex ID.", IGRAPH_EINVVID); + } + break; + case IGRAPH_VS_RANGE: + { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + if (vs.data.range.start < 0 || + vs.data.range.start > no_of_nodes || + (no_of_nodes > 0 && vs.data.range.start == no_of_nodes)) { + IGRAPH_ERROR("Cannot create range iterator, starting vertex ID out of range.", IGRAPH_EINVAL); + } + if (vs.data.range.end < 0 || vs.data.range.end > no_of_nodes) { + IGRAPH_ERROR("Cannot create range iterator, ending vertex ID out of range.", IGRAPH_EINVAL); + } + } + vit->type = IGRAPH_VIT_RANGE; + vit->pos = vs.data.range.start; + vit->start = vs.data.range.start; + vit->end = vs.data.range.end; + break; + default: + IGRAPH_ERROR("Cannot create iterator, invalid selector.", IGRAPH_EINVAL); + break; + } + return IGRAPH_SUCCESS; +} + +/** + * \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_RANGE: + case IGRAPH_VIT_VECTORPTR: + break; + case IGRAPH_VIT_VECTOR: + igraph_vector_int_destroy((igraph_vector_int_t*) vit->vec); + igraph_free((igraph_vector_int_t*) vit->vec); + break; + default: + /* IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */ + break; + } +} + +igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t *v) { + igraph_integer_t i; + + IGRAPH_CHECK(igraph_vector_int_resize(v, IGRAPH_VIT_SIZE(*vit))); + + switch (vit->type) { + case IGRAPH_VIT_RANGE: + 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 IGRAPH_SUCCESS; +} + +/*******************************************************/ + +/** + * \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: + * \clist + * \cli IGRAPH_EDGEORDER_ID + * Edge ID order; currently performs the fastest. + * \cli 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. + * \cli 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. + * \endclist + * 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). + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_error_t igraph_es_none(igraph_es_t *es) { + es->type = IGRAPH_ES_NONE; + return IGRAPH_SUCCESS; +} + +/** + * \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). + */ + +igraph_error_t igraph_es_1(igraph_es_t *es, igraph_integer_t eid) { + es->type = IGRAPH_ES_1; + es->data.eid = eid; + return IGRAPH_SUCCESS; +} + +/** + * \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 into a vector + * containing edge IDs. Do not destroy the vector before destroying + * the edge selector. Since selectors are not tied to any specific + * graph, this function does not check whether the edge IDs in + * the vector are valid. + * + * \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). + */ + +igraph_error_t igraph_es_vector(igraph_es_t *es, const igraph_vector_int_t *v) { + es->type = IGRAPH_ES_VECTORPTR; + es->data.vecptr = v; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_es_vector_copy + * \brief Edge set, based on a vector, with copying. + * + * This function makes it possible to handle an \type igraph_vector_int_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. Since selectors + * are not tied to any specific graph, this function does not check + * whether the edge IDs in the vector are valid. + * + * \param es Pointer to an uninitialized edge selector. + * \param v Pointer to a \type igraph_vector_int_t object. + * \return Error code. + * \sa \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +igraph_error_t igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_int_t *v) { + igraph_vector_int_t* vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); + IGRAPH_FINALLY_CLEAN(1); + + es->type = IGRAPH_ES_VECTOR; + es->data.vecptr = vec; + + return IGRAPH_SUCCESS; +} + +/** + * \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_int_t *v) { + igraph_es_t es; + es.type = IGRAPH_ES_VECTORPTR; + es.data.vecptr = v; + return es; +} + +/** + * \function igraph_es_range + * \brief Edge selector, a sequence of edge IDs. + * + * Creates an edge selector containing all edges with edge ID + * equal to or bigger than \p from and smaller than \p to. Note that the + * interval is closed from the left and open from the right, following C + * conventions. + * + * \param vs Pointer to an uninitialized edge selector object. + * \param start The first edge ID to be included in the edge selector. + * \param end The first edge ID \em not to be included in the edge selector. + * \return Error code. + * \sa \ref igraph_ess_range(), \ref igraph_es_destroy() + * + * Time complexity: O(1). + */ + +igraph_error_t igraph_es_range(igraph_es_t *es, igraph_integer_t start, igraph_integer_t end) { + *es = igraph_ess_range(start, end); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_ess_range + * \brief Immediate version of the sequence edge selector. + * + * \param start The first edge ID to be included in the edge selector. + * \param end The first edge ID \em not to be included in the edge selector. + * \return The initialized edge selector. + * \sa \ref igraph_es_range() + * + * Time complexity: O(1). + */ + +igraph_es_t igraph_ess_range(igraph_integer_t start, igraph_integer_t end) { + igraph_es_t es; + es.type = IGRAPH_ES_RANGE; + es.data.range.start = start; + es.data.range.end = end; + return es; +} + +/** + * \function igraph_es_seq + * \brief Edge selector, a sequence of edge IDs, with inclusive endpoints (deprecated). + * + * All edge IDs between \p from and \p to (inclusive) will be + * included in the edge selection. + * + * \deprecated-by igraph_es_range 0.10.0 + * + * \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). + */ + +igraph_error_t igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to) { + *es = igraph_ess_range(from, to + 1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_ess_seq + * \brief Immediate version of the sequence edge selector, with inclusive endpoints. + * + * \deprecated-by igraph_ess_range 0.10.0 + * + * \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) { + return igraph_ess_range(from, to + 1); +} + +/** + * \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 + */ + +igraph_error_t igraph_es_pairs(igraph_es_t *es, const igraph_vector_int_t *v, + igraph_bool_t directed) { + igraph_vector_int_t* vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); + IGRAPH_FINALLY_CLEAN(1); + + es->type = IGRAPH_ES_PAIRS; + es->data.path.mode = directed; + es->data.path.ptr = vec; + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * 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 es Pointer to an uninitialized edge selector object. + * \param directed Whether the graph is directed or not. + * \param ... The additional arguments give the edges to be included in the + * selector, as pairs of vertex IDs. The last argument must be -1. + * The \p first parameter is present for technical reasons and represents + * the first variadic argument. + * \return Error code. + * \sa \ref igraph_es_pairs(), \ref igraph_es_destroy() + * + * Time complexity: O(n), the number of edges being selected. + */ + +igraph_error_t igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, int first, ...) { + va_list ap; + igraph_integer_t i, n = 0; + igraph_vector_int_t *vec; + int num; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + + va_start(ap, first); + num = first; + while (num != -1) { + n++; + num = va_arg(ap, int); + } + va_end(ap); + + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); + + if (n > 0) { + va_start(ap, first); + VECTOR(*vec)[0] = first; + for (i = 1; i < n; i++) { + VECTOR(*vec)[i] = va_arg(ap, int); + } + va_end(ap); + } + + IGRAPH_FINALLY_CLEAN(2); + + es->type = IGRAPH_ES_PAIRS; + es->data.path.mode = directed; + es->data.path.ptr = vec; + + return IGRAPH_SUCCESS; +} + +/** + * \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 IDs 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. + */ +igraph_error_t igraph_es_path(igraph_es_t *es, const igraph_vector_int_t *v, + igraph_bool_t directed) { + igraph_vector_int_t *vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); + IGRAPH_FINALLY_CLEAN(1); + + es->type = IGRAPH_ES_PATH; + es->data.path.mode = directed; + es->data.path.ptr = vec; + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, int first, ...) { + va_list ap; + igraph_integer_t i, n = 0; + igraph_vector_int_t *vec; + int num; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + + va_start(ap, first); + num = first; + while (num != -1) { + n++; + num = va_arg(ap, int); + } + va_end(ap); + + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); + + if (n > 0) { + va_start(ap, first); + VECTOR(*vec)[0] = first; + for (i = 1; i < n; i++) { + VECTOR(*vec)[i] = va_arg(ap, int); + } + va_end(ap); + } + + IGRAPH_FINALLY_CLEAN(2); + + es->type = IGRAPH_ES_PATH; + es->data.path.mode = directed; + es->data.path.ptr = vec; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_es_all_between + * \brief Edge selector, all edge IDs between a pair of vertices. + * + * This function takes a pair of vertices and creates a selector that matches + * all edges between those vertices. + * + * \param es Pointer to an uninitialized edge selector object. + * \param from The ID of the source vertex. + * \param to The ID of the target vertex. + * \param direectd 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(1). + */ +igraph_error_t igraph_es_all_between( + igraph_es_t *es, igraph_integer_t from, igraph_integer_t to, + igraph_bool_t directed +) { + es->type = IGRAPH_ES_ALL_BETWEEN; + es->data.between.from = from; + es->data.between.to = to; + es->data.between.directed = directed; + return IGRAPH_SUCCESS; +} + +/** + * \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_RANGE: + case IGRAPH_ES_ALL_BETWEEN: + break; + case IGRAPH_ES_VECTOR: + igraph_vector_int_destroy((igraph_vector_int_t*)es->data.vecptr); + IGRAPH_FREE(es->data.vecptr); + break; + case IGRAPH_ES_PAIRS: + case IGRAPH_ES_PATH: + igraph_vector_int_destroy((igraph_vector_int_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 \c true if \p es was created with \ref + * igraph_es_all() or \ref igraph_ess_all(), and \c false 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() + */ +igraph_error_t igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src) { + igraph_vector_int_t *vec; + + memcpy(dest, src, sizeof(igraph_es_t)); + switch (dest->type) { + case IGRAPH_ES_VECTOR: + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot copy edge selector."); + IGRAPH_FINALLY(igraph_free, &vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.vecptr)); + dest->data.vecptr = vec; + IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ + break; + case IGRAPH_ES_PATH: + case IGRAPH_ES_PAIRS: + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot copy edge selector."); + IGRAPH_FINALLY(igraph_free, &vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.path.ptr)); + dest->data.path.ptr = vec; + IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ + break; + default: + break; + } + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, + igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_es_type + * \brief Returns the type of the edge selector. + */ +igraph_es_type_t igraph_es_type(const igraph_es_t *es) { + return es->type; +} + +static igraph_error_t igraph_i_es_pairs_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result); +static igraph_error_t igraph_i_es_path_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result); +static igraph_error_t igraph_i_es_all_between_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. + */ +igraph_error_t igraph_es_size(const igraph_t *graph, const igraph_es_t *es, + igraph_integer_t *result) { + igraph_vector_int_t v; + + switch (es->type) { + case IGRAPH_ES_ALL: + *result = igraph_ecount(graph); + return IGRAPH_SUCCESS; + + case IGRAPH_ES_ALLFROM: + *result = igraph_ecount(graph); + return IGRAPH_SUCCESS; + + case IGRAPH_ES_ALLTO: + *result = igraph_ecount(graph); + return IGRAPH_SUCCESS; + + case IGRAPH_ES_INCIDENT: + IGRAPH_VECTOR_INT_INIT_FINALLY(&v, 0); + IGRAPH_CHECK(igraph_incident(graph, &v, + es->data.incident.vid, es->data.incident.mode)); + *result = igraph_vector_int_size(&v); + igraph_vector_int_destroy(&v); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + + case IGRAPH_ES_NONE: + *result = 0; + return IGRAPH_SUCCESS; + + case IGRAPH_ES_1: + if (es->data.eid < igraph_ecount(graph) && es->data.eid >= 0) { + *result = 1; + } else { + *result = 0; + } + return IGRAPH_SUCCESS; + + case IGRAPH_ES_VECTOR: + case IGRAPH_ES_VECTORPTR: + *result = igraph_vector_int_size(es->data.vecptr); + return IGRAPH_SUCCESS; + + case IGRAPH_ES_RANGE: + *result = es->data.range.end - es->data.range.start; + return IGRAPH_SUCCESS; + + case IGRAPH_ES_PAIRS: + IGRAPH_CHECK(igraph_i_es_pairs_size(graph, es, result)); + return IGRAPH_SUCCESS; + + case IGRAPH_ES_PATH: + IGRAPH_CHECK(igraph_i_es_path_size(graph, es, result)); + return IGRAPH_SUCCESS; + + case IGRAPH_ES_ALL_BETWEEN: + IGRAPH_CHECK(igraph_i_es_all_between_size(graph, es, result)); + return IGRAPH_SUCCESS; + + default: + IGRAPH_ERROR("Cannot calculate selector length, invalid selector type.", + IGRAPH_EINVAL); + } +} + +static igraph_error_t igraph_i_es_pairs_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result) { + igraph_integer_t i, n = igraph_vector_int_size(es->data.path.ptr); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (n % 2 != 0) { + IGRAPH_ERROR("Cannot calculate edge selector length from odd number of vertices.", + IGRAPH_EINVAL); + } + if (!igraph_vector_int_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot calculate edge selector length.", IGRAPH_EINVVID); + } + + *result = n / 2; + /* Check for the existence of all edges */ + for (i = 0; i < *result; i++) { + igraph_integer_t from = VECTOR(*es->data.path.ptr)[2 * i]; + igraph_integer_t to = VECTOR(*es->data.path.ptr)[2 * i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es->data.path.mode, + /*error=*/ 1)); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_es_path_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result) { + igraph_integer_t i, n = igraph_vector_int_size(es->data.path.ptr); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (!igraph_vector_int_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 = n - 1; + } + for (i = 0; i < *result; i++) { + igraph_integer_t from = VECTOR(*es->data.path.ptr)[i]; + igraph_integer_t to = VECTOR(*es->data.path.ptr)[i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es->data.path.mode, + /*error=*/ 1)); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_es_all_between_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t from = es->data.between.from; + igraph_integer_t to = es->data.between.to; + igraph_bool_t directed = es->data.between.directed; + igraph_vector_int_t vec; + + if (from < 0 || from >= no_of_nodes || to < 0 || to >= no_of_nodes) { + IGRAPH_ERROR("Cannot calculate selector length.", IGRAPH_EINVVID); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_get_all_eids_between(graph, &vec, from, to, directed)); + *result = igraph_vector_int_size(&vec); + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/**************************************************/ + +static igraph_error_t igraph_i_eit_create_allfromto(const igraph_t *graph, + igraph_eit_t *eit, + igraph_neimode_t mode); +static igraph_error_t igraph_i_eit_create_incident(const igraph_t* graph, + igraph_es_t es, igraph_eit_t *eit); +static igraph_error_t igraph_i_eit_pairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); +static igraph_error_t igraph_i_eit_path(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); + +static igraph_error_t igraph_i_eit_create_allfromto(const igraph_t *graph, + igraph_eit_t *eit, + igraph_neimode_t mode) { + igraph_vector_int_t *vec; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec); + + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(vec, no_of_edges)); + + if (igraph_is_directed(graph)) { + igraph_vector_int_t adj; + IGRAPH_VECTOR_INT_INIT_FINALLY(&adj, 0); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_incident(graph, &adj, i, mode)); + igraph_vector_int_append(vec, &adj); /* reserved */ + } + igraph_vector_int_destroy(&adj); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_vector_int_t adj; + igraph_bool_t *added; + IGRAPH_VECTOR_INT_INIT_FINALLY(&adj, 0); + added = IGRAPH_CALLOC(no_of_edges, igraph_bool_t); + IGRAPH_CHECK_OOM(added, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, added); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_incident(graph, &adj, i, IGRAPH_ALL)); + const igraph_integer_t length = igraph_vector_int_size(&adj); + for (igraph_integer_t j = 0; j < length; j++) { + if (!added[ VECTOR(adj)[j] ]) { + igraph_vector_int_push_back(vec, VECTOR(adj)[j]); /* reserved */ + added[ VECTOR(adj)[j] ] = true; + } + } + } + igraph_vector_int_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_int_size(eit->vec); + + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_eit_create_incident(const igraph_t* graph, + igraph_es_t es, igraph_eit_t *eit) { + igraph_vector_int_t vec; + igraph_vector_int_t* vec_int; + igraph_integer_t i, n; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_incident(graph, &vec, es.data.incident.vid, es.data.incident.mode)); + + vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec_int, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec_int); + + n = igraph_vector_int_size(&vec); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, n); + + for (i = 0; i < n; i++) { + VECTOR(*vec_int)[i] = VECTOR(vec)[i]; + } + + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(3); + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->vec = vec_int; + eit->end = igraph_vector_int_size(vec_int); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_eit_pairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit) { + igraph_integer_t n = igraph_vector_int_size(es.data.path.ptr); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + igraph_vector_int_t* vec; + + if (n % 2 != 0) { + IGRAPH_ERROR("Cannot create edge iterator from odd number of vertices.", + IGRAPH_EINVAL); + } + if (!igraph_vector_int_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot create edge iterator.", IGRAPH_EINVVID); + } + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n / 2); + + for (i = 0; i < n / 2; i++) { + igraph_integer_t from = VECTOR(*es.data.path.ptr)[2 * i]; + igraph_integer_t to = VECTOR(*es.data.path.ptr)[2 * i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es.data.path.mode, + /*error=*/ 1)); + VECTOR(*vec)[i] = eid; + } + + IGRAPH_FINALLY_CLEAN(2); + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->end = n / 2; + eit->vec = vec; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_eit_path(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit) { + igraph_integer_t n = igraph_vector_int_size(es.data.path.ptr); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, len; + igraph_vector_int_t* vec; + + if (!igraph_vector_int_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; + } + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec); + + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, len); + + for (i = 0; i < len; i++) { + igraph_integer_t from = VECTOR(*es.data.path.ptr)[i]; + igraph_integer_t to = VECTOR(*es.data.path.ptr)[i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es.data.path.mode, + /*error=*/ 1)); + VECTOR(*vec)[i] = eid; + } + + IGRAPH_FINALLY_CLEAN(2); + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->end = len; + eit->vec = vec; + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_eit_all_between( + const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit +) { + igraph_integer_t from = es.data.between.from; + igraph_integer_t to = es.data.between.to; + igraph_bool_t directed = es.data.between.directed; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t* vec; + + if (from < 0 || from >= no_of_nodes || to < 0 || to >= no_of_nodes) { + IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID); + } + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, 0); + IGRAPH_CHECK(igraph_get_all_eids_between(graph, vec, from, to, directed)); + IGRAPH_FINALLY_CLEAN(2); + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->end = igraph_vector_int_size(vec); + eit->vec = vec; + + return IGRAPH_SUCCESS; +} + +/** + * \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(), \ref igraph_es_vector(), \ref igraph_es_range() it is + * O(1). For \ref igraph_es_incident() it is O(d) where d is the number of + * incident edges of the vertex. + */ + +igraph_error_t 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_RANGE; + 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: + IGRAPH_CHECK(igraph_i_eit_create_incident(graph, es, eit)); + break; + case IGRAPH_ES_NONE: + eit->type = IGRAPH_EIT_RANGE; + eit->pos = 0; + eit->start = 0; + eit->end = 0; + break; + case IGRAPH_ES_1: + eit->type = IGRAPH_EIT_RANGE; + 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_int_size(eit->vec); + if (!igraph_vector_int_isininterval(eit->vec, 0, igraph_ecount(graph) - 1)) { + IGRAPH_ERROR("Cannot create iterator, invalid edge ID.", IGRAPH_EINVAL); + } + break; + case IGRAPH_ES_RANGE: + { + igraph_integer_t no_of_edges = igraph_ecount(graph); + if (es.data.range.start < 0 || + es.data.range.start > no_of_edges || + (no_of_edges > 0 && es.data.range.start == no_of_edges)) { + IGRAPH_ERROR("Cannot create range iterator, starting edge ID out of range.", IGRAPH_EINVAL); + } + if (es.data.range.end < 0 || es.data.range.end > no_of_edges) { + IGRAPH_ERROR("Cannot create range iterator, ending edge ID out of range.", IGRAPH_EINVAL); + } + } + eit->type = IGRAPH_EIT_RANGE; + eit->pos = es.data.range.start; + eit->start = es.data.range.start; + eit->end = es.data.range.end; + break; + case IGRAPH_ES_PAIRS: + IGRAPH_CHECK(igraph_i_eit_pairs(graph, es, eit)); + break; + case IGRAPH_ES_PATH: + IGRAPH_CHECK(igraph_i_eit_path(graph, es, eit)); + break; + case IGRAPH_ES_ALL_BETWEEN: + IGRAPH_CHECK(igraph_i_eit_all_between(graph, es, eit)); + break; + default: + IGRAPH_ERROR("Cannot create iterator, invalid selector.", IGRAPH_EINVAL); + break; + } + return IGRAPH_SUCCESS; +} + +/** + * \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_RANGE: + case IGRAPH_EIT_VECTORPTR: + break; + case IGRAPH_EIT_VECTOR: + igraph_vector_int_destroy((igraph_vector_int_t*)eit->vec); + igraph_free((igraph_vector_int_t*)eit->vec); + break; + default: + /* IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */ + break; + } +} + +igraph_error_t igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_int_t *v) { + + igraph_integer_t i; + + IGRAPH_CHECK(igraph_vector_int_resize(v, IGRAPH_EIT_SIZE(*eit))); + + switch (eit->type) { + case IGRAPH_EIT_RANGE: + 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 IGRAPH_SUCCESS; +} diff --git a/src/graph/type_common.c b/src/graph/type_common.c new file mode 100644 index 0000000..da01c83 --- /dev/null +++ b/src/graph/type_common.c @@ -0,0 +1,207 @@ +/* -*- 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_datatype.h" +#include "igraph_interface.h" + +/* Internal functions */ + +/* The functions in this file are sensible "default" implementations for some + * of the core API functions that simply call other core API functions. If + * you are implementing your own data type, chances are that you can use these + * as is. */ + +/** + * \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/creation.c + */ +igraph_error_t 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_delete_vertices + * \brief Removes some 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 + */ +igraph_error_t igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices) { + return igraph_delete_vertices_idx(graph, vertices, /* idx= */ 0, /* invidx= */ 0); +} + +/** + * \function igraph_edge + * \brief Returns 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 (source) of + * the edge will be placed here. + * \param to Pointer to an \type igraph_integer_t. The head (target) of the + * edge will be placed here. + * \return Error code. + * + * \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). + */ +igraph_error_t igraph_edge( + const igraph_t *graph, igraph_integer_t eid, + igraph_integer_t *from, igraph_integer_t *to +) { + + if (eid < 0 || eid >= igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid edge ID when retrieving edge endpoints.", IGRAPH_EINVAL); + } + + 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() 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. + */ +igraph_error_t igraph_edges(const igraph_t *graph, igraph_es_t eids, igraph_vector_int_t *edges) { + igraph_eit_t eit; + igraph_integer_t 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_int_resize(edges, n * 2)); + + if (igraph_is_directed(graph)) { + for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + igraph_integer_t 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)) { + igraph_integer_t 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; +} + +/** + * \function igraph_invalidate_cache + * \brief Invalidates the internal cache of an igraph graph. + * + * + * igraph graphs cache some basic properties about themselves in an internal + * data structure. This function invalidates the contents of the cache and + * forces a recalculation of the cached properties the next time they are + * needed. + * + * + * You should not need to call this function during normal usage; however, we + * might ask you to call this function explicitly if we suspect that you are + * running into a bug in igraph's cache handling. A tell-tale sign of an invalid + * cache entry is that the result of a cached igraph function (such as + * \ref igraph_is_dag() or \ref igraph_is_simple()) is different before and + * after a cache invalidation. + * + * \param graph The graph whose cache is to be invalidated. + * + * Time complexity: O(1). + */ +void igraph_invalidate_cache(const igraph_t* graph) { + igraph_i_property_cache_invalidate_all(graph); +} diff --git a/src/graph/type_indexededgelist.c b/src/graph/type_indexededgelist.c new file mode 100644 index 0000000..73fdd69 --- /dev/null +++ b/src/graph/type_indexededgelist.c @@ -0,0 +1,1905 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-2021 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 + +*/ + +#include "igraph_datatype.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "graph/attributes.h" +#include "graph/caching.h" +#include "graph/internal.h" +#include "math/safe_intop.h" + +/* Internal functions */ + +static igraph_error_t igraph_i_create_start_vectors( + igraph_vector_int_t *res, igraph_vector_int_t *el, + igraph_vector_int_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. + * + * This section lists all the functions and macros that are considered + * as part of the core API from the point of view of the \em users + * of igraph. Some of these functions and macros have sensible default + * implementations that simply call some other core function (e.g., + * \ref igraph_empty() calls \ref igraph_empty_attrs() with a null attribute + * table pointer). If you wish to experiment with implementing an alternative + * data type, the actual number of functions that you need to replace is lower + * as you can rely on the same default implementations in most cases. + */ + +/** + * \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(). + * + * + * This function does not set any vertex attributes. To create a graph which has + * vertex attributes, call this function specifying 0 vertices, then use + * \ref igraph_add_vertices() to add vertices and their attributes. + * + * \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 graph attributes. Supply \c NULL if not graph attributes + * are to be set. + * \return Error code: + * \c IGRAPH_EINVAL: invalid number of vertices. + * + * \sa \ref igraph_empty() to create an empty graph without attributes; + * \ref igraph_add_vertices() and \ref igraph_add_edges() to add vertices + * and edges, possibly with associated attributes. + * + * Time complexity: O(|V|) for a graph with + * |V| vertices (and no edges). + */ +igraph_error_t igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr) { + + if (n < 0) { + IGRAPH_ERROR("Number of vertices must not be negative.", IGRAPH_EINVAL); + } + + graph->n = 0; + graph->directed = directed; + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->from, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->to, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->oi, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->ii, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->os, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->is, 1); + + /* init cache */ + graph->cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); + IGRAPH_CHECK_OOM(graph->cache, "Cannot create graph."); + IGRAPH_FINALLY(igraph_free, graph->cache); + IGRAPH_CHECK(igraph_i_property_cache_init(graph->cache)); + IGRAPH_FINALLY(igraph_i_property_cache_destroy, graph->cache); + + 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(8); + return IGRAPH_SUCCESS; +} + +/** + * \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_i_property_cache_destroy(graph->cache); + IGRAPH_FREE(graph->cache); + + igraph_vector_int_destroy(&graph->from); + igraph_vector_int_destroy(&graph->to); + igraph_vector_int_destroy(&graph->oi); + igraph_vector_int_destroy(&graph->ii); + igraph_vector_int_destroy(&graph->os); + igraph_vector_int_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 + */ + +igraph_error_t igraph_copy(igraph_t *to, const igraph_t *from) { + to->n = from->n; + to->directed = from->directed; + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->from, &from->from)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->from); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->to, &from->to)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->to); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->oi, &from->oi)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->oi); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->ii, &from->ii)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->ii); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->os, &from->os)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->os); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->is, &from->is)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->is); + + to->cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); + IGRAPH_CHECK_OOM(to->cache, "Cannot copy graph."); + IGRAPH_FINALLY(igraph_free, to->cache); + IGRAPH_CHECK(igraph_i_property_cache_copy(to->cache, from->cache)); + IGRAPH_FINALLY(igraph_i_property_cache_destroy, to->cache); + + IGRAPH_I_ATTRIBUTE_COPY(to, from, true, true, true); /* does IGRAPH_CHECK */ + + IGRAPH_FINALLY_CLEAN(8); + return IGRAPH_SUCCESS; +} + +/** + * \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 \ref 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. You can supply a null pointer + * here if you do not need edge attributes. + * \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/creation.c + */ +igraph_error_t igraph_add_edges(igraph_t *graph, const igraph_vector_int_t *edges, + void *attr) { + igraph_integer_t no_of_edges = igraph_vector_int_size(&graph->from); + igraph_integer_t edges_to_add = igraph_vector_int_size(edges) / 2; + igraph_integer_t new_no_of_edges; + igraph_integer_t i = 0; + igraph_vector_int_t newoi, newii; + igraph_bool_t directed = igraph_is_directed(graph); + + if (igraph_vector_int_size(edges) % 2 != 0) { + IGRAPH_ERROR("Invalid (odd) length of edges vector.", IGRAPH_EINVEVECTOR); + } + if (!igraph_vector_int_isininterval(edges, 0, igraph_vcount(graph) - 1)) { + IGRAPH_ERROR("Out-of-range vertex IDs when adding edges.", IGRAPH_EINVVID); + } + + /* from & to */ + IGRAPH_SAFE_ADD(no_of_edges, edges_to_add, &new_no_of_edges); + if (new_no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERRORF("Maximum edge count (%" IGRAPH_PRId ") exceeded.", IGRAPH_ERANGE, + IGRAPH_ECOUNT_MAX); + } + IGRAPH_CHECK(igraph_vector_int_reserve(&graph->from, no_of_edges + edges_to_add)); + IGRAPH_CHECK(igraph_vector_int_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_int_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_int_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ + } else { + igraph_vector_int_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_int_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ + } + } + + /* If an error occurs while the edges are being added, we make the necessary fixup + * to ensure that the graph is still in a consistent state when this function returns. + * The graph may already be on the finally stack when calling this function. We use + * a separate finally stack level to avoid its destructor from being called on error, + * so that the fixup can succeed. + */ + +#define CHECK_ERR(expr) \ + do { \ + igraph_error_t err = (expr); \ + if (err != IGRAPH_SUCCESS) { \ + igraph_vector_int_resize(&graph->from, no_of_edges); /* gets smaller, error safe */ \ + igraph_vector_int_resize(&graph->to, no_of_edges); /* gets smaller, error safe */ \ + IGRAPH_FINALLY_EXIT(); \ + IGRAPH_ERROR("Cannot add edges.", err); \ + } \ + } while (0) + + /* oi & ii */ + IGRAPH_FINALLY_ENTER(); + { + CHECK_ERR(igraph_vector_int_init(&newoi, no_of_edges)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &newoi); + CHECK_ERR(igraph_vector_int_init(&newii, no_of_edges)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &newii); + CHECK_ERR(igraph_vector_int_pair_order(&graph->from, &graph->to, &newoi, graph->n)); + CHECK_ERR(igraph_vector_int_pair_order(&graph->to, &graph->from, &newii, graph->n)); + + /* Attributes */ + if (graph->attr) { + /* TODO: Does this keep the attribute table in a consistent state upon failure? */ + CHECK_ERR(igraph_i_attribute_add_edges(graph, edges, attr)); + } + + /* os & is, its length does not change, error safe */ + igraph_i_create_start_vectors(&graph->os, &graph->from, &newoi, graph->n); + igraph_i_create_start_vectors(&graph->is, &graph->to, &newii, graph->n); + + /* everything went fine */ + igraph_vector_int_destroy(&graph->oi); + igraph_vector_int_destroy(&graph->ii); + IGRAPH_FINALLY_CLEAN(2); + + graph->oi = newoi; + graph->ii = newii; + } + IGRAPH_FINALLY_EXIT(); + +#undef CHECK_ERR + + /* modification successful, clear the cached properties of the graph. + * + * Adding one or more edges cannot make a strongly or weakly connected + * graph disconnected, so we keep those flags if they are cached as true. + * + * Adding one or more edges may turn a DAG into a non-DAG or a forest into + * a non-forest, so we can keep those flags only if they are cached as + * false. + * + * Also, adding one or more edges does not change HAS_LOOP, HAS_MULTI and + * HAS_MUTUAL if they were already true. + */ + igraph_i_property_cache_invalidate_conditionally( + graph, + /* keep_always = */ 0, + /* keep_when_false = */ + (1 << IGRAPH_PROP_IS_DAG) | (1 << IGRAPH_PROP_IS_FOREST), + /* keep_when_true = */ + (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED) | + (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | + (1 << IGRAPH_PROP_HAS_LOOP) | + (1 << IGRAPH_PROP_HAS_MULTI) | + (1 << IGRAPH_PROP_HAS_MUTUAL) + ); + + return IGRAPH_SUCCESS; +} + +/** + * \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 specifying the number of vertices to add. + * \param attr The attributes of the new vertices. You can supply a null pointer + * here if you do not need vertex attributes. + * \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/creation.c + */ +igraph_error_t igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { + igraph_integer_t ec = igraph_ecount(graph); + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t new_vc; + igraph_integer_t i; + + if (nv < 0) { + IGRAPH_ERROR("Cannot add negative number of vertices.", IGRAPH_EINVAL); + } + + IGRAPH_SAFE_ADD(graph->n, nv, &new_vc); + if (new_vc > IGRAPH_VCOUNT_MAX) { + IGRAPH_ERRORF("Maximum vertex count (%" IGRAPH_PRId ") exceeded.", IGRAPH_ERANGE, + IGRAPH_VCOUNT_MAX); + } + IGRAPH_CHECK(igraph_vector_int_reserve(&graph->os, new_vc + 1)); + IGRAPH_CHECK(igraph_vector_int_reserve(&graph->is, new_vc + 1)); + + igraph_vector_int_resize(&graph->os, new_vc + 1); /* reserved */ + igraph_vector_int_resize(&graph->is, new_vc + 1); /* reserved */ + for (i = graph->n + 1; i < new_vc + 1; i++) { + VECTOR(graph->os)[i] = ec; + VECTOR(graph->is)[i] = ec; + } + + graph->n += nv; + + /* Add attributes if necessary. This section is protected with + * FINALLY_ENTER/EXIT so that the graph would not be accidentally + * free upon error until it could be restored to a consistant state. */ + + if (graph->attr) { + igraph_error_t err; + IGRAPH_FINALLY_ENTER(); + err = igraph_i_attribute_add_vertices(graph, nv, attr); + if (err != IGRAPH_SUCCESS) { + /* Restore original vertex count on failure */ + graph->n = vc; + igraph_vector_int_resize(&graph->os, vc + 1); /* shrinks */ + igraph_vector_int_resize(&graph->is, vc + 1); /* shrinks */ + } + IGRAPH_FINALLY_EXIT(); + if (err != IGRAPH_SUCCESS) { + IGRAPH_ERROR("Cannot add vertices.", err); + } + } + + /* modification successful, clear the cached properties of the graph. + * + * Adding one or more nodes does not change the following cached properties: + * + * - IGRAPH_PROP_HAS_LOOP + * - IGRAPH_PROP_HAS_MULTI + * - IGRAPH_PROP_HAS_MUTUAL + * - IGRAPH_PROP_IS_DAG (adding a node does not create/destroy cycles) + * - IGRAPH_PROP_IS_FOREST (same) + * + * Adding one or more nodes without any edges incident on them is sure to + * make the graph disconnected (weakly or strongly), so we can keep the + * connectivity-related properties if they are currently cached as false. + * (Actually, even if they weren't cached as false, we could still set them + * to false, but we don't have that functionality yet). The only exception + * is when the graph had zero vertices and gained only one vertex, because + * it then becomes connected. That's why we have the condition below in the + * keep_when_false section. + */ + igraph_i_property_cache_invalidate_conditionally( + graph, + /* keep_always = */ + (1 << IGRAPH_PROP_HAS_LOOP) | + (1 << IGRAPH_PROP_HAS_MULTI) | + (1 << IGRAPH_PROP_HAS_MUTUAL) | + (1 << IGRAPH_PROP_IS_DAG) | + (1 << IGRAPH_PROP_IS_FOREST), + /* keep_when_false = */ + igraph_vcount(graph) >= 2 ? ( + (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | + (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED) + ) : 0, + /* keep_when_true = */ + 0 + ); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup interface + * \function igraph_delete_edges + * \brief Removes edges from a graph. + * + * + * The edges to remove are specified as an edge selector. + * + * + * This function cannot remove vertices; vertices 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 + */ +igraph_error_t igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t edges_to_remove = 0; + igraph_integer_t remaining_edges; + igraph_eit_t eit; + + igraph_vector_int_t newfrom, newto; + igraph_vector_int_t newoi, newii; + + igraph_bool_t *mark; + igraph_integer_t i, j; + + mark = IGRAPH_CALLOC(no_of_edges, igraph_bool_t); + IGRAPH_CHECK_OOM(mark, "Cannot delete edges."); + 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)) { + igraph_integer_t e = IGRAPH_EIT_GET(eit); + if (! mark[e]) { + edges_to_remove++; + mark[e] = true; + } + } + 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_INT_INIT_FINALLY(&newfrom, remaining_edges); + IGRAPH_VECTOR_INT_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]) { + VECTOR(newfrom)[j] = VECTOR(graph->from)[i]; + VECTOR(newto)[j] = VECTOR(graph->to)[i]; + j++; + } + } + + /* Create index, this might require additional memory */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&newoi, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newii, remaining_edges); + IGRAPH_CHECK(igraph_vector_int_pair_order(&newfrom, &newto, &newoi, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_pair_order(&newto, &newfrom, &newii, 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_int_t idx; + IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, remaining_edges); + for (i = 0, j = 0; i < no_of_edges; i++) { + if (! mark[i]) { + VECTOR(idx)[j++] = i; + } + } + IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, graph, &idx)); + igraph_vector_int_destroy(&idx); + IGRAPH_FINALLY_CLEAN(1); + } + + /* Ok, we've all memory needed, free the old structure */ + igraph_vector_int_destroy(&graph->from); + igraph_vector_int_destroy(&graph->to); + igraph_vector_int_destroy(&graph->oi); + igraph_vector_int_destroy(&graph->ii); + graph->from = newfrom; + graph->to = newto; + graph->oi = newoi; + graph->ii = newii; + IGRAPH_FINALLY_CLEAN(4); + + IGRAPH_FREE(mark); + IGRAPH_FINALLY_CLEAN(1); + + /* Create start vectors, no memory is needed for this */ + igraph_i_create_start_vectors(&graph->os, &graph->from, &graph->oi, no_of_nodes); + igraph_i_create_start_vectors(&graph->is, &graph->to, &graph->ii, no_of_nodes); + + /* modification successful, clear the cached properties of the graph. + * + * Deleting one or more edges cannot make a directed acyclic graph cyclic, + * or an undirected forest into a cyclic graph, so we keep those flags if + * they are cached as true. + * + * Similarly, deleting one or more edges cannot make a disconnected graph + * connected, so we keep the connectivity flags if they are cached as false. + * + * Also, if the graph had no loop edges before the deletion, it will have + * no loop edges after the deletion either. The same applies to reciprocal + * edges or multiple edges as well. + */ + igraph_i_property_cache_invalidate_conditionally( + graph, + /* keep_always = */ 0, + /* keep_when_false = */ + (1 << IGRAPH_PROP_HAS_LOOP) | + (1 << IGRAPH_PROP_HAS_MULTI) | + (1 << IGRAPH_PROP_HAS_MUTUAL) | + (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | + (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED), + /* keep_when_true = */ + (1 << IGRAPH_PROP_IS_DAG) | + (1 << IGRAPH_PROP_IS_FOREST) + ); + + /* Nothing to deallocate... */ + return IGRAPH_SUCCESS; +} + +/** + * \ingroup interface + * \function igraph_delete_vertices_idx + * \brief Removes some 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). You can use the + * \c idx argument to obtain the mapping from old vertex IDs to the new ones, + * and the \c newidx argument to obtain the reverse mapping. + * + * + * 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. + * \param idx An optional pointer to a vector that provides the mapping from + * the vertex IDs \em before the removal to the vertex IDs \em after + * the removal, \em plus one. Zero is used to represent vertices that were + * removed during the operation. You can supply \c NULL here if you are not + * interested. + * \param invidx An optional pointer to a vector that provides the mapping from + * the vertex IDs \em after the removal to the vertex IDs \em before + * the removal. You can supply \c NULL here if you are not interested. + * \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 + */ +igraph_error_t igraph_delete_vertices_idx( + igraph_t *graph, const igraph_vs_t vertices, igraph_vector_int_t *idx, + igraph_vector_int_t *invidx +) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t edge_recoding, vertex_recoding; + igraph_vector_int_t *my_vertex_recoding = &vertex_recoding; + igraph_vit_t vit; + igraph_t newgraph; + igraph_integer_t i, j; + igraph_integer_t remaining_vertices, remaining_edges; + + if (idx) { + my_vertex_recoding = idx; + IGRAPH_CHECK(igraph_vector_int_resize(idx, no_of_nodes)); + igraph_vector_int_null(idx); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_recoding, no_of_nodes); + } + + IGRAPH_VECTOR_INT_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) ) { + igraph_integer_t 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++) { + igraph_integer_t from = VECTOR(graph->from)[i]; + igraph_integer_t to = 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 = remaining_vertices; + newgraph.directed = graph->directed; + + /* allocate vectors */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.from, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.to, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.oi, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.ii, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.os, remaining_vertices + 1); + IGRAPH_VECTOR_INT_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) { + igraph_integer_t from = VECTOR(graph->from)[i]; + igraph_integer_t to = 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_int_pair_order(&newgraph.from, &newgraph.to, &newgraph.oi, + remaining_vertices)); + IGRAPH_CHECK(igraph_vector_int_pair_order(&newgraph.to, &newgraph.from, &newgraph.ii, + remaining_vertices)); + + IGRAPH_CHECK(igraph_i_create_start_vectors(&newgraph.os, &newgraph.from, + &newgraph.oi, remaining_vertices)); + IGRAPH_CHECK(igraph_i_create_start_vectors(&newgraph.is, &newgraph.to, + &newgraph.ii, remaining_vertices)); + + newgraph.cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); + IGRAPH_CHECK_OOM(newgraph.cache, "Cannot delete vertices."); + IGRAPH_FINALLY(igraph_free, newgraph.cache); + IGRAPH_CHECK(igraph_i_property_cache_init(newgraph.cache)); + IGRAPH_FINALLY(igraph_i_property_cache_destroy, newgraph.cache); + + /* attributes */ + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, + /*graph=*/ 1, /*vertex=*/0, /*edge=*/0); + + /* at this point igraph_destroy can take over the responsibility of + * deallocating the graph */ + IGRAPH_FINALLY_CLEAN(8); /* 2 for the property cache, 6 for the vectors */ + IGRAPH_FINALLY(igraph_destroy, &newgraph); + + if (newgraph.attr) { + igraph_vector_int_t iidx; + IGRAPH_VECTOR_INT_INIT_FINALLY(&iidx, remaining_vertices); + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t jj = 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_int_resize(&iidx, remaining_edges)); + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t jj = VECTOR(edge_recoding)[i]; + if (jj != 0) { + VECTOR(iidx)[ jj - 1 ] = i; + } + } + IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &iidx)); + igraph_vector_int_destroy(&iidx); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vit_destroy(&vit); + igraph_vector_int_destroy(&edge_recoding); + igraph_destroy(graph); + *graph = newgraph; + + IGRAPH_FINALLY_CLEAN(3); + + /* TODO: this is duplicate */ + if (invidx) { + IGRAPH_CHECK(igraph_vector_int_resize(invidx, remaining_vertices)); + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t newid = VECTOR(*my_vertex_recoding)[i]; + if (newid != 0) { + VECTOR(*invidx)[newid - 1] = i; + } + } + } + + if (!idx) { + igraph_vector_int_destroy(my_vertex_recoding); + IGRAPH_FINALLY_CLEAN(1); + } + + /* modification successful, clear the cached properties of the graph. + * + * Deleting one or more vertices cannot make a directed acyclic graph cyclic, + * or an undirected forest into a cyclic graph, so we keep those flags if + * they are cached as true. + * + * Also, if the graph had no loop edges before the deletion, it will have + * no loop edges after the deletion either. The same applies to reciprocal + * edges or multiple edges as well. + */ + igraph_i_property_cache_invalidate_conditionally( + graph, + /* keep_always = */ 0, + /* keep_when_false = */ + (1 << IGRAPH_PROP_HAS_LOOP) | + (1 << IGRAPH_PROP_HAS_MULTI) | + (1 << IGRAPH_PROP_HAS_MUTUAL), + /* keep_when_true = */ + (1 << IGRAPH_PROP_IS_DAG) | + (1 << IGRAPH_PROP_IS_FOREST) + ); + + return IGRAPH_SUCCESS; +} + +/** + * \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_vector_int_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. If one neighbor is connected with multiple + * edges, the neighbor will be returned multiple times. + * \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 + */ +igraph_error_t igraph_neighbors(const igraph_t *graph, igraph_vector_int_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); + } +} + +igraph_error_t igraph_i_neighbors(const igraph_t *graph, igraph_vector_int_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) \ + ) { \ + length -= n; \ + if (loops == IGRAPH_LOOPS_ONCE) { \ + last_added = -1; \ + } \ + continue; \ + } else if (multiple == IGRAPH_NO_MULTIPLE && vertex == last_added) { \ + length -= n; \ + continue; \ + } else { \ + last_added = vertex; \ + } \ + } + + igraph_integer_t length = 0, idx = 0; + igraph_integer_t i, j; + + igraph_integer_t node = pnode; + igraph_integer_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_int_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 = VECTOR(graph->os)[node + 1]; + for (i = VECTOR(graph->os)[node]; i < j; i++) { + igraph_integer_t to = VECTOR(graph->to)[ VECTOR(graph->oi)[i] ]; + DEDUPLICATE_IF_NEEDED(to, 1); + VECTOR(*neis)[idx++] = to; + } + } + + if (mode & IGRAPH_IN) { + j = VECTOR(graph->is)[node + 1]; + for (i = VECTOR(graph->is)[node]; i < j; i++) { + igraph_integer_t from = VECTOR(graph->from)[ 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. */ + igraph_integer_t j1 = VECTOR(graph->os)[node + 1]; + igraph_integer_t j2 = VECTOR(graph->is)[node + 1]; + igraph_integer_t i1 = VECTOR(graph->os)[node]; + igraph_integer_t i2 = VECTOR(graph->is)[node]; + igraph_integer_t eid1, eid2; + igraph_integer_t n1, n2; + + should_filter_duplicates = !(multiple == IGRAPH_MULTIPLE && + loops == IGRAPH_LOOPS_TWICE); + + while (i1 < j1 && i2 < j2) { + eid1 = VECTOR(graph->oi)[i1]; + eid2 = VECTOR(graph->ii)[i2]; + n1 = VECTOR(graph->to)[eid1]; + n2 = 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--; + if (loops == IGRAPH_LOOPS_ONCE) { + last_added = -1; + } + continue; + } + VECTOR(*neis)[idx++] = n2; + } + } + + while (i1 < j1) { + eid1 = VECTOR(graph->oi)[i1++]; + igraph_integer_t to = VECTOR(graph->to)[eid1]; + DEDUPLICATE_IF_NEEDED(to, 1); + VECTOR(*neis)[idx++] = to; + } + + while (i2 < j2) { + eid2 = VECTOR(graph->ii)[i2++]; + igraph_integer_t from = VECTOR(graph->from)[eid2]; + DEDUPLICATE_IF_NEEDED(from, 1); + VECTOR(*neis)[idx++] = from; + } + + } + IGRAPH_CHECK(igraph_vector_int_resize(neis, length)); + + return IGRAPH_SUCCESS; +#undef DEDUPLICATE_IF_NEEDED +} + +/** + * \ingroup internal + */ + +static igraph_error_t igraph_i_create_start_vectors( + igraph_vector_int_t *res, igraph_vector_int_t *el, + igraph_vector_int_t *iindex, igraph_integer_t nodes) { + +# define EDGE(i) (VECTOR(*el)[ VECTOR(*iindex)[(i)] ]) + + igraph_integer_t no_of_nodes; + igraph_integer_t no_of_edges; + igraph_integer_t i, j, idx; + + no_of_nodes = nodes; + no_of_edges = igraph_vector_int_size(el); + + /* result */ + + IGRAPH_CHECK(igraph_vector_int_resize(res, nodes + 1)); + + /* create the index */ + + if (no_of_edges == 0) { + /* empty graph */ + igraph_vector_int_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++) { + igraph_integer_t n = EDGE(i) - EDGE(VECTOR(*res)[idx]); + for (j = 0; j < n; j++) { + idx++; VECTOR(*res)[idx] = i; + } + } + j = EDGE(VECTOR(*res)[idx]); + for (i = 0; i < no_of_nodes - j; i++) { + idx++; VECTOR(*res)[idx] = no_of_edges; + } + } + + /* clean */ + +# undef EDGE + return IGRAPH_SUCCESS; +} + +/** + * \ingroup interface + * \function igraph_is_directed + * \brief Is this a directed graph? + * + * \param graph The graph. + * \return Logical value, \c true if the graph is directed, + * \c 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_1 + * \brief The degree of of a single vertex in the graph. + * + * This function calculates the in-, out- or total degree of a single vertex. + * For a single vertex, it is more efficient than calling \ref igraph_degree(). + * + * \param graph The graph. + * \param deg Pointer to the integer where the computed degree will be stored. + * \param vid The vertex for which the degree will be calculated. + * \param mode Defines the type of the degree for directed graphs. 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. + * + * \sa \ref igraph_degree() to compute the degree of several vertices at once. + * + * Time complexity: O(1) if \p loops is \c true, and + * O(d) otherwise, where d is the degree. + */ +igraph_error_t igraph_degree_1(const igraph_t *graph, igraph_integer_t *deg, + igraph_integer_t vid, igraph_neimode_t mode, igraph_bool_t loops) { + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + *deg = 0; + if (mode & IGRAPH_OUT) { + *deg += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); + } + if (mode & IGRAPH_IN) { + *deg += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); + } + if (! loops) { + /* When loops should not be counted, we remove their contribution from the + * previously computed degree. */ + if (mode & IGRAPH_OUT) { + for (igraph_integer_t i = VECTOR(graph->os)[vid]; i < VECTOR(graph->os)[vid + 1]; i++) { + if (VECTOR(graph->to)[ VECTOR(graph->oi)[i] ] == vid) { + *deg -= 1; + } + } + } + if (mode & IGRAPH_IN) { + for (igraph_integer_t i = VECTOR(graph->is)[vid]; i < VECTOR(graph->is)[vid + 1]; i++) { + if (VECTOR(graph->from)[ VECTOR(graph->ii)[i] ] == vid) { + *deg -= 1; + } + } + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * This function returns the result as a vector of \c igraph_integer_t + * values. In applications where \c igraph_real_t is desired, use + * \ref igraph_strength() with \c NULL weights. + * + * \param graph The graph. + * \param res Integer vector, this will contain the result. It should be + * initialized and will be resized to be the appropriate size. + * \param vids Vertex selector, giving the vertex IDs of which the degree will + * be calculated. + * \param mode Defines the type of the degree for directed graphs. 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 \p loops is \c 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; \ref igraph_degree_1() to efficiently compute the + * degree of a single vertex; \ref igraph_maxdegree() if you only need + * the largest degree. + * + * \example examples/simple/igraph_degree.c + */ +igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, + const igraph_vs_t vids, + igraph_neimode_t mode, igraph_bool_t loops) { + + igraph_integer_t nodes_to_calc; + igraph_integer_t 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("Invalid mode for degree calculation.", IGRAPH_EINVMODE); + } + + if (! loops) { + /* If the graph is known not to have loops, we can use the faster + * loops == true code path, which has O(1) complexity instead of of O(d). */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + loops = true; + } + } + + nodes_to_calc = IGRAPH_VIT_SIZE(vit); + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_CHECK(igraph_vector_int_resize(res, nodes_to_calc)); + igraph_vector_int_null(res); + + if (loops) { + if (mode & IGRAPH_OUT) { + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t 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++) { + igraph_integer_t vid = IGRAPH_VIT_GET(vit); + VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); + } + } + } else if (igraph_vs_is_all(&vids)) { /* no loops, calculating degree for all vertices */ + // When calculating degree for all vertices, iterating over edges is faster + igraph_integer_t no_of_edges = igraph_ecount(graph); + + if (mode & IGRAPH_OUT) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + if (from != IGRAPH_TO(graph, edge)) { + VECTOR(*res)[from]++; + } + } + } + if (mode & IGRAPH_IN) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + igraph_integer_t to = IGRAPH_TO(graph, edge); + if (IGRAPH_FROM(graph, edge) != to) { + VECTOR(*res)[to]++; + } + } + } + } else { /* no loops */ + if (mode & IGRAPH_OUT) { + for (IGRAPH_VIT_RESET(vit), i = 0; + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t vid = IGRAPH_VIT_GET(vit); + VECTOR(*res)[i] += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); + for (j = VECTOR(graph->os)[vid]; + j < VECTOR(graph->os)[vid + 1]; j++) { + if (VECTOR(graph->to)[ 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++) { + igraph_integer_t vid = IGRAPH_VIT_GET(vit); + VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); + for (j = VECTOR(graph->is)[vid]; + j < VECTOR(graph->is)[vid + 1]; j++) { + if (VECTOR(graph->from)[ VECTOR(graph->ii)[j] ] == vid) { + VECTOR(*res)[i] -= 1; + } + } + } + } + } /* loops */ + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* These are unsafe macros. Only supply variable names, i.e. no + expressions as parameters, otherwise nasty things can happen. + + BINSEARCH is an inline binary search in the 'edgelist' vector, which is + assumed to be sorted in the order of indices stored in the 'iindex' vector. + (So, [edgelist[iindex[x]] for x in 0..] is assumed to be sorted). 'N' must + be the same as 'end' when invoking the macro but it must be a separate + variable as we want to modify 'end' independently of 'N'. Upon exiting the + macro, 'result' is the index of the _leftmost_ item in the sorted 'edgelist' + (i.e. indexed by 'iindex') where the value was found, if it was found; + otherwise 'pos' is left intact. + + FIND_DIRECTED_EDGE looks for an edge from 'xfrom' to 'xto' in the graph, and + stores the ID of the edge in 'eid' if it is found; otherwise 'eid' is left + intact. + + FIND_UNDIRECTED_EDGE looks for an edge between 'xfrom' and 'xto' in an + undirected graph, swapping them if necessary. It stores the ID of the edge + in 'eid' if it is found; otherwise 'eid' is left intact. + */ + +#define BINSEARCH(start, end, value, iindex, edgelist, N, result, result_pos) \ + do { \ + while ((start) < (end)) { \ + igraph_integer_t mid =(start)+((end)-(start))/2; \ + igraph_integer_t e = VECTOR((iindex))[mid]; \ + if (VECTOR((edgelist))[e] < (value)) { \ + (start) = mid+1; \ + } else { \ + (end) = mid; \ + } \ + } \ + if ((start) < (N)) { \ + igraph_integer_t e = VECTOR((iindex))[(start)]; \ + if (VECTOR((edgelist))[e] == (value)) { \ + *(result) = e; \ + if (result_pos != 0) { *(result_pos) = start; } \ + } \ + } \ + } while (0) + +#define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid) \ + do { \ + igraph_integer_t start = VECTOR(graph->os)[xfrom]; \ + igraph_integer_t end = VECTOR(graph->os)[xfrom+1]; \ + igraph_integer_t N = end; \ + igraph_integer_t start2 = VECTOR(graph->is)[xto]; \ + igraph_integer_t end2 = VECTOR(graph->is)[xto+1]; \ + igraph_integer_t N2 = end2; \ + igraph_integer_t *nullpointer = NULL; \ + if (end-start < end2-start2) { \ + BINSEARCH(start, end, xto, graph->oi, graph->to, N, eid, nullpointer); \ + } else { \ + BINSEARCH(start2, end2, xfrom, graph->ii, graph->from, N2, eid, nullpointer); \ + } \ + } while (0) + +#define FIND_UNDIRECTED_EDGE(graph, from, to, eid) \ + do { \ + igraph_integer_t xfrom1 = from > to ? from : to; \ + igraph_integer_t 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 endpoints of an edge. + * + * For undirected graphs \c from and \c to are exchangeable. + * + * \param graph The graph object. + * \param eid Pointer to an integer, the edge ID will be stored here. + * If \p error is false and no edge was found, -1 + * will be returned. + * \param from The starting point of the edge. + * \param to 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. Note that invalid vertex IDs in input + * arguments (\p from or \p to) always trigger an error, + * regardless of this setting. + * \return Error code. + * \sa \ref igraph_edge() for the opposite operation, \ref igraph_get_all_eids_between() + * to retrieve all edge IDs between a pair of vertices. + * + * Time complexity: O(log (d)), where d is smaller of the out-degree + * of \c from and in-degree of \c to 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 to and the in-degree of \c from. + * + * \example examples/simple/igraph_get_eid.c + * + * Added in version 0.2. + */ + +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + + if (from < 0 || to < 0 || from >= no_of_nodes || to >= no_of_nodes) { + 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; +} + +/** + * \function igraph_get_eids + * Return edge IDs based on the adjacent vertices. + * + * The pairs of vertex IDs for which the edges are looked up are taken + * consecutively from the \c pairs vector, i.e. VECTOR(pairs)[0] + * and VECTOR(pairs)[1] specify the first pair, + * VECTOR(pairs)[2] and VECTOR(pairs)[3] the second + * pair, etc. + * + * + * If you have a sequence of vertex IDs that describe a \em path on the graph, + * use \ref igraph_expand_path_to_pairs() to convert them to a list of vertex + * pairs along the path. + * + * + * If the \c error argument is true, then it is an error to specify pairs + * of vertices that are not connected. Otherwise -1 is reported for vertex pairs + * without at least one edge between them. + * + * + * If there are multiple edges in the graph, then these are ignored; + * i.e. for a given pair of vertex IDs, igraph always returns the same edge ID, + * even if the pair appears multiple times in \c pairs. + * + * \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 to fetch the edges for. + * \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. + * + * \example examples/simple/igraph_get_eids.c + */ +igraph_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, + const igraph_vector_int_t *pairs, + igraph_bool_t directed, igraph_bool_t error) { + + igraph_integer_t n = pairs ? igraph_vector_int_size(pairs) : 0; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + igraph_integer_t eid = -1; + + if (n == 0) { + igraph_vector_int_clear(eids); + return IGRAPH_SUCCESS; + } + + if (n % 2 != 0) { + IGRAPH_ERROR("Cannot get edge IDs, invalid length of edge IDs", + IGRAPH_EINVAL); + } + + if (!igraph_vector_int_isininterval(pairs, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot get edge IDs, invalid vertex ID", IGRAPH_EINVVID); + } + + IGRAPH_CHECK(igraph_vector_int_resize(eids, n / 2)); + + if (igraph_is_directed(graph)) { + for (i = 0; i < n / 2; i++) { + igraph_integer_t from = VECTOR(*pairs)[2 * i]; + igraph_integer_t to = 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++) { + igraph_integer_t from = VECTOR(*pairs)[2 * i]; + igraph_integer_t to = 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 IGRAPH_SUCCESS; +} + +#undef FIND_DIRECTED_EDGE +#undef FIND_UNDIRECTED_EDGE + +#define FIND_ALL_DIRECTED_EDGES(graph, xfrom, xto, eidvec) \ + do { \ + igraph_integer_t start = VECTOR(graph->os)[xfrom]; \ + igraph_integer_t end = VECTOR(graph->os)[xfrom+1]; \ + igraph_integer_t N = end; \ + igraph_integer_t start2 = VECTOR(graph->is)[xto]; \ + igraph_integer_t end2 = VECTOR(graph->is)[xto+1]; \ + igraph_integer_t N2 = end2; \ + igraph_integer_t eid = -1; \ + igraph_integer_t pos = -1; \ + if (end-start < end2-start2) { \ + BINSEARCH(start, end, xto, graph->oi, graph->to, N, &eid, &pos); \ + while (pos >= 0 && pos < N) { \ + eid = VECTOR(graph->oi)[pos++]; \ + if (VECTOR(graph->to)[eid] != xto) { break; } \ + IGRAPH_CHECK(igraph_vector_int_push_back(eidvec, eid)); \ + } \ + } else { \ + BINSEARCH(start2, end2, xfrom, graph->ii, graph->from, N2, &eid, &pos); \ + while (pos >= 0 && pos < N2) { \ + eid = VECTOR(graph->ii)[pos++]; \ + if (VECTOR(graph->from)[eid] != xfrom) { break; } \ + IGRAPH_CHECK(igraph_vector_int_push_back(eidvec, eid)); \ + } \ + } \ + } while (0) + +#define FIND_ALL_UNDIRECTED_EDGES(graph, from, to, eidvec) \ + do { \ + igraph_integer_t xfrom1 = from > to ? from : to; \ + igraph_integer_t xto1 = from > to ? to : from; \ + FIND_ALL_DIRECTED_EDGES(graph, xfrom1, xto1, eidvec); \ + } while (0) + +/** + * \function igraph_get_all_eids_between + * \brief Returns all edge IDs between a pair of vertices. + * + * + * For undirected graphs \c source and \c target are exchangeable. + * + * \param graph The input graph. + * \param eids Pointer to an initialized vector, the result is stored + * here. It will be resized as needed. + * \param source The ID of the source vertex + * \param target The ID of the target vertex + * \param directed Logical scalar, whether to consider edge directions + * in directed graphs. This is ignored for undirected graphs. + * \return Error code. + * + * Time complexity: TODO + * + * \sa \ref igraph_get_eid() for a single edge. + */ +igraph_error_t igraph_get_all_eids_between( + const igraph_t *graph, igraph_vector_int_t *eids, + igraph_integer_t source, igraph_integer_t target, igraph_bool_t directed +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (source < 0 || source >= no_of_nodes) { + IGRAPH_ERROR("Cannot get edge IDs, invalid source vertex ID", IGRAPH_EINVVID); + } + + if (target < 0 || target >= no_of_nodes) { + IGRAPH_ERROR("Cannot get edge IDs, invalid target vertex ID", IGRAPH_EINVVID); + } + + igraph_vector_int_clear(eids); + + if (igraph_is_directed(graph)) { + /* look in the specified direction first */ + FIND_ALL_DIRECTED_EDGES(graph, source, target, eids); + if (!directed) { + /* look in the reverse direction as well */ + FIND_ALL_DIRECTED_EDGES(graph, target, source, eids); + } + } else { + FIND_ALL_UNDIRECTED_EDGES(graph, source, target, eids); + } + + return IGRAPH_SUCCESS; +} + +#undef FIND_DIRECTED_EDGE +#undef FIND_UNDIRECTED_EDGE +#undef BINSEARCH + +/** + * \function igraph_incident + * \brief Gives the incident edges of a vertex. + * + * \param graph The graph object. + * \param eids An initialized vector. 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. + */ + +igraph_error_t igraph_incident(const igraph_t *graph, igraph_vector_int_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); + } else { + return igraph_i_incident(graph, eids, pnode, mode, IGRAPH_LOOPS_ONCE); + } +} + +igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t pnode, + igraph_neimode_t mode, igraph_loops_t loops) { + igraph_integer_t length = 0, idx = 0; + igraph_integer_t i, j; + igraph_integer_t node = pnode; + igraph_bool_t directed = igraph_is_directed(graph); + + 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 (!directed) { + 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_int_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 (!directed || mode != IGRAPH_ALL) { + /* We did not ask for both directions; this is the easy case */ + + if (mode & IGRAPH_OUT) { + j = VECTOR(graph->os)[node + 1]; + for (i = VECTOR(graph->os)[node]; i < j; i++) { + igraph_integer_t edge = VECTOR(graph->oi)[i]; + igraph_integer_t other = VECTOR(graph->to)[edge]; + if (loops == IGRAPH_NO_LOOPS && other == pnode) { + length--; + } else { + VECTOR(*eids)[idx++] = edge; + } + } + } + + if (mode & IGRAPH_IN) { + j = VECTOR(graph->is)[node + 1]; + for (i = VECTOR(graph->is)[node]; i < j; i++) { + igraph_integer_t edge = VECTOR(graph->ii)[i]; + igraph_integer_t other = VECTOR(graph->from)[edge]; + if ((loops == IGRAPH_NO_LOOPS || (loops == IGRAPH_LOOPS_ONCE && !directed)) && other == pnode) { + length--; + } else { + VECTOR(*eids)[idx++] = edge; + } + } + } + } else { + /* both in- and out- neighbors in a directed graph, + we need to merge the two 'vectors' */ + igraph_integer_t j1 = VECTOR(graph->os)[node + 1]; + igraph_integer_t j2 = VECTOR(graph->is)[node + 1]; + igraph_integer_t i1 = VECTOR(graph->os)[node]; + igraph_integer_t i2 = VECTOR(graph->is)[node]; + igraph_integer_t eid1, eid2; + igraph_integer_t n1, n2; + igraph_bool_t seen_loop_edge = false; + + while (i1 < j1 && i2 < j2) { + eid1 = VECTOR(graph->oi)[i1]; + eid2 = VECTOR(graph->ii)[i2]; + n1 = VECTOR(graph->to)[eid1]; + n2 = VECTOR(graph->from)[eid2]; + if (n1 < n2) { + i1++; + VECTOR(*eids)[idx++] = eid1; + } else if (n1 > n2) { + i2++; + VECTOR(*eids)[idx++] = eid2; + } else if (n1 != pnode) { + /* multiple edge */ + i1++; + i2++; + VECTOR(*eids)[idx++] = eid1; + VECTOR(*eids)[idx++] = eid2; + } else { + /* loop edge */ + i1++; + i2++; + if (loops == IGRAPH_NO_LOOPS) { + length -= 2; + } else if (loops == IGRAPH_LOOPS_ONCE) { + length--; + if (!seen_loop_edge) { + VECTOR(*eids)[idx++] = eid1; + } else { + VECTOR(*eids)[idx++] = eid2; + } + seen_loop_edge = !seen_loop_edge; + } else { + VECTOR(*eids)[idx++] = eid1; + VECTOR(*eids)[idx++] = eid2; + } + } + } + + while (i1 < j1) { + eid1 = VECTOR(graph->oi)[i1++]; + VECTOR(*eids)[idx++] = eid1; + } + + while (i2 < j2) { + eid2 = VECTOR(graph->ii)[i2++]; + VECTOR(*eids)[idx++] = eid2; + } + } + IGRAPH_CHECK(igraph_vector_int_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 \ref igraph_isomorphic() to test if two graphs are isomorphic. + */ + +igraph_error_t igraph_is_same_graph(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *res) { + igraph_integer_t nv1 = igraph_vcount(graph1); + igraph_integer_t nv2 = igraph_vcount(graph2); + igraph_integer_t ne1 = igraph_ecount(graph1); + igraph_integer_t ne2 = igraph_ecount(graph2); + igraph_integer_t i, eid1, eid2; + + *res = false; /* 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 = VECTOR(graph1->ii)[i]; + eid2 = 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 = true; /* No difference was found, graphs are the same */ + return IGRAPH_SUCCESS; +} + + +/* Reverses the direction of all edges in a directed graph. + * The graph is modified in-place. + * Attributes are preserved. + */ +igraph_error_t igraph_i_reverse(igraph_t *graph) { + + /* Nothing to do for undirected graphs. */ + if (! igraph_is_directed(graph)) { + return IGRAPH_SUCCESS; + } + + igraph_vector_int_swap(&graph->to, &graph->from); + igraph_vector_int_swap(&graph->oi, &graph->ii); + igraph_vector_int_swap(&graph->os, &graph->is); + + return IGRAPH_SUCCESS; +} diff --git a/src/graph/visitors.c b/src/graph/visitors.c new file mode 100644 index 0000000..f635ca2 --- /dev/null +++ b/src/graph/visitors.c @@ -0,0 +1,660 @@ +/* + IGraph library. + Copyright (C) 2006-2023 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_visitor.h" + +#include "igraph_adjlist.h" +#include "igraph_bitset.h" +#include "igraph_interface.h" +#include "igraph_dqueue.h" +#include "igraph_stack.h" + +/** + * \function igraph_bfs + * \brief 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 parents If not a null pointer, then the id of the parent of + * each vertex is stored here. When a vertex was not visited + * during the traversal, -2 will be stored as the ID of its parent. + * When a vertex was visited during the traversal and it was one of + * the roots of the search trees, -1 will be stored as the ID of + * its parent. + * \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 as the predecessor of the vertex. + * If the vertex was not visited at all, then -2 is stored for + * the predecessor of the vertex. + * \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 as the successor of the vertex. + * If the vertex was not visited at all, then -2 is stored for + * the successor of the vertex. + * \param dist If not a null pointer, then the distance from the root of + * the current search tree is stored here for each vertex. If a + * vertex was not reached during the traversal, its distance will + * be -1 in this vector. + * \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 + */ +igraph_error_t igraph_bfs(const igraph_t *graph, + igraph_integer_t root, const igraph_vector_int_t *roots, + igraph_neimode_t mode, igraph_bool_t unreachable, + const igraph_vector_int_t *restricted, + igraph_vector_int_t *order, igraph_vector_int_t *rank, + igraph_vector_int_t *parents, + igraph_vector_int_t *pred, igraph_vector_int_t *succ, + igraph_vector_int_t *dist, igraph_bfshandler_t *callback, + void *extra) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + + igraph_error_t ret; + + igraph_dqueue_int_t Q; + igraph_integer_t actroot = 0; + igraph_bitset_t added; + + igraph_lazy_adjlist_t adjlist; + + igraph_integer_t act_rank = 0; + igraph_integer_t pred_vec = -1; + + igraph_integer_t rootpos = 0; + igraph_integer_t noroots = roots ? igraph_vector_int_size(roots) : 1; + + if (!roots && (root < 0 || root >= no_of_nodes)) { + IGRAPH_ERROR("Invalid root vertex in BFS.", IGRAPH_EINVVID); + } + + if (roots && !igraph_vector_int_isininterval(roots, 0, no_of_nodes-1)) { + IGRAPH_ERROR("Invalid root vertex in BFS.", IGRAPH_EINVVID); + } + + if (restricted && !igraph_vector_int_isininterval(restricted, 0, no_of_nodes-1)) { + IGRAPH_ERROR("Invalid vertex ID in restricted set.", IGRAPH_EINVVID); + } + + 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_BITSET_INIT_FINALLY(&added, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); + + 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) { + igraph_integer_t i, n = igraph_vector_int_size(restricted); + igraph_bitset_fill(&added, true); + for (i = 0; i < n; i++) { + igraph_integer_t v = VECTOR(*restricted)[i]; + IGRAPH_BIT_CLEAR(added, v); + } + } + + /* Resize result vectors, and fill them with the initial value. */ + +# define VINIT(v, initial) \ + if (v) { \ + IGRAPH_CHECK(igraph_vector_int_resize((v), no_of_nodes)); \ + igraph_vector_int_fill((v), initial); \ + } + + VINIT(order, -1); + VINIT(rank, -1); + VINIT(parents, -2); + VINIT(pred, -2); + VINIT(succ, -2); + VINIT(dist, -1); +# undef VINIT + + while (1) { + + /* Get the next root vertex, if any */ + + if (roots && rootpos < noroots) { + /* We are still going through the 'roots' vector */ + actroot = 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 (IGRAPH_BIT_TEST(added, actroot)) { + continue; + } + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actroot)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); + IGRAPH_BIT_SET(added, actroot); + if (parents) { + VECTOR(*parents)[actroot] = -1; + } + + pred_vec = -1; + + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t actvect = igraph_dqueue_int_pop(&Q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&Q); + igraph_integer_t succ_vec; + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect); + + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + const igraph_integer_t 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 (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t nei = VECTOR(*neis)[i]; + if (! IGRAPH_BIT_TEST(added, nei)) { + IGRAPH_BIT_SET(added, nei); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actdist + 1)); + if (parents) { + VECTOR(*parents)[nei] = actvect; + } + } + } + + succ_vec = igraph_dqueue_int_empty(&Q) + ? -1 + : igraph_dqueue_int_head(&Q); + if (callback) { + IGRAPH_CHECK_CALLBACK( + callback(graph, actvect, pred_vec, succ_vec, act_rank - 1, actdist, extra), + &ret + ); + + if (ret == IGRAPH_STOP) { + goto cleanup; + } + } + + if (succ) { + VECTOR(*succ)[actvect] = succ_vec; + } + pred_vec = actvect; + + } /* while Q !empty */ + + } /* for actroot < no_of_nodes */ + +cleanup: + + igraph_lazy_adjlist_destroy(&adjlist); + igraph_dqueue_int_destroy(&Q); + igraph_bitset_destroy(&added); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \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 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 order 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 order 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 order + * 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 set to -2 for + * vertices that were \em not visited, and -1 for the root vertex. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \example examples/simple/igraph_bfs_simple.c + */ +igraph_error_t igraph_bfs_simple( + const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, + igraph_vector_int_t *order, igraph_vector_int_t *layers, + igraph_vector_int_t *parents +) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_integer_t num_visited = 0; + igraph_vector_int_t neis; + igraph_bitset_t added; + igraph_integer_t 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 */ + + IGRAPH_BITSET_INIT_FINALLY(&added, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + + /* results */ + if (order) { + igraph_vector_int_clear(order); + } + if (layers) { + igraph_vector_int_clear(layers); + } + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + igraph_vector_int_fill(parents, -2); + } + + /* ok start with root */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, root)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + if (layers) { + IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); + } + if (order) { + IGRAPH_CHECK(igraph_vector_int_push_back(order, root)); + } + if (parents) { + VECTOR(*parents)[root] = -1; + } + num_visited++; + IGRAPH_BIT_SET(added, root); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actvect = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actvect, + mode)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); + for (igraph_integer_t i = 0; i < nei_count; i++) { + const igraph_integer_t neighbor = VECTOR(neis)[i]; + if (! IGRAPH_BIT_TEST(added, neighbor)) { + IGRAPH_BIT_SET(added, neighbor); + if (parents) { + VECTOR(*parents)[neighbor] = actvect; + } + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + if (layers && lastlayer != actdist + 1) { + IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); + } + if (order) { + IGRAPH_CHECK(igraph_vector_int_push_back(order, neighbor)); + } + num_visited++; + lastlayer = actdist + 1; + } + } /* for i in neis */ + } /* while ! dqueue_int_empty */ + + if (layers) { + IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); + } + + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&q); + igraph_bitset_destroy(&added); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_dfs + * \brief 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. The tail of + * the vector will be padded with -1 to ensure that the length of the + * vector is the same as the number of vertices, even if some vertices + * were not visited during the traversal. + * \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. The tail of the vector will be padded with -1 to ensure + * that the length of the vector is the same as the number of vertices, + * even if some vertices were not visited during the traversal. + * \param parents If not a null pointer, then the id of the parent of + * each vertex is stored here. -1 will be stored for the root of the + * search tree; -2 will be stored for vertices that were not visited. + * \param dist If not a null pointer, then the distance from the root of + * the current search tree is stored here. -1 will be stored for vertices + * that were not visited. + * \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. + */ + +igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, + igraph_neimode_t mode, igraph_bool_t unreachable, + igraph_vector_int_t *order, + igraph_vector_int_t *order_out, igraph_vector_int_t *parents, + igraph_vector_int_t *dist, igraph_dfshandler_t *in_callback, + igraph_dfshandler_t *out_callback, + void *extra) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_lazy_adjlist_t adjlist; + igraph_stack_int_t stack; + igraph_bitset_t added; + igraph_vector_int_t nptr; + igraph_error_t ret; + igraph_integer_t act_rank = 0; + igraph_integer_t rank_out = 0; + igraph_integer_t 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_BITSET_INIT_FINALLY(&added, no_of_nodes); + IGRAPH_STACK_INT_INIT_FINALLY(&stack, 100); + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&nptr, no_of_nodes); + +# define FREE_ALL() do { \ + igraph_vector_int_destroy(&nptr); \ + igraph_lazy_adjlist_destroy(&adjlist); \ + igraph_stack_int_destroy(&stack); \ + igraph_bitset_destroy(&added); \ + IGRAPH_FINALLY_CLEAN(4); } while (0) + + /* Resize result vectors and fill them with the initial value */ + +# define VINIT(v, initial) if (v) { \ + IGRAPH_CHECK(igraph_vector_int_resize(v, no_of_nodes)); \ + igraph_vector_int_fill(v, initial); } + + VINIT(order, -1); + VINIT(order_out, -1); + VINIT(parents, -2); + VINIT(dist, -1); + +# undef VINIT + + IGRAPH_CHECK(igraph_stack_int_push(&stack, root)); + IGRAPH_BIT_SET(added, root); + if (parents) { + VECTOR(*parents)[root] = -1; + } + if (order) { + VECTOR(*order)[act_rank++] = root; + } + if (dist) { + VECTOR(*dist)[root] = 0; + } + if (in_callback) { + IGRAPH_CHECK_CALLBACK(in_callback(graph, root, 0, extra), &ret); + if (ret == IGRAPH_STOP) { + FREE_ALL(); + return IGRAPH_SUCCESS; + } + } + + for (igraph_integer_t actroot = 0; actroot < no_of_nodes; ) { + + /* 'root' first, then all other vertices */ + if (igraph_stack_int_empty(&stack)) { + if (!unreachable) { + break; + } + if (IGRAPH_BIT_TEST(added, actroot)) { + actroot++; + continue; + } + IGRAPH_CHECK(igraph_stack_int_push(&stack, actroot)); + IGRAPH_BIT_SET(added, actroot); + if (parents) { + VECTOR(*parents)[actroot] = -1; + } + if (order) { + VECTOR(*order)[act_rank++] = actroot; + } + if (dist) { + VECTOR(*dist)[actroot] = 0; + } + + if (in_callback) { + IGRAPH_CHECK_CALLBACK(in_callback(graph, actroot, 0, extra), &ret); + if (ret == IGRAPH_STOP) { + FREE_ALL(); + return IGRAPH_SUCCESS; + } + } + + actroot++; + } + + while (!igraph_stack_int_empty(&stack)) { + igraph_integer_t actvect = igraph_stack_int_top(&stack); + igraph_integer_t *ptr = igraph_vector_int_get_ptr(&nptr, actvect); + + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + + const igraph_integer_t n = igraph_vector_int_size(neis); + + /* Search for a neighbor that was not yet visited */ + igraph_bool_t any = false; + igraph_integer_t nei = 0; + while (!any && (*ptr) < n) { + nei = VECTOR(*neis)[(*ptr)]; + any = !IGRAPH_BIT_TEST(added, nei); + (*ptr) ++; + } + if (any) { + /* There is such a neighbor, add it */ + IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); + IGRAPH_BIT_SET(added, nei); + if (parents) { + VECTOR(*parents)[ nei ] = actvect; + } + if (order) { + VECTOR(*order)[act_rank++] = nei; + } + act_dist++; + if (dist) { + VECTOR(*dist)[nei] = act_dist; + } + + if (in_callback) { + IGRAPH_CHECK_CALLBACK( + in_callback(graph, nei, act_dist, extra), + &ret + ); + if (ret == IGRAPH_STOP) { + FREE_ALL(); + return IGRAPH_SUCCESS; + } + } + + } else { + /* There is no such neighbor, finished with the subtree */ + igraph_stack_int_pop(&stack); + if (order_out) { + VECTOR(*order_out)[rank_out++] = actvect; + } + act_dist--; + + if (out_callback) { + IGRAPH_CHECK_CALLBACK( + out_callback(graph, actvect, act_dist, extra), + &ret + ); + + if (ret == IGRAPH_STOP) { + FREE_ALL(); + return IGRAPH_SUCCESS; + } + } + } + } + } + + FREE_ALL(); +# undef FREE_ALL + + return IGRAPH_SUCCESS; +} diff --git a/src/hrg/dendro.h b/src/hrg/dendro.h new file mode 100644 index 0000000..7b66331 --- /dev/null +++ b/src/hrg/dendro.h @@ -0,0 +1,296 @@ +/* -*- 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 std::string; + +namespace fitHRG { + +// *********************************************************************** +// ******** Basic Structures ********************************************* + +enum {DENDRO, GRAPH, LEFT, RIGHT}; + +struct block { + double x; + int y; +}; + +struct ipair { + int x; + int y; + short int t; + string sp; +}; + +struct child { + int index; + short int type; + child* next; +}; + +// *********************************************************************** +// ******** Cnode Class ************************************************** + +struct cnode { + int index = -1; // array index of this node + int degree = 0; // number of children in list + int parent = -1; // index of parent node + double weight = 0.0; // sampled posterior weight + child* children = nullptr; // list of children (and their types) + child* lastChild = nullptr; // pointer to last child in list + cnode() = default; + cnode(const cnode &) = delete; + cnode & operator = (const cnode &) = delete; + ~cnode() { + child *curr = children; + while (curr != nullptr) { + child *prev = curr; + curr = curr->next; + delete prev; + } + lastChild = nullptr; + } +}; + +// *********************************************************************** +// ******** Split Class ************************************************** + +class split { +public: + string s; // partition assignment of leaf vertices + split() = default; + void initializeSplit(const int n) { + s = ""; + for (int i = 0; i < n; i++) { + s += "-"; + } + } + bool checkSplit() const { + if (s.empty() || s.find('-', 0) != string::npos) { + return false; + } else { + return true; + } + } +}; + +// *********************************************************************** +// ******** Internal Edge Class ****************************************** +// The usefulness of this data structure is to provide an easy to way +// maintain the set of internal edges, and the corresponding splits, +// in the dendrogram D. It allows for the selection of a random +// internal edge in O(1) time, and it takes O(1) time to update its +// structure given an internal move. This structure does not provide +// any means to directly manipulate the splits, but does allow them to +// be replaced. A split has the form "int.int...int#int.int...int", +// where all ints on the left side of the # are in the left partition +// and all ints on the right side of the # marker are in the right +// partition defined by the split. + +class interns { + ipair* edgelist; // list of internal edges represented + string* splitlist; // split representation of the internal edges + int** indexLUT; // table of indices of internal edges in edgelist + int q; // number of internal edges + int count; // (for adding edges) edgelist index of new edge to add +public: + explicit interns(int); + ~interns(); + + // add an internal edge, O(1) + bool addEdge(int, int, short int); + // returns the ith edge of edgelist, O(1) + ipair* getEdge(int) const; + // returns a uniformly random internal edge, O(1) + ipair* getRandomEdge() const; + // returns the ith split of the splitlist, O(1) + string getSplit(int) const; + // replace an existing split, O(1) + bool replaceSplit(int i, const string &sp); + // swaps two edges, O(1) + bool swapEdges(int, int, short int, int, int, short int); +}; + +// *********************************************************************** +// ******** Tree elementd Class ****************************************** + +struct elementd { + short int type = DENDRO; // either DENDRO or GRAPH + double logL = 0.0; // log-likelihood contribution of this internal node + double p = 0.0; // probability p_i that an edge exists between L and + // R subtrees + int e = 0; // number of edges between L and R subtrees + int n = 0; // number of leafs in subtree rooted here + int label = -1; // subtree label: smallest leaf index + int index = -1; // index in containing array + + elementd *M = nullptr; // pointer to parent node + elementd *L = nullptr; // pointer for L subtree + elementd *R = nullptr; // pointer for R subtree +}; + +// *********************************************************************** +// ******** Dendrogram Class ********************************************* + +class dendro { + elementd* root = nullptr; // root of the dendrogram + elementd* internal = nullptr; // array of n-1 internal vertices (the dendrogram D) + elementd* leaf = nullptr; // array of n leaf vertices (the graph G) + int n; // number of leaf vertices to allocate + interns* d = nullptr; // list of internal edges of dendrogram D + splittree* splithist = nullptr; // histogram of cumulative split weights + list** paths = nullptr; // 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 = nullptr; // (consensus tree) array of internal tree nodes + int* cancestor = nullptr; // (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(double) const; + // build split for this internal edge + string buildSplit(elementd*) const; + // compute number of edges between two internal subtrees + int computeEdgeCount(int, short int, int, short int); + // (consensus tree) counts children + static size_t countChildren(const string &s); + // find internal node of D that is common ancestor of i,j + elementd* findCommonAncestor(list**, int, int); + // return reverse of path to leaf from root + list* reversePathToRoot(int); + // quicksort functions + static void QsortMain(block*, int, int); + static int QsortPartition(block*, int, int, int); + + // underlying G (dangerously accessible) + graph* g = nullptr; + +public: + + // constructor / destructor + dendro() = default; + dendro(const dendro &) = delete; + dendro & operator = (const dendro &) = delete; + ~dendro(); + + igraph_error_t setGraph(const igraph_t *igraph); + void setGraph(graph *ig) { g = ig; } + const graph *getGraph() const { return g; } + + // 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() const; + // return likelihood of G given D + double getLikelihood() const; + // store splits in this splittree + void getSplitList(splittree*) const; + // return total weight of splittree + double getSplitTotalWeight() const; + // make random G from D + void makeRandomGraph(); + // make single MCMC move + void monteCarloMove(double &, bool &, double); + // record consensus tree from splithist + void recordConsensusTree(igraph_vector_int_t *parents, + igraph_vector_t *weights); + // record D structure + void recordDendrogramStructure(igraph_hrg_t *hrg) const noexcept; + // record G structure to igraph graph + igraph_error_t recordGraphStructure(igraph_t *graph) const noexcept; + // 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(); +}; + +} // namespace fitHRG + +#endif diff --git a/src/hrg/graph.h b/src/hrg/graph.h new file mode 100644 index 0000000..4e72b24 --- /dev/null +++ b/src/hrg/graph.h @@ -0,0 +1,156 @@ +/* -*- 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 +#include "hrg/rbtree.h" + +#include +#include +#include + +namespace fitHRG { + +// ******** Basic Structures ********************************************* + +struct edge { + int x = -1; // stored integer value (edge terminator) + double* h = nullptr; // (histogram) weights of edge existence + double total_weight = 0.0; // (histogram) total weight observed + int obs_count = 0; // number of observations in histogram + edge* next = nullptr; // pointer to next elementd + edge() = default; + edge(const edge &) = delete; + edge & operator = (const edge &) = delete; + ~edge() { + delete [] h; + } +}; + +struct vert { + std::string name; // (external) name of vertex + int degree = 0; // degree of this vertex +}; + +// ******** Graph Class with Edge Statistics ***************************** + +class graph { +public: + explicit graph(int, bool predict = false); + ~graph(); + + // add (i,j) to graph + bool addLink(int, int); + // add weight to (i,j)'s histogram + bool addAdjacencyObs(int, int, double, double); + // add to obs_count and total_weight + void addAdjacencyEnd(); + // true if (i,j) is already in graph + bool doesLinkExist(int, int) const; + // returns degree of vertex i + int getDegree(int) const; + // returns name of vertex i + std::string getName(int) const; + // returns edge list of vertex i + const edge* getNeighborList(int) const noexcept; + // return ptr to histogram of edge (i,j) + double* getAdjacencyHist(int, int) const; + // return average value of adjacency A(i,j) + double getAdjacencyAverage(int, int) const; + // returns bin_resolution + double getBinResolution() const; + // returns num_bins + int getNumBins() const; + // returns m + int numLinks() const; + // returns n + int numNodes() const; + // returns total_weight + double getTotalWeight() const; + // reset edge (i,j)'s histogram + void resetAdjacencyHistogram(int, int); + // reset all edge histograms + void resetAllAdjacencies(); + // clear all links from graph + void resetLinks(); + // allocate edge histograms + void setAdjacencyHistograms(igraph_integer_t); + // set name of vertex i + bool setName(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 = nullptr; // 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/hrg/graph_simp.h b/src/hrg/graph_simp.h new file mode 100644 index 0000000..4ba5682 --- /dev/null +++ b/src/hrg/graph_simp.h @@ -0,0 +1,142 @@ +/* -*- 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 +#include + +namespace fitHRG { + +// ******** Basic Structures ********************************************* + +struct simpleEdge { + int x = -1; // index of edge terminator + simpleEdge* next = nullptr; // pointer to next elementd +}; + +struct simpleVert { + std::string name; // (external) name of vertex + int degree = 0; // degree of this vertex + int group_true = -1; // index of vertex's true group +}; + +struct twoEdge { + int o = -1; // index of edge originator + int x = -1; // index of edge terminator +}; + +// ******** Graph Class with Edge Statistics ***************************** + +class simpleGraph { +public: + explicit simpleGraph(int); + ~simpleGraph(); + + // add group label to vertex i + bool addGroup(int, int); + // add (i,j) to graph + bool addLink(int, int); + // true if (i,j) is already in graph + bool doesLinkExist(int, int) const; + // returns A(i,j) + double getAdjacency(int, int) const; + // returns degree of vertex i + int getDegree(int) const; + // returns group label of vertex i + int getGroupLabel(int) const; + // returns name of vertex i + std::string getName(int) const; + // returns edge list of vertex i + const simpleEdge* getNeighborList(int) const; + // return pointer to a node + const simpleVert* getNode(int) const; + // returns num_groups + int getNumGroups() const; + // returns m + int getNumLinks() const; + // returns n + int getNumNodes() const; + // set name of vertex i + bool setName(int i, const std::string &text); + +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 + static void QsortMain(block*, int, int); + static int QsortPartition(block*, int, int, int); +}; + +} // namespace fitHRG + +#endif diff --git a/src/hrg/hrg.cc b/src/hrg/hrg.cc new file mode 100644 index 0000000..ca1697f --- /dev/null +++ b/src/hrg/hrg.cc @@ -0,0 +1,1093 @@ +/* -*- 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_attributes.h" +#include "igraph_hrg.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "hrg/dendro.h" +#include "hrg/graph.h" +#include "hrg/graph_simp.h" + +#include "core/exceptions.h" + +#include +#include + +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 void markovChainMonteCarlo(dendro &d, const igraph_integer_t 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 (igraph_integer_t i = 0; i < period; i++) { + + // make a MCMC move + 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(); +} + +static void markovChainMonteCarlo2(dendro &d, const int num_samples) { + bool flag_taken; + double dL; + const double ptest = 1.0 / (50.0 * static_cast(d.getGraph()->numNodes())); + igraph_integer_t sample_num = 0; + int t = 1; + const int thresh = 200 * d.getGraph()->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(); + } + + t++; + + // correct floating-point errors O(n) + d.refreshLikelihood(); // TODO: less frequently + } +} + +static void 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; + igraph_real_t newMeanL = -1e-49; + + while (true) { + const igraph_real_t oldMeanL = newMeanL; + newMeanL = 0.0; + for (int i = 0; i < 65536; i++) { + d.monteCarloMove(dL, flag_taken, 1.0); + const igraph_real_t 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); + } +} + +igraph_error_t dendro::setGraph(const igraph_t *igraph) { + igraph_integer_t no_of_nodes = igraph_vcount(igraph); + igraph_integer_t no_of_edges = igraph_ecount(igraph); + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph too large for the HRG module.", IGRAPH_EOVERFLOW); + } + + // TODO: Can this be relaxed? buildDendrogram() creates a tree with n-2 internal edges, + // i.e. zero internal edges for a 2-vertex graph. This is not handled at the moment. + if (no_of_nodes < 3) { + IGRAPH_ERROR("Graph must have at least 3 vertices for HRG, got only %" IGRAPH_PRId " vertices.", IGRAPH_EINVAL); + } + + // Create graph + g = new graph(no_of_nodes); + + // Add edges + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + int from = IGRAPH_FROM(igraph, i); + int to = IGRAPH_TO(igraph, i); + if (from == to) { + continue; + } + if (!g->doesLinkExist(from, to)) { + g->addLink(from, to); + } + if (!g->doesLinkExist(to, from)) { + g->addLink(to, from); + } + } + + buildDendrogram(); + + return IGRAPH_SUCCESS; +} + +static std::unique_ptr igraph_i_hrg_getsimplegraph(const igraph_t *igraph, + dendro &d, + igraph_integer_t num_bins) { + + const igraph_integer_t no_of_nodes = igraph_vcount(igraph); + const igraph_integer_t no_of_edges = igraph_ecount(igraph); + + // TODO replace the following throw's with IGRAPH_ERROR + + if (no_of_nodes > INT_MAX) { + throw std::runtime_error("Graph too large for the HRG module."); + } + + // TODO: Can this be relaxed? buildDendrogram() creates a tree with n-2 internal edges, + // i.e. zero internal edges for a 2-vertex graph. This is not handled at the moment. + if (no_of_nodes < 3) { + throw std::runtime_error("Graph must have at least 3 vertices for HRG."); + } + + // Create graphs + std::unique_ptr g(new graph(no_of_nodes, true)); + g->setAdjacencyHistograms(num_bins); + + std::unique_ptr sg(new simpleGraph(no_of_nodes)); + + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + int from = (int) IGRAPH_FROM(igraph, i); + int to = (int) IGRAPH_TO(igraph, i); + if (from == to) { + continue; + } + if (!g->doesLinkExist(from, to)) { + g->addLink(from, to); + } + if (!g->doesLinkExist(to, from)) { + g->addLink(to, from); + } + if (! sg->doesLinkExist(from, to)) { + sg->addLink(from, to); + } + if (! sg->doesLinkExist(to, from)) { + sg->addLink(to, from); + } + } + + d.setGraph(g.release()); + d.buildDendrogram(); + + return sg; +} + +/** + * \function igraph_hrg_init + * \brief 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. + */ + +igraph_error_t igraph_hrg_init(igraph_hrg_t *hrg, igraph_integer_t n) { + if (n < 0) { + IGRAPH_ERRORF("Number of vertices should not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); + } + if (n == 0) { + n = 1; + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->left, n - 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->right, n - 1); + IGRAPH_VECTOR_INIT_FINALLY (&hrg->prob, n - 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->edges, n - 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->vertices, n - 1); + IGRAPH_FINALLY_CLEAN(5); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hrg_destroy + * \brief 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_int_destroy(&hrg->left); + igraph_vector_int_destroy(&hrg->right); + igraph_vector_destroy(&hrg->prob); + igraph_vector_int_destroy(&hrg->edges); + igraph_vector_int_destroy(&hrg->vertices); +} + +/** + * \function igraph_hrg_size + * \brief 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). + */ + +igraph_integer_t igraph_hrg_size(const igraph_hrg_t *hrg) { + return igraph_vector_int_size(&hrg->left) + 1; +} + +/** + * \function igraph_hrg_resize + * \brief 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. + */ + +igraph_error_t igraph_hrg_resize(igraph_hrg_t *hrg, igraph_integer_t newsize) { + igraph_integer_t origsize = igraph_hrg_size(hrg); + + /* The data structure must be left in a consistent state if resizing fails. */ + +#define CHECK_ERR(expr) \ + do { \ + igraph_error_t err = (expr); \ + if (err != IGRAPH_SUCCESS) { \ + igraph_vector_int_resize(&hrg->left, origsize); \ + igraph_vector_int_resize(&hrg->right, origsize); \ + igraph_vector_resize(&hrg->prob, origsize); \ + igraph_vector_int_resize(&hrg->edges, origsize); \ + igraph_vector_int_resize(&hrg->vertices, origsize); \ + IGRAPH_FINALLY_EXIT(); \ + IGRAPH_ERROR("Cannot resize HRG.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ + } \ + } while (0) + + IGRAPH_FINALLY_ENTER(); + { + CHECK_ERR(igraph_vector_int_resize(&hrg->left, newsize - 1)); + CHECK_ERR(igraph_vector_int_resize(&hrg->right, newsize - 1)); + CHECK_ERR(igraph_vector_resize(&hrg->prob, newsize - 1)); + CHECK_ERR(igraph_vector_int_resize(&hrg->edges, newsize - 1)); + CHECK_ERR(igraph_vector_int_resize(&hrg->vertices, newsize - 1)); + } + IGRAPH_FINALLY_EXIT(); + +#undef CHECK_ERR + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hrg_fit + * \brief 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 \p start argument is true. + * \param start Logical, whether to start the fitting from the given + * HRG model. + * \param steps Integer, the number of MCMC steps to take in the + * fitting procedure. If this is zero, then the fitting stops if a + * convergence criteria is fulfilled. + * \return Error code. + * + * Time complexity: TODO. + */ + +igraph_error_t igraph_hrg_fit(const igraph_t *graph, + igraph_hrg_t *hrg, + igraph_bool_t start, + igraph_integer_t steps) { + + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + + RNG_BEGIN(); + + dendro d; + + // If we want to start from HRG + if (start) { + if (igraph_hrg_size(hrg) != no_of_nodes) { + IGRAPH_ERROR("Invalid HRG to start from.", IGRAPH_EINVAL); + } + // Convert the igraph graph + IGRAPH_CHECK(d.setGraph(graph)); + d.clearDendrograph(); + d.importDendrogramStructure(hrg); + } else { + // Convert the igraph graph + IGRAPH_CHECK(d.setGraph(graph)); + IGRAPH_CHECK(igraph_hrg_resize(hrg, no_of_nodes)); + } + + // Run fixed number of steps, or until convergence + if (steps > 0) { + markovChainMonteCarlo(d, steps, hrg); + } else { + MCMCEquilibrium_Find(d, hrg); + } + + RNG_END(); + + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END +} + +/** + * \function igraph_hrg_sample + * \brief Sample from a hierarchical random graph model. + * + * This function draws a single sample from a hierarchical random graph model. + * + * \param hrg A HRG model to sample from + * \param sample Pointer to an uninitialized graph; the sample is stored here. + * \return Error code. + * + * Time complexity: TODO. + */ + +igraph_error_t igraph_hrg_sample(const igraph_hrg_t *hrg, igraph_t *sample) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + dendro d; + + // TODO: error handling + + RNG_BEGIN(); + + d.clearDendrograph(); + d.importDendrogramStructure(hrg); + d.makeRandomGraph(); + IGRAPH_CHECK(d.recordGraphStructure(sample)); + + RNG_END(); + + return IGRAPH_SUCCESS; + IGRAPH_HANDLE_EXCEPTIONS_END +} + +/** + * \function igraph_hrg_sample_many + * \brief Draw multiple samples from a hierarchical random graph model. + * + * This function draws multiple samples from the hierarchical random graph + * ensemble \p hrg. + * + * \param hrg A HRG model to sample from + * \param samples An initialized graph list that will contain the sampled + * graphs. Note that existing graphs in the graph list are \em not removed + * so make sure you supply an empty list if you do not need the old contents + * of the list. + * \param num_samples The number of samples to generate. + * \return Error code. + * + * Time complexity: TODO. + */ + +igraph_error_t igraph_hrg_sample_many( + const igraph_hrg_t *hrg, igraph_graph_list_t *samples, + igraph_integer_t num_samples +) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + igraph_t g; + dendro d; + + if (num_samples < 0) { + IGRAPH_ERROR("Number of samples must be non-negative.", IGRAPH_EINVAL); + } + + if (num_samples == 0) { + return IGRAPH_SUCCESS; + } + + RNG_BEGIN(); + + d.clearDendrograph(); + d.importDendrogramStructure(hrg); + while (num_samples-- > 0) { + d.makeRandomGraph(); + IGRAPH_CHECK(d.recordGraphStructure(&g)); + IGRAPH_FINALLY(igraph_destroy, &g); + IGRAPH_CHECK(igraph_graph_list_push_back(samples, &g)); + IGRAPH_FINALLY_CLEAN(1); + } + + RNG_END(); + + return IGRAPH_SUCCESS; + IGRAPH_HANDLE_EXCEPTIONS_END +} + +/** + * \function igraph_hrg_game + * \brief 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. + * \return Error code. + * + * Time complexity: TODO. + */ + +igraph_error_t igraph_hrg_game(igraph_t *graph, + const igraph_hrg_t *hrg) { + return igraph_hrg_sample(hrg, graph); +} + +/** + * \function igraph_from_hrg_dendrogram + * \brief Create a graph representation of the dendrogram of a hierarchical random graph model. + * + * Creates the igraph graph equivalent of the dendrogram encoded in an + * \ref igraph_hrg_t data structure. The probabilities associated to the + * nodes are returned in a vector so this function works without an + * attribute handler. + * + * \param graph Pointer to an uninitialized graph, the result is + * stored here. + * \param hrg The hierarchical random graph to convert. + * \param prob Pointer to an \em initialized vector; the probabilities + * associated to the nodes of the dendrogram will be stored here. Leaf nodes + * will have an associated probability of \c IGRAPH_NAN . + * You may set this to \c NULL if you do not need the probabilities. + * \return Error code. + * + * Time complexity: O(n), the number of vertices in the graph. + */ + +igraph_error_t igraph_from_hrg_dendrogram( + igraph_t *graph, const igraph_hrg_t *hrg, igraph_vector_t *prob +) { + const igraph_integer_t orig_nodes = igraph_hrg_size(hrg); + const igraph_integer_t no_of_nodes = orig_nodes * 2 - 1; + const igraph_integer_t no_of_edges = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + igraph_vector_int_t edges; + igraph_integer_t i, idx = 0; + + // Probability labels, for leaf nodes they are IGRAPH_NAN + if (prob) { + IGRAPH_CHECK(igraph_vector_resize(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_INT_INIT_FINALLY(&edges, no_of_edges * 2); + + for (i = 0; i < orig_nodes - 1; i++) { + igraph_integer_t left = VECTOR(hrg->left)[i]; + igraph_integer_t 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, NULL)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); // + 1 for graph + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hrg_dendrogram + * \brief 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. + * + * \deprecated-by igraph_from_hrg_dendrogram 0.10.5 + */ +igraph_error_t igraph_hrg_dendrogram(igraph_t *graph, const igraph_hrg_t *hrg) { + const igraph_integer_t orig_nodes = igraph_hrg_size(hrg); + const igraph_integer_t no_of_nodes = orig_nodes * 2 - 1; + const igraph_integer_t no_of_edges = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + igraph_vector_int_t edges; + igraph_integer_t 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_INT_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++) { + igraph_integer_t left = VECTOR(hrg->left)[i]; + igraph_integer_t 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, NULL)); + + igraph_vector_ptr_destroy(&vattrs); + igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&prob); + IGRAPH_FINALLY_CLEAN(4); // + 1 for graph + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hrg_consensus + * \brief Calculate a consensus tree for a HRG. + * + * The calculation can be started from the given HRG (\p hrg), or (if + * \p 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 \p parents. + * \param hrg A hierarchical random graph. It is used as a starting + * point for the sampling, if the \p start argument is true. It is + * modified along the MCMC. + * \param start Logical, whether to use the supplied HRG (in \p 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. + */ + +igraph_error_t igraph_hrg_consensus(const igraph_t *graph, + igraph_vector_int_t *parents, + igraph_vector_t *weights, + igraph_hrg_t *hrg, + igraph_bool_t start, + igraph_integer_t num_samples) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + + if (start && !hrg) { + IGRAPH_ERROR("`hrg' must be given if `start' is true.", IGRAPH_EINVAL); + } + + RNG_BEGIN(); + + dendro d; + + if (start) { + IGRAPH_CHECK(d.setGraph(graph)); + d.clearDendrograph(); + d.importDendrogramStructure(hrg); + } else { + IGRAPH_CHECK(d.setGraph(graph)); + if (hrg) { + igraph_hrg_resize(hrg, igraph_vcount(graph)); + } + MCMCEquilibrium_Find(d, hrg); + } + + markovChainMonteCarlo2(d, num_samples); + + d.recordConsensusTree(parents, weights); + + RNG_END(); + + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END +} + +static void MCMCEquilibrium_Sample(dendro &d, igraph_integer_t 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; + igraph_integer_t sample_num = 0; + igraph_integer_t t = 1, thresh = 100 * d.getGraph()->numNodes(); + double ptest = 1.0 / 10.0 / d.getGraph()->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++; + } +} + +static igraph_integer_t QsortPartition (pblock* array, igraph_integer_t left, igraph_integer_t right, igraph_integer_t index) { + pblock p_value = array[index]; + + std::swap(array[right], array[index]); + + igraph_integer_t stored = left; + for (igraph_integer_t i = left; i < right; i++) { + if (array[i].L <= p_value.L) { + std::swap(array[i], array[stored]); + stored++; + } + } + std::swap(array[right], array[stored]); + + return stored; +} + +static void QsortMain (pblock* array, igraph_integer_t left, igraph_integer_t right) { + if (right > left) { + igraph_integer_t pivot = left; + igraph_integer_t part = QsortPartition(array, left, right, pivot); + QsortMain(array, left, part - 1); + QsortMain(array, part + 1, right ); + } +} + +static void rankCandidatesByProbability(const simpleGraph &sg, const 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.getGraph()->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); +} + +static igraph_error_t recordPredictions(const pblock *br_list, igraph_vector_int_t *edges, + igraph_vector_t *prob, int mk) { + + IGRAPH_CHECK(igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_hrg_predict + * \brief 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. + */ + +igraph_error_t igraph_hrg_predict(const igraph_t *graph, + igraph_vector_int_t *edges, + igraph_vector_t *prob, + igraph_hrg_t *hrg, + igraph_bool_t start, + igraph_integer_t num_samples, + igraph_integer_t num_bins) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + + if (start && !hrg) { + IGRAPH_ERROR("`hrg' must be given when `start' is true", IGRAPH_EINVAL); + } + + RNG_BEGIN(); + + dendro d; + + std::unique_ptr sg = igraph_i_hrg_getsimplegraph(graph, d, num_bins); + + int mk = sg->getNumNodes() * (sg->getNumNodes() - 1) / 2 - sg->getNumLinks() / 2; + std::unique_ptr 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(); + d.importDendrogramStructure(hrg); + } else { + if (hrg) { + igraph_hrg_resize(hrg, igraph_vcount(graph)); + } + MCMCEquilibrium_Find(d, hrg); + } + + MCMCEquilibrium_Sample(d, num_samples); + rankCandidatesByProbability(*sg, d, br_list.get(), mk); + IGRAPH_CHECK(recordPredictions(br_list.get(), edges, prob, mk)); + + RNG_END(); + + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END +} + +/** + * \function igraph_hrg_create + * \brief 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. + * \return Error code. + * + * Time complexity: O(n), the number of vertices in the tree. + */ + +igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, + const igraph_t *graph, + const igraph_vector_t *prob) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_internal = no_of_nodes > 0 ? (no_of_nodes - 1) / 2 : 0; + igraph_vector_int_t deg, idx; + igraph_integer_t root = 0; + igraph_integer_t d0 = 0, d1 = 0, d2 = 0; + igraph_integer_t ii = 0, il = 0; + igraph_vector_int_t neis; + igraph_vector_int_t path; + igraph_bool_t simple; + + // -------------------------------------------------------- + // 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 / 2) { + IGRAPH_ERRORF("HRG probability vector size (%" IGRAPH_PRId ") should be equal " + "to the number of internal nodes (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + igraph_vector_size(prob), no_of_nodes / 2); + } + + // 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_CHECK(igraph_is_simple(graph, &simple)); + if (!simple) { + IGRAPH_ERROR("HRG graph must be a simple graph.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_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 (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t 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++) { + igraph_integer_t 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_INT_INIT_FINALLY(&idx, no_of_nodes); + VECTOR(idx)[root] = - (ii++) - 1; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t d = VECTOR(deg)[i]; + if (i == root) { + continue; + } + if (d == 2) { + VECTOR(idx)[i] = - (ii++) - 1; + } + if (d == 0) { + VECTOR(idx)[i] = (il++); + } + } + + IGRAPH_CHECK(igraph_hrg_resize(hrg, no_of_internal + 1)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t ri = VECTOR(idx)[i]; + if (ri >= 0) { + continue; + } + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT)); + VECTOR(hrg->left )[-ri - 1] = VECTOR(idx)[ VECTOR(neis)[0] ]; + VECTOR(hrg->right)[-ri - 1] = VECTOR(idx)[ VECTOR(neis)[1] ]; + VECTOR(hrg->prob )[-ri - 1] = VECTOR(*prob)[i]; + } + + // Calculate the number of vertices and edges in each subtree + igraph_vector_int_null(&hrg->edges); + igraph_vector_int_null(&hrg->vertices); + IGRAPH_VECTOR_INT_INIT_FINALLY(&path, 0); + IGRAPH_CHECK(igraph_vector_int_push_back(&path, VECTOR(idx)[root])); + while (!igraph_vector_int_empty(&path)) { + igraph_integer_t ri = igraph_vector_int_tail(&path); + igraph_integer_t lc = VECTOR(hrg->left)[-ri - 1]; + igraph_integer_t rc = VECTOR(hrg->right)[-ri - 1]; + if (lc < 0 && VECTOR(hrg->vertices)[-lc - 1] == 0) { + // Go left + IGRAPH_CHECK(igraph_vector_int_push_back(&path, lc)); + } else if (rc < 0 && VECTOR(hrg->vertices)[-rc - 1] == 0) { + // Go right + IGRAPH_CHECK(igraph_vector_int_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_int_pop_back(&path); + } + } + + igraph_vector_int_destroy(&path); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&idx); + igraph_vector_int_destroy(°); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} diff --git a/src/hrg/hrg_types.cc b/src/hrg/hrg_types.cc new file mode 100644 index 0000000..ec9bae1 --- /dev/null +++ b/src/hrg/hrg_types.cc @@ -0,0 +1,3589 @@ +// *********************************************************************** +// *** 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 "igraph_hrg.h" +#include "igraph_constructors.h" +#include "igraph_random.h" + +#include "hrg/rbtree.h" +#include "hrg/dendro.h" +#include "hrg/graph.h" +#include "hrg/splittree_eq.h" +#include "hrg/graph_simp.h" + +#include +#include +#include + +using std::string; + +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 != nullptr) { + if (root->left != leaf || root->right != leaf) { + deleteSubTree(root); + } else { + delete root; + } + } + delete leaf; +} + +void rbtree::deleteTree() { + if (root != nullptr) { + deleteSubTree(root); root = nullptr; + } +} // 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=nullptr and returns +// FALSE +elementrb* rbtree::findItem(const int searchKey) const { + elementrb *current = root; + + // empty tree; bail out + if (current->key == -1) { + return nullptr; + } + + 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 nullptr; + } + } else { + // left-or-right? + if (searchKey > current->key) { + // try moving down-left + if (current->right != leaf) { + current = current->right; + } else { + // failure; bail out + return nullptr; + } + } else { + // found (searchKey==current->key) + return current; + } + } + } + return nullptr; +} + +int rbtree::returnValue(const int searchKey) const { + const elementrb* test = findItem(searchKey); + if (!test) { + return 0; + } else { + return test->value; + } +} + + +// ******** Return Item Functions **************************************** + +int* rbtree::returnArrayOfKeys() const { + IGRAPH_ASSERT(support >= 0); + int* 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 == nullptr) { + flag_go = false; + } + } + } + } + + return array; +} + +list* rbtree::returnListOfKeys() const { + keyValuePair *curr, *prev; + list *head = nullptr, *tail = nullptr, *newlist; + + curr = returnTreeAsList(); + while (curr != nullptr) { + newlist = new list; + newlist->x = curr->x; + if (head == nullptr) { + head = newlist; tail = head; + } else { + tail->next = newlist; tail = newlist; + } + prev = curr; + curr = curr->next; + delete prev; + } + return head; +} + +keyValuePair* rbtree::returnTreeAsList() const { + if (root->key == -1) { + return nullptr; /* empty tree */ + } + + // 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); + } + + return head; +} + +keyValuePair* rbtree::returnSubtreeAsList(const elementrb *z, keyValuePair *head) const { + 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() const { + 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() const { + 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) const { + 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) const { + 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 != nullptr) && (w == current->right)) { + w = current; + // move up in tree until find a non-right-child + current = current->parent; + } + return current; +} + +int rbtree::returnNodecount() const { + 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 == nullptr) { + newNode = new elementrb; // elementrb for the rbtree + newNode->key = newKey; + newNode->value = newValue; + newNode->color = true; // new nodes are always RED + newNode->parent = nullptr; // 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); + } +} + +// private house-keeping function for insertion +void rbtree::insertCleanup(elementrb *z) { + + // fix now if z is root + if (z->parent == nullptr) { + z->color = false; + return; + } + + elementrb *temp; + + // while z is not root and z's parent is RED + while (z->parent != nullptr && 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 +} + +// ******** Delete +// ******** Functions ********************************************* + +void rbtree::replaceItem(int key, int newValue) { + elementrb* ptr; + ptr = findItem(key); + ptr->value = newValue; +} + +void rbtree::incrementValue(int key) { + elementrb* ptr; + ptr = findItem(key); + ptr->value = 1 + ptr->value; +} + +// public delete function +void rbtree::deleteItem(int killKey) { + elementrb *x, *y, *z; + + z = findItem(killKey); + if (z == nullptr) { + 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 = nullptr; + root->left = leaf; + root->right = leaf; + support--; // set support to zero + return; // exit - no more work to do + } + + if (z != nullptr) { + 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 == nullptr) { + 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 = nullptr; + } +} + +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) +} + +// ******** 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 == nullptr) { + 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 +} + +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 == nullptr) { + 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 +} + +// *********************************************************************** +// *** 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() { + list *curr, *prev; + + delete g; // O(m) + delete [] internal; // O(n) + delete [] leaf; // O(n) + delete d; // O(n) + delete splithist; // potentially long + + if (paths) { + for (int i = 0; i < n; i++) { + curr = paths[i]; + while (curr) { + prev = curr; + curr = curr->next; + delete prev; + } + paths[i] = nullptr; + } + delete [] paths; + } + + delete [] ctree; // O(n) + delete [] cancestor; // O(n) +} + +// ********************************************************************* + +void dendro::binarySearchInsert(elementd* x, elementd* y) { + if (y->p < x->p) { // go to left subtree + if (x->L == nullptr) { // 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 == nullptr) { // 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); + } + } +} + +// ********************************************************************** + +list* dendro::binarySearchFind(const double v) const { + list *head = nullptr, *tail = nullptr, *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) const { + // 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 == nullptr) { + flag_go = false; curr = nullptr; + } 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 == nullptr) { + 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 == nullptr) { + 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 != nullptr) { + list *curr, *prev; + for (int i = 0; i < n; i++) { + curr = paths[i]; + while (curr != nullptr) { + prev = curr; + curr = curr->next; + delete prev; + prev = nullptr; + } + paths[i] = nullptr; + } + delete [] paths; + } + 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; + const 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 != nullptr) { + 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 != nullptr) { + 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 != nullptr) { + 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; + } + paths[i] = nullptr; + } + delete [] paths; + paths = nullptr; + + // --- 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 +} + +// *********************************************************************** + +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. + + delete [] leaf; // O(n) + leaf = nullptr; + + delete [] internal; // O(n) + internal = nullptr; + + delete d; // O(n) + d = nullptr; + + root = nullptr; +} + +// ********************************************************************** + +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 == nullptr) { + 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 == nullptr) { + 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 + + const 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 != nullptr) { + // to see if v_j is in A + if (subtreeR.findItem(current->x) != nullptr) { + 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 != nullptr) { + // to see if v_j is in B + if (subtreeL.findItem(current->x) != nullptr) { + 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; +} + +// *********************************************************************** + +size_t dendro::countChildren(const string &s) { + size_t len = s.size(); + size_t numC = 0; + for (size_t i = 0; i < len; i++) { + if (s[i] == 'C') { + numC++; + } + } + return numC; +} + +// *********************************************************************** + +void dendro::cullSplitHist() { + string *array = splithist->returnArrayOfKeys(); + double tot = splithist->returnTotal(); + int 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 = nullptr; +} + +// ********************************************************************** + +elementd* dendro::findCommonAncestor(list** paths_, const int i, const int j) { + list* headOne = paths_[i]; + list* headTwo = paths_[j]; + elementd* lastStep = nullptr; + while (headOne->x == headTwo->x) { + lastStep = &internal[headOne->x]; + headOne = headOne->next; + headTwo = headTwo->next; + if (headOne == nullptr || headTwo == nullptr) { + 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 = nullptr; + return numCons; +} + +// ********************************************************************** + +splittree* dendro::getConsensusSplits() const { + 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 = nullptr; + return consensusTree; +} + +// *********************************************************************** + +double dendro::getLikelihood() const { + return L; +} + +// *********************************************************************** + +void dendro::getSplitList(splittree* split_tree) const { + 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); + } + } +} + +// *********************************************************************** + +double dendro::getSplitTotalWeight() const { + if (splithist) { + return splithist->returnTotal(); + } else { + return 0; + } +} + +// *********************************************************************** + +bool dendro::importDendrogramStructure(const igraph_hrg_t *hrg) { + igraph_integer_t size = igraph_hrg_size(hrg); + + if (size > INT_MAX) { + throw std::range_error("Hierarchical random graph too large for the HRG module"); + } + + n = (int) size; + + // 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() { + delete g; + g = new graph(n); + + if (paths) { + for (int i = 0; i < n; i++) { + list *curr = paths[i]; + while (curr != nullptr) { + list *prev = curr; + curr = curr->next; + delete prev; + } + paths[i] = nullptr; + } + delete [] paths; + } +// build paths from root O(n d) + paths = new list* [n]; + for (int i = 0; i < n; i++) { + paths[i] = reversePathToRoot(i); + } + +// 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++) { + const elementd* 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++) { + list *curr = paths[i]; + while (curr != nullptr) { + list *prev = curr; + curr = curr->next; + delete prev; + } + paths[i] = nullptr; + } + delete [] paths; // delete paths data structure O(n log n) + paths = nullptr; +} + +// ********************************************************************** + +void 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; + const 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; + } + } + } +} + +// ********************************************************************** + +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; + } +} + +// ********************************************************************** + +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 ); + } +} + +int dendro::QsortPartition (block* array, int left, int right, int index) { + block p_value = array[index]; + + std::swap(array[index], array[right]); + + int stored = left; + for (int i = left; i < right; i++) { + if (array[i].x <= p_value.x) { + std::swap(array[stored], array[i]); + stored++; + } + } + std::swap(array[right], array[stored]); + + return stored; +} + +void dendro::recordConsensusTree(igraph_vector_int_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 != nullptr) { + 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 = nullptr; + // - attach child to list + if (ctree[ii].lastChild == nullptr) { + 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 = nullptr; + // - attach child to list + if (ctree[ii].lastChild == nullptr) { + 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_int_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 = nullptr; + } + + // 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) const noexcept { + 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; + } +} + +igraph_error_t dendro::recordGraphStructure(igraph_t *graph) const noexcept { + igraph_vector_int_t edges; + int no_of_nodes = g->numNodes(); + int no_of_edges = g->numLinks() / 2; + int idx = 0; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + + for (int i = 0; i < n; i++) { + const edge *curr = g->getNeighborList(i); + while (curr) { + if (i < curr->x) { + VECTOR(edges)[idx++] = i; + VECTOR(edges)[idx++] = curr->x; + } + curr = curr->next; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, /* directed= */ false)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +// ********************************************************************** + +list* dendro::reversePathToRoot(const int leafIndex) { + list *head, *subhead, *newlist; + head = subhead = newlist = nullptr; + elementd *current = &leaf[leafIndex]; + + // continue until we're finished + while (current != nullptr) { + // add this node to the path + newlist = new list; + newlist->x = current->index; + newlist->next = nullptr; + if (head == nullptr) { + head = newlist; + } else { + subhead = head; + head = newlist; + head->next = subhead; + } + current = current->M; + } + return head; +} + +// *********************************************************************** + +bool dendro::sampleSplitLikelihoods() { + // 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. + + 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 == nullptr) { + 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 = nullptr; + 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; + } + const elementd* ancestor; + list *currL, *prevL; + if (paths != nullptr) { + for (int i = 0; i < n; i++) { + currL = paths[i]; + while (currL != nullptr) { + prevL = currL; + currL = currL->next; + delete prevL; + prevL = nullptr; + } + paths[i] = nullptr; + } + delete [] paths; + } + paths = nullptr; + 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: update total weight in histograms + g->addAdjacencyEnd(); +} + +void dendro::resetDendrograph() { + // Reset the dendrograph structure for the next trial + if (leaf != nullptr) { + delete [] leaf; // O(n) + leaf = nullptr; + } + if (internal != nullptr) { + delete [] internal; // O(n) + internal = nullptr; + } + if (d != nullptr) { + delete d; // O(n) + d = nullptr; + } + root = nullptr; + if (paths != nullptr) { + list *curr, *prev; + for (int i = 0; i < n; i++) { + curr = paths[i]; + while (curr != nullptr) { + prev = curr; + curr = curr->next; + delete prev; + prev = nullptr; + } + paths[i] = nullptr; + } + delete [] paths; + } + paths = nullptr; + L = 1.0; +} + +// ********************************************************************** +// *** 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) +{ + IGRAPH_ASSERT(n >= 0); + nodes = new vert [n]; + nodeLink = new edge* [n]; + nodeLinkTail = new edge* [n]; + for (int i = 0; i < n; i++) { + nodeLink[i] = nullptr; + nodeLinkTail[i] = nullptr; + } + 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 != nullptr) { + prev = curr; + curr = curr->next; + delete prev; + } + } + delete [] nodeLink; + delete [] nodeLinkTail; + delete [] nodes; + + 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; + } +} + +// ********************************************************************** + +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] == nullptr) { + // 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++; + } +} + +bool graph::doesLinkExist(const int i, const int j) const { + // 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 != nullptr) { + if (curr->x == j) { + return true; + } + curr = curr->next; + } + } + return false; +} + +// ********************************************************************** + +int graph::getDegree(const int i) const { + if (i >= 0 && i < n) { + return nodes[i].degree; + } else { + return -1; + } +} + +string graph::getName(const int i) const { + if (i >= 0 && i < n) { + return nodes[i].name; + } else { + return ""; + } +} + +// NOTE: Returns address; deallocation of returned object is dangerous +const edge* graph::getNeighborList(const int i) const noexcept { + if (i >= 0 && i < n) { + return nodeLink[i]; + } else { + return nullptr; + } +} + +double* graph::getAdjacencyHist(const int i, const int j) const { + if (i >= 0 && i < n && j >= 0 && j < n) { + return A[i][j]; + } else { + return nullptr; + } +} + +// ********************************************************************** + +double graph::getAdjacencyAverage(const int i, const int j) const { + 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() const { + return m; +} + +int graph::numNodes() const { + return n; +} + +double graph::getBinResolution() const { + return bin_resolution; +} + +int graph::getNumBins() const { + return num_bins; +} + +double graph::getTotalWeight() const { + 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; +} + +// ********************************************************************** + +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; + } + } +} + +// ********************************************************************** + +void graph::resetLinks() { + edge *curr, *prev; + for (int i = 0; i < n; i++) { + curr = nodeLink[i]; + while (curr != nullptr) { + prev = curr; + curr = curr->next; + delete prev; + } + nodeLink[i] = nullptr; + nodeLinkTail[i] = nullptr; + nodes[i].degree = 0; + } + m = 0; +} + +// ********************************************************************** + +void graph::setAdjacencyHistograms(const igraph_integer_t 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; + } + } + } +} + +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) +{ + IGRAPH_ASSERT(n >= 0); + edgelist = new ipair [q]; + IGRAPH_ASSUME(q >= 0); // work around false positive GCC warning + 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) const { + return &edgelist[i]; +} + +// *********************************************************************** + +// NOTE: Returns an address to another object -- do not deallocate +ipair* interns::getRandomEdge() const { + return &edgelist[(int)(floor((double)(q) * RNG_UNIF01()))]; +} + +// *********************************************************************** + +string interns::getSplit(const int i) const { + 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 { + // do nothing + } // 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; +} + +splittree::~splittree() { + if (root != nullptr && (root->left != leaf || root->right != leaf)) { + deleteSubTree(root); root = nullptr; + } + delete root; + delete leaf; +} + +void splittree::deleteTree() { + if (root != nullptr) { + deleteSubTree(root); + root = nullptr; + } +} + +void splittree::deleteSubTree(elementsp *z) { + if (z->left != leaf) { + deleteSubTree(z->left); + z->left = nullptr; + } + if (z->right != leaf) { + deleteSubTree(z->right); + z->right = nullptr; + } + delete z; +} + +// ******** Reset Functions ********************************************* + +// O(n lg n) +void splittree::clearTree() { + string *array = returnArrayOfKeys(); + for (int i = 0; i < support; i++) { + deleteItem(array[i]); + } + delete [] array; +} + +// ******** 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=nullptr and returns +// FALSE +elementsp* splittree::findItem(const string &searchKey) { + + elementsp *current = root; + if (current->split.empty()) { + return nullptr; // 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 nullptr; + } + } 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 nullptr; + } + } else { + // found (searchKey==current->split) + return current; + } + } + } + return nullptr; +} + +double splittree::returnValue(const string &searchKey) { + const elementsp* test = findItem(searchKey); + if (test == nullptr) { + 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() { + IGRAPH_ASSERT(support >= 0); + string* 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 { + /* TODO: This is present in the original consensusHRG code, + * but it makes no sense to assign -1 to a string. */ + /* + 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 == nullptr) { + flag_go = false; + } + } + } + } + + return array; +} + +slist* splittree::returnListOfKeys() { + keyValuePairSplit *curr, *prev; + slist *head = nullptr, *tail = nullptr, *newlist; + + curr = returnTreeAsList(); + while (curr != nullptr) { + newlist = new slist; + newlist->x = curr->x; + if (head == nullptr) { + head = newlist; tail = head; + } else { + tail->next = newlist; tail = newlist; + } + prev = curr; + curr = curr->next; + delete prev; + prev = nullptr; + } + 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 nullptr; /* 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 != nullptr) && (w == current->right)) { + w = current; + current = current->parent; + } + return current; +} + +int splittree::returnNodecount() { + IGRAPH_ASSERT(support > 0); + return support; +} + +keyValuePairSplit* splittree::returnTheseSplits(const int target) { + keyValuePairSplit *head, *curr, *prev, *newhead, *newtail, *newpair; + int count; + + head = returnTreeAsList(); + prev = newhead = newtail = newpair = nullptr; + curr = head; + + while (curr != nullptr) { + count = 0; + for (auto c : curr->x) { + if (c == 'M') count++; + } + if (count == target && curr->x[1] != '*') { + newpair = new keyValuePairSplit; + newpair->x = curr->x; + newpair->y = curr->y; + newpair->next = nullptr; + if (newhead == nullptr) { + newhead = newpair; newtail = newpair; + } else { + newtail->next = newpair; newtail = newpair; + } + } + prev = curr; + curr = curr->next; + delete prev; + } + + return newhead; +} + +double splittree::returnTotal() const { + 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++; + } +} + +// public insert function +bool splittree::insertItem(const 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 != nullptr) { + 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 = nullptr; // 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 == nullptr) { + z->color = false; + return; + } + elementsp *temp; + // while z is not root and z's parent is RED + while (z->parent != nullptr && 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 +} + +// ******** Delete Functions ******************************************** +// public delete function +void splittree::deleteItem(const string &killKey) { + elementsp *x, *y, *z; + + z = findItem(killKey); + if (z == nullptr) { + 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 = nullptr; // + 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 != nullptr) { + 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 == nullptr) { + 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 = nullptr; // point y to nullptr for safety + } +} + +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) +} + +// ******** Rotation Functions ******************************************* + +void splittree::rotateLeft(elementsp *x) { + // do pointer-swapping operations for left-rotation + elementsp *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 == nullptr) { + 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 +} + +void splittree::rotateRight(elementsp *y) { + // do pointer-swapping operations for right-rotation + elementsp *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 == nullptr) { + 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 +} + +// *********************************************************************** +// *** 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] = nullptr; nodeLinkTail[i] = nullptr; + A[i] = new double [n]; + for (int j = 0; j < n; j++) { + A[i][j] = 0.0; + } + } + E = nullptr; +} + +simpleGraph::~simpleGraph() { + simpleEdge *curr, *prev; + for (int i = 0; i < n; i++) { + curr = nodeLink[i]; + delete [] A[i]; + while (curr != nullptr) { + prev = curr; + curr = curr->next; + delete prev; + } + } + delete [] E; + delete [] A; + delete [] nodeLink; + delete [] nodeLinkTail; + delete [] nodes; +} + +// *********************************************************************** + +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] == nullptr) { // 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 = nullptr; + return true; + } else { + return false; + } +} + +// *********************************************************************** + +bool simpleGraph::doesLinkExist(const int i, const int j) const { + // 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) const { + if (i >= 0 && i < n && j >= 0 && j < n) { + return A[i][j]; + } else { + return -1.0; + } +} + +int simpleGraph::getDegree(const int i) const { + if (i >= 0 && i < n) { + return nodes[i].degree; + } else { + return -1; + } +} + +int simpleGraph::getGroupLabel(const int i) const { + if (i >= 0 && i < n) { + return nodes[i].group_true; + } else { + return -1; + } +} + +string simpleGraph::getName(const int i) const { + if (i >= 0 && i < n) { + return nodes[i].name; + } else { + return ""; + } +} + +// NOTE: The following three functions return addresses; deallocation +// of returned object is dangerous +const simpleEdge* simpleGraph::getNeighborList(const int i) const { + if (i >= 0 && i < n) { + return nodeLink[i]; + } else { + return nullptr; + } +} +// END-NOTE + +// ********************************************************************* + +int simpleGraph::getNumGroups() const { + return num_groups; +} +int simpleGraph::getNumLinks() const { + return m; +} +int simpleGraph::getNumNodes() const { + return n; +} +const simpleVert* simpleGraph::getNode(const int i) const { + if (i >= 0 && i < n) { + return &nodes[i]; + } else { + return nullptr; + } +} + +// ********************************************************************** + +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 ); + } +} + +int simpleGraph::QsortPartition (block* array, int left, int right, + int index) { + block p_value = array[index]; + + std::swap(array[index], array[right]); + + int stored = left; + for (int i = left; i < right; i++) { + if (array[i].x <= p_value.x) { + std::swap(array[stored], array[i]); + stored++; + } + } + std::swap(array[right], array[stored]); + + return stored; +} + +// *********************************************************************** diff --git a/src/hrg/rbtree.h b/src/hrg/rbtree.h new file mode 100644 index 0000000..222eba8 --- /dev/null +++ b/src/hrg/rbtree.h @@ -0,0 +1,144 @@ +/* -*- 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 ********************************************* + +struct list { + int x = -1; // stored elementd in linked-list + list* next = nullptr; // pointer to next elementd +}; + +struct keyValuePair { + int x = -1; // elementrb key (int) + int y = -1; // stored value (int) + keyValuePair* next = nullptr; // linked-list pointer +}; + +// ******** Tree elementrb Class ***************************************** + +struct elementrb { + int key = -1; // search key (int) + int value = -1; // stored value (int) + + bool color = false; // F: BLACK, T: RED + short int mark = 0; // marker + + elementrb *parent = nullptr; // pointer to parent node + elementrb *left = nullptr; // pointer for left subtree + elementrb *right = nullptr; // pointer for right subtree +}; + +// ******** 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 { + 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(const elementrb *z, keyValuePair *head) const; + void deleteSubTree(elementrb *z); // delete subtree rooted at z + elementrb* returnMinKey(elementrb *z) const; // returns minimum of subtree + // rooted at z + elementrb* returnSuccessor(elementrb *z) const; // returns successor of z's key + +public: + rbtree(); ~rbtree(); // default constructor/destructor + + // returns value associated with searchKey + int returnValue(int searchKey) const; + // returns T if searchKey found, and points foundNode at the + // corresponding node + elementrb* findItem(int searchKey) const; + // insert a new key with stored value + void insertItem(int newKey, int newValue); + // delete 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() const; + // return list of keys in tree + list* returnListOfKeys() const; + // return the tree as a list of keyValuePairs + keyValuePair* returnTreeAsList() const; + // returns the maximum key in the tree + keyValuePair returnMaxKey() const; + // returns the minimum key in the tree + keyValuePair returnMinKey() const; + // returns number of items in tree + int returnNodecount() const; +}; + +} +#endif diff --git a/src/hrg/splittree_eq.h b/src/hrg/splittree_eq.h new file mode 100644 index 0000000..0c83f85 --- /dev/null +++ b/src/hrg/splittree_eq.h @@ -0,0 +1,168 @@ +/* -*- 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 ********************************************* + +struct slist { + std::string x; // stored elementd in linked-list + slist* next = nullptr; // pointer to next elementd +}; + +struct keyValuePairSplit { + std::string x; // elementsp split (string) + double y = 0.0; // stored weight (double) + int c = 0; // stored count (int) + keyValuePairSplit* next = nullptr; // linked-list pointer +}; + +// ******** Tree elementsp Class ***************************************** + +struct elementsp { + std::string split; // split represented as a string + double weight = 0.0; // total weight of this split + int count = 0; // number of observations of this split + + bool color = false; // F: BLACK, T: RED + short int mark = 0; // marker + + elementsp *parent = nullptr; // pointer to parent node + elementsp *left = nullptr; // pointer for left subtree + elementsp *right = nullptr; // pointer for right subtree +}; + +// ******** 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 { + elementsp* root; // binary tree root + elementsp* leaf; // all leaf nodes + int support = 0; // number of nodes in the tree + double total_weight = 0.0; // total weight stored + int total_count = 0; // 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(const std::string &, double); + void clearTree(); + // delete a node with given key + void deleteItem(const 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(int); + // returns sum of stored values + double returnTotal() const; +}; + +} // namespace fitHRG + +#endif diff --git a/src/internal/glpk_support.c b/src/internal/glpk_support.c new file mode 100644 index 0000000..384390e --- /dev/null +++ b/src/internal/glpk_support.c @@ -0,0 +1,172 @@ +/* -*- 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 = true; + 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 = true; + 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); + } +} + +igraph_error_t igraph_i_glpk_check(int retval, const char* message) { + const char *code = "none"; + char message_and_code[4096]; + igraph_error_t ret; + + if (retval == 0) { + return IGRAPH_SUCCESS; + } + + /* handle errors */ +#define HANDLE_CODE(c) case c: code = #c; ret = IGRAPH_##c; break; +#define HANDLE_CODE2(c) case c: code = #c; ret = IGRAPH_FAILURE; break; +#define HANDLE_CODE3(c) case c: code = #c; ret = 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 + + snprintf(message_and_code, sizeof(message_and_code) / sizeof(message_and_code[0]), + "%s (%s)", message, code); + IGRAPH_ERROR(message_and_code, ret); +} + +#else + +int igraph_glpk_dummy(void) { + /* 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/internal/glpk_support.h b/src/internal/glpk_support.h new file mode 100644 index 0000000..9ecc99a --- /dev/null +++ b/src/internal/glpk_support.h @@ -0,0 +1,149 @@ +/* -*- 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 "igraph_decls.h" +#include "igraph_error.h" + +#include +#include +#include + +__BEGIN_DECLS + +typedef struct igraph_i_glpk_error_info_s { + jmp_buf jmp; /* used for bailing when there is a GLPK error */ + bool is_interrupted; /* Boolean; true if there was an interruption */ + bool 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; + +igraph_error_t igraph_i_glpk_check(int retval, const char* message); +void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info); +IGRAPH_FUNCATTR_NORETURN 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 { \ + igraph_error_t igraph_i_ret = igraph_i_glpk_check(func, message); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) { \ + 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 = false; \ + igraph_i_glpk_error_info.is_error = false; \ + 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) + +__END_DECLS + +#endif /* HAVE_GLPK */ + +#endif /* IGRAPH_GLPK_SUPPORT_H */ diff --git a/src/internal/gmp_internal.h b/src/internal/gmp_internal.h new file mode 100644 index 0000000..e7f6b7e --- /dev/null +++ b/src/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/internal/hacks.c b/src/internal/hacks.c new file mode 100644 index 0000000..09f4220 --- /dev/null +++ b/src/internal/hacks.c @@ -0,0 +1,62 @@ +/* -*- 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. */ + +/** + * 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 = malloc(sizeof(char) * n); + if (result) { + memcpy(result, s, n); + } + return result; +} + +/** + * Drop-in replacement for strndup. + * Used only in compilers that do not have strndup or _strndup + */ +char *igraph_i_strndup(const char *s1, size_t n) { + size_t i; + /* We need to check if the string is shorter than n characters. + * We could use strlen, but that would do more work for long s1 and small n. + * TODO: Maybe memchr would be nicer here. + */ + for (i = 0; s1[i] != '\0' && i < n; i++) {} + n = i; + char *result = malloc(sizeof(char) * (n + 1)); + if (result) { + memcpy(result, s1, n); + result[n] = '\0'; + } + return result; +} diff --git a/src/internal/hacks.h b/src/internal/hacks.h new file mode 100644 index 0000000..b38bfce --- /dev/null +++ b/src/internal/hacks.h @@ -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 + +*/ + +#ifndef IGRAPH_HACKS_INTERNAL_H +#define IGRAPH_HACKS_INTERNAL_H + +#include "igraph_decls.h" + +#include "config.h" + +/* The CMake feature test looks for strcasecmp/strncasecmp in strings.h */ +#if defined(HAVE_STRCASECMP) || defined(HAVE_STRNCASECMP) +#include +#endif + +#include + +__BEGIN_DECLS + +#ifndef HAVE_STRDUP + #define strdup igraph_i_strdup + char* igraph_i_strdup(const char *s); +#endif + +#ifndef HAVE_STRNDUP + #define strndup igraph_i_strndup + char* igraph_i_strndup(const char *s, size_t n); +#endif + +#ifndef HAVE_STRCASECMP + #ifdef HAVE__STRICMP + #define strcasecmp _stricmp + #else + #error "igraph needs strcasecmp() or _stricmp()" + #endif +#endif + +#ifndef HAVE_STRNCASECMP + #ifdef HAVE__STRNICMP + #define strncasecmp _strnicmp + #else + #error "igraph needs strncasecmp() or _strnicmp()" + #endif +#endif + +/* Magic macro to fail the build if certain condition does not hold. See: + * https://stackoverflow.com/questions/4079243/how-can-i-use-sizeof-in-a-preprocessor-macro + */ +#define IGRAPH_STATIC_ASSERT(condition) ((void)sizeof(char[1 - 2*!(condition)])) + +__END_DECLS + +#endif diff --git a/src/internal/lsap.c b/src/internal/lsap.c new file mode 100644 index 0000000..23eb1a0 --- /dev/null +++ b/src/internal/lsap.c @@ -0,0 +1,689 @@ + +#include "igraph_lsap.h" + +#include "igraph_error.h" +#include "igraph_memory.h" + +/* #include */ +#include +#include /* DBL_MAX */ + +/* constants used for improving readability of code */ + +typedef enum covered_t { + COVERED = 1, + UNCOVERED = 0 +} covered_t; + +typedef enum assigned_t { + ASSIGNED = 1, + UNASSIGNED = 0 +} assigned_t; + +typedef enum marked_t { + MARKED = 1, + UNMARKED = 0 +} marked_t; + +typedef enum reduce_t { + REDUCE = 1, + NOREDUCE = 0 +} reduce_t; + +typedef struct { + igraph_integer_t n; /* order of problem */ + double **C; /* cost matrix */ + double **c; /* reduced cost matrix */ + igraph_integer_t *s; /* assignment */ + igraph_integer_t *f; /* column i is assigned to f[i] */ + igraph_integer_t na; /* number of assigned items; */ + igraph_integer_t runs; /* number of iterations */ + double cost; /* minimum cost */ +} AP; + +/* public interface */ + +/* constructors and destructor */ +static igraph_error_t ap_create_problem(AP **problem, const double *t, const igraph_integer_t 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 igraph_integer_t ap_get_result(AP *p, igraph_integer_t *res); +/* static int ap_costmatrix(AP *p, double **m); */ +/* static int ap_datamatrix(AP *p, double **m); */ +/* static int ap_iterations(AP *p); */ +static igraph_error_t 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 igraph_error_t preassign(AP *p); +static igraph_error_t cover(AP *p, covered_t *ri, covered_t *ci, reduce_t *res); +static void reduce(AP *p, const covered_t *ri, const covered_t *ci); + +/* Error message used on memory allocation failure. */ +static const char *memerr = "Insufficient memory for LSAP."; + +igraph_error_t ap_hungarian(AP *p) { + covered_t *ri; /* covered rows */ + covered_t *ci; /* covered columns */ + + const igraph_integer_t n = p->n; /* size of problem */ + p->runs = 0; + + /* allocate memory */ + /* Note: p is already on the finally stack here. */ + p->s = IGRAPH_CALLOC(1 + n, igraph_integer_t); + IGRAPH_CHECK_OOM(p->s, memerr); + + p->f = IGRAPH_CALLOC(1 + n, igraph_integer_t); + IGRAPH_CHECK_OOM(p->f, memerr); + + ri = IGRAPH_CALLOC(1 + n, covered_t); + IGRAPH_CHECK_OOM(ri, memerr); + IGRAPH_FINALLY(igraph_free, ri); + + ci = IGRAPH_CALLOC(1 + n, covered_t); + IGRAPH_CHECK_OOM(ci, memerr); + IGRAPH_FINALLY(igraph_free, ci); + + preprocess(p); + IGRAPH_CHECK(preassign(p)); + + while (p->na < n) { + reduce_t res; + IGRAPH_CHECK(cover(p, ri, ci, &res)); + if (REDUCE == res) { + reduce(p, ri, ci); + } + ++p->runs; + } + + /* check if assignment is a permutation of (1..n) */ + for (igraph_integer_t i = 1; i <= n; i++) { + igraph_integer_t ok = 0; + for (igraph_integer_t 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 (igraph_integer_t i = 1; i <= n; i++) { + p->cost += p->C[i][p->s[i]]; + } + + /* reset result back to base-0 indexing */ + for (igraph_integer_t i = 1; i <= n; i++) { + p->s[i - 1] = p->s[i] - 1; + } + + /* free memory */ + + IGRAPH_FREE(ri); + IGRAPH_FREE(ci); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/* abbreviated interface */ +igraph_integer_t ap_get_result(AP *p, igraph_integer_t *res) { + for (igraph_integer_t 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 */ +igraph_error_t ap_create_problem(AP **problem, const double *t, const igraph_integer_t n) { + *problem = IGRAPH_CALLOC(1, AP); + IGRAPH_CHECK_OOM(*problem, memerr); + IGRAPH_FINALLY(ap_free, *problem); + + AP *p = *problem; + + p->n = n; + + p->C = IGRAPH_CALLOC(n+1, double *); + IGRAPH_CHECK_OOM(p->C, memerr); + + p->c = IGRAPH_CALLOC(n+1, double *); + IGRAPH_CHECK_OOM(p->c, memerr); + + for (igraph_integer_t i = 1; i <= n; i++) { + p->C[i] = IGRAPH_CALLOC(n+1, double); + IGRAPH_CHECK_OOM(p->C[i], memerr); + p->c[i] = IGRAPH_CALLOC(n+1, double); + IGRAPH_CHECK_OOM(p->c[i], memerr); + } + + for (igraph_integer_t i = 1; i <= n; i++) { + for (igraph_integer_t 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; + + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* destructor */ +void ap_free(AP *p) { + IGRAPH_FREE(p->s); + IGRAPH_FREE(p->f); + + for (igraph_integer_t i = 1; i <= p->n; i++) { + IGRAPH_FREE(p->C[i]); + IGRAPH_FREE(p->c[i]); + } + + IGRAPH_FREE(p->C); + IGRAPH_FREE(p->c); + IGRAPH_FREE(p); +} + +/* set + get functions */ + +/* +void ap_show_data(AP *p) +{ + igraph_integer_t 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; +} + +igraph_integer_t 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) +{ + igraph_integer_t 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) { + igraph_integer_t 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) { + igraph_integer_t 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 */ +/*************************************************************/ + +igraph_error_t cover(AP *p, covered_t *ri, covered_t *ci, reduce_t *res) { + marked_t *mr; + igraph_integer_t r; + + const igraph_integer_t n = p->n; + + mr = IGRAPH_CALLOC(1 + p->n, marked_t); + IGRAPH_CHECK_OOM(mr, memerr); + + /* reset cover indices */ + for (igraph_integer_t 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 (igraph_integer_t i = 1; i <= n; i++) + if (mr[i] == MARKED) { + r = i; + break; + } + + if (r == 0) { + break; + } + for (igraph_integer_t 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; + + IGRAPH_FREE(mr); + *res = NOREDUCE; + return IGRAPH_SUCCESS; + } + } + mr[r] = UNMARKED; + } + + IGRAPH_FREE(mr); + *res = REDUCE; + return IGRAPH_SUCCESS; +} + +void reduce(AP *p, const covered_t *ri, const covered_t *ci) { + double min; + const igraph_integer_t n = p->n; + + /* find minimum in uncovered c-matrix */ + min = DBL_MAX; + for (igraph_integer_t i = 1; i <= n; i++) + for (igraph_integer_t 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 (igraph_integer_t i = 1; i <= n; i++) + for (igraph_integer_t 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; + } + } +} + +igraph_error_t preassign(AP *p) { + igraph_integer_t min, r, c, n, count; + assigned_t *ri, *ci; + igraph_integer_t *rz, *cz; + + n = p->n; + p->na = 0; + + /* row and column markers */ + ri = IGRAPH_CALLOC(1 + n, assigned_t); + IGRAPH_CHECK_OOM(ri, memerr); + IGRAPH_FINALLY(igraph_free, ri); + + ci = IGRAPH_CALLOC(1 + n, assigned_t); + IGRAPH_CHECK_OOM(ci, memerr); + IGRAPH_FINALLY(igraph_free, ci); + + /* row and column counts of zeroes */ + rz = IGRAPH_CALLOC(1 + n, igraph_integer_t); + IGRAPH_CHECK_OOM(rz, memerr); + IGRAPH_FINALLY(igraph_free, rz); + + cz = IGRAPH_CALLOC(1 + n, igraph_integer_t); + IGRAPH_CHECK_OOM(cz, memerr); + IGRAPH_FINALLY(igraph_free, cz); + + for (igraph_integer_t i = 1; i <= n; i++) { + count = 0; + for (igraph_integer_t j = 1; j <= n; j++) + if (p->c[i][j] == 0) { + ++count; + } + rz[i] = count; + } + + for (igraph_integer_t i = 1; i <= n; i++) { + count = 0; + for (igraph_integer_t 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 = IGRAPH_INTEGER_MAX; + r = 0; + for (igraph_integer_t 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 = IGRAPH_INTEGER_MAX; + for (igraph_integer_t 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 (igraph_integer_t i = 1; i <= n; i++) + if (p->c[i][c] == 0) { + --rz[i]; + } + } + } + + /* free memory */ + IGRAPH_FREE(ri); + IGRAPH_FREE(ci); + IGRAPH_FREE(rz); + IGRAPH_FREE(cz); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +void preprocess(AP *p) { + double min; + const igraph_integer_t n = p->n; + + /* subtract column minima in each row */ + for (igraph_integer_t i = 1; i <= n; i++) { + min = p->c[i][1]; + for (igraph_integer_t j = 2; j <= n; j++) + if (p->c[i][j] < min) { + min = p->c[i][j]; + } + for (igraph_integer_t j = 1; j <= n; j++) { + p->c[i][j] -= min; + } + } + + /* subtract row minima in each column */ + for (igraph_integer_t i = 1; i <= n; i++) { + min = p->c[1][i]; + for (igraph_integer_t j = 2; j <= n; j++) + if (p->c[j][i] < min) { + min = p->c[j][i]; + } + for (igraph_integer_t 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 assignment 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, extra tasks with zero costs + * 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. + */ +igraph_error_t igraph_solve_lsap(const 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 (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + n, igraph_matrix_nrow(c)); + } + if (n != igraph_matrix_ncol(c)) { + IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " + "not equal to number of tasks (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + n, igraph_matrix_ncol(c)); + } + IGRAPH_CHECK(igraph_vector_int_resize(p, n)); + igraph_vector_int_null(p); + + IGRAPH_CHECK(ap_create_problem(&ap, &MATRIX(*c, 0, 0), n)); + IGRAPH_FINALLY(ap_free, ap); + IGRAPH_CHECK(ap_hungarian(ap)); + ap_get_result(ap, VECTOR(*p)); + ap_free(ap); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/internal/qsort.c b/src/internal/qsort.c new file mode 100644 index 0000000..191b9f2 --- /dev/null +++ b/src/internal/qsort.c @@ -0,0 +1,267 @@ +/*- + * 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" +#include "igraph_error.h" + +#ifdef _MSC_VER + /* MSVC does not have inline when compiling C source files */ + #define inline __inline + #define __unused +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1927 + /* MSVC does not understand restrict before version 19.27 */ + #define restrict __restrict +#endif + +#ifndef __unused + #define __unused __attribute__ ((unused)) +#endif + +#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 * restrict a, char * restrict 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; + + /* if there are less than 2 elements, then sorting is not needed */ + if (IGRAPH_UNLIKELY(n < 2)) + return; +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/internal/qsort_r.c b/src/internal/qsort_r.c new file mode 100644 index 0000000..f7c0e54 --- /dev/null +++ b/src/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/internal/utils.c b/src/internal/utils.c new file mode 100644 index 0000000..0698e47 --- /dev/null +++ b/src/internal/utils.c @@ -0,0 +1,95 @@ +/* + IGraph library. + Copyright (C) 2023 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_interface.h" + +#include "internal/utils.h" + +/** + * \function igraph_i_matrix_subset_vertices + * \brief Subsets a matrix whose rows/columns correspond to graph vertices. + * + * This is a convenience function to subset a matrix computed from a graph. + * It takes a matrix whose rows and columns correspond to the vertices + * of a graph, and subsets it in-place to retain only some of the vertices. + * + * \param m A square matrix with the same number of rows/columns as the vertex + * count of \p graph. It will be modified in-place, deleting rows \em not present + * in \p from and columns \em not present in \p to. + * \param graph The corresponding graph. m[u,v] is assumed to contain + * a value associated with vertices \c u and \c v of \p graph, e.g. the graph + * distance between them, their similarity, etc. + * \param from Vertex set, these rows of the matrix will be retained. + * \param to Vertex set, these columns of the matrix will be retained. + * \return Error code. + * + * Time complexity: + * O(1) when taking all vertices, + * O(|from|*|to|) otherwise where |from| and |to| denote the size + * of the source and target vertex sets. + */ +igraph_error_t igraph_i_matrix_subset_vertices( + igraph_matrix_t *m, + const igraph_t *graph, + igraph_vs_t from, + igraph_vs_t to) { + + /* Assertion: the size of 'm' agrees with 'graph': */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t ncol = igraph_matrix_ncol(m); + igraph_integer_t nrow = igraph_matrix_nrow(m); + + IGRAPH_ASSERT(nrow == no_of_nodes && nrow == ncol); + + /* When taking all vertices, nothing needs to be done: */ + + if (igraph_vs_is_all(&from) && igraph_vs_is_all(&to)) { + return IGRAPH_SUCCESS; + } + + /* Otherwise, allocate a temporary matrix to copy the data into: */ + + igraph_vit_t fromvit, tovit; + igraph_matrix_t tmp; + + IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); + IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); + + IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); + IGRAPH_FINALLY(igraph_vit_destroy, &tovit); + + IGRAPH_MATRIX_INIT_FINALLY(&tmp, IGRAPH_VIT_SIZE(fromvit), IGRAPH_VIT_SIZE(tovit)); + + for (igraph_integer_t j=0; ! IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), j++) { + igraph_integer_t i; + for (IGRAPH_VIT_RESET(fromvit), i=0; ! IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) { + MATRIX(tmp, i, j) = MATRIX(*m, IGRAPH_VIT_GET(fromvit), IGRAPH_VIT_GET(tovit)); + } + } + + /* This is O(1) time */ + IGRAPH_CHECK(igraph_matrix_swap(m, &tmp)); + + igraph_matrix_destroy(&tmp); + igraph_vit_destroy(&tovit); + igraph_vit_destroy(&fromvit); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/internal/utils.h b/src/internal/utils.h new file mode 100644 index 0000000..94be451 --- /dev/null +++ b/src/internal/utils.h @@ -0,0 +1,32 @@ +/* + 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 . +*/ + +#ifndef IGRAPH_INTERNAL_UTILS_H +#define IGRAPH_INTERNAL_UTILS_H + +#include "igraph_datatype.h" +#include "igraph_iterators.h" +#include "igraph_matrix.h" + +igraph_error_t igraph_i_matrix_subset_vertices( + igraph_matrix_t *m, + const igraph_t *graph, + igraph_vs_t from, + igraph_vs_t to); + +#endif /* IGRAPH_INTERNAL_UTILS_H */ diff --git a/src/internal/zeroin.c b/src/internal/zeroin.c new file mode 100644 index 0000000..89ade54 --- /dev/null +++ b/src/internal/zeroin.c @@ -0,0 +1,201 @@ +/* -*- 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 + +igraph_error_t 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 IGRAPH_SUCCESS; + } + if (fb == 0.0) { + *Tol = 0.0; + *Maxit = 0; + *res = b; + return IGRAPH_SUCCESS; + } + + 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 calculated in the form p/q; */ + igraph_real_t q; /* division operations are 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 best approximation */ + a = b; b = c; c = a; + 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 IGRAPH_SUCCESS; /* 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/io/dimacs.c b/src/io/dimacs.c new file mode 100644 index 0000000..3539db8 --- /dev/null +++ b/src/io/dimacs.c @@ -0,0 +1,389 @@ +/* -*- 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 + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ +#define IGRAPH_DIMACS_MAX_VERTEX_COUNT (1 << 20) +#define IGRAPH_DIMACS_MAX_EDGE_COUNT (1 << 20) +#else +#define IGRAPH_DIMACS_MAX_VERTEX_COUNT INT32_MAX +#define IGRAPH_DIMACS_MAX_EDGE_COUNT INT32_MAX +#endif + +/** + * \function igraph_read_graph_dimacs + * \brief Read a graph in DIMACS format (deprecated alias). + * + * \deprecated-by igraph_read_graph_dimacs_flow 0.10.0 + */ +igraph_error_t igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, + igraph_strvector_t *problem, + igraph_vector_int_t *label, + igraph_integer_t *source, + igraph_integer_t *target, + igraph_vector_t *capacity, + igraph_bool_t directed) { + return igraph_read_graph_dimacs_flow( + graph, instream, problem, label, source, target, capacity, directed + ); +} + +#define EXPECT(actual, expected) \ + do { \ + if ((actual) != (expected)) { \ + IGRAPH_ERROR("Reading DIMACS flow problem file failed.", IGRAPH_PARSEERROR); \ + } \ + } while (0) + +#define CHECK_VID(vid) \ + do { \ + if (vid > IGRAPH_DIMACS_MAX_VERTEX_COUNT) { \ + IGRAPH_ERRORF("Vertex ID %" IGRAPH_PRId " too large in DIMACS file.", IGRAPH_PARSEERROR, vid); \ + } \ + } while(0) + +/** + * \function igraph_read_graph_dimacs_flow + * \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 + * http://archive.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 c the line is a comment line and it is + * ignored. There is one problem line (\c 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 + * (\c max or \c edge), the number of vertices, + * and number of edges in the graph. In MAX problems, + * exactly two node identification lines are expected + * (\c n), one for the source, and one for the target vertex. + * These have two fields: the ID of the vertex and the type of the + * vertex, either \c s ( = source) or \c t ( = target). + * Arc lines start with \c a and have three fields: the source vertex, + * the target vertex and the edge capacity. In EDGE problems, + * there may be a node line (\c n) for each node. It specifies the + * node index and an integer node label. Nodes for which no explicit + * label was specified will use their index as label. In EDGE problems, + * each edge is specified as an edge line (\c e). + * + * + * Within DIMACS files, vertex IDs are numbered from 1. + * + * \param graph Pointer to an uninitialized graph object. + * \param instream The file to read from. + * \param problem If not \c NULL, it will contain the problem type. + * \param label If not \c NULL, node labels will be stored here for \c edge + * problems. Ignored for \c max problems. + * \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 \c NULL. + * \param target Pointer to an integer, the (igraph) ID of the target + * node will be stored here. It is ignored if \c 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() + */ +igraph_error_t igraph_read_graph_dimacs_flow( + igraph_t *graph, FILE *instream, + igraph_strvector_t *problem, + igraph_vector_int_t *label, + igraph_integer_t *source, + igraph_integer_t *target, + igraph_vector_t *capacity, + igraph_bool_t directed) { + + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = -1; + igraph_integer_t no_of_edges = -1; + igraph_integer_t tsource = -1; + igraph_integer_t ttarget = -1; + char prob[21]; + enum { + PROBLEM_NONE, + PROBLEM_EDGE, + PROBLEM_MAX + } problem_type = PROBLEM_NONE; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + if (capacity) { + igraph_vector_clear(capacity); + } + + while (!feof(instream)) { + int read; + char str[2]; + + IGRAPH_ALLOW_INTERRUPTION(); + + read = fscanf(instream, "%2c", str); + if (feof(instream)) { + break; + } + EXPECT(read, 1); + switch (str[0]) { + igraph_integer_t tmp, tmp2; + igraph_integer_t 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 %" IGRAPH_PRId " %" IGRAPH_PRId "", prob, + &no_of_nodes, &no_of_edges); + EXPECT(read, 3); + if (no_of_nodes > IGRAPH_DIMACS_MAX_VERTEX_COUNT) { + IGRAPH_ERROR("Vertex count too large in DIMACS file.", IGRAPH_PARSEERROR); + } + if (no_of_nodes < 0) { + IGRAPH_ERROR("Invalid (negative) vertex count in DIMACS file.", IGRAPH_PARSEERROR); + } + if (no_of_edges > IGRAPH_DIMACS_MAX_EDGE_COUNT) { + IGRAPH_ERROR("Edge count too large in DIMACS file.", IGRAPH_PARSEERROR); + } + if (no_of_edges < 0) { + IGRAPH_ERROR("Invalid (negative) edge count in DIMACS file.", IGRAPH_PARSEERROR); + } + if (!strcmp(prob, "edge")) { + /* edge list */ + problem_type = PROBLEM_EDGE; + if (label) { + IGRAPH_CHECK(igraph_vector_int_range(label, 1, no_of_nodes+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_push_back(problem, prob)); + } + IGRAPH_CHECK(igraph_vector_int_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, "%" IGRAPH_PRId " %1s", &tmp, str); + EXPECT(read, 2); + if (str[0] == 's') { + if (tsource != -1) { + IGRAPH_ERROR("Reading DIMACS file: multiple source vertex line.", + IGRAPH_PARSEERROR); + } else { + tsource = tmp; + } + } else if (str[0] == 't') { + if (ttarget != -1) { + IGRAPH_ERROR("Reading DIMACS file: multiple target vertex line.", + IGRAPH_PARSEERROR); + } else { + ttarget = tmp; + } + } else { + IGRAPH_ERROR("Invalid node descriptor line in DIMACS file.", + IGRAPH_PARSEERROR); + } + } else { /* PROBLEM_EDGE */ + read = fscanf(instream, "%" IGRAPH_PRId " %" IGRAPH_PRId "", &tmp, &tmp2); + EXPECT(read, 1); + if (label) { + if (tmp < 0 || tmp >= no_of_nodes) { + IGRAPH_ERRORF("Invalid node index %" IGRAPH_PRId " in DIMACS file. " + "Number of nodes was given as %" IGRAPH_PRId".", + IGRAPH_PARSEERROR, tmp, no_of_nodes); + } + 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, "%" IGRAPH_PRId " %" IGRAPH_PRId " %lf", &from, &to, &cap); + EXPECT(read, 3); + CHECK_VID(from); + CHECK_VID(to); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from - 1)); + IGRAPH_CHECK(igraph_vector_int_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, "%" IGRAPH_PRId " %" IGRAPH_PRId "", &from, &to); + EXPECT(read, 2); + CHECK_VID(from); + CHECK_VID(to); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from - 1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to - 1)); + break; + + default: + IGRAPH_ERROR("Unknown line type in DIMACS file.", IGRAPH_PARSEERROR); + } + + /* Go to next line */ + while (!feof(instream) && getc(instream) != '\n') ; + } + + if (source) { + *source = tsource - 1; + } + if (target) { + *target = ttarget - 1; + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_write_graph_dimacs + * \brief Write a graph in DIMACS format (deprecated alias). + * + * \deprecated-by igraph_write_graph_dimacs_flow 0.10.0 + */ +igraph_error_t igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity) { + return igraph_write_graph_dimacs_flow(graph, outstream, source, target, capacity); +} + +/** + * \function igraph_write_graph_dimacs_flow + * \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_flow(), 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 \ref igraph_read_graph_dimacs_flow() + */ +igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outstream, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_eit_t it; + igraph_integer_t 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 %" IGRAPH_PRId " %" IGRAPH_PRId "\nn %" IGRAPH_PRId " s\nn %" IGRAPH_PRId " 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 %" IGRAPH_PRId " %" IGRAPH_PRId " ", + from + 1, 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 IGRAPH_SUCCESS; +} diff --git a/src/io/dl-header.h b/src/io/dl-header.h new file mode 100644 index 0000000..b349109 --- /dev/null +++ b/src/io/dl-header.h @@ -0,0 +1,53 @@ +/* + 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" + +/* TODO: Find out maximum supported vertex count. */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ +#define IGRAPH_DL_MAX_VERTEX_COUNT (1 << 20) +#else +#define IGRAPH_DL_MAX_VERTEX_COUNT INT32_MAX +#endif + +typedef enum { IGRAPH_DL_MATRIX, + IGRAPH_DL_EDGELIST1, IGRAPH_DL_NODELIST1 + } igraph_i_dl_type_t; + +typedef struct { + void *scanner; + int eof; + char errmsg[300]; + igraph_error_t igraph_errno; + int mode; + igraph_integer_t n; + igraph_integer_t from, to; + igraph_vector_int_t edges; + igraph_vector_t weights; + igraph_strvector_t labels; + igraph_trie_t trie; + igraph_i_dl_type_t type; +} igraph_i_dl_parsedata_t; diff --git a/src/io/dl.c b/src/io/dl.c new file mode 100644 index 0000000..103033f --- /dev/null +++ b/src/io/dl.c @@ -0,0 +1,203 @@ +/* -*- 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" +#include "io/parsers/dl-parser.h" + +int igraph_dl_yylex_init_extra (igraph_i_dl_parsedata_t *user_defined, + void *scanner); +int igraph_dl_yylex_destroy(void *scanner); +int igraph_dl_yyparse(igraph_i_dl_parsedata_t *context); +void igraph_dl_yyset_in(FILE *in_str, void *yyscanner); + +/* for IGRAPH_FINALLY, which assumes that destructor functions return void */ +void igraph_dl_yylex_destroy_wrapper (void *scanner ) { + (void) igraph_dl_yylex_destroy(scanner); +} + +/** + * \function igraph_read_graph_dl + * \brief Reads 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 + */ + +igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, + igraph_bool_t directed) { + + igraph_integer_t 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; + context.errmsg[0] = '\0'; + context.igraph_errno = IGRAPH_SUCCESS; + + IGRAPH_VECTOR_INT_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_wrapper, context.scanner); + + igraph_dl_yyset_in(instream, context.scanner); + + /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_dl_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ + if (context.errmsg[0] != 0) { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); + } else { + IGRAPH_ERROR("Cannot read DL file.", IGRAPH_PARSEERROR); + } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read DL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading DL file.", err); + } + + /* Extend the weight vector, if needed */ + n = igraph_vector_size(&context.weights); + n2 = igraph_vector_int_size(&context.edges) / 2; + if (n != 0) { + IGRAPH_CHECK(igraph_vector_resize(&context.weights, n2)); + for (; n < n2; n++) { + VECTOR(context.weights)[n] = IGRAPH_NAN; + } + } + + /* Check number of vertices */ + if (n2 > 0) { + n = igraph_vector_int_max(&context.edges); + } else { + n = 0; + } + if (n >= context.n) { + IGRAPH_WARNING("More vertices than specified in `DL' file"); + context.n = n; + } + + /* Prepare attributes */ + + /* Labels */ + if (igraph_strvector_size(&context.labels) != 0) { + namevec = (const igraph_strvector_t*) &context.labels; + } else if (igraph_trie_size(&context.trie) != 0) { + namevec = igraph_i_trie_borrow_keys(&context.trie); + } + 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; + } + + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_vertices(graph, 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_int_destroy(&context.edges); + igraph_vector_destroy(&context.weights); + igraph_dl_yylex_destroy(context.scanner); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} diff --git a/src/io/dot.c b/src/io/dot.c new file mode 100644 index 0000000..4b8a904 --- /dev/null +++ b/src/io/dot.c @@ -0,0 +1,322 @@ +/* -*- 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 & strdup */ +#include "math/safe_intop.h" /* IGRAPH_MAX_EXACT_REAL */ + +#include +#include + +#define CHECK(cmd) do { int ret=cmd; if (ret<0) IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_EFILE); } while (0) + +static igraph_error_t dot_escape(const char *orig, igraph_vector_char_t* result) { + /* do we have to escape the string at all? */ + igraph_integer_t i, j, len = strlen(orig), newlen = 0; + igraph_bool_t need_quote = false, is_number = true; + + /* first, check whether the string is equal to some reserved word, or empty */ + if (!strcasecmp(orig, "graph") || !strcasecmp(orig, "digraph") || + !strcasecmp(orig, "node") || !strcasecmp(orig, "edge") || + !strcasecmp(orig, "strict") || !strcasecmp(orig, "subgraph") || len == 0) { + need_quote = true; + is_number = false; + } + + /* 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 = true; + newlen++; + } + } else if (orig[i] == '_') { + is_number = false; newlen++; + } else if (orig[i] == '\\' || orig[i] == '"' || orig[i] == '\n') { + need_quote = true; is_number = false; newlen += 2; /* will be escaped */ + } else if (isalpha(orig[i])) { + is_number = false; newlen++; + } else { + is_number = false; need_quote = true; newlen++; + } + } + if (is_number && len > 0 && orig[len - 1] == '.') { + is_number = false; + } + if (!is_number && isdigit(orig[0])) { + need_quote = true; + } + + if (is_number || !need_quote) { + IGRAPH_CHECK(igraph_vector_char_resize(result, newlen + 1)); + memcpy(VECTOR(*result), orig, newlen); + } else { + newlen += 2; + IGRAPH_CHECK(igraph_vector_char_resize(result, newlen + 1)); + VECTOR(*result)[0] = '"'; + VECTOR(*result)[newlen - 1] = '"'; + + /* Escape quotes, backslashes and newlines. + * Even though the format spec at https://graphviz.org/doc/info/lang.html + * claims that only quotes need escaping, escaping backslashes appears to + * be necessary as well for GraphViz to render labels correctly. + * Tested with GraphViz 2.50. */ + for (i = 0, j = 1; i < len; i++) { + if (orig[i] == '\n') { + VECTOR(*result)[j++] = '\\'; + VECTOR(*result)[j++] = 'n'; + continue; + } + if (orig[i] == '\\' || orig[i] == '"') { + VECTOR(*result)[j++] = '\\'; + } + VECTOR(*result)[j++] = orig[i]; + } + } + VECTOR(*result)[newlen] = 0; + + return IGRAPH_SUCCESS; +} + +/* Writes exactly representable integral values in standard integer notation, without decimal points or e-notation. + * Floating point values that are written with e-notation are quoted, otherwise the Graphviz parser cannot handle them. + */ +static igraph_error_t fprint_integral_or_precise(FILE *file, igraph_real_t x, igraph_vector_char_t *buf) { + if (fabs(x) <= IGRAPH_MAX_EXACT_REAL && floor(x) == x) { + /* write exactly representable integral values in standard integer notation; + * the above conditional skips +-Inf and NaN */ + CHECK(fprintf(file, "%.f", x)); + } else { + /* write as precise float and quote if necessary */ + char str[50]; /* large enough to hold any precisely printed real */ + CHECK(igraph_real_snprintf_precise(str, sizeof(str) / sizeof(str[0]), x)); + IGRAPH_CHECK(dot_escape(str, buf)); + CHECK(fputs(VECTOR(*buf), file)); + } + 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, no visualization + * information is written. + * + * + * This format is meant solely for interoperability with Graphviz. + * It is not recommended for data exchange or archival. + * + * \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 + */ +igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + const igraph_bool_t directed = igraph_is_directed(graph); + const char *edgeop = directed ? "->" : "--"; + igraph_strvector_t gnames, vnames, enames; + igraph_vector_int_t gtypes, vtypes, etypes; + igraph_vector_t numv; + igraph_strvector_t strv; + igraph_vector_bool_t boolv; + igraph_vector_char_t buf, buf2; + + IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&etypes, 0); + IGRAPH_CHECK(igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &vnames, &vtypes, + &enames, &etypes)); + + IGRAPH_VECTOR_INIT_FINALLY(&numv, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 0); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 0); + + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&buf, 0); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&buf2, 0); + + CHECK(fprintf(outstream, "/* Created by igraph %s */\n", IGRAPH_VERSION)); + + if (directed) { + CHECK(fprintf(outstream, "digraph {\n")); + } else { + CHECK(fprintf(outstream, "graph {\n")); + } + + /* Write the graph attributes */ + if (igraph_vector_int_size(>ypes) > 0) { + CHECK(fprintf(outstream, " graph [\n")); + for (igraph_integer_t i = 0; i < igraph_vector_int_size(>ypes); i++) { + const char *name; + name = igraph_strvector_get(&gnames, i); + IGRAPH_CHECK(dot_escape(name, &buf)); + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv)); + CHECK(fprintf(outstream, " %s=", VECTOR(buf))); + IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0], &buf)); + CHECK(fputc('\n', outstream)); + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); + s = igraph_strvector_get(&strv, 0); + IGRAPH_CHECK(dot_escape(s, &buf2)); + CHECK(fprintf(outstream, " %s=%s\n", VECTOR(buf), VECTOR(buf2))); + } 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", VECTOR(buf), VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("Boolean graph attribute was converted to numeric."); + } else { + IGRAPH_WARNING("A non-numeric, non-string, non-boolean graph attribute was ignored."); + } + } + CHECK(fprintf(outstream, " ];\n")); + } + + /* Write the vertices */ + if (igraph_vector_int_size(&vtypes) > 0) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + CHECK(fprintf(outstream, " %" IGRAPH_PRId " [\n", i)); + for (igraph_integer_t j = 0; j < igraph_vector_int_size(&vtypes); j++) { + const char *name; + name = igraph_strvector_get(&vnames, j); + IGRAPH_CHECK(dot_escape(name, &buf)); + if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, igraph_vss_1(i), &numv)); + CHECK(fprintf(outstream, " %s=", VECTOR(buf))); + IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0], &buf)); + CHECK(fputc('\n', outstream)); + } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_STRING) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, igraph_vss_1(i), &strv)); + s = igraph_strvector_get(&strv, 0); + IGRAPH_CHECK(dot_escape(s, &buf2)); + CHECK(fprintf(outstream, " %s=%s\n", VECTOR(buf), VECTOR(buf2))); + } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name, igraph_vss_1(i), &boolv)); + CHECK(fprintf(outstream, " %s=%d\n", VECTOR(buf), 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."); + } + } + CHECK(fprintf(outstream, " ];\n")); + } + } else { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + CHECK(fprintf(outstream, " %" IGRAPH_PRId ";\n", i)); + } + } + CHECK(fprintf(outstream, "\n")); + + /* Write the edges */ + if (igraph_vector_int_size(&etypes) > 0) { + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + CHECK(fprintf(outstream, " %" IGRAPH_PRId " %s %" IGRAPH_PRId " [\n", from, edgeop, to)); + for (igraph_integer_t j = 0; j < igraph_vector_int_size(&etypes); j++) { + const char *name; + name = igraph_strvector_get(&enames, j); + IGRAPH_CHECK(dot_escape(name, &buf)); + if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, + name, igraph_ess_1(i), &numv)); + CHECK(fprintf(outstream, " %s=", VECTOR(buf))); + IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0], &buf)); + CHECK(fputc('\n', outstream)); + } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_STRING) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, + name, igraph_ess_1(i), &strv)); + s = igraph_strvector_get(&strv, 0); + IGRAPH_CHECK(dot_escape(s, &buf2)); + CHECK(fprintf(outstream, " %s=%s\n", VECTOR(buf), VECTOR(buf2))); + } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, + name, igraph_ess_1(i), &boolv)); + CHECK(fprintf(outstream, " %s=%d\n", VECTOR(buf), VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean edge attribute was converted to numeric."); + } else { + IGRAPH_WARNING("A non-numeric, non-string graph attribute ignored."); + } + } + CHECK(fprintf(outstream, " ];\n")); + } + } else { + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + CHECK(fprintf(outstream, " %" IGRAPH_PRId " %s %" IGRAPH_PRId ";\n", from, edgeop, to)); + } + } + CHECK(fprintf(outstream, "}\n")); + + igraph_vector_char_destroy(&buf2); + igraph_vector_char_destroy(&buf); + igraph_vector_bool_destroy(&boolv); + igraph_strvector_destroy(&strv); + igraph_vector_destroy(&numv); + igraph_vector_int_destroy(&etypes); + igraph_vector_int_destroy(&vtypes); + igraph_vector_int_destroy(>ypes); + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&gnames); + IGRAPH_FINALLY_CLEAN(11); + + return IGRAPH_SUCCESS; +} + +#undef CHECK diff --git a/src/io/edgelist.c b/src/io/edgelist.c new file mode 100644 index 0000000..ed1d7c3 --- /dev/null +++ b/src/io/edgelist.c @@ -0,0 +1,161 @@ +/* -*- 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 "io/parse_utils.h" + +/** + * \section about_loadsave + * + * These functions can write a graph to a file, or read a graph + * from a file. + * + * They assume that the current locale uses a decimal point and not + * a decimal comma. See \ref igraph_enter_safelocale() and + * \ref igraph_exit_safelocale() for more information. + * + * 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. + * + * + * The largest vertex ID plus one, or the parameter \p n determines the vertex count, + * whichever is larger. See \ref igraph_read_graph_ncol() for reading files where + * vertices are specified by name instead of by a numerical vertex ID. + * + * \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. + */ +igraph_error_t igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, + igraph_integer_t n, igraph_bool_t directed) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t from, to; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 100)); + + for (;;) { + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_i_fskip_whitespace(instream)); + + if (feof(instream)) break; + + IGRAPH_CHECK(igraph_i_fget_integer(instream, &from)); + IGRAPH_CHECK(igraph_i_fget_integer(instream, &to)); + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Protect from very large memory allocations when fuzzing. */ +#define IGRAPH_EDGELIST_MAX_VERTEX_COUNT (1L << 20) + if (from > IGRAPH_EDGELIST_MAX_VERTEX_COUNT || to > IGRAPH_EDGELIST_MAX_VERTEX_COUNT) { + IGRAPH_ERROR("Vertex count too large in edgelist file.", IGRAPH_EINVAL); + } +#endif + + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup loadsave + * \function igraph_write_graph_edgelist + * \brief Writes the edge list of a graph to a file. + * + * + * Edges are represented as pairs of 0-based vertex indices. + * 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. + */ +igraph_error_t 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, "%" IGRAPH_PRId " %" IGRAPH_PRId "\n", + from, + to); + if (ret < 0) { + IGRAPH_ERROR("Failed writing edgelist.", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/io/gml-header.h b/src/io/gml-header.h new file mode 100644 index 0000000..b8d590d --- /dev/null +++ b/src/io/gml-header.h @@ -0,0 +1,42 @@ +/* + 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, 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; + char errmsg[300]; + igraph_error_t igraph_errno; + int depth; + igraph_gml_tree_t *tree; +} igraph_i_gml_parsedata_t; + +/** + * Initializes a GML parser context. + */ +igraph_error_t 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/io/gml-tree.c b/src/io/gml-tree.c new file mode 100644 index 0000000..59b0807 --- /dev/null +++ b/src/io/gml-tree.c @@ -0,0 +1,279 @@ +/* + IGraph library. + Copyright (C) 2007-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 +*/ + +#include "igraph_memory.h" +#include "igraph_error.h" + +#include "io/gml-tree.h" + +#include + +igraph_error_t igraph_gml_tree_init_integer(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_integer_t value) { + + igraph_integer_t *p; + + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&t->types, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); + + /* names */ + VECTOR(t->names)[0] = (void*) name; + + /* line number */ + VECTOR(t->lines)[0] = line; + + /* types */ + VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_INTEGER; + + /* children */ + p = IGRAPH_CALLOC(1, igraph_integer_t); + IGRAPH_CHECK_OOM(p, "Cannot create integer GML tree node."); + *p = value; + VECTOR(t->children)[0] = p; + + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_gml_tree_init_real(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_real_t value) { + + igraph_real_t *p; + + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&t->types, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); + + /* names */ + VECTOR(t->names)[0] = (void*) name; + + /* line number */ + VECTOR(t->lines)[0] = line; + + /* types */ + VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_REAL; + + /* children */ + p = IGRAPH_CALLOC(1, igraph_real_t); + IGRAPH_CHECK_OOM(p, "Cannot create real GML tree node."); + *p = value; + VECTOR(t->children)[0] = p; + + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_gml_tree_init_string(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + const char *value) { + + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&t->types, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); + + /* names */ + VECTOR(t->names)[0] = (void*) name; + + /* line number */ + VECTOR(t->lines)[0] = line; + + /* types */ + VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_STRING; + + /* children */ + VECTOR(t->children)[0] = (void*) value; + + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_gml_tree_init_tree(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_gml_tree_t *value) { + + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 1); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&t->types, 1); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); + + /* names */ + VECTOR(t->names)[0] = (void*) name; + + /* line number */ + VECTOR(t->lines)[0] = line; + + /* types */ + VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_TREE; + + /* children */ + VECTOR(t->children)[0] = value; + + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; + +} + +igraph_error_t igraph_gml_tree_init_empty(igraph_gml_tree_t *t) { + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 0); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&t->types, 0); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 0); + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; +} + +/* merge is destructive, the _second_ tree is destroyed */ +igraph_error_t igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2) { + igraph_integer_t 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_CHECK(igraph_vector_int_push_back(&t1->lines, VECTOR(t2->lines)[i])); + } + + igraph_vector_ptr_destroy(&t2->names); + igraph_vector_char_destroy(&t2->types); + igraph_vector_ptr_destroy(&t2->children); + igraph_vector_int_destroy(&t2->lines); + + return IGRAPH_SUCCESS; +} + +void igraph_gml_tree_destroy(igraph_gml_tree_t *t) { + + igraph_integer_t i, n = igraph_vector_ptr_size(&t->children); + for (i = 0; i < n; i++) { + igraph_i_gml_tree_type_t type = (igraph_i_gml_tree_type_t) 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_vector_int_destroy(&t->lines); + IGRAPH_FREE(t); +} + +igraph_integer_t igraph_gml_tree_length(const igraph_gml_tree_t *t) { + return igraph_vector_ptr_size(&t->names); +} + +igraph_integer_t igraph_gml_tree_find( + const igraph_gml_tree_t *t, const char *name, igraph_integer_t from +) { + igraph_integer_t 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; +} + +igraph_integer_t igraph_gml_tree_findback( + const igraph_gml_tree_t *t, const char *name, igraph_integer_t from +) { + while ( from >= 0 && (! VECTOR(t->names)[from] || + strcmp(VECTOR(t->names)[from], name)) ) { + from--; + } + + return from; +} + +igraph_i_gml_tree_type_t igraph_gml_tree_type(const igraph_gml_tree_t *t, igraph_integer_t pos) { + return (igraph_i_gml_tree_type_t) VECTOR(t->types)[pos]; +} + +const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, igraph_integer_t pos) { + return VECTOR(t->names)[pos]; +} + +igraph_integer_t igraph_gml_tree_line(const igraph_gml_tree_t *t, igraph_integer_t pos) { + return VECTOR(t->lines)[pos]; +} + +igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, + igraph_integer_t 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, + igraph_integer_t pos) { + igraph_real_t *d = VECTOR(t->children)[pos]; + return *d; +} + +const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, + igraph_integer_t 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, + igraph_integer_t pos) { + igraph_gml_tree_t *tree = VECTOR(t->children)[pos]; + return tree; +} + +void igraph_gml_tree_delete(igraph_gml_tree_t *t, igraph_integer_t 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/io/gml-tree.h b/src/io/gml-tree.h new file mode 100644 index 0000000..26e9308 --- /dev/null +++ b/src/io/gml-tree.h @@ -0,0 +1,87 @@ +/* + IGraph library. + Copyright (C) 2007-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 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_vector_int_t lines; /* line numbers where names appear */ +} igraph_gml_tree_t; + +igraph_error_t igraph_gml_tree_init_integer(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_integer_t value); +igraph_error_t igraph_gml_tree_init_real(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_real_t value); +igraph_error_t igraph_gml_tree_init_string(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + const char *value); +igraph_error_t igraph_gml_tree_init_tree(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_gml_tree_t *value); +igraph_error_t igraph_gml_tree_init_empty(igraph_gml_tree_t *t); +void igraph_gml_tree_destroy(igraph_gml_tree_t *t); + +void igraph_gml_tree_delete(igraph_gml_tree_t *t, igraph_integer_t pos); +igraph_error_t igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2); + +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gml_tree_length(const igraph_gml_tree_t *t); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gml_tree_find(const igraph_gml_tree_t *t, + const char *name, igraph_integer_t from); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gml_tree_findback(const igraph_gml_tree_t *t, + const char *name, igraph_integer_t from); +IGRAPH_FUNCATTR_PURE igraph_i_gml_tree_type_t igraph_gml_tree_type(const igraph_gml_tree_t *t, igraph_integer_t pos); +IGRAPH_FUNCATTR_PURE const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, igraph_integer_t pos); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gml_tree_line(const igraph_gml_tree_t *t, igraph_integer_t pos); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, + igraph_integer_t pos); +IGRAPH_FUNCATTR_PURE igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, + igraph_integer_t pos); +IGRAPH_FUNCATTR_PURE const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, + igraph_integer_t pos); + +IGRAPH_FUNCATTR_PURE igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, + igraph_integer_t pos); + +__END_DECLS + +#endif diff --git a/src/io/gml.c b/src/io/gml.c new file mode 100644 index 0000000..f5ddc1c --- /dev/null +++ b/src/io/gml.c @@ -0,0 +1,1320 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2005-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 +*/ + +#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 "internal/hacks.h" /* strdup, strncasecmp */ +#include "math/safe_intop.h" + +#include "io/gml-header.h" +#include "io/parsers/gml-parser.h" + +#include +#include +#include + +int igraph_gml_yylex_init_extra(igraph_i_gml_parsedata_t *user_defined, void *scanner); +int igraph_gml_yylex_destroy(void *scanner); +int igraph_gml_yyparse(igraph_i_gml_parsedata_t *context); +void igraph_gml_yyset_in(FILE *in_str, void *yyscanner); + +/* Checks if a null-terminated string needs encoding or decoding. + * + * Encoding is needed when an " or & character is present. + * + * Decoding is needed when an &xyz; style entity is present, so it's sufficient to look + * for & characters. " characters are never present in the raw strings returned by the + * GML parser, so we can use the same function to detect the need for either encoding + * or decoding. + */ +static igraph_bool_t needs_coding(const char *str) { + while (*str) { + if (*str == '&' || *str == '"') { + return true; + } + str++; + } + return false; +} + +/* Encode & and " character in 'src' to & and " + * '*dest' must be deallocated by the caller. + */ +static igraph_error_t entity_encode(const char *src, char **dest, igraph_bool_t only_quot) { + igraph_integer_t destlen = 0; + const char *s; + char *d; + + for (s = src; *s != '\0'; s++, destlen++) { + switch (*s) { + case '&': /* & */ + if (! only_quot) { + destlen += 4; + } + break; + case '"': /* " */ + destlen += 5; break; + } + } + *dest = IGRAPH_CALLOC(destlen + 1, char); + IGRAPH_CHECK_OOM(dest, "Not enough memory to encode string for GML export."); + for (s = src, d = *dest; *s != '\0'; s++, d++) { + switch (*s) { + case '&': + if (! only_quot) { + strcpy(d, "&"); + d += 4; + } else { + *d = *s; + } + break; + case '"': + strcpy(d, """); d += 5; break; + default: + *d = *s; + } + } + *d = '\0'; + return IGRAPH_SUCCESS; +} + +/* Decode the five standard predefined XML entities. Unknown entities or stray & characters + * will be passed through unchanged. '*dest' must be deallocated by the caller. + * If '*warned' is false, warnings will be issued for unsupported entities and + * '*warned' will be set to true. This is to prevent a flood of warnings in some files. + */ +static igraph_error_t entity_decode(const char *src, char **dest, igraph_bool_t *warned) { + const char *entity_names[] = { + """, "&", "'", "<", ">" + }; + + const char entity_values[] = { + '"', '&', '\'', '<', '>' + }; + + const int entity_count = sizeof entity_values / sizeof entity_values[0]; + + const char *s; + char *d; + size_t len = strlen(src); + *dest = IGRAPH_CALLOC(len+1, char); /* at most as much storage needed as for 'src' */ + IGRAPH_CHECK_OOM(dest, "Not enough memory to decode string during GML import."); + + for (s = src, d = *dest; *s != '\0';) { + if (*s == '&') { + int i; + for (i=0; i < entity_count; i++) { + size_t entity_len = strlen(entity_names[i]); + if (!strncasecmp(s, entity_names[i], entity_len)) { + *d++ = entity_values[i]; + s += entity_len; + break; + } + } + /* None of the known entities match, report warning and pass through unchanged. */ + if (i == entity_count) { + if (! *warned) { + const int max_entity_name_length = 34; + int j = 0; + while (s[j] != '\0' && s[j] != ';' && j < max_entity_name_length) { + j++; + } + if (s[j] == '\0' || j == max_entity_name_length) { + IGRAPH_WARNING("Unterminated entity or stray & character found, will be returned verbatim."); + } else { + IGRAPH_WARNINGF("One or more unknown entities will be returned verbatim (%.*s).", j+1, s); + } + *warned = true; /* warn only once */ + } + *d++ = *s++; + } + } else { + *d++ = *s++; + } + } + *d = '\0'; + + return IGRAPH_SUCCESS; +} + +static void igraph_i_gml_destroy_attrs(igraph_vector_ptr_t **ptr) { + + igraph_vector_ptr_t *vec; + for (igraph_integer_t i = 0; i < 3; i++) { + vec = ptr[i]; + for (igraph_integer_t 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 if (atrec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *value = (igraph_strvector_t*)atrec->value; + if (value != 0) { + igraph_strvector_destroy(value); + IGRAPH_FREE(value); + } + } else { + /* Some empty attribute records may have been created for composite attributes */ + } + IGRAPH_FREE(atrec->name); + IGRAPH_FREE(atrec); + } + igraph_vector_ptr_destroy(vec); + } +} + +static igraph_real_t igraph_i_gml_toreal(igraph_gml_tree_t *node, igraph_integer_t pos) { + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, pos); + + switch (type) { + case IGRAPH_I_GML_TREE_INTEGER: + return igraph_gml_tree_get_integer(node, pos); + case IGRAPH_I_GML_TREE_REAL: + return igraph_gml_tree_get_real(node, pos); + case IGRAPH_I_GML_TREE_TREE: + return IGRAPH_NAN; /* default value of NaN when composite is ignored */ + default: + /* Must never reach here, regardless of the contents of the GML file. */ + IGRAPH_FATALF("Unexpected node type in GML tree, line %" IGRAPH_PRId ".", + igraph_gml_tree_line(node, pos)); /* LCOV_EXCL_LINE */ + } +} + +static const char *igraph_i_gml_tostring(igraph_gml_tree_t *node, igraph_integer_t pos) { + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, pos); + static char tmp[100]; + const char *p = tmp; + igraph_integer_t 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), "%" IGRAPH_PRId, 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; + case IGRAPH_I_GML_TREE_TREE: + tmp[0] = '\0'; /* default value of "" when composite is ignored */ + break; + default: + /* Must never reach here, regardless of the contents of the GML file. */ + IGRAPH_FATALF("Unexpected node type in GML tree, line %" IGRAPH_PRId ".", + igraph_gml_tree_line(node, pos)); /* LCOV_EXCL_LINE */ + } + + return p; +} + +igraph_error_t igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t *context) { + context->depth = 0; + context->scanner = NULL; + context->tree = NULL; + context->errmsg[0] = '\0'; + context->igraph_errno = IGRAPH_SUCCESS; + + return IGRAPH_SUCCESS; +} + +void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t *context) { + if (context->tree != NULL) { + igraph_gml_tree_destroy(context->tree); + context->tree = NULL; + } + + if (context->scanner != NULL) { + (void) igraph_gml_yylex_destroy(context->scanner); + context->scanner = NULL; + } +} + +/* Takes a vector of attribute records and removes those elements + * whose type is unspecified, i.e. IGRAPH_ATTRIBUTE_UNSPECIFIED. */ +static void prune_unknown_attributes(igraph_vector_ptr_t *attrs) { + igraph_integer_t i, j; + for (i = 0, j = 0; i < igraph_vector_ptr_size(attrs); i++) { + igraph_attribute_record_t *atrec = VECTOR(*attrs)[i]; + if (atrec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + IGRAPH_FREE(atrec->name); + IGRAPH_FREE(atrec); + } else { + VECTOR(*attrs)[j++] = VECTOR(*attrs)[i]; + } + } + igraph_vector_ptr_resize(attrs, j); /* shrinks */ +} + +/* Converts an integer id to an optionally prefixed string id. */ +static const char *strid(igraph_integer_t id, const char *prefix) { + static char name[100]; + snprintf(name, sizeof(name) / sizeof(char) - 1, "%s%" IGRAPH_PRId, prefix, id); + return name; +} + +/* Creates an empty attribute record or if it exists, updates its type as needed. + * 'name' is the attribute name. 'type' is the current type in the GML tree, + * which will determine the igraph attribute type to use. */ +static igraph_error_t create_or_update_attribute(const char *name, + igraph_i_gml_tree_type_t type, + igraph_trie_t *attrnames, + igraph_vector_ptr_t *attrs) { + + igraph_integer_t trieid, triesize = igraph_trie_size(attrnames); + IGRAPH_CHECK(igraph_trie_get(attrnames, name, &trieid)); + if (trieid == triesize) { + /* new attribute */ + igraph_attribute_record_t *atrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + IGRAPH_CHECK_OOM(atrec, "Cannot read GML file."); + IGRAPH_FINALLY(igraph_free, atrec); + + atrec->name = strdup(name); + IGRAPH_CHECK_OOM(atrec->name, "Cannot read GML file."); + IGRAPH_FINALLY(igraph_free, (char *) atrec->name); + + if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) { + atrec->type = IGRAPH_ATTRIBUTE_NUMERIC; + } else if (type == IGRAPH_I_GML_TREE_STRING) { + atrec->type = IGRAPH_ATTRIBUTE_STRING; + } else { + atrec->type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, atrec)); + IGRAPH_FINALLY_CLEAN(2); + } else { + /* already seen, should we update type? */ + igraph_attribute_record_t *atrec = VECTOR(*attrs)[trieid]; + igraph_attribute_type_t type1 = atrec->type; + if (type == IGRAPH_I_GML_TREE_STRING) { + atrec->type = IGRAPH_ATTRIBUTE_STRING; + } else if (type1 == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) { + atrec->type = IGRAPH_ATTRIBUTE_NUMERIC; + } + } + } + + return IGRAPH_SUCCESS; +} + +/* Allocates the contents of attribute records stored in 'attrs'. + * 'no_of_items' is the length of attribute vectors, i.e. no_of_nodes, + * no_of_edges, or 1 for vertex, edge and graph attributes. + * The 'kind' parameter can be "vertex", "edge" or "graph", and + * is used solely for showing better warning messages. */ +static igraph_error_t allocate_attributes(igraph_vector_ptr_t *attrs, + igraph_integer_t no_of_items, + const char *kind) { + + igraph_integer_t i, n = igraph_vector_ptr_size(attrs); + for (i = 0; i < n; i++) { + igraph_attribute_record_t *atrec = VECTOR(*attrs)[i]; + igraph_attribute_type_t type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *p = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_CHECK_OOM(p, "Cannot read GML file."); + IGRAPH_FINALLY(igraph_free, p); + IGRAPH_CHECK(igraph_vector_init(p, no_of_items)); + igraph_vector_fill(p, IGRAPH_NAN); /* use NaN as default */ + atrec->value = p; + IGRAPH_FINALLY_CLEAN(1); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *p = IGRAPH_CALLOC(1, igraph_strvector_t); + IGRAPH_CHECK_OOM(p, "Cannot read GML file."); + IGRAPH_FINALLY(igraph_free, p); + IGRAPH_CHECK(igraph_strvector_init(p, no_of_items)); + atrec->value = p; + IGRAPH_FINALLY_CLEAN(1); + } else if (type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + IGRAPH_WARNINGF("Composite %s attribute '%s' ignored in GML file.", kind, atrec->name); + } else { + /* Must never reach here. */ + IGRAPH_FATAL("Unexpected attribute type."); + } + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_read_graph_gml + * \brief Read a graph in GML format. + * + * GML is a simple textual format, see + * https://web.archive.org/web/20190207140002/http://www.fim.uni-passau.de/index.php?id=17297%26L=1 + * 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 attributes with a simple type are used: integer, real or + * string. If an attribute is composite, i.e. an array or a record, + * then it is ignored. When some values of the attribute are simple and + * some compound, the composite ones are replaced with a default value + * (NaN for numeric, "" for string). + * \oli comment fields are not ignored. They are treated as any + * other field and converted to attributes. + * \oli Top level attributes except for Version and the + * first graph attribute are completely ignored. + * \oli There is no maximum line length or maximum keyword length. + * \oli Only the \c quot, \c amp, \c apos, \c lt and \c gt character entities + * are supported. Any other entity is passed through unchanged by the reader + * after issuing a warning, and is expected to be decoded by the user. + * \oli We allow inf, -inf and nan + * (not a number) as a real number. This is case insensitive, so + * nan, NaN and NAN are equivalent. + * \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 + */ +igraph_error_t igraph_read_graph_gml(igraph_t *graph, FILE *instream) { + + igraph_integer_t i; + igraph_integer_t no_of_nodes = 0, no_of_edges = 0; + igraph_integer_t node_no; + igraph_trie_t trie; + igraph_vector_int_t edges; + igraph_bool_t directed = IGRAPH_UNDIRECTED; + igraph_bool_t has_directed = false; + igraph_gml_tree_t *gtree; + igraph_integer_t 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]; + igraph_integer_t edgeptr = 0; + igraph_i_gml_parsedata_t context; + igraph_bool_t entity_warned = false; /* used to warn at most once about unsupported entities */ + + 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); + + /* Protect 'context' from being destroyed before returning from yyparse() */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_gml_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ + if (context.errmsg[0] != '\0') { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); + } else { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_PARSEERROR); + } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading GML file.", err); /* LCOV_EXCL_LINE */ + } + + /* 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_WARNINGF("Unknown GML version: %" IGRAPH_PRId ". " + "Parsing will continue assuming GML version 1, but may fail.", + igraph_gml_tree_get_integer(context.tree, i)); + } + + /* 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_ERRORF("Invalid type for 'graph' object in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(context.tree, gidx)); + } + gtree = igraph_gml_tree_get_tree(context.tree, gidx); + + IGRAPH_FINALLY(igraph_i_gml_destroy_attrs, attrs); + IGRAPH_CHECK(igraph_vector_ptr_init(&gattrs, 0)); + IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 0)); + IGRAPH_CHECK(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); + + /* Now we go over all objects in the graph to + * - collect the attribute names and types + * - collect node IDs + * - set directedness + * - do some checks which the following code relies on + * + * The 'id' fields of 'node' objects are converted into strings, so that they + * can be inserted into a trie and re-encoded as consecutive integers starting + * at 0. The GML spec allows isolated nodes with no 'id' field. These get a + * generated string id of the form "n123" consisting of "n" and their count + * (i.e. ordinal position) within the GML file. + * + * We use an attribute type value of IGRAPH_ATTRIBUTE_UNSPECIFIED to mark attribute + * records which correspond to composite GML values and must therefore be removed + * before creating the graph. + */ + node_no = 0; + for (i = 0; i < igraph_gml_tree_length(gtree); i++) { + const char *name = igraph_gml_tree_name(gtree, i); + if (!strcmp(name, "node")) { + igraph_gml_tree_t *node; + igraph_bool_t hasid; + node_no++; + no_of_nodes++; + if (igraph_gml_tree_type(gtree, i) != IGRAPH_I_GML_TREE_TREE) { + IGRAPH_ERRORF("'node' is not a list in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(gtree, i)); + } + node = igraph_gml_tree_get_tree(gtree, i); + hasid = false; + for (igraph_integer_t j = 0; j < igraph_gml_tree_length(node); j++) { + const char *name = igraph_gml_tree_name(node, j); + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, j); + IGRAPH_CHECK(create_or_update_attribute(name, type, &vattrnames, &vattrs)); + /* check id */ + if (!strcmp(name, "id")) { + igraph_integer_t id, trie_id; + igraph_integer_t trie_size = igraph_trie_size(&trie); + if (hasid) { + /* A 'node' must not have more than one 'id' field. + * This error cannot be relaxed into a warning because all ids we find are + * added to the trie, and eventually converted to igraph vertex ids. */ + IGRAPH_ERRORF("Node has multiple 'id' fields in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(node, j)); + } + if (type != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERRORF("Non-integer node id in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(node, j)); + } + id = igraph_gml_tree_get_integer(node, j); + IGRAPH_CHECK(igraph_trie_get(&trie, strid(id, ""), &trie_id)); + if (trie_id != trie_size) { + /* This id has already been seen in a previous node. */ + IGRAPH_ERRORF("Duplicate node id in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(node, j)); + } + hasid = true; + } + } + if (!hasid) { + /* Isolated nodes are allowed not to have an id. + * We generate an "n"-prefixed string id to be used in the trie. */ + igraph_integer_t trie_id; + IGRAPH_CHECK(igraph_trie_get(&trie, strid(node_no, "n"), &trie_id)); + } + } else if (!strcmp(name, "edge")) { + igraph_gml_tree_t *edge; + igraph_bool_t has_source = false, has_target = false; + no_of_edges++; + if (igraph_gml_tree_type(gtree, i) != IGRAPH_I_GML_TREE_TREE) { + IGRAPH_ERRORF("'edge' is not a list in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(gtree, i)); + } + edge = igraph_gml_tree_get_tree(gtree, i); + for (igraph_integer_t j = 0; j < igraph_gml_tree_length(edge); j++) { + const char *name = igraph_gml_tree_name(edge, j); + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(edge, j); + if (!strcmp(name, "source")) { + if (has_source) { + /* An edge must not have more than one 'source' field. + * This could be relaxed to a warning, but we keep it as an error + * for consistency with the handling of duplicate node 'id' field, + * and because it indicates a serious corruption in the GML file. */ + IGRAPH_ERRORF("Duplicate 'source' in an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(edge, j)); + } + has_source = true; + if (type != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERRORF("Non-integer 'source' for an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(edge, j)); + } + } else if (!strcmp(name, "target")) { + if (has_target) { + /* An edge must not have more than one 'target' field. */ + IGRAPH_ERRORF("Duplicate 'target' in an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(edge, j)); + } + has_target = true; + if (type != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERRORF("Non-integer 'target' for an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(edge, j)); + } + } else { + IGRAPH_CHECK(create_or_update_attribute(name, type, &eattrnames, &eattrs)); + } + } /* for */ + if (!has_source) { + IGRAPH_ERRORF("No 'source' for edge in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(gtree, i)); + } + if (!has_target) { + IGRAPH_ERRORF("No 'target' for edge in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(gtree, i)); + } + } else if (! strcmp(name, "directed")) { + /* Set directedness of graph. */ + if (has_directed) { + /* Be tolerant of duplicate entries, but do show a warning. */ + IGRAPH_WARNINGF("Duplicate 'directed' field in 'graph', line %" IGRAPH_PRId ". " + "Ignoring previous 'directed' fields.", + igraph_gml_tree_line(gtree, i)); + } + if (igraph_gml_tree_type(gtree, i) == IGRAPH_I_GML_TREE_INTEGER) { + igraph_integer_t dir = igraph_gml_tree_get_integer(gtree, i); + if (dir != 0 && dir != 1) { + IGRAPH_WARNINGF( + "Invalid value %" IGRAPH_PRId " for 'directed' attribute on line %" IGRAPH_PRId ", should be 0 or 1.", + dir, igraph_gml_tree_line(gtree, i)); + } + if (dir) { + directed = IGRAPH_DIRECTED; + } + has_directed = true; + } else { + IGRAPH_WARNINGF("Invalid type for 'directed' attribute on line %" IGRAPH_PRId ", assuming undirected.", + igraph_gml_tree_line(gtree, i)); + } + } else { + /* Add the rest of items as graph attributes. */ + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(gtree, i); + IGRAPH_CHECK(create_or_update_attribute(name, type, &gattrnames, &gattrs)); + } + } + + /* At this point, all nodes must have an id (from the file or generated) stored + * in the trie. Any condition that violates this should have been caught during + * the preceding checks. */ + IGRAPH_ASSERT(igraph_trie_size(&trie) == no_of_nodes); + + /* Now we allocate the vectors and strvectors for the attributes */ + IGRAPH_CHECK(allocate_attributes(&vattrs, no_of_nodes, "vertex")); + IGRAPH_CHECK(allocate_attributes(&eattrs, no_of_edges, "edge")); + IGRAPH_CHECK(allocate_attributes(&gattrs, 1, "graph")); + + /* Add edges, edge attributes and vertex attributes */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + node_no = 0; + for (i = 0; i < igraph_gml_tree_length(gtree); i++) { + const char *name; + name = igraph_gml_tree_name(gtree, i); + if (!strcmp(name, "node")) { + igraph_gml_tree_t *node = igraph_gml_tree_get_tree(gtree, i); + igraph_integer_t iidx = igraph_gml_tree_find(node, "id", 0); + igraph_integer_t trie_id; + const char *sid; + node_no++; + if (iidx < 0) { + /* Isolated node with no id field, use n-prefixed generated id */ + sid = strid(node_no, "n"); + } else { + sid = strid(igraph_gml_tree_get_integer(node, iidx), ""); + } + IGRAPH_CHECK(igraph_trie_get(&trie, sid, &trie_id)); + for (igraph_integer_t j = 0; j < igraph_gml_tree_length(node); j++) { + const char *aname = igraph_gml_tree_name(node, j); + igraph_attribute_record_t *atrec; + igraph_attribute_type_t type; + igraph_integer_t ai; + IGRAPH_CHECK(igraph_trie_get(&vattrnames, aname, &ai)); + atrec = VECTOR(vattrs)[ai]; + type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *v = (igraph_vector_t *) atrec->value; + VECTOR(*v)[trie_id] = igraph_i_gml_toreal(node, j); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *v = (igraph_strvector_t *) atrec->value; + const char *value = igraph_i_gml_tostring(node, j); + if (needs_coding(value)) { + char *value_decoded; + IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); + IGRAPH_FINALLY(igraph_free, value_decoded); + IGRAPH_CHECK(igraph_strvector_set(v, trie_id, value_decoded)); + IGRAPH_FREE(value_decoded); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_CHECK(igraph_strvector_set(v, trie_id, value)); + } + } else { + /* Ignored composite attribute */ + } + } + } else if (!strcmp(name, "edge")) { + igraph_gml_tree_t *edge; + igraph_integer_t from, to, fromidx = 0, toidx = 0; + edge = igraph_gml_tree_get_tree(gtree, i); + for (igraph_integer_t j = 0; j < igraph_gml_tree_length(edge); j++) { + const char *aname = igraph_gml_tree_name(edge, j); + if (!strcmp(aname, "source")) { + fromidx = igraph_gml_tree_find(edge, "source", 0); + } else if (!strcmp(aname, "target")) { + toidx = igraph_gml_tree_find(edge, "target", 0); + } else { + igraph_integer_t edgeid = edgeptr / 2; + igraph_integer_t ai; + igraph_attribute_record_t *atrec; + igraph_attribute_type_t type; + IGRAPH_CHECK(igraph_trie_get(&eattrnames, aname, &ai)); + atrec = VECTOR(eattrs)[ai]; + type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *v = (igraph_vector_t *) atrec->value; + VECTOR(*v)[edgeid] = igraph_i_gml_toreal(edge, j); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *v = (igraph_strvector_t *) atrec->value; + const char *value = igraph_i_gml_tostring(edge, j); + if (needs_coding(value)) { + char *value_decoded; + IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); + IGRAPH_FINALLY(igraph_free, value_decoded); + IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value_decoded)); + IGRAPH_FREE(value_decoded); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value)); + } + } else { + /* Ignored composite attribute */ + } + } + } + from = igraph_gml_tree_get_integer(edge, fromidx); + to = igraph_gml_tree_get_integer(edge, toidx); + IGRAPH_CHECK(igraph_trie_check(&trie, strid(from, ""), &from)); + if (from < 0) { + IGRAPH_ERRORF("Unknown source node id found in an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, igraph_gml_tree_line(edge, fromidx)); + } + IGRAPH_CHECK(igraph_trie_check(&trie, strid(to, ""), &to)); + if (to < 0) { + IGRAPH_ERRORF("Unknown target node id found in an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, igraph_gml_tree_line(edge, toidx)); + } + VECTOR(edges)[edgeptr++] = from; + VECTOR(edges)[edgeptr++] = to; + } else if (! strcmp(name, "directed")) { + /* Nothing to do for 'directed' field, already handled earlier. */ + } else { + /* Set the rest as graph attributes */ + igraph_integer_t ai; + igraph_attribute_record_t *atrec; + igraph_attribute_type_t type; + IGRAPH_CHECK(igraph_trie_get(&gattrnames, name, &ai)); + atrec = VECTOR(gattrs)[ai]; + type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *v = (igraph_vector_t *) atrec->value; + VECTOR(*v)[0] = igraph_i_gml_toreal(gtree, i); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *v = (igraph_strvector_t *) atrec->value; + const char *value = igraph_i_gml_tostring(gtree, i); + if (needs_coding(value)) { + char *value_decoded; + IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); + IGRAPH_FINALLY(igraph_free, value_decoded); + IGRAPH_CHECK(igraph_strvector_set(v, 0, value_decoded)); + IGRAPH_FREE(value_decoded); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_CHECK(igraph_strvector_set(v, 0, value)); + } + } else { + /* Ignored composite attribute */ + } + } + } + + /* Remove composite attributes */ + prune_unknown_attributes(&vattrs); + prune_unknown_attributes(&eattrs); + prune_unknown_attributes(&gattrs); + + 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, &gattrs)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_vertices(graph, no_of_nodes, &vattrs)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, &eattrs)); + IGRAPH_FINALLY_CLEAN(1); /* do not destroy 'graph', just pop it from the stack */ + + igraph_vector_int_destroy(&edges); + igraph_i_gml_destroy_attrs(attrs); + igraph_i_gml_parsedata_destroy(&context); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_gml_convert_to_key(const char *orig, char **key) { + char strno[50]; + size_t i, len = strlen(orig), newlen = 0, plen = 0; + + /* do we need a prefix? */ + if (len == 0 || !isalpha(orig[0])) { + 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); + IGRAPH_CHECK_OOM(*key, "Writing GML format failed."); + 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; +} + +/* Checks if a vector is free of duplicates. Since NaN == NaN is false, duplicate NaN values + * will not be detected. */ +static igraph_error_t igraph_i_vector_is_duplicate_free(const igraph_vector_t *v, igraph_bool_t *res) { + igraph_vector_t u; + igraph_integer_t n = igraph_vector_size(v); + + if (n < 2) { + *res = true; + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_vector_init_copy(&u, v)); + IGRAPH_FINALLY(igraph_vector_destroy, &u); + igraph_vector_sort(&u); + + *res = true; + for (igraph_integer_t i=1; i < n; i++) { + if (VECTOR(u)[i-1] == VECTOR(u)[i]) { + *res = false; + break; + } + } + + igraph_vector_destroy(&u); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +#define CHECK(cmd) do { int 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 + * https://web.archive.org/web/20190207140002/http://www.fim.uni-passau.de/index.php?id=17297%26L=1 + * for details. + * + * + * The graph, vertex and edges attributes are written to the + * file as well, if they are numeric or string. Boolean attributes are converted + * to numeric, with 0 and 1 used for false and true, respectively. + * NaN values of numeric attributes are skipped, as NaN is not part of the GML + * specification and other software may not be able to read files containing them. + * This is consistent with \ref igraph_read_graph_gml(), which produces NaN + * when an attribute value is missing. In contrast with NaN, infinite values + * are retained. Ensure that none of the numeric attributes values are infinite + * to produce a conformant GML file that can be read by other software. + * + * + * 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 \c NULL 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 \c NULL and there is a + * numeric id vertex attribute, it will be used instead. If ids + * are not specified in either way then the regular igraph vertex IDs are used. + * If some of the supplied id values are invalid (non-integer or NaN), all supplied + * id are ignored and igraph vertex IDs are used instead. + * + * + * Note that whichever way vertex IDs are specified, their uniqueness is not checked. + * + * + * If the graph has edge attributes that become source + * or target after encoding, or the graph has an attribute that becomes + * directed, they will be ignored with a warning. GML uses these attributes + * to specify the edge endpoints, and the graph directedness, 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 options Set of |-combinable boolean flags for writing the GML file. + * \clist + * \cli 0 + * All options turned off. + * \cli IGRAPH_WRITE_GML_DEFAULT_SW + * Default options, currently equivalent to 0. May change in future versions. + * \cli IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW + * Do not encode any other characters than " as entities. Specifically, this + * option prevents the encoding of &. Useful when re-exporting a graph + * that was read from a GML file in which igraph could not interpret all entities, + * and thus passed them through without decoding. + * \endclist + * \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 \c NULL, the igraph version with the current date and time is added. + * If "", the creator line is omitted. Otherwise, the + * supplied string is used verbatim. + * \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 + */ + +igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, + igraph_write_gml_sw_t options, + const igraph_vector_t *id, const char *creator) { + igraph_strvector_t gnames, vnames, enames; /* attribute names */ + igraph_vector_int_t gtypes, vtypes, etypes; /* attribute types */ + igraph_integer_t gattr_no, vattr_no, eattr_no; /* attribute counts */ + igraph_vector_t numv; + igraph_strvector_t strv; + igraph_vector_bool_t boolv; + igraph_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + /* Each element is a bit field used to prevent showing more + * than one warning for each vertex or edge attribute. */ + igraph_vector_int_t warning_shown; + + igraph_vector_t v_myid; + const igraph_vector_t *myid = id; + + /* Creator line */ + if (creator == NULL) { + 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\"\n", + IGRAPH_VERSION, timestr)); + } else if (creator[0] == '\0') { + /* creator == "", omit Creator line */ + } else { + if (needs_coding(creator)) { + char *d; + IGRAPH_CHECK(entity_encode(creator, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); + IGRAPH_FINALLY(igraph_free, d); + CHECK(fprintf(outstream, + "Creator \"%s\"\n", + creator)); + IGRAPH_FREE(d); + IGRAPH_FINALLY_CLEAN(1); + } else { + CHECK(fprintf(outstream, + "Creator \"%s\"\n", + creator)); + } + } + + /* Version line */ + CHECK(fprintf(outstream, "Version 1\n")); + + /* The graph */ + CHECK(fprintf(outstream, "graph\n[\n")); + + IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&etypes, 0); + IGRAPH_CHECK(igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &vnames, &vtypes, + &enames, &etypes)); + gattr_no = igraph_vector_int_size(>ypes); + vattr_no = igraph_vector_int_size(&vtypes); + eattr_no = igraph_vector_int_size(&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 = false; + for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { + const char *n = igraph_strvector_get(&vnames, i); + if (!strcmp(n, "id") && VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + found = true; 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; + } + } + + /* Scan id vector for invalid values. If any are found, all ids are ignored. + * Invalid values may occur as a result of reading a GML file in which some + * nodes did not have an id, or by adding new vertices to a graph with an "id" + * attribute. In this case, the "id" attribute will contain NaN values. + */ + if (myid) { + if (igraph_vector_size(myid) != no_of_nodes) { + IGRAPH_ERROR("Size of id vector must match vertex count.", IGRAPH_EINVAL); + } + for (i = 0; i < no_of_nodes; ++i) { + igraph_real_t val = VECTOR(*myid)[i]; + igraph_real_t trunc_val = trunc(val); + if (! (val == trunc_val && igraph_i_is_real_representable_as_integer(trunc_val))) { + IGRAPH_WARNINGF("%g is not a valid integer id for GML files, ignoring all supplied ids.", val); + if (myid == &v_myid) { + igraph_vector_destroy(&v_myid); + IGRAPH_FINALLY_CLEAN(1); + } + myid = NULL; + break; + } + } + } + + if (myid) { + igraph_bool_t duplicate_free; + IGRAPH_CHECK(igraph_i_vector_is_duplicate_free(myid, &duplicate_free)); + if (! duplicate_free) { + IGRAPH_WARNING("Duplicate id values found, ignoring supplies ids."); + if (myid == &v_myid) { + igraph_vector_destroy(&v_myid); + IGRAPH_FINALLY_CLEAN(1); + } + myid = NULL; + } + } + + /* directedness */ + CHECK(fprintf(outstream, " directed %i\n", igraph_is_directed(graph) ? 1 : 0)); + + /* Graph attributes first */ + for (i = 0; i < gattr_no; i++) { + const char *name; + char *newname; + name = igraph_strvector_get(&gnames, i); + IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (!strcmp(newname, "directed")|| !strcmp(newname, "edge") || !strcmp(newname, "node")) { + IGRAPH_WARNINGF("The graph attribute '%s' was ignored while writing GML format.", name); + } else { + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv)); + /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ + if (! isnan(VECTOR(numv)[0])) { + if (! isfinite(VECTOR(numv)[0])) { + IGRAPH_WARNINGF("Infinite value in numeric graph attribute '%s'. " + "Produced GML file will not be conformant.", name); + } + 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) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); + s = igraph_strvector_get(&strv, 0); + if (needs_coding(s)) { + char *d; + IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); + IGRAPH_FINALLY(igraph_free, d); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); + IGRAPH_FREE(d); + IGRAPH_FINALLY_CLEAN(1); + } else { + 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); + } + + /* Macros used to work with the bit fiels in 'warning_shown', + * and avoid showing warnings more than once for each attribute. */ +#define GETBIT(k, i) ((k) & (1 << i)) +#define SETBIT(k, i) ((k) |= (1 << i)) +#define WARN_ONCE(attrno, bit, warn) \ + do { \ + igraph_integer_t *p = &VECTOR(warning_shown)[attrno]; \ + if (! GETBIT(*p, bit)) { \ + warn; \ + SETBIT(*p, bit); \ + } \ + } while (0) + + + /* Now come the vertices */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&warning_shown, vattr_no); + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t j; + CHECK(fprintf(outstream, " node\n [\n")); + /* id */ + CHECK(fprintf(outstream, " id %" IGRAPH_PRId "\n", myid ? (igraph_integer_t)VECTOR(*myid)[i] : i)); + /* other attributes */ + for (j = 0; j < vattr_no; j++) { + igraph_attribute_type_t type = (igraph_attribute_type_t) VECTOR(vtypes)[j]; + const char *name; + char *newname; + name = igraph_strvector_get(&vnames, j); + if (!strcmp(name, "id")) { + /* No warning, the presence of this attribute is expected, and is handled specially. */ + continue; + } + IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (!strcmp(newname, "id")) { + /* In case an attribute name would conflict with 'id' only after encoding. */ + WARN_ONCE(j, 0, + IGRAPH_WARNINGF("The vertex attribute '%s' was ignored while writing GML format.", name)); + } else { + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, + igraph_vss_1(i), &numv)); + /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ + if (! isnan(VECTOR(numv)[0])) { + if (! isfinite(VECTOR(numv)[0])) { + WARN_ONCE(j, 3, + IGRAPH_WARNINGF("Infinite value in numeric vertex attribute '%s'. " + "Produced GML file will not be conformant.", name)); + } + CHECK(fprintf(outstream, " %s ", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, + igraph_vss_1(i), &strv)); + s = igraph_strvector_get(&strv, 0); + if (needs_coding(s)) { + char *d; + IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); + IGRAPH_FINALLY(igraph_free, d); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); + IGRAPH_FREE(d); + IGRAPH_FINALLY_CLEAN(1); + } else { + 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(i), &boolv)); + CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + WARN_ONCE(j, 1, + IGRAPH_WARNINGF("The boolean vertex attribute '%s' was converted to numeric.", name)); + } else { + WARN_ONCE(j, 2, + IGRAPH_WARNINGF("The non-numeric, non-string, non-boolean vertex attribute '%s' was ignored.", name)); + } + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + CHECK(fprintf(outstream, " ]\n")); + } + + /* The edges too */ + IGRAPH_CHECK(igraph_vector_int_resize(&warning_shown, eattr_no)); + igraph_vector_int_null(&warning_shown); + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_integer_t j; + CHECK(fprintf(outstream, " edge\n [\n")); + /* source and target */ + CHECK(fprintf(outstream, " source %" IGRAPH_PRId "\n", + myid ? (igraph_integer_t)VECTOR(*myid)[from] : from)); + CHECK(fprintf(outstream, " target %" IGRAPH_PRId "\n", + myid ? (igraph_integer_t)VECTOR(*myid)[to] : to)); + + /* other attributes */ + for (j = 0; j < eattr_no; j++) { + igraph_attribute_type_t type = (igraph_attribute_type_t) VECTOR(etypes)[j]; + const char *name; + char *newname; + name = igraph_strvector_get(&enames, j); + IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname)); + IGRAPH_FINALLY(igraph_free, newname); + if (!strcmp(newname, "source") || !strcmp(newname, "target")) { + WARN_ONCE(j, 0, + IGRAPH_WARNINGF("The edge attribute '%s' was ignored while writing GML format.", name)); + } else { + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, + igraph_ess_1(i), &numv)); + /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ + if (! isnan(VECTOR(numv)[0])) { + if (! isfinite(VECTOR(numv)[0])) { + WARN_ONCE(j, 3, + IGRAPH_WARNINGF("Infinite value in numeric edge attribute '%s'. " + "Produced GML file will not be conformant.", name)); + } + CHECK(fprintf(outstream, " %s ", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, + igraph_ess_1(i), &strv)); + s = igraph_strvector_get(&strv, 0); + if (needs_coding(s)) { + char *d; + IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); + IGRAPH_FINALLY(igraph_free, d); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); + IGRAPH_FREE(d); + IGRAPH_FINALLY_CLEAN(1); + } else { + 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(i), &boolv)); + CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + WARN_ONCE(j, 1, + IGRAPH_WARNINGF("The boolean edge attribute '%s' was converted to numeric.", name)); + } else { + WARN_ONCE(j, 2, + IGRAPH_WARNINGF("The non-numeric, non-string, non-boolean edge attribute '%s' was ignored.", name)); + } + } + IGRAPH_FREE(newname); + IGRAPH_FINALLY_CLEAN(1); + } + CHECK(fprintf(outstream, " ]\n")); + } + + CHECK(fprintf(outstream, "]\n")); + +#undef GETBIT +#undef SETBIT +#undef WARN_ONCE + + if (&v_myid == myid) { + igraph_vector_destroy(&v_myid); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&warning_shown); + igraph_vector_bool_destroy(&boolv); + igraph_strvector_destroy(&strv); + igraph_vector_destroy(&numv); + igraph_vector_int_destroy(&etypes); + igraph_vector_int_destroy(&vtypes); + igraph_vector_int_destroy(>ypes); + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&gnames); + IGRAPH_FINALLY_CLEAN(10); + + return IGRAPH_SUCCESS; +} + +#undef CHECK diff --git a/src/io/graphdb.c b/src/io/graphdb.c new file mode 100644 index 0000000..1920e39 --- /dev/null +++ b/src/io/graphdb.c @@ -0,0 +1,130 @@ +/* -*- 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/interruption.h" + +/* Read a little-endian encoded 16-bit unsigned word. + * Returns negative value on failure. */ +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 && b2 != EOF) { + c1 = (unsigned char) b1; c2 = (unsigned char) b2; + return c1 | (c2 << 8); + } else { + return -1; + } +} + +/* Determine whether the read failed due to an input error or end-of-file condition. + * Must only be called after a read failure, always returns a non-success error code. */ +static igraph_error_t handle_input_error(FILE *instream) { + if (feof(instream)) { + IGRAPH_ERROR("Unexpected end of file, truncated graphdb file.", IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Cannot read from file.", IGRAPH_EFILE); + } +} + +/** + * \function igraph_read_graph_graphdb + * \brief Read a graph in the binary graph database format. + * + * This is a binary format, used in the ARG Graph Database + * for isomorphism testing. For more information, see + * https://mivia.unisa.it/datasets/graph-database/arg-database/ + * + * + * From the 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 + * + * + * As of igraph 0.10, only unlabelled graphs are implemented. + * + * \param graph Pointer to an uninitialized graph object. + * \param instream The stream to read from. It should be opened + * in binary mode. + * \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 + */ + +igraph_error_t igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, + igraph_bool_t directed) { + + const igraph_integer_t nodes = igraph_i_read_graph_graphdb_getword(instream); + if (nodes < 0) { + IGRAPH_CHECK(handle_input_error(instream)); + } + + igraph_vector_int_t edges; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 100); + igraph_vector_int_clear(&edges); + + for (igraph_integer_t i = 0; i < nodes; i++) { + igraph_integer_t len = igraph_i_read_graph_graphdb_getword(instream); + if (len < 0) { + IGRAPH_CHECK(handle_input_error(instream)); + } + for (igraph_integer_t j = 0; j < len; j++) { + igraph_integer_t to = igraph_i_read_graph_graphdb_getword(instream); + if (to < 0) { + IGRAPH_CHECK(handle_input_error(instream)); + } + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_ALLOW_INTERRUPTION(); + } + } + if (fgetc(instream) != EOF) { + IGRAPH_ERROR("Extra bytes at end of graphdb file.", IGRAPH_PARSEERROR); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/io/graphml.c b/src/io/graphml.c new file mode 100644 index 0000000..80e1a19 --- /dev/null +++ b/src/io/graphml.c @@ -0,0 +1,2150 @@ +/* + IGraph library. + Copyright (C) 2006-2023 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_foreign.h" + +#include "igraph_attributes.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "core/interruption.h" +#include "core/trie.h" +#include "graph/attributes.h" +#include "internal/hacks.h" /* strcasecmp & strdup */ +#include "io/parse_utils.h" + +#include "config.h" + +#include /* isdigit */ +#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 + NULL, /* _private */ +#endif + XML_ENTITY_DECL, /* type */ + NULL, /* name */ + NULL, /* children */ + NULL, /* last */ + NULL, /* parent */ + NULL, /* next */ + NULL, /* prev */ + NULL, /* doc */ + + NULL, /* orig */ + NULL, /* content */ + 0, /* length */ + XML_EXTERNAL_GENERAL_PARSED_ENTITY, /* etype */ + NULL, /* ExternalID */ + NULL, /* SystemID */ + + NULL, /* nexte */ + NULL, /* URI */ + 0, /* owner */ +#if LIBXML_VERSION < 21100 /* Versions < 2.11.0: */ + 1 /* checked */ +#else /* Starting with verson 2.11.0: */ + 1, /* flags */ + 0 /* expandedSize */ +#endif +}; + +xmlEntityPtr blankEntity = &blankEntityStruct; + +#define toXmlChar(a) (BAD_CAST(a)) +#define fromXmlChar(a) ((char *)(a)) /* not the most elegant way... */ + +#define GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) \ + do { \ + if (state->successful) { \ + igraph_i_graphml_sax_handler_error(state, msg); \ + } \ + } while (0) +#define RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) \ + do { \ + GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code); \ + return; \ + } while (0) + +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; + +typedef enum { + START, INSIDE_GRAPHML, INSIDE_GRAPH, INSIDE_NODE, INSIDE_EDGE, + INSIDE_KEY, INSIDE_DEFAULT, INSIDE_DATA, FINISH, UNKNOWN, ERROR +} igraph_i_graphml_parser_state_index_t; + +struct igraph_i_graphml_parser_state { + igraph_i_graphml_parser_state_index_t st; + igraph_t *g; + igraph_trie_t node_trie; + igraph_strvector_t edgeids; + igraph_vector_int_t edgelist; + igraph_vector_int_t prev_state_stack; + unsigned int unknown_depth; + igraph_integer_t index; + igraph_bool_t successful; + igraph_bool_t edges_directed; + igraph_trie_t v_attr_ids; + igraph_vector_ptr_t v_attrs; + igraph_trie_t e_attr_ids; + igraph_vector_ptr_t e_attrs; + igraph_trie_t g_attr_ids; + 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; + igraph_vector_char_t data_char; + igraph_integer_t 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, target); +} + +static igraph_error_t igraph_i_graphml_parse_numeric( + const char* char_data, igraph_real_t* result, igraph_real_t default_value +) { + const char* trimmed; + size_t trimmed_length; + + if (char_data == 0) { + *result = default_value; + return IGRAPH_SUCCESS; + } + + igraph_i_trim_whitespace(char_data, strlen(char_data), &trimmed, &trimmed_length); + if (trimmed_length > 0) { + IGRAPH_CHECK(igraph_i_parse_real(trimmed, trimmed_length, result)); + } else { + *result = default_value; + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_graphml_parse_boolean( + const char* char_data, igraph_bool_t* result, igraph_bool_t default_value +) { + igraph_integer_t value; + const char* trimmed; + size_t trimmed_length; + + if (char_data == 0) { + *result = default_value; + return IGRAPH_SUCCESS; + } + + igraph_i_trim_whitespace(char_data, strlen(char_data), &trimmed, &trimmed_length); + + if (trimmed_length == 4 && !strncasecmp(trimmed, "true", trimmed_length)) { + *result = 1; + return IGRAPH_SUCCESS; + } + + if (trimmed_length == 3 && !strncasecmp(trimmed, "yes", trimmed_length)) { + *result = 1; + return IGRAPH_SUCCESS; + } + + if (trimmed_length == 5 && !strncasecmp(trimmed, "false", trimmed_length)) { + *result = 0; + return IGRAPH_SUCCESS; + } + + if (trimmed_length == 2 && !strncasecmp(trimmed, "no", trimmed_length)) { + *result = 0; + return IGRAPH_SUCCESS; + } + + if (trimmed_length > 0) { + if (isdigit(trimmed[0])) { + IGRAPH_CHECK(igraph_i_parse_integer(trimmed, trimmed_length, &value)); + } else { + IGRAPH_ERRORF("Cannot parse '%.*s' as Boolean value.", IGRAPH_PARSEERROR, + (int) trimmed_length, trimmed); + } + } else { + *result = default_value; + return IGRAPH_SUCCESS; + } + + *result = value != 0; + return IGRAPH_SUCCESS; +} + +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); + } + } else if (rec->record.type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + /* no value was set */ + } + if (rec->id != NULL) { + xmlFree((void *) rec->id); + rec->id = NULL; + } + if (rec->record.name != 0) { + IGRAPH_FREE(rec->record.name); + } +} + +static igraph_error_t igraph_i_graphml_parser_state_init(struct igraph_i_graphml_parser_state* state, igraph_t* graph, igraph_integer_t 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_int_init(&state->edgelist, 0)); + IGRAPH_FINALLY(igraph_vector_int_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_attr_ids, 0)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->v_attr_ids); + + IGRAPH_CHECK(igraph_trie_init(&state->e_attr_ids, 0)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->e_attr_ids); + + IGRAPH_CHECK(igraph_trie_init(&state->g_attr_ids, 0)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->g_attr_ids); + + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&state->data_char, 0); + + IGRAPH_FINALLY_CLEAN(11); + + 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_attr_ids); + igraph_trie_destroy(&state->e_attr_ids); + igraph_trie_destroy(&state->g_attr_ids); + igraph_vector_int_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); + + igraph_vector_char_destroy(&state->data_char); + + if (state->data_key) { + xmlFree((void *) state->data_key); + state->data_key = NULL; + } + + if (state->error_message) { + IGRAPH_FREE(state->error_message); + } +} + +static void igraph_i_graphml_parser_state_set_error_from_varargs( + struct igraph_i_graphml_parser_state *state, const char* msg, va_list args +) { + const size_t max_error_message_length = 4096; + + state->successful = 0; + state->st = ERROR; + + 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); + } + + /* we need to guard against state->error_message == 0, which may happen + * if the memory allocation for the error message itself failed */ + if (state->error_message != 0) { + vsnprintf(state->error_message, max_error_message_length, msg, args); + } +} + +static void igraph_i_graphml_parser_state_set_error_from_xmlerror( + struct igraph_i_graphml_parser_state *state, const xmlError *error +) { + const size_t max_error_message_length = 4096; + + state->successful = 0; + state->st = ERROR; + + 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); + } + + /* we need to guard against state->error_message == 0, which may happen + * if the memory allocation for the error message itself failed */ + if (state->error_message != 0) { + snprintf(state->error_message, max_error_message_length, "Line %d: %s", + error->line, error->message); + } +} + +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; + va_list args; + va_start(args, msg); + igraph_i_graphml_parser_state_set_error_from_varargs(state, msg, args); + va_end(args); +} + +static xmlEntityPtr igraph_i_graphml_sax_handler_get_entity(void *state0, + const xmlChar* name) { + xmlEntityPtr predef = xmlGetPredefinedEntity(name); + const char* entityName; + + IGRAPH_UNUSED(state0); + if (predef != NULL) { + return predef; + } + + entityName = fromXmlChar(name); + IGRAPH_WARNINGF("Unknown XML entity found: '%s'.", entityName); + + return blankEntity; +} + +static igraph_error_t igraph_i_graphml_handle_unknown_start_tag(struct igraph_i_graphml_parser_state *state) { + if (state->st != UNKNOWN) { + IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); + state->st = UNKNOWN; + state->unknown_depth = 1; + } else { + state->unknown_depth++; + } + return IGRAPH_SUCCESS; +} + +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->unknown_depth = 0; + state->ignore_namespaces = 0; +} + +static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_parser_state *state) { + igraph_integer_t i, l; + igraph_attribute_record_t idrec, eidrec; + const char *idstr = "id"; + igraph_bool_t already_has_vertex_id = false, already_has_edge_id = false; + igraph_vector_ptr_t vattr, eattr, gattr; + igraph_integer_t esize; + + 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)); /* +1 for 'id' */ + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattr); + igraph_vector_ptr_resize(&vattr, 0); /* will be filled with push_back() */ + + 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_vector_ptr_resize(&eattr, 0); /* will be filled with push_back() */ + + IGRAPH_CHECK(igraph_vector_ptr_init(&gattr, igraph_vector_ptr_size(&state->g_attrs))); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &gattr); + igraph_vector_ptr_resize(&gattr, 0); /* will be filled with push_back() */ + + 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 add the complementary '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; + igraph_integer_t origsize = igraph_vector_size(vec); + igraph_integer_t 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; + igraph_integer_t origsize = igraph_strvector_size(strvec); + igraph_integer_t 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; + igraph_integer_t origsize = igraph_vector_bool_size(boolvec); + igraph_integer_t 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; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + continue; /* skipped attribute */ + } + igraph_vector_ptr_push_back(&vattr, rec); /* reserved */ + } + if (!already_has_vertex_id) { + idrec.name = idstr; + idrec.type = IGRAPH_ATTRIBUTE_STRING; + idrec.value = igraph_i_trie_borrow_keys(&state->node_trie); + igraph_vector_ptr_push_back(&vattr, &idrec); /* reserved */ + } else { + IGRAPH_WARNING("Could not add vertex ids, there is already an 'id' vertex attribute."); + } + + 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; + igraph_integer_t origsize = igraph_vector_size(vec); + igraph_integer_t edges = igraph_vector_int_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; + igraph_integer_t origsize = igraph_strvector_size(strvec); + igraph_integer_t edges = igraph_vector_int_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; + igraph_integer_t origsize = igraph_vector_bool_size(boolvec); + igraph_integer_t edges = igraph_vector_int_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; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + continue; /* skipped attribute */ + } + igraph_vector_ptr_push_back(&eattr, rec); /* reserved */ + } + if (igraph_strvector_size(&state->edgeids) != 0) { + if (!already_has_edge_id) { + igraph_integer_t origsize = igraph_strvector_size(&state->edgeids); + eidrec.name = idstr; + eidrec.type = IGRAPH_ATTRIBUTE_STRING; + IGRAPH_CHECK(igraph_strvector_resize(&state->edgeids, igraph_vector_int_size(&state->edgelist) / 2)); + for (; origsize < igraph_strvector_size(&state->edgeids); origsize++) { + IGRAPH_CHECK(igraph_strvector_set(&state->edgeids, origsize, "")); + } + eidrec.value = &state->edgeids; + igraph_vector_ptr_push_back(&eattr, &eidrec); /* reserved */ + } else { + 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; + igraph_integer_t 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; + igraph_integer_t 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; + igraph_integer_t 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; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + continue; /* skipped attribute */ + } + igraph_vector_ptr_push_back(&gattr, rec); /* reserved */ + } + + IGRAPH_CHECK(igraph_empty_attrs(state->g, 0, state->edges_directed, &gattr)); + IGRAPH_FINALLY(igraph_destroy, state->g); /* because the next two lines may fail as well */ + IGRAPH_CHECK(igraph_add_vertices(state->g, igraph_trie_size(&state->node_trie), &vattr)); + IGRAPH_CHECK(igraph_add_edges(state->g, &state->edgelist, &eattr)); + IGRAPH_FINALLY_CLEAN(1); /* graph construction completed successfully */ + + igraph_vector_ptr_destroy(&vattr); + igraph_vector_ptr_destroy(&eattr); + igraph_vector_ptr_destroy(&gattr); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/* See https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-parser.html#startElementNsSAX2Func */ +#define XML_ATTR_LOCALNAME(it) it[0] +#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_LENGTH(it) (size_t)(it[4] - it[3]) +#define XML_ATTR_VALUE(it) it[3], (int)(it[4] - it[3]) /* for use in strnxxx()-style functions that take a char * and a length */ +#define XML_ATTR_VALUE_PF(it) (int)(it[4] - it[3]), it[3] /* for use in printf-style function with "%.*s" */ + +static igraph_bool_t xmlAttrValueEqual(xmlChar** attr, const char* expected) { + size_t expected_length = strlen(expected); + return ( + expected_length == XML_ATTR_VALUE_LENGTH(attr) && + !xmlStrncmp(toXmlChar(expected), XML_ATTR_VALUE(attr)) + ); +} + +static igraph_error_t igraph_i_graphml_add_attribute_key( + igraph_i_graphml_attribute_record_t** record, + const xmlChar** attrs, int nb_attrs, + struct igraph_i_graphml_parser_state *state +) { + + /* This function must return in three possible ways: + * + * - a proper newly allocated attribute record is returned in 'record' and + * the function returns IGRAPH_SUCCESS; the parser will process the attribute + * - NULL is returned in 'record' and the function returns an igraph error + * code; the parser will handle the error + * - NULL is returned in 'record', but the function itself returns + * IGRAPH_SUCCESS; the parser will skip the attribute + * + * The caller should be prepared to handle all three cases. + */ + + xmlChar **it; + xmlChar *localname; + xmlChar *xmlStr; + igraph_trie_t *trie = NULL; + igraph_vector_ptr_t *ptrvector = NULL; + igraph_integer_t i, n; + igraph_integer_t id; + igraph_i_graphml_attribute_record_t *rec = NULL; + igraph_bool_t skip = false; + + if (!state->successful) { + /* Parser is already in an error state */ + goto exit; + } + + rec = IGRAPH_CALLOC(1, igraph_i_graphml_attribute_record_t); + if (rec == NULL) { + IGRAPH_ERROR("Cannot allocate attribute record.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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"))) { + xmlStr = xmlStrndup(XML_ATTR_VALUE(it)); + IGRAPH_CHECK_OOM(xmlStr, "Cannot duplicate value of 'id' attribute."); + rec->id = fromXmlChar(xmlStr); + xmlStr = NULL; + } else if (xmlStrEqual(localname, toXmlChar("attr.name"))) { + xmlStr = xmlStrndup(XML_ATTR_VALUE(it)); + IGRAPH_CHECK_OOM(xmlStr, "Cannot duplicate value of 'attr.name' attribute."); + rec->record.name = fromXmlChar(xmlStr); + xmlStr = NULL; + } else if (xmlStrEqual(localname, toXmlChar("attr.type"))) { + if (xmlAttrValueEqual(it, "boolean")) { + rec->type = I_GRAPHML_BOOLEAN; + rec->record.type = IGRAPH_ATTRIBUTE_BOOLEAN; + rec->default_value.as_boolean = 0; + } else if (xmlAttrValueEqual(it, "string")) { + char *str = strdup(""); + IGRAPH_CHECK_OOM(str, "Cannot allocate new empty string."); + rec->type = I_GRAPHML_STRING; + rec->record.type = IGRAPH_ATTRIBUTE_STRING; + rec->default_value.as_string = str; + } else if (xmlAttrValueEqual(it, "float")) { + rec->type = I_GRAPHML_FLOAT; + rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->default_value.as_numeric = IGRAPH_NAN; + } else if (xmlAttrValueEqual(it, "double")) { + rec->type = I_GRAPHML_DOUBLE; + rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->default_value.as_numeric = IGRAPH_NAN; + } else if (xmlAttrValueEqual(it, "int")) { + rec->type = I_GRAPHML_INTEGER; + rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->default_value.as_numeric = IGRAPH_NAN; + } else if (xmlAttrValueEqual(it, "long")) { + rec->type = I_GRAPHML_LONG; + rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->default_value.as_numeric = IGRAPH_NAN; + } else { + IGRAPH_ERRORF("Unknown attribute type '%.*s'.", IGRAPH_PARSEERROR, + XML_ATTR_VALUE_PF(it)); + } + } else if (xmlStrEqual(*it, toXmlChar("for"))) { + /* graph, vertex or edge attribute? */ + if (xmlAttrValueEqual(it, "graph")) { + trie = &state->g_attr_ids; + ptrvector = &state->g_attrs; + } else if (xmlAttrValueEqual(it, "node")) { + trie = &state->v_attr_ids; + ptrvector = &state->v_attrs; + } else if (xmlAttrValueEqual(it, "edge")) { + trie = &state->e_attr_ids; + ptrvector = &state->e_attrs; + } else if (xmlAttrValueEqual(it, "graphml")) { + igraph_i_report_unhandled_attribute_target("graphml", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (xmlAttrValueEqual(it, "hyperedge")) { + igraph_i_report_unhandled_attribute_target("hyperedge", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (xmlAttrValueEqual(it, "port")) { + igraph_i_report_unhandled_attribute_target("port", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (xmlAttrValueEqual(it, "endpoint")) { + igraph_i_report_unhandled_attribute_target("endpoint", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else if (xmlAttrValueEqual(it, "all")) { + /* TODO: we should handle this */ + igraph_i_report_unhandled_attribute_target("all", IGRAPH_FILE_BASENAME, __LINE__); + skip = 1; + } else { + IGRAPH_ERRORF("Unknown value '%.*s' in the 'for' attribute of a tag.", IGRAPH_PARSEERROR, + XML_ATTR_VALUE_PF(it)); + } + } + } + + /* throw an error if there is no ID; this is a clear violation of the GraphML DTD */ + if (rec->id == NULL) { + IGRAPH_ERROR("Found tag with no 'id' attribute.", IGRAPH_PARSEERROR); + } + + /* throw an error if the ID is an empty string; this is also a clear violation of the GraphML DTD */ + if (*(rec->id) == '\0') { + IGRAPH_ERROR("Found tag with an empty 'id' attribute.", IGRAPH_PARSEERROR); + } + + /* in case of a missing attr.name attribute, use the id as the attribute name */ + if (rec->record.name == NULL) { + rec->record.name = strdup(rec->id); + IGRAPH_CHECK_OOM(rec->record.name, "Cannot duplicate attribute ID as name."); + } + + /* if the attribute type is missing, ignore the attribute with a warning */ + if (!skip && rec->type == I_GRAPHML_UNKNOWN_TYPE) { + IGRAPH_WARNINGF("Ignoring because of a missing 'attr.type' attribute.", rec->id); + skip = 1; + } + + /* if the value of the 'for' attribute was unknown, throw an error */ + if (!skip && trie == 0) { + IGRAPH_ERROR("Missing 'for' attribute in a tag.", IGRAPH_PARSEERROR); + } + + /* If attribute is skipped, proceed according to the type of the associated graph element. */ + if (skip) { + if (trie == 0) { + /* Attribute was skipped because it is not for a node, edge or the graph. + * Free everything and return. */ + if (rec) { + igraph_i_graphml_attribute_record_destroy(rec); + IGRAPH_FREE(rec); + } + IGRAPH_FINALLY_CLEAN(2); + goto exit; + } else { + /* If the skipped attribute was for a supported graph element, we add it + * as "UNSPECIFIED" so that we can avoid reporting "unknown attribute" warnings + * later. */ + rec->record.type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + } + } + + /* check if we have already seen this ID */ + IGRAPH_CHECK(igraph_trie_check(trie, rec->id, &id)); + if (id >= 0) { + IGRAPH_ERRORF("Duplicate attribute ID found: '%s'.", IGRAPH_PARSEERROR, rec->id); + } + + /* check if we have already seen this attribute name */ + n = igraph_vector_ptr_size(ptrvector); + for (i = 0; i < n; i++) { + if (!strcmp( + rec->record.name, + ((igraph_i_graphml_attribute_record_t*) igraph_vector_ptr_get(ptrvector, i))->record.name + )) { + IGRAPH_ERRORF( + "Duplicate attribute name found: '%s' (for ).", + IGRAPH_PARSEERROR, rec->record.name, rec->id + ); + } + } + + /* add to trie, attributes */ + IGRAPH_CHECK(igraph_trie_get(trie, rec->id, &id)); + IGRAPH_CHECK(igraph_vector_ptr_push_back(ptrvector, rec)); + + /* Ownership of 'rec' is now taken by ptrvector so we are not responsible + * for destroying and freeing it any more */ + IGRAPH_FINALLY_CLEAN(2); + + /* 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); + IGRAPH_CHECK_OOM(boolvec, "Cannot allocate value vector for Boolean attribute."); + IGRAPH_FINALLY(igraph_free, boolvec); + IGRAPH_CHECK(igraph_vector_bool_init(boolvec, 0)); + rec->record.value = boolvec; + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_NUMERIC: + vec = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_CHECK_OOM(vec, "Cannot allocate value vector for numeric attribute."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_init(vec, 0)); + rec->record.value = vec; + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_STRING: + strvec = IGRAPH_CALLOC(1, igraph_strvector_t); + IGRAPH_CHECK_OOM(strvec, "Cannot allocate value vector for string attribute."); + IGRAPH_FINALLY(igraph_free, strvec); + IGRAPH_CHECK(igraph_strvector_init(strvec, 0)); + rec->record.value = strvec; + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_UNSPECIFIED: + rec->record.value = NULL; + break; + default: + IGRAPH_FATAL("Unexpected attribute type."); + } + +exit: + *record = rec; + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; + } + + 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) { + xmlFree((void *) state->data_key); + state->data_key = NULL; + } + state->data_key = xmlStrndup(XML_ATTR_VALUE(it)); + if (state->data_key == 0) { + return IGRAPH_ENOMEM; /* LCOV_EXCL_LINE */ + } + igraph_vector_char_clear(&state->data_char); + state->data_type = type; + } else { + /* ignore */ + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_graphml_append_to_data_char( + struct igraph_i_graphml_parser_state *state, const xmlChar *data, int len +) { + if (!state->successful) { + return IGRAPH_SUCCESS; + } + + /* vector_push_back() minimizes reallocations by doubling the size of the buffer, + * while vector_append() would only allocate as much additional memory as needed. */ + IGRAPH_STATIC_ASSERT(sizeof(char) == sizeof(xmlChar)); + for (int i=0; i < len; i++) { + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, data[i])); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t recid, id = 0; + igraph_error_t result = IGRAPH_SUCCESS; + + switch (type) { + case IGRAPH_ATTRIBUTE_GRAPH: + trie = &state->g_attr_ids; + ptrvector = &state->g_attrs; + id = 0; + break; + case IGRAPH_ATTRIBUTE_VERTEX: + trie = &state->v_attr_ids; + ptrvector = &state->v_attrs; + id = state->act_node; + break; + case IGRAPH_ATTRIBUTE_EDGE: + trie = &state->e_attr_ids; + ptrvector = &state->e_attrs; + id = igraph_vector_int_size(&state->edgelist) / 2 - 1; /* hack */ + break; + default: + IGRAPH_FATAL("Unexpected attribute element type."); + } + + if (key == 0) { + /* no key specified, issue a warning */ + IGRAPH_WARNING("Missing attribute key in a tag, ignoring attribute."); + goto exit; + } + + IGRAPH_CHECK(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.", + key + ); + goto exit; + } + + graphmlrec = VECTOR(*ptrvector)[recid]; + rec = &graphmlrec->record; + + switch (rec->type) { + igraph_vector_bool_t *boolvec; + igraph_vector_t *vec; + igraph_strvector_t *strvec; + igraph_integer_t 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) { + IGRAPH_CHECK(igraph_vector_bool_resize(boolvec, id + 1)); + for (i = s; i < id; i++) { + VECTOR(*boolvec)[i] = graphmlrec->default_value.as_boolean; + } + } + + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); + IGRAPH_CHECK(igraph_i_graphml_parse_boolean( + VECTOR(state->data_char), VECTOR(*boolvec) + id, graphmlrec->default_value.as_boolean + )); + break; + + case IGRAPH_ATTRIBUTE_NUMERIC: + vec = (igraph_vector_t *)rec->value; + s = igraph_vector_size(vec); + if (id >= s) { + IGRAPH_CHECK(igraph_vector_resize(vec, id + 1)); + for (i = s; i < id; i++) { + VECTOR(*vec)[i] = graphmlrec->default_value.as_numeric; + } + } + + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); + IGRAPH_CHECK(igraph_i_graphml_parse_numeric( + VECTOR(state->data_char), VECTOR(*vec) + id, + graphmlrec->default_value.as_numeric + )); + break; + + case IGRAPH_ATTRIBUTE_STRING: + strvec = (igraph_strvector_t *)rec->value; + s = igraph_strvector_size(strvec); + if (id >= s) { + IGRAPH_CHECK(igraph_strvector_resize(strvec, id + 1)); + strvalue = graphmlrec->default_value.as_string; + for (i = s; i < id; i++) { + IGRAPH_CHECK(igraph_strvector_set(strvec, i, strvalue)); + } + } + if (igraph_vector_char_size(&state->data_char) > 0) { + /* Ensure that the vector ends with a null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); + strvalue = VECTOR(state->data_char); + } else { + strvalue = graphmlrec->default_value.as_string; + } + IGRAPH_CHECK(igraph_strvector_set(strvec, id, strvalue)); + break; + + case IGRAPH_ATTRIBUTE_UNSPECIFIED: + break; + + default: + IGRAPH_FATAL("Unexpected attribute type."); + } + +exit: + igraph_vector_char_clear(&state->data_char); + + return result; +} + +static igraph_error_t 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; + igraph_error_t result = IGRAPH_SUCCESS; + char* str = 0; + + IGRAPH_ASSERT(state->current_attr_record != NULL); + + if (igraph_vector_char_size(&state->data_char) == 0) { + return IGRAPH_SUCCESS; + } + + switch (graphmlrec->record.type) { + case IGRAPH_ATTRIBUTE_BOOLEAN: + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); + IGRAPH_CHECK(igraph_i_graphml_parse_boolean( + VECTOR(state->data_char), &graphmlrec->default_value.as_boolean, 0 + )); + break; + case IGRAPH_ATTRIBUTE_NUMERIC: + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); + IGRAPH_CHECK(igraph_i_graphml_parse_numeric( + VECTOR(state->data_char), &graphmlrec->default_value.as_numeric, IGRAPH_NAN + )); + break; + case IGRAPH_ATTRIBUTE_STRING: + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); + str = strdup(VECTOR(state->data_char)); + IGRAPH_CHECK_OOM(str, "Cannot allocate memory for string attribute."); + + if (graphmlrec->default_value.as_string != 0) { + IGRAPH_FREE(graphmlrec->default_value.as_string); + } + graphmlrec->default_value.as_string = str; + str = NULL; + break; + case IGRAPH_ATTRIBUTE_UNSPECIFIED: + break; + default: + IGRAPH_FATAL("Unexpected attribute type."); + } + + igraph_vector_char_clear(&state->data_char); + + return result; +} + +static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( + struct igraph_i_graphml_parser_state* state, const xmlChar* localname, const xmlChar* prefix, + const xmlChar* uri, int nb_namespaces, const xmlChar** namespaces, + int nb_attributes, int nb_defaulted, const xmlChar** attributes) { + xmlChar** it; + xmlChar* attr_value = 0; + igraph_integer_t id1, id2; + int i; + igraph_bool_t tag_is_unknown = false; + + IGRAPH_UNUSED(prefix); + IGRAPH_UNUSED(nb_namespaces); + IGRAPH_UNUSED(namespaces); + IGRAPH_UNUSED(nb_defaulted); + + 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_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + goto exit; + } + + 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_CHECK(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 (xmlAttrValueEqual(it, "directed")) { + state->edges_directed = 1; + } else if (xmlAttrValueEqual(it, "undirected")) { + state->edges_directed = 0; + } + } + } + } + state->index--; + } else if (xmlStrEqual(localname, toXmlChar("key"))) { + IGRAPH_CHECK( + igraph_i_graphml_add_attribute_key( + &state->current_attr_record, + attributes, nb_attributes, state + ) + ); + /* NULL is okay here for state->current_attr_record -- we should have + * triggered an error in the parser already if we returned NULL, and + * the rest of the code is prepared to handle NULLs */ + state->st = INSIDE_KEY; + } else { + IGRAPH_CHECK(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_CHECK(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_CHECK(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 = xmlStrndup(XML_ATTR_VALUE(it)); + if (attr_value == 0) { + IGRAPH_ERROR("Cannot copy value of edge source attribute.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(xmlFree, attr_value); + + IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id1)); + + xmlFree(attr_value); attr_value = NULL; + IGRAPH_FINALLY_CLEAN(1); + } else if (xmlStrEqual(*it, toXmlChar("target"))) { + attr_value = xmlStrndup(XML_ATTR_VALUE(it)); + if (attr_value == 0) { + IGRAPH_ERROR("Cannot copy value of edge target attribute.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(xmlFree, attr_value); + + IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id2)); + + xmlFree(attr_value); attr_value = NULL; + IGRAPH_FINALLY_CLEAN(1); + } else if (xmlStrEqual(*it, toXmlChar("id"))) { + igraph_integer_t edges = igraph_vector_int_size(&state->edgelist) / 2 + 1; + igraph_integer_t origsize = igraph_strvector_size(&state->edgeids); + + attr_value = xmlStrndup(XML_ATTR_VALUE(it)); + if (attr_value == 0) { + IGRAPH_ERROR("Cannot copy value of edge ID attribute.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(xmlFree, attr_value); + + IGRAPH_CHECK(igraph_strvector_resize(&state->edgeids, edges)); + + for (; origsize < edges - 1; origsize++) { + IGRAPH_CHECK(igraph_strvector_set(&state->edgeids, origsize, "")); + } + + IGRAPH_CHECK(igraph_strvector_set(&state->edgeids, edges - 1, fromXmlChar(attr_value))); + + xmlFree(attr_value); attr_value = NULL; + IGRAPH_FINALLY_CLEAN(1); + } + } + if (id1 >= 0 && id2 >= 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(&state->edgelist, id1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->edgelist, id2)); + } else { + IGRAPH_ERROR("Edge with missing source or target encountered.", IGRAPH_PARSEERROR); + } + 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 = xmlStrndup(XML_ATTR_VALUE(it)); + if (attr_value == 0) { + IGRAPH_ERROR("Cannot copy value of node ID attribute.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(xmlFree, attr_value); + + IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id1)); + + xmlFree(attr_value); attr_value = NULL; + IGRAPH_FINALLY_CLEAN(1); + break; + } + } + if (id1 >= 0) { + state->act_node = id1; + } else { + state->act_node = -1; + IGRAPH_ERROR("Node with missing ID encountered.", IGRAPH_PARSEERROR); + } + state->st = INSIDE_NODE; + } else if (xmlStrEqual(localname, toXmlChar("data"))) { + IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( + state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_GRAPH + )); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); + state->st = INSIDE_DATA; + } else { + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + } + break; + + case INSIDE_NODE: + if (xmlStrEqual(localname, toXmlChar("data"))) { + IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( + state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_VERTEX + )); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); + state->st = INSIDE_DATA; + } else { + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + } + break; + + case INSIDE_EDGE: + if (xmlStrEqual(localname, toXmlChar("data"))) { + IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( + state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_EDGE + )); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); + state->st = INSIDE_DATA; + } else { + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + } + break; + + case INSIDE_DATA: + /* We do not expect any new tags within a tag */ + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + break; + + case UNKNOWN: + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + break; + + case FINISH: + break; + + default: + IGRAPH_FATALF("Unexpected GraphML reader state %d.", (int) state->st); + } + +exit: + return IGRAPH_SUCCESS; +} + +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; + igraph_error_t result; + + if (!state->successful) { + return; + } + + result = igraph_i_graphml_sax_handler_start_element_ns_inner( + state, localname, prefix, uri, nb_namespaces, namespaces, + nb_attributes, nb_defaulted, attributes + ); + + if (result != IGRAPH_SUCCESS) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); + } +} + +static igraph_error_t igraph_i_graphml_sax_handler_end_element_ns_inner( + struct igraph_i_graphml_parser_state* state, + const xmlChar* localname, const xmlChar* prefix, + const xmlChar* uri +) { + 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_CHECK(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_CHECK(igraph_i_graphml_attribute_data_finish(state)); + IGRAPH_ASSERT(!igraph_vector_int_empty(&state->prev_state_stack)); + state->st = (igraph_i_graphml_parser_state_index_t) igraph_vector_int_pop_back(&state->prev_state_stack); + break; + + case UNKNOWN: + state->unknown_depth--; + if (!state->unknown_depth) { + IGRAPH_ASSERT(!igraph_vector_int_empty(&state->prev_state_stack)); + state->st = (igraph_i_graphml_parser_state_index_t) igraph_vector_int_pop_back(&state->prev_state_stack); + } + break; + + case FINISH: + break; + + default: + IGRAPH_FATALF("Unexpected GraphML reader state %d.", (int) state->st); + } + + return IGRAPH_SUCCESS; +} + +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; + igraph_error_t result; + + if (!state->successful) { + return; + } + + result = igraph_i_graphml_sax_handler_end_element_ns_inner( + state, localname, prefix, uri + ); + + if (result != IGRAPH_SUCCESS) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); + } +} + +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; + igraph_error_t result = IGRAPH_SUCCESS; + + if (!state->successful) { + return; + } + + switch (state->st) { + case INSIDE_KEY: + break; + + case INSIDE_DATA: + case INSIDE_DEFAULT: + result = igraph_i_graphml_append_to_data_char(state, ch, len); + break; + + default: + /* just ignore it */ + break; + } + + if (result != IGRAPH_SUCCESS) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); + } +} + +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 // HAVE_LIBXML == 1 + +#define IS_FORBIDDEN_CONTROL_CHAR(x) ((x) < ' ' && (x) != '\t' && (x) != '\r' && (x) != '\n') + +static igraph_error_t igraph_i_xml_escape(const char* src, char** dest) { + igraph_integer_t destlen = 0; + const char *s; + char *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)) { + IGRAPH_ERRORF("Forbidden control character 0x%02X found in igraph_i_xml_escape.", IGRAPH_EINVAL, ch); + } + } + *dest = IGRAPH_CALLOC(destlen + 1, char); + if (!*dest) { + IGRAPH_ERROR("Not enough memory.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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 IGRAPH_SUCCESS; +} + +#if HAVE_LIBXML == 1 +static void igraph_i_libxml_generic_error_handler(void* ctx, const char* msg, ...) { + struct igraph_i_graphml_parser_state* state = (struct igraph_i_graphml_parser_state*) ctx; + va_list args; + va_start(args, msg); + igraph_i_graphml_parser_state_set_error_from_varargs(state, msg, args); + va_end(args); +} + +#if LIBXML_VERSION < 21200 +static void igraph_i_libxml_structured_error_handler(void* ctx, xmlError *error) { +#else +static void igraph_i_libxml_structured_error_handler(void* ctx, const xmlError *error) { +#endif + struct igraph_i_graphml_parser_state* state = (struct igraph_i_graphml_parser_state*) ctx; + igraph_i_graphml_parser_state_set_error_from_xmlerror(state, error); +} +#endif // HAVE_LIBXML == 1 + +/** + * \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, see \ref igraph_set_attribute_table(). String attrribute values + * are returned in UTF-8 encoding. + * + * + * 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 + */ +igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, igraph_integer_t index) { + +#if HAVE_LIBXML == 1 + xmlParserCtxtPtr ctxt; + xmlGenericErrorFunc libxml_old_generic_error_handler; + void* libxml_old_generic_error_context; + xmlStructuredErrorFunc libxml_old_structured_error_handler; + void* libxml_old_structured_error_context; + 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 and use the first 4K to detect the + * encoding */ + res = (int) fread(buffer, 1, sizeof(buffer), instream); + if (res < (int) sizeof buffer && !feof(instream)) { + IGRAPH_ERROR("IO error while reading GraphML data.", IGRAPH_EFILE); + } + + /* Retrieve the current libxml2 error handlers and temporarily replace them + * with ones that do not print anything to stdout/stderr */ + libxml_old_generic_error_handler = xmlGenericError; + libxml_old_generic_error_context = xmlGenericErrorContext; + libxml_old_structured_error_handler = xmlStructuredError; + libxml_old_structured_error_context = xmlStructuredErrorContext; + xmlSetGenericErrorFunc(&state, &igraph_i_libxml_generic_error_handler); + xmlSetStructuredErrorFunc(&state, &igraph_i_libxml_structured_error_handler); + + /* 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 put a barrier on the FINALLY stack + * that prevents IGRAPH_ERROR() from freeing the parser state, and then we + * do this ourselves when needed */ + IGRAPH_FINALLY_ENTER(); + { + ctxt = xmlCreatePushParserCtxt(&igraph_i_graphml_sax_handler, + &state, + buffer, + res, + NULL); + if (ctxt) { + if (xmlCtxtUseOptions(ctxt, + XML_PARSE_NOBLANKS | + XML_PARSE_NONET | XML_PARSE_NSCLEAN | + XML_PARSE_NOCDATA | XML_PARSE_HUGE + )) { + xmlFreeParserCtxt(ctxt); + ctxt = NULL; + } + } + + /* Do the parsing */ + if (ctxt) { + while ((res = (int) fread(buffer, 1, sizeof buffer, instream)) > 0) { + xmlParseChunk(ctxt, buffer, res, 0); + if (!state.successful) { + break; + } + IGRAPH_ALLOW_INTERRUPTION(); + } + xmlParseChunk(ctxt, buffer, res, 1); + } + } + IGRAPH_FINALLY_EXIT(); + + /* Restore error handlers */ + xmlSetGenericErrorFunc(libxml_old_generic_error_context, libxml_old_generic_error_handler); + xmlSetStructuredErrorFunc(libxml_old_structured_error_context, libxml_old_structured_error_handler); + + /* Free the context */ + if (ctxt) { + 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); + } + } else { + /* We could not create the context earlier so no parsing was done */ + IGRAPH_ERROR("Cannot create XML parser context.", IGRAPH_FAILURE); + } + + /* 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); + + /* ...and we can also put the error message pointer on the FINALLY stack */ + if (error_message != NULL) { + IGRAPH_FINALLY(igraph_free, error_message); + } + + /* Trigger the stored error if needed */ + if (!parsing_successful) { + if (error_message != NULL) { + size_t len = strlen(error_message); + if (error_message[len-1] == '\n') { + error_message[len-1] = '\0'; + } + 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 // HAVE_LIBXML == 1 + IGRAPH_UNUSED(graph); + IGRAPH_UNUSED(instream); + IGRAPH_UNUSED(index); + + IGRAPH_ERROR("GraphML support is disabled.", IGRAPH_UNIMPLEMENTED); +#endif // HAVE_LIBXML == 1 +} + +/** + * \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. + * + * + * When a numerical attribute value is NaN, it will be omitted from the file. + * + * + * This function assumes that non-ASCII characters in attribute names and string + * attribute values are UTF-8 encoded. If this is not the case, the resulting + * XML file will be invalid. + * + * \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 + */ +igraph_error_t 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_int_t gtypes, vtypes, etypes; + igraph_integer_t 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_" : ""; + + 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_INT_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&etypes, 0); + igraph_i_attribute_get_info(graph, + &gnames, >ypes, + &vnames, &vtypes, + &enames, &etypes); + + /* graph attributes */ + for (i = 0; i < igraph_vector_int_size(>ypes); i++) { + const char *name; char *name_escaped; + name = igraph_strvector_get(&gnames, i); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + IGRAPH_FINALLY(igraph_free, 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); + IGRAPH_FINALLY_CLEAN(1); + } + + /* vertex attributes */ + for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { + const char *name; char *name_escaped; + name = igraph_strvector_get(&vnames, i); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + IGRAPH_FINALLY(igraph_free, 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); + IGRAPH_FINALLY_CLEAN(1); + } + + /* edge attributes */ + for (i = 0; i < igraph_vector_int_size(&etypes); i++) { + const char *name; char *name_escaped; + name = igraph_strvector_get(&enames, i); + IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped)); + IGRAPH_FINALLY(igraph_free, 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); + IGRAPH_FINALLY_CLEAN(1); + } + + 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_int_size(>ypes); i++) { + const char *name; char *name_escaped; + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + name = igraph_strvector_get(&gnames, i); + 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) { + const char *s; + char *s_escaped; + name = igraph_strvector_get(&gnames, i); + 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); + } + IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); + s = igraph_strvector_get(&strv, 0); + 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) { + name = igraph_strvector_get(&gnames, i); + 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++) { + const char *name; char *name_escaped; + ret = fprintf(outstream, " \n", l); + + if (ret < 0) { + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + } + + for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { + if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + name = igraph_strvector_get(&vnames, i); + 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) { + const char *s; + char *s_escaped; + name = igraph_strvector_get(&vnames, i); + 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); + } + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, + igraph_vss_1(l), &strv)); + s = igraph_strvector_get(&strv, 0); + 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) { + name = igraph_strvector_get(&vnames, i); + 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(IGRAPH_EDGEORDER_ID), &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t from, to; + const char *name; char *name_escaped; + igraph_integer_t edge = IGRAPH_EIT_GET(it); + igraph_edge(graph, edge, &from, &to); + ret = fprintf(outstream, " \n", + from, to); + if (ret < 0) { + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + } + + for (i = 0; i < igraph_vector_int_size(&etypes); i++) { + if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + name = igraph_strvector_get(&enames, i); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, + igraph_ess_1(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) { + const char *s; + char *s_escaped; + name = igraph_strvector_get(&enames, i); + 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); + } + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, + igraph_ess_1(edge), &strv)); + s = igraph_strvector_get(&strv, 0); + 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) { + name = igraph_strvector_get(&enames, i); + IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, name, + igraph_ess_1(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); + } + + igraph_strvector_destroy(&gnames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&enames); + igraph_vector_int_destroy(>ypes); + igraph_vector_int_destroy(&vtypes); + igraph_vector_int_destroy(&etypes); + igraph_vector_destroy(&numv); + igraph_strvector_destroy(&strv); + igraph_vector_bool_destroy(&boolv); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} diff --git a/src/io/leda.c b/src/io/leda.c new file mode 100644 index 0000000..cf742cd --- /dev/null +++ b/src/io/leda.c @@ -0,0 +1,310 @@ +/* -*- 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 { \ + int ret=(cmd); \ + if (ret<0) IGRAPH_ERROR("Writing LEDA format 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 should 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 should be stored. + * \return Error code. + * + * Time complexity: O(|V|+|E|), the number of vertices and edges in the + * graph. + */ + +igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, + const char *vertex_attr_name, + const char *edge_attr_name) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_eit_t it; + igraph_integer_t i = 0; + igraph_attribute_type_t vertex_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + igraph_attribute_type_t edge_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + 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)) { + IGRAPH_WARNINGF("The vertex attribute '%s' does not exist. No vertex values will be written.", + vertex_attr_name); + vertex_attr_name = NULL; + } + 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_type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_WARNINGF("The vertex attribute '%s' is not numeric, string or boolean. " + "No vertex values will be written.", + vertex_attr_name); + vertex_attr_name = NULL; vertex_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + } + } + + /* Check if we have the edge attribute */ + if (edge_attr_name && + !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, edge_attr_name)) { + IGRAPH_WARNINGF("The edge attribute '%s' does not exist. No edge values will be written.", + edge_attr_name); + edge_attr_name = NULL; + } + 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_type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_WARNINGF("The edge attribute '%s' is not numeric, string or boolean. " + "No edge values will be written.", + edge_attr_name); + edge_attr_name = NULL; edge_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + } + } + + /* Start writing header */ + CHECK(fprintf(outstream, "LEDA.GRAPH\n")); + + switch (vertex_attr_type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + CHECK(fprintf(outstream, "double\n")); + break; + case IGRAPH_ATTRIBUTE_STRING: + CHECK(fprintf(outstream, "string\n")); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + CHECK(fprintf(outstream, "bool\n")); + break; + default: + CHECK(fprintf(outstream, "void\n")); + } + + switch (edge_attr_type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + CHECK(fprintf(outstream, "double\n")); + break; + case IGRAPH_ATTRIBUTE_STRING: + CHECK(fprintf(outstream, "string\n")); + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + CHECK(fprintf(outstream, "bool\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, "%" IGRAPH_PRId "\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 = igraph_strvector_get(&values, i); + if (strchr(str, '\n') != 0) { + IGRAPH_ERROR("Vertex attribute values cannot contain newline characters.", + IGRAPH_EINVAL); + } + CHECK(fprintf(outstream, "|{%s}|\n", str)); + } + + igraph_strvector_destroy(&values); + IGRAPH_FINALLY_CLEAN(1); + } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + /* Vertices with boolean attributes */ + igraph_vector_bool_t values; + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, no_of_nodes); + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr( + graph, vertex_attr_name, igraph_vss_all(), &values)); + + for (i = 0; i < no_of_nodes; i++) { + CHECK(fprintf(outstream, "|{%s|}\n", VECTOR(values)[i] ? "true" : "false")); + } + + igraph_vector_bool_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, "%" IGRAPH_PRId "\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)) { + igraph_integer_t eid = IGRAPH_EIT_GET(it); + igraph_edge(graph, eid, &from, &to); + igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); + if (rev == IGRAPH_EIT_GET(it)) { + rev = -1; + } + CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{", + from + 1, to + 1, + 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)) { + igraph_integer_t eid = IGRAPH_EIT_GET(it); + const char *str = igraph_strvector_get(&values, eid); + igraph_edge(graph, eid, &from, &to); + igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); + 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, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{%s}|\n", + from + 1, to + 1, + rev + 1, str)); + IGRAPH_EIT_NEXT(it); + } + igraph_strvector_destroy(&values); + IGRAPH_FINALLY_CLEAN(1); + } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + /* Edges with boolean attributes */ + igraph_vector_bool_t values; + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, no_of_edges); + IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr( + graph, vertex_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values)); + + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t eid = IGRAPH_EIT_GET(it); + igraph_edge(graph, eid, &from, &to); + igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); + if (rev == IGRAPH_EIT_GET(it)) { + rev = -1; + } + CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{%s}|\n", + from + 1, to + 1, + rev + 1, + VECTOR(values)[eid] ? "true" : "false")); + IGRAPH_EIT_NEXT(it); + } + + igraph_vector_bool_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, IGRAPH_DIRECTED, false); + if (rev == IGRAPH_EIT_GET(it)) { + rev = -1; + } + CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{}|\n", + from + 1, to + 1, + rev + 1)); + IGRAPH_EIT_NEXT(it); + } + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +#undef CHECK diff --git a/src/io/lgl-header.h b/src/io/lgl-header.h new file mode 100644 index 0000000..f8579e4 --- /dev/null +++ b/src/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; + char errmsg[300]; + igraph_error_t igraph_errno; + igraph_bool_t has_weights; + igraph_vector_int_t *vector; + igraph_vector_t *weights; + igraph_trie_t *trie; + igraph_integer_t actvertex; +} igraph_i_lgl_parsedata_t; diff --git a/src/io/lgl.c b/src/io/lgl.c new file mode 100644 index 0000000..8c2f477 --- /dev/null +++ b/src/io/lgl.c @@ -0,0 +1,457 @@ +/* -*- 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 "io/lgl-header.h" +#include "io/parsers/lgl-parser.h" + +int igraph_lgl_yylex_init_extra (igraph_i_lgl_parsedata_t *user_defined, + void *scanner); +int igraph_lgl_yylex_destroy(void *scanner); +int igraph_lgl_yyparse(igraph_i_lgl_parsedata_t *context); +void igraph_lgl_yyset_in(FILE *in_str, void *yyscanner); + +/* for IGRAPH_FINALLY, which assumes that destructor functions return void */ +void igraph_lgl_yylex_destroy_wrapper (void *scanner ) { + (void) igraph_lgl_yylex_destroy(scanner); +} + +/** + * \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 + * (https://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 \c 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 + */ +igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, + igraph_bool_t names, + igraph_add_weights_t weights, + igraph_bool_t directed) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t 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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_TRIE_INIT_FINALLY(&trie, names); + + context.has_weights = false; + context.vector = &edges; + context.weights = &ws; + context.trie = ≜ + context.errmsg[0] = '\0'; + context.igraph_errno = IGRAPH_SUCCESS; + + igraph_lgl_yylex_init_extra(&context, &context.scanner); + IGRAPH_FINALLY(igraph_lgl_yylex_destroy_wrapper, context.scanner); + + igraph_lgl_yyset_in(instream, context.scanner); + + /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_lgl_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ + if (context.errmsg[0] != '\0') { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); + } else { + IGRAPH_ERROR("Cannot read LGL file.", IGRAPH_PARSEERROR); + } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read LGL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading LGL file.", err); + } + + /* Prepare attributes, if needed */ + + if (names) { + 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 = igraph_i_trie_borrow_keys(&trie); + 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; + } + + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_vertices(graph, 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_int_destroy(&edges); + igraph_vector_destroy(&ws); + igraph_lgl_yylex_destroy(context.scanner); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t check_name(const char *name) { + size_t len = 0; + + for (; *name != '\0'; name++, len++) { + if ( *name <= 0x020 /* space or non-printable */ + || *name == 0x7f /* del */ + || *name == '#') { + IGRAPH_ERRORF("The LGL format does not allow non-printable characters, spaces or '#' in vertex names. " + "Character code 0x%02X found.", IGRAPH_EINVAL, + *name); + } + } + if (len == 0) { + IGRAPH_ERROR("The LGL format does not support empty vertex names.", IGRAPH_EINVAL); + } + return IGRAPH_SUCCESS; +} + + +/** + * \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 \c true isolated vertices are also written + * to the file. If \c 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 \c 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 + */ +igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, + const char *names, const char *weights, + igraph_bool_t isolates) { + igraph_eit_t it; + igraph_integer_t actvertex = -1; + igraph_attribute_type_t nametype, weighttype; + const igraph_integer_t vcount = igraph_vcount(graph), ecount = igraph_ecount(graph); + + 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)) { + IGRAPH_WARNINGF("Names attribute '%s' does not exist.", names); + names = NULL; + } + if (names) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype, + IGRAPH_ATTRIBUTE_VERTEX, names)); + if (nametype != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_WARNINGF("Ignoring names attribute '%s', unknown attribute type.", names); + names = NULL; + } + } + + /* Check the weights as well */ + if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, weights)) { + IGRAPH_WARNINGF("Weights attribute '%s' does not exist.", weights); + weights = NULL; + } + if (weights) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype, + IGRAPH_ATTRIBUTE_EDGE, weights)); + if (weighttype != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_WARNINGF("Ignoring weights attribute '%s', unknown attribute type.", weights); + weights = NULL; + } + } + + if (names == NULL && weights == NULL) { + /* 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, "%" IGRAPH_PRId "\n", to); + } else { + actvertex = from; + ret = fprintf(outstream, "# %" IGRAPH_PRId "\n%" IGRAPH_PRId "\n", from, to); + } + if (ret < 0) { + IGRAPH_ERROR("Writing LGL file failed.", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + } else if (weights == NULL) { + /* No weights but use names */ + igraph_strvector_t nvec; + IGRAPH_STRVECTOR_INIT_FINALLY(&nvec, vcount); + 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; + const char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + str2 = igraph_strvector_get(&nvec, to); + IGRAPH_CHECK(check_name(str2)); + + if (from == actvertex) { + ret = fprintf(outstream, "%s\n", str2); + } else { + actvertex = from; + str1 = igraph_strvector_get(&nvec, from); + IGRAPH_CHECK(check_name(str1)); + ret = fprintf(outstream, "# %s\n%s\n", str1, str2); + } + if (ret < 0) { + IGRAPH_ERROR("Writing LGL file failed.", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_strvector_destroy(&nvec); + IGRAPH_FINALLY_CLEAN(1); + } else if (names == NULL) { + /* No names but weights */ + igraph_vector_t wvec; + IGRAPH_VECTOR_INIT_FINALLY(&wvec, ecount); + 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, "%" IGRAPH_PRId " ", to); + } else { + actvertex = from; + ret1 = fprintf(outstream, "# %" IGRAPH_PRId "\n%" IGRAPH_PRId " ", from, to); + } + ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); + ret3 = fputc('\n', outstream); + if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { + IGRAPH_ERROR("Writing LGL file 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, ecount); + IGRAPH_STRVECTOR_INIT_FINALLY(&nvec, vcount); + 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; + const char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + str2 = igraph_strvector_get(&nvec, to); + IGRAPH_CHECK(check_name(str2)); + + if (from == actvertex) { + ret = fprintf(outstream, "%s ", str2); + } else { + actvertex = from; + str1 = igraph_strvector_get(&nvec, from); + IGRAPH_CHECK(check_name(str1)); + ret = fprintf(outstream, "# %s\n%s ", str1, str2); + } + if (ret < 0) { + IGRAPH_ERROR("Writing LGL file failed.", IGRAPH_EFILE); + } + ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); + ret2 = fputc('\n', outstream); + if (ret < 0 || ret2 == EOF) { + IGRAPH_ERROR("Writing LGL file failed.", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_strvector_destroy(&nvec); + igraph_vector_destroy(&wvec); + IGRAPH_FINALLY_CLEAN(2); + } + + if (isolates) { + igraph_integer_t nov = vcount; + igraph_integer_t i; + int ret = 0; + igraph_integer_t deg; + igraph_strvector_t nvec; + const char *str; + + IGRAPH_STRVECTOR_INIT_FINALLY(&nvec, 1); + for (i = 0; i < nov; i++) { + IGRAPH_CHECK(igraph_degree_1(graph, °, i, IGRAPH_ALL, IGRAPH_LOOPS)); + if (deg == 0) { + if (names == NULL) { + ret = fprintf(outstream, "# %" IGRAPH_PRId "\n", i); + } else { + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names, + igraph_vss_1(i), &nvec)); + str = igraph_strvector_get(&nvec, 0); + IGRAPH_CHECK(check_name(str)); + ret = fprintf(outstream, "# %s\n", str); + } + } + if (ret < 0) { + IGRAPH_ERROR("Writing LGL file failed.", IGRAPH_EFILE); + } + } + igraph_strvector_destroy(&nvec); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/io/ncol-header.h b/src/io/ncol-header.h new file mode 100644 index 0000000..655ef2e --- /dev/null +++ b/src/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; + char errmsg[300]; + igraph_error_t igraph_errno; + igraph_bool_t has_weights; + igraph_vector_int_t *vector; + igraph_vector_t *weights; + igraph_trie_t *trie; +} igraph_i_ncol_parsedata_t; diff --git a/src/io/ncol.c b/src/io/ncol.c new file mode 100644 index 0000000..084a77c --- /dev/null +++ b/src/io/ncol.c @@ -0,0 +1,438 @@ +/* -*- 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 "io/ncol-header.h" +#include "io/parsers/ncol-parser.h" + +int igraph_ncol_yylex_init_extra (igraph_i_ncol_parsedata_t *user_defined, + void *scanner); +int igraph_ncol_yylex_destroy(void *scanner); +int igraph_ncol_yyparse(igraph_i_ncol_parsedata_t *context); +void igraph_ncol_yyset_in(FILE *in_str, void *yyscanner); + +/* for IGRAPH_FINALLY, which assumes that destructor functions return void */ +void igraph_ncol_yylex_destroy_wrapper (void *scanner ) { + (void) igraph_ncol_yylex_destroy(scanner); +} + +/** + * \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 + * (https://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 \c 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() + */ +igraph_error_t 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_int_t edges; + igraph_vector_t ws; + igraph_trie_t trie = IGRAPH_TRIE_NULL; + igraph_integer_t no_of_nodes; + igraph_integer_t no_predefined = 0; + igraph_vector_ptr_t name, weight; + igraph_vector_ptr_t *pname = NULL, *pweight = NULL; + igraph_attribute_record_t namerec, weightrec; + const char *namestr = "name", *weightstr = "weight"; + igraph_i_ncol_parsedata_t context; + + IGRAPH_VECTOR_INT_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) { + igraph_integer_t i, id, n; + const char *key; + n = no_predefined = igraph_strvector_size(predefnames); + for (i = 0; i < n; i++) { + key = igraph_strvector_get(predefnames, i); + IGRAPH_CHECK(igraph_trie_get(&trie, key, &id)); + if (id != i) { + IGRAPH_WARNING("Reading NCOL file, duplicate entry in predefined names."); + no_predefined--; + } + } + } + + context.has_weights = false; + context.vector = &edges; + context.weights = &ws; + context.trie = ≜ + context.errmsg[0] = '\0'; + context.igraph_errno = IGRAPH_SUCCESS; + + igraph_ncol_yylex_init_extra(&context, &context.scanner); + IGRAPH_FINALLY(igraph_ncol_yylex_destroy_wrapper, context.scanner); + + igraph_ncol_yyset_in(instream, context.scanner); + + /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_ncol_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ + if (context.errmsg[0] != '\0') { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); + } else { + IGRAPH_ERROR("Cannot read NCOL file.", IGRAPH_PARSEERROR); + } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read NCOL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading NCOL file.", err); + } + + if (predefnames != 0 && + igraph_trie_size(&trie) != no_predefined) { + IGRAPH_WARNING("Unknown vertex/vertices found in NCOL file, predefined names extended."); + } + + /* Prepare attributes, if needed */ + + if (names) { + 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 = igraph_i_trie_borrow_keys(&trie); + 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; + } + + if (igraph_vector_int_empty(&edges)) { + no_of_nodes = 0; + } else { + no_of_nodes = igraph_vector_int_max(&edges) + 1; + } + + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + 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); + IGRAPH_FINALLY_CLEAN(1); + } + if (pweight) { + igraph_vector_ptr_destroy(pweight); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_destroy(&ws); + igraph_trie_destroy(&trie); + igraph_vector_int_destroy(&edges); + igraph_ncol_yylex_destroy(context.scanner); + IGRAPH_FINALLY_CLEAN(5); /* +1 for 'graph' */ + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t check_name(const char *name) { + size_t len = 0; + + for (; *name != '\0'; name++, len++) { + if ( *name <= 0x020 /* space or non-printable */ + || *name == 0x7f /* del */) { + IGRAPH_ERRORF("The NCOL format does not allow non-printable characters or spaces in vertex names. " + "Character code 0x%02X found.", IGRAPH_EINVAL, + *name); + } + } + if (len == 0) { + IGRAPH_ERROR("The NCOL format does not support empty vertex names.", IGRAPH_EINVAL); + } + return IGRAPH_SUCCESS; +} + + +/** + * \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() + */ +igraph_error_t 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_ID), + &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)) { + IGRAPH_WARNINGF("Names attribute '%s' does not exist.", names); + names = NULL; + } + if (names) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype, + IGRAPH_ATTRIBUTE_VERTEX, names)); + if (nametype != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_WARNINGF("Ignoring names attribute '%s', " + "attribute type is not a string.", names); + names = NULL; + } + } + + /* Check the weights as well */ + if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, weights)) { + IGRAPH_WARNINGF("Weights attribute '%s' does not exist.", weights); + weights = NULL; + } + if (weights) { + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype, + IGRAPH_ATTRIBUTE_EDGE, weights)); + if (weighttype != IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_WARNINGF("Ignoring weights attribute '%s', " + "attribute type is not numeric.", weights); + weights = NULL; + } + } + + if (names == NULL && weights == NULL) { + /* 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, "%" IGRAPH_PRId " %" IGRAPH_PRId "\n", from, to); + if (ret < 0) { + IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + } else if (weights == NULL) { + /* 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; + const char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + str1 = igraph_strvector_get(&nvec, from); + IGRAPH_CHECK(check_name(str1)); + str2 = igraph_strvector_get(&nvec, to); + IGRAPH_CHECK(check_name(str2)); + ret = fprintf(outstream, "%s %s\n", str1, str2); + if (ret < 0) { + IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); + } + IGRAPH_EIT_NEXT(it); + } + igraph_strvector_destroy(&nvec); + IGRAPH_FINALLY_CLEAN(1); + } else if (names == NULL) { + /* 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, "%" IGRAPH_PRId " %" IGRAPH_PRId " ", from, to); + ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); + ret3 = fputc('\n', outstream); + if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { + IGRAPH_ERROR("Writing NCOL file 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; + const char *str1, *str2; + igraph_edge(graph, edge, &from, &to); + str1 = igraph_strvector_get(&nvec, from); + IGRAPH_CHECK(check_name(str1)); + str2 = igraph_strvector_get(&nvec, to); + IGRAPH_CHECK(check_name(str2)); + ret = fprintf(outstream, "%s %s ", str1, str2); + if (ret < 0) { + IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); + } + ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); + ret2 = fputc('\n', outstream); + if (ret < 0 || ret2 == EOF) { + IGRAPH_ERROR("Writing NCOL file 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 IGRAPH_SUCCESS; +} diff --git a/src/io/pajek-header.h b/src/io/pajek-header.h new file mode 100644 index 0000000..caa2360 --- /dev/null +++ b/src/io/pajek-header.h @@ -0,0 +1,62 @@ +/* + 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" + +/* According to Pajek's author, limits of the Pajek program as of 2022-1-1 are: + * "At the moment regular Pajek has limit one billion vertices, + * PajekXXL two billions, while Pajek 3XL ten billions." + * Hard-coding the limit INT32_MAX is safe when compiling wiht 32-bit integers, + * and likely sufficient for practical applications. + */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ +#define IGRAPH_PAJEK_MAX_VERTEX_COUNT (1 << 18) +#else +#define IGRAPH_PAJEK_MAX_VERTEX_COUNT INT32_MAX +#endif + +#define CHECK_OOM_RP(p) IGRAPH_CHECK_OOM((p), "Not enough memory to read Pajek format.") +#define CHECK_OOM_WP(p) IGRAPH_CHECK_OOM((p), "Not enough memory to write Pajek format.") + +typedef struct { + void *scanner; + igraph_bool_t eof; + char errmsg[300]; + igraph_error_t igraph_errno; + igraph_vector_int_t *vector; + igraph_bool_t directed; + igraph_integer_t vcount, vcount2; + igraph_integer_t actfrom; + igraph_integer_t actto; + igraph_trie_t *vertex_attribute_names; + igraph_vector_ptr_t *vertex_attributes; + igraph_trie_t *edge_attribute_names; + igraph_vector_ptr_t *edge_attributes; + igraph_integer_t vertexid; + igraph_integer_t actvertex; + igraph_integer_t actedge; +} igraph_i_pajek_parsedata_t; diff --git a/src/io/pajek.c b/src/io/pajek.c new file mode 100644 index 0000000..d54c293 --- /dev/null +++ b/src/io/pajek.c @@ -0,0 +1,790 @@ +/* -*- 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 "internal/hacks.h" /* IGRAPH_STATIC_ASSERT */ + +#include "io/pajek-header.h" +#include "io/parsers/pajek-parser.h" + +#include +#include + +int igraph_pajek_yylex_init_extra(igraph_i_pajek_parsedata_t *user_defined, + void *scanner); +int igraph_pajek_yylex_destroy(void *scanner); +int igraph_pajek_yyparse(igraph_i_pajek_parsedata_t *context); +void igraph_pajek_yyset_in(FILE *in_str, void *yyscanner); + +/* for IGRAPH_FINALLY, which assumes that destructor functions return void */ +void igraph_pajek_yylex_destroy_wrapper (void *scanner ) { + (void) igraph_pajek_yylex_destroy(scanner); +} + +void igraph_i_pajek_destroy_attr_vector(igraph_vector_ptr_t *attrs) { + const igraph_integer_t attr_count = igraph_vector_ptr_size(attrs); + for (igraph_integer_t i = 0; i < attr_count; i++) { + igraph_attribute_record_t *rec = VECTOR(*attrs)[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_BOOLEAN) { + igraph_vector_bool_t *vec = (igraph_vector_bool_t*) rec->value; + igraph_vector_bool_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); + } else { + /* Must never reach here */ + IGRAPH_FATAL("Unknown attribute type encountered."); + } + IGRAPH_FREE(rec->name); + IGRAPH_FREE(rec); + } + igraph_vector_ptr_destroy(attrs); +} + +/** + * \function igraph_read_graph_pajek + * \brief Reads a file in Pajek format. + * + * Only a subset of the Pajek format is implemented. This is partially + * because there is no formal specification for this format, but also because + * igraph does not support some Pajek features, like + * mixed graphs. + * + * + * Starting from version 0.6.1 igraph reads bipartite (two-mode) + * graphs from Pajek files and adds the \c type Boolean 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. + * \oli Temporal networks (i.e. with time events) are not supported. + * \oli Graphs with both directed and non-directed edges are not + * supported, as they cannot be represented in igraph. + * \oli Only Pajek networks are supported; permutations, hierarchies, + * clusters and vectors are not. + * \oli Multi-relational networks (i.e. networks with multiple edge + * types) are not supported. + * \oli Unicode characters encoded as &#dddd;, or newlines + * encoded as \n will not be decoded. + * \endolist + * + * + * If an attribute handler is 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 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. + * + * + * Unknown vertex or edge parameters are read as string vertex + * or edge attributes. If the parameter name conflicts with one + * the standard attribute names mentioned above, a _ + * character is appended to it to avoid conflict. + * + * + * In addition the following vertex attributes might be added: \c id + * and \c name are added (with the same value) if there are vertex IDs in the + * file. \c id is deprecated in favour of \c name and will no longer be used + * by future versions of igraph. \c x and \c y, and potentially \c z are also + * added if there are vertex coordinates in the file. + * + * + * The \c weight edge attribute will 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. The Pajek manual, + * http://vlado.fmf.uni-lj.si/pub/networks/pajek/doc/pajekman.pdf, + * and http://mrvar.fdv.uni-lj.si/pajek/DrawEPS.htm + * have information on the Pajek file format. There is additional + * useful information and sample files at + * http://mrvar.fdv.uni-lj.si/pajek/history.htm + * + * \param graph Pointer to an uninitialized graph object. + * \param file An already opened file handler. + * \return Error code. + * + * 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 + */ + +igraph_error_t igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { + + igraph_vector_int_t edges; + igraph_trie_t vattrnames; + igraph_vector_ptr_t vattrs; + igraph_trie_t eattrnames; + igraph_vector_ptr_t eattrs; + igraph_integer_t i, j; + igraph_i_pajek_parsedata_t context; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 1); + IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 0)); + IGRAPH_FINALLY(igraph_i_pajek_destroy_attr_vector, &vattrs); + + IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 1); + IGRAPH_CHECK(igraph_vector_ptr_init(&eattrs, 0)); + IGRAPH_FINALLY(igraph_i_pajek_destroy_attr_vector, &eattrs); + + context.directed = false; /* assume undirected until an element implying directedness is encountered */ + context.vector = &edges; + 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 = false; + context.errmsg[0] = '\0'; + context.igraph_errno = IGRAPH_SUCCESS; + + igraph_pajek_yylex_init_extra(&context, &context.scanner); + IGRAPH_FINALLY(igraph_pajek_yylex_destroy_wrapper, context.scanner); + + igraph_pajek_yyset_in(instream, context.scanner); + + /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_pajek_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ + if (context.errmsg[0] != 0) { + IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); + } else { + IGRAPH_ERROR("Cannot read Pajek file.", IGRAPH_PARSEERROR); + } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read Pajek file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading Pajek file.", err); + } + + /* Prepare attributes */ + const igraph_integer_t eattr_count = igraph_vector_ptr_size(&eattrs); + for (i = 0; i < eattr_count; 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_integer_t origsize = igraph_vector_size(vec); + IGRAPH_CHECK(igraph_vector_resize(vec, context.actedge)); + for (j = origsize; j < context.actedge; j++) { + VECTOR(*vec)[j] = IGRAPH_NAN; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + /* Boolean attributes are not currently added by the parser. + * This section is here for future-proofing. */ + igraph_vector_bool_t *vec = (igraph_vector_bool_t*)rec->value; + igraph_integer_t origsize = igraph_vector_bool_size(vec); + IGRAPH_CHECK(igraph_vector_bool_resize(vec, context.actedge)); + for (j = origsize; j < context.actedge; j++) { + VECTOR(*vec)[j] = 0; + } + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value; + /* strvector_resize() adds empty strings */ + IGRAPH_CHECK(igraph_strvector_resize(strvec, context.actedge)); + } else { + /* Must never reach here */ + IGRAPH_FATAL("Unknown attribute type encountered."); + } + } + + /* Create graph */ + 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)); + + igraph_vector_int_destroy(&edges); + igraph_i_pajek_destroy_attr_vector(&eattrs); + igraph_trie_destroy(&eattrnames); + igraph_i_pajek_destroy_attr_vector(&vattrs); + igraph_trie_destroy(&vattrnames); + igraph_pajek_yylex_destroy(context.scanner); + IGRAPH_FINALLY_CLEAN(7); /* +1 for 'graph' */ + + return IGRAPH_SUCCESS; +} + +/***** Writing Pajek files *****/ + +/* 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_LABELDIST 7 +#define V_LABELDEGREE2 8 +#define V_FRAMEWIDTH 9 +#define V_FONTSIZE 10 +#define V_ROTATION 11 +#define V_RADIUS 12 +#define V_DIAMONDRATIO 13 +#define V_LABELDEGREE 14 +#define V_FONT 15 +#define V_URL 16 +#define V_COLOR 17 +#define V_FRAMECOLOR 18 +#define V_LABELCOLOR 19 +#define V_LAST 20 + +#define E_WEIGHT 0 +#define E_LAST 1 + +/* Pajek encodes newlines as \n, and any unicode character can be encoded + * in the form &#hhhh;. Therefore we encode quotation marks as " */ +static igraph_error_t igraph_i_pajek_escape(const char* src, char** dest) { + igraph_integer_t destlen = 0; + igraph_bool_t need_escape = false; + + /* Determine whether the string contains characters to be escaped */ + const char *s; + char *d; + for (s = src; *s; s++, destlen++) { + if (*s == '\n' || *s == '\r') { + need_escape = true; + destlen++; + } else if (*s == '"') { + need_escape = true; + destlen += 4; + } else if (!isalnum(*s)) { + need_escape = true; + } + } + + if (!need_escape) { + /* At this point, we know that the string does not contain any chars + * that would warrant encoding. 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); + CHECK_OOM_WP(*dest); + + d = *dest; + strcpy(d + 1, src); + d[0] = d[destlen + 1] = '"'; + d[destlen + 2] = 0; + return IGRAPH_SUCCESS; + } + + *dest = IGRAPH_CALLOC(destlen + 3, char); + CHECK_OOM_WP(*dest); + + d = *dest; + *d = '"'; d++; + + for (s = src; *s; s++, d++) { + switch (*s) { + /* Encode quotation marks as ", as they would otherwise signify + the end/beginning of a string. */ + case '"': + strcpy(d, """); d += 4; break; + break; + /* Encode both CR and LF as \n, as neither should apear in a quoted string. + \n is the _only_ escape sequence Pajek understands. */ + case '\n': + case '\r': + *d = '\\'; d++; + *d = 'n'; + 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. + * + * Writes files in the native format of the Pajek software. This format + * is not recommended for data exchange or archival. It is meant solely + * for interoperability with Pajek. + * + * + * 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. + * + * + * Vertex and edge attributes that do not correspond to any documented + * Pajek parameter are discarded. + * + * + * 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). + * + * + * Pajek will only interpret UTF-8 encoded files if they contain a byte-order + * mark (BOM) at the beginning. igraph is agnostic of string attribute encodings + * and therefore it will never write a BOM. You need to add this manually + * if/when necessary. + * + * \param graph The graph object to write. + * \param outstream The file to write to. It should be opened and writable. + * \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 + */ + +igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + igraph_attribute_type_t vtypes[V_LAST], etypes[E_LAST]; + igraph_bool_t write_vertex_attrs = false; + + /* Same order as the #define's */ + const char *vnames[] = { "id", "x", "y", "z", "shape", "xfact", "yfact", + "labeldist", "labeldegree2", "framewidth", + "fontsize", "rotation", "radius", + "diamondratio", "labeldegree", + "font", "url", "color", "framecolor", + "labelcolor" + }; + IGRAPH_STATIC_ASSERT(sizeof(vnames) / sizeof(vnames[0]) == V_LAST); + + /* Arrays called xxx[] are igraph attribute names, + * xxx2[] are the corresponding Pajek names. */ + const char *vnumnames[] = { "xfact", "yfact", "labeldist", + "labeldegree2", "framewidth", "fontsize", + "rotation", "radius", "diamondratio", + "labeldegree" + }; + const char *vnumnames2[] = { "x_fact", "y_fact", "lr", "lphi", "bw", + "fos", "phi", "r", "q", "la" + }; + IGRAPH_STATIC_ASSERT(sizeof(vnumnames) == sizeof(vnumnames2)); + + const char *vstrnames[] = { "font", "url", "color", "framecolor", + "labelcolor" + }; + const char *vstrnames2[] = { "font", "url", "ic", "bc", "lc" }; + IGRAPH_STATIC_ASSERT(sizeof(vstrnames) == sizeof(vstrnames2)); + + /* Same order as the #define's */ + const char *enames[] = { "weight" }; + IGRAPH_STATIC_ASSERT(sizeof(enames) / sizeof(enames[0]) == E_LAST); + + 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" + }; + IGRAPH_STATIC_ASSERT(sizeof(enumnames) == sizeof(enumnames2)); + + const char *estrnames[] = { "arrowtype", "linepattern", "label", + "labelcolor", "color", "font" + }; + const char *estrnames2[] = { "a", "p", "l", "lc", "c", "font" }; + IGRAPH_STATIC_ASSERT(sizeof(estrnames) == sizeof(estrnames2)); + + /* 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_int_t ex_numa; + igraph_vector_int_t ex_stra; + igraph_vector_int_t vx_numa; + igraph_vector_int_t vx_stra; + + const char *s; + char *escaped; + + igraph_bool_t bipartite = false; + igraph_vector_int_t bip_index, bip_index2; + igraph_vector_bool_t bvec; + igraph_integer_t notop = 0, nobottom = 0; + + IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); + IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&ex_numa, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ex_stra, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vx_numa, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vx_stra, 0); + + /* Check if graph is bipartite, i.e. whether it has a Boolean 'type' vertex attribute. */ + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, "type")) { + igraph_attribute_type_t type_type; + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &type_type, IGRAPH_ATTRIBUTE_VERTEX, "type")); + if (type_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + bipartite = true; write_vertex_attrs = true; + /* Count top and bottom vertices, we go over them twice, + because we want to keep their original order */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&bip_index, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&bip_index2, no_of_nodes); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&bvec, 1); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, + "type", igraph_vss_1(i), &bvec)); + if (VECTOR(bvec)[0]) { + notop++; + } else { + nobottom++; + } + } + for (igraph_integer_t i = 0, bptr = 0, tptr = nobottom; i < no_of_nodes; i++) { + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, + "type", igraph_vss_1(i), &bvec)); + if (VECTOR(bvec)[0]) { + VECTOR(bip_index)[tptr] = i; + VECTOR(bip_index2)[i] = tptr; + tptr++; + } else { + VECTOR(bip_index)[bptr] = i; + VECTOR(bip_index2)[i] = bptr; + bptr++; + } + } + igraph_vector_bool_destroy(&bvec); + IGRAPH_FINALLY_CLEAN(1); + } + } + + /* Write header */ + if (bipartite) { + if (fprintf(outstream, "*Vertices %" IGRAPH_PRId " %" IGRAPH_PRId "%s", no_of_nodes, nobottom, + newline) < 0) { + IGRAPH_ERROR("Cannot write pajek file.", IGRAPH_EFILE); + } + } else { + if (fprintf(outstream, "*Vertices %" IGRAPH_PRId "%s", no_of_nodes, newline) < 0) { + IGRAPH_ERROR("Cannot write pajek file.", IGRAPH_EFILE); + } + } + + /* Check the vertex attributes, and determine if we need to write them. */ + memset(vtypes, 0, sizeof(vtypes[0])*V_LAST); + for (igraph_integer_t i = 0; i < V_LAST; i++) { + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vnames[i])) { + IGRAPH_CHECK(igraph_i_attribute_gettype( + graph, &vtypes[i], IGRAPH_ATTRIBUTE_VERTEX, vnames[i])); + write_vertex_attrs = true; + } else { + vtypes[i] = (igraph_attribute_type_t) -1; + } + } + for (igraph_integer_t i = 0; i < (igraph_integer_t) (sizeof(vnumnames) / sizeof(vnumnames[0])); i++) { + igraph_attribute_type_t type; + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vnumnames[i])) { + IGRAPH_CHECK(igraph_i_attribute_gettype( + graph, &type, IGRAPH_ATTRIBUTE_VERTEX, vnumnames[i])); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_vector_int_push_back(&vx_numa, i)); + } + } + } + for (igraph_integer_t i = 0; i < (igraph_integer_t) (sizeof(vstrnames) / sizeof(vstrnames[0])); i++) { + igraph_attribute_type_t type; + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vstrnames[i])) { + IGRAPH_CHECK(igraph_i_attribute_gettype( + graph, &type, IGRAPH_ATTRIBUTE_VERTEX, vstrnames[i])); + if (type == IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_CHECK(igraph_vector_int_push_back(&vx_stra, i)); + } + } + } + + /* Write vertices */ + if (write_vertex_attrs) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t id = bipartite ? VECTOR(bip_index)[i] : i; + + /* vertex ID */ + fprintf(outstream, "%" IGRAPH_PRId, i + 1); + if (vtypes[V_ID] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vnames[V_ID], igraph_vss_1(id), &numv)); + fputs(" \"", outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + fputc('"', outstream); + } else if (vtypes[V_ID] == IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr( + graph, vnames[V_ID], igraph_vss_1(id), &strv)); + s = igraph_strvector_get(&strv, 0); + IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); + fprintf(outstream, " %s", escaped); + IGRAPH_FREE(escaped); + } else { + fprintf(outstream, " \"%" IGRAPH_PRId "\"", id + 1); + } + + /* coordinates */ + if (vtypes[V_X] == IGRAPH_ATTRIBUTE_NUMERIC && + vtypes[V_Y] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vnames[V_X], igraph_vss_1(id), &numv)); + fputc(' ', outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vnames[V_Y], igraph_vss_1(id), &numv)); + fputc(' ', outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + if (vtypes[V_Z] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_Z], + igraph_vss_1(id), &numv)); + fputc(' ', outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + } + } + + /* shape */ + if (vtypes[V_SHAPE] == IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr( + graph, vnames[V_SHAPE], igraph_vss_1(id), &strv)); + s = igraph_strvector_get(&strv, 0); + IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); + fprintf(outstream, " %s", escaped); + IGRAPH_FREE(escaped); + } + + /* numeric parameters */ + for (igraph_integer_t j = 0; j < igraph_vector_int_size(&vx_numa); j++) { + igraph_integer_t idx = VECTOR(vx_numa)[j]; + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vnumnames[idx], igraph_vss_1(id), &numv)); + fprintf(outstream, " %s ", vnumnames2[idx]); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + } + + /* string parameters */ + for (igraph_integer_t j = 0; j < igraph_vector_int_size(&vx_stra); j++) { + igraph_integer_t idx = VECTOR(vx_stra)[j]; + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr( + graph, vstrnames[idx], igraph_vss_1(id), &strv)); + s = igraph_strvector_get(&strv, 0); + 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 */ + /* TODO: refactor and simplify since only "weight" is relevant */ + for (igraph_integer_t i = 0; i < E_LAST; i++) { + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, enames[i])) { + IGRAPH_CHECK(igraph_i_attribute_gettype( + graph, &etypes[i], IGRAPH_ATTRIBUTE_EDGE, enames[i])); + } else { + etypes[i] = (igraph_attribute_type_t) -1; + } + } + for (igraph_integer_t i = 0; i < (igraph_integer_t) (sizeof(enumnames) / sizeof(enumnames[0])); i++) { + igraph_attribute_type_t type; + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, enumnames[i])) { + IGRAPH_CHECK(igraph_i_attribute_gettype( + graph, &type, IGRAPH_ATTRIBUTE_EDGE, enumnames[i])); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_vector_int_push_back(&ex_numa, i)); + } + } + } + for (igraph_integer_t i = 0; i < (igraph_integer_t) (sizeof(estrnames) / sizeof(estrnames[0])); i++) { + igraph_attribute_type_t type; + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, estrnames[i])) { + IGRAPH_CHECK(igraph_i_attribute_gettype( + graph, &type, IGRAPH_ATTRIBUTE_EDGE, estrnames[i])); + if (type == IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_CHECK(igraph_vector_int_push_back(&ex_stra, i)); + } + } + } + + for (igraph_integer_t i = 0; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit), i++) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + igraph_edge(graph, edge, &from, &to); + if (bipartite) { + from = VECTOR(bip_index2)[from]; + to = VECTOR(bip_index2)[to]; + } + fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId , from + 1, to + 1); + + /* Weights */ + if (etypes[E_WEIGHT] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr( + graph, enames[E_WEIGHT], igraph_ess_1(edge), &numv)); + fputc(' ', outstream); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + } + + /* numeric parameters */ + for (igraph_integer_t j = 0; j < igraph_vector_int_size(&ex_numa); j++) { + igraph_integer_t idx = VECTOR(ex_numa)[j]; + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr( + graph, enumnames[idx], igraph_ess_1(edge), &numv)); + fprintf(outstream, " %s ", enumnames2[idx]); + igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); + } + + /* string parameters */ + for (igraph_integer_t j = 0; j < igraph_vector_int_size(&ex_stra); j++) { + igraph_integer_t idx = VECTOR(ex_stra)[j]; + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr( + graph, estrnames[idx], igraph_ess_1(edge), &strv)); + s = igraph_strvector_get(&strv, 0); + 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_int_destroy(&ex_numa); + igraph_vector_int_destroy(&ex_stra); + igraph_vector_int_destroy(&vx_numa); + igraph_vector_int_destroy(&vx_stra); + igraph_strvector_destroy(&strv); + igraph_vector_destroy(&numv); + IGRAPH_FINALLY_CLEAN(6); + return IGRAPH_SUCCESS; +} diff --git a/src/io/parse_utils.c b/src/io/parse_utils.c new file mode 100644 index 0000000..ad2d484 --- /dev/null +++ b/src/io/parse_utils.c @@ -0,0 +1,381 @@ + +#include "parse_utils.h" + +#include "igraph_foreign.h" +#include "igraph_memory.h" + +#include "config.h" + +#include +#include +#include +#include + +#if defined(HAVE_XLOCALE) +/* On some systems, xlocale.h exists, but uselocale() is still in locale.h. + * Thus we include both. */ +#include +#include +#else +#include +#endif + +/* Trims whitespace from the beginning and the end of a string with a specified length. + * A pointer to the first character of the result substring, as well as its length, are returned. + * + * If you have a null-terminated string, call this function as + * + * igraph_i_trim_whitespace(str, strlen(str), &res, &len); + * + * This does not carry a performance penalty, as the end of the string would need to be + * determined anyway. + */ +void igraph_i_trim_whitespace(const char *str, size_t str_len, const char **res, size_t *res_len) { + const char *beg = str, *end = str + str_len; + while (beg < end && isspace(beg[0]) ) beg++; + while (end > beg && isspace(end[-1])) end--; + *res = beg; + *res_len = end - beg; +} + + +/* TODO: Support for reporting line number where parse error occurred. */ + +/* Converts a string to an integer. Throws an error if the result is not representable. + * + * The input is a not-necessarily-null-terminated string that must contain only the number. + * Any additional characters at the end of the string, such as whitespace, will trigger + * a parsing error. + * + * An error is returned if the input is an empty string. + */ +igraph_error_t igraph_i_parse_integer(const char *str, size_t length, igraph_integer_t *value) { + char buffer[128]; + char *tmp, *end; + char last_char; + igraph_bool_t out_of_range, dynamic_alloc; + long long val; + + if (length == 0) { + IGRAPH_ERROR("Cannot parse integer from empty string.", IGRAPH_PARSEERROR); + } + + dynamic_alloc = length+1 > sizeof(buffer) / sizeof(buffer[0]); + + if (dynamic_alloc) { + tmp = IGRAPH_CALLOC(length+1, char); + IGRAPH_CHECK_OOM(tmp, "Failed to parse integer."); + } else { + tmp = buffer; + } + + strncpy(tmp, str, length); + tmp[length]='\0'; + + /* To avoid having to choose the appropriate strto?() function based on + * the definition of igraph_integer_t, we first use a long long variable + * which should be at least as large as igraph_integer_t on any platform. */ + errno = 0; + val = strtoll(tmp, &end, 10); + out_of_range = errno == ERANGE; + *value = (igraph_integer_t) val; + last_char = *end; + if (*value != val) { + out_of_range = true; + } + + /* Free memory before raising any errors. */ + if (dynamic_alloc) { + IGRAPH_FREE(tmp); + } + + if (out_of_range) { + IGRAPH_ERROR("Failed to parse integer.", val > 0 ? IGRAPH_EOVERFLOW : IGRAPH_EUNDERFLOW); + } + + /* Did we parse to the end of the string? */ + if (last_char) { + IGRAPH_ERRORF("Unexpected character '%c' while parsing integer.", IGRAPH_PARSEERROR, last_char); + } + + return IGRAPH_SUCCESS; +} + + +/* Converts a string to a real number. Throws an error if the result is not representable. + * + * The input is a not-necessarily-null-terminated string that must contain only the number. + * Any additional characters at the end of the string, such as whitespace, will trigger + * a parsing error. + * + * NaN and Inf are supported. An error is returned if the input is an empty string. + */ +igraph_error_t igraph_i_parse_real(const char *str, size_t length, igraph_real_t *value) { + char buffer[128]; + char *tmp, *end; + char last_char; + igraph_bool_t out_of_range, dynamic_alloc; + + if (length == 0) { + IGRAPH_ERROR("Cannot parse real number from empty string.", IGRAPH_PARSEERROR); + } + + dynamic_alloc = length+1 > sizeof(buffer) / sizeof(buffer[0]); + + if (dynamic_alloc) { + tmp = IGRAPH_CALLOC(length+1, char); + IGRAPH_CHECK_OOM(tmp, "Failed to parse real number."); + } else { + tmp = buffer; + } + + strncpy(tmp, str, length); + tmp[length]='\0'; + + errno = 0; + *value = strtod(tmp, &end); + out_of_range = errno == ERANGE; /* This does not trigger when reading +-Inf. */ + last_char = *end; + + /* Free memory before raising any errors. */ + if (dynamic_alloc) { + IGRAPH_FREE(tmp); + } + + if (out_of_range) { + IGRAPH_ERROR("Failed to parse real number.", *value > 0 ? IGRAPH_EOVERFLOW : IGRAPH_EUNDERFLOW); + } + + /* Did we parse to the end of the string? */ + if (last_char) { + IGRAPH_ERRORF("Unexpected character '%c' while parsing real number.", IGRAPH_PARSEERROR, last_char); + } + + return IGRAPH_SUCCESS; +} + + +/* Skips all whitespace in a file. */ +igraph_error_t igraph_i_fskip_whitespace(FILE *file) { + int ch; + + do { + ch = fgetc(file); + } while (isspace(ch)); + if (ferror(file)) { + IGRAPH_ERROR("Error reading file.", IGRAPH_EFILE); + } + ungetc(ch, file); + + return IGRAPH_SUCCESS; +} + + +/* Reads an integer from a file. Throws an error if the result is not representable. + * + * Any initial whitespace is skipped. If no number is found, an error is raised. + * + * This function assumes that the number is followed by whitespace or the end of the file. + * If this is not the case, an error will be raised. + */ +igraph_error_t igraph_i_fget_integer(FILE *file, igraph_integer_t *value) { + /* The value requiring the most characters on 64-bit is -2^63, i.e. "-9223372036854775808". + * This is 20 characters long, plus one for the null terminator, requiring a buffer of + * at least 21 characters. We use a slightly larger buffer to allow for leading zeros and + * clearer error messages. + * + * Note: The string held in this buffer is not null-terminated. + */ + char buf[32]; + int ch; + + IGRAPH_CHECK(igraph_i_fskip_whitespace(file)); + + int i = 0; /* must be 'int' due to use in printf format specifier */ + while (1) { + ch = fgetc(file); + if (ch == EOF) break; + if (isspace(ch)) { + ungetc(ch, file); + break; + } + if (i == sizeof(buf)) { + /* Reached the end of the buffer. */ + IGRAPH_ERRORF("'%.*s' is not a valid integer value.", IGRAPH_PARSEERROR, i, buf); + } + buf[i++] = ch; + } + if (ferror(file)) { + IGRAPH_ERROR("Error while reading integer.", IGRAPH_EFILE); + } + + if (i == 0) { + IGRAPH_ERROR("Integer expected, reached end of file instead.", IGRAPH_PARSEERROR); + } + + IGRAPH_CHECK(igraph_i_parse_integer(buf, i, value)); + + return IGRAPH_SUCCESS; +} + + +/* Reads a real number from a file. Throws an error if the result is not representable. + * + * Any initial whitespace is skipped. If no number is found, an error is raised. + * + * This function assumes that the number is followed by whitespace or the end of the file. + * If this is not the case, an error will be raised. + */ +igraph_error_t igraph_i_fget_real(FILE *file, igraph_real_t *value) { + /* The value requiring the most characters with an IEEE-754 double is the smallest + * representable number, with signs added, "-2.2250738585072014e-308" + * + * This is 24 characters long, plus one for the null terminator, requiring a buffer of + * at least 25 characters. This is 17 mantissa digits for lossless representation, + * 3 exponent digits, "e", and up to two minus signs. We use a larger buffer as some + * files may have more digits specified than necessary for exact representation. + * + * Note: The string held in this buffer is not null-terminated. + */ + char buf[64]; + int ch; + + IGRAPH_CHECK(igraph_i_fskip_whitespace(file)); + + int i = 0; /* must be 'int' due to use in printf format specifier */ + while (1) { + ch = fgetc(file); + if (ch == EOF) break; + if (isspace(ch)) { + ungetc(ch, file); + break; + } + if (i == sizeof(buf)) { + /* Reached the end of the buffer. */ + IGRAPH_ERRORF("'%.*s' is not a valid real value.", IGRAPH_PARSEERROR, i, buf); + } + buf[i++] = ch; + } + if (ferror(file)) { + IGRAPH_ERROR("Error while reading real number.", IGRAPH_EFILE); + } + + if (i == 0) { + IGRAPH_ERROR("Real number expected, reached end of file instead.", IGRAPH_PARSEERROR); + } + + IGRAPH_CHECK(igraph_i_parse_real(buf, i, value)); + + return IGRAPH_SUCCESS; +} + + +/* igraph_i_safelocale() and igraph_i_unsafelocale() will set the numeric locale to "C" + * and re-set it to its original value. This is to ensure that parsing and writing + * numbers uses a decimal point instead of a comma. + * + * These functions attempt to set the locale only for the current thread on a best-effort + * basis. On some platforms this is not possible, so the global locale will be changed. + * This is not safe to do in multi-threaded programs (not even if igraph runs only in + * a single thread). + */ + +struct igraph_safelocale_s { +#ifdef HAVE_USELOCALE + locale_t original_locale; + locale_t c_locale; +#else + char *original_locale; +# ifdef HAVE__CONFIGTHREADLOCALE + int per_thread_locale; +# endif +#endif +}; + +/** + * \function igraph_enter_safelocale + * \brief Temporarily set the C locale. + * + * \experimental + * + * igraph's foreign format readers and writers require a locale that uses a + * decimal point instead of a decimal comma. This is a convenience function + * that temporarily sets the C locale so that readers and writers would work + * correctly. It \em must be paired with a call to \ref igraph_exit_safelocale(), + * otherwise a memory leak will occur. + * + * + * This function tries to set the locale for the current thread only on a + * best-effort basis. Restricting the locale change to a single thread is not + * supported on all platforms. In these cases, this function falls back to using + * the standard setlocale() function, which affects the entire process + * and is not safe to use from concurrent threads. + * + * + * It is generally recommended to run igraph within a thread that has been + * permanently set to the C locale using system-specific means. This is a convenience + * function for situations when this is not easily possible because the programmer + * is not in control of the process, such as when developing plugins/extensions. + * Note that processes start up in the C locale by default, thus nothing needs to + * be done unless the locale has been changed away from the default. + * + * \param loc Pointer to a variable of type \c igraph_safelocale_t. The current + * locale will be stored here, so that it can be restored using + * \ref igraph_exit_safelocale(). + * \return Error code. + * + * \example examples/simple/safelocale.c + */ + +igraph_error_t igraph_enter_safelocale(igraph_safelocale_t *loc) { + *loc = IGRAPH_CALLOC(1, struct igraph_safelocale_s); + IGRAPH_CHECK_OOM(loc, "Could not set C locale."); + igraph_safelocale_t l = *loc; +#ifdef HAVE_USELOCALE + l->c_locale = newlocale(LC_NUMERIC_MASK, "C", NULL); + if (! l->c_locale) { + IGRAPH_ERROR("Could not set C locale.", IGRAPH_FAILURE); + } + l->original_locale = uselocale(l->c_locale); +#else + l->original_locale = strdup(setlocale(LC_NUMERIC, NULL)); + IGRAPH_CHECK_OOM(l->original_locale, "Not enough memory."); +# ifdef HAVE__CONFIGTHREADLOCALE + /* On Windows, we can enable per-thread locale */ + l->per_thread_locale = _configthreadlocale(0); + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); +# endif + setlocale(LC_NUMERIC, "C"); +#endif + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_exit_safelocale + * \brief Temporarily set the C locale. + * + * \experimental + * + * Restores a locale saved by \ref igraph_enter_safelocale() and deallocates + * all associated data. This function \em must be paired with a call to + * \ref igraph_enter_safelocale(). + * + * \param loc A variable of type \c igraph_safelocale_t, originally set + * by \ref igraph_enter_safelocale(). + */ + +void igraph_exit_safelocale(igraph_safelocale_t *loc) { + igraph_safelocale_t l = *loc; +#ifdef HAVE_USELOCALE + uselocale(l->original_locale); + freelocale(l->c_locale); +#else + setlocale(LC_NUMERIC, l->original_locale); + IGRAPH_FREE(l->original_locale); +# ifdef HAVE__CONFIGTHREADLOCALE + /* Restore per-thread locale setting on Windows */ + _configthreadlocale(l->per_thread_locale); +# endif +#endif + IGRAPH_FREE(*loc); +} diff --git a/src/io/parse_utils.h b/src/io/parse_utils.h new file mode 100644 index 0000000..49a23d0 --- /dev/null +++ b/src/io/parse_utils.h @@ -0,0 +1,41 @@ + +#ifndef IGRAPH_PARSE_UTILS_H +#define IGRAPH_PARSE_UTILS_H + +#include "igraph_error.h" +#include "igraph_types.h" + +/* This macro must be used only in Bison actions, in place of IGRAPH_CHECK(). */ +#define IGRAPH_YY_CHECK(expr) \ + do { \ + igraph_error_t igraph_i_ret = (expr); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) { \ + context->igraph_errno = igraph_i_ret; \ + yyerror(&yylloc, context, "failed"); \ + YYABORT; \ + } \ + } while (0) + +/* This macro must be used only in Bison actions, in place of IGRAPH_CHECK(). */ +/* Note: + * Don't name macro argument 'igraph_errno' due to use of context->igraph_errno, + * or 'errno' due to use of #include in parse_utils.c. */ +#define IGRAPH_YY_ERRORF(reason, error_code, ...) \ + do { \ + igraph_errorf(reason, IGRAPH_FILE_BASENAME, __LINE__, \ + error_code, __VA_ARGS__) ; \ + context->igraph_errno = error_code; \ + YYABORT; \ + } while (0) + +void igraph_i_trim_whitespace(const char *str, size_t str_len, const char **res, size_t *res_len); + +igraph_error_t igraph_i_fskip_whitespace(FILE *file); + +igraph_error_t igraph_i_parse_integer(const char *str, size_t length, igraph_integer_t *value); +igraph_error_t igraph_i_parse_real(const char *str, size_t length, igraph_real_t *value); + +igraph_error_t igraph_i_fget_integer(FILE *file, igraph_integer_t *value); +igraph_error_t igraph_i_fget_real(FILE *file, igraph_real_t *value); + +#endif /* IGRAPH_PARSE_UTILS_H */ diff --git a/src/io/parsers/dl-lexer.c b/src/io/parsers/dl-lexer.c new file mode 100644 index 0000000..18ff361 --- /dev/null +++ b/src/io/parsers/dl-lexer.c @@ -0,0 +1,2533 @@ + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + yy_size_t yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + #define YY_LINENO_REWIND_TO(dst) \ + do {\ + const char *p;\ + for ( p = yy_cp-1; p >= (dst); --p)\ + if ( *p == '\n' )\ + --yylineno;\ + }while(0) + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via 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, yy_size_t 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 yynoreturn 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 = (yy_size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 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, 30, 31, 1, 32, + + 33, 34, 35, 1, 36, 1, 1, 37, 38, 39, + 40, 1, 1, 41, 42, 43, 44, 1, 1, 45, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 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[47] = + { 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, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3 + } ; + +static const flex_int16_t yy_base[138] = + { 0, + 0, 38, 76, 121, 166, 211, 256, 301, 346, 391, + 101, 493, 4, 72, 64, 2, 1, 4, 0, 22, + 15, 493, 55, 60, 0, 493, 24, 0, 31, 35, + 77, 0, 45, 41, 53, 493, 53, 39, 66, 48, + 69, 91, 93, 97, 101, 107, 112, 0, 113, 0, + 114, 121, 110, 138, 0, 131, 129, 77, 150, 107, + 118, 154, 158, 173, 169, 151, 154, 41, 127, 145, + 179, 187, 493, 156, 159, 191, 193, 192, 198, 202, + 215, 436, 221, 493, 232, 0, 193, 183, 205, 208, + 212, 211, 220, 224, 223, 232, 251, 253, 250, 250, + + 252, 258, 255, 262, 257, 262, 253, 253, 255, 265, + 256, 260, 273, 294, 14, 293, 8, 239, 312, 286, + 316, 317, 318, 322, 323, 328, 330, 493, 475, 478, + 481, 484, 487, 490, 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[540] = + { 0, + 55, 13, 14, 15, 13, 35, 50, 48, 35, 16, + 16, 37, 37, 41, 38, 17, 45, 18, 121, 45, + 19, 39, 20, 42, 119, 49, 42, 40, 49, 41, + 38, 17, 51, 18, 43, 51, 19, 39, 20, 13, + 14, 15, 13, 40, 52, 52, 56, 16, 16, 56, + 57, 57, 73, 17, 35, 18, 45, 35, 19, 45, + 20, 45, 37, 37, 45, 58, 36, 59, 46, 17, + 59, 18, 60, 47, 19, 36, 20, 12, 14, 15, + 22, 58, 22, 61, 46, 53, 52, 52, 60, 47, + 68, 23, 42, 54, 62, 42, 24, 62, 45, 61, + + 128, 45, 45, 43, 128, 45, 68, 23, 45, 54, + 128, 45, 24, 45, 49, 51, 45, 49, 51, 65, + 65, 12, 12, 14, 15, 22, 64, 22, 69, 53, + 52, 52, 56, 63, 70, 56, 23, 54, 57, 57, + 74, 24, 64, 66, 69, 66, 128, 67, 67, 63, + 70, 59, 23, 54, 59, 62, 74, 24, 62, 45, + 67, 67, 45, 67, 67, 75, 12, 26, 14, 15, + 26, 71, 12, 128, 45, 27, 27, 45, 65, 65, + 45, 75, 78, 45, 79, 54, 128, 71, 45, 72, + 76, 45, 45, 81, 45, 45, 81, 45, 78, 83, + + 79, 54, 83, 85, 82, 72, 85, 77, 91, 84, + 92, 12, 26, 14, 15, 26, 81, 12, 80, 81, + 27, 27, 83, 77, 91, 83, 92, 82, 93, 94, + 95, 96, 128, 85, 80, 97, 85, 90, 98, 99, + 122, 128, 128, 122, 93, 94, 95, 96, 90, 128, + 123, 97, 100, 90, 98, 99, 12, 29, 14, 15, + 29, 30, 12, 30, 90, 31, 31, 101, 100, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 101, 116, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 117, + + 116, 12, 29, 14, 15, 29, 30, 12, 30, 118, + 31, 31, 120, 124, 125, 117, 124, 126, 122, 122, + 126, 122, 122, 124, 127, 118, 124, 127, 120, 126, + 125, 127, 126, 128, 127, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 12, 33, 14, 15, + 33, 128, 12, 128, 128, 34, 34, 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, 12, 33, 14, 15, 33, 128, 12, 128, 128, + + 34, 34, 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, 12, 86, 128, 128, + 86, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 87, 88, 128, 128, 128, 128, 89, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 87, 88, + 128, 128, 128, 128, 89, 12, 12, 12, 21, 21, + 21, 25, 25, 25, 28, 28, 28, 32, 32, 32, + 44, 44, 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, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 128 + } ; + +static const flex_int16_t yy_chk[540] = + { 0, + 137, 1, 1, 1, 1, 13, 136, 135, 13, 1, + 1, 16, 16, 19, 17, 1, 21, 1, 117, 21, + 1, 17, 1, 20, 115, 27, 20, 18, 27, 19, + 17, 1, 29, 1, 20, 29, 1, 17, 1, 2, + 2, 2, 2, 18, 30, 30, 33, 2, 2, 33, + 34, 34, 68, 2, 35, 2, 23, 35, 2, 23, + 2, 24, 37, 37, 24, 38, 15, 39, 23, 2, + 39, 2, 40, 24, 2, 14, 2, 3, 3, 3, + 3, 38, 3, 41, 23, 31, 31, 31, 40, 24, + 58, 3, 42, 31, 43, 42, 3, 43, 44, 41, + + 11, 44, 45, 42, 0, 45, 58, 3, 46, 31, + 0, 46, 3, 47, 49, 51, 47, 49, 51, 53, + 53, 3, 4, 4, 4, 4, 47, 4, 60, 52, + 52, 52, 56, 46, 61, 56, 4, 52, 57, 57, + 69, 4, 47, 54, 60, 54, 0, 54, 54, 46, + 61, 59, 4, 52, 59, 62, 69, 4, 62, 63, + 66, 66, 63, 67, 67, 70, 4, 5, 5, 5, + 5, 63, 5, 0, 64, 5, 5, 64, 65, 65, + 71, 70, 74, 71, 75, 65, 0, 63, 72, 64, + 71, 72, 76, 78, 77, 76, 78, 77, 74, 79, + + 75, 65, 79, 80, 78, 64, 80, 72, 87, 79, + 88, 5, 6, 6, 6, 6, 81, 6, 77, 81, + 6, 6, 83, 72, 87, 83, 88, 81, 89, 90, + 91, 92, 0, 85, 77, 93, 85, 83, 94, 95, + 118, 0, 0, 118, 89, 90, 91, 92, 85, 0, + 118, 93, 96, 83, 94, 95, 6, 7, 7, 7, + 7, 7, 7, 7, 85, 7, 7, 97, 96, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 97, 112, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 113, + + 112, 7, 8, 8, 8, 8, 8, 8, 8, 114, + 8, 8, 116, 119, 120, 113, 119, 121, 122, 123, + 121, 122, 123, 124, 125, 114, 124, 125, 116, 126, + 120, 127, 126, 0, 127, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8, 9, 9, 9, + 9, 0, 9, 0, 0, 9, 9, 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, 0, + 0, 9, 10, 10, 10, 10, 0, 10, 0, 0, + + 10, 10, 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, 0, 0, 10, 82, 0, 0, + 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 82, 82, 0, 0, 0, 0, 82, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 82, + 0, 0, 0, 0, 82, 129, 129, 129, 130, 130, + 130, 131, 131, 131, 132, 132, 132, 133, 133, 133, + 134, 134, 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, 128, 128, 128, 128, 128, 128 + } ; + +/* Table of booleans, true if rule could match eol. */ +static const flex_int32_t yy_rule_can_match_eol[25] = + { 0, +1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, }; + +/* 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 +/* + 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 +#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 +#define YY_NO_INPUT 1 + +#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; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int 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 = '*'; \ + yy_size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (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 ); + } + + { + + 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] != 493 ); + +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; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + yy_size_t yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + } + +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 +{ return NEWLINE; } + YY_BREAK +case 2: +YY_RULE_SETUP +{ return DL; } + YY_BREAK +case 3: +YY_RULE_SETUP +{ return NEQ; } + YY_BREAK +case 4: +YY_RULE_SETUP +{ return NUM; } + YY_BREAK +case 5: +YY_RULE_SETUP +{ + 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 +{ BEGIN(LABELM); return LABELS; } + YY_BREAK +case 7: +YY_RULE_SETUP +{ + return LABELSEMBEDDED; } + YY_BREAK +case 8: +YY_RULE_SETUP +{ + yyextra->mode=0; return FORMATFULLMATRIX; } + YY_BREAK +case 9: +YY_RULE_SETUP +{ + yyextra->mode=1; return FORMATEDGELIST1; } + YY_BREAK +case 10: +YY_RULE_SETUP +{ + yyextra->mode=2; return FORMATNODELIST1; } + YY_BREAK +case 11: +YY_RULE_SETUP +{ /* eaten up */ } + YY_BREAK +case 12: +YY_RULE_SETUP +{ return LABEL; } + YY_BREAK +case 13: +YY_RULE_SETUP +{ return DIGIT; } + YY_BREAK +case 14: +YY_RULE_SETUP +{ return LABEL; } + YY_BREAK +case 15: +YY_RULE_SETUP +{ } + YY_BREAK +case 16: +YY_RULE_SETUP +{ return NUM; } + YY_BREAK +case 17: +YY_RULE_SETUP +{ return LABEL; } + YY_BREAK +case 18: +YY_RULE_SETUP +{ } + YY_BREAK +case 19: +YY_RULE_SETUP +{ return NUM; } + YY_BREAK +case 20: +YY_RULE_SETUP +{ return LABEL; } + YY_BREAK +case 21: +YY_RULE_SETUP +{ } + YY_BREAK +case 22: +YY_RULE_SETUP +{ /* 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): +{ + if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=1; + BEGIN(INITIAL); + return EOFF; + } + } + YY_BREAK +case 23: +YY_RULE_SETUP +{ return 0; } + YY_BREAK +case 24: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK + + 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 + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + 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. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) 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)] : 46); + 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 = 46; + 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 */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + 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; + + if ( c == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + + 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, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + yy_size_t 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 yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yy_size_t 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" + diff --git a/src/io/parsers/dl-lexer.h b/src/io/parsers/dl-lexer.h new file mode 100644 index 0000000..9d4cfa6 --- /dev/null +++ b/src/io/parsers/dl-lexer.h @@ -0,0 +1,731 @@ +#ifndef igraph_dl_yyHEADER_H +#define igraph_dl_yyHEADER_H 1 +#define igraph_dl_yyIN_HEADER 1 + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#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, yy_size_t 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 ); + + 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 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 + +#undef igraph_dl_yyIN_HEADER +#endif /* igraph_dl_yyHEADER_H */ diff --git a/src/io/parsers/dl-parser.c b/src/io/parsers/dl-parser.c new file mode 100644 index 0000000..34dc3e3 --- /dev/null +++ b/src/io/parsers/dl-parser.c @@ -0,0 +1,2192 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 1 + +/* Substitute the variable and function names. */ +#define yyparse igraph_dl_yyparse +#define yylex igraph_dl_yylex +#define yyerror igraph_dl_yyerror +#define yylval igraph_dl_yylval +#define yychar igraph_dl_yychar +#define yydebug igraph_dl_yydebug +#define yynerrs igraph_dl_yynerrs +#define yylloc igraph_dl_yylloc + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + NUM = 258, + NEWLINE = 259, + DL = 260, + NEQ = 261, + DATA = 262, + LABELS = 263, + LABELSEMBEDDED = 264, + FORMATFULLMATRIX = 265, + FORMATEDGELIST1 = 266, + FORMATNODELIST1 = 267, + DIGIT = 268, + LABEL = 269, + EOFF = 270, + ERROR = 271 + }; +#endif +/* Tokens. */ +#define END 0 +#define NUM 258 +#define NEWLINE 259 +#define DL 260 +#define NEQ 261 +#define DATA 262 +#define LABELS 263 +#define LABELSEMBEDDED 264 +#define FORMATFULLMATRIX 265 +#define FORMATEDGELIST1 266 +#define FORMATNODELIST1 267 +#define DIGIT 268 +#define LABEL 269 +#define EOFF 270 +#define ERROR 271 + + + + +/* Copy the first part of user declarations. */ + + + +/* + 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 "internal/hacks.h" +#include "io/dl-header.h" +#include "io/parsers/dl-parser.h" +#include "io/parsers/dl-lexer.h" +#include "io/parse_utils.h" + +int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, + const char *s); +static igraph_error_t igraph_i_dl_add_str(char *newstr, yy_size_t length, + igraph_i_dl_parsedata_t *context); +static igraph_error_t igraph_i_dl_add_edge(igraph_integer_t from, igraph_integer_t to, + igraph_i_dl_parsedata_t *context); +static igraph_error_t igraph_i_dl_add_edge_w(igraph_integer_t from, igraph_integer_t to, + igraph_real_t weight, + igraph_i_dl_parsedata_t *context); +static igraph_error_t igraph_i_dl_check_vid(igraph_integer_t dl_vid); + +#define scanner context->scanner + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + igraph_integer_t integer; + igraph_real_t real; +} +/* Line 193 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + YYLTYPE yyls; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 4 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 118 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 17 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 37 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 66 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 138 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 271 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 11, 12, 15, 16, 18, 20, 22, + 24, 28, 30, 31, 33, 37, 45, 51, 52, 56, + 57, 61, 62, 65, 67, 69, 73, 74, 78, 80, + 82, 85, 89, 93, 97, 105, 111, 121, 131, 132, + 135, 140, 144, 146, 147, 150, 155, 159, 161, 163, + 167, 170, 178, 184, 194, 204, 205, 208, 212, 214, + 215, 218, 219, 222, 226, 228, 229 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 18, 0, -1, 5, 6, 39, 4, 21, 19, 20, + -1, -1, 19, 23, -1, -1, 15, -1, 22, -1, + 35, -1, 44, -1, 10, 23, 24, -1, 24, -1, + -1, 4, -1, 7, 23, 26, -1, 8, 23, 25, + 23, 7, 23, 26, -1, 9, 23, 7, 23, 29, + -1, -1, 25, 23, 14, -1, -1, 26, 27, 4, + -1, -1, 27, 28, -1, 13, -1, 30, -1, 31, + 4, 33, -1, -1, 31, 23, 32, -1, 14, -1, + 34, -1, 33, 34, -1, 14, 27, 4, -1, 11, + 23, 36, -1, 7, 23, 37, -1, 8, 23, 25, + 23, 7, 23, 37, -1, 9, 23, 7, 23, 40, + -1, 8, 23, 25, 23, 9, 23, 7, 23, 40, + -1, 9, 23, 8, 23, 25, 23, 7, 23, 40, + -1, -1, 37, 38, -1, 39, 39, 42, 4, -1, + 39, 39, 4, -1, 3, -1, -1, 40, 41, -1, + 43, 43, 42, 4, -1, 43, 43, 4, -1, 3, + -1, 14, -1, 12, 23, 45, -1, 7, 46, -1, + 8, 23, 25, 23, 7, 23, 46, -1, 9, 23, + 7, 23, 50, -1, 8, 23, 25, 23, 9, 23, + 7, 23, 50, -1, 9, 23, 8, 23, 25, 23, + 7, 23, 50, -1, -1, 46, 47, -1, 48, 49, + 4, -1, 3, -1, -1, 49, 39, -1, -1, 50, + 51, -1, 52, 53, 4, -1, 43, -1, -1, 53, + 43, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 104, 104, 114, 114, 116, 116, 118, 119, 120, + 123, 123, 125, 125, 127, 128, 129, 132, 133, 139, + 139, 144, 144, 146, 161, 163, 165, 165, 167, 171, + 175, 180, 184, 186, 187, 188, 189, 190, 193, 194, + 197, 202, 209, 217, 218, 221, 223, 227, 235, 254, + 256, 257, 258, 259, 260, 263, 264, 267, 269, 276, + 276, 284, 285, 288, 290, 294, 294 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "$undefined", "\"number\"", + "\"end of line\"", "\"DL\"", "\"n=vertexcount\"", "\"data:\"", + "\"labels:\"", "\"labels embedded:\"", "FORMATFULLMATRIX", + "FORMATEDGELIST1", "FORMATNODELIST1", "\"binary digit\"", "\"label\"", + "EOFF", "ERROR", "$accept", "input", "trail", "eof", "rest", + "formfullmatrix", "newline", "fullmatrix", "labels", "fullmatrixdata", + "zerooneseq", "zeroone", "labeledfullmatrixdata", + "reallabeledfullmatrixdata", "labelseq", "label", "labeledmatrixlines", + "labeledmatrixline", "edgelist1", "edgelist1rest", "edgelist1data", + "edgelist1dataline", "integer", "labelededgelist1data", + "labelededgelist1dataline", "weight", "elabel", "nodelist1", + "nodelist1rest", "nodelist1data", "nodelist1dataline", "from", "tolist", + "labelednodelist1data", "labelednodelist1dataline", "fromelabel", + "labeltolist", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 17, 18, 19, 19, 20, 20, 21, 21, 21, + 22, 22, 23, 23, 24, 24, 24, 25, 25, 26, + 26, 27, 27, 28, 29, 30, 31, 31, 32, 33, + 33, 34, 35, 36, 36, 36, 36, 36, 37, 37, + 38, 38, 39, 40, 40, 41, 41, 42, 43, 44, + 45, 45, 45, 45, 45, 46, 46, 47, 48, 49, + 49, 50, 50, 51, 52, 53, 53 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 7, 0, 2, 0, 1, 1, 1, 1, + 3, 1, 0, 1, 3, 7, 5, 0, 3, 0, + 3, 0, 2, 1, 1, 3, 0, 3, 1, 1, + 2, 3, 3, 3, 7, 5, 9, 9, 0, 2, + 4, 3, 1, 0, 2, 4, 3, 1, 1, 3, + 2, 7, 5, 9, 9, 0, 2, 3, 1, 0, + 2, 0, 2, 3, 1, 0, 2 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 1, 42, 0, 0, 12, 12, + 12, 12, 12, 12, 3, 7, 11, 8, 9, 13, + 19, 17, 0, 0, 0, 0, 5, 14, 12, 12, + 10, 12, 12, 12, 32, 55, 12, 12, 49, 6, + 2, 4, 0, 0, 26, 38, 17, 0, 50, 17, + 0, 20, 23, 22, 12, 18, 16, 24, 12, 33, + 12, 12, 12, 58, 56, 59, 12, 12, 12, 19, + 0, 0, 39, 0, 0, 43, 17, 0, 0, 61, + 17, 15, 21, 25, 29, 28, 27, 0, 12, 12, + 35, 12, 57, 60, 12, 12, 52, 12, 0, 30, + 47, 41, 0, 38, 0, 48, 44, 0, 0, 55, + 0, 64, 62, 65, 0, 31, 40, 34, 12, 0, + 12, 51, 12, 0, 12, 43, 46, 0, 43, 61, + 63, 66, 61, 36, 45, 37, 53, 54 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 2, 26, 40, 14, 15, 20, 16, 28, 27, + 42, 53, 56, 57, 58, 86, 83, 84, 17, 34, + 59, 72, 73, 90, 106, 102, 107, 18, 38, 48, + 64, 65, 77, 96, 112, 113, 123 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -114 +static const yytype_int8 yypact[] = +{ + 8, 38, 11, 43, -114, -114, 44, 57, 46, 46, + 46, 46, 46, 46, -114, -114, -114, -114, -114, -114, + -114, -114, 69, 53, 63, 66, 6, 65, 46, 46, + -114, 46, 46, 46, -114, -114, 46, 46, -114, -114, + -114, -114, 5, 19, -114, -114, -114, 76, 84, -114, + 82, -114, -114, -114, 46, -114, -114, -114, 93, 43, + 46, 46, 46, -114, -114, -114, 46, 46, 46, -114, + 85, 86, -114, 43, 23, -114, -114, 88, 33, -114, + -114, 65, -114, 85, -114, -114, -114, 90, 46, 46, + 87, 46, -114, -114, 46, 46, 87, 46, 25, -114, + -114, -114, 94, -114, 95, -114, -114, 87, 29, -114, + 96, -114, -114, -114, 49, -114, -114, 43, 46, 92, + 46, 84, 46, 2, 46, -114, -114, 100, -114, -114, + -114, -114, -114, 87, -114, 87, 87, 87 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -114, -114, -114, -114, -114, -114, -9, 83, -41, 36, + 26, -114, -114, -114, -114, -114, -114, 24, -114, -114, + 7, -114, 4, -113, -114, -7, -82, -114, -114, 9, + -114, -114, -114, -98, -114, -114, -114 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -22 +static const yytype_int16 yytable[] = +{ + 21, 22, 23, 24, 25, 60, 130, 6, 66, 51, + 19, 4, 133, 1, 111, 135, 105, 41, 52, 43, + 44, 39, 45, 46, 47, 119, 54, 49, 50, 115, + 88, 136, 89, 55, 137, 91, 120, 55, 52, 97, + 94, 131, 95, 55, 3, 69, 5, 55, 7, 71, + 19, 74, 75, 76, 111, 111, 124, 78, 79, 80, + 8, 9, 10, 55, 8, 9, 10, 11, 12, 13, + 31, 32, 33, 35, 36, 37, 29, 87, -21, 103, + 104, 93, 108, 61, 62, 109, 110, 63, 114, 67, + 68, 5, 92, 100, 101, 100, 126, 70, 116, 82, + 85, 105, 118, 122, 134, 81, 30, 99, 98, 125, + 117, 128, 127, 129, 0, 132, 0, 0, 121 +}; + +static const yytype_int16 yycheck[] = +{ + 9, 10, 11, 12, 13, 46, 4, 3, 49, 4, + 4, 0, 125, 5, 96, 128, 14, 26, 13, 28, + 29, 15, 31, 32, 33, 107, 7, 36, 37, 4, + 7, 129, 9, 14, 132, 76, 7, 14, 13, 80, + 7, 123, 9, 14, 6, 54, 3, 14, 4, 58, + 4, 60, 61, 62, 136, 137, 7, 66, 67, 68, + 7, 8, 9, 14, 7, 8, 9, 10, 11, 12, + 7, 8, 9, 7, 8, 9, 7, 73, 13, 88, + 89, 77, 91, 7, 8, 94, 95, 3, 97, 7, + 8, 3, 4, 3, 4, 3, 4, 4, 4, 14, + 14, 14, 7, 7, 4, 69, 23, 83, 82, 118, + 103, 120, 119, 122, -1, 124, -1, -1, 109 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 5, 18, 6, 0, 3, 39, 4, 7, 8, + 9, 10, 11, 12, 21, 22, 24, 35, 44, 4, + 23, 23, 23, 23, 23, 23, 19, 26, 25, 7, + 24, 7, 8, 9, 36, 7, 8, 9, 45, 15, + 20, 23, 27, 23, 23, 23, 23, 23, 46, 23, + 23, 4, 13, 28, 7, 14, 29, 30, 31, 37, + 25, 7, 8, 3, 47, 48, 25, 7, 8, 23, + 4, 23, 38, 39, 23, 23, 23, 49, 23, 23, + 23, 26, 14, 33, 34, 14, 32, 39, 7, 9, + 40, 25, 4, 39, 7, 9, 50, 25, 27, 34, + 3, 4, 42, 23, 23, 14, 41, 43, 23, 23, + 23, 43, 51, 52, 23, 4, 4, 37, 7, 43, + 7, 46, 7, 53, 7, 23, 4, 42, 23, 23, + 4, 43, 23, 40, 4, 40, 50, 50 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, &yylloc, scanner) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_dl_parsedata_t* context; +#endif +{ + if (!yyvaluep) + return; + YYUSE (yylocationp); + YYUSE (context); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_dl_parsedata_t* context) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_dl_parsedata_t* context; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_dl_parsedata_t* context) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule, context) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; + igraph_i_dl_parsedata_t* context; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule, context); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_dl_parsedata_t* context) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; + igraph_i_dl_parsedata_t* context; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (igraph_i_dl_parsedata_t* context); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (igraph_i_dl_parsedata_t* context) +#else +int +yyparse (context) + igraph_i_dl_parsedata_t* context; +#endif +#endif +{ + /* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; +/* Location data for the look-ahead symbol. */ +YYLTYPE yylloc; + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[2]; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + yylsp = yyls; +#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + /* Initialize the default location before parsing starts. */ + yylloc.first_line = yylloc.last_line = 1; + yylloc.first_column = yylloc.last_column = 0; +#endif + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + YYSTACK_RELOCATE (yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: + + { + context->n=(yyvsp[(3) - (7)].integer); + if (context->n < 0) { + IGRAPH_YY_ERRORF("Invalid vertex count in DL file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->n); + } + if (context->n > IGRAPH_DL_MAX_VERTEX_COUNT) { + IGRAPH_YY_ERRORF("Vertex count too large in DL file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->n); + } +;} + break; + + case 7: + + { context->type=IGRAPH_DL_MATRIX; ;} + break; + + case 8: + + { context->type=IGRAPH_DL_EDGELIST1; ;} + break; + + case 9: + + { context->type=IGRAPH_DL_NODELIST1; ;} + break; + + case 10: + + {;} + break; + + case 11: + + {;} + break; + + case 14: + + { ;} + break; + + case 15: + + { ;} + break; + + case 16: + + { ;} + break; + + case 17: + + {;} + break; + + case 18: + + { + IGRAPH_YY_CHECK(igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context)); ;} + break; + + case 19: + + {;} + break; + + case 20: + + { + context->from += 1; + context->to = 0; + ;} + break; + + case 22: + + { ;} + break; + + case 23: + + { + /* TODO: What if the digit is neither 0 or 1? Are multigraphs allowed? */ + char c = igraph_dl_yyget_text(scanner)[0]; + if (c == '1') { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, + context->from)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, + context->to)); + } else if (c != '0') { + IGRAPH_YY_ERRORF("Unexpected digit '%c' in adjacency matrix in DL file.", + IGRAPH_EINVAL, c); + } + context->to += 1; +;} + break; + + case 24: + + {;} + break; + + case 25: + + {;} + break; + + case 28: + + { IGRAPH_YY_CHECK(igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context)); ;} + break; + + case 29: + + { + context->from += 1; + context->to = 0; + ;} + break; + + case 30: + + { + context->from += 1; + context->to = 0; + ;} + break; + + case 31: + + { ;} + break; + + case 32: + + {;} + break; + + case 33: + + {;} + break; + + case 34: + + {;} + break; + + case 35: + + {;} + break; + + case 36: + + {;} + break; + + case 37: + + {;} + break; + + case 38: + + {;} + break; + + case 39: + + {;} + break; + + case 40: + + { + igraph_integer_t from = (yyvsp[(1) - (4)].integer), to = (yyvsp[(2) - (4)].integer); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(from)); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); + IGRAPH_YY_CHECK(igraph_i_dl_add_edge_w(from-1, to-1, (yyvsp[(3) - (4)].real), context)); ;} + break; + + case 41: + + { + igraph_integer_t from = (yyvsp[(1) - (3)].integer), to = (yyvsp[(2) - (3)].integer); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(from)); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); + IGRAPH_YY_CHECK(igraph_i_dl_add_edge(from-1, to-1, context)); +;} + break; + + case 42: + + { + igraph_integer_t val; + IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + &val)); + (yyval.integer)=val; +;} + break; + + case 43: + + {;} + break; + + case 44: + + {;} + break; + + case 45: + + { + IGRAPH_YY_CHECK(igraph_i_dl_add_edge_w((yyvsp[(1) - (4)].integer), (yyvsp[(2) - (4)].integer), (yyvsp[(3) - (4)].real), context)); ;} + break; + + case 46: + + { + IGRAPH_YY_CHECK(igraph_i_dl_add_edge((yyvsp[(1) - (3)].integer), (yyvsp[(2) - (3)].integer), context)); + ;} + break; + + case 47: + + { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + &val)); + (yyval.real)=val; +;} + break; + + case 48: + + { + igraph_integer_t trie_id; + + /* Copy label list to trie, if needed */ + if (igraph_strvector_size(&context->labels) != 0) { + igraph_integer_t i, id, n=igraph_strvector_size(&context->labels); + for (i=0; itrie, igraph_strvector_get(&context->labels, i), &id)); + } + igraph_strvector_clear(&context->labels); + } + IGRAPH_YY_CHECK(igraph_trie_get_len(&context->trie, igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), &trie_id)); + IGRAPH_ASSERT(0 <= trie_id && trie_id < IGRAPH_DL_MAX_VERTEX_COUNT); + (yyval.integer) = trie_id; + ;} + break; + + case 49: + + {;} + break; + + case 50: + + {;} + break; + + case 51: + + {;} + break; + + case 52: + + {;} + break; + + case 53: + + {;} + break; + + case 54: + + {;} + break; + + case 55: + + {;} + break; + + case 56: + + {;} + break; + + case 57: + + {;} + break; + + case 58: + + { + IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + &context->from)); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(context->from)); +;} + break; + + case 59: + + {;} + break; + + case 60: + + { + igraph_integer_t to = (yyvsp[(2) - (2)].integer); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, + context->from-1)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, to-1)); + ;} + break; + + case 61: + + {;} + break; + + case 62: + + {;} + break; + + case 63: + + { ;} + break; + + case 64: + + { + context->from=(yyvsp[(1) - (1)].integer); + ;} + break; + + case 66: + + { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, + context->from)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, (yyvsp[(2) - (2)].integer))); + ;} + break; + + +/* Line 1267 of yacc.c. */ + + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (&yylloc, context, yymsg); + } + else + { + yyerror (&yylloc, context, YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + yyerror_range[0] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[0] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[0] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + yyerror_range[1] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the look-ahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + + + +int igraph_dl_yyerror(YYLTYPE* locp, + igraph_i_dl_parsedata_t* context, + const char *s) { + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, + "Parse error in DL file, line %i (%s)", + locp->first_line, s); + return 0; +} + +static igraph_error_t igraph_i_dl_add_str(char *newstr, yy_size_t length, + igraph_i_dl_parsedata_t *context) { + IGRAPH_CHECK(igraph_strvector_push_back_len(&context->labels, newstr, length)); + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_dl_add_edge(igraph_integer_t from, igraph_integer_t to, + igraph_i_dl_parsedata_t *context) { + //IGRAPH_CHECK(igraph_i_dl_check_vid(from+1)); + //IGRAPH_CHECK(igraph_i_dl_check_vid(to+1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&context->edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&context->edges, to)); + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_dl_add_edge_w(igraph_integer_t from, igraph_integer_t to, + igraph_real_t weight, + igraph_i_dl_parsedata_t *context) { + igraph_integer_t n=igraph_vector_size(&context->weights); + igraph_integer_t n2=igraph_vector_int_size(&context->edges)/2; + if (n != n2) { + IGRAPH_CHECK(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 IGRAPH_SUCCESS; +} + +/* Raise an error if the vertex index is invalid in the DL file. + * DL files use 1-based vertex indices. */ +static igraph_error_t igraph_i_dl_check_vid(igraph_integer_t dl_vid) { + if (dl_vid < 1) { + IGRAPH_ERRORF("Invalid vertex index in DL file: %" IGRAPH_PRId ".", + IGRAPH_EINVAL, dl_vid); + } + if (dl_vid > IGRAPH_DL_MAX_VERTEX_COUNT) { + IGRAPH_ERRORF("Vertex index too large in DL file: %" IGRAPH_PRId ".", + IGRAPH_EINVAL, dl_vid); + } + return IGRAPH_SUCCESS; +} + diff --git a/src/io/parsers/dl-parser.h b/src/io/parsers/dl-parser.h new file mode 100644 index 0000000..08765e9 --- /dev/null +++ b/src/io/parsers/dl-parser.h @@ -0,0 +1,109 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + NUM = 258, + NEWLINE = 259, + DL = 260, + NEQ = 261, + DATA = 262, + LABELS = 263, + LABELSEMBEDDED = 264, + FORMATFULLMATRIX = 265, + FORMATEDGELIST1 = 266, + FORMATNODELIST1 = 267, + DIGIT = 268, + LABEL = 269, + EOFF = 270, + ERROR = 271 + }; +#endif +/* Tokens. */ +#define END 0 +#define NUM 258 +#define NEWLINE 259 +#define DL 260 +#define NEQ 261 +#define DATA 262 +#define LABELS 263 +#define LABELSEMBEDDED 264 +#define FORMATFULLMATRIX 265 +#define FORMATEDGELIST1 266 +#define FORMATNODELIST1 267 +#define DIGIT 268 +#define LABEL 269 +#define EOFF 270 +#define ERROR 271 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + igraph_integer_t integer; + igraph_real_t real; +} +/* Line 1529 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + diff --git a/src/io/parsers/gml-lexer.c b/src/io/parsers/gml-lexer.c new file mode 100644 index 0000000..1fc1007 --- /dev/null +++ b/src/io/parsers/gml-lexer.c @@ -0,0 +1,2346 @@ + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + yy_size_t yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + #define YY_LINENO_REWIND_TO(dst) \ + do {\ + const char *p;\ + for ( p = yy_cp-1; p >= (dst); --p)\ + if ( *p == '\n' )\ + --yylineno;\ + }while(0) + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via 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, yy_size_t 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 yynoreturn 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 = (yy_size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 11 +#define YY_END_OF_BUFFER 12 +/* 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[43] = + { 0, + 0, 0, 0, 0, 12, 10, 8, 9, 9, 10, + 10, 4, 5, 6, 7, 1, 10, 5, 5, 8, + 9, 0, 2, 4, 0, 0, 5, 1, 0, 0, + 5, 5, 4, 0, 4, 0, 0, 3, 3, 3, + 3, 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, 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, 12, 12, 12, 13, 14, + 12, 12, 15, 12, 12, 12, 12, 16, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 17, 1, 18, 1, 12, 1, 19, 12, 12, 12, + + 13, 20, 12, 12, 21, 12, 12, 12, 12, 22, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 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[24] = + { 0, + 1, 1, 2, 2, 1, 1, 1, 1, 1, 3, + 3, 3, 3, 3, 3, 3, 1, 1, 3, 3, + 3, 3, 4 + } ; + +static const flex_int16_t yy_base[46] = + { 0, + 0, 78, 17, 77, 82, 85, 79, 23, 25, 74, + 63, 21, 0, 85, 85, 0, 30, 19, 25, 70, + 39, 66, 85, 40, 59, 47, 0, 0, 40, 47, + 45, 45, 50, 38, 37, 50, 52, 0, 0, 85, + 85, 85, 74, 34, 77 + } ; + +static const flex_int16_t yy_def[46] = + { 0, + 42, 1, 1, 3, 42, 42, 42, 42, 42, 43, + 42, 42, 44, 42, 42, 45, 42, 44, 44, 42, + 42, 43, 42, 42, 42, 42, 44, 45, 42, 42, + 44, 44, 42, 42, 42, 42, 42, 44, 44, 42, + 42, 0, 42, 42, 42 + } ; + +static const flex_int16_t yy_nxt[109] = + { 0, + 6, 7, 8, 9, 10, 6, 11, 11, 6, 12, + 13, 13, 13, 13, 13, 13, 14, 15, 13, 13, + 13, 13, 6, 17, 17, 21, 21, 21, 21, 25, + 24, 18, 19, 26, 31, 32, 27, 18, 19, 24, + 31, 21, 21, 32, 29, 30, 35, 35, 25, 24, + 29, 30, 26, 34, 34, 36, 35, 37, 38, 33, + 39, 36, 26, 40, 38, 37, 39, 41, 33, 40, + 23, 20, 24, 41, 22, 22, 22, 28, 23, 28, + 20, 42, 16, 16, 5, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + + 42, 42, 42, 42, 42, 42, 42, 42 + } ; + +static const flex_int16_t yy_chk[109] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 3, 8, 8, 9, 9, 12, + 12, 3, 3, 12, 18, 19, 44, 3, 3, 17, + 18, 21, 21, 19, 17, 17, 35, 34, 24, 24, + 17, 17, 24, 26, 26, 29, 26, 30, 31, 33, + 32, 29, 33, 36, 31, 30, 32, 37, 25, 36, + 22, 20, 11, 37, 43, 43, 43, 45, 10, 45, + 7, 5, 4, 2, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + + 42, 42, 42, 42, 42, 42, 42, 42 + } ; + +/* Table of booleans, true if rule could match eol. */ +static const flex_int32_t yy_rule_can_match_eol[12] = + { 0, +0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, }; + +/* 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 +/* + 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, 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 + +#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 +#define YY_NO_INPUT 1 +/* Use to parse inf/nan as number only when expecting a value, i.e. after a keyword. + * Otherwise they are parsed as a keyword. */ + +#define INITIAL 0 +#define VALUE 1 + +#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; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int 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 = '*'; \ + yy_size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (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 ); + } + + { + + 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 >= 43 ) + 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] != 85 ); + +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; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + yy_size_t yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + } + +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 +{ /* comments ignored */ } + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +{ BEGIN(INITIAL); return STRING; } + YY_BREAK +case 3: +YY_RULE_SETUP +{ BEGIN(INITIAL); return NUM; } + YY_BREAK +case 4: +YY_RULE_SETUP +{ BEGIN(INITIAL); return NUM; } + YY_BREAK +case 5: +YY_RULE_SETUP +{ BEGIN(VALUE); return KEYWORD; } + YY_BREAK +case 6: +YY_RULE_SETUP +{ + BEGIN(INITIAL); + yyextra->depth++; + if (yyextra->depth >= 32) { + return ERROR; + } else { + return LISTOPEN; + } + } + YY_BREAK +case 7: +YY_RULE_SETUP +{ + yyextra->depth--; + return LISTCLOSE; + } + YY_BREAK +case 8: +YY_RULE_SETUP +{ /* other whitespace ignored */ } + YY_BREAK +case 9: +/* rule 9 can match eol */ +YY_RULE_SETUP +{ yy_set_bol(true); /* set "beginning of line" even after \r */ } + YY_BREAK +case 10: +YY_RULE_SETUP +{ return ERROR; } + YY_BREAK +case 11: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(VALUE): + yyterminate(); + + 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 + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + 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. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) 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)] : 23); + 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 >= 43 ) + 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 = 23; + 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 >= 43 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 42); + + (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 */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + 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'); + if ( YY_CURRENT_BUFFER_LVALUE->yy_at_bol ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + + 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, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + yy_size_t 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 yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yy_size_t 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" + diff --git a/src/io/parsers/gml-lexer.h b/src/io/parsers/gml-lexer.h new file mode 100644 index 0000000..f3267a5 --- /dev/null +++ b/src/io/parsers/gml-lexer.h @@ -0,0 +1,730 @@ +#ifndef igraph_gml_yyHEADER_H +#define igraph_gml_yyHEADER_H 1 +#define igraph_gml_yyIN_HEADER 1 + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#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, yy_size_t 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 +#define VALUE 1 + +#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 ); + + 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 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 + +#undef igraph_gml_yyIN_HEADER +#endif /* igraph_gml_yyHEADER_H */ diff --git a/src/io/parsers/gml-parser.c b/src/io/parsers/gml-parser.c new file mode 100644 index 0000000..45213a9 --- /dev/null +++ b/src/io/parsers/gml-parser.c @@ -0,0 +1,1859 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 1 + +/* Substitute the variable and function names. */ +#define yyparse igraph_gml_yyparse +#define yylex igraph_gml_yylex +#define yyerror igraph_gml_yyerror +#define yylval igraph_gml_yylval +#define yychar igraph_gml_yychar +#define yydebug igraph_gml_yydebug +#define yynerrs igraph_gml_yynerrs +#define yylloc igraph_gml_yylloc + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + STRING = 258, + NUM = 259, + KEYWORD = 260, + LISTOPEN = 261, + LISTCLOSE = 262, + ERROR = 263 + }; +#endif +/* Tokens. */ +#define END 0 +#define STRING 258 +#define NUM 259 +#define KEYWORD 260 +#define LISTOPEN 261 +#define LISTCLOSE 262 +#define ERROR 263 + + + + +/* Copy the first part of user declarations. */ + + + +/* + 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 "igraph_error.h" +#include "igraph_memory.h" + +#include "io/gml-header.h" +#include "io/gml-tree.h" +#include "io/parsers/gml-parser.h" +#include "io/parsers/gml-lexer.h" +#include "io/parse_utils.h" +#include "internal/hacks.h" /* strcasecmp & strndup */ +#include "math/safe_intop.h" + +#include +#include +#include + +int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, + const char *s); +static igraph_error_t igraph_i_gml_get_keyword(const char *s, size_t len, char **res); +static igraph_error_t igraph_i_gml_get_string(const char *s, size_t len, char **res); +static igraph_error_t igraph_i_gml_make_numeric(const char *name, + int line, + igraph_real_t value, + igraph_gml_tree_t **tree); +static igraph_error_t igraph_i_gml_make_string(const char *name, + int line, + char *value, + igraph_gml_tree_t **tree); +static igraph_error_t igraph_i_gml_make_list(const char *name, + int line, + igraph_gml_tree_t *list, + igraph_gml_tree_t **tree); +static igraph_error_t igraph_i_gml_make_empty(igraph_gml_tree_t **tree); +static igraph_error_t igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2); + +#define scanner context->scanner + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + char *str; + igraph_gml_tree_t *tree; + igraph_real_t real; +} +/* Line 193 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + YYLTYPE yyls; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 6 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 10 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 9 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 7 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 11 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 15 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 263 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 6, 8, 11, 14, 17, 22, + 24, 26 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 10, 0, -1, 11, -1, -1, 12, -1, 11, 12, + -1, 13, 14, -1, 13, 15, -1, 13, 6, 11, + 7, -1, 5, -1, 4, -1, 3, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 122, 122, 124, 125, 126, 128, 130, 132, 136, + 139, 147 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "$undefined", "\"string\"", "\"number\"", + "\"keyword\"", "\"[\"", "\"]\"", "ERROR", "$accept", "input", "list", + "keyvalue", "key", "num", "string", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 9, 10, 11, 11, 11, 12, 12, 12, 13, + 14, 15 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 0, 1, 2, 2, 2, 4, 1, + 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 3, 9, 0, 2, 4, 0, 1, 5, 11, 10, + 3, 6, 7, 0, 8 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 2, 3, 4, 5, 11, 12 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -4 +static const yytype_int8 yypact[] = +{ + 1, -4, 3, 1, -4, -2, -4, -4, -4, -4, + 1, -4, -4, 0, -4 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -4, -4, -1, -3, -4, -4, -4 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 7, 8, 9, 6, 10, 1, 1, 14, 0, 13, + 7 +}; + +static const yytype_int8 yycheck[] = +{ + 3, 3, 4, 0, 6, 5, 5, 7, -1, 10, + 13 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 5, 10, 11, 12, 13, 0, 12, 3, 4, + 6, 14, 15, 11, 7 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, &yylloc, scanner) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_gml_parsedata_t* context; +#endif +{ + if (!yyvaluep) + return; + YYUSE (yylocationp); + YYUSE (context); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_gml_parsedata_t* context) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_gml_parsedata_t* context; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_gml_parsedata_t* context) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule, context) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; + igraph_i_gml_parsedata_t* context; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule, context); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_gml_parsedata_t* context) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; + igraph_i_gml_parsedata_t* context; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + case 11: /* "list" */ + + { igraph_gml_tree_destroy((yyvaluep->tree)); }; + + break; + case 12: /* "keyvalue" */ + + { igraph_gml_tree_destroy((yyvaluep->tree)); }; + + break; + case 13: /* "key" */ + + { free((yyvaluep->str)); }; + + break; + case 15: /* "string" */ + + { free((yyvaluep->str)); }; + + break; + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (igraph_i_gml_parsedata_t* context); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (igraph_i_gml_parsedata_t* context) +#else +int +yyparse (context) + igraph_i_gml_parsedata_t* context; +#endif +#endif +{ + /* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; +/* Location data for the look-ahead symbol. */ +YYLTYPE yylloc; + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[2]; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + yylsp = yyls; +#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + /* Initialize the default location before parsing starts. */ + yylloc.first_line = yylloc.last_line = 1; + yylloc.first_column = yylloc.last_column = 0; +#endif + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + YYSTACK_RELOCATE (yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: + + { context->tree=(yyvsp[(1) - (1)].tree); ;} + break; + + case 3: + + { IGRAPH_YY_CHECK(igraph_i_gml_make_empty(&(yyval.tree))); ;} + break; + + case 4: + + { (yyval.tree)=(yyvsp[(1) - (1)].tree); ;} + break; + + case 5: + + { IGRAPH_YY_CHECK(igraph_i_gml_merge((yyvsp[(1) - (2)].tree), (yyvsp[(2) - (2)].tree))); (yyval.tree) = (yyvsp[(1) - (2)].tree); ;} + break; + + case 6: + + { IGRAPH_YY_CHECK(igraph_i_gml_make_numeric((yyvsp[(1) - (2)].str), (yylsp[(1) - (2)]).first_line, (yyvsp[(2) - (2)].real), &(yyval.tree))); ;} + break; + + case 7: + + { IGRAPH_YY_CHECK(igraph_i_gml_make_string((yyvsp[(1) - (2)].str), (yylsp[(1) - (2)]).first_line, (yyvsp[(2) - (2)].str), &(yyval.tree))); ;} + break; + + case 8: + + { IGRAPH_YY_CHECK(igraph_i_gml_make_list((yyvsp[(1) - (4)].str), (yylsp[(1) - (4)]).first_line, (yyvsp[(3) - (4)].tree), &(yyval.tree))); ;} + break; + + case 9: + + { IGRAPH_YY_CHECK(igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &(yyval.str))); ;} + break; + + case 10: + + { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &val)); + (yyval.real)=val; +;} + break; + + case 11: + + { IGRAPH_YY_CHECK(igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &(yyval.str))); ;} + break; + + +/* Line 1267 of yacc.c. */ + + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (&yylloc, context, yymsg); + } + else + { + yyerror (&yylloc, context, YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + yyerror_range[0] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[0] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[0] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + yyerror_range[1] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the look-ahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + + + +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; +} + +static igraph_error_t igraph_i_gml_get_keyword(const char *s, size_t len, char **res) { + *res = strndup(s, len); + if (! *res) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_gml_get_string(const char *s, size_t len, char **res) { + *res = strndup(s+1, len-2); + if (! *res) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_gml_make_numeric(const char *name, + int line, + igraph_real_t value, + igraph_gml_tree_t **tree) { + + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + if (!t) { + IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, t); + + /* The GML spec only requires support for 32-bit signed integers, + * but igraph tries to support the same range as igraph_integer_t, + * so that it can read/write all graphs it can represent. + * We treat anything out of that range as real. These values end + * up as igraph_real_t anyway, as igraph does not currently support + * integer-typed attributes. */ + igraph_real_t trunc_value = trunc(value); + if (value == trunc_value && igraph_i_is_real_representable_as_integer(trunc_value)) { + IGRAPH_CHECK(igraph_gml_tree_init_integer(t, name, line, value)); + } else { + IGRAPH_CHECK(igraph_gml_tree_init_real(t, name, line, value)); + } + + *tree = t; + IGRAPH_FINALLY_CLEAN(1); /* t */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_gml_make_string(const char *name, + int line, + char *value, + igraph_gml_tree_t **tree) { + + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + if (!t) { + IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, t); + + /* 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 */ + IGRAPH_FINALLY(igraph_free, value); + IGRAPH_CHECK(igraph_gml_tree_init_string(t, name, line, value)); + + IGRAPH_FINALLY_CLEAN(1); /* value */ + + *tree = t; + IGRAPH_FINALLY_CLEAN(1); /* t */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_gml_make_list(const char *name, + int line, + igraph_gml_tree_t *list, + igraph_gml_tree_t **tree) { + + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + if (!t) { + IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, t); + + IGRAPH_CHECK(igraph_gml_tree_init_tree(t, name, line, list)); + + *tree = t; + IGRAPH_FINALLY_CLEAN(1); /* t */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_gml_make_empty(igraph_gml_tree_t **tree) { + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + if (!t) { + IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, t); + + IGRAPH_CHECK(igraph_gml_tree_init_empty(t)); + + *tree = t; + IGRAPH_FINALLY_CLEAN(1); /* t */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { + + IGRAPH_CHECK(igraph_gml_tree_mergedest(t1, t2)); + IGRAPH_FREE(t2); + + return IGRAPH_SUCCESS; +} + diff --git a/src/io/parsers/gml-parser.h b/src/io/parsers/gml-parser.h new file mode 100644 index 0000000..7b24236 --- /dev/null +++ b/src/io/parsers/gml-parser.h @@ -0,0 +1,94 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + STRING = 258, + NUM = 259, + KEYWORD = 260, + LISTOPEN = 261, + LISTCLOSE = 262, + ERROR = 263 + }; +#endif +/* Tokens. */ +#define END 0 +#define STRING 258 +#define NUM 259 +#define KEYWORD 260 +#define LISTOPEN 261 +#define LISTCLOSE 262 +#define ERROR 263 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + char *str; + igraph_gml_tree_t *tree; + igraph_real_t real; +} +/* Line 1529 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + diff --git a/src/io/parsers/lgl-lexer.c b/src/io/parsers/lgl-lexer.c new file mode 100644 index 0000000..07747e7 --- /dev/null +++ b/src/io/parsers/lgl-lexer.c @@ -0,0 +1,2284 @@ + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + yy_size_t yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + #define YY_LINENO_REWIND_TO(dst) \ + do {\ + const char *p;\ + for ( p = yy_cp-1; p >= (dst); --p)\ + if ( *p == '\n' )\ + --yylineno;\ + }while(0) + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via 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, yy_size_t 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 yynoreturn 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 = (yy_size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 6 +#define YY_END_OF_BUFFER 7 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[16] = + { 0, + 0, 0, 0, 0, 7, 5, 1, 4, 4, 3, + 2, 1, 4, 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, 5, 5, 6, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 1, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5 + } ; + +static const YY_CHAR yy_meta[7] = + { 0, + 1, 2, 3, 4, 5, 1 + } ; + +static const flex_int16_t yy_base[20] = + { 0, + 0, 0, 0, 0, 11, 12, 0, 0, 0, 0, + 12, 0, 12, 0, 12, 8, 5, 5, 2 + } ; + +static const flex_int16_t yy_def[20] = + { 0, + 15, 1, 1, 1, 15, 15, 16, 17, 18, 19, + 15, 16, 15, 19, 0, 15, 15, 15, 15 + } ; + +static const flex_int16_t yy_nxt[19] = + { 0, + 6, 7, 8, 9, 10, 11, 14, 13, 13, 12, + 15, 5, 15, 15, 15, 15, 15, 15 + } ; + +static const flex_int16_t yy_chk[19] = + { 0, + 1, 1, 1, 1, 1, 1, 19, 18, 17, 16, + 5, 15, 15, 15, 15, 15, 15, 15 + } ; + +/* Table of booleans, true if rule could match eol. */ +static const flex_int32_t yy_rule_can_match_eol[7] = + { 0, +0, 0, 0, 1, 0, 0, }; + +/* 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 +/* + 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 + +#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 +#define YY_NO_INPUT 1 +/* Anything except non-printable (00-1F), space (20), del (7F) and # */ + +#define INITIAL 0 +#define LINE 1 + +#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; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int 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 = '*'; \ + yy_size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (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 ); + } + + { + + /* ------------------------------------------------whitespace------*/ + + 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 >= 16 ) + 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; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + yy_size_t yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + } + +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 +{ /* skip space */ } + YY_BREAK +/* --------------------------------------------------hashmark------*/ +case 2: +YY_RULE_SETUP +{ BEGIN(LINE); return HASH; } + YY_BREAK +/* ----------------------------------------------alphanumeric------*/ +case 3: +YY_RULE_SETUP +{ BEGIN(LINE); return ALNUM; } + YY_BREAK +/* ---------------------------------------------------newline------*/ +case 4: +/* rule 4 can match eol */ +YY_RULE_SETUP +case YY_STATE_EOF(LINE): +{ BEGIN(INITIAL); return NEWLINE; } + YY_BREAK +/* ---------------------------------------------anything else------*/ +case 5: +YY_RULE_SETUP +{ return ERROR; } + YY_BREAK +case 6: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +case YY_STATE_EOF(INITIAL): + yyterminate(); + + 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 + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + 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. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) 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 >= 16 ) + 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 >= 16 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 15); + + (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 */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + 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; + + if ( c == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + + 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, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + yy_size_t 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 yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yy_size_t 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" + diff --git a/src/io/parsers/lgl-lexer.h b/src/io/parsers/lgl-lexer.h new file mode 100644 index 0000000..5c8bf40 --- /dev/null +++ b/src/io/parsers/lgl-lexer.h @@ -0,0 +1,730 @@ +#ifndef igraph_lgl_yyHEADER_H +#define igraph_lgl_yyHEADER_H 1 +#define igraph_lgl_yyIN_HEADER 1 + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#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, yy_size_t 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 +#define LINE 1 + +#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 ); + + 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 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 + +#undef igraph_lgl_yyIN_HEADER +#endif /* igraph_lgl_yyHEADER_H */ diff --git a/src/io/parsers/lgl-parser.c b/src/io/parsers/lgl-parser.c new file mode 100644 index 0000000..54f0717 --- /dev/null +++ b/src/io/parsers/lgl-parser.c @@ -0,0 +1,1691 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 1 + +/* Substitute the variable and function names. */ +#define yyparse igraph_lgl_yyparse +#define yylex igraph_lgl_yylex +#define yyerror igraph_lgl_yyerror +#define yylval igraph_lgl_yylval +#define yychar igraph_lgl_yychar +#define yydebug igraph_lgl_yydebug +#define yynerrs igraph_lgl_yynerrs +#define yylloc igraph_lgl_yylloc + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + ALNUM = 258, + NEWLINE = 259, + HASH = 260, + ERROR = 261 + }; +#endif +/* Tokens. */ +#define END 0 +#define ALNUM 258 +#define NEWLINE 259 +#define HASH 260 +#define ERROR 261 + + + + +/* Copy the first part of user declarations. */ + + + +/* + 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 "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" + +#include "io/lgl-header.h" +#include "io/parsers/lgl-parser.h" +#include "io/parsers/lgl-lexer.h" +#include "io/parse_utils.h" +#include "internal/hacks.h" + +#include +#include + +int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, + const char *s); + +#define scanner context->scanner + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + igraph_integer_t edgenum; + igraph_real_t weightnum; +} +/* Line 193 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + YYLTYPE yyls; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 10 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 7 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 8 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 12 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 17 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 261 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 4, 7, 10, 13, 17, 18, 21, + 24, 28, 30 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 8, 0, -1, -1, 8, 4, -1, 8, 9, -1, + 10, 11, -1, 5, 13, 4, -1, -1, 11, 12, + -1, 13, 4, -1, 13, 14, 4, -1, 3, -1, + 3, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 92, 92, 93, 94, 97, 99, 101, 101, 103, + 108, 117, 127 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "$undefined", "\"alphanumeric\"", + "\"end of line\"", "\"#\"", "ERROR", "$accept", "input", "vertex", + "vertexdef", "edges", "edge", "edgeid", "weight", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 7, 8, 8, 8, 9, 10, 11, 11, 12, + 12, 13, 14 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 2, 2, 2, 3, 0, 2, 2, + 3, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 2, 0, 1, 3, 0, 4, 7, 11, 0, 5, + 6, 8, 0, 12, 9, 0, 10 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 5, 6, 9, 11, 8, 15 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -3 +static const yytype_int8 yypact[] = +{ + -3, 0, -3, -3, 3, -3, -3, -3, -1, 3, + -3, -3, -2, -3, -3, 4, -3 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -3, -3, -3, -3, -3, -3, 1, -3 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 2, 13, 14, 10, 3, 4, 7, 0, 16, 0, + 12 +}; + +static const yytype_int8 yycheck[] = +{ + 0, 3, 4, 4, 4, 5, 3, -1, 4, -1, + 9 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 8, 0, 4, 5, 9, 10, 3, 13, 11, + 4, 12, 13, 3, 4, 14, 4 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, &yylloc, scanner) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_lgl_parsedata_t* context; +#endif +{ + if (!yyvaluep) + return; + YYUSE (yylocationp); + YYUSE (context); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_lgl_parsedata_t* context) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_lgl_parsedata_t* context; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_lgl_parsedata_t* context) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule, context) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; + igraph_i_lgl_parsedata_t* context; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule, context); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_lgl_parsedata_t* context) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; + igraph_i_lgl_parsedata_t* context; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (igraph_i_lgl_parsedata_t* context); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (igraph_i_lgl_parsedata_t* context) +#else +int +yyparse (context) + igraph_i_lgl_parsedata_t* context; +#endif +#endif +{ + /* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; +/* Location data for the look-ahead symbol. */ +YYLTYPE yylloc; + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[2]; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + yylsp = yyls; +#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + /* Initialize the default location before parsing starts. */ + yylloc.first_line = yylloc.last_line = 1; + yylloc.first_column = yylloc.last_column = 0; +#endif + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + YYSTACK_RELOCATE (yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 6: + + { context->actvertex=(yyvsp[(2) - (3)].edgenum); ;} + break; + + case 9: + + { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actvertex)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(1) - (2)].edgenum))); + IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, 0)); + ;} + break; + + case 10: + + { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actvertex)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(1) - (3)].edgenum))); + IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, (yyvsp[(2) - (3)].weightnum))); + context->has_weights = 1; + ;} + break; + + case 11: + + { + igraph_integer_t trie_id; + IGRAPH_YY_CHECK(igraph_trie_get_len(context->trie, + igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner), + &trie_id + )); + (yyval.edgenum) = trie_id; +;} + break; + + case 12: + + { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner), + &val)); + (yyval.weightnum)=val; +;} + break; + + +/* Line 1267 of yacc.c. */ + + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (&yylloc, context, yymsg); + } + else + { + yyerror (&yylloc, context, YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + yyerror_range[0] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[0] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[0] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + yyerror_range[1] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the look-ahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + + + +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; +} + diff --git a/src/io/parsers/lgl-parser.h b/src/io/parsers/lgl-parser.h new file mode 100644 index 0000000..5a1105c --- /dev/null +++ b/src/io/parsers/lgl-parser.h @@ -0,0 +1,89 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + ALNUM = 258, + NEWLINE = 259, + HASH = 260, + ERROR = 261 + }; +#endif +/* Tokens. */ +#define END 0 +#define ALNUM 258 +#define NEWLINE 259 +#define HASH 260 +#define ERROR 261 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + igraph_integer_t edgenum; + igraph_real_t weightnum; +} +/* Line 1529 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + diff --git a/src/io/parsers/ncol-lexer.c b/src/io/parsers/ncol-lexer.c new file mode 100644 index 0000000..da65c13 --- /dev/null +++ b/src/io/parsers/ncol-lexer.c @@ -0,0 +1,2279 @@ + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + yy_size_t yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + #define YY_LINENO_REWIND_TO(dst) \ + do {\ + const char *p;\ + for ( p = yy_cp-1; p >= (dst); --p)\ + if ( *p == '\n' )\ + --yylineno;\ + }while(0) + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via 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, yy_size_t 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 yynoreturn 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 = (yy_size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 5 +#define YY_END_OF_BUFFER 6 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[15] = + { 0, + 0, 0, 0, 0, 6, 4, 1, 3, 3, 2, + 1, 3, 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, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 1, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5 + } ; + +static const YY_CHAR yy_meta[6] = + { 0, + 1, 2, 3, 4, 5 + } ; + +static const flex_int16_t yy_base[19] = + { 0, + 0, 0, 0, 0, 10, 11, 0, 0, 0, 0, + 0, 11, 0, 11, 7, 4, 4, 1 + } ; + +static const flex_int16_t yy_def[19] = + { 0, + 14, 1, 1, 1, 14, 14, 15, 16, 17, 18, + 15, 14, 18, 0, 14, 14, 14, 14 + } ; + +static const flex_int16_t yy_nxt[17] = + { 0, + 6, 7, 8, 9, 10, 13, 12, 12, 11, 14, + 5, 14, 14, 14, 14, 14 + } ; + +static const flex_int16_t yy_chk[17] = + { 0, + 1, 1, 1, 1, 1, 18, 17, 16, 15, 5, + 14, 14, 14, 14, 14, 14 + } ; + +/* Table of booleans, true if rule could match eol. */ +static const flex_int32_t yy_rule_can_match_eol[6] = + { 0, +0, 0, 1, 0, 0, }; + +/* 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 +/* + 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 "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 +#define YY_NO_INPUT 1 +/* Anything except non-printable (00-1F), space (20) and del (7F) */ + +#define INITIAL 0 +#define LINE 1 + +#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; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int 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 = '*'; \ + yy_size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (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 ); + } + + { + + /* ------------------------------------------------whitespace------*/ + + 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 >= 15 ) + 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; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + yy_size_t yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + } + +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 +{ /* skip space */ } + YY_BREAK +/* ----------------------------------------------alphanumeric------*/ +case 2: +YY_RULE_SETUP +{ BEGIN(LINE); return ALNUM; } + YY_BREAK +/* ---------------------------------------------------newline------*/ +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +case YY_STATE_EOF(LINE): +{ BEGIN(INITIAL); return NEWLINE; } + YY_BREAK +/* ---------------------------------------------anything else------*/ +case 4: +YY_RULE_SETUP +{ return ERROR; } + YY_BREAK +case 5: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +case YY_STATE_EOF(INITIAL): + yyterminate(); + + 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 + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + 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. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) 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 >= 15 ) + 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 >= 15 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 14); + + (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 */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + 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; + + if ( c == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + + 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, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + yy_size_t 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 yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yy_size_t 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" + diff --git a/src/io/parsers/ncol-lexer.h b/src/io/parsers/ncol-lexer.h new file mode 100644 index 0000000..fd7630f --- /dev/null +++ b/src/io/parsers/ncol-lexer.h @@ -0,0 +1,730 @@ +#ifndef igraph_ncol_yyHEADER_H +#define igraph_ncol_yyHEADER_H 1 +#define igraph_ncol_yyIN_HEADER 1 + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#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, yy_size_t 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 +#define LINE 1 + +#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 ); + + 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 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 + +#undef igraph_ncol_yyIN_HEADER +#endif /* igraph_ncol_yyHEADER_H */ diff --git a/src/io/parsers/ncol-parser.c b/src/io/parsers/ncol-parser.c new file mode 100644 index 0000000..29a85ed --- /dev/null +++ b/src/io/parsers/ncol-parser.c @@ -0,0 +1,1683 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 1 + +/* Substitute the variable and function names. */ +#define yyparse igraph_ncol_yyparse +#define yylex igraph_ncol_yylex +#define yyerror igraph_ncol_yyerror +#define yylval igraph_ncol_yylval +#define yychar igraph_ncol_yychar +#define yydebug igraph_ncol_yydebug +#define yynerrs igraph_ncol_yynerrs +#define yylloc igraph_ncol_yylloc + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + ALNUM = 258, + NEWLINE = 259, + ERROR = 260 + }; +#endif +/* Tokens. */ +#define END 0 +#define ALNUM 258 +#define NEWLINE 259 +#define ERROR 260 + + + + +/* Copy the first part of user declarations. */ + + + +/* + 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 "igraph_types.h" +#include "igraph_memory.h" +#include "igraph_error.h" + +#include "io/ncol-header.h" +#include "io/parsers/ncol-parser.h" +#include "io/parsers/ncol-lexer.h" +#include "io/parse_utils.h" +#include "internal/hacks.h" + +#include +#include + +int igraph_ncol_yyerror(YYLTYPE* locp, + igraph_i_ncol_parsedata_t *context, + const char *s); + +#define scanner context->scanner + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + igraph_integer_t edgenum; + igraph_real_t weightnum; +} +/* Line 193 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + YYLTYPE yyls; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 7 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 6 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 6 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 9 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 13 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 260 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 4, 7, 10, 13, 17, 20, 22 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 7, 0, -1, -1, 7, 4, -1, 7, 8, -1, + 9, 4, -1, 9, 11, 4, -1, 10, 10, -1, + 3, -1, 3, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 92, 92, 93, 94, 97, 100, 106, 111, 121 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "$undefined", "\"alphanumeric\"", + "\"end of line\"", "ERROR", "$accept", "input", "edge", "endpoints", + "edgeid", "weight", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 6, 7, 7, 7, 8, 8, 9, 10, 11 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 2, 2, 2, 3, 2, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 2, 0, 1, 8, 3, 4, 0, 0, 9, 5, + 0, 7, 6 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 5, 6, 7, 10 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -3 +static const yytype_int8 yypact[] = +{ + -3, 0, -3, -3, -3, -3, -2, 2, -3, -3, + 3, -3, -3 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -3, -3, -3, -3, -1, -3 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 2, 8, 9, 3, 4, 3, 11, 12 +}; + +static const yytype_uint8 yycheck[] = +{ + 0, 3, 4, 3, 4, 3, 7, 4 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 7, 0, 3, 4, 8, 9, 10, 3, 4, + 11, 10, 4 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, &yylloc, scanner) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_ncol_parsedata_t* context; +#endif +{ + if (!yyvaluep) + return; + YYUSE (yylocationp); + YYUSE (context); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_ncol_parsedata_t* context) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_ncol_parsedata_t* context; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_ncol_parsedata_t* context) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule, context) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; + igraph_i_ncol_parsedata_t* context; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule, context); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_ncol_parsedata_t* context) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; + igraph_i_ncol_parsedata_t* context; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (igraph_i_ncol_parsedata_t* context); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (igraph_i_ncol_parsedata_t* context) +#else +int +yyparse (context) + igraph_i_ncol_parsedata_t* context; +#endif +#endif +{ + /* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; +/* Location data for the look-ahead symbol. */ +YYLTYPE yylloc; + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[2]; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + yylsp = yyls; +#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + /* Initialize the default location before parsing starts. */ + yylloc.first_line = yylloc.last_line = 1; + yylloc.first_column = yylloc.last_column = 0; +#endif + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + YYSTACK_RELOCATE (yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 5: + + { + IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, 0.0)); + ;} + break; + + case 6: + + { + IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, (yyvsp[(2) - (3)].weightnum))); + context->has_weights = true; + ;} + break; + + case 7: + + { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(1) - (2)].edgenum))); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(2) - (2)].edgenum))); +;} + break; + + case 8: + + { + igraph_integer_t trie_id; + IGRAPH_YY_CHECK(igraph_trie_get_len(context->trie, + igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner), + &trie_id + )); + (yyval.edgenum) = trie_id; +;} + break; + + case 9: + + { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner), + &val)); + (yyval.weightnum)=val; +;} + break; + + +/* Line 1267 of yacc.c. */ + + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (&yylloc, context, yymsg); + } + else + { + yyerror (&yylloc, context, YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + yyerror_range[0] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[0] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[0] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + yyerror_range[1] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the look-ahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + + + +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; +} + diff --git a/src/io/parsers/ncol-parser.h b/src/io/parsers/ncol-parser.h new file mode 100644 index 0000000..367720e --- /dev/null +++ b/src/io/parsers/ncol-parser.h @@ -0,0 +1,87 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + ALNUM = 258, + NEWLINE = 259, + ERROR = 260 + }; +#endif +/* Tokens. */ +#define END 0 +#define ALNUM 258 +#define NEWLINE 259 +#define ERROR 260 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + igraph_integer_t edgenum; + igraph_real_t weightnum; +} +/* Line 1529 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + diff --git a/src/io/parsers/pajek-lexer.c b/src/io/parsers/pajek-lexer.c new file mode 100644 index 0000000..464cdef --- /dev/null +++ b/src/io/parsers/pajek-lexer.c @@ -0,0 +1,2683 @@ + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + yy_size_t yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + #define YY_LINENO_REWIND_TO(dst) \ + do {\ + const char *p;\ + for ( p = yy_cp-1; p >= (dst); --p)\ + if ( *p == '\n' )\ + --yylineno;\ + }while(0) + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via 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, yy_size_t 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 yynoreturn 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 = (yy_size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 57 +#define YY_END_OF_BUFFER 58 +/* 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, 0, 0, 6, 6, 0, 0, 0, 0, + 0, 0, 58, 56, 8, 17, 17, 55, 56, 55, + 19, 55, 56, 4, 5, 5, 4, 3, 6, 6, + 2, 2, 2, 2, 55, 55, 55, 55, 55, 24, + 23, 55, 55, 55, 40, 38, 55, 55, 55, 47, + 39, 41, 37, 8, 17, 55, 0, 18, 19, 55, + 55, 0, 7, 7, 55, 16, 16, 16, 16, 16, + 16, 4, 4, 5, 6, 6, 0, 26, 27, 55, + 25, 29, 28, 55, 30, 55, 55, 55, 55, 42, + 44, 46, 55, 35, 36, 43, 45, 52, 51, 48, + + 49, 19, 55, 19, 7, 16, 16, 16, 16, 16, + 1, 55, 32, 55, 22, 34, 55, 55, 55, 53, + 55, 16, 16, 16, 16, 16, 33, 31, 55, 55, + 54, 50, 11, 16, 16, 16, 16, 55, 55, 16, + 12, 16, 16, 16, 20, 21, 16, 16, 15, 16, + 16, 16, 16, 9, 16, 13, 16, 10, 14, 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, 5, 6, 5, 5, 7, 5, 5, 5, + 5, 8, 9, 5, 10, 11, 5, 12, 13, 14, + 12, 12, 12, 12, 12, 12, 12, 5, 5, 5, + 5, 5, 5, 5, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 5, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 5, + 5, 5, 5, 5, 39, 5, 40, 41, 42, 43, + + 44, 45, 46, 47, 48, 5, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 5, 5, 5, 5, 5, 1, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 64, 5, 5, 5, + 65, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 66, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5 + } ; + +static const YY_CHAR yy_meta[68] = + { 0, + 1, 2, 3, 3, 4, 2, 4, 2, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 5 + } ; + +static const flex_int16_t yy_base[172] = + { 0, + 0, 67, 15, 23, 31, 35, 18, 26, 134, 33, + 201, 35, 337, 392, 331, 328, 328, 0, 243, 32, + 39, 53, 69, 237, 208, 197, 0, 392, 196, 0, + 392, 178, 392, 94, 30, 32, 45, 99, 42, 0, + 0, 47, 93, 86, 97, 0, 65, 35, 86, 152, + 0, 0, 0, 115, 392, 0, 109, 392, 252, 93, + 164, 119, 108, 98, 232, 0, 63, 90, 109, 108, + 112, 95, 0, 392, 84, 0, 0, 0, 0, 134, + 0, 0, 0, 123, 0, 137, 137, 175, 179, 0, + 0, 0, 190, 0, 0, 0, 0, 0, 0, 197, + + 0, 240, 215, 260, 392, 214, 211, 212, 227, 230, + 392, 234, 0, 246, 0, 0, 260, 261, 244, 0, + 255, 247, 261, 250, 246, 250, 0, 0, 270, 271, + 0, 0, 264, 258, 268, 265, 272, 264, 265, 276, + 284, 273, 280, 300, 0, 0, 287, 298, 0, 301, + 307, 294, 296, 0, 297, 0, 297, 0, 0, 392, + 355, 360, 365, 370, 3, 375, 378, 1, 381, 384, + 387 + } ; + +static const flex_int16_t yy_def[172] = + { 0, + 161, 161, 162, 162, 163, 163, 164, 164, 161, 9, + 161, 11, 160, 160, 160, 160, 160, 165, 166, 165, + 165, 167, 168, 169, 160, 160, 169, 160, 170, 170, + 160, 160, 160, 160, 165, 165, 165, 165, 165, 165, + 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, + 165, 165, 165, 160, 160, 165, 166, 160, 165, 165, + 165, 171, 160, 160, 167, 168, 168, 168, 168, 168, + 168, 169, 169, 160, 170, 170, 160, 165, 165, 165, + 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, + 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, + + 165, 165, 165, 165, 160, 168, 168, 168, 168, 168, + 160, 165, 165, 165, 165, 165, 165, 165, 165, 165, + 165, 168, 168, 168, 168, 168, 165, 165, 165, 165, + 165, 165, 168, 168, 168, 168, 168, 165, 165, 168, + 168, 168, 168, 168, 165, 165, 168, 168, 168, 168, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 0, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, + 160 + } ; + +static const flex_int16_t yy_nxt[460] = + { 0, + 14, 15, 16, 17, 66, 19, 56, 14, 20, 20, + 160, 21, 21, 21, 160, 14, 24, 25, 26, 32, + 33, 160, 14, 14, 24, 25, 26, 32, 33, 160, + 28, 14, 29, 25, 26, 14, 29, 25, 26, 22, + 23, 22, 23, 59, 59, 59, 78, 94, 95, 60, + 59, 59, 59, 160, 62, 63, 64, 61, 62, 80, + 62, 81, 160, 86, 111, 79, 14, 14, 15, 16, + 17, 78, 19, 22, 23, 20, 20, 87, 21, 21, + 21, 14, 61, 67, 80, 75, 81, 68, 86, 14, + 79, 34, 93, 106, 69, 70, 72, 14, 96, 97, + + 105, 14, 87, 71, 102, 102, 102, 107, 67, 90, + 91, 105, 68, 82, 58, 83, 54, 93, 106, 69, + 70, 63, 64, 108, 89, 92, 109, 84, 71, 85, + 110, 88, 107, 14, 14, 15, 16, 17, 82, 19, + 83, 14, 20, 20, 114, 21, 21, 21, 108, 35, + 92, 109, 84, 36, 85, 110, 37, 77, 38, 115, + 112, 116, 39, 40, 41, 113, 98, 42, 99, 114, + 43, 44, 103, 103, 35, 104, 104, 104, 36, 54, + 100, 37, 101, 38, 115, 112, 116, 39, 40, 41, + 113, 98, 42, 99, 117, 43, 44, 75, 118, 74, + + 14, 14, 15, 16, 17, 100, 19, 101, 14, 20, + 20, 74, 21, 21, 21, 45, 119, 46, 121, 117, + 47, 120, 48, 118, 49, 50, 104, 104, 104, 51, + 122, 123, 52, 62, 63, 64, 53, 62, 72, 62, + 45, 119, 46, 121, 124, 47, 120, 48, 58, 49, + 50, 102, 102, 102, 51, 122, 123, 52, 61, 125, + 126, 53, 60, 59, 59, 59, 127, 14, 128, 124, + 61, 104, 104, 104, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 61, 125, 126, 138, 139, 140, 141, + 142, 127, 143, 128, 144, 61, 145, 146, 147, 129, + + 130, 131, 132, 133, 134, 135, 136, 137, 148, 149, + 150, 138, 139, 140, 141, 142, 151, 143, 152, 144, + 153, 145, 146, 147, 154, 155, 156, 157, 158, 159, + 55, 55, 54, 148, 149, 150, 160, 160, 160, 160, + 160, 151, 160, 152, 160, 153, 160, 160, 160, 154, + 155, 156, 157, 158, 159, 18, 18, 18, 18, 18, + 27, 27, 27, 27, 27, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 57, 57, 160, 57, 65, + 65, 65, 73, 160, 73, 76, 160, 76, 62, 62, + 62, 13, 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, 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[460] = + { 0, + 1, 1, 1, 1, 168, 1, 165, 1, 1, 1, + 0, 1, 1, 1, 0, 3, 3, 3, 3, 7, + 7, 0, 3, 4, 4, 4, 4, 8, 8, 0, + 4, 5, 5, 5, 5, 6, 6, 6, 6, 10, + 10, 12, 12, 20, 20, 20, 35, 48, 48, 21, + 21, 21, 21, 0, 22, 22, 22, 21, 22, 36, + 22, 37, 0, 39, 77, 35, 1, 2, 2, 2, + 2, 35, 2, 2, 2, 2, 2, 42, 2, 2, + 2, 3, 21, 23, 36, 75, 37, 23, 39, 4, + 35, 8, 47, 67, 23, 23, 72, 5, 49, 49, + + 64, 6, 42, 23, 60, 60, 60, 68, 23, 45, + 45, 63, 23, 38, 57, 38, 54, 47, 67, 23, + 23, 62, 62, 69, 44, 45, 70, 38, 23, 38, + 71, 43, 68, 2, 9, 9, 9, 9, 38, 9, + 38, 9, 9, 9, 84, 9, 9, 9, 69, 9, + 45, 70, 38, 9, 38, 71, 9, 34, 9, 86, + 80, 87, 9, 9, 9, 80, 50, 9, 50, 84, + 9, 9, 61, 61, 9, 61, 61, 61, 9, 32, + 50, 9, 50, 9, 86, 80, 87, 9, 9, 9, + 80, 50, 9, 50, 88, 9, 9, 29, 89, 26, + + 9, 11, 11, 11, 11, 50, 11, 50, 11, 11, + 11, 25, 11, 11, 11, 11, 93, 11, 100, 88, + 11, 93, 11, 89, 11, 11, 103, 103, 103, 11, + 106, 107, 11, 65, 65, 65, 11, 65, 24, 65, + 11, 93, 11, 100, 108, 11, 93, 11, 19, 11, + 11, 102, 102, 102, 11, 106, 107, 11, 102, 109, + 110, 11, 59, 59, 59, 59, 112, 11, 114, 108, + 59, 104, 104, 104, 117, 118, 119, 121, 122, 123, + 124, 125, 126, 102, 109, 110, 129, 130, 133, 134, + 135, 112, 136, 114, 137, 59, 138, 139, 140, 117, + + 118, 119, 121, 122, 123, 124, 125, 126, 141, 142, + 143, 129, 130, 133, 134, 135, 144, 136, 147, 137, + 148, 138, 139, 140, 150, 151, 152, 153, 155, 157, + 17, 16, 15, 141, 142, 143, 13, 0, 0, 0, + 0, 144, 0, 147, 0, 148, 0, 0, 0, 150, + 151, 152, 153, 155, 157, 161, 161, 161, 161, 161, + 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, + 164, 164, 164, 164, 164, 166, 166, 0, 166, 167, + 167, 167, 169, 0, 169, 170, 0, 170, 171, 171, + 171, 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, 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 + } ; + +/* Table of booleans, true if rule could match eol. */ +static const flex_int32_t yy_rule_can_match_eol[58] = + { 0, +0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, }; + +/* 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 +/* + 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 "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) +#define YY_USER_INIT BEGIN(bom) /* we start in the 'bom' start condition */ +#ifdef USING_R +#define fprintf(file, msg, ...) (1) +#ifdef stdout +# undef stdout +#endif +#define stdout 0 +#endif +#define YY_NO_INPUT 1 +/* Any use of {newline} below must use yy_set_bol(true) in order to mark the character + following a single \r as the first on a new line, and allow the ^ pattern to match. + This pattern must match single newlines only, in order to follow Pajek's "no newline + after *Vertices" convention. */ +/* Anything except non-printable (00-1F), space (20), del (7F), '"' and '*'. */ +/* 'unknown' skips text at the beginning of the file, lines below an unknown *Word + * 'unknown_line' skips the rest of the line after an unknown *Word. */ + +/* Notes: + * - Unquoted '*' characters may only appear at the start of a line-initial word. + * - Both LF and CR LF line endings are allowed. + * - Pajek files do not allow empty lines after *Vertices (empty lines should signify the end of the file), + * therefore we are careful not to skip newlines in the lexer. + */ + +#define INITIAL 0 +#define unknown 1 +#define unknown_line 2 +#define bom 3 +#define vert 4 +#define edge 5 + +#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; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int 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 = '*'; \ + yy_size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (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 ); + } + + { + + /* Skip a UTF-8 BOM at the very beginning of the file, if present, then immediately switch to 'unknown'. */ + + 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 >= 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] != 392 ); + +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; + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + yy_size_t yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + } + +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 +{ } + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +{ BEGIN(unknown); yyless(0); yy_set_bol(true); } + YY_BREAK +/* Skip all text until the next *Word at the beginning of a line. */ +case 3: +YY_RULE_SETUP +{ BEGIN(INITIAL); yyless(0); yy_set_bol(true); } + YY_BREAK +case 4: +YY_RULE_SETUP +{ } /* match cannot start with a * in order not to take precedence over ^\* above */ + YY_BREAK +case 5: +/* rule 5 can match eol */ +YY_RULE_SETUP +{ yy_set_bol(true); } + YY_BREAK +case 6: +YY_RULE_SETUP +{ BEGIN(unknown); } + YY_BREAK +case 7: +/* rule 7 can match eol */ +YY_RULE_SETUP +{ yy_set_bol(true); } /* comments */ + YY_BREAK +case 8: +YY_RULE_SETUP +{ } + YY_BREAK +case 9: +YY_RULE_SETUP +{ BEGIN(unknown_line); return NETWORKLINE; } + YY_BREAK +case 10: +YY_RULE_SETUP +{ BEGIN(vert); return VERTICESLINE; } + YY_BREAK +case 11: +YY_RULE_SETUP +{ BEGIN(edge); return ARCSLINE; } + YY_BREAK +case 12: +YY_RULE_SETUP +{ BEGIN(edge); return EDGESLINE; } + YY_BREAK +case 13: +YY_RULE_SETUP +{ BEGIN(INITIAL); return ARCSLISTLINE; } + YY_BREAK +case 14: +YY_RULE_SETUP +{ BEGIN(INITIAL);return EDGESLISTLINE; } + YY_BREAK +case 15: +YY_RULE_SETUP +{ BEGIN(INITIAL); return MATRIXLINE; } + YY_BREAK +case 16: +YY_RULE_SETUP +{ BEGIN(unknown_line); IGRAPH_WARNINGF("Skipping unknown section '%s' on line %d.", yytext, yylineno); } + YY_BREAK +case 17: +/* rule 17 can match eol */ +YY_RULE_SETUP +{ yy_set_bol(true); return NEWLINE; } + YY_BREAK +/* Newlines not allowed in strings. */ +case 18: +YY_RULE_SETUP +{ return QSTR; } + YY_BREAK +case 19: +YY_RULE_SETUP +{ return NUM; } + YY_BREAK + +/* http://mrvar.fdv.uni-lj.si/pajek/DrawEPS.htm */ +case 20: +YY_RULE_SETUP +{ return VP_X_FACT; } + YY_BREAK +case 21: +YY_RULE_SETUP +{ return VP_Y_FACT; } + YY_BREAK +case 22: +YY_RULE_SETUP +{ return VP_PHI; } + YY_BREAK +case 23: +YY_RULE_SETUP +{ return VP_R; } + YY_BREAK +case 24: +YY_RULE_SETUP +{ return VP_Q; } + YY_BREAK +case 25: +YY_RULE_SETUP +{ return VP_IC; } + YY_BREAK +case 26: +YY_RULE_SETUP +{ return VP_BC; } + YY_BREAK +case 27: +YY_RULE_SETUP +{ return VP_BW; } + YY_BREAK +case 28: +YY_RULE_SETUP +{ return VP_LC; } + YY_BREAK +case 29: +YY_RULE_SETUP +{ return VP_LA; } + YY_BREAK +case 30: +YY_RULE_SETUP +{ return VP_LR; } + YY_BREAK +case 31: +YY_RULE_SETUP +{ return VP_LPHI; } + YY_BREAK +case 32: +YY_RULE_SETUP +{ return VP_FOS; } + YY_BREAK +case 33: +YY_RULE_SETUP +{ return VP_FONT; } + YY_BREAK +/* http://mrvar.fdv.uni-lj.si/pajek/history.htm */ +case 34: +YY_RULE_SETUP +{ return VP_URL; } + YY_BREAK + +/* http://mrvar.fdv.uni-lj.si/pajek/DrawEPS.htm */ +case 35: +YY_RULE_SETUP +{ return EP_H1; } + YY_BREAK +case 36: +YY_RULE_SETUP +{ return EP_H2; } + YY_BREAK +case 37: +YY_RULE_SETUP +{ return EP_W; } + YY_BREAK +case 38: +YY_RULE_SETUP +{ return EP_C; } + YY_BREAK +case 39: +YY_RULE_SETUP +{ return EP_P; } + YY_BREAK +case 40: +YY_RULE_SETUP +{ return EP_A; } + YY_BREAK +case 41: +YY_RULE_SETUP +{ return EP_S; } + YY_BREAK +case 42: +YY_RULE_SETUP +{ return EP_A1; } + YY_BREAK +case 43: +YY_RULE_SETUP +{ return EP_K1; } + YY_BREAK +case 44: +YY_RULE_SETUP +{ return EP_A2; } + YY_BREAK +case 45: +YY_RULE_SETUP +{ return EP_K2; } + YY_BREAK +case 46: +YY_RULE_SETUP +{ return EP_AP; } + YY_BREAK +case 47: +YY_RULE_SETUP +{ return EP_L; } + YY_BREAK +case 48: +YY_RULE_SETUP +{ return EP_LP; } + YY_BREAK +case 49: +YY_RULE_SETUP +{ return EP_LR; } + YY_BREAK +case 50: +YY_RULE_SETUP +{ return EP_LPHI; } + YY_BREAK +case 51: +YY_RULE_SETUP +{ return EP_LC; } + YY_BREAK +case 52: +YY_RULE_SETUP +{ return EP_LA; } + YY_BREAK +case 53: +YY_RULE_SETUP +{ return EP_FOS; } + YY_BREAK +case 54: +YY_RULE_SETUP +{ return EP_FONT; } + YY_BREAK + +case 55: +YY_RULE_SETUP +{ return ALNUM; } + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(unknown): +case YY_STATE_EOF(unknown_line): +case YY_STATE_EOF(bom): +case YY_STATE_EOF(vert): +case YY_STATE_EOF(edge): +{ if (yyextra->eof) { + yyterminate(); + } else { + yyextra->eof=true; + return NEWLINE; + } + } + YY_BREAK +case 56: +YY_RULE_SETUP +{ return ERROR; } + YY_BREAK +case 57: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK + + 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 + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + 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. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) 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)] : 67); + 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 = 67; + 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 */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + 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'); + if ( YY_CURRENT_BUFFER_LVALUE->yy_at_bol ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + + 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, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + yy_size_t 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 yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yy_size_t 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" + diff --git a/src/io/parsers/pajek-lexer.h b/src/io/parsers/pajek-lexer.h new file mode 100644 index 0000000..2f63580 --- /dev/null +++ b/src/io/parsers/pajek-lexer.h @@ -0,0 +1,734 @@ +#ifndef igraph_pajek_yyHEADER_H +#define igraph_pajek_yyHEADER_H 1 +#define igraph_pajek_yyIN_HEADER 1 + +#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; +typedef uint64_t flex_uint64_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* 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. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#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, yy_size_t 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 +#define unknown 1 +#define unknown_line 2 +#define bom 3 +#define vert 4 +#define edge 5 + +#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 ); + + 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 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 + +#undef igraph_pajek_yyIN_HEADER +#endif /* igraph_pajek_yyHEADER_H */ diff --git a/src/io/parsers/pajek-parser.c b/src/io/parsers/pajek-parser.c new file mode 100644 index 0000000..67222bd --- /dev/null +++ b/src/io/parsers/pajek-parser.c @@ -0,0 +1,2830 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 1 + +/* Substitute the variable and function names. */ +#define yyparse igraph_pajek_yyparse +#define yylex igraph_pajek_yylex +#define yyerror igraph_pajek_yyerror +#define yylval igraph_pajek_yylval +#define yychar igraph_pajek_yychar +#define yydebug igraph_pajek_yydebug +#define yynerrs igraph_pajek_yynerrs +#define yylloc igraph_pajek_yylloc + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + NEWLINE = 258, + NUM = 259, + ALNUM = 260, + QSTR = 261, + NETWORKLINE = 262, + VERTICESLINE = 263, + ARCSLINE = 264, + EDGESLINE = 265, + ARCSLISTLINE = 266, + EDGESLISTLINE = 267, + MATRIXLINE = 268, + ERROR = 269, + VP_X_FACT = 270, + VP_Y_FACT = 271, + VP_PHI = 272, + VP_R = 273, + VP_Q = 274, + VP_IC = 275, + VP_BC = 276, + VP_BW = 277, + VP_LC = 278, + VP_LA = 279, + VP_LR = 280, + VP_LPHI = 281, + VP_FOS = 282, + VP_FONT = 283, + VP_URL = 284, + EP_H1 = 285, + EP_H2 = 286, + EP_W = 287, + EP_C = 288, + EP_P = 289, + EP_A = 290, + EP_S = 291, + EP_A1 = 292, + EP_K1 = 293, + EP_A2 = 294, + EP_K2 = 295, + EP_AP = 296, + EP_L = 297, + EP_LP = 298, + EP_LR = 299, + EP_LPHI = 300, + EP_LC = 301, + EP_LA = 302, + EP_FOS = 303, + EP_FONT = 304 + }; +#endif +/* Tokens. */ +#define END 0 +#define NEWLINE 258 +#define NUM 259 +#define ALNUM 260 +#define QSTR 261 +#define NETWORKLINE 262 +#define VERTICESLINE 263 +#define ARCSLINE 264 +#define EDGESLINE 265 +#define ARCSLISTLINE 266 +#define EDGESLISTLINE 267 +#define MATRIXLINE 268 +#define ERROR 269 +#define VP_X_FACT 270 +#define VP_Y_FACT 271 +#define VP_PHI 272 +#define VP_R 273 +#define VP_Q 274 +#define VP_IC 275 +#define VP_BC 276 +#define VP_BW 277 +#define VP_LC 278 +#define VP_LA 279 +#define VP_LR 280 +#define VP_LPHI 281 +#define VP_FOS 282 +#define VP_FONT 283 +#define VP_URL 284 +#define EP_H1 285 +#define EP_H2 286 +#define EP_W 287 +#define EP_C 288 +#define EP_P 289 +#define EP_A 290 +#define EP_S 291 +#define EP_A1 292 +#define EP_K1 293 +#define EP_A2 294 +#define EP_K2 295 +#define EP_AP 296 +#define EP_L 297 +#define EP_LP 298 +#define EP_LR 299 +#define EP_LPHI 300 +#define EP_LC 301 +#define EP_LA 302 +#define EP_FOS 303 +#define EP_FONT 304 + + + + +/* Copy the first part of user declarations. */ + + + +/* + 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 "igraph_attributes.h" +#include "igraph_error.h" +#include "igraph_memory.h" +#include "igraph_types.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 "io/parse_utils.h" +#include "internal/hacks.h" /* strdup */ + +#include +#include +#include + +int igraph_pajek_yyerror(YYLTYPE* locp, + igraph_i_pajek_parsedata_t *context, + const char *s); + +static igraph_error_t add_string_vertex_attribute(const char *name, + const char *value, + size_t len, + igraph_i_pajek_parsedata_t *context); +static igraph_error_t add_string_edge_attribute(const char *name, + const char *value, + size_t len, + igraph_i_pajek_parsedata_t *context); +static igraph_error_t add_numeric_vertex_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context); +static igraph_error_t add_numeric_edge_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context); +static igraph_error_t add_numeric_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + igraph_integer_t count, + const char *attrname, + igraph_integer_t vid, + igraph_real_t number); +static igraph_error_t add_string_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + igraph_integer_t count, + const char *attrname, + igraph_integer_t vid, + const char *str, + igraph_integer_t str_len); + +static igraph_error_t add_bipartite_type(igraph_i_pajek_parsedata_t *context); +static igraph_error_t check_bipartite(igraph_i_pajek_parsedata_t *context); + +static igraph_error_t make_dynstr(const char *src, size_t len, char **res); +static igraph_bool_t is_standard_vattr(const char *attrname); +static igraph_bool_t is_standard_eattr(const char *attrname); +static igraph_error_t deconflict_attrname(char **attrname); + +#define scanner context->scanner + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + igraph_integer_t intnum; + igraph_real_t realnum; + struct { + char *str; + size_t len; + } string; + char *dynstr; +} +/* Line 193 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + YYLTYPE yyls; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 4 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 215 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 50 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 52 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 115 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 178 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 304 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 8, 9, 12, 13, 15, 19, 22, + 26, 27, 30, 33, 34, 42, 44, 46, 47, 50, + 54, 55, 57, 58, 61, 63, 66, 69, 72, 75, + 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, + 108, 111, 112, 115, 118, 121, 124, 127, 131, 136, + 137, 140, 141, 148, 152, 157, 158, 161, 162, 169, + 170, 172, 173, 176, 178, 181, 184, 187, 190, 193, + 196, 199, 202, 205, 208, 211, 214, 217, 220, 223, + 226, 229, 232, 235, 238, 241, 245, 246, 249, 253, + 254, 257, 259, 261, 265, 266, 269, 273, 274, 277, + 279, 281, 285, 287, 288, 291, 294, 295, 298, 300, + 302, 304, 306, 308, 310, 312 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 51, 0, -1, 53, 54, 66, 52, -1, -1, 3, + 52, -1, -1, 7, -1, 55, 3, 56, -1, 8, + 97, -1, 8, 97, 97, -1, -1, 56, 57, -1, + 59, 3, -1, -1, 59, 58, 60, 61, 62, 63, + 3, -1, 97, -1, 101, -1, -1, 98, 98, -1, + 98, 98, 98, -1, -1, 101, -1, -1, 63, 64, + -1, 65, -1, 15, 98, -1, 16, 98, -1, 25, + 98, -1, 26, 98, -1, 22, 98, -1, 27, 98, + -1, 17, 98, -1, 18, 98, -1, 19, 98, -1, + 24, 98, -1, 28, 100, -1, 29, 100, -1, 20, + 100, -1, 21, 100, -1, 23, 100, -1, 99, 100, + -1, -1, 66, 67, -1, 66, 71, -1, 66, 79, + -1, 66, 85, -1, 66, 91, -1, 9, 3, 68, + -1, 9, 98, 3, 68, -1, -1, 68, 69, -1, + -1, 59, 59, 70, 75, 76, 3, -1, 10, 3, + 72, -1, 10, 98, 3, 72, -1, -1, 72, 73, + -1, -1, 59, 59, 74, 75, 76, 3, -1, -1, + 98, -1, -1, 76, 77, -1, 78, -1, 36, 98, + -1, 32, 98, -1, 30, 98, -1, 31, 98, -1, + 37, 98, -1, 39, 98, -1, 38, 98, -1, 40, + 98, -1, 41, 98, -1, 43, 98, -1, 44, 98, + -1, 45, 98, -1, 47, 98, -1, 48, 98, -1, + 35, 100, -1, 34, 100, -1, 42, 100, -1, 46, + 100, -1, 33, 100, -1, 49, 100, -1, 99, 100, + -1, 11, 3, 80, -1, -1, 80, 81, -1, 83, + 82, 3, -1, -1, 82, 84, -1, 59, -1, 59, + -1, 12, 3, 86, -1, -1, 86, 87, -1, 89, + 88, 3, -1, -1, 88, 90, -1, 59, -1, 59, + -1, 92, 3, 93, -1, 13, -1, -1, 93, 94, + -1, 95, 3, -1, -1, 96, 95, -1, 98, -1, + 4, -1, 4, -1, 101, -1, 101, -1, 5, -1, + 4, -1, 6, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 188, 188, 199, 199, 201, 201, 203, 205, 215, + 233, 233, 235, 236, 236, 239, 258, 263, 264, 268, + 274, 274, 278, 278, 281, 282, 285, 288, 291, 294, + 297, 300, 303, 306, 309, 314, 317, 320, 323, 326, + 329, 344, 344, 344, 344, 344, 344, 346, 347, 349, + 349, 351, 351, 356, 357, 359, 359, 361, 361, 366, + 366, 370, 370, 373, 374, 377, 380, 383, 386, 389, + 392, 395, 398, 401, 404, 407, 410, 413, 418, 421, + 424, 427, 430, 433, 436, 451, 453, 453, 455, 457, + 457, 459, 461, 466, 468, 468, 470, 472, 472, 474, + 476, 483, 485, 490, 490, 492, 494, 494, 496, 516, + 524, 532, 536, 538, 540, 542 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "$undefined", "\"end of line\"", + "\"number\"", "\"word\"", "\"quoted string\"", "\"*Network line\"", + "\"*Vertices line\"", "\"*Arcs line\"", "\"*Edges line\"", + "\"*Arcslist line\"", "\"*Edgeslist line\"", "\"*Matrix line\"", "ERROR", + "VP_X_FACT", "VP_Y_FACT", "VP_PHI", "VP_R", "VP_Q", "VP_IC", "VP_BC", + "VP_BW", "VP_LC", "VP_LA", "VP_LR", "VP_LPHI", "VP_FOS", "VP_FONT", + "VP_URL", "EP_H1", "EP_H2", "EP_W", "EP_C", "EP_P", "EP_A", "EP_S", + "EP_A1", "EP_K1", "EP_A2", "EP_K2", "EP_AP", "EP_L", "EP_LP", "EP_LR", + "EP_LPHI", "EP_LC", "EP_LA", "EP_FOS", "EP_FONT", "$accept", "input", + "final_newlines", "nethead", "vertices", "verticeshead", "vertdefs", + "vertexline", "@1", "vertex", "vertexid", "vertexcoords", "shape", + "vertparams", "vertparam", "vpword", "edgeblock", "arcs", "arcsdefs", + "arcsline", "@2", "edges", "edgesdefs", "edgesline", "@3", "weight", + "edgeparams", "edgeparam", "epword", "arcslist", "arcslistlines", + "arclistline", "arctolist", "arclistfrom", "arclistto", "edgeslist", + "edgelistlines", "edgelistline", "edgetolist", "edgelistfrom", + "edgelistto", "adjmatrix", "matrixline", "adjmatrixlines", + "adjmatrixline", "adjmatrixnumbers", "adjmatrixentry", "integer", + "number", "parname", "parstrval", "word", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 50, 51, 52, 52, 53, 53, 54, 55, 55, + 56, 56, 57, 58, 57, 59, 60, 61, 61, 61, + 62, 62, 63, 63, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, + 65, 66, 66, 66, 66, 66, 66, 67, 67, 68, + 68, 70, 69, 71, 71, 72, 72, 74, 73, 75, + 75, 76, 76, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 78, 78, + 78, 78, 78, 78, 78, 79, 80, 80, 81, 82, + 82, 83, 84, 85, 86, 86, 87, 88, 88, 89, + 90, 91, 92, 93, 93, 94, 95, 95, 96, 97, + 98, 99, 100, 101, 101, 101 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 4, 0, 2, 0, 1, 3, 2, 3, + 0, 2, 2, 0, 7, 1, 1, 0, 2, 3, + 0, 1, 0, 2, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 0, 2, 2, 2, 2, 2, 3, 4, 0, + 2, 0, 6, 3, 4, 0, 2, 0, 6, 0, + 1, 0, 2, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 0, 2, 3, 0, + 2, 1, 1, 3, 0, 2, 3, 0, 2, 1, + 1, 3, 1, 0, 2, 2, 0, 2, 1, 1, + 1, 1, 1, 1, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 5, 6, 0, 0, 1, 0, 41, 0, 109, 8, + 3, 10, 9, 3, 0, 0, 0, 0, 102, 2, + 42, 43, 44, 45, 46, 0, 7, 4, 49, 110, + 0, 55, 0, 86, 94, 103, 11, 13, 15, 47, + 49, 53, 55, 85, 93, 101, 12, 0, 0, 50, + 48, 0, 56, 54, 91, 87, 89, 99, 95, 97, + 104, 0, 106, 108, 114, 113, 115, 17, 16, 51, + 57, 0, 0, 105, 107, 20, 0, 59, 59, 88, + 92, 90, 96, 100, 98, 22, 21, 18, 61, 60, + 61, 0, 19, 0, 0, 14, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 23, 24, 0, 111, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 62, 63, 0, 58, + 25, 26, 31, 32, 33, 37, 112, 38, 29, 39, + 34, 27, 28, 30, 35, 36, 40, 66, 67, 65, + 82, 79, 78, 64, 68, 70, 69, 71, 72, 80, + 73, 74, 75, 81, 76, 77, 83, 84 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 2, 19, 3, 6, 7, 26, 36, 47, 48, + 67, 75, 85, 91, 111, 112, 10, 20, 39, 49, + 77, 21, 41, 52, 78, 88, 93, 136, 137, 22, + 43, 55, 71, 56, 81, 23, 44, 58, 72, 59, + 84, 24, 25, 45, 60, 61, 62, 38, 63, 138, + 145, 146 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -95 +static const yytype_int16 yypact[] = +{ + 2, -95, 20, 6, -95, 18, -95, 26, -95, 18, + 47, -95, -95, 29, 1, 14, 34, 39, -95, -95, + -95, -95, -95, -95, -95, 40, 18, -95, -95, -95, + 42, -95, 44, -95, -95, -95, -95, 51, -95, 18, + -95, 18, -95, 18, 18, 57, -95, 7, 18, -95, + 18, 18, -95, 18, -95, -95, -95, -95, -95, -95, + -95, 52, 57, -95, -95, -95, -95, 57, -95, -95, + -95, 32, 36, -95, -95, 7, 57, 57, 57, -95, + -95, -95, -95, -95, -95, -95, -95, 57, -95, -95, + -95, 186, -95, 92, 139, -95, 57, 57, 57, 57, + 57, 7, 7, 57, 7, 57, 57, 57, 57, 7, + 7, -95, -95, 7, -95, -95, 57, 57, 57, 7, + 7, 7, 57, 57, 57, 57, 57, 57, 7, 57, + 57, 57, 7, 57, 57, 7, -95, -95, 7, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -95, -95, 53, -95, -95, -95, -95, -95, -95, -20, + -95, -95, -95, -95, -95, -95, -95, -95, 25, -95, + -95, -95, 27, -95, -95, -11, -22, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, 8, -95, -2, -14, -19, + -94, -45 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 30, 32, 68, 9, 28, 29, 37, 12, 147, 1, + 149, 64, 65, 66, 5, 154, 155, 31, 29, 156, + 4, 51, 8, 54, 57, 160, 161, 162, 69, 11, + 86, 70, 13, 51, 169, 79, 8, 33, 173, 82, + 8, 176, 34, 35, 177, 40, 114, 42, 114, 114, + 13, 80, 83, 76, 46, 73, 14, 15, 16, 17, + 18, 29, 87, 89, 89, 50, 27, 90, 94, 53, + 74, 0, 113, 92, 0, 0, 0, 0, 0, 0, + 0, 0, 140, 141, 142, 143, 144, 0, 0, 148, + 0, 150, 151, 152, 153, 115, 64, 65, 66, 0, + 0, 0, 157, 158, 159, 0, 0, 0, 163, 164, + 165, 166, 167, 168, 0, 170, 171, 172, 0, 174, + 175, 0, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 139, 64, 65, 66, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 95, + 64, 65, 66, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110 +}; + +static const yytype_int16 yycheck[] = +{ + 14, 15, 47, 5, 3, 4, 26, 9, 102, 7, + 104, 4, 5, 6, 8, 109, 110, 3, 4, 113, + 0, 41, 4, 43, 44, 119, 120, 121, 48, 3, + 75, 51, 3, 53, 128, 3, 4, 3, 132, 3, + 4, 135, 3, 3, 138, 3, 91, 3, 93, 94, + 3, 71, 72, 67, 3, 3, 9, 10, 11, 12, + 13, 4, 76, 77, 78, 40, 13, 78, 90, 42, + 62, -1, 91, 87, -1, -1, -1, -1, -1, -1, + -1, -1, 96, 97, 98, 99, 100, -1, -1, 103, + -1, 105, 106, 107, 108, 3, 4, 5, 6, -1, + -1, -1, 116, 117, 118, -1, -1, -1, 122, 123, + 124, 125, 126, 127, -1, 129, 130, 131, -1, 133, + 134, -1, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 3, 4, 5, 6, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 3, + 4, 5, 6, -1, -1, -1, -1, -1, -1, -1, + -1, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 7, 51, 53, 0, 8, 54, 55, 4, 97, + 66, 3, 97, 3, 9, 10, 11, 12, 13, 52, + 67, 71, 79, 85, 91, 92, 56, 52, 3, 4, + 98, 3, 98, 3, 3, 3, 57, 59, 97, 68, + 3, 72, 3, 80, 86, 93, 3, 58, 59, 69, + 68, 59, 73, 72, 59, 81, 83, 59, 87, 89, + 94, 95, 96, 98, 4, 5, 6, 60, 101, 59, + 59, 82, 88, 3, 95, 61, 98, 70, 74, 3, + 59, 84, 3, 59, 90, 62, 101, 98, 75, 98, + 75, 63, 98, 76, 76, 3, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 64, 65, 99, 101, 3, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 77, 78, 99, 3, + 98, 98, 98, 98, 98, 100, 101, 100, 98, 100, + 98, 98, 98, 98, 100, 100, 100, 98, 98, 98, + 100, 100, 100, 98, 98, 98, 98, 98, 98, 100, + 98, 98, 98, 100, 98, 98, 100, 100 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, &yylloc, scanner) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_pajek_parsedata_t* context; +#endif +{ + if (!yyvaluep) + return; + YYUSE (yylocationp); + YYUSE (context); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, igraph_i_pajek_parsedata_t* context) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + igraph_i_pajek_parsedata_t* context; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, igraph_i_pajek_parsedata_t* context) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule, context) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; + igraph_i_pajek_parsedata_t* context; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule, context); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, igraph_i_pajek_parsedata_t* context) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; + igraph_i_pajek_parsedata_t* context; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + case 99: /* "parname" */ + + { free((yyvaluep->dynstr)); }; + + break; + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (igraph_i_pajek_parsedata_t* context); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (igraph_i_pajek_parsedata_t* context) +#else +int +yyparse (context) + igraph_i_pajek_parsedata_t* context; +#endif +#endif +{ + /* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; +/* Location data for the look-ahead symbol. */ +YYLTYPE yylloc; + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[2]; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + yylsp = yyls; +#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + /* Initialize the default location before parsing starts. */ + yylloc.first_line = yylloc.last_line = 1; + yylloc.first_column = yylloc.last_column = 0; +#endif + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + YYSTACK_RELOCATE (yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: + + { + if (context->vcount2 > 0) { check_bipartite(context); } + if (! context->eof) { + /* In Pajek files, an empty line after *Vertices signifies the end of the network data. + * If there is more data after one or more empty lines, we warn the user, as this + * may indicate file corruption, for example a stray empty lines before *Edges. */ + IGRAPH_WARNINGF("Empty line encountered, ignoring rest of file after line %d.", (yylsp[(4) - (4)]).first_line); + } + YYACCEPT; /* stop parsing even if there is more data in the file. */ + ;} + break; + + case 8: + + { + context->vcount=(yyvsp[(2) - (2)].intnum); + context->vcount2=0; + if (context->vcount < 0) { + IGRAPH_YY_ERRORF("Invalid vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); + } + if (context->vcount > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { + IGRAPH_YY_ERRORF("Vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); + } + ;} + break; + + case 9: + + { + context->vcount=(yyvsp[(2) - (3)].intnum); + context->vcount2=(yyvsp[(3) - (3)].intnum); + if (context->vcount < 0) { + IGRAPH_YY_ERRORF("Invalid vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); + } + if (context->vcount > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { + IGRAPH_YY_ERRORF("Vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); + } + if (context->vcount2 < 0) { + IGRAPH_YY_ERRORF("Invalid two-mode vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount2); + } + if (context->vcount2 > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { + IGRAPH_YY_ERRORF("2-mode vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount2); + } + IGRAPH_YY_CHECK(add_bipartite_type(context)); +;} + break; + + case 13: + + { context->actvertex=(yyvsp[(1) - (1)].intnum); ;} + break; + + case 14: + + { ;} + break; + + case 15: + + { + igraph_integer_t v = (yyvsp[(1) - (1)].intnum); + /* Per feedback from Pajek's authors, negative signs should be ignored for vertex IDs. + * See https://nascol.discourse.group/t/pajek-arcslist-edgelist-format/44/2 + * This applies to all of *Edges, *Arcs, *Edgeslist, *Arcslist and *Vertices section. + * IGRAPH_INTEGER_MIN cannot be negated on typical platforms so we keep it as-is. + */ + if (v < 0 && v > IGRAPH_INTEGER_MIN) { + v = -v; + } + if (v < 1 || v > context->vcount) { + IGRAPH_YY_ERRORF( + "Invalid vertex ID (%" IGRAPH_PRId ") in Pajek file. " + "The number of vertices is %" IGRAPH_PRId ".", + IGRAPH_EINVAL, v, context->vcount); + } + (yyval.intnum) = v; +;} + break; + + case 16: + + { + IGRAPH_YY_CHECK(add_string_vertex_attribute("id", (yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, context)); + IGRAPH_YY_CHECK(add_string_vertex_attribute("name", (yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, context)); +;} + break; + + case 18: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("x", (yyvsp[(1) - (2)].realnum), context)); + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("y", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 19: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("x", (yyvsp[(1) - (3)].realnum), context)); + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("y", (yyvsp[(2) - (3)].realnum), context)); + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("z", (yyvsp[(3) - (3)].realnum), context)); + ;} + break; + + case 21: + + { + IGRAPH_YY_CHECK(add_string_vertex_attribute("shape", (yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, context)); +;} + break; + + case 25: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("xfact", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 26: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("yfact", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 27: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("labeldist", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 28: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("labeldegree2", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 29: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("framewidth", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 30: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("fontsize", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 31: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("rotation", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 32: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("radius", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 33: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("diamondratio", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 34: + + { + IGRAPH_YY_CHECK(add_numeric_vertex_attribute("labeldegree", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 35: + + { + IGRAPH_YY_CHECK(add_string_vertex_attribute("font", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 36: + + { + IGRAPH_YY_CHECK(add_string_vertex_attribute("url", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 37: + + { + IGRAPH_YY_CHECK(add_string_vertex_attribute("color", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 38: + + { + IGRAPH_YY_CHECK(add_string_vertex_attribute("framecolor", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 39: + + { + IGRAPH_YY_CHECK(add_string_vertex_attribute("labelcolor", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 40: + + { + IGRAPH_FINALLY(igraph_free, (yyvsp[(1) - (2)].dynstr)); + if (is_standard_vattr((yyvsp[(1) - (2)].dynstr))) { + IGRAPH_YY_CHECK(deconflict_attrname(&(yyvsp[(1) - (2)].dynstr))); + /* update address on finally stack */ + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_free, (yyvsp[(1) - (2)].dynstr)); + } + IGRAPH_YY_CHECK(add_string_vertex_attribute( + (yyvsp[(1) - (2)].dynstr), (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + IGRAPH_FREE((yyvsp[(1) - (2)].dynstr)); + IGRAPH_FINALLY_CLEAN(1); + ;} + break; + + case 47: + + { context->directed=true; ;} + break; + + case 48: + + { context->directed=true; ;} + break; + + case 51: + + { context->actedge++; ;} + break; + + case 52: + + { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(1) - (6)].intnum)-1)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(2) - (6)].intnum)-1)); ;} + break; + + case 53: + + { context->directed=0; ;} + break; + + case 54: + + { context->directed=0; ;} + break; + + case 57: + + { context->actedge++; ;} + break; + + case 58: + + { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(1) - (6)].intnum)-1)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(2) - (6)].intnum)-1)); ;} + break; + + case 60: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context)); +;} + break; + + case 64: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("arrowsize", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 65: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("edgewidth", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 66: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("hook1", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 67: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("hook2", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 68: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("angle1", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 69: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("angle2", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 70: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("velocity1", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 71: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("velocity2", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 72: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("arrowpos", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 73: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("labelpos", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 74: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("labelangle", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 75: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("labelangle2", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 76: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("labeldegree", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 77: + + { + IGRAPH_YY_CHECK(add_numeric_edge_attribute("fontsize", (yyvsp[(2) - (2)].realnum), context)); + ;} + break; + + case 78: + + { + IGRAPH_YY_CHECK(add_string_edge_attribute("arrowtype", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 79: + + { + IGRAPH_YY_CHECK(add_string_edge_attribute("linepattern", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 80: + + { + IGRAPH_YY_CHECK(add_string_edge_attribute("label", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 81: + + { + IGRAPH_YY_CHECK(add_string_edge_attribute("labelcolor", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 82: + + { + IGRAPH_YY_CHECK(add_string_edge_attribute("color", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 83: + + { + IGRAPH_YY_CHECK(add_string_edge_attribute("font", (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + ;} + break; + + case 84: + + { + IGRAPH_FINALLY(igraph_free, (yyvsp[(1) - (2)].dynstr)); + if (is_standard_eattr((yyvsp[(1) - (2)].dynstr))) { + IGRAPH_YY_CHECK(deconflict_attrname(&(yyvsp[(1) - (2)].dynstr))); + /* update address on finally stack */ + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY(igraph_free, (yyvsp[(1) - (2)].dynstr)); + } + IGRAPH_YY_CHECK(add_string_edge_attribute( + (yyvsp[(1) - (2)].dynstr), (yyvsp[(2) - (2)].string).str, (yyvsp[(2) - (2)].string).len, context)); + IGRAPH_FREE((yyvsp[(1) - (2)].dynstr)); + IGRAPH_FINALLY_CLEAN(1); + ;} + break; + + case 85: + + { context->directed=true; ;} + break; + + case 91: + + { context->actfrom=(yyvsp[(1) - (1)].intnum)-1; ;} + break; + + case 92: + + { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(1) - (1)].intnum)-1)); +;} + break; + + case 93: + + { context->directed=0; ;} + break; + + case 99: + + { context->actfrom=(yyvsp[(1) - (1)].intnum)-1; ;} + break; + + case 100: + + { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, (yyvsp[(1) - (1)].intnum)-1)); +;} + break; + + case 102: + + { context->actfrom=0; + context->actto=0; + context->directed=(context->vcount2==0); + ;} + break; + + case 105: + + { context->actfrom++; context->actto=0; ;} + break; + + case 108: + + { + if ((yyvsp[(1) - (1)].realnum) != 0) { + if (context->vcount2==0) { + context->actedge++; + IGRAPH_YY_CHECK(add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actto)); + } else if (context->vcount2 + context->actto < context->vcount) { + context->actedge++; + IGRAPH_YY_CHECK(add_numeric_edge_attribute("weight", (yyvsp[(1) - (1)].realnum), context)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, + context->vcount2+context->actto)); + } + } + context->actto++; +;} + break; + + case 109: + + { + igraph_integer_t val; + IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner), + &val)); + (yyval.intnum)=val; +;} + break; + + case 110: + + { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner), + &val)); + (yyval.realnum)=val; +;} + break; + + case 111: + + { + IGRAPH_YY_CHECK(make_dynstr((yyvsp[(1) - (1)].string).str, (yyvsp[(1) - (1)].string).len, &(yyval.dynstr))); +;} + break; + + case 112: + + { (yyval.string)=(yyvsp[(1) - (1)].string); ;} + break; + + case 113: + + { (yyval.string).str=igraph_pajek_yyget_text(scanner); + (yyval.string).len=igraph_pajek_yyget_leng(scanner); ;} + break; + + case 114: + + { (yyval.string).str=igraph_pajek_yyget_text(scanner); + (yyval.string).len=igraph_pajek_yyget_leng(scanner); ;} + break; + + case 115: + + { (yyval.string).str=igraph_pajek_yyget_text(scanner)+1; + (yyval.string).len=igraph_pajek_yyget_leng(scanner)-2; ;} + break; + + +/* Line 1267 of yacc.c. */ + + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (&yylloc, context, yymsg); + } + else + { + yyerror (&yylloc, context, YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + yyerror_range[0] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[0] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[0] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + yyerror_range[1] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the look-ahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + + + +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; +} + +/* TODO: NA's */ + +static igraph_error_t add_numeric_attribute(igraph_trie_t *names, + igraph_vector_ptr_t *attrs, + igraph_integer_t count, + const char *attrname, + igraph_integer_t elem_id, + igraph_real_t number) { + igraph_integer_t attrsize = igraph_trie_size(names); + igraph_integer_t id; + igraph_vector_t *na; + igraph_attribute_record_t *rec; + + IGRAPH_CHECK(igraph_trie_get(names, attrname, &id)); + if (id == attrsize) { + /* add a new attribute */ + rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + CHECK_OOM_RP(rec); + IGRAPH_FINALLY(igraph_free, rec); + + na = IGRAPH_CALLOC(1, igraph_vector_t); + CHECK_OOM_RP(na); + IGRAPH_FINALLY(igraph_free, na); + IGRAPH_VECTOR_INIT_FINALLY(na, count); + + rec->name = strdup(attrname); + CHECK_OOM_RP(rec->name); + IGRAPH_FINALLY(igraph_free, (void *) rec->name); + + rec->type = IGRAPH_ATTRIBUTE_NUMERIC; + rec->value = na; + + IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); + IGRAPH_FINALLY_CLEAN(4); /* ownership of rec transferred to attrs */ + } + + rec = VECTOR(*attrs)[id]; + na = (igraph_vector_t *) rec->value; + if (igraph_vector_size(na) == elem_id) { + IGRAPH_CHECK(igraph_vector_push_back(na, number)); + } else if (igraph_vector_size(na) < elem_id) { + igraph_integer_t origsize=igraph_vector_size(na); + IGRAPH_CHECK(igraph_vector_resize(na, elem_id+1)); + for (;origsize 21) { + IGRAPH_ERROR("Too many attributes in Pajek file.", IGRAPH_PARSEERROR); + } +#endif + + /* add a new attribute */ + rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + CHECK_OOM_RP(rec); + IGRAPH_FINALLY(igraph_free, rec); + + na = IGRAPH_CALLOC(1, igraph_strvector_t); + CHECK_OOM_RP(na); + IGRAPH_FINALLY(igraph_free, na); + IGRAPH_STRVECTOR_INIT_FINALLY(na, count); + + rec->name = strdup(attrname); + CHECK_OOM_RP(rec->name); + IGRAPH_FINALLY(igraph_free, (char *) rec->name); + + rec->type = IGRAPH_ATTRIBUTE_STRING; + rec->value = na; + + IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); + IGRAPH_FINALLY_CLEAN(4); /* ownership of rec transferred to attrs */ + } + + rec = VECTOR(*attrs)[id]; + na = (igraph_strvector_t *) rec->value; + if (igraph_strvector_size(na) <= elem_id) { + IGRAPH_CHECK(igraph_strvector_resize(na, elem_id+1)); + } + IGRAPH_CHECK(igraph_strvector_set_len(na, elem_id, str, str_len)); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t add_string_vertex_attribute(const char *name, + const char *value, + size_t len, + igraph_i_pajek_parsedata_t *context) { + + return add_string_attribute(context->vertex_attribute_names, + context->vertex_attributes, + context->vcount, + name, context->actvertex-1, + value, len); +} + +static igraph_error_t add_string_edge_attribute(const char *name, + const char *value, + size_t len, + igraph_i_pajek_parsedata_t *context) { + + return add_string_attribute(context->edge_attribute_names, + context->edge_attributes, + context->actedge, + name, context->actedge-1, + value, len); +} + +static igraph_error_t add_numeric_vertex_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context) { + + return add_numeric_attribute(context->vertex_attribute_names, + context->vertex_attributes, + context->vcount, + name, context->actvertex-1, + value); +} + +static igraph_error_t add_numeric_edge_attribute(const char *name, + igraph_real_t value, + igraph_i_pajek_parsedata_t *context) { + + return add_numeric_attribute(context->edge_attribute_names, + context->edge_attributes, + context->actedge, + name, context->actedge-1, + value); +} + +static igraph_error_t 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; + igraph_integer_t n=context->vcount, n1=context->vcount2; + igraph_integer_t attrid, attrsize = igraph_trie_size(names); + igraph_attribute_record_t *rec; + igraph_vector_bool_t *na; + + if (n1 > n) { + IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file.", + IGRAPH_PARSEERROR); + } + + IGRAPH_CHECK(igraph_trie_get(names, attrname, &attrid)); + + /* It should not be possible for the "type" attribute to be already + * present at this point. */ + IGRAPH_ASSERT(attrid == attrsize); + + /* add a new attribute */ + rec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + CHECK_OOM_RP(rec); + IGRAPH_FINALLY(igraph_free, rec); + + na = IGRAPH_CALLOC(1, igraph_vector_bool_t); + CHECK_OOM_RP(na); + IGRAPH_FINALLY(igraph_free, na); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(na, n); + + rec->name = strdup(attrname); + CHECK_OOM_RP(rec->name); + IGRAPH_FINALLY(igraph_free, (char *) rec->name); + + rec->type = IGRAPH_ATTRIBUTE_BOOLEAN; + rec->value = na; + + IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); + IGRAPH_FINALLY_CLEAN(4); /* ownership of 'rec' transferred to 'attrs' */ + + for (igraph_integer_t i=0; ivector; + igraph_integer_t n1=context->vcount2; + igraph_integer_t ne=igraph_vector_int_size(edges); + + for (igraph_integer_t i=0; i n1 && v2 > n1) ) { + IGRAPH_WARNING("Invalid edge in bipartite graph."); + } + } + + return IGRAPH_SUCCESS; +} + +/* Check if attrname is a standard vertex attribute name used by igraph + for Pajek data. All of these must be listed here to prevent overwriting + standard attributes, or crashes due to incompatible attribute types. */ +static igraph_bool_t is_standard_vattr(const char *attrname) { + const char *names[] = { + /* vertex names: */ + "id", /* TODO: remove for 0.11 */ "name", + /* other vertex attributes: */ + "type", "x", "y", "z", + /* vertex parameters: */ + "xfact", "yfact", + "labeldist", "labeldegree2", "framewidth", + "fontsize", "rotation", "radius", + "diamondratio", "labeldegree", + "font", "url", "color", "framecolor", + "labelcolor" + }; + for (size_t i=0; i < sizeof(names) / sizeof(names[0]); i++) { + if (strcmp(attrname, names[i]) == 0) { + return true; + } + } + return false; +} + +/* Check if attrname is a standard edge attribute name used by igraph + for Pajek data. All of these must be listed here to prevent overwriting + standard attributes, or crashes due to incompatible attribute types. */ +static igraph_bool_t is_standard_eattr(const char *attrname) { + const char *names[] = { + /* other edge attributes: */ + "weight", + /* edge parameters: */ + "arrowsize", "edgewidth", "hook1", "hook2", + "angle1", "angle2", "velocity1", "velocity2", + "arrowpos", "labelpos", "labelangle", + "labelangle2", "labeldegree", "fontsize", "font", + "arrowtype", "linepattern", "label", "labelcolor", + "color" + }; + for (size_t i=0; i < sizeof(names) / sizeof(names[0]); i++) { + if (strcmp(attrname, names[i]) == 0) { + return true; + } + } + return false; +} + +/* Add a _ character at the end of an attribute name to avoid conflict + * with standard Pajek attributes. */ +static igraph_error_t deconflict_attrname(char **attrname) { + size_t len = strlen(*attrname); + char *tmp = IGRAPH_REALLOC(*attrname, len+2, char); + CHECK_OOM_RP(tmp); + tmp[len] = '_'; + tmp[len+1] = '\0'; + *attrname = tmp; + return IGRAPH_SUCCESS; +} + diff --git a/src/io/parsers/pajek-parser.h b/src/io/parsers/pajek-parser.h new file mode 100644 index 0000000..2c06b65 --- /dev/null +++ b/src/io/parsers/pajek-parser.h @@ -0,0 +1,180 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + END = 0, + NEWLINE = 258, + NUM = 259, + ALNUM = 260, + QSTR = 261, + NETWORKLINE = 262, + VERTICESLINE = 263, + ARCSLINE = 264, + EDGESLINE = 265, + ARCSLISTLINE = 266, + EDGESLISTLINE = 267, + MATRIXLINE = 268, + ERROR = 269, + VP_X_FACT = 270, + VP_Y_FACT = 271, + VP_PHI = 272, + VP_R = 273, + VP_Q = 274, + VP_IC = 275, + VP_BC = 276, + VP_BW = 277, + VP_LC = 278, + VP_LA = 279, + VP_LR = 280, + VP_LPHI = 281, + VP_FOS = 282, + VP_FONT = 283, + VP_URL = 284, + EP_H1 = 285, + EP_H2 = 286, + EP_W = 287, + EP_C = 288, + EP_P = 289, + EP_A = 290, + EP_S = 291, + EP_A1 = 292, + EP_K1 = 293, + EP_A2 = 294, + EP_K2 = 295, + EP_AP = 296, + EP_L = 297, + EP_LP = 298, + EP_LR = 299, + EP_LPHI = 300, + EP_LC = 301, + EP_LA = 302, + EP_FOS = 303, + EP_FONT = 304 + }; +#endif +/* Tokens. */ +#define END 0 +#define NEWLINE 258 +#define NUM 259 +#define ALNUM 260 +#define QSTR 261 +#define NETWORKLINE 262 +#define VERTICESLINE 263 +#define ARCSLINE 264 +#define EDGESLINE 265 +#define ARCSLISTLINE 266 +#define EDGESLISTLINE 267 +#define MATRIXLINE 268 +#define ERROR 269 +#define VP_X_FACT 270 +#define VP_Y_FACT 271 +#define VP_PHI 272 +#define VP_R 273 +#define VP_Q 274 +#define VP_IC 275 +#define VP_BC 276 +#define VP_BW 277 +#define VP_LC 278 +#define VP_LA 279 +#define VP_LR 280 +#define VP_LPHI 281 +#define VP_FOS 282 +#define VP_FONT 283 +#define VP_URL 284 +#define EP_H1 285 +#define EP_H2 286 +#define EP_W 287 +#define EP_C 288 +#define EP_P 289 +#define EP_A 290 +#define EP_S 291 +#define EP_A1 292 +#define EP_K1 293 +#define EP_A2 294 +#define EP_K2 295 +#define EP_AP 296 +#define EP_L 297 +#define EP_LP 298 +#define EP_LR 299 +#define EP_LPHI 300 +#define EP_LC 301 +#define EP_LA 302 +#define EP_FOS 303 +#define EP_FONT 304 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE + +{ + igraph_integer_t intnum; + igraph_real_t realnum; + struct { + char *str; + size_t len; + } string; + char *dynstr; +} +/* Line 1529 of yacc.c. */ + + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + diff --git a/src/isomorphism/bliss.cc b/src/isomorphism/bliss.cc new file mode 100644 index 0000000..fedc3c3 --- /dev/null +++ b/src/isomorphism/bliss.cc @@ -0,0 +1,629 @@ +/* + 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 "core/exceptions.h" + +#include +#include + +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) { + igraph_integer_t nof_vertices = igraph_vcount(graph); + igraph_integer_t nof_edges = igraph_ecount(graph); + + if (nof_vertices > UINT_MAX || nof_edges > UINT_MAX) { + throw std::runtime_error("Graph too large for BLISS"); + } + + AbstractGraph *g; + + if (igraph_is_directed(graph)) { + g = new Digraph(static_cast(nof_vertices)); + } else { + g = new Graph(static_cast(nof_vertices)); + } + + /* g->set_verbose_level(0); */ + + for (unsigned int i = 0; i < static_cast(nof_edges); i++) { + g->add_edge( + static_cast(IGRAPH_FROM(graph, i)), + static_cast(IGRAPH_TO(graph, i)) + ); + } + + return g; +} + + +void bliss_free_graph(AbstractGraph *g) { + delete g; +} + + +inline igraph_error_t 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 igraph_error_t 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) { + igraph_integer_t color = VECTOR(*colors)[i]; + if (color < INT_MIN || color > INT_MAX) { + IGRAPH_ERRORF("Invalid vertex color index %" IGRAPH_PRId " for vertex %d.", IGRAPH_EOVERFLOW, color, i); + } + g->change_color(i, static_cast(color)); + } + return IGRAPH_SUCCESS; +} + + +inline igraph_error_t 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); /* LCOV_EXCL_LINE */ + } + 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_int_list_t *generators; + +public: + AutCollector(igraph_vector_int_list_t *generators_) : generators(generators_) { } + + void operator ()(unsigned int n, const unsigned int *aut) { + igraph_vector_int_t newvector; + igraph_error_t err; + + err = igraph_vector_int_init(&newvector, n); + if (err != IGRAPH_SUCCESS) { + throw bad_alloc(); + } + + copy(aut, aut + n, VECTOR(newvector)); // takes care of unsigned int -> igraph_integer_t conversion + + err = igraph_vector_int_list_push_back(generators, &newvector); + if (err != IGRAPH_SUCCESS) { + 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 \ref igraph_is_same_graph() + * + * Time complexity: exponential, in practice it is fast for many graphs. + */ +igraph_error_t igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_vector_int_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_int_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 (deprecated alias). + * + * \deprecated-by igraph_count_automorphisms 0.10.5 + */ +igraph_error_t igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { + return igraph_count_automorphisms(graph, colors, sh, info); +} + +/** + * \function igraph_count_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. 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. + */ +igraph_error_t igraph_count_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 interger vector list. + * The generators of the automorphism group will be stored here. + * \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. + */ +igraph_error_t igraph_automorphism_group( + const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_int_list_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_int_list_clear(generators); + 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. + */ +igraph_error_t 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_int_t *map12, + igraph_vector_int_t *map21, igraph_bliss_sh_t sh, + igraph_bliss_info_t *info1, igraph_bliss_info_t *info2) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph1); + igraph_integer_t no_of_edges = igraph_ecount(graph1); + igraph_vector_int_t perm1, perm2; + igraph_vector_int_t vmap12, *mymap12 = &vmap12; + igraph_vector_int_t from, to, index; + igraph_vector_int_t from2, to2, index2; + igraph_bool_t directed; + igraph_integer_t 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_int_clear(map12); + } + if (map21) { + igraph_vector_int_clear(map21); + } + return IGRAPH_SUCCESS; + } + + if (map12) { + mymap12 = map12; + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(mymap12, 0); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&perm1, no_of_nodes); + IGRAPH_VECTOR_INT_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_int_resize(mymap12, no_of_nodes)); + + /* The inverse of perm2 is produced in mymap12 */ + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*mymap12)[ 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)[ VECTOR(perm1)[i] ]; + } + /* Copy it to mymap12 */ + IGRAPH_CHECK(igraph_vector_int_update(mymap12, &perm2)); + + igraph_vector_int_destroy(&perm1); + igraph_vector_int_destroy(&perm2); + IGRAPH_FINALLY_CLEAN(2); + + /* Check isomorphism, we apply the permutation in mymap12 to graph1 + and should get graph2 */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&from, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&to, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&from2, no_of_edges * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&to2, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&index2, no_of_edges); + + for (i = 0; i < no_of_edges; i++) { + VECTOR(from)[i] = VECTOR(*mymap12)[ IGRAPH_FROM(graph1, i) ]; + VECTOR(to)[i] = VECTOR(*mymap12)[ IGRAPH_TO (graph1, i) ]; + if (! directed && VECTOR(from)[i] < VECTOR(to)[i]) { + igraph_integer_t tmp = VECTOR(from)[i]; + VECTOR(from)[i] = VECTOR(to)[i]; + VECTOR(to)[i] = tmp; + } + } + igraph_vector_int_pair_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_integer_t tmp = VECTOR(from2)[i]; + VECTOR(from2)[i] = VECTOR(to2)[i]; + VECTOR(to2)[i] = tmp; + } + } + igraph_vector_int_resize(&from2, no_of_edges); + igraph_vector_int_pair_order(&from2, &to2, &index2, no_of_nodes); + + *iso = 1; + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t i1 = VECTOR(index)[i]; + igraph_integer_t i2 = 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)[ VECTOR(*mymap12)[i] ]) { + *iso = 0; + break; + } + } + } + + igraph_vector_int_destroy(&index2); + igraph_vector_int_destroy(&to2); + igraph_vector_int_destroy(&from2); + igraph_vector_int_destroy(&index); + igraph_vector_int_destroy(&to); + igraph_vector_int_destroy(&from); + IGRAPH_FINALLY_CLEAN(6); + + if (*iso) { + /* The inverse of mymap12 */ + if (map21) { + IGRAPH_CHECK(igraph_vector_int_resize(map21, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*map21)[ VECTOR(*mymap12)[i] ] = i; + } + } + } else { + if (map12) { + igraph_vector_int_clear(map12); + } + if (map21) { + igraph_vector_int_clear(map21); + } + } + + if (!map12) { + igraph_vector_int_destroy(mymap12); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/isomorphism/bliss/CMakeLists.txt b/src/isomorphism/bliss/CMakeLists.txt new file mode 100644 index 0000000..1378f9d --- /dev/null +++ b/src/isomorphism/bliss/CMakeLists.txt @@ -0,0 +1,43 @@ +# Declare the files needed to compile bliss +add_library( + bliss + OBJECT + EXCLUDE_FROM_ALL + defs.cc + graph.cc + heap.cc + orbit.cc + partition.cc + uintseqhash.cc + utils.cc +) + +target_include_directories( + bliss + PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/vendor + ${PROJECT_BINARY_DIR}/include + ${PROJECT_BINARY_DIR}/src + $<$:${GMP_INCLUDE_DIR}> +) + +if (BUILD_SHARED_LIBS) + set_property(TARGET bliss PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +# Since these are included as object files, they should call the +# function as is (without visibility specification) +target_compile_definitions(bliss PRIVATE IGRAPH_STATIC) + +use_all_warnings(bliss) + +if (MSVC) + target_compile_options(bliss PRIVATE /wd4100) # disable unreferenced parameter warning +else() + target_compile_options( + bliss PRIVATE + $<$:-Wno-unused-variable> + ) +endif() diff --git a/src/isomorphism/bliss/bignum.hh b/src/isomorphism/bliss/bignum.hh new file mode 100644 index 0000000..546da5b --- /dev/null +++ b/src/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/isomorphism/bliss/defs.cc b/src/isomorphism/bliss/defs.cc new file mode 100644 index 0000000..9760006 --- /dev/null +++ b/src/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/isomorphism/bliss/defs.hh b/src/isomorphism/bliss/defs.hh new file mode 100644 index 0000000..51b8246 --- /dev/null +++ b/src/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/isomorphism/bliss/graph.cc b/src/isomorphism/bliss/graph.cc new file mode 100644 index 0000000..7260423 --- /dev/null +++ b/src/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/isomorphism/bliss/graph.hh b/src/isomorphism/bliss/graph.hh new file mode 100644 index 0000000..5a60389 --- /dev/null +++ b/src/isomorphism/bliss/graph.hh @@ -0,0 +1,876 @@ +#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 static_cast(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 static_cast(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 static_cast(edges_in.size()); } + unsigned int nof_edges_out() const { return static_cast(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 static_cast(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/isomorphism/bliss/heap.cc b/src/isomorphism/bliss/heap.cc new file mode 100644 index 0000000..0e57893 --- /dev/null +++ b/src/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/isomorphism/bliss/heap.hh b/src/isomorphism/bliss/heap.hh new file mode 100644 index 0000000..bb84cd4 --- /dev/null +++ b/src/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/isomorphism/bliss/igraph-changes.md b/src/isomorphism/bliss/igraph-changes.md new file mode 100644 index 0000000..3c7058b --- /dev/null +++ b/src/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/isomorphism/bliss/kqueue.hh b/src/isomorphism/bliss/kqueue.hh new file mode 100644 index 0000000..06de3b4 --- /dev/null +++ b/src/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/isomorphism/bliss/kstack.hh b/src/isomorphism/bliss/kstack.hh new file mode 100644 index 0000000..ecb9b83 --- /dev/null +++ b/src/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/isomorphism/bliss/orbit.cc b/src/isomorphism/bliss/orbit.cc new file mode 100644 index 0000000..cb73862 --- /dev/null +++ b/src/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/isomorphism/bliss/orbit.hh b/src/isomorphism/bliss/orbit.hh new file mode 100644 index 0000000..1d59445 --- /dev/null +++ b/src/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/isomorphism/bliss/partition.cc b/src/isomorphism/bliss/partition.cc new file mode 100644 index 0000000..d0b2d52 --- /dev/null +++ b/src/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/isomorphism/bliss/partition.hh b/src/isomorphism/bliss/partition.hh new file mode 100644 index 0000000..52cefda --- /dev/null +++ b/src/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/isomorphism/bliss/stats.hh b/src/isomorphism/bliss/stats.hh new file mode 100644 index 0000000..5294581 --- /dev/null +++ b/src/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/isomorphism/bliss/uintseqhash.cc b/src/isomorphism/bliss/uintseqhash.cc new file mode 100644 index 0000000..3e6f2b3 --- /dev/null +++ b/src/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/isomorphism/bliss/uintseqhash.hh b/src/isomorphism/bliss/uintseqhash.hh new file mode 100644 index 0000000..4ac4e01 --- /dev/null +++ b/src/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/isomorphism/bliss/utils.cc b/src/isomorphism/bliss/utils.cc new file mode 100644 index 0000000..e2913cb --- /dev/null +++ b/src/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/isomorphism/bliss/utils.hh b/src/isomorphism/bliss/utils.hh new file mode 100644 index 0000000..4f3514a --- /dev/null +++ b/src/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/isomorphism/isoclasses.c b/src/isomorphism/isoclasses.c new file mode 100644 index 0000000..27162e9 --- /dev/null +++ b/src/isomorphism/isoclasses.c @@ -0,0 +1,2933 @@ +/* -*- 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. + * These values can also be retrieved using \ref igraph_graph_count(). + * 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. + */ +igraph_error_t igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) { + igraph_integer_t e; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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. + */ +igraph_error_t igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_int_t *vids, + igraph_integer_t *isoclass) { + igraph_integer_t subgraph_size = igraph_vector_int_size(vids); + igraph_vector_int_t neis; + + unsigned int mul, idx; + const unsigned int *arr_idx, *arr_code; + unsigned int code = 0; + + igraph_integer_t i, j, s; + + IGRAPH_VECTOR_INT_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++) { + igraph_integer_t from = VECTOR(*vids)[i]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, IGRAPH_OUT)); + s = igraph_vector_int_size(&neis); + for (j = 0; j < s; j++) { + igraph_integer_t nei = VECTOR(neis)[j], to; + if (igraph_vector_int_search(vids, 0, nei, &to)) { + idx = (mul * i + to); + code |= arr_idx[idx]; + } + } + } + + *isoclass = arr_code[code]; + igraph_vector_int_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. + */ +igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, + igraph_integer_t number, igraph_bool_t directed) { + igraph_vector_int_t edges; + const unsigned int *classedges; + igraph_integer_t graphcount; + igraph_integer_t pos; + unsigned int power; + unsigned int code; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + +#define CHECK_ISOCLASS(number, directed, size, graphcount) \ + IGRAPH_ERRORF( \ + "Isoclass %" IGRAPH_PRId " requested, but there are only %" \ + IGRAPH_PRId " %s graphs of size %" IGRAPH_PRId ".", IGRAPH_EINVAL, \ + 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[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[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[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[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[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[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_int_push_back(&edges, classedges[2 * pos])); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, classedges[2 * pos + 1])); + code -= power; + } + power /= 2; + pos++; + } + + IGRAPH_CHECK(igraph_create(graph, &edges, size, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* https://oeis.org/A000088 */ +static igraph_integer_t undirected_graph_counts[] = { + 1, 1, 2, 4, 11, 34, 156, 1044, 12346, 274668, 12005168, 1018997864, +#if IGRAPH_INTEGER_SIZE == 64 + 165091172592, 50502031367952, 29054155657235488 +#endif +}; + +/* https://oeis.org/A000273 */ +static igraph_integer_t directed_graph_counts[] = { + 1, 1, 3, 16, 218, 9608, 1540944, 882033440, +#if IGRAPH_INTEGER_SIZE == 64 + 1793359192848, 13027956824399552 +#endif +}; + +/** + * \function igraph_graph_count + * \brief The number of unlabelled graphs on the given number of vertices. + * + * Gives the number of unlabelled \em simple graphs on the specified number of vertices. + * The "isoclass" of a graph of this size is at most one less than this value. + * + * + * This function is meant to be used in conjunction with isoclass and motif finder + * functions. It will only work for small \p n values for which the result is + * represetable in an \type igraph_integer_t. For larger \p n values, an overflow + * error is raised. + * + * \param n The number of vertices. + * \param directed Boolean, whether to consider directed graphs. + * \param count Pointer to an integer, the result will be stored here. + * \return Error code. + * + * \sa \ref igraph_isoclass(), \ref igraph_motifs_randesu_callback(). + * + * Time complexity: O(1). + */ +igraph_error_t igraph_graph_count(igraph_integer_t n, igraph_bool_t directed, igraph_integer_t *count) { + if (n < 0) { + IGRAPH_ERROR("Graph size must not be negative.", IGRAPH_EINVAL); + } + if (directed) { + if (n >= (igraph_integer_t) (sizeof directed_graph_counts / sizeof directed_graph_counts[0])) { + IGRAPH_ERRORF("Graph size of % " IGRAPH_PRId " too large.", IGRAPH_EOVERFLOW, n); + } + *count = directed_graph_counts[n]; + } else { + if (n >= (igraph_integer_t) (sizeof undirected_graph_counts / sizeof undirected_graph_counts[0])) { + IGRAPH_ERRORF("Graph size of % " IGRAPH_PRId " too large.", IGRAPH_EOVERFLOW, n); + } + *count = undirected_graph_counts[n]; + } + return IGRAPH_SUCCESS; +} diff --git a/src/isomorphism/isoclasses.h b/src/isomorphism/isoclasses.h new file mode 100644 index 0000000..67fa9a2 --- /dev/null +++ b/src/isomorphism/isoclasses.h @@ -0,0 +1,46 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + 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 + 02110-1301 USA + +*/ + +#ifndef IGRAPH_ISOCLASSES_H +#define IGRAPH_ISOCLASSES_H + +#include "igraph_decls.h" + +__BEGIN_DECLS + +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 + +#endif diff --git a/src/isomorphism/isomorphism_misc.c b/src/isomorphism/isomorphism_misc.c new file mode 100644 index 0000000..2b8ff8e --- /dev/null +++ b/src/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() + * + */ +igraph_error_t 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_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t pto = -1, pfrom = -1; + igraph_integer_t 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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_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)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t 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_int_push_back(&edges, from); + igraph_vector_int_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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/isomorphism/lad.c b/src/isomorphism/lad.c new file mode 100644 index 0000000..689c326 --- /dev/null +++ b/src/isomorphism/lad.c @@ -0,0 +1,1656 @@ +/* -*- 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_adjlist.h" +#include "igraph_bitset.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_matrix.h" +#include "igraph_qsort.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +#include "core/interruption.h" + +#include +#include +#include +#include + +/* 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); \ + IGRAPH_CHECK_OOM(VAR, "Cannot allocate '" #VAR "' array in LAD isomorphism search."); \ + 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); \ + IGRAPH_CHECK_OOM(VAR, "Cannot allocate '" #VAR "' array in LAD isomorphism search."); \ + IGRAPH_FINALLY(igraph_free, VAR); \ + IGRAPH_CHECK(igraph_vector_ptr_push_back(HISTORY, VAR)); \ + IGRAPH_FINALLY_CLEAN(1); \ + } + +/* ---------------------------------------------------------*/ +/* Coming from graph.c */ +/* ---------------------------------------------------------*/ + +#define ISEDGE(G, i, j) IGRAPH_BIT_TEST(G->isEdge, i * G->nbVertices + j) +#define SETEDGE(G, i, j) IGRAPH_BIT_SET(G->isEdge, i * G->nbVertices + j) + +typedef struct { + igraph_integer_t nbVertices; /* Number of vertices */ + igraph_vector_int_t nbSucc; + igraph_adjlist_t succ; + igraph_bitset_t isEdge; +} Tgraph; + +static igraph_error_t igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { + igraph_integer_t i, j, n; + igraph_integer_t 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_INT_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_BITSET_INIT_FINALLY(&graph->isEdge, no_of_nodes * no_of_nodes); + + 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++) { + igraph_integer_t v = VECTOR(*neis)[j]; + if (ISEDGE(graph, i, v)) { + IGRAPH_ERROR("LAD functions do not support graphs with multi-edges.", IGRAPH_EINVAL); + } + SETEDGE(graph, i, v); + } + } + + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_lad_destroyGraph(Tgraph *graph) { + igraph_bitset_destroy(&graph->isEdge); + igraph_adjlist_destroy(&graph->succ); + igraph_vector_int_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] */ + igraph_integer_t 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) */ + igraph_integer_t nextOutToFilter; /* position in toFilter of the next pattern node whose + domain should be filtered (-1 if no domain to + filter) */ + igraph_integer_t 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_bitset_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_bitset_null(&D->markedToFilter); + D->nextOutToFilter = -1; +} + + +static igraph_integer_t igraph_i_lad_nextToFilter(Tdomain* D, igraph_integer_t size) { + /* precondition: emptyToFilter = false + remove a node from toFilter (FIFO) + unmark this node and return it */ + igraph_integer_t u = VECTOR(D->toFilter)[D->nextOutToFilter]; + IGRAPH_BIT_CLEAR(D->markedToFilter, u); + 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(igraph_integer_t u, Tdomain* D, igraph_integer_t size) { + /* if u is not marked, then add it to toFilter and mark it */ + if (IGRAPH_BIT_TEST(D->markedToFilter, u)) { + return; + } + IGRAPH_BIT_SET(D->markedToFilter, u); + 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(igraph_integer_t u, igraph_integer_t 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 igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D, igraph_integer_t 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 */ + igraph_integer_t *fifo, *pred; + igraph_bitset_t marked; + igraph_integer_t nextIn = 0; + igraph_integer_t nextOut = 0; + igraph_integer_t i, v, v2, u2; + + *result = false; + + /* Allocate memory */ + ALLOC_ARRAY(fifo, nbV, igraph_integer_t); + ALLOC_ARRAY(pred, nbV, igraph_integer_t); + IGRAPH_BITSET_INIT_FINALLY(&marked, nbV); + + 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; + IGRAPH_BIT_SET(marked, v); + } + 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 (!IGRAPH_BIT_TEST(marked, v)) { /* v is not free and not marked => add it to fifo */ + pred[v] = u2; + fifo[nextIn++] = v; + IGRAPH_BIT_SET(marked, v); + } + } + } + +cleanup: + igraph_free(fifo); + igraph_free(pred); + igraph_bitset_destroy(&marked); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_lad_removeAllValuesButOne(igraph_integer_t u, igraph_integer_t 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 */ + igraph_integer_t j, oldPos, newPos; + igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u); + igraph_integer_t n = igraph_vector_int_size(uneis); + /* add all successors of u in toFilter */ + for (j = 0; j < n; j++) { + igraph_i_lad_addToFilter(VECTOR(*uneis)[j], D, + 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, Gt->nbVertices, result)); + } else { + *result = true; + } + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_lad_removeValue(igraph_integer_t u, igraph_integer_t 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 */ + igraph_integer_t j; + igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u); + igraph_integer_t n = igraph_vector_int_size(uneis); + igraph_integer_t oldPos, newPos; + + /* add all successors of u in toFilter */ + for (j = 0; j < n; j++) { + igraph_i_lad_addToFilter(VECTOR(*uneis)[j], D, + 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, Gt->nbVertices, result)); + } else { + *result = true; + } + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vector_int_t* toBeMatched, + bool induced, Tdomain* D, Tgraph* Gp, + Tgraph* Gt, igraph_bool_t *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; */ + igraph_integer_t 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 = true; + return IGRAPH_SUCCESS; + } + } + if (ISEDGE(Gp, 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 (ISEDGE(Gt, 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 = true; + return IGRAPH_SUCCESS; + } + } + } + } 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 (!ISEDGE(Gt, 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 = true; + return IGRAPH_SUCCESS; + } + } + } + } else { + for (j = 0; j < VECTOR(Gt->nbSucc)[v]; j++) { + if (igraph_i_lad_isInD(u2, VECTOR(*vneis)[j], D)) { + IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(*vneis)[j], D, Gp, Gt, &result)); + if (!result) { + *invalid = true; + return IGRAPH_SUCCESS; + } + } + } + } + } + if (VECTOR(D->nbVal)[u2] == 0) { + *invalid = true; /* D[u2] is empty */ + return IGRAPH_SUCCESS; + } + if ((VECTOR(D->nbVal)[u2] == 1) && (oldNbVal > 1)) { + VECTOR(*toBeMatched)[nb++] = u2; + } + } + } + } + *invalid = false; + return IGRAPH_SUCCESS; +} + + +static bool igraph_i_lad_matchVertex(igraph_integer_t u, bool induced, Tdomain* D, Tgraph* Gp, + Tgraph *Gt) { + igraph_bool_t 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_FINALLY(&toBeMatched, Gp->nbVertices); + 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; +} + + +static int igraph_i_lad_qcompare (void const *a, void const *b) { + /* function used by the qsort function */ + igraph_integer_t pa = ((*((igraph_integer_t*)a) - *((igraph_integer_t*)b))); + if (pa < 0) { + return -1; + } else if (pa > 0) { + return 1; + } + return 0; +} + +static bool igraph_i_lad_compare(igraph_integer_t size_mu, igraph_integer_t* mu, igraph_integer_t size_mv, igraph_integer_t* 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 */ + igraph_integer_t i, j; + igraph_qsort(mu, (size_t) size_mu, sizeof(mu[0]), igraph_i_lad_qcompare); + igraph_qsort(mv, (size_t) size_mv, sizeof(mv[0]), 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 igraph_error_t igraph_i_lad_initDomains(bool initialDomains, + const igraph_vector_int_list_t *domains, Tdomain *D, + const Tgraph *Gp, const Tgraph *Gt, igraph_bool_t *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 */ + igraph_integer_t *val; + igraph_bitset_t dom; + igraph_integer_t *mu, *mv; + igraph_integer_t matchingSize, u, v, i, j; + igraph_vector_int_t *vec; + + ALLOC_ARRAY(val, Gp->nbVertices * Gt->nbVertices, igraph_integer_t); + IGRAPH_BITSET_INIT_FINALLY(&dom, Gt->nbVertices); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingP, Gp->nbVertices); + igraph_vector_int_fill(&D->globalMatchingP, -1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingT, Gt->nbVertices); + igraph_vector_int_fill(&D->globalMatchingT, -1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->nbVal, Gp->nbVertices); + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->firstVal, Gp->nbVertices); + IGRAPH_MATRIX_INT_INIT_FINALLY(&D->posInVal, + Gp->nbVertices, Gt->nbVertices); + IGRAPH_MATRIX_INT_INIT_FINALLY(&D->firstMatch, + Gp->nbVertices, Gt->nbVertices); + IGRAPH_BITSET_INIT_FINALLY(&D->markedToFilter, Gp->nbVertices); + 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 = igraph_vector_int_list_get_ptr(domains, u); + i = igraph_vector_int_size(vec); + igraph_bitset_null(&dom); + for (j = 0; j < i; j++) { + v = VECTOR(*vec)[j]; + IGRAPH_BIT_SET(dom, v); + } + } + IGRAPH_BIT_SET(D->markedToFilter, u); + 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) && (!IGRAPH_BIT_TEST(dom, v))) { /* v not in D(u) */ + MATRIX(D->posInVal, u, v) = 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(VECTOR(Gp->nbSucc)[u], igraph_integer_t); + IGRAPH_CHECK_OOM(mu, "Insufficient memory for subisomorphism search with LAD."); + IGRAPH_FINALLY(igraph_free, mu); + + mv = IGRAPH_CALLOC(VECTOR(Gt->nbSucc)[v], igraph_integer_t); + IGRAPH_CHECK_OOM(mu, "Insufficient memory for subisomorphism search with LAD."); + IGRAPH_FINALLY(igraph_free, mv); + + for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { + mu[i] = VECTOR(Gp->nbSucc)[VECTOR(*Gp_uneis)[i]]; + } + for (i = 0; i < VECTOR(Gt->nbSucc)[v]; i++) { + mv[i] = VECTOR(Gt->nbSucc)[VECTOR(*Gt_vneis)[i]]; + } + if (igraph_i_lad_compare(VECTOR(Gp->nbSucc)[u], mu, + VECTOR(Gt->nbSucc)[v], mv)) { + 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) = + VECTOR(D->firstVal)[u] + Gt->nbVertices; + } + IGRAPH_FREE(mu); + IGRAPH_FREE(mv); + IGRAPH_FINALLY_CLEAN(2); + } else { /* v not in D(u) */ + MATRIX(D->posInVal, u, v) = VECTOR(D->firstVal)[u] + Gt->nbVertices; + } + } + } + if (VECTOR(D->nbVal)[u] == 0) { + *empty = true; /* empty domain */ + + igraph_free(val); + igraph_bitset_destroy(&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 = Gp->nbVertices - 1; + + *empty = false; + + igraph_free(val); + igraph_bitset_destroy(&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_bitset_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(igraph_integer_t u, igraph_integer_t* list, igraph_integer_t* nb, igraph_integer_t* marked) { + if (marked[u] < toBeDeleted) { + list[(*nb)++] = u; + marked[u] = toBeDeleted; + } +} + +static igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igraph_integer_t sizeOfV, + igraph_vector_int_t *degree, + igraph_vector_int_t *firstAdj, + igraph_vector_int_t *adj, + igraph_vector_int_t * matchedWithU, + igraph_bool_t *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 */ + + igraph_integer_t *matchedWithV; /* matchedWithV[matchedWithU[u]]=u */ + igraph_integer_t *nbPred; /* nbPred[i] = nb of predecessors of the ith + vertex of V in the DAG */ + igraph_integer_t *pred; /* pred[i][j] = jth predecessor the ith + vertex of V in the DAG */ + igraph_integer_t *nbSucc; /* nbSucc[i] = nb of successors of the ith + vertex of U in the DAG */ + igraph_integer_t *succ; /* succ[i][j] = jth successor of the ith + vertex of U in the DAG */ + igraph_integer_t *listV, *listU, *listDV, *listDU; + igraph_integer_t nbV, nbU, nbDV, nbDU; + igraph_integer_t i, j, k, stop, u, v; + igraph_integer_t *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 */ + igraph_integer_t nbUnmatched = 0; /* number of vertices of U that are not matched */ + igraph_integer_t *unmatched; /* vertices of U that are not matched */ + igraph_integer_t *posInUnmatched; /* unmatched[posInUnmatched[u]]=u */ + igraph_vector_int_t path; + + if (sizeOfU > sizeOfV) { + *invalid = true; /* trivial case of infeasibility */ + return IGRAPH_SUCCESS; + } + + ALLOC_ARRAY(matchedWithV, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(nbPred, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(pred, sizeOfV * sizeOfU, igraph_integer_t); + ALLOC_ARRAY(nbSucc, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(succ, sizeOfU * sizeOfV, igraph_integer_t); + ALLOC_ARRAY(listV, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(listU, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(listDV, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(listDU, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(markedV, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(markedU, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(unmatched, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(posInUnmatched, sizeOfU, igraph_integer_t); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&path, 0); + + /* initialize matchedWithV and unmatched */ + memset(matchedWithV, -1, (size_t)sizeOfV * sizeof(matchedWithV[0])); + 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(markedU[0])); + memset(nbSucc, 0, (size_t) sizeOfU * sizeof(nbSucc[0])); + memset(markedV, white, (size_t) sizeOfV * sizeof(markedV[0])); + memset(nbPred, 0, (size_t) sizeOfV * sizeof(nbPred[0])); + /* 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 = true; + /* 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); + } + 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 = false; + +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 IGRAPH_SUCCESS; +} + +static void igraph_i_lad_DFS(igraph_integer_t nbU, igraph_integer_t nbV, igraph_integer_t u, igraph_bitset_t *marked, igraph_integer_t* nbSucc, + igraph_integer_t* succ, igraph_vector_int_t * matchedWithU, + igraph_integer_t* order, igraph_integer_t* 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 */ + igraph_integer_t i; + igraph_integer_t v = VECTOR(*matchedWithU)[u]; /* the only one predecessor of v is u */ + IGRAPH_BIT_SET(*marked, u); + if (v >= 0) { + for (i = 0; i < nbSucc[v]; i++) { + if (!IGRAPH_BIT_TEST(*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 igraph_error_t igraph_i_lad_SCC(igraph_integer_t nbU, igraph_integer_t nbV, igraph_integer_t* numV, igraph_integer_t* numU, + igraph_integer_t* nbSucc, igraph_integer_t* succ, + igraph_integer_t* nbPred, igraph_integer_t* 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 */ + igraph_integer_t *order; + igraph_bitset_t marked; + igraph_integer_t *fifo; + igraph_integer_t u, v, i, j, k, nbSCC, nb; + + /* Allocate memory */ + ALLOC_ARRAY(order, nbU, igraph_integer_t); + IGRAPH_BITSET_INIT_FINALLY(&marked, nbU); + ALLOC_ARRAY(fifo, nbV, igraph_integer_t); + + /* Order vertices of Gp wrt DFS */ + nb = nbU - 1; + for (u = 0; u < nbU; u++) { + if (!IGRAPH_BIT_TEST(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(numU[0])); + memset(numV, -1, (size_t) nbV * sizeof(numV[0])); + 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_bitset_destroy(&marked); + igraph_free(order); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt, + Tdomain* D, igraph_bool_t *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} */ + igraph_integer_t *nbPred; /* nbPred[u] = nb of predecessors of u in Go */ + igraph_integer_t *pred; /* pred[u][i] = ith + predecessor of u in Go */ + igraph_integer_t *nbSucc; /* nbSucc[v] = nb of successors of v in Go */ + igraph_integer_t *succ; /* succ[v][i] = ith + successor of v in Go */ + igraph_integer_t u, v, i, w, oldNbVal, nbToMatch; + igraph_integer_t *numV, *numU; + igraph_vector_int_t toMatch; + igraph_bitset_t used; + igraph_integer_t *list; + igraph_integer_t nb = 0; + bool result; + + /* Allocate memory */ + ALLOC_ARRAY(nbPred, Gp->nbVertices, igraph_integer_t); + ALLOC_ARRAY(pred, Gp->nbVertices * Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(nbSucc, Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(succ, Gt->nbVertices * Gp->nbVertices, igraph_integer_t); + ALLOC_ARRAY(numV, Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(numU, Gp->nbVertices, igraph_integer_t); + IGRAPH_BITSET_INIT_FINALLY(&used, Gp->nbVertices * Gt->nbVertices); + ALLOC_ARRAY(list, Gt->nbVertices, igraph_integer_t); + IGRAPH_VECTOR_INT_INIT_FINALLY(&toMatch, Gp->nbVertices); + + 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) */ + IGRAPH_BIT_CLEAR(used, u * Gt->nbVertices + v); + 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]; + IGRAPH_BIT_SET(used, u * Gt->nbVertices + v); + if (numU[u] == false) { + numU[u] = true; + w = VECTOR(D->globalMatchingP)[u]; + IGRAPH_BIT_SET(used, u * Gt->nbVertices + w); + if (numV[w] == false) { + list[nb++] = w; + numV[w] = true; + } + } + } + } + + /* look for strongly connected components in Go */ + IGRAPH_CHECK( + igraph_i_lad_SCC(Gp->nbVertices, 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 ((!IGRAPH_BIT_TEST(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 = true; + /* Yes, this is ugly. */ + goto cleanup; + } + } + } + if (VECTOR(D->nbVal)[u] == 0) { + *invalid = true; + /* 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_bitset_destroy(&used); + igraph_free(numU); + igraph_free(numV); + igraph_free(succ); + igraph_free(nbSucc); + igraph_free(pred); + igraph_free(nbPred); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} + +/* ---------------------------------------------------------*/ +/* Coming from lad.c */ +/* ---------------------------------------------------------*/ + +static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t v, Tdomain* D, Tgraph* Gp, Tgraph* Gt, + bool *result) { + /* return true if G_(u, v) has a adj(u)-covering matching; false + otherwise */ + igraph_integer_t u2, v2, i, j; + igraph_integer_t nbMatched = 0; + igraph_vector_int_t *Gp_uneis = igraph_adjlist_get(&Gp->succ, u); + + igraph_integer_t *num, *numInv; + igraph_vector_int_t nbComp; + igraph_vector_int_t firstComp; + igraph_vector_int_t comp; + igraph_integer_t nbNum = 0; + igraph_integer_t posInComp = 0; + igraph_vector_int_t matchedWithU; + igraph_bool_t invalid; + + /* special case when u has only 1 adjacent node => no need to call + Hopcroft and Karp */ + if (VECTOR(Gp->nbSucc)[u] == 1) { + u2 = 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 IGRAPH_SUCCESS; + } + /* 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 (ISEDGE(Gt, v, VECTOR(D->val)[i])) { + VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ] = + VECTOR(D->val)[i]; + *result = true; + return IGRAPH_SUCCESS; + } + } + *result = false; + return IGRAPH_SUCCESS; + } + + /* 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 = 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 IGRAPH_SUCCESS; + } /* The matching still covers adj(u) */ + + /* Allocate memory */ + ALLOC_ARRAY(num, Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(numInv, Gt->nbVertices, igraph_integer_t); + + /* 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_VECTOR_INT_INIT_FINALLY(&nbComp, VECTOR(Gp->nbSucc)[u]); + IGRAPH_VECTOR_INT_INIT_FINALLY(&firstComp, VECTOR(Gp->nbSucc)[u]); + /* comp[firstComp[u]..firstComp[u]+nbComp[u]-1] = nodes of Gt that + are compatible with u */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&comp, (VECTOR(Gp->nbSucc)[u] * Gt->nbVertices)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&matchedWithU, VECTOR(Gp->nbSucc)[u]); + memset(num, -1, (size_t) (Gt->nbVertices) * sizeof(num[0])); + for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { + u2 = 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 (ISEDGE(Gt, 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 = 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(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 IGRAPH_SUCCESS; +} + +/* ---------------------------------------------------------*/ +/* Coming from main.c */ +/* ---------------------------------------------------------*/ + +static igraph_error_t 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 */ + igraph_integer_t u, v, i, oldNbVal; + igraph_bool_t invalid; + bool result2; + while (!igraph_i_lad_toFilterEmpty(D)) { + while (!igraph_i_lad_toFilterEmpty(D)) { + u = igraph_i_lad_nextToFilter(D, 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 IGRAPH_SUCCESS; + } + } + } + if ((VECTOR(D->nbVal)[u] == 1) && (oldNbVal > 1) && + (!igraph_i_lad_matchVertex(u, induced, D, Gp, Gt))) { + *result = false; + return IGRAPH_SUCCESS; + } + if (VECTOR(D->nbVal)[u] == 0) { + *result = false; + return IGRAPH_SUCCESS; + } + } + igraph_i_lad_ensureGACallDiff(induced, Gp, Gt, D, &invalid); + if (invalid) { + *result = false; + return IGRAPH_SUCCESS; + } + } + *result = true; + return IGRAPH_SUCCESS; +} + + + +static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstSol, bool induced, + Tdomain* D, Tgraph* Gp, Tgraph* Gt, + igraph_bool_t *invalid, igraph_bool_t *iso, + igraph_vector_int_t *vec, igraph_vector_int_t *map, igraph_vector_int_list_t *maps, + igraph_integer_t *nbNodes, igraph_integer_t *nbFail, igraph_integer_t *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 */ + + igraph_integer_t u, v, minDom, i; + igraph_integer_t* nbVal; + igraph_integer_t* globalMatching; + clock_t end = clock(); + igraph_integer_t* 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, igraph_integer_t, alloc_history); + ALLOC_ARRAY_IN_HISTORY(globalMatching, Gp->nbVertices, igraph_integer_t, 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 = false; + 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 = true; + } + (*nbSol)++; + if (map && igraph_vector_int_size(map) == 0) { + IGRAPH_CHECK(igraph_vector_int_resize(map, Gp->nbVertices)); + for (u = 0; u < Gp->nbVertices; u++) { + VECTOR(*map)[u] = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ]; + } + } + if (maps) { + IGRAPH_CHECK(igraph_vector_int_resize(vec, Gp->nbVertices)); + for (u = 0; u < Gp->nbVertices; u++) { + VECTOR(*vec)[u] = VECTOR(D->val)[ VECTOR(D->firstVal)[u] ]; + } + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(maps, vec)); + } + igraph_i_lad_resetToFilter(D); + *invalid = false; + goto cleanup; + } + + /* save the domain of minDom to iterate on its values */ + ALLOC_ARRAY_IN_HISTORY(val, VECTOR(D->nbVal)[minDom], igraph_integer_t, 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, vec, 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 = false; + + 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 IGRAPH_SUCCESS; +} + +/** + * \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 An integer vector list of \c NULL. The length of each + * 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 \c true if a subgraph + * isomorphism is found, and to \c false 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 to a list of integer vectors or a null pointer. If not + * a null pointer, then all subgraph isomorphisms are stored in the + * vector list, in \ref igraph_vector_int_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 + */ + +igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, + const igraph_vector_int_list_t *domains, + igraph_bool_t *iso, igraph_vector_int_t *map, + igraph_vector_int_list_t *maps, + igraph_bool_t induced, igraph_integer_t time_limit) { + + bool firstSol = maps == NULL; + bool initialDomains = domains != NULL; + Tgraph Gp, Gt; + Tdomain D; + igraph_bool_t invalidDomain; + igraph_integer_t u, nbToMatch = 0; + igraph_vector_int_t toMatch; + /* Helper vector in which we build the current subisomorphism mapping */ + igraph_vector_int_t vec; + /* Number of nodes in the search tree */ + igraph_integer_t nbNodes = 0; + /* number of failed nodes in the search tree */ + igraph_integer_t nbFail = 0; + /* number of solutions found */ + igraph_integer_t 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 specify at 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 = IGRAPH_INTEGER_MAX; + } + + if (iso) { + *iso = (igraph_vcount(pattern) == 0); + } + if (map) { + igraph_vector_int_clear(map); + } + if (maps) { + igraph_vector_int_list_clear(maps); + } + + if (igraph_vcount(pattern) == 0) { + /* Special case for null patterns */ + if (maps) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(maps, NULL)); + } + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + + 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(Gp.nbVertices, + Gt.nbVertices, + &D.nbVal, &D.firstVal, &D.val, + &D.globalMatchingP, + &invalidDomain)); + if (invalidDomain) { + goto exit; + } + + IGRAPH_CHECK(igraph_i_lad_ensureGACallDiff((bool) 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_VECTOR_INT_INIT_FINALLY(&toMatch, Gp.nbVertices); + + 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, (bool) 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, (bool) induced, &D, + &Gp, &Gt, &invalidDomain, iso, &vec, 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_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/isomorphism/queries.c b/src/isomorphism/queries.c new file mode 100644 index 0000000..1337fd8 --- /dev/null +++ b/src/isomorphism/queries.c @@ -0,0 +1,243 @@ +/* -*- 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 directed graphs with three and + * four vertices and all undirected graphs with 3-6 vertices are precomputed + * and stored in igraph, so for these small graphs there is a separate fast + * path in the code that does not use more complex, generic isomorphism + * algorithms. + */ + +static igraph_error_t igraph_i_isomorphic_small( + const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso +); + +/** + * \function igraph_isomorphic + * \brief Are two graphs 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. + * + * 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 both graphs are + * simplified and colorized using \ref igraph_simplify_and_colorize() and sent to VF2. + * \oli If the two graphs does not have the same number of vertices + * and edges it returns with \c false. + * \oli Otherwise, if the \ref igraph_isoclass() function supports both + * graphs (which is true for directed graphs with 3 and 4 vertices, and + * undirected graphs with 3-6 vertices), 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 \c true + * if the two graphs are isomorphic, and \c false otherwise. + * \return Error code. + * \sa \ref igraph_isoclass(), \ref igraph_isoclass_subgraph(), + * \ref igraph_isoclass_create(). + * + * Time complexity: exponential. + */ +igraph_error_t igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, + igraph_bool_t *iso) { + + igraph_integer_t nodes1 = igraph_vcount(graph1), nodes2 = igraph_vcount(graph2); + igraph_integer_t 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; + + if (dir1 != dir2) { + IGRAPH_ERROR("Cannot compare directed and undirected graphs for isomorphism.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_has_multiple(graph1, &multi1)); + IGRAPH_CHECK(igraph_has_multiple(graph2, &multi2)); + + if (multi1 || multi2) { + igraph_t r1; + igraph_t r2; + igraph_vector_int_t vc1; + igraph_vector_int_t vc2; + igraph_vector_int_t ec1; + igraph_vector_int_t ec2; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vc1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vc2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ec1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ec2, 0); + IGRAPH_CHECK(igraph_simplify_and_colorize(graph1, &r1, &vc1, &ec1)); + IGRAPH_FINALLY(igraph_destroy, &r1); + IGRAPH_CHECK(igraph_simplify_and_colorize(graph2, &r2, &vc2, &ec2)); + IGRAPH_FINALLY(igraph_destroy, &r2); + IGRAPH_CHECK(igraph_isomorphic_vf2(&r1, &r2, &vc1, &vc2, &ec1, &ec2, iso, + NULL, NULL, NULL, NULL, NULL)); + igraph_destroy(&r2); + igraph_destroy(&r1); + igraph_vector_int_destroy(&ec2); + igraph_vector_int_destroy(&ec1); + igraph_vector_int_destroy(&vc2); + igraph_vector_int_destroy(&vc1); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; + } + + if (nodes1 != nodes2 || edges1 != edges2) { + *iso = false; + } else if (nodes1 >= 3 && nodes1 <= (dir1 ? 4 : 6)) { + IGRAPH_CHECK(igraph_has_loop(graph1, &loop1)); + IGRAPH_CHECK(igraph_has_loop(graph2, &loop2)); + if (!loop1 && !loop2) { + IGRAPH_CHECK(igraph_i_isomorphic_small(graph1, graph2, iso)); + } else { + IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso, + NULL, NULL, /*sh=*/ IGRAPH_BLISS_FL, NULL, NULL)); + } + } else { + IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso, + NULL, NULL, /*sh=*/ IGRAPH_BLISS_FL, NULL, NULL)); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_isomorphic_34 + * \brief Graph isomorphism for 3-4 vertices (deprecated). + * + * \deprecated-by igraph_isomorphic 0.10.0 + * + * If you really care about performance and you \em know for sure that your + * input graphs are simple and have either 3 or 4 vertices for directed graphs, + * or 3-6 vertices for undirected graphs, you can compare their isomorphism + * classes obtained from \ref igraph_isoclass() directly instead of calling + * \ref igraph_isomorphic(); this saves the cost of checking whether the graphs + * do not contain multiple edges or self-loops. + * + * \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). + */ +igraph_error_t igraph_isomorphic_34( + const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso +) { + return igraph_i_isomorphic_small(graph1, graph2, iso); +} + +/** + * \function igraph_i_isomorphic_small + * \brief Graph isomorphism for small graphs. + * + * This function uses precomputed indices to decide isomorphism + * problems for directed graphs with only 3 or 4 vertices, or for undirected + * graphs with 3, 4, 5 or 6 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). + */ +igraph_error_t igraph_i_isomorphic_small( + 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t 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/isomorphism/vf2.c b/src/isomorphism/vf2.c new file mode 100644 index 0000000..782141f --- /dev/null +++ b/src/isomorphism/vf2.c @@ -0,0 +1,1782 @@ +/* -*- 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_stack.h" +#include "igraph_structural.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. + * + */ + +static igraph_error_t igraph_i_perform_vf2_pre_checks( + const igraph_t* graph1, const igraph_t* graph2 +) { + igraph_bool_t has_loops; + + if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { + IGRAPH_ERROR("Cannot compare directed and undirected graphs", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_has_loop(graph1, &has_loops)); + if (!has_loops) { + IGRAPH_CHECK(igraph_has_loop(graph2, &has_loops)); + } + + if (has_loops) { + IGRAPH_ERROR("The VF2 algorithm does not support graphs with loop edges.", + IGRAPH_EINVAL); + } + + /* TODO: VF2 does not support graphs with multiple edges either, but we + * don't check for this as the check would be complex, comparable to + * the runtime of the algorithm itself */ + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_isomorphisms_vf2_callback + * 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 \c IGRAPH_SUCCESS, then the search is continued, + * otherwise it stops. \c IGRAPH_STOP as a return value can be used to + * indicate normal premature termination; any other return value will be + * treated as an igraph error code, making the caller function return the + * same error code as well. 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. + */ + +igraph_error_t igraph_get_isomorphisms_vf2_callback( + 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph1); + igraph_integer_t no_of_edges = igraph_ecount(graph1); + igraph_vector_int_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2; + igraph_vector_int_t in_1, in_2, out_1, out_2; + igraph_integer_t 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; + igraph_integer_t matched_nodes = 0; + igraph_integer_t depth; + igraph_integer_t cand1, cand2; + igraph_integer_t last1, last2; + igraph_stack_int_t path; + igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2; + igraph_vector_int_t indeg1, indeg2, outdeg1, outdeg2; + igraph_integer_t vsize; + + IGRAPH_CHECK(igraph_i_perform_vf2_pre_checks(graph1, graph2)); + + 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 IGRAPH_SUCCESS; + } + + 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) { + igraph_bool_t ret = false; + igraph_vector_int_t tmp1, tmp2; + IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp1, vertex_color1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); + IGRAPH_CHECK(igraph_vector_int_init_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 IGRAPH_SUCCESS; + } + } + + /* Check edge color distribution */ + if (edge_color1) { + igraph_bool_t ret = false; + igraph_vector_int_t tmp1, tmp2; + IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp1, edge_color1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); + IGRAPH_CHECK(igraph_vector_int_init_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 IGRAPH_SUCCESS; + } + } + + if (map12) { + core_1 = map12; + IGRAPH_CHECK(igraph_vector_int_resize(core_1, no_of_nodes)); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(core_1, no_of_nodes); + } + igraph_vector_int_fill(core_1, -1); + if (map21) { + core_2 = map21; + IGRAPH_CHECK(igraph_vector_int_resize(core_2, no_of_nodes)); + igraph_vector_int_null(core_2); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(core_2, no_of_nodes); + } + igraph_vector_int_fill(core_2, -1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_1, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_2, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_1, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_2, no_of_nodes); + IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_int_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_INT_INIT_FINALLY(&indeg1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&indeg2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg2, 0); + + IGRAPH_CHECK(igraph_stack_int_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) { + igraph_integer_t 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 = igraph_stack_int_pop(&path); + last1 = igraph_stack_int_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, last1); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, last1); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, last2); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, last2); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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 */ + igraph_integer_t xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0; + igraph_bool_t end = false; + + inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); + outneis_1 = igraph_lazy_adjlist_get(&outadj1, cand1); + inneis_2 = igraph_lazy_adjlist_get(&inadj2, cand2); + outneis_2 = igraph_lazy_adjlist_get(&outadj2, cand2); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + + if (VECTOR(indeg1)[cand1] != VECTOR(indeg2)[cand2] || + VECTOR(outdeg1)[cand1] != VECTOR(outdeg2)[cand2]) { + end = true; + } + if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) { + end = true; + } + if (node_compat_fn && !node_compat_fn(graph1, graph2, cand1, cand2, arg)) { + end = true; + } + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; !end && i < vsize; i++) { + igraph_integer_t node = VECTOR(*inneis_1)[i]; + if (VECTOR(*core_1)[node] >= 0) { + igraph_integer_t node2 = VECTOR(*core_1)[node]; + /* check if there is a node2->cand2 edge */ + if (!igraph_vector_int_binsearch2(inneis_2, node2)) { + end = true; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, node, cand1, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, node2, cand2, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = true; + } + } + } 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++) { + igraph_integer_t node = VECTOR(*outneis_1)[i]; + if (VECTOR(*core_1)[node] >= 0) { + igraph_integer_t node2 = VECTOR(*core_1)[node]; + /* check if there is a cand2->node2 edge */ + if (!igraph_vector_int_binsearch2(outneis_2, node2)) { + end = true; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, cand1, node, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, cand2, node2, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = true; + } + } + } 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++) { + igraph_integer_t node = VECTOR(*inneis_2)[i]; + if (VECTOR(*core_2)[node] >= 0) { + igraph_integer_t node2 = VECTOR(*core_2)[node]; + /* check if there is a node2->cand1 edge */ + if (!igraph_vector_int_binsearch2(inneis_1, node2)) { + end = true; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, node2, cand1, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, node, cand2, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = true; + } + } + } 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++) { + igraph_integer_t node = VECTOR(*outneis_2)[i]; + if (VECTOR(*core_2)[node] >= 0) { + igraph_integer_t node2 = VECTOR(*core_2)[node]; + /* check if there is a cand1->node2 edge */ + if (!igraph_vector_int_binsearch2(outneis_1, node2)) { + end = true; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, cand1, node2, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, cand2, node, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = true; + } + } + } 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_int_push(&path, cand1)); + IGRAPH_CHECK(igraph_stack_int_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, cand1); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, cand1); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, cand2); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, cand2); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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) { + igraph_error_t ret; + IGRAPH_CHECK_CALLBACK(isohandler_fn(core_1, core_2, arg), &ret); + if (ret == IGRAPH_STOP) { + break; + } + } + } + + igraph_vector_int_destroy(&outdeg2); + igraph_vector_int_destroy(&outdeg1); + igraph_vector_int_destroy(&indeg2); + igraph_vector_int_destroy(&indeg1); + igraph_lazy_adjlist_destroy(&outadj2); + igraph_lazy_adjlist_destroy(&inadj2); + igraph_lazy_adjlist_destroy(&outadj1); + igraph_lazy_adjlist_destroy(&inadj1); + igraph_stack_int_destroy(&path); + igraph_vector_int_destroy(&out_2); + igraph_vector_int_destroy(&out_1); + igraph_vector_int_destroy(&in_2); + igraph_vector_int_destroy(&in_1); + IGRAPH_FINALLY_CLEAN(13); + if (!map21) { + igraph_vector_int_destroy(core_2); + IGRAPH_FINALLY_CLEAN(1); + } + if (!map12) { + igraph_vector_int_destroy(core_1); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_isomorphic_function_vf2 + * \brief The generic VF2 interface (deprecated alias). + * + * \deprecated-by igraph_get_isomorphisms_vf2_callback 0.10.0 + */ +igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +) { + return igraph_get_isomorphisms_vf2_callback( + graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, + map12, map21, isohandler_fn, node_compat_fn, edge_compat_fn, arg + ); +} + +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_error_t igraph_i_isomorphic_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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 = true; + return IGRAPH_STOP; +} + +/** + * \function igraph_isomorphic_vf2 + * \brief Isomorphism via VF2. + * + * + * This function performs the VF2 algorithm via calling \ref + * igraph_get_isomorphisms_vf2_callback(). + * + * 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 + */ + +igraph_error_t 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_int_t *map12, + igraph_vector_int_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 = false; + IGRAPH_CHECK(igraph_get_isomorphisms_vf2_callback(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + map12, map21, + (igraph_isohandler_t*) igraph_i_isomorphic_vf2_cb, + ncb, ecb, &data)); + if (! *iso) { + if (map12) { + igraph_vector_int_clear(map12); + } + if (map21) { + igraph_vector_int_clear(map21); + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_count_isomorphisms_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_count_isomorphisms_vf2 + * \brief Number of isomorphisms via VF2. + * + * This function counts the number of isomorphic mappings between two + * graphs. It uses the generic \ref igraph_get_isomorphisms_vf2_callback() + * 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. + * + * \sa igraph_count_automorphisms() + * + * Time complexity: exponential. + */ + +igraph_error_t 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_get_isomorphisms_vf2_callback(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + 0, 0, + (igraph_isohandler_t*) igraph_i_count_isomorphisms_vf2_cb, + ncb, ecb, &data)); + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_store_mapping_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_t *map21, + void *arg +) { + igraph_i_iso_cb_data_t *data = arg; + igraph_vector_int_list_t *ptrvector = data->arg; + IGRAPH_UNUSED(map12); + return igraph_vector_int_list_push_back_copy(ptrvector, map21); +} + +/** + * \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_get_isomorphisms_vf2_callback() + * 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 to a list of integer vectors. On return it is empty if + * the input graphs are not isomorphic. Otherwise it contains pointers to + * \ref igraph_vector_int_t objects, each vector is an + * isomorphic mapping of \p graph2 to \p graph1. + * \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. + */ + +igraph_error_t 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_int_list_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_int_list_clear(maps); + IGRAPH_CHECK(igraph_get_isomorphisms_vf2_callback(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + NULL, NULL, + (igraph_isohandler_t*) igraph_i_store_mapping_vf2_cb, + ncb, ecb, &data)); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_subisomorphisms_vf2_callback + * \brief Generic VF2 function for subgraph isomorphism problems. + * + * This function is the pair of \ref igraph_get_isomorphisms_vf2_callback(), + * 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 \c IGRAPH_SUCCESS, + * then the search is continued. If the function returns \c IGRAPH_STOP, + * the search is terminated normally. Any other value is treated as an + * igraph error code. + * \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. + */ + +igraph_error_t igraph_get_subisomorphisms_vf2_callback( + 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +) { + + igraph_integer_t no_of_nodes1 = igraph_vcount(graph1), + no_of_nodes2 = igraph_vcount(graph2); + igraph_integer_t no_of_edges1 = igraph_ecount(graph1), + no_of_edges2 = igraph_ecount(graph2); + igraph_vector_int_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2; + igraph_vector_int_t in_1, in_2, out_1, out_2; + igraph_integer_t 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; + igraph_integer_t matched_nodes = 0; + igraph_integer_t depth; + igraph_integer_t cand1, cand2; + igraph_integer_t last1, last2; + igraph_stack_int_t path; + igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2; + igraph_vector_int_t indeg1, indeg2, outdeg1, outdeg2; + igraph_integer_t vsize; + + IGRAPH_CHECK(igraph_i_perform_vf2_pre_checks(graph1, graph2)); + + if (no_of_nodes1 < no_of_nodes2 || no_of_edges1 < no_of_edges2) { + return IGRAPH_SUCCESS; + } + + 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_int_resize(core_1, no_of_nodes1)); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(core_1, no_of_nodes1); + } + igraph_vector_int_fill(core_1, -1); + if (map21) { + core_2 = map21; + IGRAPH_CHECK(igraph_vector_int_resize(core_2, no_of_nodes2)); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(core_2, no_of_nodes2); + } + igraph_vector_int_fill(core_2, -1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_1, no_of_nodes1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_2, no_of_nodes2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_1, no_of_nodes1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_2, no_of_nodes2); + IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_int_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_INT_INIT_FINALLY(&indeg1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&indeg2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg2, 0); + + IGRAPH_CHECK(igraph_stack_int_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) { + igraph_integer_t 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 = igraph_stack_int_pop(&path); + last1 = igraph_stack_int_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, last1); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, last1); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, last2); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, last2); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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 */ + igraph_integer_t xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0; + igraph_bool_t end = false; + + inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); + outneis_1 = igraph_lazy_adjlist_get(&outadj1, cand1); + inneis_2 = igraph_lazy_adjlist_get(&inadj2, cand2); + outneis_2 = igraph_lazy_adjlist_get(&outadj2, cand2); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + + if (VECTOR(indeg1)[cand1] < VECTOR(indeg2)[cand2] || + VECTOR(outdeg1)[cand1] < VECTOR(outdeg2)[cand2]) { + end = true; + } + if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) { + end = true; + } + if (node_compat_fn && !node_compat_fn(graph1, graph2, cand1, cand2, arg)) { + end = true; + } + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; !end && i < vsize; i++) { + igraph_integer_t node = 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++) { + igraph_integer_t node = 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++) { + igraph_integer_t node = VECTOR(*inneis_2)[i]; + if (VECTOR(*core_2)[node] >= 0) { + igraph_integer_t node2 = VECTOR(*core_2)[node]; + /* check if there is a node2->cand1 edge */ + if (!igraph_vector_int_binsearch2(inneis_1, node2)) { + end = true; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, node2, cand1, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, node, cand2, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = true; + } + } + } 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++) { + igraph_integer_t node = VECTOR(*outneis_2)[i]; + if (VECTOR(*core_2)[node] >= 0) { + igraph_integer_t node2 = VECTOR(*core_2)[node]; + /* check if there is a cand1->node2 edge */ + if (!igraph_vector_int_binsearch2(outneis_1, node2)) { + end = true; + } else if (edge_color1 || edge_compat_fn) { + igraph_integer_t eid1, eid2; + igraph_get_eid(graph1, &eid1, cand1, node2, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, cand2, node, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; + } + if (edge_compat_fn && !edge_compat_fn(graph1, graph2, + eid1, eid2, arg)) { + end = true; + } + } + } 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_int_push(&path, cand1)); + IGRAPH_CHECK(igraph_stack_int_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, cand1); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(inneis_1); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, cand1); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(outneis_1); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, cand2); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(inneis_2); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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, cand2); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + + vsize = igraph_vector_int_size(outneis_2); + for (i = 0; i < vsize; i++) { + igraph_integer_t node = 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) { + igraph_error_t ret; + IGRAPH_CHECK_CALLBACK(isohandler_fn(core_1, core_2, arg), &ret); + if (ret == IGRAPH_STOP) { + break; + } + } + } + + igraph_vector_int_destroy(&outdeg2); + igraph_vector_int_destroy(&outdeg1); + igraph_vector_int_destroy(&indeg2); + igraph_vector_int_destroy(&indeg1); + igraph_lazy_adjlist_destroy(&outadj2); + igraph_lazy_adjlist_destroy(&inadj2); + igraph_lazy_adjlist_destroy(&outadj1); + igraph_lazy_adjlist_destroy(&inadj1); + igraph_stack_int_destroy(&path); + igraph_vector_int_destroy(&out_2); + igraph_vector_int_destroy(&out_1); + igraph_vector_int_destroy(&in_2); + igraph_vector_int_destroy(&in_1); + IGRAPH_FINALLY_CLEAN(13); + if (!map21) { + igraph_vector_int_destroy(core_2); + IGRAPH_FINALLY_CLEAN(1); + } + if (!map12) { + igraph_vector_int_destroy(core_1); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_subisomorphic_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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 = true; + return IGRAPH_STOP; +} + +/** + * \function igraph_subisomorphic_function_vf2 + * \brief Generic VF2 function for subgraph isomorphism problems (deprecated alias). + * + * \deprecated-by igraph_get_subisomorphisms_vf2_callback 0.10.0 + */ +igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +) { + return igraph_get_subisomorphisms_vf2_callback( + graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, + map12, map21, isohandler_fn, node_compat_fn, edge_compat_fn, arg + ); +} + +/** + * \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_get_subisomorphisms_vf2_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 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. + */ + +igraph_error_t 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_int_t *map12, + igraph_vector_int_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 = false; + IGRAPH_CHECK(igraph_get_subisomorphisms_vf2_callback(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + map12, map21, + (igraph_isohandler_t *) igraph_i_subisomorphic_vf2_cb, + ncb, ecb, &data)); + if (! *iso) { + if (map12) { + igraph_vector_int_clear(map12); + } + if (map21) { + igraph_vector_int_clear(map21); + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_count_subisomorphisms_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \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_get_subisomorphisms_vf2_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 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. + */ + +igraph_error_t 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_get_subisomorphisms_vf2_callback(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + 0, 0, + (igraph_isohandler_t*) igraph_i_count_subisomorphisms_vf2_cb, + ncb, ecb, &data)); + return 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_get_subisomorphisms_vf2_callback() 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 to a list of integer vectors. On return it contains + * pointers to \ref igraph_vector_int_t objects, each vector is an isomorphic + * mapping of \p graph2 to a subgraph of \p graph1. + * \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. + */ + +igraph_error_t 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_int_list_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_int_list_clear(maps); + IGRAPH_CHECK(igraph_get_subisomorphisms_vf2_callback(graph1, graph2, + vertex_color1, vertex_color2, + edge_color1, edge_color2, + NULL, NULL, + (igraph_isohandler_t*) igraph_i_store_mapping_vf2_cb, + ncb, ecb, &data)); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/circular.c b/src/layout/circular.c new file mode 100644 index 0000000..ed1bad3 --- /dev/null +++ b/src/layout/circular.c @@ -0,0 +1,188 @@ +/* -*- 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 arbitrary order. + * + * \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 to place vertices in the + * order of their vertex IDs. + * \return Error code. + * + * Time complexity: O(|V|), the number of vertices. + */ +igraph_error_t igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t order) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t vs_size; + 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_CHECK(igraph_vit_create(graph, order, &vit)); + for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_real_t phi = 2 * M_PI / vs_size * i; + igraph_integer_t idx = IGRAPH_VIT_GET(vit); + MATRIX(*res, idx, 0) = cos(phi); + MATRIX(*res, idx, 1) = sin(phi); + } + igraph_vit_destroy(&vit); + + return IGRAPH_SUCCESS; +} + +/** + * \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. You can set it to + * any arbitrary value for the special case when the input graph has no + * vertices; otherwise it must be between 0 and the number of vertices + * minus 1. + * \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. + */ +igraph_error_t igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t center, const igraph_vector_int_t *order) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (no_of_nodes > 0 && (center < 0 || center >= no_of_nodes)) { + IGRAPH_ERROR("The given center is not a vertex of the graph.", IGRAPH_EINVAL); + } + if (order && igraph_vector_int_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 if (no_of_nodes > 1) { + igraph_real_t phi = 0.0; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t node = order ? 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 != center) { + MATRIX(*res, node, 0) = cos(phi); + MATRIX(*res, node, 1) = sin(phi); + phi += 2.0 * M_PI / (no_of_nodes - 1); + } else { + MATRIX(*res, node, 0) = MATRIX(*res, node, 1) = 0.0; + } + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_layout_sphere + * \brief Places vertices (more or less) uniformly on a sphere. + * + * The vertices are placed with approximately equal spacing on a spiral + * wrapped around a sphere, in the order of their vertex IDs. Vertices + * with consecutive vertex IDs are placed near each other. + * + * + * 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. https://doi.org/10.1007/BF03024331 + * + * \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. + */ +igraph_error_t igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_real_t sqrt_no_of_nodes = sqrt(no_of_nodes); + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); + + igraph_real_t phi = 0; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_real_t r, z; + + /* The first and last point are handled separately to avoid + * division by zero or 1-z*z becoming slightly negative due + * to roundoff errors. */ + if (i == 0) { + z = -1; r = 0; + } else if (i == no_of_nodes-1) { + z = 1; r = 0; + } else { + z = -1.0 + 2.0 * i / (no_of_nodes - 1); + r = sqrt(1 - z*z); + phi += 3.6 / (sqrt_no_of_nodes*r); + } + + igraph_real_t x = r*cos(phi); + igraph_real_t y = r*sin(phi); + + MATRIX(*res, i, 0) = x; + MATRIX(*res, i, 1) = y; + MATRIX(*res, i, 2) = z; + + IGRAPH_ALLOW_INTERRUPTION(); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/davidson_harel.c b/src/layout/davidson_harel.c new file mode 100644 index 0000000..b0ac699 --- /dev/null +++ b/src/layout/davidson_harel.c @@ -0,0 +1,456 @@ +/* -*- 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/interruption.h" +#include "core/math.h" +#include "layout/layout_internal.h" + +#include + +/* not 'static', used in tests */ +igraph_bool_t igraph_i_layout_segments_intersect(igraph_real_t p0_x, igraph_real_t p0_y, + igraph_real_t p1_x, igraph_real_t p1_y, + igraph_real_t p2_x, igraph_real_t p2_y, + igraph_real_t p3_x, igraph_real_t p3_y) { + igraph_real_t s1_x = p1_x - p0_x; + igraph_real_t s1_y = p1_y - p0_y; + igraph_real_t s2_x = p3_x - p2_x; + igraph_real_t s2_y = p3_y - p2_y; + + igraph_real_t 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 false; + } + 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; +} + +/* not 'static', used in tests */ +igraph_real_t igraph_i_layout_point_segment_dist2(igraph_real_t v_x, igraph_real_t v_y, + igraph_real_t u1_x, igraph_real_t u1_y, + igraph_real_t u2_x, igraph_real_t u2_y) { + + igraph_real_t dx = u2_x - u1_x; + igraph_real_t dy = u2_y - u1_y; + igraph_real_t l2 = dx * dx + dy * dy; + igraph_real_t 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 + * \brief 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. + * https://doi.org/10.1145/234535.234538 + * + * + * 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 \c 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. + * + */ + +igraph_error_t 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); + igraph_real_t width = sqrt(no_nodes) * 10, height = width; + igraph_vector_int_t perm; + igraph_bool_t fine_tuning = false; + igraph_vector_t try_x, try_y; + igraph_vector_int_t try_idx; + igraph_real_t move_radius = width / 2; + igraph_real_t fine_tuning_factor = 0.01; + igraph_vector_int_t neis; + igraph_real_t min_x = width / 2, max_x = -width / 2, min_y = height / 2, max_y = -height / 2; + + igraph_integer_t no_tries = 30; + igraph_real_t w_node_dist = weight_node_dist ; /* 1.0 */ + igraph_real_t w_borderlines = weight_border; /* 0.0 */ + igraph_real_t w_edge_lengths = weight_edge_lengths; /* 0.0001; */ + igraph_real_t w_edge_crossings = weight_edge_crossings; /* 1.0 */ + igraph_real_t w_node_edge_dist = weight_node_edge_dist; /* 0.2 */ + + if (maxiter < 0) { + IGRAPH_ERROR("Number of iterations must not be negative for the Davidson-Harel layout.", IGRAPH_EINVAL); + } + if (fineiter < 0) { + IGRAPH_ERROR("Number of fine tuning iterations must not be negative for the Davidson-Harel layout.", + IGRAPH_EINVAL); + } + if (cool_fact <= 0 || cool_fact >= 1) { + IGRAPH_ERROR("Cooling factor must be in (0,1) for the Davidson-Harel layout.", IGRAPH_EINVAL); + } + if (use_seed) { + if (igraph_matrix_nrow(res) != no_nodes || igraph_matrix_ncol(res) != 2) { + IGRAPH_ERROR("Invalid start position matrix size in Davidson-Harel layout.", IGRAPH_EINVAL); + } + } else { + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); + } + + if (no_nodes == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_vector_int_init_range(&perm, 0, no_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &perm); + IGRAPH_VECTOR_INIT_FINALLY(&try_x, no_tries); + IGRAPH_VECTOR_INIT_FINALLY(&try_y, no_tries); + IGRAPH_CHECK(igraph_vector_int_init_range(&try_idx, 0, no_tries)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &try_idx); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 100); + + RNG_BEGIN(); + + if (!use_seed) { + for (igraph_integer_t i = 0; i < no_nodes; i++) { + igraph_real_t 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 (igraph_integer_t i = 0; i < no_nodes; i++) { + igraph_real_t x = MATRIX(*res, i, 0); + igraph_real_t 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 (igraph_integer_t i = 0; i < no_tries; i++) { + double phi = 2 * M_PI / no_tries * i; + VECTOR(try_x)[i] = cos(phi); + VECTOR(try_y)[i] = sin(phi); + } + + for (igraph_integer_t round = 0; round < maxiter + fineiter; round++) { + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_vector_int_shuffle(&perm); + + fine_tuning = round >= maxiter; + if (fine_tuning) { + igraph_real_t fx = fine_tuning_factor * (max_x - min_x); + igraph_real_t fy = fine_tuning_factor * (max_y - min_y); + move_radius = fx < fy ? fx : fy; + } + + for (igraph_integer_t p = 0; p < no_nodes; p++) { + igraph_integer_t v = VECTOR(perm)[p]; + igraph_vector_int_shuffle(&try_idx); + + for (igraph_integer_t t = 0; t < no_tries; t++) { + igraph_real_t diff_energy = 0.0; + igraph_integer_t ti = VECTOR(try_idx)[t]; + + /* Try moving it */ + igraph_real_t old_x = MATRIX(*res, v, 0); + igraph_real_t old_y = MATRIX(*res, v, 1); + igraph_real_t new_x = old_x + move_radius * VECTOR(try_x)[ti]; + igraph_real_t 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) { + for (igraph_integer_t u = 0; u < no_nodes; u++) { + igraph_real_t 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) { + igraph_real_t odx1 = width / 2 - old_x, odx2 = old_x + width / 2; + igraph_real_t ody1 = height / 2 - old_y, ody2 = old_y + height / 2; + igraph_real_t dx1 = width / 2 - new_x, dx2 = new_x + width / 2; + igraph_real_t 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_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); + igraph_integer_t len = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < len; j++) { + igraph_integer_t u = VECTOR(neis)[j]; + igraph_real_t odx = old_x - MATRIX(*res, u, 0); + igraph_real_t ody = old_y - MATRIX(*res, u, 1); + igraph_real_t odist2 = odx * odx + ody * ody; + igraph_real_t dx = new_x - MATRIX(*res, u, 0); + igraph_real_t dy = new_y - MATRIX(*res, u, 1); + igraph_real_t dist2 = dx * dx + dy * dy; + diff_energy += w_edge_lengths * (dist2 - odist2); + } + } + + if (w_edge_crossings != 0) { + igraph_integer_t no = 0; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); + igraph_integer_t len = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < len; j++) { + igraph_integer_t u = VECTOR(neis)[j]; + igraph_real_t u_x = MATRIX(*res, u, 0); + igraph_real_t 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); + igraph_real_t 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) { + /* All non-incident edges from the moved 'v' */ + for (igraph_integer_t e = 0; e < no_edges; e++) { + igraph_integer_t u1 = IGRAPH_FROM(graph, e); + igraph_integer_t u2 = IGRAPH_TO(graph, e); + igraph_real_t 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_CHECK(igraph_incident(graph, &neis, v, IGRAPH_ALL)); + igraph_integer_t no = igraph_vector_int_size(&neis); + for (igraph_integer_t e = 0; e < no; e++) { + igraph_integer_t mye = VECTOR(neis)[e]; + igraph_integer_t u = IGRAPH_OTHER(graph, mye, v); + igraph_real_t u_x = MATRIX(*res, u, 0); + igraph_real_t u_y = MATRIX(*res, u, 1); + for (igraph_integer_t w = 0; w < no_nodes; w++) { + igraph_real_t 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_int_destroy(&neis); + igraph_vector_int_destroy(&try_idx); + igraph_vector_destroy(&try_x); + igraph_vector_destroy(&try_y); + igraph_vector_int_destroy(&perm); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/drl/DensityGrid.cpp b/src/layout/drl/DensityGrid.cpp new file mode 100644 index 0000000..ce60292 --- /dev/null +++ b/src/layout/drl/DensityGrid.cpp @@ -0,0 +1,258 @@ +/* + * 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 +#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() { + + Density = new float[GRID_SIZE][GRID_SIZE]; + fall_off = new float[RADIUS * 2 + 1][RADIUS * 2 + 1]; + Bins = new deque[GRID_SIZE * GRID_SIZE]; + + // 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) ) { + throw runtime_error("Exceeded density grid in DrL."); + } + + /* 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) ) { + throw runtime_error("Exceeded density grid in DrL."); + } + + /* 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/layout/drl/DensityGrid.h b/src/layout/drl/DensityGrid.h new file mode 100644 index 0000000..def0c7a --- /dev/null +++ b/src/layout/drl/DensityGrid.h @@ -0,0 +1,81 @@ +/* + * 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" + +#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/layout/drl/DensityGrid_3d.cpp b/src/layout/drl/DensityGrid_3d.cpp new file mode 100644 index 0000000..7d06cae --- /dev/null +++ b/src/layout/drl/DensityGrid_3d.cpp @@ -0,0 +1,282 @@ +/* + * 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 +#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() { + + 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]; + + // 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) ) { + throw runtime_error("Exceeded density grid in DrL."); + } + + /* 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) ) { + throw runtime_error("Exceeded density grid in DrL."); + } + + /* 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/layout/drl/DensityGrid_3d.h b/src/layout/drl/DensityGrid_3d.h new file mode 100644 index 0000000..6ad1b49 --- /dev/null +++ b/src/layout/drl/DensityGrid_3d.h @@ -0,0 +1,81 @@ +/* + * 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" + +#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/layout/drl/drl_Node.h b/src/layout/drl/drl_Node.h new file mode 100644 index 0000000..387bbb2 --- /dev/null +++ b/src/layout/drl/drl_Node.h @@ -0,0 +1,70 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __NODE_H__ +#define __NODE_H__ + +#include + +// 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 + igraph_integer_t id; + + // position of this node + float x, y; + float sub_x, sub_y; + float energy; + +public: + + Node( igraph_integer_t node_id ) { + x = y = 0.0; fixed = false; + id = node_id; + } + ~Node() { } + +}; + +} // namespace drl + +#endif //__NODE_H__ diff --git a/src/layout/drl/drl_Node_3d.h b/src/layout/drl/drl_Node_3d.h new file mode 100644 index 0000000..250e934 --- /dev/null +++ b/src/layout/drl/drl_Node_3d.h @@ -0,0 +1,70 @@ +/* + * Copyright 2007 Sandia Corporation. Under the terms of Contract + * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + * certain rights in this software. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sandia National Laboratories nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __NODE_H__ +#define __NODE_H__ + +#include + +// 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 + igraph_integer_t id; + + // position of this node + float x, y, z; + float sub_x, sub_y, sub_z; + float energy; + +public: + + Node( igraph_integer_t node_id ) { + x = y = z = 0.0; fixed = false; + id = node_id; + } + ~Node() { } + +}; + +} // namespace drl3d + +#endif //__NODE_H__ diff --git a/src/layout/drl/drl_graph.cpp b/src/layout/drl/drl_graph.cpp new file mode 100644 index 0000000..219a112 --- /dev/null +++ b/src/layout/drl/drl_graph.cpp @@ -0,0 +1,1270 @@ +/* + * 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" + +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); + igraph_integer_t no_of_edges = igraph_ecount(igraph); + for (igraph_integer_t i = 0; i < num_nodes; i++) { + id_catalog[i] = 1; + } + map::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 + igraph_integer_t node_1, node_2; + igraph_real_t weight; + for (igraph_integer_t 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 + igraph_integer_t 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 = (igraph_integer_t)real_parm; + } else if ( real_parm == 1) { + real_iterations = full_comp_iters + simmer.iterations + 100; + } else { + real_iterations = (igraph_integer_t)(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) { + igraph_integer_t n = igraph_matrix_nrow(real_mat); + for (igraph_integer_t 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 = 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 + igraph_integer_t square_num_nodes = (igraph_integer_t)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); + + for ( igraph_integer_t 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 ) { + // calculate node energy possibilities + if ( !(positions[i].fixed && real_fixed) ) { + update_node_pos ( i, old_positions, new_positions ); + } + } + + // check if anything was actually updated (e.g. everything was fixed) + all_fixed = true; + for ( size_t 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 ) { + // 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 ( size_t 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 (size_t 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 ( igraph_integer_t 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 ( size_t 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( igraph_integer_t 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( igraph_integer_t 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; + + tot_energy = my_tot_energy; + + 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) { + while (ReCompute()) { + IGRAPH_ALLOW_INTERRUPTION(); + } + igraph_integer_t n = positions.size(); + IGRAPH_CHECK(igraph_matrix_resize(res, n, 2)); + for (igraph_integer_t 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/layout/drl/drl_graph.h b/src/layout/drl/drl_graph.h new file mode 100644 index 0000000..df1424f --- /dev/null +++ b/src/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 { + igraph_integer_t 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 ); + 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 ( igraph_integer_t node_ind ); + void Solve_Analytic ( igraph_integer_t 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 ( igraph_integer_t node_ind, + float old_positions[2 * MAX_PROCS], + float new_positions[2 * MAX_PROCS] ); + + // MPI information + int myid, num_procs; + + // graph decomposition information + igraph_integer_t 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; + igraph_integer_t 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 + igraph_integer_t real_iterations; // number of iterations to hold .real input fixed + igraph_integer_t tot_iterations; + igraph_integer_t tot_expected_iterations; // for progress bar + bool real_fixed; +}; + +} // namespace drl diff --git a/src/layout/drl/drl_graph_3d.cpp b/src/layout/drl/drl_graph_3d.cpp new file mode 100644 index 0000000..98dc567 --- /dev/null +++ b/src/layout/drl/drl_graph_3d.cpp @@ -0,0 +1,837 @@ +/* + * 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" + +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); + igraph_integer_t no_of_edges = igraph_ecount(igraph); + for (igraph_integer_t i = 0; i < num_nodes; i++) { + id_catalog[i] = 1; + } + map< igraph_integer_t, igraph_integer_t>::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 + igraph_integer_t node_1, node_2; + igraph_real_t weight; + for (igraph_integer_t 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 + igraph_integer_t 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) { + igraph_integer_t n = igraph_matrix_nrow(real_mat); + for (igraph_integer_t 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 = 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 + igraph_integer_t square_num_nodes = (igraph_integer_t)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); + + for ( igraph_integer_t 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 ) { + // calculate node energy possibilities + if ( !(positions[i].fixed && real_fixed) ) { + update_node_pos ( i, old_positions, new_positions ); + } + } + + // check if anything was actually updated (e.g. everything was fixed) + all_fixed = true; + for ( size_t 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 ) { + // update positions (old to new) + update_density ( node_indices, old_positions, new_positions ); + } + + /* + if ( myid == 0 ) + { + // output node list (for debugging) + for ( size_t j = 0; j < node_indices.size(); j++ ) + cout << node_indices[j] << " "; + cout << endl; + } + */ + + // compute node list for next update + for ( size_t 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 (size_t 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 ( igraph_integer_t 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 ( size_t 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( igraph_integer_t 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( igraph_integer_t 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; + + tot_energy = my_tot_energy; + + return tot_energy; + +} + + +int graph::draw_graph(igraph_matrix_t *res) { + while (ReCompute()) { + IGRAPH_ALLOW_INTERRUPTION(); + } + size_t n = positions.size(); + IGRAPH_CHECK(igraph_matrix_resize(res, n, 3)); + for (size_t 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/layout/drl/drl_graph_3d.h b/src/layout/drl/drl_graph_3d.h new file mode 100644 index 0000000..b87ad86 --- /dev/null +++ b/src/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 { + igraph_integer_t 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 ); + 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 ( igraph_integer_t node_ind ); + void Solve_Analytic ( igraph_integer_t 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 ( igraph_integer_t node_ind, + float old_positions[3 * MAX_PROCS], + float new_positions[3 * MAX_PROCS] ); + + // MPI information + int myid, num_procs; + + // graph decomposition information + igraph_integer_t 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; + igraph_integer_t 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 + igraph_integer_t real_iterations; // number of iterations to hold .real input fixed + igraph_integer_t tot_iterations; + igraph_integer_t tot_expected_iterations; // for progress bar + bool real_fixed; +}; + +} // namespace drl3d diff --git a/src/layout/drl/drl_layout.cpp b/src/layout/drl/drl_layout.cpp new file mode 100644 index 0000000..6316d44 --- /dev/null +++ b/src/layout/drl/drl_layout.cpp @@ -0,0 +1,485 @@ +/* + * 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 + +// layout routines and constants +#include "drl_layout.h" +#include "drl_parse.h" +#include "drl_graph.h" + +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). + */ + +igraph_error_t 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. + * \return Error code. + * + * Time complexity: ???. + */ + +igraph_error_t 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 char msg[] = "Damping multipliers cannot be negative, got %g."; + + 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); + } + + if (weights) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Length of weight vector does not match number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0 && igraph_vector_min(weights) <= 0) { + IGRAPH_ERROR("Weights must be positive for DrL layout.", IGRAPH_EINVAL); + } + } + + 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); + } + neighbors.draw_graph(res); + + RNG_END(); + ); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/drl/drl_layout.h b/src/layout/drl/drl_layout.h new file mode 100644 index 0000000..8d3cd29 --- /dev/null +++ b/src/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/layout/drl/drl_layout_3d.cpp b/src/layout/drl/drl_layout_3d.cpp new file mode 100644 index 0000000..557adb9 --- /dev/null +++ b/src/layout/drl/drl_layout_3d.cpp @@ -0,0 +1,136 @@ +/* + * 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 + +// layout routines and constants +#include "drl_layout_3d.h" +#include "drl_parse.h" +#include "drl_graph_3d.h" + +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. + * \return Error code. + * + * Time complexity: ???. + * + * \sa \ref igraph_layout_drl() for the standard 2d version. + */ + +igraph_error_t 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 char msg[] = "Damping multipliers cannot be negative, got %g."; + + 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); + } + + if (weights) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Length of weight vector does not match number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0 && igraph_vector_min(weights) <= 0) { + IGRAPH_ERROR("Weights must be positive for DrL layout.", IGRAPH_EINVAL); + } + } + + 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); + } + neighbors.draw_graph(res); + + RNG_END(); + ); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/drl/drl_layout_3d.h b/src/layout/drl/drl_layout_3d.h new file mode 100644 index 0000000..d9b0095 --- /dev/null +++ b/src/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/layout/drl/drl_parse.cpp b/src/layout/drl/drl_parse.cpp new file mode 100644 index 0000000..c0e98cd --- /dev/null +++ b/src/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/layout/drl/drl_parse.h b/src/layout/drl/drl_parse.h new file mode 100644 index 0000000..b1e2761 --- /dev/null +++ b/src/layout/drl/drl_parse.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. + */ +// The parse class contains the methods necessary to parse +// the command line, print help, and do error checking + +#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/layout/fruchterman_reingold.c b/src/layout/fruchterman_reingold.c new file mode 100644 index 0000000..a676b17 --- /dev/null +++ b/src/layout/fruchterman_reingold.c @@ -0,0 +1,692 @@ +/* -*- 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" +#include "layout/layout_internal.h" + +static igraph_error_t 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_t dispx, dispy; + igraph_real_t temp = start_temp; + igraph_real_t difftemp = start_temp / niter; + igraph_bool_t conn = true; + igraph_real_t C = 0; + + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (!conn) { + C = no_nodes * sqrt(no_nodes); + } + + if (!use_seed) { + igraph_i_layout_random_bounded(graph, res, minx, maxx, miny, maxy); + } + + IGRAPH_VECTOR_INIT_FINALLY(&dispx, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); + + RNG_BEGIN(); + 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_null(&dispx); + igraph_vector_null(&dispy); + if (conn) { + for (v = 0; v < no_nodes; v++) { + for (u = v + 1; u < no_nodes; u++) { + 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 dlen = dx * dx + dy * dy; + + while (dlen == 0) { + dx = RNG_UNIF(-1e-9, 1e-9); + dy = RNG_UNIF(-1e-9, 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++) { + 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 dlen, rdlen; + + dlen = dx * dx + dy * dy; + while (dlen == 0) { + dx = RNG_UNIF(-1e-9, 1e-9); + dy = RNG_UNIF(-1e-9, 1e-9); + 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 edge 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_UNIF(-1e-9, 1e-9); + igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF(-1e-9, 1e-9); + igraph_real_t displen = sqrt(dx * dx + dy * dy); + + if (displen > temp) { + dx *= temp/displen; + dy *= temp/displen; + } + + if (displen > 0) { + MATRIX(*res, v, 0) += dx; + MATRIX(*res, v, 1) += dy; + } + 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_destroy(&dispx); + igraph_vector_destroy(&dispy); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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); + igraph_real_t width = sqrt(no_nodes), height = width; + igraph_2dgrid_t grid; + igraph_vector_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 igraph_real_t cellsize = 2.0; + + if (!use_seed) { + igraph_i_layout_random_bounded(graph, res, minx, maxx, miny, maxy); + } + + /* 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_VECTOR_INIT_FINALLY(&dispx, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); + + RNG_BEGIN(); + for (i = 0; i < niter; i++) { + igraph_integer_t v, u, e; + + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_vector_null(&dispx); + igraph_vector_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) { + 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 dlen = dx * dx + dy * dy; + while (dlen == 0) { + dx = RNG_UNIF(-1e-9, 1e-9); + dy = RNG_UNIF(-1e-9, 1e-9); + 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_UNIF(-1e-9, 1e-9); + igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF(-1e-9, 1e-9); + igraph_real_t displen = sqrt(dx * dx + dy * dy); + + if (displen > temp) { + dx *= temp/displen; + dy *= temp/displen; + } + + if (displen > 0) { + MATRIX(*res, v, 0) += dx; + MATRIX(*res, v, 1) += dy; + } + 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_destroy(&dispx); + igraph_vector_destroy(&dispy); + igraph_2dgrid_destroy(&grid); + IGRAPH_FINALLY_CLEAN(3); + return IGRAPH_SUCCESS; +} + +/** + * \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 that simulates an attractive force \c f_a between + * connected vertex pairs and a repulsive force \c f_r between all vertex pairs. + * The forces are computed as a function of the distance \c d between the two vertices as + * + * + * f_a(d) = -w * d^2 and f_r(d) = 1/d, + * + * + * where \c w represents the edge weight. The equilibrium distance of two connected + * vertices is thus 1/w^3, assuming no other forces acting on them. + * + * + * In disconnected graphs, igraph effectively inserts a weak connection of weight + * n^(-3/2) between all pairs of vertices, where \c n is the vertex count. + * This ensures that components are kept near each other. + * + * + * Reference: + * + * + * Fruchterman, T.M.J. and Reingold, E.M.: + * Graph Drawing by Force-directed Placement. + * Software -- Practice and Experience, 21/11, 1129--1164, + * 1991. https://doi.org/10.1002/spe.4380211102 + * + * \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 weights Pointer to a vector containing edge weights, + * the attraction along the edges will be multiplied by these. + * Weights must be positive. + * 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. + */ + +igraph_error_t 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 *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); + + 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 (weights && igraph_vector_size(weights) != no_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + if (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { + IGRAPH_ERROR("Weights must be positive for Fruchterman-Reingold layout.", 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, + weights, minx, maxx, miny, maxy); + } else { + return igraph_layout_i_fr(graph, res, use_seed, niter, start_temp, + weights, 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 weights Pointer to a vector containing edge weights, + * the attraction along the edges will be multiplied by these. + * Weights must be positive. + * 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. + * + */ + +igraph_error_t 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 *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) { + + const igraph_integer_t no_nodes = igraph_vcount(graph); + const igraph_integer_t no_edges = igraph_ecount(graph); + igraph_integer_t i; + igraph_vector_t dispx, dispy, dispz; + igraph_real_t temp = start_temp; + igraph_real_t difftemp = start_temp / niter; + igraph_bool_t conn = true; + igraph_real_t 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 (weights && igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + if (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { + IGRAPH_ERROR("Weights must be positive for Fruchterman-Reingold layout.", 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 * sqrt(no_nodes); + } + + if (!use_seed) { + igraph_i_layout_random_bounded_3d(graph, res, minx, maxx, miny, maxy, minz, maxz); + } + + IGRAPH_VECTOR_INIT_FINALLY(&dispx, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&dispz, no_nodes); + + RNG_BEGIN(); + 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_null(&dispx); + igraph_vector_null(&dispy); + igraph_vector_null(&dispz); + if (conn) { + for (v = 0; v < no_nodes; v++) { + for (u = v + 1; u < no_nodes; u++) { + 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 dlen = dx * dx + dy * dy + dz * dz; + + while (dlen == 0) { + dx = RNG_UNIF(-1e-9, 1e-9); + dy = RNG_UNIF(-1e-9, 1e-9); + dz = RNG_UNIF(-1e-9, 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++) { + 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 dlen, rdlen; + + dlen = dx * dx + dy * dy + dz * dz; + while (dlen == 0) { + dx = RNG_UNIF(-1e-9, 1e-9); + dy = RNG_UNIF(-1e-9, 1e-9); + dz = RNG_UNIF(-1e-9, 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 = weights ? VECTOR(*weights)[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_UNIF(-1e-9, 1e-9); + igraph_real_t dy = VECTOR(dispy)[v] + RNG_UNIF(-1e-9, 1e-9); + igraph_real_t dz = VECTOR(dispz)[v] + RNG_UNIF(-1e-9, 1e-9); + igraph_real_t displen = sqrt(dx * dx + dy * dy + dz * dz); + + if (displen > temp) { + dx *= temp/displen; + dy *= temp/displen; + dz *= temp/displen; + } + + if (displen > 0) { + MATRIX(*res, v, 0) += dx; + MATRIX(*res, v, 1) += dy; + MATRIX(*res, v, 2) += dz; + } + 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_destroy(&dispx); + igraph_vector_destroy(&dispy); + igraph_vector_destroy(&dispz); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/gem.c b/src/layout/gem.c new file mode 100644 index 0000000..4526b7e --- /dev/null +++ b/src/layout/gem.c @@ -0,0 +1,254 @@ +/* -*- 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 "igraph_structural.h" + +#include "core/interruption.h" +#include "core/math.h" + +/** + * \ingroup layout + * \function igraph_layout_gem + * \brief Layout graph according to GEM algorithm. + * + * 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, 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. + */ + +igraph_error_t 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_t impulse_x, impulse_y, temp, skew_gauge; + igraph_integer_t i; + igraph_real_t temp_global; + igraph_integer_t perm_pointer = 0; + igraph_real_t barycenter_x = 0, barycenter_y = 0; + igraph_vector_t phi; + igraph_vector_int_t neis; + const igraph_real_t elen_des2 = 128 * 128; + const igraph_real_t gamma = 1 / 16.0; + const igraph_real_t alpha_o = M_PI; + const igraph_real_t alpha_r = M_PI / 3.0; + const igraph_real_t sigma_o = 1.0 / 3.0; + const igraph_real_t sigma_r = 1.0 / 2.0 / no_nodes; + + if (maxiter < 0) { + IGRAPH_ERRORF("Number of iterations must be non-negative in GEM layout, " + "got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, maxiter); + } + if (use_seed && igraph_matrix_nrow(res) != no_nodes) { + IGRAPH_ERRORF("In GEM layout, seed matrix number of rows should equal number of nodes (%" IGRAPH_PRId "), got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, no_nodes, igraph_matrix_nrow(res)); + } + if (use_seed && igraph_matrix_ncol(res) != 2) { + IGRAPH_ERRORF("In GEM layout, seed matrix number of columns should be 2, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_matrix_ncol(res)); + } + if (temp_max <= 0) { + IGRAPH_ERRORF("Maximum temperature should be positive in GEM layout, got %g.", + IGRAPH_EINVAL, temp_max); + } + if (temp_min <= 0) { + IGRAPH_ERRORF("Minimum temperature should be positive in GEM layout, got %g.", + IGRAPH_EINVAL, temp_min); + } + if (temp_init <= 0) { + IGRAPH_ERRORF("Initial temperature should be positive in GEM layout, got %g.", + IGRAPH_EINVAL, temp_init); + } + if (temp_max < temp_init || temp_init < temp_min) { + IGRAPH_ERRORF("Minimum <= Initial <= Maximum temperature is required " + "in GEM layout, but %g is not larger than %g and smaller than %g.", IGRAPH_EINVAL, + temp_init, temp_min, temp_max); + } + + if (no_nodes == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&impulse_x, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&impulse_y, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&temp, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&skew_gauge, no_nodes); + IGRAPH_CHECK(igraph_vector_int_init_range(&perm, 0, no_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &perm); + IGRAPH_VECTOR_INIT_FINALLY(&phi, no_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 10); + + RNG_BEGIN(); + + /* Initialization */ + IGRAPH_CHECK(igraph_strength(graph, &phi, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, /* weights = */ 0)); + + 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_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; + igraph_real_t px, py, pvx, pvy; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* choose a vertex v to update */ + if (perm_pointer <= 0) { + 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++) { + igraph_real_t 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_int_size(&neis); + for (j = 0; j < nlen; j++) { + igraph_integer_t u = VECTOR(neis)[j]; + 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 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) { + igraph_real_t plen = sqrt(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) { + igraph_real_t beta = atan2(pvy - py, pvx - px); + igraph_real_t sin_beta = sin(beta); + igraph_real_t sign_sin_beta = (sin_beta > 0) ? 1 : ((sin_beta < 0) ? -1 : 0); + igraph_real_t cos_beta = cos(beta); + igraph_real_t abs_cos_beta = fabs(cos_beta); + igraph_real_t 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 >= cos(alpha_o / 2.0)) { + VECTOR(temp)[v] *= sigma_o * cos_beta; + } + VECTOR(temp)[v] *= (1 - fabs(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_int_destroy(&neis); + igraph_vector_destroy(&phi); + igraph_vector_int_destroy(&perm); + igraph_vector_destroy(&skew_gauge); + igraph_vector_destroy(&temp); + igraph_vector_destroy(&impulse_y); + igraph_vector_destroy(&impulse_x); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/graphopt.c b/src/layout/graphopt.c new file mode 100644 index 0000000..432cb64 --- /dev/null +++ b/src/layout/graphopt.c @@ -0,0 +1,434 @@ +/* -*- 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, + igraph_integer_t a, igraph_integer_t b); + +static void 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, + igraph_integer_t other_node, + igraph_integer_t this_node); + +static void igraph_i_apply_electrical_force( + const igraph_matrix_t *pos, + igraph_vector_t *pending_forces_x, + igraph_vector_t *pending_forces_y, + igraph_integer_t other_node, igraph_integer_t this_node, + igraph_real_t node_charge, + igraph_real_t distance); + +static void 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, + igraph_integer_t other_node, + igraph_integer_t this_node); + +static void igraph_i_apply_spring_force( + const igraph_matrix_t *pos, + igraph_vector_t *pending_forces_x, + igraph_vector_t *pending_forces_y, + igraph_integer_t other_node, + igraph_integer_t this_node, igraph_real_t spring_length, + igraph_real_t spring_constant); + +static void 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, + igraph_integer_t a, igraph_integer_t 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 void 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, + igraph_integer_t other_node, + igraph_integer_t 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; + } +} + +static void igraph_i_apply_electrical_force( + const igraph_matrix_t *pos, + igraph_vector_t *pending_forces_x, + igraph_vector_t *pending_forces_y, + igraph_integer_t other_node, igraph_integer_t 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; +} + +static void 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, + igraph_integer_t other_node, igraph_integer_t 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; + } +} + +static void igraph_i_apply_spring_force( + const igraph_matrix_t *pos, + igraph_vector_t *pending_forces_x, + igraph_vector_t *pending_forces_y, + igraph_integer_t other_node, + igraph_integer_t 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; + } + + 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; +} + +static void 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 + + igraph_integer_t 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; + + } +} + +/** + * \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, the support for + * layers was removed and the code was reorganized to avoid some + * unnecessary steps when 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|). + */ +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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); + + igraph_integer_t this_node, other_node, edge; + igraph_real_t distance; + igraph_integer_t 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++) { + igraph_integer_t tthis_node = IGRAPH_FROM(graph, edge); + igraph_integer_t 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/layout/kamada_kawai.c b/src/layout/kamada_kawai.c new file mode 100644 index 0000000..59f52cb --- /dev/null +++ b/src/layout/kamada_kawai.c @@ -0,0 +1,702 @@ +/* -*- 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 "core/interruption.h" +#include "layout/layout_internal.h" + +/* Energy gradient values below this threshold are considered to be zero. */ +#define KK_EPS 1e-13 + +/** + * \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 undirected 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. + * + * + * Disconnected graphs are handled by assuming that the graph distance between + * vertices in different components is the same as the graph diameter. + * + * + * This layout works particularly well for locally connected spatial networks + * such as lattices. + * + * + * 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. + * Weights must be positive. Pass \c NULL to assume unit weights + * for all 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. + */ + +igraph_error_t 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-negative 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 (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { + IGRAPH_ERROR("Weights must be positive for Kamada-Kawai layout.", 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) { + igraph_i_layout_random_bounded(graph, res, minx, maxx, miny, maxy); + } else { + igraph_layout_circle(graph, res, /* order= */ igraph_vss_all()); + /* The original paper recommends using a radius of 0.5*L0 here. + * The coefficient of 0.36 was chosen empirically so that this initial + * layout would be as close as possible to the equilibrium layout + * when the graph is a cycle graph. */ + igraph_matrix_scale(res, 0.36 * L0); + } + } + + if (no_nodes <= 1) { + return IGRAPH_SUCCESS; + } + + 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_distances_dijkstra(graph, &dij, igraph_vss_all(), + igraph_vss_all(), weights, IGRAPH_ALL)); + + /* Find largest finite distance */ + max_dij = 0.0; + for (i = 0; i < no_nodes; i++) { + for (j = i + 1; j < no_nodes; j++) { + if (!isfinite(MATRIX(dij, i, j))) { + continue; + } + if (MATRIX(dij, i, j) > max_dij) { + max_dij = MATRIX(dij, i, j); + } + } + } + + /* Replace infinite distances by the largest finite distance, + * effectively making the graph connected. */ + 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]; + + /* We need to solve the following linear equations, corresponding to + * eqs. (11) and (12) in the paper. + * + * A * delta_x + B * delta_y == myD1 + * B * delta_x + C * delta_y == myD2 + * + * We special-case the equilibrium case, i.e. when the energy gradient + * is zero and no displacement is necessary. This is important for the + * case of path graphs, where the determinant of the LHS will be + * zero in equilibrium, causing numerical problems. + */ + if (myD1*myD1 + myD2*myD2 < KK_EPS*KK_EPS) { + delta_x = 0; + delta_y = 0; + } else { + igraph_real_t det = C * A - B * B; + delta_y = (B * myD1 - A * myD2) / det; + delta_x = (B * myD2 - C * myD1) / det; + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \ingroup layout + * \function igraph_layout_kamada_kawai_3d + * \brief 3D version of the Kamada-Kawai layout generator. + * + * This is the 3D version of \ref 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. + * Weights must be positive. Pass \c NULL to assume unit weights + * for all 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. + */ + +igraph_error_t 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) { + + const igraph_integer_t no_nodes = igraph_vcount(graph); + const 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 (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { + IGRAPH_ERROR("Weights must be positive for Kamada-Kawai layout.", 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) { + igraph_i_layout_random_bounded_3d(graph, res, minx, maxx, miny, maxy, minz, maxz); + } else { + igraph_layout_sphere(graph, res); + /* The coefficient of 0.36 was chosen empirically so that this initial layout + * would be as close as possible to the equilibrium layout when the graph is + * a Goldberg polyhedron, i.e. having a naturally spherical layout. */ + igraph_matrix_scale(res, 0.36*L0); + } + } + + if (no_nodes <= 1) { + return IGRAPH_SUCCESS; + } + + 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_distances_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 (!isfinite(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_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)) + + /* See comments in 2D version for the reason for this check */ + if (Ax*Ax + Ay*Ay + Az*Az < KK_EPS*KK_EPS) { + delta_x = delta_y = delta_z = 0; + } else { + igraph_real_t detnum; + detnum = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Axz, Ayz, Azz); + delta_x = DET(Ax, Ay, Az, Axy, Ayy, Ayz, Axz, Ayz, Azz) / detnum; + delta_y = DET(Axx, Axy, Axz, Ax, Ay, Az, Axz, Ayz, Azz) / detnum; + delta_z = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Ax, Ay, Az ) / detnum; + } + + new_x = old_x + delta_x; + new_y = old_y + delta_y; + new_z = old_z + delta_z; + + /* Limits, if given */ + if (minx && new_x < VECTOR(*minx)[m]) { + new_x = VECTOR(*minx)[m]; + } + if (maxx && new_x > VECTOR(*maxx)[m]) { + new_x = VECTOR(*maxx)[m]; + } + if (miny && new_y < VECTOR(*miny)[m]) { + new_y = VECTOR(*miny)[m]; + } + if (maxy && new_y > VECTOR(*maxy)[m]) { + new_y = VECTOR(*maxy)[m]; + } + if (minz && new_z < VECTOR(*minz)[m]) { + new_z = VECTOR(*minz)[m]; + } + if (maxz && new_z > VECTOR(*maxz)[m]) { + new_z = VECTOR(*maxz)[m]; + } + + /* Update delta, only with/for the affected node */ + VECTOR(D1)[m] = VECTOR(D2)[m] = VECTOR(D3)[m] = 0.0; + for (i = 0; i < no_nodes; i++) { + igraph_real_t old_dx, old_dy, old_dz, old_mi_dist, new_dx, new_dy, new_dz, new_mi_dist; + if (i == m) { + continue; + } + old_dx = old_x - MATRIX(*res, i, 0); + old_dy = old_y - MATRIX(*res, i, 1); + old_dz = old_z - MATRIX(*res, i, 2); + old_mi_dist = sqrt(old_dx * old_dx + old_dy * old_dy + + old_dz * old_dz); + new_dx = new_x - MATRIX(*res, i, 0); + new_dy = new_y - MATRIX(*res, i, 1); + new_dz = new_z - MATRIX(*res, i, 2); + new_mi_dist = sqrt(new_dx * new_dx + new_dy * new_dy + + new_dz * new_dz); + + VECTOR(D1)[i] -= MATRIX(kij, m, i) * + (-old_dx + MATRIX(lij, m, i) * old_dx / old_mi_dist); + VECTOR(D2)[i] -= MATRIX(kij, m, i) * + (-old_dy + MATRIX(lij, m, i) * old_dy / old_mi_dist); + VECTOR(D3)[i] -= MATRIX(kij, m, i) * + (-old_dz + MATRIX(lij, m, i) * old_dz / old_mi_dist); + + VECTOR(D1)[i] += MATRIX(kij, m, i) * + (-new_dx + MATRIX(lij, m, i) * new_dx / new_mi_dist); + VECTOR(D2)[i] += MATRIX(kij, m, i) * + (-new_dy + MATRIX(lij, m, i) * new_dy / new_mi_dist); + VECTOR(D3)[i] += MATRIX(kij, m, i) * + (-new_dz + MATRIX(lij, m, i) * new_dz / new_mi_dist); + + VECTOR(D1)[m] += MATRIX(kij, m, i) * + (new_dx - MATRIX(lij, m, i) * new_dx / new_mi_dist); + VECTOR(D2)[m] += MATRIX(kij, m, i) * + (new_dy - MATRIX(lij, m, i) * new_dy / new_mi_dist); + VECTOR(D3)[m] += MATRIX(kij, m, i) * + (new_dz - MATRIX(lij, m, i) * new_dz / new_mi_dist); + } + + /* Update coordinates*/ + MATRIX(*res, m, 0) = new_x; + MATRIX(*res, m, 1) = new_y; + MATRIX(*res, m, 2) = new_z; + } + + igraph_vector_destroy(&D3); + igraph_vector_destroy(&D2); + igraph_vector_destroy(&D1); + igraph_matrix_destroy(&lij); + igraph_matrix_destroy(&kij); + igraph_matrix_destroy(&dij); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/large_graph.c b/src/layout/large_graph.c new file mode 100644 index 0000000..2bcbfa5 --- /dev/null +++ b/src/layout/large_graph.c @@ -0,0 +1,389 @@ +/* -*- 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 "igraph_random.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. It must be connnected; + * disconnected graphs are not handled by the algorithm. + * \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. + */ + +igraph_error_t 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) { + + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t root; + igraph_integer_t no_of_layers, actlayer = 0; + igraph_vector_int_t vids; + igraph_vector_int_t layers; + igraph_vector_int_t parents; + igraph_vector_int_t edges; + igraph_2dgrid_t grid; + igraph_vector_int_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; + + if (no_of_nodes == 0) { + /* We skip parameter checks for the null graph, as following the recommendations + * for parameter choices in the documentation would lead to zero values that are + * considered invalid in general, but don't cause problems for the null graph. */ + IGRAPH_CHECK(igraph_matrix_resize(res, 0, 2)); + return IGRAPH_SUCCESS; + } + + /* TODO: is zero okay? */ + if (maxit < 0) { + IGRAPH_ERRORF("Maximum number of iterations must not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, maxit); + } + + if (maxdelta <= 0) { + IGRAPH_ERRORF("Maximum delta must be positive, got %g.", IGRAPH_EINVAL, maxdelta); + } + + if (area <= 0) { + IGRAPH_ERRORF("Placement area size must be positive, got %g.", IGRAPH_EINVAL, area); + } + + if (coolexp <= 0) { + IGRAPH_ERRORF("Cooling exponent must be positive, got %g.", IGRAPH_EINVAL, coolexp); + } + + if (repulserad <= 0) { + IGRAPH_ERRORF("Repulsion cutoff radius must be positive, got %g.", IGRAPH_EINVAL, repulserad); + } + + if (cellsize <= 0) { + IGRAPH_ERRORF("Cell size must be positive, got %g.", IGRAPH_EINVAL, cellsize); + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + + /* Note: The LGL paper describes an algorithm that uses weights, and + * determines the layers by traversing the minimum spanning tree (MST) + * of the weighted graph starting from a chosen root. This function + * does not currently use weights, so all spanning trees are of the + * same weight. Therefore, we currently use a BFS traversal of the + * original graph from the root. + * + * TODO: If this function is updated to handle weights, it should + * construct the MST and traverse that instead. */ + + RNG_BEGIN(); + + /* 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_INT_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&layers, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&parents, 0); + if (no_of_nodes > 0) { + IGRAPH_CHECK(igraph_bfs_simple(graph, root, IGRAPH_ALL, &vids, &layers, &parents)); + } + no_of_layers = igraph_vector_int_size(&layers) - 1; + + /* Check whether we have reached all the nodes -- if not, the graph is + * disconnected */ + if (no_of_nodes > 0 && igraph_vector_int_min(&parents) <= -2) { + IGRAPH_WARNING("LGL layout does not support disconnected graphs yet."); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges)); + IGRAPH_VECTOR_INT_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; + igraph_integer_t i, j; + igraph_real_t massx, massy; + igraph_real_t px, py; + igraph_real_t sx, sy; + + igraph_integer_t it = 0; + igraph_real_t epsilon = 10e-6; + igraph_real_t maxchange = epsilon + 1; + /* igraph_integer_t 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 */ + /*-----------------------------------------*/ + + j = VECTOR(layers)[actlayer]; + for (i = VECTOR(layers)[actlayer - 1]; + i < VECTOR(layers)[actlayer]; i++) { + + igraph_integer_t vid = VECTOR(vids)[i]; + igraph_integer_t par = VECTOR(parents)[vid]; + + if (par < 0) { + /* this is either the root vertex or an unreachable node */ + continue; + } + + 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)[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, VECTOR(vids)[j], sx + rx, sy + ry); + j++; + } + } + + /*-----------------------------------------*/ + /* Step 2: add the edges of the next layer */ + /*-----------------------------------------*/ + + for (j = VECTOR(layers)[actlayer]; + j < VECTOR(layers)[actlayer + 1]; j++) { + igraph_integer_t vid = VECTOR(vids)[j]; + igraph_integer_t k; + IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_CHECK(igraph_incident(graph, &eids, vid, IGRAPH_ALL)); + for (k = 0; k < igraph_vector_int_size(&eids); k++) { + igraph_integer_t eid = VECTOR(eids)[k]; + igraph_integer_t from = IGRAPH_FROM(graph, eid), to = IGRAPH_TO(graph, eid); + if ((from != vid && igraph_2dgrid_in(&grid, from)) || + (to != vid && igraph_2dgrid_in(&grid, to))) { + igraph_vector_int_push_back(&edges, eid); + } + } + } + + /*-----------------------------------------*/ + /* Step 3: let the springs spring */ + /*-----------------------------------------*/ + + maxchange = epsilon + 1; + while (it < maxit && maxchange > epsilon) { + igraph_integer_t jj; + igraph_real_t t = maxdelta * pow((maxit - it) / (igraph_real_t) maxit, coolexp); + igraph_integer_t vid, nei; + + IGRAPH_PROGRESS("Large graph layout", + 100.0 * ((actlayer - 1.0) / (no_of_layers - 1.0) + (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_int_size(&edges); jj++) { + igraph_integer_t from = IGRAPH_FROM(graph, VECTOR(edges)[jj]); + igraph_integer_t to = IGRAPH_TO(graph, VECTOR(edges)[jj]); + igraph_real_t xd, yd, dist, force; + IGRAPH_ALLOW_INTERRUPTION(); + xd = MATRIX(*res, from, 0) - MATRIX(*res, to, 0); + yd = MATRIX(*res, from, 1) - MATRIX(*res, to, 1); + dist = sqrt(xd*xd + yd*yd); + if (dist != 0) { + xd /= dist; + yd /= dist; + } + force = dist * dist / frk; + VECTOR(forcex)[from] -= xd * force; + VECTOR(forcex)[to] += xd * force; + VECTOR(forcey)[from] -= yd * force; + VECTOR(forcey)[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, vid, 0) - MATRIX(*res, nei, 0); + igraph_real_t yd = MATRIX(*res, vid, 1) - MATRIX(*res, 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)[vid] += xd * force; + VECTOR(forcex)[nei] -= xd * force; + VECTOR(forcey)[vid] += yd * force; + VECTOR(forcey)[nei] -= yd * force; + } + } + } + + /* printf("verties: %li iterations: %li\n", */ + /* VECTOR(layers)[actlayer+1], pairs); */ + + /* apply the changes */ + for (jj = 0; jj < VECTOR(layers)[actlayer + 1]; jj++) { + igraph_integer_t vvid = 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); */ + } + } + + RNG_END(); + + IGRAPH_PROGRESS("Large graph layout", 100.0, 0); + igraph_vector_int_destroy(&vids); + igraph_vector_int_destroy(&layers); + igraph_vector_int_destroy(&parents); + igraph_vector_int_destroy(&edges); + igraph_2dgrid_destroy(&grid); + igraph_vector_int_destroy(&eids); + igraph_vector_destroy(&forcex); + igraph_vector_destroy(&forcey); + IGRAPH_FINALLY_CLEAN(8); + return IGRAPH_SUCCESS; + +} diff --git a/src/layout/layout_bipartite.c b/src/layout/layout_bipartite.c new file mode 100644 index 0000000..92d018b --- /dev/null +++ b/src/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(). + */ +igraph_error_t 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, igraph_integer_t maxiter) { + + igraph_integer_t i, no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t layers; + + if (igraph_vector_bool_size(types) != no_of_nodes) { + IGRAPH_ERRORF("The vertex type vector size (%" IGRAPH_PRId ") should be equal to the number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_bool_size(types), no_of_nodes); + } + if (hgap < 0) { + IGRAPH_ERRORF("The horizontal gap cannot be negative, got %g.", IGRAPH_EINVAL, hgap); + } + + IGRAPH_VECTOR_INT_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_int_destroy(&layers); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/layout_grid.c b/src/layout/layout_grid.c new file mode 100644 index 0000000..16ec85b --- /dev/null +++ b/src/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. + */ +igraph_error_t igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t width) { + igraph_integer_t 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 = 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t width, igraph_integer_t height) { + igraph_integer_t 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 = ceil(pow(no_of_nodes, 1.0 / 3)); + } else if (width <= 0) { + width = ceil(sqrt(no_of_nodes / (double)height)); + } else if (height <= 0) { + height = 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 IGRAPH_SUCCESS; +} diff --git a/src/layout/layout_internal.h b/src/layout/layout_internal.h new file mode 100644 index 0000000..a9b193a --- /dev/null +++ b/src/layout/layout_internal.h @@ -0,0 +1,76 @@ +/* -*- 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_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_matrix.h" + +#include "layout/merge_grid.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, + igraph_integer_t 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 igraph_error_t igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x, + igraph_real_t *y, igraph_real_t *r); + +IGRAPH_PRIVATE_EXPORT igraph_error_t 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 IGRAPH_FUNCATTR_CONST igraph_real_t igraph_i_layout_point_segment_dist2( + igraph_real_t v_x, igraph_real_t v_y, + igraph_real_t u1_x, igraph_real_t u1_y, + igraph_real_t u2_x, igraph_real_t u2_y); + +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_CONST igraph_bool_t igraph_i_layout_segments_intersect( + igraph_real_t p0_x, igraph_real_t p0_y, + igraph_real_t p1_x, igraph_real_t p1_y, + igraph_real_t p2_x, igraph_real_t p2_y, + igraph_real_t p3_x, igraph_real_t p3_y); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_umap_fit_ab(igraph_real_t min_dist, + igraph_real_t *a_p, + igraph_real_t *b_p); + +igraph_error_t igraph_i_layout_random_bounded( + const igraph_t *graph, igraph_matrix_t *res, + const igraph_vector_t *minx, const igraph_vector_t *maxx, + const igraph_vector_t *miny, const igraph_vector_t *maxy); + +igraph_error_t igraph_i_layout_random_bounded_3d( + const igraph_t *graph, igraph_matrix_t *res, + 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); + +__END_DECLS + +#endif diff --git a/src/layout/layout_random.c b/src/layout/layout_random.c new file mode 100644 index 0000000..429ff1d --- /dev/null +++ b/src/layout/layout_random.c @@ -0,0 +1,288 @@ +/* -*- 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 "layout/layout_internal.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. + */ +igraph_error_t igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ +igraph_error_t igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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; +} + + +/* The following functions generate suitable initial random layouts for + * the Fruchterman-Reingold and Kamada-Kawai algorithms. */ + +igraph_error_t igraph_i_layout_random_bounded( + const igraph_t *graph, + igraph_matrix_t *res, + const igraph_vector_t *minx, const igraph_vector_t *maxx, + const igraph_vector_t *miny, const igraph_vector_t *maxy) { + + const igraph_integer_t no_nodes = igraph_vcount(graph); + const igraph_real_t width = sqrt(no_nodes), height = width; + + igraph_real_t dminx = -width/2, dmaxx = width/2, + dminy = -height/2, dmaxy = height/2; /* default values */ + + /* Caller should ensure that minx, etc. do not contain NaN. */ + + if (minx && !igraph_vector_empty(minx)) { + igraph_real_t m = igraph_vector_max(minx); + if (m == IGRAPH_POSINFINITY) { + IGRAPH_ERROR("Infinite lower coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m > dmaxx) { + dmaxx += m; + } + } + if (maxx && !igraph_vector_empty(maxx)) { + igraph_real_t m = igraph_vector_min(maxx); + if (m == IGRAPH_NEGINFINITY) { + IGRAPH_ERROR("Negative infinite upper coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m < dminx) { + dminx -= m; + } + } + if (miny && !igraph_vector_empty(miny)) { + igraph_real_t m = igraph_vector_max(miny); + if (m == IGRAPH_POSINFINITY) { + IGRAPH_ERROR("Infinite lower coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m > dmaxy) { + dmaxy += m; + } + } + if (maxy && !igraph_vector_empty(maxy)) { + igraph_real_t m = igraph_vector_min(maxy); + if (m == IGRAPH_NEGINFINITY) { + IGRAPH_ERROR("Negative infinite upper coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m < dminy) { + dminy -= m; + } + } + + RNG_BEGIN(); + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); + for (igraph_integer_t i = 0; i < no_nodes; i++) { + igraph_real_t x1 = minx ? VECTOR(*minx)[i] : dminx; + igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : dmaxx; + igraph_real_t y1 = miny ? VECTOR(*miny)[i] : dminy; + igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : dmaxy; + if (!isfinite(x1)) { + x1 = -width / 2; + } + if (!isfinite(x2)) { + x2 = width / 2; + } + if (!isfinite(y1)) { + y1 = -height / 2; + } + if (!isfinite(y2)) { + y2 = height / 2; + } + MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); + MATRIX(*res, i, 1) = RNG_UNIF(y1, y2); + } + RNG_END(); + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_i_layout_random_bounded_3d( + const igraph_t *graph, igraph_matrix_t *res, + 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) { + + const igraph_integer_t no_nodes = igraph_vcount(graph); + const igraph_real_t width = sqrt(no_nodes), height = width, depth = width; + + igraph_real_t dminx = -width/2, dmaxx = width/2, + dminy = -height/2, dmaxy = height/2, + dminz = -depth/2, dmaxz = depth/2; /* default values */ + + /* Caller should ensure that minx, etc. do not contain NaN. */ + + if (minx && !igraph_vector_empty(minx)) { + igraph_real_t m = igraph_vector_max(minx); + if (m == IGRAPH_POSINFINITY) { + IGRAPH_ERROR("Infinite lower coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m > dmaxx) { + dmaxx += m; + } + } + if (maxx && !igraph_vector_empty(maxx)) { + igraph_real_t m = igraph_vector_min(maxx); + if (m == IGRAPH_NEGINFINITY) { + IGRAPH_ERROR("Negative infinite upper coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m < dminx) { + dminx -= m; + } + } + if (miny && !igraph_vector_empty(miny)) { + igraph_real_t m = igraph_vector_max(miny); + if (m == IGRAPH_POSINFINITY) { + IGRAPH_ERROR("Infinite lower coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m > dmaxy) { + dmaxy += m; + } + } + if (maxy && !igraph_vector_empty(maxy)) { + igraph_real_t m = igraph_vector_min(maxy); + if (m == IGRAPH_NEGINFINITY) { + IGRAPH_ERROR("Negative infinite upper coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m < dminy) { + dminy -= m; + } + } + if (minz && !igraph_vector_empty(minz)) { + igraph_real_t m = igraph_vector_max(minz); + if (m == IGRAPH_POSINFINITY) { + IGRAPH_ERROR("Infinite lower coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m > dmaxz) { + dmaxz += m; + } + } + if (maxz && !igraph_vector_empty(maxz)) { + igraph_real_t m = igraph_vector_min(maxz); + if (m == IGRAPH_NEGINFINITY) { + IGRAPH_ERROR("Negative infinite upper coordinate bound for graph layout.", IGRAPH_EINVAL); + } + if (m < dminz) { + dminz -= m; + } + } + + RNG_BEGIN(); + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 3)); + for (igraph_integer_t i = 0; i < no_nodes; i++) { + igraph_real_t x1 = minx ? VECTOR(*minx)[i] : dminx; + igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : dmaxx; + igraph_real_t y1 = miny ? VECTOR(*miny)[i] : dminy; + igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : dmaxy; + igraph_real_t z1 = minz ? VECTOR(*minz)[i] : dminz; + igraph_real_t z2 = maxz ? VECTOR(*maxz)[i] : dmaxz; + if (!isfinite(x1)) { + x1 = -width / 2; + } + if (!isfinite(x2)) { + x2 = width / 2; + } + if (!isfinite(y1)) { + y1 = -height / 2; + } + if (!isfinite(y2)) { + y2 = height / 2; + } + if (!isfinite(z1)) { + z1 = -depth / 2; + } + if (!isfinite(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(); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/mds.c b/src/layout/mds.c new file mode 100644 index 0000000..97d0cc3 --- /dev/null +++ b/src/layout/mds.c @@ -0,0 +1,304 @@ +/* -*- 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" + +#include + +static igraph_error_t igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra); + +static igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, igraph_integer_t dim); + +static igraph_error_t 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_CHECK(igraph_blas_dgemv_array(0, 1, matrix, from, 0, to)); + return IGRAPH_SUCCESS; +} + +/* MDS layout for a connected graph, with no error checking on the + * input parameters. The distance matrix will be modified in-place. */ +igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, igraph_integer_t dim) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t nev = dim; + igraph_matrix_t vectors; + igraph_vector_t values, row_means; + igraph_real_t grand_mean; + igraph_integer_t i, j, k; + igraph_eigen_which_t which; + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph too large for eigenvector calculations", IGRAPH_EOVERFLOW); + } + + if (nev > INT_MAX) { + IGRAPH_ERROR("Dimensionality too large for eigenvector calculations", IGRAPH_EOVERFLOW); + } + + /* Handle the trivial cases */ + if (no_of_nodes == 1) { + IGRAPH_CHECK(igraph_matrix_resize(res, 1, dim)); + igraph_matrix_null(res); + return IGRAPH_SUCCESS; + } + if (no_of_nodes == 2) { + IGRAPH_CHECK(igraph_matrix_resize(res, 2, dim)); + igraph_matrix_null(res); + 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_CHECK(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 undirected 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). + */ + +igraph_error_t igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, + const igraph_matrix_t *dist, igraph_integer_t dim) { + igraph_integer_t 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 (no_of_nodes > 0 && 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_MATRIX_INIT_FINALLY(&m, no_of_nodes, no_of_nodes); + IGRAPH_CHECK(igraph_distances(graph, &m, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL)); + } else { + IGRAPH_CHECK(igraph_matrix_init_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_matrix_list_t layouts; + igraph_vector_int_t vertex_order; + igraph_vector_int_t comp; + igraph_t subgraph; + igraph_matrix_t layout; + igraph_matrix_t dist_submatrix; + igraph_bool_t *seen_vertices; + igraph_integer_t j, n, processed_vertex_count = 0; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&comp, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_order, no_of_nodes); + + IGRAPH_MATRIX_LIST_INIT_FINALLY(&layouts, 0); + IGRAPH_MATRIX_INIT_FINALLY(&layout, 0, 0); + + 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); /* LCOV_EXCL_LINE */ + } + 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)); + /* Lay out the subgraph */ + IGRAPH_CHECK(igraph_i_layout_mds_single(&subgraph, &layout, &dist_submatrix, dim)); + /* Store the layout */ + IGRAPH_CHECK(igraph_matrix_list_push_back_copy(&layouts, &layout)); + /* Free the newly created subgraph */ + igraph_destroy(&subgraph); + IGRAPH_FINALLY_CLEAN(1); + /* Mark all the vertices in the component as visited */ + n = igraph_vector_int_size(&comp); + for (j = 0; j < n; j++) { + seen_vertices[VECTOR(comp)[j]] = 1; + VECTOR(vertex_order)[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_matrix_destroy(&layout); + igraph_matrix_list_destroy(&layouts); + igraph_vector_int_destroy(&vertex_order); + igraph_vector_int_destroy(&comp); + IGRAPH_FINALLY_CLEAN(6); + } + + RNG_END(); + + igraph_matrix_destroy(&m); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/merge_dla.c b/src/layout/merge_dla.c new file mode 100644 index 0000000..7082540 --- /dev/null +++ b/src/layout/merge_dla.c @@ -0,0 +1,292 @@ +/* -*- 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/interruption.h" +#include "core/math.h" +#include "layout/merge_grid.h" +#include "layout/layout_internal.h" + +/** + * \function igraph_layout_merge_dla + * \brief Merges multiple layouts by using a DLA algorithm. + * + * \experimental + * + * 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 List of matrices 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. + * + * + * Time complexity: TODO. + */ + +igraph_error_t igraph_layout_merge_dla( + const igraph_vector_ptr_t *thegraphs, const igraph_matrix_list_t *coords, + igraph_matrix_t *res +) { + igraph_integer_t coords_len = igraph_matrix_list_size(coords); + igraph_vector_t sizes; + igraph_vector_t x, y, r; + igraph_vector_t nx, ny, nr; + igraph_integer_t allnodes = 0; + igraph_integer_t i, j; + igraph_integer_t actg; + igraph_i_layout_mergegrid_t grid; + igraph_integer_t jpos = 0; + igraph_real_t minx, maxx, miny, maxy; + igraph_real_t area = 0; + igraph_real_t maxr = 0; + igraph_integer_t respos; + + /* Graphs are currently not used, only the coordinates */ + IGRAPH_UNUSED(thegraphs); + + IGRAPH_VECTOR_INIT_FINALLY(&sizes, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&x, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&y, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&r, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&nx, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&ny, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&nr, coords_len); + + RNG_BEGIN(); + + for (i = 0; i < coords_len; i++) { + igraph_matrix_t *mat = igraph_matrix_list_get_ptr(coords, i); + igraph_integer_t 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_get_ptr(&nx, i), + igraph_vector_get_ptr(&ny, i), + igraph_vector_get_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 = 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 < coords_len) { + IGRAPH_ALLOW_INTERRUPTION(); + /* fprintf(stderr, "comp: %li", jpos); */ + IGRAPH_PROGRESS("Merging layouts via DLA", (100.0 * jpos) / coords_len, NULL); + + actg = VECTOR(sizes)[jpos++]; + /* 2. random walk, TODO: tune parameters */ + igraph_i_layout_merge_dla(&grid, actg, + igraph_vector_get_ptr(&x, actg), + igraph_vector_get_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 < coords_len; i++) { + igraph_matrix_t *mat = igraph_matrix_list_get_ptr(coords, i); + igraph_integer_t size = igraph_matrix_nrow(mat); + 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_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 IGRAPH_SUCCESS; +} + +igraph_error_t igraph_i_layout_sphere_2d(igraph_matrix_t *coords, + igraph_real_t *x, igraph_real_t *y, + igraph_real_t *r) { + igraph_integer_t nodes = igraph_matrix_nrow(coords); + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +igraph_error_t 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_integer_t nodes = igraph_matrix_nrow(coords); + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +#define DIST(x,y) (sqrt(pow((x)-cx,2)+pow((y)-cy,2))) + +igraph_error_t igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, + igraph_integer_t 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_integer_t sp = -1; + igraph_real_t angle, len; + + /* The graph is not used, only its coordinates */ + IGRAPH_UNUSED(actg); + + while (sp < 0) { + /* start particle */ + do { + 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; + 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; + } + } + } + + return IGRAPH_SUCCESS; +} diff --git a/src/layout/merge_grid.c b/src/layout/merge_grid.c new file mode 100644 index 0000000..38e36d5 --- /dev/null +++ b/src/layout/merge_grid.c @@ -0,0 +1,218 @@ +/* -*- 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 "layout/merge_grid.h" + +#include "igraph_memory.h" + + +static igraph_error_t igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid, + igraph_real_t xc, igraph_real_t yc, + igraph_integer_t *x, igraph_integer_t *y) { + if (xc <= grid->minx) { + *x = 0; + } else if (xc >= grid->maxx) { + *x = grid->stepsx - 1; + } else { + *x = floor((xc - (grid->minx)) / (grid->deltax)); + } + + if (yc <= grid->miny) { + *y = 0; + } else if (yc >= grid->maxy) { + *y = grid->stepsy - 1; + } else { + *y = floor((yc - (grid->miny)) / (grid->deltay)); + } + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, + igraph_real_t minx, igraph_real_t maxx, igraph_integer_t stepsx, + igraph_real_t miny, igraph_real_t maxy, igraph_integer_t 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, igraph_integer_t); + if (grid->data == 0) { + IGRAPH_ERROR("Cannot create grid", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + return IGRAPH_SUCCESS; +} + +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))) + +igraph_error_t igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y, igraph_real_t r, + igraph_integer_t id) { + igraph_integer_t cx, cy; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +igraph_integer_t igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y) { + igraph_integer_t cx, cy; + igraph_integer_t 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))) + +igraph_integer_t igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y, igraph_real_t r) { + igraph_integer_t cx, cy; + igraph_integer_t i, j; + igraph_integer_t 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) { */ +/* igraph_integer_t i,j; */ + +/* for (i=0; istepsx; i++) { */ +/* for (j=0; jstepsy; j++) { */ +/* printf("%li ", MAT(i,j)-1); */ +/* } */ +/* printf("\n"); */ +/* } */ +/* } */ diff --git a/src/layout/merge_grid.h b/src/layout/merge_grid.h new file mode 100644 index 0000000..82c6518 --- /dev/null +++ b/src/layout/merge_grid.h @@ -0,0 +1,59 @@ +/* -*- 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_error.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 { + igraph_integer_t *data; + igraph_integer_t stepsx, stepsy; + igraph_real_t minx, maxx, deltax; + igraph_real_t miny, maxy, deltay; +} igraph_i_layout_mergegrid_t; + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, + igraph_real_t minx, igraph_real_t maxx, igraph_integer_t stepsx, + igraph_real_t miny, igraph_real_t maxy, igraph_integer_t stepsy); + +IGRAPH_PRIVATE_EXPORT void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y, igraph_real_t r, + igraph_integer_t id); + +igraph_integer_t igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y); + +igraph_integer_t 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/layout/reingold_tilford.c b/src/layout/reingold_tilford.c new file mode 100644 index 0000000..607368e --- /dev/null +++ b/src/layout/reingold_tilford.c @@ -0,0 +1,1012 @@ +/* -*- 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 igraph_error_t igraph_i_layout_reingold_tilford_unreachable( + const igraph_t *graph, + igraph_neimode_t mode, + igraph_integer_t real_root, + igraph_integer_t no_of_nodes, + igraph_vector_int_t *pnewedges) { + + igraph_integer_t no_of_newedges; + igraph_vector_bool_t visited; + igraph_integer_t i, j, n; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + igraph_adjlist_t allneis; + igraph_vector_int_t *neis; + + igraph_vector_int_clear(pnewedges); + + /* traverse from real_root and see what nodes you cannot reach */ + no_of_newedges = 0; + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, no_of_nodes); + IGRAPH_DQUEUE_INT_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_int_push(&q, real_root)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + VECTOR(visited)[actnode] = true; + for (j = 0; j < n; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (!VECTOR(visited)[neighbor]) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + } + } + } + + for (j = 0; j < no_of_nodes; j++) { + no_of_newedges += VECTOR(visited)[j] ? 0 : 1; + } + + /* if any nodes are unreachable, add edges between them and real_root */ + if (no_of_newedges != 0) { + + igraph_vector_int_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_int_destroy(&q); + igraph_adjlist_destroy(&allneis); + igraph_vector_bool_destroy(&visited); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Internal structure for Reingold-Tilford layout */ +struct igraph_i_reingold_tilford_vertex { + igraph_integer_t parent; /* Parent node index */ + igraph_integer_t level; /* Level of the node */ + igraph_real_t offset; /* X offset from parent node */ + igraph_integer_t left_contour; /* Next left node of the contour + of the subtree rooted at this node */ + igraph_integer_t 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 */ + igraph_integer_t left_extreme; /* Leftmost node on the deepest layer of the subtree rooted at this node */ + igraph_integer_t 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 void igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata, + igraph_integer_t node, igraph_integer_t vcount); +static void igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata, + igraph_matrix_t *res, igraph_integer_t node, + igraph_integer_t vcount, igraph_real_t xpos); + +/* uncomment the next line for debugging the Reingold-Tilford layout */ +/* #define LAYOUT_RT_DEBUG 1 */ + +static igraph_error_t igraph_i_layout_reingold_tilford(const igraph_t *graph, + igraph_matrix_t *res, + igraph_neimode_t mode, + igraph_integer_t root) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, n, j; + igraph_dqueue_int_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_INT_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); /* LCOV_EXCL_LINE */ + } + 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_int_push(&q, root)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + + for (j = 0; j < n; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (vdata[neighbor].parent >= 0) { + continue; + } + MATRIX(*res, neighbor, 1) = actdist + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_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_int_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( + "%3" IGRAPH_PRId ": offset = %.2f, contours = [%" IGRAPH_PRId ", %" IGRAPH_PRId "], 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 = [%" IGRAPH_PRId ", %" IGRAPH_PRId "], 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 IGRAPH_SUCCESS; +} + +static void igraph_i_layout_reingold_tilford_calc_coords( + struct igraph_i_reingold_tilford_vertex *vdata, + igraph_matrix_t *res, igraph_integer_t node, + igraph_integer_t vcount, igraph_real_t xpos) { + + MATRIX(*res, node, 0) = xpos; + for (igraph_integer_t 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); + } + } +} + +static void igraph_i_layout_reingold_tilford_postorder( + struct igraph_i_reingold_tilford_vertex *vdata, + igraph_integer_t node, igraph_integer_t vcount) { + + igraph_integer_t childcount, leftroot, leftrootidx; + const igraph_real_t minsep = 1; + igraph_real_t avg; + +#ifdef LAYOUT_RT_DEBUG + printf("Starting visiting node %" IGRAPH_PRId "\n", node); +#endif + + /* Check whether this node is a leaf node */ + childcount = 0; + for (igraph_integer_t 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; + } + + /* 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 %" IGRAPH_PRId " and arranged its subtrees\n", node); +#endif + for (igraph_integer_t 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 */ + igraph_integer_t lnode, rnode, auxnode; + igraph_real_t loffset, roffset, rootsep, newoffset; + +#ifdef LAYOUT_RT_DEBUG + printf(" Placing child %" IGRAPH_PRId " on level %" IGRAPH_PRId ", to the right of %" IGRAPH_PRId "\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: [%" IGRAPH_PRId ", %" IGRAPH_PRId "], 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 %" IGRAPH_PRId " gets connected to node %" IGRAPH_PRId ")\n", auxnode, vdata[rnode].left_contour); + printf(" New contour following offset for node %" IGRAPH_PRId " 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 %" IGRAPH_PRId " gets connected to node %" IGRAPH_PRId ")\n", auxnode, lnode); + printf(" New contour following offset for node %" IGRAPH_PRId " is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); +#endif + } + rnode = -1; + } +#ifdef LAYOUT_RT_DEBUG + printf(" Contour: [%" IGRAPH_PRId ", %" IGRAPH_PRId "], 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 %" IGRAPH_PRId " 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 %" IGRAPH_PRId " on level %" IGRAPH_PRId " 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 %" IGRAPH_PRId " 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 (igraph_integer_t i = 0; i < vcount; i++) { + if (i == node) { + continue; + } + if (vdata[i].parent == node) { + vdata[i].offset -= avg; + } + } +} + +/* This function computes the number of outgoing (or incoming) connections + * of clusters, represented as a membership vector. It only works with + * directed graphs. */ +igraph_error_t igraph_i_layout_reingold_tilford_cluster_degrees_directed( + const igraph_t *graph, + const igraph_vector_int_t *membership, + igraph_integer_t no_comps, + igraph_neimode_t mode, + igraph_vector_int_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_int_resize(degrees, no_comps)); + igraph_vector_int_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 eccentricity (if 'use_eccentricity' is true) or based on + * highest degree (out- or in-degree in directed mode). + */ + +/** + * \function igraph_roots_for_tree_layout + * \brief Roots suitable for a nice tree layout. + * + * This function chooses a root, or a set of roots suitable for visualizing a tree, + * or a tree-like graph. It is typically used with \ref igraph_layout_reingold_tilford(). + * 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, vertices are prioritized based on the given \p heuristic. + * + * \param graph The graph, typically a tree, but any graph is accepted. + * \param mode Whether to interpret the input as undirected, a directed out-tree or in-tree. + * \param roots An initialized integer vector, the roots will be returned here. + * \param heuristic The heuristic to use for breaking ties when multiple root + * choices are possible. + * \clist + * \cli IGRAPH_ROOT_CHOICE_DEGREE + * Choose the vertices with the highest degree (out- or in-degree + * in directed mode). This simple heuristic is fast even in large graphs. + * \cli IGRAPH_ROOT_CHOICE_ECCENTRICITY + * Choose the vertices with the lowest eccentricity. This usually results + * in a "wide and shallow" tree layout. While this heuristic produces + * high-quality results, it is slow for large graphs: computing the + * eccentricities has quadratic complexity in the number of vertices. + * \endclist + * \return Error code. + * + * Time complexity: depends on the heuristic. + */ +igraph_error_t igraph_roots_for_tree_layout( + const igraph_t *graph, + igraph_neimode_t mode, + igraph_vector_int_t *roots, + igraph_root_choice_t heuristic) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t order, membership; + igraph_integer_t no_comps; + igraph_integer_t i, j; + igraph_bool_t use_eccentricity; + + switch (heuristic) { + case IGRAPH_ROOT_CHOICE_DEGREE: + use_eccentricity = false; break; + case IGRAPH_ROOT_CHOICE_ECCENTRICITY: + use_eccentricity = true; break; + default: + IGRAPH_ERROR("Invalid root choice heuristic given.", IGRAPH_EINVAL); + } + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (no_of_nodes == 0) { + igraph_vector_int_clear(roots); + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + if (use_eccentricity) { + /* Sort vertices by decreasing eccentricity. */ + + 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, IGRAPH_ASCENDING)); + + 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_INT_INIT_FINALLY(&membership, no_of_nodes); + IGRAPH_CHECK(igraph_connected_components( + graph, &membership, /*csize=*/ NULL, + &no_comps, mode == IGRAPH_ALL ? IGRAPH_WEAK : IGRAPH_STRONG + )); + + IGRAPH_CHECK(igraph_vector_int_resize(roots, no_comps)); + igraph_vector_int_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_int_t cluster_degrees; + + IGRAPH_VECTOR_INT_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) { + igraph_integer_t v = VECTOR(order)[i]; + igraph_integer_t cl = VECTOR(membership)[v]; + if (VECTOR(cluster_degrees)[cl] == 0 && VECTOR(*roots)[cl] == -1) { + VECTOR(*roots)[cl] = v; + } + } + + igraph_vector_int_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_int_resize(roots, j); + + } else { + /* Undirected case: + * + * Select the highest degree node from each component. + */ + + igraph_integer_t no_seen = 0; + + for (i=0; i < no_of_nodes; ++i) { + igraph_integer_t v = VECTOR(order)[i]; + igraph_integer_t 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_int_destroy(&membership); + igraph_vector_int_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 undirected 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 eccentricity vertices in graphs with fewer than + * 500 vertices, and high degree vertices (according to \p mode) in larger graphs. + * The root selection heuristic may change without notice. To ensure a consistent + * output, please specify the roots manually. The \ref igraph_roots_for_tree_layout() + * function gives more control over automatic root selection. + * \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(), \ref igraph_roots_for_tree_layout() + * + * \example examples/simple/igraph_layout_reingold_tilford.c + */ +igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, + igraph_matrix_t *res, + igraph_neimode_t mode, + const igraph_vector_int_t *roots, + const igraph_vector_int_t *rootlevel) { + + const igraph_integer_t no_of_nodes_orig = igraph_vcount(graph); + igraph_integer_t no_of_nodes = no_of_nodes_orig; + igraph_integer_t real_root; + igraph_t extended; + const igraph_t *pextended = graph; + igraph_vector_int_t myroots; + const igraph_vector_int_t *proots = roots; + igraph_vector_int_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_INT_INIT_FINALLY(&newedges, 0); + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if ( (!roots || igraph_vector_int_size(roots) == 0) && + rootlevel && igraph_vector_int_size(rootlevel) != 0 ) { + IGRAPH_WARNING("Reingold-Tilford layout: 'rootlevel' ignored"); + } + + /* ----------------------------------------------------------------------- */ + /* If root vertices are not given, perform automated root selection. */ + + if (!roots || igraph_vector_int_size(roots) == 0) { + + IGRAPH_VECTOR_INT_INIT_FINALLY(&myroots, 0); + igraph_roots_for_tree_layout(graph, mode, &myroots, + no_of_nodes < 500 ? IGRAPH_ROOT_CHOICE_DEGREE : IGRAPH_ROOT_CHOICE_ECCENTRICITY); + proots = &myroots; + + } else if (rootlevel && igraph_vector_int_size(rootlevel) > 0 && + igraph_vector_int_size(roots) > 1) { + + /* ----------------------------------------------------------------------- */ + /* Many roots were given to us, check 'rootlevel' */ + + igraph_integer_t plus_levels = 0; + + if (igraph_vector_int_size(roots) != igraph_vector_int_size(rootlevel)) { + IGRAPH_ERROR("Reingold-Tilford: 'roots' and 'rootlevel' lengths differ", + IGRAPH_EINVAL); + } + + /* count the rootlevels that are not zero */ + for (igraph_integer_t i = 0; i < igraph_vector_int_size(roots); i++) { + plus_levels += VECTOR(*rootlevel)[i]; + } + + /* make copy of graph, add vertices/edges */ + if (plus_levels != 0) { + igraph_integer_t edgeptr = 0; + + pextended = &extended; + IGRAPH_CHECK(igraph_copy(&extended, graph)); + IGRAPH_FINALLY(igraph_destroy, &extended); + IGRAPH_CHECK(igraph_add_vertices(&extended, plus_levels, 0)); + + igraph_vector_int_resize(&newedges, plus_levels * 2); + + for (igraph_integer_t i = 0; i < igraph_vector_int_size(roots); i++) { + igraph_integer_t rl = VECTOR(*rootlevel)[i]; + igraph_integer_t rn = VECTOR(*roots)[i]; + igraph_integer_t 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_int_size(proots) == 1) { + real_root = 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 { + igraph_integer_t 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_int_size(proots); + igraph_vector_int_resize(&newedges, no_of_newedges * 2); + for (igraph_integer_t 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_int_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_int_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; + igraph_integer_t 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_int_destroy(&myroots); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \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 undirected 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 eccentricity vertices in graphs with fewer than + * 500 vertices, and high degree vertices (according to \p mode) in larger graphs. + * The root selection 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(). + */ +igraph_error_t igraph_layout_reingold_tilford_circular(const igraph_t *graph, + igraph_matrix_t *res, + igraph_neimode_t mode, + const igraph_vector_int_t *roots, + const igraph_vector_int_t *rootlevel) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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 (igraph_integer_t 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 (igraph_integer_t 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/layout/sugiyama.c b/src/layout/sugiyama.c new file mode 100644 index 0000000..0107bf2 --- /dev/null +++ b/src/layout/sugiyama.c @@ -0,0 +1,1328 @@ +/* -*- 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_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_int_list_t layers; +} igraph_i_layering_t; + +/** + * Initializes a layering. + */ +static igraph_error_t igraph_i_layering_init(igraph_i_layering_t* layering, + const igraph_vector_int_t* membership) { + igraph_integer_t i, n, num_layers; + + if (igraph_vector_int_size(membership) == 0) { + num_layers = 0; + } else { + num_layers = igraph_vector_int_max(membership) + 1; + } + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&layering->layers, num_layers); + + n = igraph_vector_int_size(membership); + for (i = 0; i < n; i++) { + igraph_integer_t l = VECTOR(*membership)[i]; + igraph_vector_int_t* vec = igraph_vector_int_list_get_ptr(&layering->layers, l); + IGRAPH_CHECK(igraph_vector_int_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_int_list_destroy(&layering->layers); +} + +/** + * Returns the number of layers in a layering. + */ +static igraph_integer_t igraph_i_layering_num_layers(const igraph_i_layering_t* layering) { + return igraph_vector_int_list_size(&layering->layers); +} + +/** + * Returns the list of vertices in a given layer + */ +static igraph_vector_int_t* igraph_i_layering_get(const igraph_i_layering_t* layering, + igraph_integer_t index) { + return igraph_vector_int_list_get_ptr(&layering->layers, index); +} + + +/** + * Forward declarations + */ + +static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, + const igraph_vector_t* weights, igraph_vector_int_t* membership); +static igraph_error_t igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, + igraph_matrix_t* layout, const igraph_i_layering_t* layering, + igraph_integer_t maxiter); +static igraph_error_t 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. + */ +igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, + igraph_t *extd_graph, igraph_vector_int_t *extd_to_orig_eids, + const igraph_vector_int_t* layers, igraph_real_t hgap, igraph_real_t vgap, + igraph_integer_t maxiter, const igraph_vector_t *weights) { + igraph_integer_t i, j, k, l, m, nei; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t comp_idx; + igraph_integer_t 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_int_t membership; /* components of the original graph */ + igraph_vector_int_t extd_edgelist; /* edge list of the extended graph */ + igraph_vector_int_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_int_size(layers) != no_of_nodes) { + IGRAPH_ERROR("layer vector too short or too long", IGRAPH_EINVAL); + } + + if (extd_graph != 0) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&extd_edgelist, 0); + if (extd_to_orig_eids != 0) { + igraph_vector_int_clear(extd_to_orig_eids); + } + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + IGRAPH_VECTOR_INT_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_INT_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_int_init_copy(&layers_own, layers)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &layers_own); + } + + /* Normalize layering, eliminate empty layers */ + if (no_of_nodes > 0) { + igraph_vector_int_t inds; + IGRAPH_VECTOR_INT_INIT_FINALLY(&inds, 0); + IGRAPH_CHECK(igraph_vector_int_qsort_ind(&layers_own, &inds, IGRAPH_ASCENDING)); + j = -1; dx = VECTOR(layers_own)[VECTOR(inds)[0]] - 1; + for (i = 0; i < no_of_nodes; i++) { + k = 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_int_destroy(&inds); + IGRAPH_FINALLY_CLEAN(1); + } + + /* 2. Find the connected components. */ + IGRAPH_CHECK(igraph_connected_components(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. */ + igraph_integer_t component_size, next_new_vertex_id; + igraph_vector_int_t old2new_vertex_ids; + igraph_vector_int_t new2old_vertex_ids; + igraph_vector_int_t new_layers; + igraph_vector_int_t edgelist; + igraph_vector_int_t neis; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new2old_vertex_ids, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&old2new_vertex_ids, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_layers, 0); + + igraph_vector_int_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_int_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, i, IGRAPH_OUT)); + j = igraph_vector_int_size(&neis); + for (k = 0; k < j; k++) { + igraph_integer_t eid = 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_int_push_back(&extd_edgelist, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_int_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_int_push_back(&edgelist, + VECTOR(old2new_vertex_ids)[nei])); + for (l = VECTOR(layers_own)[nei] + 1; + l < VECTOR(layers_own)[i]; l++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&new_layers, l)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id++)); + } + IGRAPH_CHECK(igraph_vector_int_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_int_push_back(&extd_edgelist, i)); + next_extd_vertex_id += VECTOR(layers_own)[i] - VECTOR(layers_own)[nei] - 1; + for (l = VECTOR(layers_own)[i] - 1, m = 1; + l > VECTOR(layers_own)[nei]; l--, m++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id - m)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id - m)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(extd_to_orig_eids, eid)); + } + } + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(extd_to_orig_eids, eid)); + } + } + } else { + /* Edge goes downwards */ + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, + VECTOR(old2new_vertex_ids)[i])); + for (l = VECTOR(layers_own)[i] + 1; + l < VECTOR(layers_own)[nei]; l++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&new_layers, l)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id++)); + } + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, + VECTOR(old2new_vertex_ids)[nei])); + /* Also add the edge to the extended graph */ + if (extd_graph != 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, i)); + for (l = VECTOR(layers_own)[i] + 1; + l < VECTOR(layers_own)[nei]; l++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id++)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(extd_to_orig_eids, eid)); + } + } + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); + if (extd_to_orig_eids != 0) { + IGRAPH_CHECK(igraph_vector_int_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, next_new_vertex_id, 1)); + IGRAPH_FINALLY(igraph_destroy, &subgraph); + + /* + igraph_vector_int_print(&edgelist); + igraph_vector_int_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, 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 = VECTOR(new2old_vertex_ids)[i]; + MATRIX(*res, l, 0) = MATRIX(layout, i, 0) + dx; + MATRIX(*res, l, 1) = VECTOR(layer_to_y)[(igraph_integer_t) 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)[(igraph_integer_t) 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_int_destroy(&new_layers); + igraph_vector_int_destroy(&old2new_vertex_ids); + igraph_vector_int_destroy(&new2old_vertex_ids); + igraph_vector_int_destroy(&edgelist); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(5); + } + + igraph_vector_int_destroy(&layers_own); + igraph_vector_destroy(&layer_to_y); + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(3); + + if (extd_graph != 0) { + IGRAPH_CHECK(igraph_create(extd_graph, &extd_edgelist, next_extd_vertex_id, igraph_is_directed(graph))); + igraph_vector_int_destroy(&extd_edgelist); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, + const igraph_vector_t* weights, igraph_vector_int_t* membership) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + + if (no_of_edges == 0) { + igraph_vector_int_null(membership); + 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 */ + igraph_integer_t i, j; + igraph_vector_t outdegs, indegs; + igraph_vector_int_t feedback_edges; + glp_prob *ip; + glp_smcp parm; + + if (no_of_edges > INT_MAX) { + IGRAPH_ERROR("Number of edges in graph too large for GLPK.", IGRAPH_EOVERFLOW); + } + + /* Allocate storage and create the problem */ + ip = glp_create_prob(); + IGRAPH_FINALLY(glp_delete_prob, ip); + IGRAPH_VECTOR_INT_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_int_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_int_size(&feedback_edges); + for (i = 0; i < j; i++) { + igraph_integer_t eid = VECTOR(feedback_edges)[i]; + igraph_integer_t from = IGRAPH_FROM(graph, eid); + igraph_integer_t 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_int_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] = (int) IGRAPH_FROM(graph, i) + 1; + ind[2] = (int) 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_int_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 igraph_error_t igraph_i_layout_sugiyama_calculate_barycenters(const igraph_t* graph, + const igraph_i_layering_t* layering, igraph_integer_t layer_index, + igraph_neimode_t direction, const igraph_matrix_t* layout, + igraph_vector_t* barycenters) { + igraph_integer_t i, j, m, n; + igraph_vector_int_t* layer_members = igraph_i_layering_get(layering, layer_index); + igraph_vector_int_t neis; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + n = igraph_vector_int_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, VECTOR(*layer_members)[i], direction)); + m = igraph_vector_int_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, (igraph_integer_t) VECTOR(neis)[j], 0); + } + VECTOR(*barycenters)[i] /= m; + } + } + + igraph_vector_int_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 igraph_error_t igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, + igraph_matrix_t* layout, const igraph_i_layering_t* layering, + igraph_integer_t maxiter) { + igraph_integer_t i, n, nei; + igraph_integer_t no_of_vertices = igraph_vcount(graph); + igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); + igraph_integer_t iter, layer_index; + igraph_vector_int_t* layer_members; + igraph_vector_int_t new_layer_members; + igraph_vector_int_t neis; + igraph_vector_t barycenters; + igraph_vector_int_t 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 */ + { + igraph_integer_t *xs = IGRAPH_CALLOC(no_of_layers, igraph_integer_t); + if (xs == 0) { + IGRAPH_ERROR("cannot order nodes horizontally", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + for (i = 0; i < no_of_vertices; i++) { + MATRIX(*layout, i, 0) = xs[(igraph_integer_t)MATRIX(*layout, i, 1)]++; + } + free(xs); + } + + IGRAPH_VECTOR_INIT_FINALLY(&barycenters, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_layer_members, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_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_int_size(layer_members); + IGRAPH_CHECK(igraph_vector_int_resize(&new_layer_members, n)); + + igraph_i_layout_sugiyama_calculate_barycenters(graph, + layering, layer_index, IGRAPH_IN, layout, &barycenters); + +#ifdef SUGIYAMA_DEBUG + printf("Layer %" IGRAPH_PRId ", aligning to upper barycenters\n", layer_index); + printf("Vertices: "); igraph_vector_int_print(layer_members); + printf("Barycenters: "); igraph_vector_print(&barycenters); +#endif + IGRAPH_CHECK(igraph_vector_qsort_ind(&barycenters, &sort_indices, IGRAPH_ASCENDING)); + for (i = 0; i < n; i++) { + nei = VECTOR(*layer_members)[VECTOR(sort_indices)[i]]; + VECTOR(new_layer_members)[i] = nei; + MATRIX(*layout, nei, 0) = i; + } + if (!igraph_vector_int_all_e(layer_members, &new_layer_members)) { + IGRAPH_CHECK(igraph_vector_int_update(layer_members, &new_layer_members)); +#ifdef SUGIYAMA_DEBUG + printf("New vertex order: "); igraph_vector_int_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_int_size(layer_members); + IGRAPH_CHECK(igraph_vector_int_resize(&new_layer_members, n)); + + igraph_i_layout_sugiyama_calculate_barycenters(graph, + layering, layer_index, IGRAPH_OUT, layout, &barycenters); + +#ifdef SUGIYAMA_DEBUG + printf("Layer %" IGRAPH_PRId ", aligning to lower barycenters\n", layer_index); + printf("Vertices: "); igraph_vector_int_print(layer_members); + printf("Barycenters: "); igraph_vector_print(&barycenters); +#endif + + IGRAPH_CHECK(igraph_vector_qsort_ind(&barycenters, &sort_indices, IGRAPH_ASCENDING)); + for (i = 0; i < n; i++) { + nei = VECTOR(*layer_members)[VECTOR(sort_indices)[i]]; + VECTOR(new_layer_members)[i] = nei; + MATRIX(*layout, nei, 0) = i; + } + if (!igraph_vector_int_all_e(layer_members, &new_layer_members)) { + IGRAPH_CHECK(igraph_vector_int_update(layer_members, &new_layer_members)); +#ifdef SUGIYAMA_DEBUG + printf("New vertex order: "); igraph_vector_int_print(layer_members); +#endif + changed = 1; + } else { +#ifdef SUGIYAMA_DEBUG + printf("Order did not change.\n"); +#endif + } + } + +#ifdef SUGIYAMA_DEBUG + printf("==== Finished iteration %" IGRAPH_PRId "\n", iter); +#endif + + iter++; + } + + igraph_vector_destroy(&barycenters); + igraph_vector_int_destroy(&new_layer_members); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&sort_indices); + IGRAPH_FINALLY_CLEAN(4); + + 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 igraph_error_t 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_int_t* roots, igraph_vector_int_t* align); +static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, + const igraph_vector_int_t* vertex_to_the_left, + const igraph_vector_int_t* roots, const igraph_vector_int_t* align, + igraph_real_t hgap, igraph_vector_t* xs); +static void igraph_i_layout_sugiyama_horizontal_compaction_place_block(igraph_integer_t v, + const igraph_vector_int_t* vertex_to_the_left, + const igraph_vector_int_t* roots, const igraph_vector_int_t* align, + igraph_vector_int_t* sinks, igraph_vector_t* shifts, + igraph_real_t hgap, igraph_vector_t* xs); + +static igraph_error_t 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) { + + igraph_integer_t i, j, k, l, n; + igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t neis1, neis2; + igraph_vector_t xs[4]; + igraph_vector_int_t roots, align; + igraph_vector_int_t vertex_to_the_left; + igraph_vector_bool_t ignored_edges; + + /* + { + igraph_vector_int_t edgelist; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, 0); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0)); + igraph_vector_int_print(&edgelist); + igraph_vector_int_destroy(&edgelist); + IGRAPH_FINALLY_CLEAN(1); + + for (i = 0; i < no_of_layers; i++) { + igraph_vector_int_t* layer = igraph_i_layering_get(layering, i); + igraph_vector_int_print(layer); + } + } + */ + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&ignored_edges, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_to_the_left, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis1, 0); + IGRAPH_VECTOR_INT_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_int_t* vertices = igraph_i_layering_get(layering, i); + n = igraph_vector_int_size(vertices); + + /* Find all the edges from this layer to the next */ + igraph_vector_int_clear(&neis1); + for (j = 0; j < n; j++) { + IGRAPH_CHECK(igraph_neighbors(graph, &neis2, VECTOR(*vertices)[j], IGRAPH_OUT)); + IGRAPH_CHECK(igraph_vector_int_append(&neis1, &neis2)); + } + + /* Consider all pairs of edges and check whether they are in a type 1 + * conflict */ + n = igraph_vector_int_size(&neis1); + for (j = 0; j < n; j++) { + igraph_integer_t u = IGRAPH_FROM(graph, j); + igraph_integer_t 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++) { + igraph_integer_t w = IGRAPH_FROM(graph, k); + igraph_integer_t 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] = true; + } else { + VECTOR(ignored_edges)[j] = true; + } + } + } + } + } + + igraph_vector_int_destroy(&neis1); + igraph_vector_int_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_int_t* vertices = igraph_i_layering_get(layering, i); + n = igraph_vector_int_size(vertices); + if (n == 0) { + continue; + } + + k = l = VECTOR(*vertices)[0]; + VECTOR(vertex_to_the_left)[k] = k; + for (j = 1; j < n; j++) { + k = 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_INT_INIT_FINALLY(&roots, no_of_nodes); + IGRAPH_VECTOR_INT_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 = */ 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_int_destroy(&roots); + igraph_vector_int_destroy(&align); + IGRAPH_FINALLY_CLEAN(2); + + for (i = 0; i < 4; i++) { + igraph_vector_destroy(&xs[i]); + } + IGRAPH_FINALLY_CLEAN(4); + + igraph_vector_int_destroy(&vertex_to_the_left); + IGRAPH_FINALLY_CLEAN(1); + + igraph_vector_bool_destroy(&ignored_edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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_int_t* roots, igraph_vector_int_t* align) { + igraph_integer_t i, j, k, n, di, dj, i_limit, j_limit, r; + igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_neimode_t neimode = (reverse ? IGRAPH_OUT : IGRAPH_IN); + igraph_vector_int_t neis; + igraph_vector_t xs; + igraph_vector_int_t inds; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&xs, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&inds, 0); + + IGRAPH_CHECK(igraph_vector_int_resize(roots, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_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_int_t *layer = igraph_i_layering_get(layering, i); + + /* r = 0 in the paper, but C arrays are indexed from 0 */ + r = align_right ? IGRAPH_INTEGER_MAX : -1; + + /* If align_right is 1, we have to process the layer in reverse order */ + j = align_right ? (igraph_vector_int_size(layer) - 1) : 0; + dj = align_right ? -1 : 1; + j_limit = align_right ? -1 : igraph_vector_int_size(layer); + for (; j != j_limit; j += dj) { + igraph_integer_t medians[2]; + igraph_integer_t vertex = VECTOR(*layer)[j]; + igraph_integer_t 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, vertex, neimode)); + + n = igraph_vector_int_size(&neis); + if (n == 0) + /* No neighbors in this direction, continue */ + { + continue; + } + if (n == 1) { + /* Just one neighbor; the median is trivial */ + medians[0] = 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(VECTOR(neis)[k]); + } + IGRAPH_CHECK(igraph_vector_qsort_ind(&xs, &inds, IGRAPH_ASCENDING)); + + if (n % 2 == 1) { + /* Odd number of neighbors, so the median is unique */ + medians[0] = VECTOR(neis)[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] = VECTOR(neis)[VECTOR(inds)[n / 2]]; + medians[1] = VECTOR(neis)[VECTOR(inds)[n / 2 - 1]]; + } else { + medians[0] = VECTOR(neis)[VECTOR(inds)[n / 2 - 1]]; + medians[1] = VECTOR(neis)[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, vertex, medians[k], IGRAPH_UNDIRECTED, /* error= */ true)); + if (VECTOR(*ignored_edges)[eid]) { + continue; + } + /* Okay, align with the median if possible */ + pos = 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_int_destroy(&inds); + igraph_vector_int_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 igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, + const igraph_vector_int_t* vertex_to_the_left, + const igraph_vector_int_t* roots, const igraph_vector_int_t* align, + igraph_real_t hgap, igraph_vector_t* xs) { + igraph_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_t shifts, old_xs; + igraph_vector_int_t sinks; + igraph_real_t shift; + + /* Initialization */ + + IGRAPH_CHECK(igraph_vector_int_init_range(&sinks, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &sinks); + + IGRAPH_VECTOR_INIT_FINALLY(&shifts, no_of_nodes); + igraph_vector_fill(&shifts, IGRAPH_INFINITY); + + IGRAPH_VECTOR_INIT_FINALLY(&old_xs, no_of_nodes); + + IGRAPH_CHECK(igraph_vector_resize(xs, no_of_nodes)); + 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_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++) { + igraph_integer_t root = VECTOR(*roots)[i]; + VECTOR(*xs)[i] = VECTOR(old_xs)[root]; + shift = VECTOR(shifts)[VECTOR(sinks)[root]]; + if (shift < IGRAPH_INFINITY) { + VECTOR(*xs)[i] += shift; + } + } + + igraph_vector_int_destroy(&sinks); + igraph_vector_destroy(&shifts); + igraph_vector_destroy(&old_xs); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_layout_sugiyama_horizontal_compaction_place_block(igraph_integer_t v, + const igraph_vector_int_t* vertex_to_the_left, + const igraph_vector_int_t* roots, const igraph_vector_int_t* align, + igraph_vector_int_t* sinks, igraph_vector_t* shifts, + igraph_real_t hgap, igraph_vector_t* xs) { + igraph_integer_t u, w; + igraph_integer_t u_sink, v_sink; + + if (VECTOR(*xs)[v] >= 0) { + return; + } + + VECTOR(*xs)[v] = 0; + + w = v; + do { + /* Check whether vertex w is the leftmost in its own layer */ + u = VECTOR(*vertex_to_the_left)[w]; + if (u != w) { + /* Get the root of u (proceeding all the way upwards in the block) */ + u = VECTOR(*roots)[u]; + /* Place the block of u recursively */ + igraph_i_layout_sugiyama_horizontal_compaction_place_block(u, + vertex_to_the_left, roots, align, sinks, shifts, hgap, xs); + + u_sink = VECTOR(*sinks)[u]; + v_sink = 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 = VECTOR(*align)[w]; + } while (w != v); +} + +#undef IS_INNER_SEGMENT +#undef IS_DUMMY +#undef X_POS + +#ifdef SUGIYAMA_DEBUG + #undef SUGIYAMA_DEBUG +#endif diff --git a/src/layout/umap.c b/src/layout/umap.c new file mode 100644 index 0000000..65c969d --- /dev/null +++ b/src/layout/umap.c @@ -0,0 +1,1261 @@ +/* -*- 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_layout.h" + +#include "igraph_interface.h" +#include "igraph_lapack.h" +#include "igraph_matrix.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" +#include "igraph_vector_list.h" + +#include "layout/layout_internal.h" +#include "core/interruption.h" + +#include + +/* This file contains the implementation of the UMAP algorithm. + * + * UMAP is typically used as a to reduce dimensionality of vectors, embedding them in + * 2D (or, less commonly, in 3D). Despite this geometric flair, UMAP heavily relies on + * graphs as intermediate data structures and is therefore a useful graph layout + * algorithm in its own right. Conceptually, there are three steps: + * + * 1. Compute a sparse graph with edges connecting similar vectors, e.g. a k-nearest + * neighbor graph. A vector of distances is associated with the graph edges. This + * file does *not* perform this part of the computation since there are many + * libraries out there that can compute knn or other sparse graphs efficiently + * starting from vector spaces (e.g. faiss). + * 2. Convert the distances into weights, which are weights between 0 and 1 + * that are larger for short-distance edges. This step is exposed via + * igraph_layout_umap_compute_weights. + * 3. Compute a layout for the graph, using its associated weights as edge + * weights. This step is exposed via igraph_layout_umap and its 3D counterpart. + * These two fuctions can also compute steps 2 and 3 in one go, since that's the + * most common use case: the argument "distances_are_weights" should be + * set to false. + * + * A few more details w/r/t steps 2 and 3, since they are computed in detail below. + * + * STEP 2 + * For each vertex, the distance to its closest neighbor, called rho, is "forfeited": + * that edge begets weight 1 (in principle, at least). Farther neighbors beget + * lower weights according to an exponential decay. The scale factor of this + * decay is called sigma and is computed from the graph itself. + * + * STEP 3 + * The layout is computed via stochastic gradient descent, i.e. applying stochastic + * forces along high-weight edges and, more rarely, low-weight edges. + * To compute the stochastic forces, one needs a smooth function that approximates + * weights but in the embedded space: + * Q(d) = ( 1 + a*d^2b )^-1 + * where d is the 2D/3D distance between the vertices and a and b are constants that + * are computed globally based on a user-chosen fudge parameter called min_dist. + * Smaller min_dist will give rise to slightly more compact embeddings. We find a + * and b via gradient descent, which is implemented de novo below. + * + * Repulsion is computed via negative sampling, typically a few nodes are picked + * at random as repulsive sources each time an attractive force is computed. + * + * During the stochastic gradient descent, the learning rate - a multiplicative factor + * on top of the stochastic forces themselves - is reduced linearly from 1 to 0. At + * the end, the stochastic forces can be strong but their effect is reduced to almost + * nothing by the small learning rate. Notice that UMAP does not formally converge: + * instead, we reduce the forces' impact steadily to a trickle and finally quench it + * altogether. + * + * FINAL COMMENTS + * This implementation uses a few more tricks to improve the result: + * - a few constants are defined to limit the force applied to vertices at each step + * and other geometric corrections + * - the layout is centered at the end of the computation. + * - a seed layout can be used. Notice that since UMAP runs for an essentially fixed + * time rather than until convergence, using a good/bad seed does not affect + * runtimes significantly. + * */ +#define UMAP_FORCE_LIMIT 4 +#define UMAP_MIN_DISTANCE_ATTRACTION 0.0001 +#define UMAP_CORRECT_DISTANCE_REPULSION 0.01 + +/* Find sigma for this vertex by binary search */ +static igraph_error_t igraph_i_umap_find_sigma(const igraph_vector_t *distances, + const igraph_vector_int_t *eids, + igraph_real_t rho, igraph_real_t *sigma_p, igraph_real_t target) { + + igraph_real_t sigma = 1; + igraph_real_t sum; + igraph_real_t tol = 0.01; + igraph_integer_t maxiter = 100; + igraph_integer_t no_of_neis = igraph_vector_int_size(eids); + igraph_integer_t eid; + igraph_real_t step = sigma; + igraph_integer_t seen_max = 0; + + /* Binary search */ + for (igraph_integer_t iter = 0; iter < maxiter; iter++) { + sum = 0; + for (igraph_integer_t j = 0; j < no_of_neis; j++) { + eid = VECTOR(*eids)[j]; + sum += exp(-(VECTOR(*distances)[eid] - rho) / sigma); + } + +#ifdef UMAP_DEBUG + printf("SIGMA function (no_of_neis = %" IGRAPH_PRId ")- sum: %g, " + "target: %g, rho: %g, sigma: %g\n", no_of_neis, sum, target, rho, sigma); +#endif + + if (sum < target) { + /* going back up after having seen an upper bound */ + if (seen_max == 1) { + step /= 2; + /* we need to go up but have not seen an upper bound yet + * first iteration we want to increase by sigma, else we must come from + * below, so we are sitting at 2 * step, we want to move to 4 * step */ + } else if (iter > 0) { + step *= 2; + } + sigma += step; + /* overshooting, we have definitely seen the max */ + } else { + seen_max = 1; + step /= 2; + sigma -= step; + } + + /* Check for convergence */ + if (fabs(sum - target) < tol) { + break; + } + } + + *sigma_p = sigma; + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_layout_umap_compute_weights + * \brief Compute weights for a UMAP layout starting from distances. + * + * \experimental + * + * UMAP is used to embed high-dimensional vectors in a low-dimensional space + * (most commonly 2D). It uses a distance graph as an intermediate data structure, + * making it also a useful graph layout algorithm. See \ref igraph_layout_umap() + * for more information. + * + * + * An early step in UMAP is to compute exponentially decaying "weights" from the + * distance graph. Connectivities can also be viewed as edge weights that quantify + * similarity between two vertices. This function computes weights from the + * distance graph. To compute the layout from precomputed weights, call + * \ref igraph_layout_umap() with the \p distances_are_weights argument set to \c true. + * + * + * While the distance graph can be directed (e.g. in a k-nearest neighbors, it is + * clear \em whom you are a neighbor of), the weights are usually undirected. Whenever two + * vertices are doubly connected in the distance graph, the resulting weight \c W is set as: + * + * + * W = W1 + W2 - W1 * W2 + * + * Because UMAP weights are interpreted as probabilities, this is just the probability + * that either edge is present, without double counting. It is called "fuzzy union" in + * the original UMAP implementation and is the default. One could also require that both + * edges are there, i.e. W = W1 * W2: this would represent the fuzzy intersection and is + * not implemented in igraph. As a consequence of this symmetrization, information is lost, + * i.e. one needs fewer weights than one had distances. To keep things efficient, here + * we set the weight for one of the two edges as above and the weight for its opposite edge + * as 0, so that it will be skipped in the UMAP gradient descent later on. + * + * + * Technical note: For each vertex, this function computes its scale factor (sigma), + * its connectivity correction (rho), and finally the weights themselves. + * + * + * References: + * + * + * Leland McInnes, John Healy, and James Melville: + * UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction (2020) + * https://arxiv.org/abs/1802.03426 + * + * \param graph Pointer to the distance graph. This can be directed (e.g. connecting + * each vertex to its neighbors in a k-nearest neighbor) or undirected, but must + * have no loops nor parallel edges. The only exception is: if the graph is directed, + * having pairs of edges with opposite direction is accepted. + * \param distances Pointer to the vector with the vertex-to-vertex distance associated with + * each edge. This argument can be NULL, in which case all edges are assumed to have the + * same distance. + * \param weights Pointer to an initialized vector where the result will be stored. If the + * input graph is directed, the weights represent a symmetrized version which contains + * less information. Therefore, whenever two edges between the same vertices and opposite + * direction are present in the input graph, only one of the weights is set and the other + * is fixed to zero. That format is accepted by \ref igraph_layout_umap(), which skips + * all zero-weight edges from the layout optimization. + * \return Error code. + * + * \sa \ref igraph_layout_umap(), \ref igraph_layout_umap_3d() + */ +igraph_error_t igraph_layout_umap_compute_weights( + const igraph_t *graph, + const igraph_vector_t *distances, + igraph_vector_t *weights) { + + igraph_integer_t no_of_vertices = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_neis, eid, i, j, k, l; + igraph_vector_int_t eids; + igraph_vector_int_list_t neighbors_seen; + igraph_vector_list_t weights_seen; + igraph_vector_int_t* neighbors_seen_elt; + igraph_vector_t* weights_seen_elt; + igraph_real_t rho, dist_max, dist, sigma, weight, weight_inv, sigma_target, dist_min; + + /* reserve memory for the weights */ + IGRAPH_CHECK(igraph_vector_resize(weights, no_of_edges)); + + /* UMAP is sometimes used on unweighted graphs, otherwise check distance vector. */ + if (distances != NULL) { + if (igraph_vector_size(distances) != no_of_edges) { + IGRAPH_ERROR("Distances must be the same number as the edges in the graph.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + dist_min = igraph_vector_min(distances); + if (dist_min < 0) { + IGRAPH_ERROR("Distance values must not be negative.", IGRAPH_EINVAL); + } else if (isnan(dist_min)) { + IGRAPH_ERROR("Distance values must not be NaN.", IGRAPH_EINVAL); + } + } + } + + /* Initialize auxiliary vectors */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&neighbors_seen, no_of_vertices); + IGRAPH_VECTOR_LIST_INIT_FINALLY(&weights_seen, no_of_vertices); + + /* Iterate over vertices x, like in the paper */ + for (i = 0; i < no_of_vertices; i++) { + /* Edges out of this vertex, e.g. to its k-nearest neighbors */ + IGRAPH_CHECK(igraph_incident(graph, &eids, i, IGRAPH_OUT)); + no_of_neis = igraph_vector_int_size(&eids); + + /* Vertex has no neighbors */ + if (no_of_neis == 0) { + continue; + } + + /* Find rho for this vertex, i.e. the minimal non-self distance */ + if (distances != NULL) { + rho = VECTOR(*distances)[VECTOR(eids)[0]]; + dist_max = rho; + for (j = 1; j < no_of_neis; j++) { + eid = VECTOR(eids)[j]; + dist = VECTOR(*distances)[eid]; + rho = fmin(rho, dist); + dist_max = fmax(dist_max, dist); + } + } else { + rho = dist_max = 0; + } + + /* If the maximal distance is rho, all neighbors are identical to + * each other. This can happen e.g. if distances == NULL. */ + if (dist_max == rho) { + /* This is a special flag for later on */ + sigma = -1; + + /* Else, find sigma for this vertex, from its rho plus binary search */ + } else { + sigma_target = log2(no_of_neis); + IGRAPH_CHECK(igraph_i_umap_find_sigma(distances, + &eids, rho, &sigma, + sigma_target)); + } + + /* Convert to weights */ + for (j = 0; j < no_of_neis; j++) { + eid = VECTOR(eids)[j]; + + /* Basically, nodes closer than rho have probability 1, the rest is + * exponentially penalized keeping rough cardinality */ + weight = sigma < 0 ? 1 : exp(-(VECTOR(*distances)[eid] - rho) / sigma); + + #ifdef UMAP_DEBUG + if (distances != NULL) + printf("distance: %g\n", VECTOR(*distances)[eid]); + printf("weight: %g\n", weight); + #endif + + /* Store in vector lists for later symmetrization */ + k = IGRAPH_OTHER(graph, eid, i); + if (k == i) { + IGRAPH_ERROR("Input graph must contain no self-loops.", IGRAPH_EINVAL); + } + + neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, i); + IGRAPH_CHECK(igraph_vector_int_push_back(neighbors_seen_elt, k)); + + weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, i); + IGRAPH_CHECK(igraph_vector_push_back(weights_seen_elt, weight)); + } + + } + + /* Symmetrize the weights. UMAP weights are probabilities of that edge being a + * "real" connection. Unlike the distances, which can represent a directed graph, + * weights are usually symmetric. We symmetrize via fuzzy union. */ + for (eid=0; eid < no_of_edges; eid++) { + i = IGRAPH_FROM(graph, eid); + k = IGRAPH_TO(graph, eid); + + /* Direct weight, if found */ + /* NOTE: this and the subsequent loop could be faster if we sorted the vectors + * beforehand. Probably not such a big deal. */ + weight = 0; + neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, i); + weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, i); + no_of_neis = igraph_vector_int_size(neighbors_seen_elt); + for (l=0; l < no_of_neis; l++) { + if (VECTOR(*neighbors_seen_elt)[l] == k) { + weight = VECTOR(*weights_seen_elt)[l]; + /* Tag this weight so we can ignore it later on if the opposite + * directed edge is found. It's ok to retag */ + VECTOR(*weights_seen_elt)[l] = -1; + break; + } + } + + /* The opposite edge has already been union-ed, set this one to -1 */ + if (weight < 0) { + VECTOR(*weights)[eid] = 0; + continue; + } + + /* Weight of the opposite edge, if found */ + weight_inv = 0; + neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, k); + weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, k); + no_of_neis = igraph_vector_int_size(neighbors_seen_elt); + for (l=0; l < no_of_neis; l++) { + if (VECTOR(*neighbors_seen_elt)[l] == i) { + weight_inv = VECTOR(*weights_seen_elt)[l]; + /* Tag this weight so we can ignore it later on if the opposite + * directed edge is found. It's ok to retag */ + VECTOR(*weights_seen_elt)[l] = -1; + break; + } + } + + /* The opposite edge has already been union-ed, set this one to -1 */ + if (weight_inv < 0) { + VECTOR(*weights)[eid] = 0; + continue; + } + + /* First time this edge or its opposite are seen, set the W */ + VECTOR(*weights)[eid] = weight + weight_inv - weight * weight_inv; + } + + igraph_vector_list_destroy(&weights_seen); + igraph_vector_int_list_destroy(&neighbors_seen); + igraph_vector_int_destroy(&eids); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Helper function to compute a and b parameters (smoothing probability metric in embedding space) */ +static igraph_error_t igraph_i_umap_get_ab_residuals(igraph_vector_t *residuals, + igraph_real_t *squared_sum_res, igraph_integer_t nr_points, igraph_real_t a, + igraph_real_t b, igraph_vector_t *powb, const igraph_vector_t *x, igraph_real_t min_dist) +{ + igraph_real_t tmp; + + *squared_sum_res = 0; + for (igraph_integer_t i = 0; i < nr_points; i++) { + /* The ideal probability is: + * + * P(d) = d < min_dist ? 1 : e^{-(d - min_dist)} + * + * which is the same as the high-dimensional probability, except + * min_dist plays the role of rho and sigma is fixed at 1. However, + * this function has a kink at min_dist (first derivative is not + * continuous). So we smoothen it with: + * + * Q(d) = ( 1 + a*d^2b )^-1 + * + * which is quite similar throughout for appropriate a and b. Notice + * that we do not need to smoothen the high-dimensional probability + * function because the vertices are not moved in the high-dimensional + * space, so there is no need for differentiating that function. + * + * The residual is of course: + * + * Q(d) - P(d) = ( 1 + a*d^2b )^-1 - [ d < min_dist ? 1 : e^{-(d - min_dist)} ] + * + * This function also sets the auxiliary vector powb. + * */ + VECTOR(*powb)[i] = pow(VECTOR(*x)[i], 2 * b); + tmp = 1 / (1 + a * VECTOR(*powb)[i]); + tmp -= VECTOR(*x)[i] <= min_dist ? 1 : exp(-(VECTOR(*x)[i] - min_dist)); + VECTOR(*residuals)[i] = tmp; + *squared_sum_res += tmp * tmp; + } + return IGRAPH_SUCCESS; +} + +/* UMAP minimizes the cross-entropy between probability of being a true edge in + * high and low dimensions. For the low-dimensional computation, it uses a smooth + * function of the Euclidean distance between two vertices: + * + * P(d) = (1 + a*d^2b)^-1 + * + * where d is the distance and a and b are hyperparameters that basically determine + * the cutoff distance at which the probability starts to decrease. + * + * We fit these two parameters using nonlinear least squares (Gauss-Newton + line search) + * on a grid of artificial distances. There is only one user-chosen input argument that + * determines this fit, called min_dist, which is approximately the cutoff distance we + * are trying to achieve. + * + * ADVANCED NOTE: + * In a way, the whole UMAP layout is invariant upon scaling transformations, of course, + * so min_dist is basically meaningless. Another way to see this is that for any pair + * (a,b) that minimize the least squares for dist_min, we can easily find a solution for + * a new dist_min2 := alpha * dist_min: + * + * P(d, a, b) = (1 + a*d^2b)^-1 + * + * P(alpha * d, a', b') = (1 + a'*(alpha * d)^2b' )^-1 + * + * that is: + * + * a*d^2b = a'*alpha^2b'*d^2b' for each d >= 0. + * + * So for d = 1 -> a = a'*alpha^2b' + * and for d = sqrt(2) -> a*2^b = a'*alpha^2b'*2^b' + * + * which solves as: + * + * b' = b + * a' = a / alpha^2b + * + * For instance, if b = 1, a -> 0.01*a moves the fit a decade towards larger min_dist, + * and a -> 100*a moves the fit a decade towards smaller min_dist. + * */ +igraph_error_t igraph_i_umap_fit_ab(igraph_real_t min_dist, igraph_real_t *a_p, igraph_real_t *b_p) +{ + /* Grid points */ + igraph_vector_t x; + /* Make a lattice from 0 to 3 * sigma with 300 points. This is what + * umap.umap_.fit_ab_params does, but sigma is fixed to 1.0 here since + * that's the default value used in scanpy and by virtually everyone */ + igraph_integer_t nr_points = 300; + igraph_real_t end_point = 3.0; + /* Initial values takes as reasonable assumptions from typical min_dist values */ + igraph_real_t b = 0.8; + igraph_real_t a = 1.8; + /* deltas */ + igraph_real_t da, db; + /* Residuals */ + igraph_vector_t residuals; + igraph_real_t squared_sum_res, squared_sum_res_old, squared_sum_res_tmp; + /* Needed for the Gauss-Newton search */ + igraph_matrix_t jacobian, jTj, jTr; + igraph_real_t tol = 0.001; + igraph_real_t maxiter = 100; + /* Auxiliary vars */ + igraph_real_t tmp; + igraph_vector_t powb; + int lapack_info; + + /* Distance lattice */ + IGRAPH_VECTOR_INIT_FINALLY(&x, nr_points); + /* Residuals */ + IGRAPH_VECTOR_INIT_FINALLY(&residuals, nr_points); + /* First derivatives, for the fitting (direction) */ + IGRAPH_MATRIX_INIT_FINALLY(&jacobian, nr_points, 2); + /* Composite matrices/vectors for linear least squares at each iteration */ + IGRAPH_MATRIX_INIT_FINALLY(&jTj, 2, 2); + IGRAPH_MATRIX_INIT_FINALLY(&jTr, 2, 1); + /* Auxiliary vars for convenience */ + IGRAPH_VECTOR_INIT_FINALLY(&powb, nr_points); + + /* Distance |x-y| (this is a lattice, there are no actual x and y) */ + for (igraph_integer_t i = 0; i < nr_points; i++) { + VECTOR(x)[i] = (end_point / nr_points) * i + 0.001; /* added a 0.001 to prevent NaNs */ + } + + /* Initialize squared_sum_res_old to a dummy value to prevent some compilers + * from complaining about uninitialized values */ + squared_sum_res_old = IGRAPH_INFINITY; + +#ifdef UMAP_DEBUG + printf("start fit_ab\n"); +#endif + for (igraph_integer_t iter = 0; iter < maxiter; iter++) { + IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, a, b, + &powb, &x, min_dist)); + + /* break if good fit (conergence to truth) */ + if (squared_sum_res < tol * tol) { +#ifdef UMAP_DEBUG + printf("convergence to zero (wow!)\n"); +#endif + break; + } + /* break if no change (convergence) */ + if ((iter > 0) && fabs(sqrt(squared_sum_res_old) - sqrt(squared_sum_res)) < tol) { +#ifdef UMAP_DEBUG + printf("no-change absolute convergence\n"); +#endif + break; + } + + /* Jacobian (first derivatives) of squared residuals at (a, b) */ + for (igraph_integer_t i = 0; i < nr_points; i++) { + tmp = 1 + a * VECTOR(powb)[i]; + MATRIX(jacobian, i, 0) = - 2 * VECTOR(powb)[i] / tmp / tmp; + MATRIX(jacobian, i, 1) = MATRIX(jacobian, i, 0) * a * log(VECTOR(x)[i]) * 2; + } + + /* At each iteration, we want to minimize the linear approximation of the sum of squared + * residuals: + * + * sum_i (Ji @ d(a,b) -r_i)^2 + * + * Putting the first derivative to zero results in a linear system of 2 equations + * (for a and b): + * + * sum_i J_i^T @ J_i @ d(a,b) = sum_i J_i^T r_i + * * + * or more compactly: + * + * J^T @ J @ d(a,b) = J^T @ r + * + * where J_T is the transpose of the Jacobian. Defining A := J^T @ J, B = J^T @ r: + * + * A @ d(a,b) = B + * + * This can be solved for d(a,b) using LAPACK within igraph + * */ + /* Compute A and B, i.e. J^T @ J and J^T @ r */ + MATRIX(jTj, 0, 0) = MATRIX(jTj, 0, 1) = MATRIX(jTj, 1, 0) = MATRIX(jTj, 1, 1) = 0; + MATRIX(jTr, 0, 0) = MATRIX(jTr, 1, 0) = 0; + for (igraph_integer_t i = 0; i < nr_points; i++) { + for (igraph_integer_t j1 = 0; j1 < 2; j1++) { + for (igraph_integer_t j2 = 0; j2 < 2; j2++) { + MATRIX(jTj, j1, j2) += MATRIX(jacobian, i, j1) * MATRIX(jacobian, i, j2); + } + MATRIX(jTr, j1, 0) += MATRIX(jacobian, i, j1) * VECTOR(residuals)[i]; + } + } + /* LAPACK puts solution into jTr */ + IGRAPH_CHECK(igraph_lapack_dgesv(&jTj, 0, &jTr, &lapack_info)); + + /* This might go wrong, in which case we should fail graciously */ + if (lapack_info != 0) { + IGRAPH_ERROR("Singular matrix in the estimation of a and b for UMAP", IGRAPH_EINVAL); + } + + da = -MATRIX(jTr, 0, 0); + db = -MATRIX(jTr, 1, 0); + + /* Improvement over GN: rough exponential line search for best delta + * start from largest change, and keep shrinking as long as we are going down + * */ + squared_sum_res_old = squared_sum_res; + IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, a + da, + b + db, &powb, &x, min_dist)); + +#ifdef UMAP_DEBUG + printf("start line search, SSR before delta: %g, current SSR:, %g\n", squared_sum_res_old, + squared_sum_res); +#endif + for (igraph_integer_t k = 0; k < 30; k++) { + /* Try new parameters */ + da /= 2.0; + db /= 2.0; + squared_sum_res_tmp = squared_sum_res; + IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, + a + da, b + db, &powb, &x, min_dist)); + + /* Compare and if we are going back uphill, undo last step and break */ +#ifdef UMAP_DEBUG + printf("during line search, k = %d, old SSR:, %g, new SSR (half a,b):, %g\n", k, + squared_sum_res_tmp, squared_sum_res); +#endif + if (squared_sum_res > squared_sum_res_tmp - tol) { + da *= 2; + db *= 2; + break; + } + } +#ifdef UMAP_DEBUG + printf("end of line search and iteration, squared_sum_res: %g \n\n", squared_sum_res_tmp); +#endif + + /* assign a, b*/ + a += da; + b += db; + + } + + /* Free memory and tidy up stack */ + igraph_vector_destroy(&x); + igraph_vector_destroy(&residuals); + igraph_matrix_destroy(&jacobian); + igraph_matrix_destroy(&jTj); + igraph_matrix_destroy(&jTr); + igraph_vector_destroy(&powb); + IGRAPH_FINALLY_CLEAN(6); + +#ifdef UMAP_DEBUG + printf("a, b: %g %g\n", a, b); +#endif + + *a_p = a; + *b_p = b; + + return IGRAPH_SUCCESS; + +} + +/* cross-entropy */ +#ifdef UMAP_DEBUG +static igraph_error_t igraph_i_umap_compute_cross_entropy(const igraph_t *graph, + const igraph_vector_t *umap_weights, const igraph_matrix_t *layout, igraph_real_t a, igraph_real_t b, + igraph_real_t *cross_entropy) { + + igraph_real_t mu, nu, xd, yd, sqd; + igraph_integer_t from, to; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_vertices = igraph_vcount(graph); + igraph_matrix_t edge_seen; + + IGRAPH_MATRIX_INIT_FINALLY(&edge_seen, no_of_vertices, no_of_vertices); + + /* Measure the (variable part of the) cross-entropy terms for debugging: + * 1. - sum_edge_e mu(e) * log(nu(e)) + * 2. - sum_edge_e (1 - mu(e)) * log(1 - nu(e)) + * NOTE: the sum goes over the whole adjacency matrix, i.e. all potential edges, + * not just the actual edges. That is because otherwise there's no benefit from + * repelling unconnected edges. + * */ + *cross_entropy = 0; + for (igraph_integer_t eid = 0; eid < no_of_edges; eid++) { + mu = VECTOR(*umap_weights)[eid]; + + /* Find vertices */ + from = IGRAPH_FROM(graph, eid); + to = IGRAPH_TO(graph, eid); + /* Find distance in layout space */ + xd = (MATRIX(*layout, from, 0) - MATRIX(*layout, to, 0)); + yd = (MATRIX(*layout, from, 1) - MATRIX(*layout, to, 1)); + sqd = xd * xd + yd * yd; + /* Find probability associated with distance using fitted Phi */ + nu = 1.0 / (1 + a * pow(sqd, b)); + + /* Term 1: entropy from the edges */ + if (mu > 0) + *cross_entropy -= mu * log(nu); + /* Term 2: entropy from the missing edges */ + if (mu < 1) + *cross_entropy -= (1 - mu) * log(1 - nu); + + MATRIX(edge_seen, from, to) = MATRIX(edge_seen, to, from) = 1; + } + /* Add the entropy from the missing edges */ + for (igraph_integer_t from = 0; from < no_of_vertices; from++) { + for (igraph_integer_t to = 0; to < from; to++) { + if (MATRIX(edge_seen, from, to) > 0) { + continue; + } + + /* Find distance in layout space */ + xd = (MATRIX(*layout, from, 0) - MATRIX(*layout, to, 0)); + yd = (MATRIX(*layout, from, 1) - MATRIX(*layout, to, 1)); + sqd = xd * xd + yd * yd; + + /* Find probability associated with distance using fitted Phi */ + nu = 1.0 / (1 + a * pow(sqd, b)); + + /* Term 2*/ + *cross_entropy -= log(1 - nu); + + MATRIX(edge_seen, from, to) = MATRIX(edge_seen, to, from) = 1; + } + } + + igraph_matrix_destroy(&edge_seen); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} +#endif /* UMAP_DEBUG */ + + +/* clip forces to avoid too rapid shifts */ +static igraph_real_t igraph_i_umap_clip_force(igraph_real_t force, igraph_real_t limit) { + return force > limit ? limit : (force < -limit ? -limit : force); +} + +static igraph_real_t igraph_i_umap_attract( + igraph_real_t dsq, + igraph_real_t a, + igraph_real_t b) +{ + return - (2 * a * b * pow(dsq, b - 1.)) / (1. + a * pow(dsq, b)); +} + +static igraph_real_t igraph_i_umap_repel( + igraph_real_t dsq, + igraph_real_t a, + igraph_real_t b) +{ + igraph_real_t dsq_min = UMAP_CORRECT_DISTANCE_REPULSION * UMAP_CORRECT_DISTANCE_REPULSION; + + return (2 * b) / (dsq_min + dsq) / (1. + a * pow(dsq, b)); +} + +static igraph_error_t igraph_i_umap_apply_forces( + const igraph_t *graph, + const igraph_vector_t *umap_weights, + igraph_matrix_t *layout, + igraph_real_t a, + igraph_real_t b, + igraph_real_t learning_rate, + igraph_bool_t avoid_neighbor_repulsion, + igraph_integer_t negative_sampling_rate, + igraph_integer_t epoch, + igraph_vector_t *next_epoch_sample_per_edge) +{ + igraph_integer_t no_of_vertices = igraph_matrix_nrow(layout); + igraph_integer_t ndim = igraph_matrix_ncol(layout); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t from, to, nneis, eid; + igraph_vector_t from_emb, to_emb, delta; + igraph_real_t force = 0, dsq, force_d; + /* The following is only used for small graphs, to avoid repelling your neighbors + * For large sparse graphs, it's not necessary. For large dense graphs, you should + * not be doing UMAP. + * */ + igraph_vector_int_t neis, negative_vertices; + igraph_integer_t n_negative_vertices = (no_of_vertices - 1 < negative_sampling_rate) ? (no_of_vertices - 1) : negative_sampling_rate; + + /* Initialize vectors */ + IGRAPH_VECTOR_INIT_FINALLY(&from_emb, ndim); + IGRAPH_VECTOR_INIT_FINALLY(&to_emb, ndim); + IGRAPH_VECTOR_INIT_FINALLY(&delta, ndim); + + if (avoid_neighbor_repulsion) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&negative_vertices, 0); + + /* Iterate over edges. Stronger edges are sampled more often */ + for (eid = 0; eid < no_of_edges; eid++) { + /* Zero-weight edges do not affect vertex positions. They can + * also emerge during the weight symmetrization. */ + if (VECTOR(*umap_weights)[eid] <= 0) { + continue; + } + + /* We sample all and only edges that are supposed to be moved at this time */ + if ((VECTOR(*next_epoch_sample_per_edge)[eid] - epoch) >= 1) { + continue; + } + + /* set next epoch at which this edge will be sampled */ + VECTOR(*next_epoch_sample_per_edge)[eid] += 1.0 / VECTOR(*umap_weights)[eid]; + + /* we move all vertices on one end of the edges, then we come back for + * the vertices on the other end. This way we don't move both ends at the + * same time, which is almost a wasted move since they attract each other */ + int swapflag = (int)(RNG_UNIF01() > 0.5); + int swapflag_end = swapflag + 2; + for (; swapflag < swapflag_end; swapflag++) { + + /* half the time, swap the from/to, otherwise some vertices are never moved. + * This has to do with the graph representation within igraph */ + if (swapflag % 2) { + from = IGRAPH_FROM(graph, eid); + to = IGRAPH_TO(graph, eid); + } else { + to = IGRAPH_FROM(graph, eid); + from = IGRAPH_TO(graph, eid); + } + + + /* Current coordinates of both vertices */ + dsq = 0; + for (igraph_integer_t d = 0; d != ndim; d++) { + VECTOR(from_emb)[d] = MATRIX(*layout, from, d); + VECTOR(to_emb)[d] = MATRIX(*layout, to, d); + VECTOR(delta)[d] = MATRIX(*layout, from, d) - MATRIX(*layout, to, d); + dsq += VECTOR(delta)[d] * VECTOR(delta)[d]; + } + + /* Apply attractive force since they are neighbors */ + /* NOTE: If they are already together, no force needed */ + if (dsq >= UMAP_MIN_DISTANCE_ATTRACTION * UMAP_MIN_DISTANCE_ATTRACTION) { + force = igraph_i_umap_attract(dsq, a, b); + for (igraph_integer_t d = 0; d != ndim; d++) { + force_d = force * VECTOR(delta)[d]; + /* clip force to avoid too rapid change */ + force_d = igraph_i_umap_clip_force(force_d, UMAP_FORCE_LIMIT); + + #ifdef UMAP_DEBUG + fprintf(stderr, "force attractive: delta[%ld] = %g, forces[%ld] = %g\n", d, VECTOR(delta)[d], d, force_d); + #endif + + MATRIX(*layout, from, d) += learning_rate * force_d; + } + } + + /* Random other nodes repel the focal vertex */ + IGRAPH_CHECK(igraph_random_sample(&negative_vertices, + 0, no_of_vertices - 2, n_negative_vertices)); + for (igraph_integer_t j = 0; j < n_negative_vertices; j++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Get random neighbor */ + to = VECTOR(negative_vertices)[j]; + /* obviously you cannot repel yourself */ + if (to >= from) { + to++; + } + + /* do not repel neighbors for small graphs, for big graphs this + * does not matter as long as the k in knn << number of vertices */ + if (avoid_neighbor_repulsion) { + /* NOTE: the efficiency of this step could be improved but it + * should be only used for small graphs anyway, so it's fine */ + igraph_bool_t skip = 0; + IGRAPH_CHECK(igraph_incident(graph, &neis, from, IGRAPH_ALL)); + nneis = igraph_vector_int_size(&neis); + for (igraph_integer_t k = 0; k < nneis; k++) { + igraph_integer_t eid2 = VECTOR(neis)[k]; + igraph_integer_t from2, to2; + from2 = IGRAPH_FROM(graph, eid2); + to2 = IGRAPH_TO(graph, eid2); + if (((from2 == from) && (to2 == to)) || ((from2 == to) && (from == to2))) { + skip = 1; + break; + } + } + if (skip == 1) { + continue; + } + } + + /* Get layout of random neighbor and gradient in embedding */ + dsq = 0; + for (igraph_integer_t d = 0; d != ndim; d++) { + VECTOR(to_emb)[d] = MATRIX(*layout, to, d); + VECTOR(delta)[d] = MATRIX(*layout, from, d) - MATRIX(*layout, to, d); + dsq += VECTOR(delta)[d] * VECTOR(delta)[d]; + } + + /* This repels the other vertex assuming it's a negative example + * that is no weight, no edge */ + force = igraph_i_umap_repel(dsq, a, b); + /* The repulsive force is already *away* from the other (non-neighbor) vertex */ + for (igraph_integer_t d = 0; d != ndim; d++) { + force_d = force * VECTOR(delta)[d]; + + /* clip force to avoid too rapid change */ + force_d = igraph_i_umap_clip_force(force_d, UMAP_FORCE_LIMIT); + + #ifdef UMAP_DEBUG + fprintf(stderr, "force repulsive: delta[%ld] = %g, forces[%ld] = %g\n", d, VECTOR(delta)[d], d, force_d); + #endif + + MATRIX(*layout, from, d) += learning_rate * force_d; + } + } + } + } + + /* Free vectors */ + igraph_vector_int_destroy(&negative_vertices); + igraph_vector_destroy(&from_emb); + igraph_vector_destroy(&to_emb); + igraph_vector_destroy(&delta); + IGRAPH_FINALLY_CLEAN(4); + + /* Free vector of neighbors if needed */ + if (avoid_neighbor_repulsion) { + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/* Edges with heavier weight/higher probability should be sampled more often. In + * other words, vertices at each end of those edges should be moved more often. If the + * edge weight is 1.0, which happens to each nearest neighbor due to the correction via + * rho, that vertices at the end of that edge are moved each single epoch. Conversely, + * vertices at the end of weak edges can be moved only once in a while. */ +static igraph_error_t igraph_i_umap_optimize_layout_stochastic_gradient( + const igraph_t *graph, + const igraph_vector_t *umap_weights, + igraph_real_t a, + igraph_real_t b, + igraph_matrix_t *layout, + igraph_integer_t epochs, + igraph_integer_t negative_sampling_rate) { + + igraph_real_t learning_rate = 1; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_t next_epoch_sample_per_edge; + +#ifdef UMAP_DEBUG + igraph_real_t cross_entropy, cross_entropy_old; +#endif + + IGRAPH_VECTOR_INIT_FINALLY(&next_epoch_sample_per_edge, no_of_edges); + + /* Explicit avoidance of neighbor repulsion, only useful in small graphs + * which are never very sparse. This is because negative sampling as implemented + * relies on an approximation that only works if the graph is sparse, which is never + * quite true for small graphs (i.e. |V| << |E| << |V|^2 is hard to judge if + * |V| is small) */ + igraph_bool_t avoid_neighbor_repulsion = 0; + if (igraph_vcount(graph) < 100) { + avoid_neighbor_repulsion = 1; + } + + /* Measure the (variable part of the) cross-entropy terms for debugging: + * 1. - sum_edge_e mu(e) * log(nu(e)) + * 2. + sum_edge_e (1 - mu(e)) * log(1 - nu(e)) + * The latter is approximated by negative sampling as: + * 2b. + sum_random_ij 1 * log(1 - nu_ij) + * whereby the mu = 0 because we assume there's no edge between i and j, and nu_ij + * is basically their distance in embedding space, lensed through the probability + * function Phi. + * */ +#ifdef UMAP_DEBUG + igraph_umap_compute_cross_entropy( + graph, umap_weights, layout, a, b, &cross_entropy); +#endif + + for (igraph_integer_t e = 0; e < epochs; e++) { + /* Apply (stochastic) forces */ + igraph_i_umap_apply_forces( + graph, + umap_weights, + layout, + a, b, + learning_rate, + avoid_neighbor_repulsion, + negative_sampling_rate, + e, + &next_epoch_sample_per_edge); + +#ifdef UMAP_DEBUG + /* Recompute CE and check how it's going*/ + cross_entropy_old = cross_entropy; + igraph_umap_compute_cross_entropy( + graph, umap_weights, layout, a, b, &cross_entropy); + + printf("Cross-entropy before shift: %g, after shift: %g\n", cross_entropy_old, cross_entropy); +#endif + + /* Adjust learning rate */ + learning_rate = 1.0 - (igraph_real_t)(e + 1) / epochs; + } + + igraph_vector_destroy(&next_epoch_sample_per_edge); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Center layout around (0,0) at the end, just for convenience */ +static igraph_error_t igraph_i_umap_center_layout(igraph_matrix_t *layout) { + igraph_integer_t no_of_vertices = igraph_matrix_nrow(layout); + igraph_real_t xm = 0, ym = 0; + + /* Compute center */ + xm = 0; + ym = 0; + for (igraph_integer_t i = 0; i < no_of_vertices; i++) { + xm += MATRIX(*layout, i, 0); + ym += MATRIX(*layout, i, 1); + } + xm /= no_of_vertices; + ym /= no_of_vertices; + + /* Shift vertices */ + for (igraph_integer_t i = 0; i < no_of_vertices; i++) { + MATRIX(*layout, i, 0) -= xm; + MATRIX(*layout, i, 1) -= ym; + } + + return IGRAPH_SUCCESS; +} + + +/* This is the main function that works for any dimensionality of the embedding + * (currently hard-constrained to 2 or 3 ONLY in the initialization). */ +static igraph_error_t igraph_i_layout_umap( + const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_integer_t ndim, + igraph_bool_t distances_are_weights) { + + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_vertices = igraph_vcount(graph); + /* probabilities of each edge being a real connection */ + igraph_vector_t weights; + igraph_vector_t *weightsp; + /* The smoothing parameters given min_dist */ + igraph_real_t a, b; + /* How many repulsions for each attraction */ + igraph_integer_t negative_sampling_rate = 5; + + /* Check input arguments */ + if (min_dist < 0) { + IGRAPH_ERRORF("Minimum distance must not be negative, got %g.", + IGRAPH_EINVAL, min_dist); + } + + if (epochs < 0) { + IGRAPH_ERRORF("Number of epochs must be non-negative, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, epochs); + } + + if ((ndim != 2) && (ndim != 3)) { + IGRAPH_ERRORF("Number of dimensions must be 2 or 3, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, ndim); + + } + + /* Compute weights (exponential weights) from distances if required. + * If the weights have already been computed, they are stored in + * the "distances" vector and we can recycle the pointer. */ + if (distances_are_weights) { + weightsp = (igraph_vector_t *) distances; + } else { + IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_edges); + IGRAPH_CHECK(igraph_layout_umap_compute_weights( + graph, distances, &weights)); + weightsp = &weights; + } + /* From now on everything lives in probability space, it does not matter whether + * the original graph was weighted/distanced or unweighted */ + + /* Compute initial layout if required. If a seed layout is used, then just + * check that the dimensions of the layout make sense. */ + if (use_seed) { + if ((igraph_matrix_nrow(res) != no_of_vertices) || (igraph_matrix_ncol(res) != ndim)) { + if (!distances_are_weights) { + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + } + IGRAPH_ERRORF("Seed layout should have %" IGRAPH_PRId " points in %" IGRAPH_PRId " dimensions, got %" IGRAPH_PRId " points in %" IGRAPH_PRId " dimensions.", + IGRAPH_EINVAL, no_of_vertices, ndim, + igraph_matrix_nrow(res), + igraph_matrix_ncol(res)); + } + + /* Trivial graphs (0 or 1 nodes) with seed - do nothing */ + if (no_of_vertices <= 1) { + if (!distances_are_weights) { + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + } + return IGRAPH_SUCCESS; + } + } else { + /* Trivial graphs (0 or 1 nodes) beget trivial - but valid - layouts */ + if (no_of_vertices <= 1) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_vertices, ndim)); + igraph_matrix_null(res); + if (!distances_are_weights) { + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + } + return IGRAPH_SUCCESS; + } + + /* Skip spectral embedding for now (see #1971), initialize at random */ + if (ndim == 2) { + igraph_layout_random(graph, res); + } else { + igraph_layout_random_3d(graph, res); + } + } + + RNG_BEGIN(); + + /* Fit a and b parameter to find smooth approximation to + * probability distribution in embedding space */ + IGRAPH_CHECK(igraph_i_umap_fit_ab(min_dist, &a, &b)); + + /* Minimize cross-entropy between high-d and low-d probability + * distributions */ + IGRAPH_CHECK(igraph_i_umap_optimize_layout_stochastic_gradient( + graph, + weightsp, + a, b, + res, + epochs, + negative_sampling_rate)); + + if (!distances_are_weights) { + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + } + RNG_END(); + + /* Center layout */ + IGRAPH_CHECK(igraph_i_umap_center_layout(res)); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_layout_umap + * \brief Layout using Uniform Manifold Approximation and Projection (UMAP). + * + * \experimental + * + * UMAP is mostly used to embed high-dimensional vectors in a low-dimensional space + * (most commonly 2D). The algorithm is probabilistic and introduces nonlinearities, + * unlike e.g. PCA and similar to T-distributed Stochastic Neighbor Embedding (t-SNE). + * Nonlinearity helps "cluster" very similar vectors together without imposing a + * global geometry on the embedded space (e.g. a rigid rotation + compression in PCA). + * UMAP uses graphs as intermediate data structures, hence it can be used as a + * graph layout algorithm as well. + * + * + * The general UMAP workflow is to start from vectors, compute a sparse distance + * graph that only contains edges between simiar points (e.g. a k-nearest neighbors + * graph), and then convert these distances into exponentially decaying weights + * between 0 and 1 that are larger for points that are closest neighbors in the + * distance graph. If a graph without any distances associated to the edges is used, + * all weights will be set to 1. + * + * + * If you are trying to use this function to embed high-dimensional vectors, you should + * first compute a k-nearest neighbors graph between your vectors and compute the + * associated distances, and then call this function on that graph. If you already + * have a distance graph, or you have a graph with no distances, you can call this + * function directly. If you already have a graph with meaningful weights + * associated to each edge, you can also call this function, but set the argument + * \p distances_are_weights to true. To compute weights from distances + * without computing the layout, see \ref igraph_layout_umap_compute_weights(). + * + * + * References: + * + * + * Leland McInnes, John Healy, and James Melville: + * UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction (2020) + * https://arxiv.org/abs/1802.03426 + * + * \param graph Pointer to the graph to find a layout for (i.e. to embed). This is + * typically a sparse graph with only edges for the shortest distances stored, e.g. + * a k-nearest neighbors graph. + * \param res Pointer to the n by 2 matrix where the layout coordinates will be stored. + * \param use_seed Logical, if \c true the supplied values in the \p res argument are + * used as an initial layout, if \c false a random initial layout is used. + * \param distances Pointer to a vector of distances associated with the graph edges. + * If this argument is \c NULL, all weights will be set to 1. + * \param min_dist A fudge parameter that decides how close two unconnected vertices + * can be in the embedding before feeling a repulsive force. It must not be + * negative. Typical values are between 0 and 1. + * \param epochs Number of iterations of the main stochastic gradient descent loop on + * the cross-entropy. Typical values are between 30 and 500. + * \param distances_are_weights Whether to use precomputed weights. If + * true, the \p distances vector contains precomputed weights. If \c false (the + * typical use case), this function will compute weights from distances and + * then use them to compute the layout. + * \return Error code. + * + * \sa \ref igraph_layout_umap_3d() + */ +igraph_error_t igraph_layout_umap(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_bool_t distances_are_weights) { + return igraph_i_layout_umap(graph, res, use_seed, + distances, min_dist, epochs, 2, distances_are_weights); +} + + +/** + * \function igraph_layout_umap_3d + * \brief 3D layout using UMAP. + * + * \experimental + * + * This is the 3D version of the UMAP algorithm + * (see \ref igraph_layout_umap() for the 2D version). + * + * \param graph Pointer to the graph to find a layout for (i.e. to embed). This is + * typically a directed, sparse graph with only edges for the shortest distances + * stored, e.g. a k-nearest neighbors graph with the edges going from each focal + * vertex to its neighbors. However, it can also be an undirected graph. If the + * \p distances_are_weights is \c true, this is treated as an undirected graph. + * \param res Pointer to the n by 3 matrix where the layout coordinates will be stored. + * \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 distances Pointer to a vector of distances associated with the graph edges. + * If this argument is \c NULL, all edges are assumed to have the same distance. + * \param min_dist A fudge parameter that decides how close two unconnected vertices + * can be in the embedding before feeling a repulsive force. It must not be + * negative. Typical values are between 0 and 1. + * \param epochs Number of iterations of the main stochastic gradient descent loop on + * the cross-entropy. Typical values are between 30 and 500. + * \param distances_are_weights Whether to use precomputed weights. If \c false (the + * typical use case), this function will compute weights from distances and + * then use them to compute the layout. If \c true, the \p distances vector contains + * precomputed weights, including possibly some weights equal to zero that are + * inconsequential for the layout optimization. + * \return Error code. + * + * \sa \ref igraph_layout_umap() + */ +igraph_error_t igraph_layout_umap_3d(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_bool_t distances_are_weights) { + return igraph_i_layout_umap(graph, res, use_seed, + distances, min_dist, epochs, 3, distances_are_weights); +} diff --git a/src/linalg/arpack.c b/src/linalg/arpack.c new file mode 100644 index 0000000..1908ecb --- /dev/null +++ b/src/linalg/arpack.c @@ -0,0 +1,1556 @@ +/* -*- 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 "core/interruption.h" +#include "linalg/arpack_internal.h" + +#include "igraph_memory.h" +#include "igraph_random.h" + +#include +#include +#include +#include + +/* The ARPACK example file dssimp.f is used as a template */ + +static igraph_error_t 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 igraph_error_t 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 igraph_error_t 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 igraph_error_t 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; + } +} + +/* Pristine ARPACK options object that is not exposed to the user; this is used + * as a template for \c igraph_i_arpack_options_default when the user requests + * a pointer to the default object */ +static const igraph_arpack_options_t igraph_i_arpack_options_pristine = { + /* .bmat = */ { 'I' }, + /* .n = */ 0, + /* .which = */ { 'X', 'X' }, + /* .nev = */ 1, + /* .tol = */ 0, + /* .ncv = */ 0, /* 0 means "automatic" */ + /* .ldv = */ 0, + /* .ishift = */ 1, + /* .mxiter = */ 3000, + /* .nb = */ 1, + /* .mode = */ 1, + /* .start = */ 0, + /* .lworl = */ 0, + /* .sigma = */ 0, + /* .sigmai = */ 0, + /* .info = */ 0, + /* .ierr = */ 0, + /* .noiter = */ 0, + /* .nconv = */ 0, + /* .numop = */ 0, + /* .numopb = */ 0, + /* .numreo = */ 0, + /* .iparam = */ { + /* same as ishift: */ 1, + 0, + /* same as mxiter: */ 3000, + /* same as nb: */ 1, + 0, + 0, + /* same as mode: */ 1 + /* the rest are all zeros */ + }, + /* .ipntr = */ { 0 /* the rest are all zeros */ } +}; + +static IGRAPH_THREAD_LOCAL igraph_arpack_options_t igraph_i_arpack_options_default; + +/** + * \function igraph_arpack_options_init + * \brief 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 = igraph_i_arpack_options_pristine; + + 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_options_get_default + * \brief Returns a pointer to a "default" ARPACK options object. + * + * This function is used by other igraph functions taking an \ref igraph_arpack_options_t + * object as an argument to get a reference to a pre-initialized "default" + * ARPACK options object when the user passes \c NULL instead of a real ARPACK + * options object. The object returned from this function is reset to a pristine + * state with every call to \c igraph_arpack_options_get_default(). + * + * + * The object returned from this function must \em not be destroyed. + * + * Time complexity: O(1). + */ +igraph_arpack_options_t* igraph_arpack_options_get_default(void) { + igraph_i_arpack_options_default = igraph_i_arpack_options_pristine; + return &igraph_i_arpack_options_default; +} + +/** + * \function igraph_arpack_storage_init + * \brief 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)). + */ + +igraph_error_t igraph_arpack_storage_init(igraph_arpack_storage_t *s, igraph_integer_t maxn, + igraph_integer_t maxncv, igraph_integer_t maxldv, + igraph_bool_t symm) { + + /* TODO: check arguments */ + if (maxn > INT_MAX) { + IGRAPH_ERROR("Maximum order of matrices too large for ARPACK.", IGRAPH_EOVERFLOW); + } + if (maxncv > INT_MAX) { + IGRAPH_ERROR("Maximum NCV parameter too large for ARPACK.", IGRAPH_EOVERFLOW); + } + if (maxldv > INT_MAX) { + IGRAPH_ERROR("Maximum LDV parameter too large for ARPACK.", IGRAPH_EOVERFLOW); + } + + 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); /* LCOV_EXCL_LINE */ \ + } \ + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_arpack_storage_destroy + * \brief 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 igraph_error_t 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; + IGRAPH_CHECK(fun(&b, &a, 1, extra)); + + 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 igraph_error_t 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; + IGRAPH_CHECK(fun(&b, &a, 1, extra)); + + 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 igraph_error_t 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 = false; + igraph_bool_t complex_evals = false; + 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; + IGRAPH_CHECK(fun(mat, vec, 2, extra)); + vec[0] = 0; vec[1] = 1; + IGRAPH_CHECK(fun(mat + 2, vec, 2, extra)); + 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_abs(eval1) > igraph_complex_abs(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_abs(eval1) < igraph_complex_abs(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 igraph_error_t 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; + IGRAPH_CHECK(fun(mat, vec, 2, extra)); + vec[0] = 0; vec[1] = 1; + IGRAPH_CHECK(fun(mat + 2, vec, 2, extra)); + 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; +} + +igraph_error_t 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'; + } else { + /* None of the above, no sorting. These 'X' values are + * ignored by ARPACK, but we set them anyway in order to + * avoid an uninitialized 'sort' which would trigger + * checkers such as MemorySanitizer. */ + sort[0] = 'X'; sort[1] = 'X'; + } + + IGRAPH_CHECK(igraph_vector_init_range(&order, 0, nconv)); + 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 IGRAPH_SUCCESS; +} + +igraph_error_t 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'; + } else { + /* None of the above, no sorting. These 'X' values are + * ignored by ARPACK, but we set them anyway in order to + * avoid an uninitialized 'sort' which would trigger + * checkers such as MemorySanitizer. */ + sort[0] = 'X'; sort[1] = 'X'; + } + +#undef which + + IGRAPH_CHECK(igraph_vector_init_range(&order, 0, nconv)); + 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 IGRAPH_SUCCESS; +} + +/** + * \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 try to keep it + * no more than min(n/2 + 2, n - 1), bounds found empirically using the + * eigen_stress.c test... + */ + if (options->ncv > options->n / 2 + 2) { + options->ncv = options->n / 2 + 2; + } + if (options->ncv > options->n - 1) { + options->ncv = options->n - 1; + } + /* ...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 storage 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. + */ + +igraph_error_t 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 = false; + int *select, i; + + int ido = 0; + int rvec = vectors || storage ? 1 : 0; /* calculate eigenvectors? */ + char *all = "A"; + + 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 = true; + +#define CHECKMEM(x) \ + if (!x) { \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ + } \ + 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 = 1; // always use a provided starting vector + if (options->start) { + // user provided the starting vector so we just use that + 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); + } + } + } else { + // we need to generate a random vector on our own; let's not rely on + // ARPACK to do so because we want to use our own RNG + RNG_BEGIN(); + for (i = 0; i < options->n; i++) { + resid[i] = RNG_UNIF(-1, 1); + } + RNG_END(); + } + + /* Ok, we have everything */ + while (1) { + igraph_real_t *from, *to; + + IGRAPH_ALLOW_INTERRUPTION(); + +#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 + /* When there is a non-zero error code in options->info, we expect that + * ARPACK requests a termination of the iteration by setting ido=99. */ + IGRAPH_ASSERT(ido == 99 || options->info == 0); + + if (ido == -1 || ido == 1) { + from = workd + options->ipntr[0] - 1; + to = workd + options->ipntr[1] - 1; + IGRAPH_CHECK(fun(to, from, options->n, extra)); + } else if (ido == 2) { + from = workd + options->ipntr[0] - 1; + to = workd + options->ipntr[1] - 1; + memcpy(to, from, sizeof(igraph_real_t) * options->n); + } else if (ido == 99) { + break; + } else { + IGRAPH_ERRORF("Unexpected IDO value %d when running ARPACK.", IGRAPH_FAILURE, ido); + } + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 = false; + int *select, i; + + int ido = 0; + int rvec = vectors || storage ? 1 : 0; + char *all = "A"; + + 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 = true; + +#define CHECKMEM(x) \ + if (!x) { \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ + } \ + 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 = 1; // always use a provided starting vector + 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); + } + } + } else { + // we need to generate a random vector on our own; let's not rely on + // ARPACK to do so because we want to use our own RNG + RNG_BEGIN(); + for (i = 0; i < options->n; i++) { + resid[i] = RNG_UNIF(-1, 1); + } + RNG_END(); + } + + /* Ok, we have everything */ + while (1) { + igraph_real_t *from, *to; + + IGRAPH_ALLOW_INTERRUPTION(); + +#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 + /* When there is a non-zero error code in options->info, we expect that + * ARPACK requests a termination of the iteration by setting ido=99. */ + IGRAPH_ASSERT(ido == 99 || options->info == 0); + + if (ido == -1 || ido == 1) { + from = workd + options->ipntr[0] - 1; + to = workd + options->ipntr[1] - 1; + IGRAPH_CHECK(fun(to, from, options->n, extra)); + } else if (ido == 2) { + from = workd + options->ipntr[0] - 1; + to = workd + options->ipntr[1] - 1; + memcpy(to, from, sizeof(igraph_real_t) * options->n); + } else if (ido == 4) { + /* same as ido == 1 but the arguments are at different places */ + from = workd + options->ipntr[0] - 1; + to = workd + options->ipntr[2] - 1; + IGRAPH_CHECK(fun(to, from, options->n, extra)); + } else if (ido == 99) { + break; + } else { + IGRAPH_ERRORF("Unexpected IDO value %d when running ARPACK.", IGRAPH_FAILURE, ido); + } + } + + 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->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 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_arpack_unpack_complex + * \brief Makes 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. + */ + +igraph_error_t igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, + igraph_integer_t nev) { + + igraph_integer_t nodes = igraph_matrix_nrow(vectors); + igraph_integer_t no_evs = igraph_matrix_nrow(values); + igraph_integer_t i, j; + igraph_integer_t new_vector_pos, 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_init_copy(vectors, &new_vectors)); + igraph_matrix_destroy(&new_vectors); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/linalg/arpack_internal.h b/src/linalg/arpack_internal.h new file mode 100644 index 0000000..8daaaaf --- /dev/null +++ b/src/linalg/arpack_internal.h @@ -0,0 +1,233 @@ +/* -*- 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_decls.h" +#include "config.h" + +__BEGIN_DECLS + +#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, double *tol, + double *resid, int *ncv, double *v, + int *ldv, int *iparam, int *ipntr, + double *workd, double *workl, + int *lworkl, int *info, + long int bmat_len, long int which_len); + +void igraphdseupd_(void *rvec, char *howmny, void *select, + double *d, double *z, int *ldz, + double *sigma, char *bmat, int *n, + char *which, int *nev, double *tol, + double *resid, int *ncv, double *v, + int *ldv, int *iparam, int *ipntr, + double *workd, double *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, double *tol, + double *resid, int *ncv, double *v, + int *ldv, int *iparam, int *ipntr, + double *workd, double *workl, + int *lworkl, int *info, + long int bmat_len, long int which_len); + +void igraphdneupd_(void *rvec, char *howmny, void *select, + double *dr, double *di, + double *z, int *ldz, + double *sigmar, double *sigmai, + double *workev, char *bmat, int *n, + char *which, int *nev, double *tol, + double *resid, int *ncv, double *v, + int *ldv, int *iparam, int *ipntr, + double *workd, double *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, double *x1, + double *x2, long int which_len); + +void igraphdsortc_(char *which, void *apply, int* n, double *xreal, + double *ximag, double *y, long int which_len); + +#else + +int igraphdsaupd_(int *ido, char *bmat, int *n, + char *which, int *nev, double *tol, + double *resid, int *ncv, double *v, + int *ldv, int *iparam, int *ipntr, + double *workd, double *workl, + int *lworkl, int *info); + +int igraphdseupd_(int *rvec, char *howmny, int *select, + double *d, double *z, int *ldz, + double *sigma, char *bmat, int *n, + char *which, int *nev, double *tol, + double *resid, int *ncv, double *v, + int *ldv, int *iparam, int *ipntr, + double *workd, double *workl, + int *lworkl, int *info); + +int igraphdnaupd_(int *ido, char *bmat, int *n, + char *which, int *nev, double *tol, + double *resid, int *ncv, double *v, + int *ldv, int *iparam, int *ipntr, + double *workd, double *workl, + int *lworkl, int *info); + +int igraphdneupd_(int *rvec, char *howmny, int *select, + double *dr, double *di, + double *z, int *ldz, + double *sigmar, double *sigmai, + double *workev, char *bmat, int *n, + char *which, int *nev, double *tol, + double *resid, int *ncv, double *v, + int *ldv, int *iparam, int *ipntr, + double *workd, double *workl, + int *lworkl, int *info); + +int igraphdsortr_(char *which, int *apply, int* n, double *x1, + double *x2); + +int igraphdsortc_(char *which, int *apply, int* n, double *xreal, + double *ximag, double *y); + +#endif + +__END_DECLS + +#endif /* ARPACK_INTERNAL_H */ diff --git a/src/linalg/blas.c b/src/linalg/blas.c new file mode 100644 index 0000000..4439ac3 --- /dev/null +++ b/src/linalg/blas.c @@ -0,0 +1,262 @@ +/* -*- 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_blas.h" +#include "linalg/blas_internal.h" + +#include + +/** + * \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 \p x and \p y are vectors + * and \p 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). + * It must always have the correct length, but its + * elements need not be set when beta=0. + * + * Time complexity: O(nk) if the matrix is of size n x k + * + * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, + * \c IGRAPH_SUCCESS otherwise. + * \sa \ref igraph_blas_dgemv_array if you have arrays instead of + * vectors. + * + * \example examples/simple/blas.c + */ +igraph_error_t 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; + + if (igraph_matrix_nrow(a) > INT_MAX || igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Matrix too large for BLAS", IGRAPH_EOVERFLOW); + } + + 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 + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_blas_dgemm + * \brief Matrix-matrix multiplication using BLAS. + * + * This function is a somewhat more user-friendly interface to + * the \c dgemm function in BLAS. \c dgemm calculates + * alpha*a*b + beta*c, where a, b and c are matrices, of which a and b + * can be transposed. + * + * \param transpose_a whether to transpose the matrix \p a + * \param transpose_b whether to transpose the matrix \p b + * \param alpha the constant \c alpha + * \param a the matrix \c a + * \param b the matrix \c b + * \param beta the constant \c beta + * \param c the matrix \c c. The result will also be stored here. + * If beta is zero, c will be resized to fit the result. + * + * Time complexity: O(n m k) where matrix a is of size n × k, and matrix b is of + * size k × m. + * + * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, + * \c IGRAPH_EINVAL if the matrices have incompatible sizes, + * \c IGRAPH_SUCCESS otherwise. + * + * \example examples/simple/blas_dgemm.c + */ +igraph_error_t igraph_blas_dgemm(igraph_bool_t transpose_a, igraph_bool_t transpose_b, + igraph_real_t alpha, const igraph_matrix_t *a, const igraph_matrix_t *b, + igraph_real_t beta, igraph_matrix_t *c) { + char trans_a = transpose_a ? 'T' : 'N'; + char trans_b = transpose_b ? 'T' : 'N'; + int m, n, k, lda, ldb, ldc; + igraph_integer_t nrow_oa = transpose_a ? igraph_matrix_ncol(a) : igraph_matrix_nrow(a); + igraph_integer_t ncol_oa = transpose_a ? igraph_matrix_nrow(a) : igraph_matrix_ncol(a); + igraph_integer_t nrow_ob = transpose_b ? igraph_matrix_ncol(b) : igraph_matrix_nrow(b); + igraph_integer_t ncol_ob = transpose_b ? igraph_matrix_nrow(b) : igraph_matrix_ncol(b); + + if (ncol_oa != nrow_ob) { + IGRAPH_ERRORF("%" IGRAPH_PRId "-by-%" IGRAPH_PRId " and %" IGRAPH_PRId "-by-%" IGRAPH_PRId + " matrices cannot be multiplied, incompatible dimensions.", IGRAPH_EINVAL, + nrow_oa, ncol_oa, nrow_ob, ncol_ob); + } + if (beta != 0 && (ncol_oa != igraph_matrix_ncol(c) || nrow_oa != igraph_matrix_nrow(c))) { + IGRAPH_ERRORF("%" IGRAPH_PRId "-by-%" IGRAPH_PRId " and %" IGRAPH_PRId "-by-%" IGRAPH_PRId + " matrices cannot be added, incompatible dimensions.", IGRAPH_EINVAL, + nrow_oa, ncol_ob, igraph_matrix_nrow(c), igraph_matrix_ncol(c)); + } + if (nrow_oa > INT_MAX || ncol_oa > INT_MAX) { + IGRAPH_ERROR("Matrix A too large for BLAS.", IGRAPH_EOVERFLOW); + } + if (ncol_ob > INT_MAX) { + IGRAPH_ERROR("Matrix B too large for BLAS.", IGRAPH_EOVERFLOW); + } + if (beta == 0) { + IGRAPH_CHECK(igraph_matrix_resize(c, nrow_oa, ncol_ob)); + } + + m = (int) nrow_oa; + k = (int) ncol_oa; + n = (int) ncol_ob; + lda = (int) igraph_matrix_nrow(a); + ldb = (int) igraph_matrix_nrow(b); + ldc = (int) igraph_matrix_nrow(c); + + +#ifdef HAVE_GFORTRAN + igraphdgemm_(&trans_a, &trans_b, &m, &n, &k, &alpha, VECTOR(a->data), + &lda, VECTOR(b->data), &ldb, &beta, VECTOR(c->data), &ldc, + /*trans_a_len*/ 1, /*trans_b_len*/ 1); +#else + igraphdgemm_(&trans_a, &trans_b, &m, &n, &k, &alpha, VECTOR(a->data), + &lda, VECTOR(b->data), &ldb, &beta, VECTOR(c->data), &ldc); +#endif + + return IGRAPH_SUCCESS; +} + +/** + * \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 + * + * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, + * \c IGRAPH_SUCCESS otherwise. + * + * \sa \ref igraph_blas_dgemv if you have vectors instead of + * arrays. + */ +igraph_error_t 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; + + if (igraph_matrix_nrow(a) > INT_MAX || igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Matrix too large for BLAS", IGRAPH_EOVERFLOW); + } + + 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 + + return IGRAPH_SUCCESS; +} + +/** + * \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) { + if (igraph_vector_size(v) > INT_MAX) { + IGRAPH_ERROR("Vector too large for BLAS", IGRAPH_EOVERFLOW); + } + + int n = (int) 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 + */ +igraph_error_t igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, + igraph_real_t *res) { + + if (igraph_vector_size(v1) > INT_MAX) { + IGRAPH_ERROR("Vector too large for BLAS", IGRAPH_EOVERFLOW); + } + + int n = (int) 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 IGRAPH_SUCCESS; +} diff --git a/src/linalg/blas_internal.h b/src/linalg/blas_internal.h new file mode 100644 index 0000000..58ff5f8 --- /dev/null +++ b/src/linalg/blas_internal.h @@ -0,0 +1,94 @@ +/* -*- 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_decls.h" +#include "config.h" + +__BEGIN_DECLS + +#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, double *alpha, + double *a, int *lda, double *x, int *incx, + double *beta, double *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, double *alpha, + double *a, int *lda, double *x, int *incx, + double *beta, double *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); + +__END_DECLS + +#endif diff --git a/src/linalg/eigen.c b/src/linalg/eigen.c new file mode 100644 index 0000000..337e129 --- /dev/null +++ b/src/linalg/eigen.c @@ -0,0 +1,1468 @@ +/* -*- 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 +#include + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 p1 = 0, p2 = which->howmany - 1, pr = 0; + int n; + + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); + + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_real_t small; + int p1, p2, pr = 0; + + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); + + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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? */ + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 p1 = 0, p2 = which->howmany / 2, pr = 0; + + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); + + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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) { + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); /* TODO: n isn't used after this assignment */ + } else if (sA) { + if (igraph_sparsemat_nrow(sA) > INT_MAX) { + IGRAPH_ERROR("Number of rows in sparse matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_sparsemat_nrow(sA); /* TODO: n isn't used after this assignment */ + 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 IGRAPH_SUCCESS; +} + +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 igraph_error_t 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_CHECK(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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; + } +} + +/* 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; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) 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; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) 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; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) 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; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) 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; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) 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; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; + igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; + igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; + + if (LESS(a_i, b_i)) { + return -1; + } else if (MORE(a_i, b_i)) { + return 1; + } else { + igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; + igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; + if (NONZERO(a_i) && ZERO(b_i)) { + return -1; + } + if (ZERO(a_i) && NONZERO(b_i)) { + return 1; + } + if (LESS(a_r, b_r)) { + return -1; + } + if (MORE(a_r, b_r)) { + return 1; + } + } + + return 0; +} + +#undef EPS +#undef LESS +#undef MORE +#undef ZERO +#undef NONZERO + +#define INITMAG() \ + do { \ + int i; \ + IGRAPH_VECTOR_INIT_FINALLY(&mag, nev); \ + hasmag=1; \ + for (i=0; i INT_MAX) { + IGRAPH_ERROR("Number of eigenvalues too large for LAPACK.", IGRAPH_EOVERFLOW); + } + nev = (int) igraph_vector_size(real); + + vextra.mag = &mag; + vextra.real = real; + vextra.imag = imag; + extra = &vextra; + + switch (which->pos) { + case IGRAPH_EIGEN_LM: + INITMAG(); + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_lm; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_ALL: + INITMAG(); + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm; + howmany = nev; + break; + case IGRAPH_EIGEN_SM: + INITMAG(); + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_LR: + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_lr; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_SR: + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sr; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_SELECT: + INITMAG(); + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_sm; + start = which->il - 1; + howmany = which->iu - which->il + 1; + break; + case IGRAPH_EIGEN_LI: + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_li; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_SI: + cmpfunc = igraph_i_eigen_matrix_lapack_cmp_si; + howmany = which->howmany; + break; + case IGRAPH_EIGEN_INTERVAL: + case IGRAPH_EIGEN_BE: + default: + IGRAPH_ERROR("Unimplemented eigenvalue ordering", IGRAPH_UNIMPLEMENTED); + break; + } + + IGRAPH_CHECK(igraph_vector_int_init_range(&idx, 0, nev)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &idx); + + 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++) { + igraph_integer_t x = VECTOR(idx)[start + i]; + VECTOR(*values)[i] = igraph_complex(VECTOR(*real)[x], + VECTOR(*imag)[x]); + } + } + + if (vectors) { + igraph_integer_t n = igraph_matrix_nrow(compressed); + IGRAPH_CHECK(igraph_matrix_complex_resize(vectors, n, howmany)); + for (i = 0; i < howmany; i++) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t n = 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 IGRAPH_SUCCESS; + +} + +static igraph_error_t 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) { + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); + } else if (sA) { + if (igraph_sparsemat_nrow(sA) > INT_MAX) { + IGRAPH_ERROR("Number of rows in sparse matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + 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); + } + + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_common(myA, which, + values, + vectors)); + + if (!A) { + igraph_matrix_destroy(&mA); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_eigen_matrix_symmetric + * + * \example examples/simple/igraph_eigen_matrix_symmetric.c + */ + +igraph_error_t 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); + } + + if (algorithm == IGRAPH_EIGEN_AUTO) { + if (which->howmany == n || n < 100) { + algorithm = IGRAPH_EIGEN_LAPACK; + } else { + algorithm = IGRAPH_EIGEN_ARPACK; + } + } + + switch (algorithm) { + 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: + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_eigen_matrix + * + */ + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] += from[nei]; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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); + } + if (n > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); + } + + 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 = (int) 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 = (int) n; + options->ncv = 2 * options->nev < options->n ? 2 * options->nev : options->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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_eigen_adjacency + * + */ + +igraph_error_t 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); + } + + if (algorithm == IGRAPH_EIGEN_AUTO) { + /* Select ARPACK unconditionally because nothing else is implemented yet */ + algorithm = IGRAPH_EIGEN_ARPACK; + } else if (algorithm == IGRAPH_EIGEN_COMP_AUTO) { + /* Select ARPACK unconditionally because nothing else is implemented yet */ + algorithm = IGRAPH_EIGEN_COMP_ARPACK; + } + + switch (algorithm) { + case IGRAPH_EIGEN_LAPACK: + IGRAPH_ERROR("'LAPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_ARPACK: + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + IGRAPH_CHECK(igraph_i_eigen_adjacency_arpack(graph, which, options, + storage, values, vectors, + cmplxvalues, + cmplxvectors)); + break; + case IGRAPH_EIGEN_COMP_LAPACK: + IGRAPH_ERROR("'COMP_LAPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + case IGRAPH_EIGEN_COMP_ARPACK: + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet", + IGRAPH_UNIMPLEMENTED); + /* TODO */ + break; + default: + IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/linalg/lapack.c b/src/linalg/lapack.c new file mode 100644 index 0000000..c50fe55 --- /dev/null +++ b/src/linalg/lapack.c @@ -0,0 +1,1058 @@ +/* -*- 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" + +#include + +#define BASE_FORTRAN_INT +#include "igraph_pmt.h" +#include "igraph_vector_type.h" +#include "igraph_vector_pmt.h" +#include "../core/vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_FORTRAN_INT + +/* Converts a Fortran integer vector to an igraph vector */ +static igraph_error_t igraph_vector_int_update_from_fortran( + igraph_vector_int_t* vec, const igraph_vector_fortran_int_t* fortran_vec +) { + igraph_integer_t size = igraph_vector_fortran_int_size(fortran_vec); + + IGRAPH_CHECK(igraph_vector_int_resize(vec, size)); + + for (igraph_integer_t i = 0; i < size; i++) { + VECTOR(*vec)[i] = VECTOR(*fortran_vec)[i]; + } + + return IGRAPH_SUCCESS; +} + +/* Allocates a Fortran integer vector from the contents of an igraph vector */ +static igraph_error_t igraph_vector_int_copy_to_fortran( + const igraph_vector_int_t* vec, igraph_vector_fortran_int_t* fortran_vec +) { + igraph_integer_t i, size = igraph_vector_int_size(vec); + + IGRAPH_CHECK(igraph_vector_fortran_int_resize(fortran_vec, size)); + + for (i = 0; i < size; i++) { + if (VECTOR(*vec)[i] > INT_MAX) { + IGRAPH_ERROR( + "Overflow error while copying an igraph integer vector to a " + "Fortran integer vector.", IGRAPH_EOVERFLOW + ); + } + VECTOR(*fortran_vec)[i] = (int) VECTOR(*vec)[i]; + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, + int *info) { + int m; + int n; + size_t num_elts; + int lda; + igraph_vector_fortran_int_t vipiv; + + if (igraph_matrix_nrow(a) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + if (igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + + m = (int) igraph_matrix_nrow(a); + n = (int) igraph_matrix_ncol(a); + num_elts = m < n ? m : n; + lda = m > 0 ? m : 1; + + IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, num_elts)); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); + + igraphdgetrf_(&m, &n, VECTOR(a->data), &lda, VECTOR(vipiv), 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_CHECK(igraph_vector_int_update_from_fortran(ipiv, &vipiv)); + } + + igraph_vector_fortran_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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 nrhs; + int lda; + int ldb; + int info; + igraph_vector_fortran_int_t vipiv; + + if (igraph_matrix_nrow(a) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + if (igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + + n = (int) igraph_matrix_nrow(a); + nrhs = (int) igraph_matrix_ncol(b); + lda = n > 0 ? n : 1; + ldb = n > 0 ? n : 1; + + 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_isininterval(ipiv, 1, n)) { + 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); + } + + IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, igraph_vector_int_size(ipiv))); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); + IGRAPH_CHECK(igraph_vector_int_copy_to_fortran(ipiv, &vipiv)); + + igraphdgetrs_(&trans, &n, &nrhs, VECTOR(a->data), &lda, VECTOR(vipiv), + VECTOR(b->data), &ldb, &info); + + igraph_vector_fortran_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); + + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_lapack_dgesv + * \brief 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 + */ + +igraph_error_t igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, + igraph_matrix_t *b, int *info) { + + if (igraph_matrix_nrow(a) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + if (igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + 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_fortran_int_t 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); + } + + IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, n)); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); + + igraphdgesv_(&n, &nrhs, VECTOR(a->data), &lda, VECTOR(vipiv), + 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_CHECK(igraph_vector_int_update_from_fortran(ipiv, &vipiv)); + } + + igraph_vector_fortran_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_lapack_dsyevr + * \brief 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 + */ + +igraph_error_t 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'; + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + int n = (int) igraph_matrix_nrow(A), lda = n, ldz = n; + int m, info; + igraph_vector_t *myvalues = values, vvalues; + igraph_vector_fortran_int_t mysupport; + igraph_vector_t work; + igraph_vector_fortran_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_init_copy(&Acopy, A)); + IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); + + IGRAPH_VECTOR_INIT_FINALLY(&work, 1); + IGRAPH_CHECK(igraph_vector_fortran_int_init(&iwork, 1)); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &iwork); + + if (!values) { + IGRAPH_VECTOR_INIT_FINALLY(&vvalues, 0); + myvalues = &vvalues; + } + + IGRAPH_CHECK(igraph_vector_fortran_int_init(&mysupport, 0)); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &mysupport); + + IGRAPH_CHECK(igraph_vector_resize(myvalues, n)); + + switch (which) { + case IGRAPH_LAPACK_DSYEV_ALL: + range = 'A'; + IGRAPH_CHECK(igraph_vector_fortran_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_fortran_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_fortran_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_fortran_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_update_from_fortran(support, &mysupport)); + IGRAPH_CHECK(igraph_vector_int_resize(support, m)); + } + + igraph_vector_fortran_int_destroy(&mysupport); + IGRAPH_FINALLY_CLEAN(1); + + if (!values) { + igraph_vector_destroy(&vvalues); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_fortran_int_destroy(&iwork); + igraph_vector_destroy(&work); + igraph_matrix_destroy(&Acopy); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_lapack_dgeev + * \brief 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 + */ + +igraph_error_t 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'; + igraph_real_t dummy; /* to prevent some Clang sanitizer warnings */ + + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + 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_init_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) : &dummy, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &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) : &dummy, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_lapack_dgeevx + * \brief 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. Note that the eigenvectors obtained for the balanced + * matrix are backtransformed to those of \p A. + * + * \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 the real and imaginary parts of the + * 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 if not NULL, \p ilo and \p ihi point to 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 + */ + +igraph_error_t 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; + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + int n = (int) igraph_matrix_nrow(A); + int lda = n, ldvl = n, ldvr = n, lwork = -1; + igraph_vector_t work; + igraph_vector_fortran_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; + igraph_real_t dummy; /* to prevent some Clang sanitizer warnings */ + int ilo_dummy; + int ihi_dummy; + + if (ilo == NULL) { + ilo = &ilo_dummy; + } + if (ihi == NULL) { + ihi = &ihi_dummy; + } + 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_init_copy(&Acopy, A)); + IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); + + IGRAPH_VECTOR_INIT_FINALLY(&work, 1); + IGRAPH_CHECK(igraph_vector_fortran_int_init(&iwork, n)); + IGRAPH_FINALLY(igraph_vector_fortran_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) : &dummy, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, + ilo, ihi, VECTOR(*myscale), abnrm, + rconde ? VECTOR(*rconde) : &dummy, + rcondv ? VECTOR(*rcondv) : &dummy, + 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) : &dummy, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, + ilo, ihi, VECTOR(*myscale), abnrm, + rconde ? VECTOR(*rconde) : &dummy, + rcondv ? VECTOR(*rcondv) : &dummy, + 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_fortran_int_destroy(&iwork); + igraph_vector_destroy(&work); + igraph_matrix_destroy(&Acopy); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_lapack_dgehrd(const igraph_matrix_t *A, + int ilo, int ihi, + igraph_matrix_t *result) { + + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + 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 IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_matrix_init_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 IGRAPH_SUCCESS; +} diff --git a/src/linalg/lapack_internal.h b/src/linalg/lapack_internal.h new file mode 100644 index 0000000..baef8d2 --- /dev/null +++ b/src/linalg/lapack_internal.h @@ -0,0 +1,186 @@ +/* -*- 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_decls.h" +#include "config.h" + +__BEGIN_DECLS + +#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, double *a, int *lda, int *ipiv, + int *info); +int igraphdgetrs_(char *trans, int *n, int *nrhs, double *a, + int *lda, int *ipiv, double *b, int *ldb, + int *info); +int igraphdgesv_(int *n, int *nrhs, double *a, int *lda, + int *ipiv, double *b, int *ldb, int *info); + +double igraphdlapy2_(double *x, double *y); + +int igraphdsyevr_(char *jobz, char *range, char *uplo, int *n, + double *a, int *lda, double *vl, + double *vu, int * il, int *iu, + double *abstol, int *m, double *w, + double *z, int *ldz, int *isuppz, + double *work, int *lwork, int *iwork, + int *liwork, int *info); + +int igraphdgeev_(char *jobvl, char *jobvr, int *n, double *a, + int *lda, double *wr, double *wi, + double *vl, int *ldvl, double *vr, int *ldvr, + double *work, int *lwork, int *info); + +int igraphdgeevx_(char *balanc, char *jobvl, char *jobvr, char *sense, + int *n, double *a, int *lda, double *wr, + double *wi, double *vl, int *ldvl, + double *vr, int *ldvr, int *ilo, int *ihi, + double *scale, double *abnrm, + double *rconde, double *rcondv, + double *work, int *lwork, int *iwork, int *info); + +int igraphdgehrd_(int *n, int *ilo, int *ihi, double *A, int *lda, + double *tau, double *work, int *lwork, + int *info); + +double igraphddot_(int *n, double *dx, int *incx, double *dy, int *incy); + +__END_DECLS + +#endif diff --git a/src/math/complex.c b/src/math/complex.c new file mode 100644 index 0000000..10ee36f --- /dev/null +++ b/src/math/complex.c @@ -0,0 +1,390 @@ +/* -*- 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 + +/** + * \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; +} + +/** + * Deprecated in favour of igraph_complex_almost_equals(), which uses relative + * tolerances. Will be removed in 0.11. + */ +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 false; + } + return true; +} + +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) { + /* hypot() avoids overflow at intermediate stages of the calculation */ + 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/math/safe_intop.c b/src/math/safe_intop.c new file mode 100644 index 0000000..ec6b8be --- /dev/null +++ b/src/math/safe_intop.c @@ -0,0 +1,186 @@ +/* + IGraph library. + Copyright (C) 2022 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 +*/ + +#include "math/safe_intop.h" + +/* Use IGRAPH_SAFE_ADD() instead unless there is a need to intercept errors. */ +igraph_error_t igraph_i_safe_add(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res) { + IGRAPH_SAFE_ADD(a, b, res); + return IGRAPH_SUCCESS; +} + +/* Use IGRAPH_SAFE_MULT() instead unless there is a need to intercept errors. */ +igraph_error_t igraph_i_safe_mult(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res) { + IGRAPH_SAFE_MULT(a, b, res); + return IGRAPH_SUCCESS; +} + +/* Overflow-safe sum of integer vector elements. */ +igraph_error_t igraph_i_safe_vector_int_sum(const igraph_vector_int_t *vec, igraph_integer_t *res) { + igraph_integer_t i, n = igraph_vector_int_size(vec); + igraph_integer_t sum = 0; + for (i=0; i < n; ++i) { + IGRAPH_SAFE_ADD(sum, VECTOR(*vec)[i], &sum); + } + *res = sum; + return IGRAPH_SUCCESS; +} + +/* Overflow-safe product of integer vector elements. */ +igraph_error_t igraph_i_safe_vector_int_prod(const igraph_vector_int_t *vec, igraph_integer_t *res) { + igraph_integer_t i, n = igraph_vector_int_size(vec); + igraph_integer_t prod = 1; + for (i=0; i < n; ++i) { + IGRAPH_SAFE_MULT(prod, VECTOR(*vec)[i], &prod); + } + *res = prod; + return IGRAPH_SUCCESS; +} + +/** + * Rounds up an integer to the next power of 2, with overflow check. + * The result for 2, 3 and 4, respectively, would be 2, 4, and 4. + * This function must not be called with negative input. + * Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + */ +igraph_error_t igraph_i_safe_next_pow_2(igraph_integer_t k, igraph_integer_t *res) { + IGRAPH_ASSERT(k >= 0); + if (k == 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + k--; + k |= k >> 1; + k |= k >> 2; + k |= k >> 4; + k |= k >> 8; + k |= k >> 16; +#if IGRAPH_INTEGER_SIZE == 32 + /* Nothing else to do. */ +#elif IGRAPH_INTEGER_SIZE == 64 + k |= k >> 32; +#else + /* If values other than 32 or 64 become allowed, + * this code will need to be updated. */ +# error "Unexpected IGRAPH_INTEGER_SIZE value." +#endif + if (k < IGRAPH_INTEGER_MAX) { + *res = k+1; + return IGRAPH_SUCCESS; + } else { + IGRAPH_ERRORF("Overflow when computing next power of 2 for %" IGRAPH_PRId ".", + IGRAPH_EOVERFLOW, k); + } +} + +/** + * Computes 2^k as an integer, with overflow check. + * This function must not be called with negative input. + */ +igraph_error_t igraph_i_safe_exp2(igraph_integer_t k, igraph_integer_t *res) { + IGRAPH_ASSERT(k >= 0); + if (k > IGRAPH_INTEGER_SIZE-2) { + IGRAPH_ERRORF("Overflow when raising 2 to power %" IGRAPH_PRId ".", + IGRAPH_EOVERFLOW, k); + } + *res = (igraph_integer_t) 1 << k; + return IGRAPH_SUCCESS; +} + +/** + * Checks if an igraph_real_t with no fractional part is representable as an igraph_integer_t. + * Avoids invoking undefined behaviour. + * Must not be called with an input that has a non-zero fractional part. + */ +igraph_bool_t igraph_i_is_real_representable_as_integer(igraph_real_t value) { + /* IGRAPH_INTEGER_MAX is one less than a power of 2, and may not be representable as + * a floating point number. Thus we cannot safely check that value <= IGRAPH_INTEGER_MAX, + * as this would convert IGRAPH_INTEGER_MAX to floating point, potentially changing its value. + * Instead, we compute int_max_plus_1 = IGRAPH_INTEGER_MAX + 1, which is exactly representable + * since it is a power of 2, and check that value < int_max_plus_1. + * + * IGRAPH_INTEGER_MIN is a power of 2 (with negative sign), so there is no such issue. + * + * NaNs and infinities are correctly rejected. + */ + const igraph_real_t int_max_plus_1 = 2.0 * (IGRAPH_INTEGER_MAX / 2 + 1); + const igraph_real_t int_min = (igraph_real_t) IGRAPH_INTEGER_MIN; + if (IGRAPH_LIKELY(int_min <= value && value < int_max_plus_1)) { + return true; + } else { + return false; + } +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is assumed to have no + * fractional part. + */ +static igraph_error_t igraph_i_safe_real_to_int(igraph_real_t value, igraph_integer_t *result) { + if (igraph_i_is_real_representable_as_integer(value)) { + *result = (igraph_integer_t) value; + return IGRAPH_SUCCESS; + } else if (isnan(value)) { + IGRAPH_ERROR("NaN cannot be converted to an integer.", IGRAPH_EINVAL); + } else { + /* %.f ensures exact printing, %g would not */ + IGRAPH_ERRORF("Cannot convert %.f to integer, outside of representable range.", IGRAPH_EOVERFLOW, value); + } +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is converted into an + * integer with ceil(). + */ +igraph_error_t igraph_i_safe_ceil(igraph_real_t value, igraph_integer_t *result) { + return igraph_i_safe_real_to_int(ceil(value), result); +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is converted into an + * integer with floor(). + */ +igraph_error_t igraph_i_safe_floor(igraph_real_t value, igraph_integer_t *result) { + return igraph_i_safe_real_to_int(floor(value), result); +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is converted into an + * integer with round(). + * + * This is typically the slowest of this set of functions. + */ +igraph_error_t igraph_i_safe_round(igraph_real_t value, igraph_integer_t* result) { + return igraph_i_safe_real_to_int(round(value), result); +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is converted into an + * integer with trunc(). + * +* This is typically the fastest of this set of functions. + */ +igraph_error_t igraph_i_safe_trunc(igraph_real_t value, igraph_integer_t* result) { + return igraph_i_safe_real_to_int(trunc(value), result); +} diff --git a/src/math/safe_intop.h b/src/math/safe_intop.h new file mode 100644 index 0000000..e690e70 --- /dev/null +++ b/src/math/safe_intop.h @@ -0,0 +1,139 @@ +/* + 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 _safe_a copy of the GNU General Public 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_SAFE_INTOP_H +#define IGRAPH_MATH_SAFE_INTOP_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +#include "config.h" + +#include + +__BEGIN_DECLS + +/* Largest positive value for igraph_real_t that can safely represent integers. */ +#define IGRAPH_MAX_EXACT_REAL ((double)(1LL << DBL_MANT_DIG)) + +/* These macros raise an error if the operation would result in an overflow. + * They must only be used in functions that return an igraph_error_t. + * + * This code is based on the recommendation of + * https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard + */ + +#ifdef HAVE_BUILTIN_OVERFLOW + +#define IGRAPH_SAFE_ADD(a, b, res) \ + do { \ + igraph_integer_t _safe_a = (a), _safe_b = (b); \ + igraph_integer_t _safe_sum; \ + if (__builtin_add_overflow(_safe_a, _safe_b, &_safe_sum)) { \ + IGRAPH_ERRORF("Overflow when adding %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ + } \ + *(res) = _safe_sum; \ + } while (0) + +#define IGRAPH_SAFE_MULT(a, b, res) \ + do { \ + igraph_integer_t _safe_a = (a), _safe_b = (b); \ + igraph_integer_t _safe_prod; \ + if (__builtin_mul_overflow(_safe_a, _safe_b, &_safe_prod)) { \ + IGRAPH_ERRORF("Overflow when multiplying %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ + } \ + *(res) = _safe_prod; \ + } while (0) + +#else + +#define IGRAPH_SAFE_ADD(a, b, res) \ + do { \ + igraph_integer_t _safe_a = (a), _safe_b = (b); \ + igraph_integer_t _safe_sum; \ + if (((_safe_b > 0) && (_safe_a > (IGRAPH_INTEGER_MAX - _safe_b))) || \ + ((_safe_b < 0) && (_safe_a < (IGRAPH_INTEGER_MIN - _safe_b)))) { \ + IGRAPH_ERRORF("Overflow when adding %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ + } \ + _safe_sum = _safe_a+_safe_b; \ + *(res) = _safe_sum; \ + } while (0) + +#define IGRAPH_SAFE_MULT(a, b, res) \ + do { \ + igraph_integer_t _safe_a = (a), _safe_b = (b); \ + igraph_integer_t _safe_prod; \ + int err=0; \ + if (_safe_a > 0) { /* _safe_a is positive */ \ + if (_safe_b > 0) { /* _safe_a and _safe_b are positive */ \ + if (_safe_a > (IGRAPH_INTEGER_MAX / _safe_b)) { \ + err=1; \ + } \ + } else { /* _safe_a positive, _safe_b nonpositive */ \ + if (_safe_b < (IGRAPH_INTEGER_MIN / _safe_a)) { \ + err=1; \ + } \ + } /* _safe_a positive, _safe_b nonpositive */ \ + } else { /* _safe_a is nonpositive */ \ + if (_safe_b > 0) { /* _safe_a is nonpositive, _safe_b is positive */ \ + if (_safe_a < (IGRAPH_INTEGER_MIN / _safe_b)) { \ + err=1; \ + } \ + } else { /* _safe_a and _safe_b are nonpositive */ \ + if ( (_safe_a != 0) && (_safe_b < (IGRAPH_INTEGER_MAX / _safe_a))) { \ + err=1; \ + } \ + } /* End if _safe_a and _safe_b are nonpositive */ \ + } /* End if _safe_a is nonpositive */ \ + if (err) { \ + IGRAPH_ERRORF("Overflow when multiplying %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ + } \ + _safe_prod = _safe_a*_safe_b; \ + *(res) = _safe_prod; \ + } while (0) + +#endif /* HAVE_BUILTIN_OVERFLOW */ + +/* Overflow-safe calculation of "n choose 2" = n*(n-1) / 2, assuming that n >= 0. */ +#define IGRAPH_SAFE_N_CHOOSE_2(n, res) \ + do { \ + igraph_integer_t _safe_n = (n); \ + if (_safe_n % 2 == 0) IGRAPH_SAFE_MULT(_safe_n / 2, _safe_n - 1, res); \ + else IGRAPH_SAFE_MULT(_safe_n, (_safe_n - 1) / 2, res); \ + } while (0) + +IGRAPH_FUNCATTR_CONST igraph_bool_t igraph_i_is_real_representable_as_integer(igraph_real_t value); + +igraph_error_t igraph_i_safe_ceil(igraph_real_t value, igraph_integer_t* result); +igraph_error_t igraph_i_safe_floor(igraph_real_t value, igraph_integer_t* result); +igraph_error_t igraph_i_safe_round(igraph_real_t value, igraph_integer_t* result); +igraph_error_t igraph_i_safe_trunc(igraph_real_t value, igraph_integer_t* result); + +igraph_error_t igraph_i_safe_next_pow_2(igraph_integer_t k, igraph_integer_t *res); +igraph_error_t igraph_i_safe_exp2(igraph_integer_t k, igraph_integer_t *res); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_safe_add(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_safe_mult(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res); +igraph_error_t igraph_i_safe_vector_int_sum(const igraph_vector_int_t *vec, igraph_integer_t *res); +igraph_error_t igraph_i_safe_vector_int_prod(const igraph_vector_int_t *vec, igraph_integer_t *res); + +__END_DECLS + +#endif /* IGRAPH_MATH_SAFE_INTOP_H */ diff --git a/src/math/utils.c b/src/math/utils.c new file mode 100644 index 0000000..7c0be5b --- /dev/null +++ b/src/math/utils.c @@ -0,0 +1,182 @@ +/* -*- 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_complex.h" +#include "igraph_nongraph.h" +#include "igraph_types.h" + +#include +#include + +int igraph_finite(double x) { + return isfinite(x); +} + +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 + * \brief 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 abs(a-b) / (abs(a) + abs(b)). The + * two numbers are considered equal if this is less than \c eps. + * + * \return True if the two floats are nearly equal to each other within + * the given level of tolerance, false otherwise. + */ +igraph_bool_t igraph_almost_equals(double a, double b, double eps) { + return igraph_cmp_epsilon(a, b, eps) == 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 + * \brief 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. + * + * + * The function supports infinities and NaN values. NaN values are considered + * not equal to any other value (even another NaN), but the ordering is + * arbitrary; in other words, we only guarantee that comparing a NaN with + * any other value will not return zero. Positive infinity is considered to + * be greater than any finite value with any tolerance. Negative infinity is + * considered to be smaller than any finite value with any tolerance. + * Positive infinity is considered to be equal to another positive infinity + * with any tolerance. Negative infinity is considered to be equal to another + * negative infinity with any tolerance. + * + * \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 abs(a-b) / (abs(a) + abs(b)). The + * two numbers are considered equal if this is less than \c eps. + * Negative epsilon values are not allowed; the returned value will + * be undefined in this case. Zero means to do an exact comparison + * without tolerance. + * + * \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); + } +} + +/** + * \function igraph_complex_almost_equals + * \brief Compare two complex numbers with a tolerance. + * + * Determines whether two complex numbers are "almost equal" + * to each other with a given level of tolerance on the relative error. + * + * \param a The first complex number. + * \param b The second complex number. + * \param eps The level of tolerance on the relative error. The relative + * error is defined as abs(a-b) / (abs(a) + abs(b)). The + * two numbers are considered equal if this is less than \c eps. + * + * \return True if the two complex numbers are nearly equal to each other within + * the given level of tolerance, false otherwise. + */ +igraph_bool_t igraph_complex_almost_equals(igraph_complex_t a, + igraph_complex_t b, + igraph_real_t eps) { + + igraph_real_t a_abs = igraph_complex_abs(a); + igraph_real_t b_abs = igraph_complex_abs(b); + igraph_real_t sum = a_abs + b_abs; + igraph_real_t abs_diff = igraph_complex_abs(igraph_complex_sub(a, b)); + + if (a_abs == 0 || b_abs == 0 || sum < DBL_MIN) { + return abs_diff < eps * DBL_MIN; + } else if (! isfinite(sum)) { + return abs_diff < (eps * a_abs + eps * b_abs); + } else { + return abs_diff/ sum < eps; + } +} + +#ifdef __INTEL_COMPILER +#pragma float_control(pop) +#endif diff --git a/src/misc/bipartite.c b/src/misc/bipartite.c new file mode 100644 index 0000000..04c44ea --- /dev/null +++ b/src/misc/bipartite.c @@ -0,0 +1,1338 @@ +/* -*- 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 "graph/attributes.h" +#include "random/random_internal.h" +#include "math/safe_intop.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. May be \c NULL + * if not needed. + * \param ecount1 Pointer to an \c igraph_integer_t, the number of + * edges in the first projection is stored here. May be \c NULL + * if not needed. + * \param vcount2 Pointer to an \c igraph_integer_t, the number of + * vertices in the second projection is stored here. May be \c NULL + * if not needed. + * \param ecount2 Pointer to an \c igraph_integer_t, the number of + * edges in the second projection is stored here. May be \c NULL + * if not needed. + * \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. + */ + +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t vc1 = 0, ec1 = 0, vc2 = 0, ec2 = 0; + igraph_adjlist_t adjlist; + igraph_vector_int_t added; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&added, no_of_nodes); + + 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; + igraph_integer_t neilen1, j; + igraph_integer_t *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++) { + igraph_integer_t k, neilen2, nei = 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++) { + igraph_integer_t nei2 = VECTOR(*neis2)[k]; + if (nei2 <= i) { + continue; + } + if (VECTOR(added)[nei2] == i + 1) { + continue; + } + VECTOR(added)[nei2] = i + 1; + (*ecptr)++; + } + } + } + + if (vcount1) { + *vcount1 = vc1; + } + + if (ecount1) { + *ecount1 = ec1; + } + + if (vcount2) { + *vcount2 = vc2; + } + + if (ecount2) { + *ecount2 = ec2; + } + + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&added); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_t *proj, + int which, + igraph_vector_int_t *multiplicity) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t remaining_nodes = 0; + igraph_vector_int_t vertex_perm, vertex_index; + igraph_vector_int_t edges; + igraph_adjlist_t adjlist; + igraph_vector_int_t *neis1, *neis2; + igraph_integer_t neilen1, neilen2; + igraph_vector_int_t added; + igraph_vector_int_t mult; + + if (which < 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_perm, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&vertex_perm, no_of_nodes)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_index, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&added, no_of_nodes); + + 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_INT_INIT_FINALLY(&mult, multiplicity ? no_of_nodes : 1); + if (multiplicity) { + igraph_vector_int_clear(multiplicity); + } + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i] == which) { + VECTOR(vertex_index)[i] = remaining_nodes++; + igraph_vector_int_push_back(&vertex_perm, i); + } + } + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i] == which) { + igraph_integer_t new_i = VECTOR(vertex_index)[i]; + igraph_integer_t iedges = 0; + neis1 = igraph_adjlist_get(&adjlist, i); + neilen1 = igraph_vector_int_size(neis1); + for (igraph_integer_t j = 0; j < neilen1; j++) { + igraph_integer_t nei = 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 (igraph_integer_t k = 0; k < neilen2; k++) { + igraph_integer_t nei2 = 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_int_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_int_push_back(&edges, nei2)); + } else { + new_nei2 = VECTOR(vertex_index)[nei2]; + IGRAPH_CHECK(igraph_vector_int_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 */ + igraph_integer_t now = igraph_vector_int_size(&edges); + igraph_integer_t from = now - iedges * 2; + for (igraph_integer_t j = from; j < now; j += 2) { + igraph_integer_t nei2 = VECTOR(edges)[j + 1]; + igraph_integer_t new_nei2 = VECTOR(vertex_index)[nei2]; + igraph_integer_t m = VECTOR(mult)[nei2]; + VECTOR(edges)[j + 1] = new_nei2; + IGRAPH_CHECK(igraph_vector_int_push_back(multiplicity, m)); + } + } + } /* if VECTOR(*type)[i] == which */ + } + + igraph_vector_int_destroy(&mult); + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&added); + igraph_vector_int_destroy(&vertex_index); + IGRAPH_FINALLY_CLEAN(4); + + IGRAPH_CHECK(igraph_create(proj, &edges, remaining_nodes, IGRAPH_UNDIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FINALLY(igraph_destroy, proj); + + /* copy graph attributes */ + IGRAPH_I_ATTRIBUTE_DESTROY(proj); + IGRAPH_I_ATTRIBUTE_COPY(proj, graph, /* graph */ true, /* vertex */ false, /* edge */ false); + + /* copy vertex attributes */ + IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, proj, &vertex_perm)); + + igraph_vector_int_destroy(&vertex_perm); + IGRAPH_FINALLY_CLEAN(2); /* +1 for proj1 */ + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * A graph is called bipartite if its vertices can be partitioned into + * two sets, V1 and V2, so that connections only run between V1 and V2, + * but not within V1 or within V2. The \p types parameter specifies + * which vertex should be considered a member of one or the other + * partition. The projection to V1 has vertex set V1, and two vertices + * are connected if they have at least one common neighbour in V2. + * The number of common neighbours is returned in \p multiplicity1, + * if requested. + * + * \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. + */ + +igraph_error_t igraph_bipartite_projection(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_t *proj1, + igraph_t *proj2, + igraph_vector_int_t *multiplicity1, + igraph_vector_int_t *multiplicity2, + igraph_integer_t probe1) { + + igraph_integer_t 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 length.", 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)[probe1]; + if (proj2) { + t2 = 1 - t1; + } else { + t2 = -1; + } + } else { + t1 = proj1 ? 0 : -1; + t2 = proj2 ? 1 : -1; + } + + if (proj1) { + IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj1, t1, multiplicity1)); + IGRAPH_FINALLY(igraph_destroy, proj1); + } + + if (proj2) { + IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj2, t2, multiplicity2)); + } + + if (proj1) { + IGRAPH_FINALLY_CLEAN(1); /* proj1 ownership change */ + } + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_full_bipartite + * \brief Creates a complete bipartite graph. + * + * 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 \type 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 \p 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 uninitialized graph 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 complete graphs, + * \ref igraph_full_multipartite() for complete multipartite graphs. + */ + +igraph_error_t 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 no_of_nodes, no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t ptr; + + if (n1 < 0 || n2 < 0) { + IGRAPH_ERROR("Invalid number of vertices for bipartite graph.", IGRAPH_EINVAL); + } + + IGRAPH_SAFE_ADD(n1, n2, &no_of_nodes); + + if (!directed) { + IGRAPH_SAFE_MULT(n1, n2, &no_of_edges); + } else if (mode == IGRAPH_OUT || mode == IGRAPH_IN) { + IGRAPH_SAFE_MULT(n1, n2, &no_of_edges); + } else { /* mode==IGRAPH_ALL */ + IGRAPH_SAFE_MULT(n1, n2, &no_of_edges); + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges); + } + + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + + ptr = 0; + + if (!directed || mode == IGRAPH_OUT) { + + for (igraph_integer_t i = 0; i < n1; i++) { + for (igraph_integer_t j = 0; j < n2; j++) { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = n1 + j; + } + } + + } else if (mode == IGRAPH_IN) { + + for (igraph_integer_t i = 0; i < n1; i++) { + for (igraph_integer_t j = 0; j < n2; j++) { + VECTOR(edges)[ptr++] = n1 + j; + VECTOR(edges)[ptr++] = i; + } + } + + } else { + + for (igraph_integer_t i = 0; i < n1; i++) { + for (igraph_integer_t j = 0; j < n2; j++) { + VECTOR(edges)[ptr++] = i; + VECTOR(edges)[ptr++] = n1 + j; + VECTOR(edges)[ptr++] = n1 + j; + VECTOR(edges)[ptr++] = i; + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_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 (igraph_integer_t i = n1; i < no_of_nodes; i++) { + VECTOR(*types)[i] = true; + } + } + + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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 + */ + +igraph_error_t igraph_create_bipartite(igraph_t *graph, const igraph_vector_bool_t *types, + const igraph_vector_int_t *edges, + igraph_bool_t directed) { + + igraph_integer_t no_of_nodes = igraph_vector_bool_size(types); + igraph_integer_t no_of_edges = igraph_vector_int_size(edges); + igraph_integer_t i; + + if (no_of_edges % 2 != 0) { + IGRAPH_ERROR("Invalid (odd) edges vector", IGRAPH_EINVEVECTOR); + } + no_of_edges /= 2; + + if (! igraph_vector_int_isininterval(edges, 0, no_of_nodes-1)) { + IGRAPH_ERROR("Invalid (negative or too large) vertex ID", IGRAPH_EINVVID); + } + + /* Check bipartiteness */ + for (i = 0; i < no_of_edges * 2; i += 2) { + igraph_integer_t from = VECTOR(*edges)[i]; + igraph_integer_t to = VECTOR(*edges)[i + 1]; + igraph_bool_t t1 = VECTOR(*types)[from]; + igraph_bool_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_incidence + * \brief Creates a bipartite graph from a bipartite adjacency matrix (deprecated alias). + * + * \deprecated-by igraph_biadjacency 0.10.5 + */ + +igraph_error_t 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 +) { + return igraph_biadjacency(graph, types, incidence, directed, mode, multiple); +} + +/** + * \function igraph_biadjacency + * \brief Creates a bipartite graph from a bipartite adjacency matrix. + * + * A bipartite (or two-mode) graph contains two types of vertices and + * edges always connect vertices of different types. A bipartite adjacency + * matrix is an \em n x \em m matrix, \em n and \em 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 \c false, then a single edge is + * created for every non-zero element in the bipartite adjacency matrix. If \p + * multiple is \c true, 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 input The bipartite adjacency matrix that serves as an input + * to this function. + * \param directed Specifies 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 matrix elements. See details above. + * \return Error code. + * + * Time complexity: O(n*m), the size of the bipartite adjacency matrix. + */ + +igraph_error_t igraph_biadjacency( + igraph_t *graph, igraph_vector_bool_t *types, + const igraph_matrix_t *input, igraph_bool_t directed, + igraph_neimode_t mode, igraph_bool_t multiple +) { + + igraph_integer_t n1 = igraph_matrix_nrow(input); + igraph_integer_t n2 = igraph_matrix_ncol(input); + igraph_integer_t no_of_nodes = n1 + n2; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + if (n1 > 0 && n2 > 0 && igraph_matrix_min(input) < 0) { + IGRAPH_ERRORF( + "Bipartite adjacencey matrix elements should be non-negative, found %g.", + IGRAPH_EINVAL, igraph_matrix_min(input) + ); + } + + if (multiple) { + + for (i = 0; i < n1; i++) { + for (j = 0; j < n2; j++) { + igraph_integer_t elem = ceil(MATRIX(*input, i, j)); + igraph_integer_t from, to; + + if (elem == 0) { + 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_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + } + } else { + for (k = 0; k < elem; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + } + } + } + } + + } else { + + for (i = 0; i < n1; i++) { + for (j = 0; j < n2; j++) { + igraph_integer_t from, to; + + if (MATRIX(*input, 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_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + } else { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + } + } + } + } + + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_incidence + * \brief Convert a bipartite graph into a bipartite adjacency matrix (deprecated alias). + * + * \deprecated-by igraph_get_biadjacency 0.10.5 + */ + +igraph_error_t igraph_get_incidence(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_matrix_t *res, + igraph_vector_int_t *row_ids, + igraph_vector_int_t *col_ids) { + return igraph_get_biadjacency(graph, types, res, row_ids, col_ids); +} + +/** + * \function igraph_get_biadjacency + * \brief Converts a bipartite graph into a bipartite adjacency matrix. + * + * In a bipartite adjacency matrix \c A, element A_ij + * gives the number of edges between the ith vertex of the + * first partition and the jth vertex of the second partition. + * + * + * If the graph contains edges within the same partition, this function + * issues a warning. + * + * \param graph The input graph, edge directions are ignored. + * \param types Boolean vector containing the vertex types. Vertices belonging + * to the first partition have type \c false, the one in the second + * partition type \c true. + * \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 \c false, + * the columns correspond to vertices with type \c true. + * \param row_ids Pointer to an initialized vector or \c NULL. + * If not a null pointer, then the IDs of vertices with type \c false + * are stored here, with the same ordering as the rows of the + * biadjacency matrix. + * \param col_ids Pointer to an initialized vector or \c NULL. + * If not a null pointer, then the IDs of vertices with type \c true + * are stored here, with the same ordering as the columns of the + * biadjacency matrix. + * \return Error code. + * + * Time complexity: O(|E|) where |E| is the number of edges. + * + * \sa \ref igraph_biadjacency() for the opposite operation. + */ + +igraph_error_t igraph_get_biadjacency( + const igraph_t *graph, const igraph_vector_bool_t *types, + igraph_matrix_t *res, igraph_vector_int_t *row_ids, + igraph_vector_int_t *col_ids +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t n1 = 0, n2 = 0, i; + igraph_vector_int_t perm; + igraph_integer_t p1, p2; + igraph_integer_t ignored_edges = 0; + + if (igraph_vector_bool_size(types) != no_of_nodes) { + IGRAPH_ERRORF("Vertex type vector size (%" IGRAPH_PRId ") not equal to number of vertices (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_bool_size(types), no_of_nodes); + } + + for (i = 0; i < no_of_nodes; i++) { + n1 += VECTOR(*types)[i] == false ? 1 : 0; + } + n2 = no_of_nodes - n1; + + IGRAPH_VECTOR_INT_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++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_integer_t from2 = VECTOR(perm)[from]; + igraph_integer_t to2 = 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("%" IGRAPH_PRId " edges running within partitions were ignored.", ignored_edges); + } + + if (row_ids) { + IGRAPH_CHECK(igraph_vector_int_resize(row_ids, n1)); + } + if (col_ids) { + IGRAPH_CHECK(igraph_vector_int_resize(col_ids, n2)); + } + if (row_ids || col_ids) { + for (i = 0; i < no_of_nodes; i++) { + if (! VECTOR(*types)[i]) { + if (row_ids) { + igraph_integer_t i2 = VECTOR(perm)[i]; + VECTOR(*row_ids)[i2] = i; + } + } else { + if (col_ids) { + igraph_integer_t i2 = VECTOR(perm)[i]; + VECTOR(*col_ids)[i2 - n1] = i; + } + } + } + } + + igraph_vector_int_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. + */ + +igraph_error_t 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. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_char_t seen; + igraph_dqueue_int_t Q; + igraph_vector_int_t neis; + igraph_bool_t bi = true; + + /* Shortcut: Graphs with self-loops are not bipartite. */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + if (res) { + *res = false; + } + return IGRAPH_SUCCESS; + } + + /* Shortcut: If the type vector is not requested, and the graph is a forest + * we can immediately return with the result that the graph is bipartite. */ + if (! types && + igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) && + igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST)) { + if (res) { + *res = true; + } + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&seen, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + for (igraph_integer_t i = 0; bi && i < no_of_nodes; i++) { + + if (VECTOR(seen)[i]) { + continue; + } + + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, i)); + VECTOR(seen)[i] = 1; + + while (bi && !igraph_dqueue_int_empty(&Q)) { + igraph_integer_t n, j; + igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); + char acttype = VECTOR(seen)[actnode]; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_ALL)); + n = igraph_vector_int_size(&neis); + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (VECTOR(seen)[nei]) { + char neitype = VECTOR(seen)[nei]; + if (neitype == acttype) { + bi = false; + break; + } + } else { + VECTOR(seen)[nei] = 3 - acttype; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + } + } + } + } + + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + /* Set the cache: A graph that is not bipartite has + * an odd-length cycle, therefore it cannot be a forest. */ + if (! bi) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, false); + } + + if (res) { + *res = bi; + } + + if (types && bi) { + IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + VECTOR(*types)[i] = VECTOR(seen)[i] - 1; + } + } + + igraph_vector_char_destroy(&seen); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_bipartite_game_gnp + * \brief Generates a random bipartite graph with a fixed connection probability. + * + * In the G(n1, n2, p) model, every possible edge between the \p n1 + * bottom vertices and \p n2 top vertices is realized independently with + * probability \p p. + * + * \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 \c NULL, then the vertex types are stored + * here. Bottom vertices come first, \p n1 of them, then \p n2 top + * vertices. + * \param n1 The number of bottom vertices. + * \param n2 The number of top vertices. + * \param p The connection probability. + * \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_gnp() for the unipartite version, + * \ref igraph_bipartite_game_gnm() for the G(n1, n2, m) model. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +igraph_error_t 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_vector_int_t edges; + igraph_vector_t s; + igraph_integer_t n; + igraph_real_t n1_real = (igraph_real_t) n1, n2_real = (igraph_real_t) n2; /* for floating-point operations */ + + if (n1 < 0 || n2 < 0) { + IGRAPH_ERROR("Invalid number of vertices for bipartite graph.", IGRAPH_EINVAL); + } + + if (p < 0.0 || p > 1.0) { + IGRAPH_ERROR("Invalid connection probability.", IGRAPH_EINVAL); + } + + IGRAPH_SAFE_ADD(n1, n2, &n); + + if (types) { + IGRAPH_CHECK(igraph_vector_bool_resize(types, n)); + igraph_vector_bool_null(types); + for (igraph_integer_t i = n1; i < n; i++) { + VECTOR(*types)[i] = true; + } + } + + if (p == 0 || n1 == 0 || n2 == 0) { + IGRAPH_CHECK(igraph_empty(graph, n, directed)); + } else if (p == 1.0) { + IGRAPH_CHECK(igraph_full_bipartite(graph, types, n1, n2, directed, mode)); + } else { + + igraph_integer_t to, from, slen; + igraph_real_t maxedges, last; + igraph_integer_t maxedges_int; + + if (!directed || mode != IGRAPH_ALL) { + maxedges = n1_real * n2_real; + } else { + maxedges = 2.0 * n1_real * n2_real; + } + + if (maxedges > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &maxedges_int)); + IGRAPH_CHECK(igraph_vector_reserve(&s, maxedges_int)); + + 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_int_reserve(&edges, slen * 2)); + + for (igraph_integer_t i = 0; i < slen; i++) { + if (!directed || mode != IGRAPH_ALL) { + to = floor(VECTOR(s)[i] / n1_real); + from = VECTOR(s)[i] - to * n1_real; + to += n1; + } else { + igraph_real_t n1n2 = n1_real * n2_real; + if (VECTOR(s)[i] < n1n2) { + to = floor(VECTOR(s)[i] / n1_real); + from = VECTOR(s)[i] - to * n1_real; + to += n1; + } else { + to = floor((VECTOR(s)[i] - n1n2) / n2_real); + from = VECTOR(s)[i] - n1n2 - to * n2_real; + from += n1; + } + } + + if (mode != IGRAPH_IN) { + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + } else { + igraph_vector_int_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_bipartite_game_gnm + * \brief Generate a random bipartite graph with a fixed number of edges. + * + * The G(n1, n2, m) model uniformly samples bipartite graphs with + * \p n1 bottom vertices and \p n2 top vertices, and precisely \p m edges. + * + * \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, \p n1 of them, then \p n2 top + * vertices. + * \param n1 The number of bottom vertices. + * \param n2 The number of top vertices. + * \param m The number of edges. + * \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_gnm() for the unipartite version, + * \ref igraph_bipartite_game_gnp() for the G(n1, n2, p) + * model. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +igraph_error_t 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_int_t edges; + igraph_vector_t s; + igraph_integer_t n; + igraph_real_t n1_real = (igraph_real_t) n1, n2_real = (igraph_real_t) n2; /* for floating-point operations */ + + if (n1 < 0 || n2 < 0) { + IGRAPH_ERROR("Invalid number of vertices for bipartite graph.", IGRAPH_EINVAL); + } + if (m < 0 || m > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Invalid number of edges.", IGRAPH_EINVAL); + } + + IGRAPH_SAFE_ADD(n1, n2, &n); + + if (types) { + igraph_integer_t i; + IGRAPH_CHECK(igraph_vector_bool_resize(types, n)); + igraph_vector_bool_null(types); + for (i = n1; i < n; i++) { + VECTOR(*types)[i] = true; + } + } + + if (m == 0 || n1 == 0 || n2 == 0) { + if (m > 0) { + IGRAPH_ERROR("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); + } + IGRAPH_CHECK(igraph_empty(graph, n, directed)); + } else { + igraph_integer_t i; + igraph_real_t maxedges; + + if (!directed || mode != IGRAPH_ALL) { + maxedges = n1_real * n2_real; + } else { + maxedges = 2.0 * n1_real * n2_real; + } + + if (m > maxedges) { + IGRAPH_ERROR("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); + } + + if (maxedges == m) { + IGRAPH_CHECK(igraph_full_bipartite(graph, types, n1, n2, directed, mode)); + } else { + + igraph_integer_t to, from; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_random_sample_real(&s, 0, maxedges - 1, m)); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, igraph_vector_size(&s) * 2)); + + for (i = 0; i < m; i++) { + if (!directed || mode != IGRAPH_ALL) { + to = floor(VECTOR(s)[i] / n1_real); + from = VECTOR(s)[i] - to * n1_real; + to += n1; + } else { + igraph_real_t n1n2 = n1_real * n2_real; + if (VECTOR(s)[i] < n1n2) { + to = floor(VECTOR(s)[i] / n1_real); + from = VECTOR(s)[i] - to * n1_real; + to += n1; + } else { + to = floor((VECTOR(s)[i] - n1n2) / n2_real); + from = VECTOR(s)[i] - n1n2 - to * n2_real; + from += n1; + } + } + + if (mode != IGRAPH_IN) { + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + } else { + igraph_vector_int_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); + } + } + + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_bipartite_game + * \brief Generate a bipartite random graph (similar to Erdős-Rényi). + * + * This function is deprecated; use \ref igraph_bipartite_game_gnm() or + * \ref igraph_bipartite_game_gnp() instead. + * + * \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 vertices. + * \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_bipartite_game_gnm(), \ref igraph_bipartite_game_gnp(). + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +igraph_error_t 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 (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/misc/chordality.c b/src/misc/chordality.c new file mode 100644 index 0000000..7cbcffc --- /dev/null +++ b/src/misc/chordality.c @@ -0,0 +1,480 @@ +/* -*- 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_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(). + */ + +igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, + igraph_vector_int_t *alpha, + igraph_vector_int_t *alpham1) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t size; + igraph_vector_int_t head, next, prev; /* doubly linked list with head */ + igraph_integer_t i; + igraph_adjlist_t adjlist; + + /***************/ + /* local j, v; */ + /***************/ + + igraph_integer_t j, v; + + if (no_of_nodes == 0) { + igraph_vector_int_clear(alpha); + if (alpham1) { + igraph_vector_int_clear(alpham1); + } + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&size, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&head, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&next, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&prev, no_of_nodes); + + 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_int_resize(alpha, no_of_nodes)); + if (alpham1) { + IGRAPH_CHECK(igraph_vector_int_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) { + igraph_integer_t 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++) { + igraph_integer_t w = VECTOR(*neis)[k]; + igraph_integer_t ws = VECTOR(size)[w]; + if (ws >= 0) { + + /******************************/ + /* delete w from set(size(w)) */ + /******************************/ + + igraph_integer_t nw = VECTOR(next)[w]; + igraph_integer_t 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_int_destroy(&prev); + igraph_vector_int_destroy(&next); + igraph_vector_int_destroy(&head); + igraph_vector_int_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(). + */ + +igraph_error_t igraph_is_chordal(const igraph_t *graph, + const igraph_vector_int_t *alpha, + const igraph_vector_int_t *alpham1, + igraph_bool_t *chordal, + igraph_vector_int_t *fill_in, + igraph_t *newgraph) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_vector_int_t *my_alpha = alpha, *my_alpham1 = alpham1; + igraph_vector_int_t v_alpha, v_alpham1; + igraph_vector_int_t f, index; + igraph_integer_t i; + igraph_adjlist_t adjlist; + igraph_vector_int_t mark; + igraph_bool_t calc_edges = fill_in || newgraph; + igraph_vector_int_t *my_fill_in = fill_in, v_fill_in; + + /*****************/ + /* local v, w, x */ + /*****************/ + + igraph_integer_t v, w, x; + + if (alpha && (igraph_vector_int_size(alpha) != no_of_nodes)) { + IGRAPH_ERRORF("Alpha vector size (%" IGRAPH_PRId ") not equal to number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(alpha), no_of_nodes); + } + + if (alpham1 && (igraph_vector_int_size(alpham1) != no_of_nodes)) { + IGRAPH_ERRORF("Inverse alpha vector size (%" IGRAPH_PRId ") not equal to number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(alpham1), no_of_nodes); + } + + if (!chordal && !calc_edges) { + /* Nothing to calculate */ + return IGRAPH_SUCCESS; + } + + if (!alpha && !alpham1) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpha, no_of_nodes); + my_alpha = &v_alpha; + IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpham1, no_of_nodes); + my_alpham1 = &v_alpham1; + IGRAPH_CHECK(igraph_maximum_cardinality_search(graph, + (igraph_vector_int_t*) my_alpha, + (igraph_vector_int_t*) my_alpham1)); + } else if (alpha && !alpham1) { + igraph_integer_t v; + IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpham1, no_of_nodes); + my_alpham1 = &v_alpham1; + for (v = 0; v < no_of_nodes; v++) { + igraph_integer_t i = VECTOR(*my_alpha)[v]; + VECTOR(*my_alpham1)[i] = v; + } + } else if (!alpha && alpham1) { + igraph_integer_t i; + IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpha, no_of_nodes); + my_alpha = &v_alpha; + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t v = VECTOR(*my_alpham1)[i]; + VECTOR(*my_alpha)[v] = i; + } + } + + if (!fill_in && newgraph) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&v_fill_in, 0); + my_fill_in = &v_fill_in; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&f, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_nodes); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, no_of_nodes); + if (my_fill_in) { + igraph_vector_int_clear(my_fill_in); + } + + if (chordal) { + *chordal = true; + } + + /*********************/ + /* for i in [1,n] -> */ + /*********************/ + + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *neis; + igraph_integer_t j, len; + + /**********************************************/ + /* w := alpham1(i); f(w) := w; index(w) := i; */ + /**********************************************/ + + w = 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 = VECTOR(*neis)[j]; + VECTOR(mark)[v] = w + 1; + } + + for (j = 0; j < len; j++) { + v = 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 = false; + } + + if (my_fill_in) { + IGRAPH_CHECK(igraph_vector_int_push_back(my_fill_in, x)); + IGRAPH_CHECK(igraph_vector_int_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_int_destroy(&mark); + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&index); + igraph_vector_int_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_int_destroy(&v_fill_in); + IGRAPH_FINALLY_CLEAN(1); + } + + if (!alpha && !alpham1) { + igraph_vector_int_destroy(&v_alpham1); + igraph_vector_int_destroy(&v_alpha); + IGRAPH_FINALLY_CLEAN(2); + } else if (alpha && !alpham1) { + igraph_vector_int_destroy(&v_alpham1); + IGRAPH_FINALLY_CLEAN(1); + } else if (!alpha && alpham1) { + igraph_vector_int_destroy(&v_alpha); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/misc/cocitation.c b/src/misc/cocitation.c new file mode 100644 index 0000000..9657162 --- /dev/null +++ b/src/misc/cocitation.c @@ -0,0 +1,763 @@ +/* + IGraph library. + Copyright (C) 2005-2023 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_cocitation.h" + +#include "igraph_adjlist.h" +#include "igraph_bitset.h" +#include "igraph_interface.h" + +#include "core/interruption.h" + +#include + +static igraph_error_t igraph_i_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 + */ + +igraph_error_t igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids) { + return igraph_i_cocitation_real(graph, res, vids, IGRAPH_OUT, NULL); +} + +/** + * \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 + */ + +igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids) { + return igraph_i_cocitation_real(graph, res, vids, IGRAPH_IN, NULL); +} + +/** + * \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. + * + * + * Note that the presence of loop edges may yield counter-intuitive + * results. A node with a loop edge is considered to be a neighbor of itself + * \em twice (because there are two edge stems incident on the node). Adding a + * loop edge to a node may decrease its similarity to other nodes, but it may + * also \em increase it. For instance, if nodes A and B are connected but share + * no common neighbors, their similarity is zero. However, if a loop edge is + * added to B, then B itself becomes a common neighbor of A and B and thus the + * similarity of A and B will be increased. Consider removing loop edges + * explicitly before invoking this function using \ref igraph_simplify(). + * + * + * 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. + * https://doi.org/10.1016/S0378-8733(03)00009-1 + * + * \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 + */ + +igraph_error_t 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_vector_int_t degrees; + igraph_neimode_t mode0 = IGRAPH_REVERSE_MODE(mode); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode for inverse log weighted similarity.", IGRAPH_EINVMODE); + } + + IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode0, true)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + VECTOR(weights)[i] = VECTOR(degrees)[i]; + if (VECTOR(weights)[i] > 1) { + VECTOR(weights)[i] = 1.0 / log(VECTOR(weights)[i]); + } + } + + IGRAPH_CHECK(igraph_i_cocitation_real(graph, res, vids, mode0, &weights)); + igraph_vector_int_destroy(°rees); + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t vids, + igraph_neimode_t mode, + igraph_vector_t *weights) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_vids; + igraph_integer_t i; + igraph_vector_int_t neis; + igraph_vector_int_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_INT_INIT_FINALLY(&vid_reverse_index, no_of_nodes); + igraph_vector_int_fill(&vid_reverse_index, -1); + for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t v = IGRAPH_VIT_GET(vit); + if (v < 0 || v >= no_of_nodes) { + IGRAPH_ERROR("Invalid vertex ID in vertex selector.", IGRAPH_EINVVID); + } + VECTOR(vid_reverse_index)[v] = i; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_vids, no_of_nodes)); + igraph_matrix_null(res); + + /* The result */ + + for (igraph_integer_t from = 0; from < no_of_nodes; from++) { + IGRAPH_ALLOW_INTERRUPTION(); + + const igraph_real_t weight = weights ? VECTOR(*weights)[from] : 1; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, mode)); + const igraph_integer_t nei_count = igraph_vector_int_size(&neis); + + for (i = 0; i < nei_count - 1; i++) { + igraph_integer_t u = VECTOR(neis)[i]; + igraph_integer_t k = VECTOR(vid_reverse_index)[u]; + for (igraph_integer_t j = i + 1; j < nei_count; j++) { + igraph_integer_t v = VECTOR(neis)[j]; + igraph_integer_t l = 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_int_destroy(&neis); + igraph_vector_int_destroy(&vid_reverse_index); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_neisets_intersect( + const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, + igraph_integer_t *len_union, igraph_integer_t *len_intersection +) { + /* ASSERT: v1 and v2 are sorted */ + igraph_integer_t n1 = igraph_vector_int_size(v1), n2 = igraph_vector_int_size(v2); + *len_intersection = igraph_vector_int_intersection_size_sorted(v1, v2); + *len_union = n1 + n2 - *len_intersection; + return IGRAPH_SUCCESS; +} + +/** + * \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 + */ +igraph_error_t 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; + igraph_integer_t i, j; + igraph_integer_t len_union, len_intersection; + igraph_vector_int_t *v1, *v2; + igraph_integer_t k; + + 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, i); + IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); + if (!igraph_vector_int_binsearch(v1, i, &k)) { + IGRAPH_CHECK(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)); + IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); + v2 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit2)); + IGRAPH_CHECK_OOM(v2, "Failed to query neighbors."); + + IGRAPH_CHECK(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 IGRAPH_SUCCESS; +} + +/** + * \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 + */ +igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_lazy_adjlist_t al; + igraph_integer_t u, v; + igraph_integer_t len_union, len_intersection; + igraph_vector_int_t *v1, *v2; + + igraph_integer_t k = igraph_vector_int_size(pairs); + if (k % 2 != 0) { + IGRAPH_ERROR("Number of elements in `pairs' must be even.", IGRAPH_EINVAL); + } + if (!igraph_vector_int_isininterval(pairs, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Invalid vertex ID in pairs.", IGRAPH_EINVVID); + } + 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 */ + + igraph_bitset_t seen; + IGRAPH_BITSET_INIT_FINALLY(&seen, no_of_nodes); + + for (igraph_integer_t i = 0; i < k; i++) { + igraph_integer_t j = VECTOR(*pairs)[i]; + if (IGRAPH_BIT_TEST(seen, j)) { + continue; + } + IGRAPH_BIT_SET(seen, j); + v1 = igraph_lazy_adjlist_get(&al, j); + IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); + if (!igraph_vector_int_binsearch(v1, j, &u)) { + IGRAPH_CHECK(igraph_vector_int_insert(v1, u, j)); + } + } + + igraph_bitset_destroy(&seen); + IGRAPH_FINALLY_CLEAN(1); + } + + for (igraph_integer_t i = 0, j = 0; i < k; i += 2, j++) { + u = VECTOR(*pairs)[i]; + v = VECTOR(*pairs)[i + 1]; + + if (u == v) { + VECTOR(*res)[j] = 1.0; + continue; + } + + v1 = igraph_lazy_adjlist_get(&al, u); + IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); + v2 = igraph_lazy_adjlist_get(&al, v); + IGRAPH_CHECK_OOM(v2, "Failed to query neighbors."); + + IGRAPH_CHECK(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 IGRAPH_SUCCESS; +} + +/** + * \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 + */ +igraph_error_t 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_int_t pairs; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&pairs, 0); + IGRAPH_CHECK(igraph_edges(graph, es, &pairs)); + IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, &pairs, mode, loops)); + igraph_vector_int_destroy(&pairs); + 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), + * where |V| is the number of vertices in the vertex iterator given, and + * 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 + */ +igraph_error_t 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_CHECK(igraph_similarity_jaccard(graph, res, vids, mode, loops)); + + igraph_integer_t nr = igraph_matrix_nrow(res); + igraph_integer_t nc = igraph_matrix_ncol(res); + for (igraph_integer_t i = 0; i < nr; i++) { + for (igraph_integer_t 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 + */ +igraph_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { + + IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, pairs, mode, loops)); + igraph_integer_t n = igraph_vector_size(res); + for (igraph_integer_t 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 + */ +igraph_error_t 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_CHECK(igraph_similarity_jaccard_es(graph, res, es, mode, loops)); + igraph_integer_t n = igraph_vector_size(res); + for (igraph_integer_t 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/misc/coloring.c b/src/misc/coloring.c new file mode 100644 index 0000000..b88da7e --- /dev/null +++ b/src/misc/coloring.c @@ -0,0 +1,308 @@ +/* + 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_adjlist.h" +#include "igraph_interface.h" + +#include "core/genheap.h" +#include "core/indheap.h" +#include "core/interruption.h" + +/* COLORED_NEIGHBORS: Choose vertices based on the number of already coloured neighbours. */ + +static igraph_error_t igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_t *colors) { + igraph_integer_t i, vertex, maxdeg; + igraph_integer_t vc = igraph_vcount(graph); + igraph_2wheap_t cn; /* indexed heap storing number of already coloured neighbours */ + igraph_vector_int_t neighbors, nei_colors; + + IGRAPH_CHECK(igraph_vector_int_resize(colors, vc)); + igraph_vector_int_null(colors); + + /* 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; + } + + /* find maximum degree and a corresponding vertex */ + { + igraph_vector_int_t degree; + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, false)); + + vertex = igraph_vector_int_which_max(°ree); + maxdeg = VECTOR(degree)[vertex]; + + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&nei_colors, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&nei_colors, maxdeg)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&neighbors, maxdeg)); + + /* two-way indexed heap holding number of already colored neighbors of yet-uncolored vertices */ + 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 */ + } + } + + /* Within this loop, a color of 0 means "uncolored", and valid color indices start at 1. + * At the beginning, all vertices are set as "uncolored", see the vector_int_fill() call above. + * Colors will be decremented to start at 0 later. */ + while (true) { + IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, vertex, IGRAPH_ALL)); + igraph_integer_t nei_count = igraph_vector_int_size(&neighbors); + + /* Colour current vertex by finding the smallest available non-0 color. + * Note that self-loops are effectively skipped as they merely prevent + * the current vertex from being colored with the color value it presently + * has, which is 0 (meaning uncolored). */ + { + igraph_integer_t col; + + IGRAPH_CHECK(igraph_vector_int_resize(&nei_colors, nei_count)); + for (i = 0; i < nei_count; ++i) { + VECTOR(nei_colors)[i] = VECTOR(*colors)[ VECTOR(neighbors)[i] ]; + } + igraph_vector_int_sort(&nei_colors); + + i = 0; + col = 0; + do { + while (i < nei_count && VECTOR(nei_colors)[i] == col) { + i++; + } + col++; + } while (i < nei_count && VECTOR(nei_colors)[i] == col); + + VECTOR(*colors)[vertex] = col; + } + + /* increment number of coloured neighbours for each neighbour of vertex */ + for (i = 0; i < nei_count; ++i) { + igraph_integer_t 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(&neighbors); + igraph_vector_int_destroy(&nei_colors); + igraph_2wheap_destroy(&cn); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/* DSATUR: Choose vertices based on the number of adjacent colours, i.e. "saturation degree" */ + +typedef struct { + igraph_integer_t saturation_degree; /* number of colors used by neighbors */ + igraph_integer_t edge_degree; /* degree in the subgraph induced by uncolored vertices */ +} dsatur_t; + +static int dsatur_t_compare(const void *left, const void *right) { + const dsatur_t *left_d = left; + const dsatur_t *right_d = right; + if (left_d->saturation_degree == right_d->saturation_degree) { + if (left_d->edge_degree == right_d->edge_degree) { + return 0; + } else if (left_d->edge_degree > right_d->edge_degree) { + return 1; + } else { + return -1; + } + } + return left_d->saturation_degree > right_d->saturation_degree ? 1 : -1; +} + +static igraph_bool_t dsatur_is_color_used_by_neighbour( + const igraph_vector_int_t *colors, igraph_integer_t color, + const igraph_vector_int_t *neighbors +) { + igraph_integer_t nei_count = igraph_vector_int_size(neighbors); + + for (igraph_integer_t i=0; i < nei_count; i++) { + igraph_integer_t nei = VECTOR(*neighbors)[i]; + if (VECTOR(*colors)[nei] == color) { + return true; + } + } + + return false; +} + +static void dsatur_update_heap( + const igraph_adjlist_t *adjlist, igraph_gen2wheap_t *node_degrees_heap, + const igraph_vector_int_t *neighbors, const igraph_vector_int_t *colors, + igraph_integer_t color +) { + igraph_gen2wheap_delete_max(node_degrees_heap); + igraph_integer_t nei_count = igraph_vector_int_size(neighbors); + for (igraph_integer_t i=0; i < nei_count; i++) { + igraph_integer_t nei = VECTOR(*neighbors)[i]; + if (!igraph_gen2wheap_has_elem(node_degrees_heap, nei)) { + continue; + } + dsatur_t deg_data = *((dsatur_t*) igraph_gen2wheap_get(node_degrees_heap, nei)); + if (!dsatur_is_color_used_by_neighbour(colors, color, igraph_adjlist_get(adjlist, nei))) { + deg_data.saturation_degree++; + } + deg_data.edge_degree--; + igraph_gen2wheap_modify(node_degrees_heap, nei, °_data); + } +} + +static igraph_integer_t dsatur_get_first_viable_color(const igraph_vector_int_t *used_colors_sorted) { + igraph_integer_t color_count = igraph_vector_int_size(used_colors_sorted); + igraph_integer_t i = 0; + igraph_integer_t col = 0; + while (i < color_count && VECTOR(*used_colors_sorted)[i] == col) { + while (i < color_count && VECTOR(*used_colors_sorted)[i] == col) { + i++; + } + col++; + } + return col; +} + +static igraph_error_t igraph_i_vertex_coloring_dsatur( + const igraph_t *graph, igraph_vector_int_t *colors +) { + igraph_integer_t vcount = igraph_vcount(graph); + IGRAPH_CHECK(igraph_vector_int_resize(colors, vcount)); + + if (vcount == 0) { + return IGRAPH_SUCCESS; + } + + if (vcount == 1) { + VECTOR(*colors)[0] = 0; + return IGRAPH_SUCCESS; + } + + igraph_vector_int_fill(colors, -1); /* -1 as a color means uncolored */ + + /* Multi-edges and self-loops are removed from the adjacency list in order to ensure the correct + * updating of a vertex's neighbors' saturation degrees when that vertex is colored. */ + igraph_adjlist_t adjlist; + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + igraph_gen2wheap_t node_degrees_heap; + IGRAPH_CHECK(igraph_gen2wheap_init(&node_degrees_heap, dsatur_t_compare, sizeof(dsatur_t), vcount)); + IGRAPH_FINALLY(igraph_gen2wheap_destroy, &node_degrees_heap); + + for (igraph_integer_t vertex = 0; vertex < vcount; vertex++) { + dsatur_t dsatur; + dsatur.saturation_degree = 0; + dsatur.edge_degree = igraph_vector_int_size(igraph_adjlist_get(&adjlist, vertex)); + IGRAPH_CHECK(igraph_gen2wheap_push_with_index(&node_degrees_heap, vertex, &dsatur)); + } + + igraph_vector_int_t used_colors_sorted; + IGRAPH_VECTOR_INT_INIT_FINALLY(&used_colors_sorted, 0); + + while (! igraph_gen2wheap_empty(&node_degrees_heap)) { + igraph_integer_t node_to_color = igraph_gen2wheap_max_index(&node_degrees_heap); + igraph_vector_int_t *neighbors = igraph_adjlist_get(&adjlist, node_to_color); + igraph_integer_t nei_count = igraph_vector_int_size(neighbors); + igraph_vector_int_clear(&used_colors_sorted); + for (igraph_integer_t i=0; i < nei_count; i++) { + igraph_integer_t nei = VECTOR(*neighbors)[i]; + if (VECTOR(*colors)[nei] != -1) { + IGRAPH_CHECK(igraph_vector_int_push_back(&used_colors_sorted, VECTOR(*colors)[nei])); + } + } + igraph_vector_int_sort(&used_colors_sorted); + igraph_integer_t color = dsatur_get_first_viable_color(&used_colors_sorted); + dsatur_update_heap(&adjlist, &node_degrees_heap, neighbors, colors, color); + VECTOR(*colors)[node_to_color] = color; + + IGRAPH_ALLOW_INTERRUPTION(); + } + + igraph_vector_int_destroy(&used_colors_sorted); + igraph_gen2wheap_destroy(&node_degrees_heap); + igraph_adjlist_destroy(&adjlist); + 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 greedily, one by one, always choosing the smallest color + * index that differs from that of already colored neighbors. Vertices are picked + * in an order determined by the speified heuristic. + * Colors are represented by 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 for more information. + * + * \return Error code. + * + * \example examples/simple/igraph_coloring.c + */ +igraph_error_t 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); + case IGRAPH_COLORING_GREEDY_DSATUR: + return igraph_i_vertex_coloring_dsatur(graph, colors); + default: + IGRAPH_ERROR("Invalid heuristic for greedy vertex coloring.", IGRAPH_EINVAL); + } +} diff --git a/src/misc/conversion.c b/src/misc/conversion.c new file mode 100644 index 0000000..a1caa0a --- /dev/null +++ b/src/misc/conversion.c @@ -0,0 +1,1037 @@ +/* -*- 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 "math/safe_intop.h" + +#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1) + +/** + * \ingroup conversion + * \function igraph_get_adjacency + * \brief 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 in the unweighted + * case, or the total weight of edges connecting vertex i to vertex j in the + * weighted case. + * + * \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 specifying 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 weights An optional vector containing the weight of each edge + * in the graph. Supply a null pointer here to make all edges have + * the same weight of 1. + * \param loops Constant specifying how loop edges should be handled. + * Possible values: + * \clist + * \cli IGRAPH_NO_LOOPS + * loop edges are ignored and the diagonal of the matrix will contain + * zeros only + * \cli IGRAPH_LOOPS_ONCE + * loop edges are counted once, i.e. a vertex with a single unweighted + * loop edge will have 1 in the corresponding diagonal entry + * \cli IGRAPH_LOOPS_TWICE + * loop edges are counted twice in \em undirected graphs, i.e. a vertex + * with a single unweighted loop edge in an undirected graph will have + * 2 in the corresponding diagonal entry. Loop edges in directed graphs + * are still counted as 1. Essentially, this means that the function is + * counting the incident edge \em stems , which makes more sense when + * using the adjacency matrix in linear algebra. + * \endclist + * \return Error code: + * \c IGRAPH_EINVAL invalid type argument. + * + * \sa \ref 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. + */ + +igraph_error_t igraph_get_adjacency( + const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type, + const igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t i, from, to; + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_null(res); + + if (directed) { + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (from != to || loops != IGRAPH_NO_LOOPS) { + MATRIX(*res, from, to) += WEIGHT_OF(i); + } + } + } else if (type == IGRAPH_GET_ADJACENCY_UPPER) { + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (to < from) { + MATRIX(*res, to, from) += WEIGHT_OF(i); + } else { + MATRIX(*res, from, to) += WEIGHT_OF(i); + } + if (to == from && loops == IGRAPH_LOOPS_TWICE) { + MATRIX(*res, to, to) += WEIGHT_OF(i); + } + } + } else if (type == IGRAPH_GET_ADJACENCY_LOWER) { + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (to < from) { + MATRIX(*res, from, to) += WEIGHT_OF(i); + } else { + MATRIX(*res, to, from) += WEIGHT_OF(i); + } + if (to == from && loops == IGRAPH_LOOPS_TWICE) { + MATRIX(*res, to, to) += WEIGHT_OF(i); + } + } + } else if (type == IGRAPH_GET_ADJACENCY_BOTH) { + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + MATRIX(*res, from, to) += WEIGHT_OF(i); + if (from != to || loops == IGRAPH_LOOPS_TWICE) { + MATRIX(*res, to, from) += WEIGHT_OF(i); + } + } + } else { + IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL); + } + + /* Erase the diagonal if we don't need loop edges */ + if (loops == IGRAPH_NO_LOOPS) { + for (i = 0; i < no_of_nodes; i++) { + MATRIX(*res, i, i) = 0; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_adjacency_sparse + * \brief Returns the adjacency matrix of a graph in a sparse matrix format. + * + * \param graph The input graph. + * \param res Pointer to an \em initialized sparse matrix. The result + * will be stored here. The matrix will be resized as needed. + * \param type Constant specifying 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 + * \return Error code: + * \c IGRAPH_EINVAL invalid type argument. + * + * \sa \ref igraph_get_adjacency(), the dense version of this function. + * + * Time complexity: TODO. + */ + +igraph_error_t igraph_get_adjacency_sparse( + const igraph_t *graph, igraph_sparsemat_t *res, igraph_get_adjacency_t type, + const igraph_vector_t *weights, igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t nzmax = directed ? no_of_edges : no_of_edges * 2; + igraph_integer_t i, from, to; + + IGRAPH_CHECK(igraph_sparsemat_resize(res, no_of_nodes, no_of_nodes, nzmax)); + + if (directed) { + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (from != to || loops != IGRAPH_NO_LOOPS) { + IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); + } + } + } else if (type == IGRAPH_GET_ADJACENCY_UPPER) { + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (to < from) { + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); + } else if (to == from) { + switch (loops) { + case IGRAPH_LOOPS_ONCE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); + break; + case IGRAPH_LOOPS_TWICE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); + break; + case IGRAPH_NO_LOOPS: + default: + break; + } + } else { + IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); + } + } + } else if (type == IGRAPH_GET_ADJACENCY_LOWER) { + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (to < from) { + IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); + } else if (to == from) { + switch (loops) { + case IGRAPH_LOOPS_ONCE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); + break; + case IGRAPH_LOOPS_TWICE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); + break; + case IGRAPH_NO_LOOPS: + default: + break; + } + } else { + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); + } + } + } else if (type == IGRAPH_GET_ADJACENCY_BOTH) { + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (to == from) { + switch (loops) { + case IGRAPH_LOOPS_ONCE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); + break; + case IGRAPH_LOOPS_TWICE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); + break; + case IGRAPH_NO_LOOPS: + default: + break; + } + } else { + IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); + } + } + } else { + IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL); + } + + return IGRAPH_SUCCESS; +} + +#undef WEIGHT_OF + +/** + * \function igraph_get_sparsemat + * \brief Converts an igraph graph to a sparse matrix (deprecated). + * + * If the graph is undirected, then a symmetric matrix is created. + * + * + * This function is deprecated in favour of \ref igraph_get_adjacency_sparse(), + * but does not work in an identical way. This function takes an \em uninitialized + * \c igraph_sparsemat_t while \ref igraph_get_adjacency_sparse() takes + * an already initialized one. + * + * \param graph The input graph. + * \param res Pointer to an \em uninitialized sparse matrix. The result + * will be stored here. + * \return Error code. + * + * \deprecated-by igraph_get_adjacency_sparse 0.10.0 + */ + +igraph_error_t igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t nzmax = igraph_is_directed(graph) ? no_of_edges : 2*no_of_edges; + IGRAPH_CHECK(igraph_sparsemat_init(res, no_of_nodes, no_of_nodes, nzmax)); + return igraph_get_adjacency_sparse(graph, res, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); +} + +/** + * \ingroup conversion + * \function igraph_get_edgelist + * \brief 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. + */ + +igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *res, igraph_bool_t bycol) { + + igraph_eit_t edgeit; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t vptr = 0; + igraph_integer_t from, to; + + IGRAPH_CHECK(igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t igraph_to_directed(igraph_t *graph, + igraph_to_directed_t mode) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t 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_int_t edges; + igraph_integer_t size = no_of_edges * 2; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, size); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + + if (mode == IGRAPH_TO_DIRECTED_RANDOM) { + RNG_BEGIN(); + + for (igraph_integer_t i=0; i < no_of_edges; ++i) { + if (RNG_INTEGER(0,1)) { + igraph_integer_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 (igraph_integer_t i=0; i < no_of_edges; ++i) { + if (VECTOR(edges)[2*i] > VECTOR(edges)[2*i+1]) { + igraph_integer_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, + no_of_nodes, + IGRAPH_DIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, true); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + igraph_destroy(graph); + *graph = newgraph; + + break; + } + case IGRAPH_TO_DIRECTED_MUTUAL: + { + igraph_t newgraph; + igraph_vector_int_t edges; + igraph_vector_int_t index; + igraph_integer_t size; + + IGRAPH_SAFE_MULT(no_of_edges, 4, &size); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size)); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_int_resize(&edges, size)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges * 2); + for (igraph_integer_t 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, + no_of_nodes, + IGRAPH_DIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges=*/false); + IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &index)); + + igraph_vector_int_destroy(&index); + igraph_vector_int_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 + */ + +igraph_error_t igraph_to_undirected(igraph_t *graph, + igraph_to_undirected_t mode, + const igraph_attribute_combination_t *edge_comb) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_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 IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + if (mode == IGRAPH_TO_UNDIRECTED_EACH) { + igraph_es_t es; + igraph_eit_t eit; + + IGRAPH_CHECK(igraph_vector_int_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)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_FROM(graph, edge))); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_TO(graph, edge))); + IGRAPH_EIT_NEXT(eit); + } + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, + no_of_nodes, + IGRAPH_UNDIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + igraph_vector_int_destroy(&edges); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, true); + IGRAPH_FINALLY_CLEAN(2); + igraph_destroy(graph); + *graph = newgraph; + + } else if (mode == IGRAPH_TO_UNDIRECTED_COLLAPSE) { + igraph_vector_int_t inadj, outadj; + igraph_vector_int_t mergeinto; + igraph_integer_t actedge = 0; + + if (attr) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges); + } + + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t n_out, n_in; + igraph_integer_t p1 = -1, p2 = -1; + igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0, last; + IGRAPH_CHECK(igraph_incident(graph, &outadj, i, IGRAPH_OUT)); + IGRAPH_CHECK(igraph_incident(graph, &inadj, i, IGRAPH_IN)); + n_out = igraph_vector_int_size(&outadj); + n_in = igraph_vector_int_size(&inadj); + +#define STEPOUT() if ( (++p1) < n_out) { \ + e1 = VECTOR(outadj)[p1]; \ + n1 = IGRAPH_TO(graph, e1); \ + } +#define STEPIN() if ( (++p2) < n_in) { \ + e2 = VECTOR(inadj )[p2]; \ + n2 = IGRAPH_FROM(graph, e2); \ + } +#define ADD_NEW_EDGE() { \ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); \ + IGRAPH_CHECK(igraph_vector_int_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_int_destroy(&outadj); + igraph_vector_int_destroy(&inadj); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(&newgraph, &edges, + no_of_nodes, + IGRAPH_UNDIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + igraph_vector_int_destroy(&edges); + IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges*/ false); /* 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.vecs, edge_comb)); + + igraph_fixed_vectorlist_destroy(&vl); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_FINALLY_CLEAN(2); + igraph_destroy(graph); + *graph = newgraph; + + if (attr) { + igraph_vector_int_destroy(&mergeinto); + IGRAPH_FINALLY_CLEAN(1); + } + } else if (mode == IGRAPH_TO_UNDIRECTED_MUTUAL) { + igraph_vector_int_t inadj, outadj; + igraph_vector_int_t mergeinto; + igraph_integer_t actedge = 0; + + if (attr) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges); + igraph_vector_int_fill(&mergeinto, -1); + } + + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t n_out, n_in; + igraph_integer_t p1 = -1, p2 = -1; + igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0; + IGRAPH_CHECK(igraph_incident(graph, &outadj, i, + IGRAPH_OUT)); + IGRAPH_CHECK(igraph_incident(graph, &inadj, i, + IGRAPH_IN)); + n_out = igraph_vector_int_size(&outadj); + n_in = igraph_vector_int_size(&inadj); + +#define STEPOUT() if ( (++p1) < n_out) { \ + e1 = VECTOR(outadj)[p1]; \ + n1 = IGRAPH_TO(graph, e1); \ + } +#define STEPIN() if ( (++p2) < n_in) { \ + e2 = 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_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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() + * + */ +igraph_error_t 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_int_t degrees; + igraph_vector_int_t neighbors; + igraph_integer_t prufer_index = 0; + igraph_integer_t n = igraph_vcount(graph); + igraph_bool_t is_tree = false; + + 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_INT_INIT_FINALLY(°rees, n); + IGRAPH_VECTOR_INT_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 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_int_size(&neighbors); + for (igraph_integer_t 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_int_destroy(°rees); + igraph_vector_int_destroy(&neighbors); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/misc/cycle_bases.c b/src/misc/cycle_bases.c new file mode 100644 index 0000000..425a62c --- /dev/null +++ b/src/misc/cycle_bases.c @@ -0,0 +1,540 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include "igraph_cycles.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_error.h" +#include "igraph_interface.h" + +#include "core/interruption.h" +#include "misc/order_cycle.h" + +/**** Fundamental cycles *****/ + +/* Computes fundamental cycles for the connected component containing 'start_vid', + * and appends them to 'result'. + * + * 'visited' must be a vector of length igraph_vcount(graph). + * visited[u] will be set to mark+1 or mark+2 for each visited vertex 'u'. + * No elements of 'visited' must have these values when calling this function. + * 'mark' can be specified in order to be able to re-use a 'visited' vector + * multiple times without having to re-set all its elements. + * + * During the operation of the function, mark+1 indicates that a vertex has been + * queued for processing, but not processed yet. mark+2 indicates that it has + * been processed. + */ +static igraph_error_t +igraph_i_fundamental_cycles_bfs( + const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t start_vid, + igraph_integer_t bfs_cutoff, + const igraph_inclist_t *inclist, + igraph_vector_int_t *visited, + igraph_integer_t mark /* mark used in 'visited' */) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vector_int_t pred_edge; + igraph_vector_int_t u_back, v_back; + + if (start_vid < 0 || start_vid >= no_of_nodes) { + IGRAPH_ERROR("Invalid starting vertex id.", IGRAPH_EINVAL); + } + + if (mark > IGRAPH_INTEGER_MAX - 2) { + IGRAPH_ERROR("Graph too large for cycle basis.", IGRAPH_EOVERFLOW); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&pred_edge, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&u_back, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&v_back, 0); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 0); + + IGRAPH_CHECK(igraph_dqueue_int_push(&q, start_vid)); /* vertex id */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); /* distance from start_vid*/ + VECTOR(*visited)[start_vid] = mark + 1; /* mark as seen */ + VECTOR(pred_edge)[start_vid] = -1; /* non-valid predecessor edge id for root vertex */ + + while (! igraph_dqueue_int_empty(&q)) { + igraph_integer_t v = igraph_dqueue_int_pop(&q); + igraph_integer_t vdist = igraph_dqueue_int_pop(&q); + + igraph_vector_int_t *incs = igraph_inclist_get(inclist, v); + igraph_integer_t n = igraph_vector_int_size(incs); + igraph_integer_t i, j; + + IGRAPH_ALLOW_INTERRUPTION(); + + for (i=0; i < n; ++i) { + igraph_integer_t e = VECTOR(*incs)[i]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + + if (e == VECTOR(pred_edge)[v]) { + /* do not follow the edge through which we came to v */ + continue; + } + + if (VECTOR(*visited)[u] == mark + 2) { + /* u has already been processed */ + continue; + } else if (VECTOR(*visited)[u] == mark + 1) { + /* u has been seen but not yet processed */ + + /* Found cycle edge u-v. Now we walk back up the BFS tree + * in order to find the common ancestor of u and v. We exploit + * that the distance of u from the start vertex is either the + * same as that of v, or one greater. */ + + igraph_integer_t up = u, vp = v; + igraph_integer_t u_back_len, v_back_len; + igraph_vector_int_t cycle; + + IGRAPH_CHECK(igraph_vector_int_push_back(&v_back, e)); + for (;;) { + igraph_integer_t upe, vpe; + + if (up == vp) { + break; + } + + upe = VECTOR(pred_edge)[up]; + IGRAPH_CHECK(igraph_vector_int_push_back(&u_back, upe)); + up = IGRAPH_OTHER(graph, upe, up); + + if (up == vp) { + break; + } + + vpe = VECTOR(pred_edge)[vp]; + IGRAPH_CHECK(igraph_vector_int_push_back(&v_back, vpe)); + vp = IGRAPH_OTHER(graph, vpe, vp); + } + + u_back_len = igraph_vector_int_size(&u_back); + v_back_len = igraph_vector_int_size(&v_back); + IGRAPH_VECTOR_INT_INIT_FINALLY(&cycle, u_back_len + v_back_len); + + for (j=0; j < v_back_len; ++j) { + VECTOR(cycle)[j] = VECTOR(v_back)[j]; + } + for (j=0; j < u_back_len; ++j) { + VECTOR(cycle)[v_back_len + j] = VECTOR(u_back)[u_back_len - j - 1]; + } + + igraph_vector_int_clear(&v_back); + igraph_vector_int_clear(&u_back); + + IGRAPH_CHECK(igraph_vector_int_list_push_back(result, &cycle)); + IGRAPH_FINALLY_CLEAN(1); /* pass ownership of 'cycle' to 'result' */ + } else { + /* encountering u for the first time, queue it for processing */ + + /* Only queue vertices with distance at most 'bfs_cutoff' from the root. */ + /* Negative 'bfs_cutoff' indicates no cutoff. */ + if (bfs_cutoff < 0 || vdist < bfs_cutoff) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, u)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, vdist + 1)); + VECTOR(*visited)[u] = mark + 1; + VECTOR(pred_edge)[u] = e; + } + } + } + + VECTOR(*visited)[v] = mark + 2; /* mark v as processed */ + } /* ! igraph_dqueue_int_empty(&q) */ + + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&v_back); + igraph_vector_int_destroy(&u_back); + igraph_vector_int_destroy(&pred_edge); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_fundamental_cycles + * \brief Finds a fundamental cycle basis. + * + * \experimental + * + * This function computes a fundamental cycle basis associated with a breadth-first + * search tree of the graph. + * + * + * Edge directions are ignored. Multi-edges and self-loops are supported. + * + * \param graph The graph object. + * \param result An initialized integer vector list. The result will be stored here, + * each vector containing the edge IDs of a basis element. + * \param start_vid If negative, a complete fundamental cycle basis is returned. + * If a vertex ID, the fundamental cycles associated with the BFS tree rooted + * in that vertex will be returned, only for the weakly connected component + * containing that vertex. + * \param bfs_cutoff If negative, a complete cycle basis is returned. Otherwise, only + * cycles of length 2*bfs_cutoff + 1 or shorter are included. \p bfs_cutoff + * is used to limit the depth of the BFS tree when searching for cycle edges. + * \param weights Currently unused. + * \return Error code. + * + * Time complexity: O(|V| + |E|). + */ +igraph_error_t igraph_fundamental_cycles(const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t start_vid, + igraph_integer_t bfs_cutoff, + const igraph_vector_t *weights) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t estimated_rank; + igraph_integer_t i; + igraph_inclist_t inclist; + igraph_vector_int_t visited; /* see comments before igraph_i_fundamental_cycles_bfs() */ + + IGRAPH_UNUSED(weights); + + if (start_vid >= no_of_nodes) { + IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&visited, no_of_nodes); + + /* Compute cycle rank assuming that the graph is connected. */ + estimated_rank = no_of_edges - no_of_nodes + 1; + estimated_rank = estimated_rank < 0 ? 0 : estimated_rank; + + igraph_vector_int_list_clear(result); + IGRAPH_CHECK(igraph_vector_int_list_reserve(result, estimated_rank)); + + if (start_vid < 0) { + for (i=0; i < no_of_nodes; ++i) { + if (! VECTOR(visited)[i]) { + IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs(graph, result, i, bfs_cutoff, &inclist, + &visited, /* mark */ 0)); + } + } + } else { + IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs(graph, result, start_vid, bfs_cutoff, &inclist, + &visited, /* mark */ 0)); + } + + igraph_vector_int_destroy(&visited); + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/***** Minimum weight cycle basis *****/ + +/* In this implementation, the cycle vectors (basis elements) are stored as a sparse representation: + * a sorted list of edge indices. */ + + +/* qsort-compatible comparison for sparse cycle vectors: shorter ones come first, use lexicographic + * order for equal length ones. Lexicographic order helps keep row insertion into the reduced matrix + * efficient during Gaussian elimination, by ensuring that insertions usually happen near the end. */ +static int cycle_cmp(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2) { + igraph_integer_t n1 = igraph_vector_int_size(v1), n2 = igraph_vector_int_size(v2); + + if (n1 < n2) { + return -1; + } else if (n1 > n2) { + return 1; + } else { + return igraph_vector_int_lex_cmp(v1, v2); + } +} + +/* Adding cycle vectors produces the symmetric difference of the corresponding edge sets. */ +static igraph_error_t cycle_add(const igraph_vector_int_t *a, const igraph_vector_int_t *b, igraph_vector_int_t *res) { + igraph_integer_t na = igraph_vector_int_size(a), nb = igraph_vector_int_size(b); + const igraph_integer_t *pa = VECTOR(*a), *pb = VECTOR(*b); + const igraph_integer_t *pa_end = pa + na, *pb_end = pb + nb; + + igraph_vector_int_clear(res); + for (;;) { + while (pa != pa_end && pb != pb_end && *pa < *pb) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, *pa)); + pa++; + } + while (pa != pa_end && pb != pb_end && *pa == *pb) { + pa++; pb++; + } + while (pa != pa_end && pb != pb_end && *pb < *pa) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, *pb)); + pb++; + } + if (pa == pa_end) { + while (pb != pb_end) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, *pb)); + pb++; + } + break; + } + if (pb == pb_end) { + while (pa != pa_end) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, *pa)); + pa++; + } + break; + } + } + + return IGRAPH_SUCCESS; +} + + +#define MATROW(m, i) (&VECTOR(m)[i]) +#define MATEL(m, i, j) VECTOR(*MATROW(m, i))[j] + +/* Gaussian elimination for sparse cycle vectors. 'reduced_matrix' is always maintained + * in row-echelon form. This function decides if 'cycle' is linearly independent of this + * matrix, and if not, it adds it to the matrix. */ +static igraph_error_t gaussian_elimination(igraph_vector_int_list_t *reduced_matrix, + const igraph_vector_int_t *cycle, + igraph_bool_t *independent) { + + const igraph_integer_t nrow = igraph_vector_int_list_size(reduced_matrix); + igraph_integer_t i; + + igraph_vector_int_t work, tmp; + + IGRAPH_CHECK(igraph_vector_int_init_copy(&work, cycle)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &work); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + + for (i=0; i < nrow; ++i) { + igraph_vector_int_t *row = MATROW(*reduced_matrix, i); + + if ( VECTOR(*row)[0] < VECTOR(work)[0] ) { + continue; + } else if ( VECTOR(*row)[0] == VECTOR(work)[0] ) { + IGRAPH_CHECK(cycle_add(row, &work, &tmp)); + if (igraph_vector_int_empty(&tmp)) { + *independent = false; + igraph_vector_int_destroy(&work); + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; + } + IGRAPH_CHECK(igraph_vector_int_swap(&work, &tmp)); + } else { /* VECTOR(*row)[0] > VECTOR(work)[0] */ + break; + } + } + + /* 'cycle' was linearly independent, insert new row into matrix */ + *independent = true; + IGRAPH_CHECK(igraph_vector_int_list_insert(reduced_matrix, i, &work)); /* transfers ownership */ + + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); /* +1, transferring ownership of 'work' to 'reduced_matrix' */ + + return IGRAPH_SUCCESS; +} + +#undef MATEL +#undef MATROW + + +/** + * \function igraph_minimum_cycle_basis + * \brief Computes a minimum weight cycle basis. + * + * \experimental + * + * This function computes a minimum weight cycle basis of a graph. Currently, + * a modified version of Horton's algorithm is used that allows for cutoffs. + * + * + * Edge directions are ignored. Multi-edges and self-loops are supported. + * + * + * References: + * + * + * Horton, J. D. (1987) + * A polynomial-time algorithm to find the shortest cycle basis of a graph, + * SIAM Journal on Computing, 16 (2): 358–366. + * https://doi.org/10.1137%2F0216026 + * + * \param graph The graph object. + * \param result An initialized integer vector list, the elements of the cycle + * basis will be stored here as vectors of edge IDs. + * \param bfs_cutoff If negative, an exact minimum cycle basis is returned. Otherwise + * only those cycles in the result will be part of some minimum cycle basis which + * are of size 2*bfs_cutoff + 1 or smaller. Cycles longer than this limit + * may not be of the smallest possible size. + * \p bfs_cutoff is used to limit the depth of the BFS tree when computing candidate + * cycles. Specifying a bfs_cutoff can speed up the computation substantially. + * \param complete Boolean value. Used only when \p bfs_cutoff was given. + * If true, a complete basis is returned. If false, only cycles not greater + * than 2*bfs_cutoff + 1 are returned. This may save computation + * time, however, the result will not span the entire cycle space. + * \param use_cycle_order If true, each cycle is returned in natural order: + * the edge IDs will appear ordered along the cycle. This comes at a small + * performance cost. If false, no guarantees are given about the ordering + * of edge IDs within cycles. This parameter exists solely to control + * performance tradeoffs. + * \param weights Currently unused. + * \return Error code. + * + * Time complexity: TODO. + */ +igraph_error_t igraph_minimum_cycle_basis(const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t bfs_cutoff, + igraph_bool_t complete, + igraph_bool_t use_cycle_order, + const igraph_vector_t *weights) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t rank; + igraph_vector_int_list_t candidates; + + IGRAPH_UNUSED(weights); + + /* Compute candidate elements for the minimum weight basis. */ + { + igraph_inclist_t inclist; + igraph_vector_int_t visited; /* visited[v] % 3 is zero for unvisited vertices, see igraph_i_fundamental_cycles_bfs() */ + igraph_vector_int_t degrees; + igraph_integer_t no_of_comps; + igraph_integer_t mark; + + /* We use the degrees to avoid doing a BFS from vertices with d < 3, except in special cases. + * Degrees cannot be computed from the inclist because there we use IGRAPH_LOOPS_ONCE. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_connected_components(graph, NULL, NULL, &no_of_comps, IGRAPH_WEAK)); + rank = no_of_edges - no_of_nodes + no_of_comps; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&visited, no_of_nodes); + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + /* TODO: estimate space to reserve. 'rank' is a lower bound only. */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&candidates, 0); + IGRAPH_CHECK(igraph_vector_int_list_reserve(&candidates, rank)); + + mark = 0; + for (igraph_integer_t i=0; i < no_of_nodes; ++i) { + igraph_integer_t degree = VECTOR(degrees)[i]; + igraph_bool_t vis = VECTOR(visited)[i] % 3 != 0; /* was vertex i visited already? */ + + /* Generally, we only need to run a BFS from vertices of degree 3 or greater. + * The exception is a connected component which is itself a cycle, and therefore + * only contains vertices of degree 2. Thus from unvisited vertices we always run + * a full BFS while from already visited ones only if their degree is at least 3. */ + + /* TODO: mark entire component as visited, not just vertex. */ + if (degree <= 1 || (vis && degree < 3)) { + continue; + } + + /* TODO: BFS is only necessary from a feedback vertex set, find fast FVS approximation algorithm. */ + + IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs( + graph, &candidates, i, (vis || !complete) ? bfs_cutoff : -1, &inclist, &visited, mark)); + mark += 3; + } + + igraph_inclist_destroy(&inclist); + igraph_vector_int_destroy(&visited); + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(3); + } + + /* Sort candidates by size (= weight) and remove duplicates. */ + { + igraph_integer_t cand_count = igraph_vector_int_list_size(&candidates); + + for (igraph_integer_t i=0; i < cand_count; ++i) { + igraph_vector_int_sort(igraph_vector_int_list_get_ptr(&candidates, i)); + } + igraph_vector_int_list_sort(&candidates, &cycle_cmp); + igraph_vector_int_list_remove_consecutive_duplicates(&candidates, igraph_vector_int_all_e); + } + + igraph_vector_int_list_clear(result); + IGRAPH_CHECK(igraph_vector_int_list_reserve(result, rank)); + + /* Find a complete basis, starting with smallest elements. */ + /* This is typically the slowest part of the algorithm. */ + { + igraph_integer_t cand_len = igraph_vector_int_list_size(&candidates); + igraph_vector_int_list_t reduced_matrix; + igraph_bool_t independent; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&reduced_matrix, 0); + + for (igraph_integer_t i=0; i < cand_len; ++i) { + const igraph_vector_int_t *cycle = igraph_vector_int_list_get_ptr(&candidates, i); + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(gaussian_elimination(&reduced_matrix, cycle, &independent)); + if (independent) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(result, cycle)); + } + + if (igraph_vector_int_list_size(&reduced_matrix) == rank) { + /* We have a complete basis. */ + break; + } + } + + igraph_vector_int_list_destroy(&reduced_matrix); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_list_destroy(&candidates); + IGRAPH_FINALLY_CLEAN(1); + + if (use_cycle_order) { + igraph_integer_t result_size = igraph_vector_int_list_size(result); + igraph_vector_int_t tmp; + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + for (igraph_integer_t i=0; i < result_size; ++i) { + igraph_vector_int_t *cycle = igraph_vector_int_list_get_ptr(result, i); + IGRAPH_CHECK(igraph_vector_int_update(&tmp, cycle)); + IGRAPH_CHECK(igraph_i_order_cycle(graph, &tmp, cycle)); + } + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/misc/degree_sequence.cpp b/src/misc/degree_sequence.cpp new file mode 100644 index 0000000..bc6ae98 --- /dev/null +++ b/src/misc/degree_sequence.cpp @@ -0,0 +1,1161 @@ +/* + IGraph library. + Constructing realizations of degree sequences and bi-degree sequences. + Copyright (C) 2018-2024 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 "core/exceptions.h" +#include "math/safe_intop.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 { + igraph_integer_t vertex; + igraph_integer_t degree; + + vd_pair(igraph_integer_t vertex, igraph_integer_t degree) : vertex(vertex), degree(degree) {} +}; + +// (indegree, outdegree) +typedef std::pair bidegree; + +// (vertex, bidegree) pair +struct vbd_pair { + igraph_integer_t vertex; + bidegree degree; + + vbd_pair(igraph_integer_t 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 igraph_error_t igraph_i_havel_hakimi(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool largest) { + igraph_integer_t n = igraph_vector_int_size(deg); + + igraph_integer_t ec = 0; // number of edges added so far + + std::vector vertices; + vertices.reserve(n); + for (igraph_integer_t 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 (igraph_integer_t 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 (igraph_integer_t 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 igraph_error_t igraph_i_havel_hakimi_index(const igraph_vector_int_t *deg, igraph_vector_int_t *edges) { + igraph_integer_t n = igraph_vector_int_size(deg); + + igraph_integer_t ec = 0; // number of edges added so far + + typedef std::list vlist; + vlist vertices; + for (igraph_integer_t i = 0; i < n; ++i) { + vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); + } + + std::vector pointers; + pointers.reserve(n); + for (auto it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + for (const auto &pt : pointers) { + vertices.sort(degree_greater); + + vd_pair vd = *pt; + vertices.erase(pt); + + if (vd.degree == 0) { + continue; + } + + igraph_integer_t 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 igraph_error_t igraph_i_realize_undirected_multi(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool loops, bool largest) { + igraph_integer_t vcount = igraph_vector_int_size(deg); + + if (vcount == 0) { + return IGRAPH_SUCCESS; + } + + std::vector vertices; + vertices.reserve(vcount); + for (igraph_integer_t i = 0; i < vcount; ++i) { + igraph_integer_t 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); + + igraph_integer_t 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 (igraph_integer_t 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 igraph_error_t igraph_i_realize_undirected_multi_index(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool loops) { + igraph_integer_t vcount = igraph_vector_int_size(deg); + + if (vcount == 0) { + return IGRAPH_SUCCESS; + } + + typedef std::list vlist; + vlist vertices; + for (igraph_integer_t i = 0; i < vcount; ++i) { + vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); + } + + std::vector pointers; + pointers.reserve(vcount); + for (auto it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + // Initial sort + vertices.sort(degree_greater); + + igraph_integer_t ec = 0; + for (const auto &pt : pointers) { + vd_pair vd = *pt; + vertices.erase(pt); + + while (vd.degree > 0) { + auto uit = vertices.begin(); + + if (vertices.empty() || uit->degree == 0) { + // We are out of non-zero degree vertices to connect to. + if (loops) { + for (igraph_integer_t 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) { + auto 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 igraph_error_t igraph_i_kleitman_wang(const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_vector_int_t *edges, bool smallest) { + igraph_integer_t n = igraph_vector_int_size(indeg); // number of vertices + + igraph_integer_t ec = 0; // number of edges added so far + + std::vector vertices; + vertices.reserve(n); + for (igraph_integer_t 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 + igraph_integer_t k = 0; + for (auto 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 igraph_error_t igraph_i_kleitman_wang_index(const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_vector_int_t *edges) { + igraph_integer_t n = igraph_vector_int_size(indeg); // number of vertices + + igraph_integer_t ec = 0; // number of edges added so far + + typedef std::list vlist; + vlist vertices; + for (igraph_integer_t 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 (auto it = vertices.begin(); it != vertices.end(); ++it) { + pointers.push_back(it); + } + + for (const auto &pt : pointers) { + // 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; + } + + igraph_integer_t 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 igraph_error_t igraph_i_realize_undirected_degree_sequence( + igraph_t *graph, + const igraph_vector_int_t *deg, + igraph_edge_type_sw_t allowed_edge_types, + igraph_realize_degseq_t method) { + igraph_integer_t node_count = igraph_vector_int_size(deg); + igraph_integer_t deg_sum; + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(deg, °_sum)); + + 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_int_min(deg) < 0) { + IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL); + } + + igraph_vector_int_t edges; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, deg_sum); + + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + 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_HANDLE_EXCEPTIONS_END; + + IGRAPH_CHECK(igraph_create(graph, &edges, node_count, false)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_realize_directed_degree_sequence( + igraph_t *graph, + const igraph_vector_int_t *outdeg, + const igraph_vector_int_t *indeg, + igraph_edge_type_sw_t allowed_edge_types, + igraph_realize_degseq_t method) { + igraph_integer_t node_count = igraph_vector_int_size(outdeg); + igraph_integer_t edge_count, edge_count2, indeg_sum; + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outdeg, &edge_count)); + + if (igraph_vector_int_size(indeg) != node_count) { + IGRAPH_ERROR("In- and out-degree sequences must have the same length.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(indeg, &indeg_sum)); + if (indeg_sum != edge_count) { + IGRAPH_ERROR("In- and out-degree sequences do not sum to the same value.", IGRAPH_EINVAL); + } + + if (node_count > 0 && (igraph_vector_int_min(outdeg) < 0 || igraph_vector_int_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_int_t edges; + IGRAPH_SAFE_MULT(edge_count, 2, &edge_count2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, edge_count2); + + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + 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_HANDLE_EXCEPTIONS_END; + + IGRAPH_CHECK(igraph_create(graph, &edges, node_count, true)); + + igraph_vector_int_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 realizes 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. In the undirected case, \c IGRAPH_REALIZE_DEGSEQ_SMALLEST + * produces a connected graph when one exists. This makes this method suitable + * for constructing trees with a given degree sequence. + * + * + * 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/2098770 + * + * + * 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 + * + * P. L. Erdős, I. Miklós, Z. Toroczkai: + * A simple Havel-Hakimi type algorithm to realize graphical degree sequences of directed graphs, + * The Electronic Journal of Combinatorics 17.1 (2010). + * http://eudml.org/doc/227072 + * + * + * Sz. Horvát and C. D. Modes: + * Connectedness matters: construction and exact random sampling of connected networks (2021). + * https://doi.org/10.1088/2632-072X/abced5 + * + * \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, + * 2021, 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_realize_bipartite_degree_sequence() to create bipartite graphs + * from two degree sequence; + * \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. + * + * \example examples/simple/igraph_realize_degree_sequence.c + */ + +igraph_error_t igraph_realize_degree_sequence( + igraph_t *graph, + const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, + igraph_edge_type_sw_t allowed_edge_types, + igraph_realize_degseq_t method) +{ + bool directed = indeg != NULL; + + 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); + } +} + + +// Uses index order to construct an undirected bipartite graph. +// degree1 is considered to range from index [0, len(degree1)[, +// so for this implementation degree1 is always the source degree +// sequence and degree2 is always the dest degree sequence. +static igraph_error_t igraph_i_realize_undirected_bipartite_index( + igraph_t *graph, + const igraph_vector_int_t *degree1, const igraph_vector_int_t *degree2, + igraph_bool_t multiedges +) { + igraph_integer_t ec = 0; // The number of edges added so far + igraph_integer_t n1 = igraph_vector_int_size(degree1); + igraph_integer_t n2 = igraph_vector_int_size(degree2); + igraph_vector_int_t edges; + igraph_integer_t ds1_sum; + igraph_integer_t ds2_sum; + + std::vector vertices1; + std::vector vertices2; + std::vector *src_vs = &vertices1; + std::vector *dest_vs = &vertices2; + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(degree1, &ds1_sum)); + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(degree2, &ds2_sum)); + + if (ds1_sum != ds2_sum) { + goto fail; + } + + // If both degree sequences are empty, it's bigraphical + if (!(n1 == 0 && n2 == 0)) { + if (igraph_vector_int_min(degree1) < 0 || igraph_vector_int_min(degree2) < 0) { + goto fail; + } + } + + vertices1.reserve(n1); + vertices2.reserve(n2); + + for (igraph_integer_t i = 0; i < n1; i++) { + vertices1.push_back(vd_pair(i, VECTOR(*degree1)[i])); + } + for (igraph_integer_t i = 0; i < n2; i++) { + vertices2.push_back(vd_pair(i + n1, VECTOR(*degree2)[i])); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, ds1_sum + ds2_sum); + + while (!vertices1.empty() && !vertices2.empty()) { + // Go by index, so we start in ds1, so ds2 needs to be sorted. + std::stable_sort(vertices2.begin(), vertices2.end(), degree_greater); + // No sorting of ds1 needed for index case + vd_pair vd_src = vertices1.front(); + // No multiedges - Take the first vertex, connect to the largest delta in opposite partition + if (!multiedges) { + // Remove the source degrees + src_vs->erase(src_vs->begin()); + + if (vd_src.degree == 0) { + continue; + } + + if (dest_vs->size() < size_t(vd_src.degree)) { + goto fail; + } + + for (igraph_integer_t i = 0; i < vd_src.degree; i++) { + if ((*dest_vs)[i].degree == 0) { + // Not enough non-zero remaining degree vertices in opposite partition. + // Not graphical. + goto fail; + } + + (*dest_vs)[i].degree--; + + VECTOR(edges)[2*(ec + i)] = vd_src.vertex; + VECTOR(edges)[2*(ec + i) + 1] = (*dest_vs)[i].vertex; + } + ec += vd_src.degree; + } + // If multiedges are allowed + else { + // If this is the last edge to be created from this vertex, we remove it. + if (src_vs->front().degree <= 1) { + src_vs->erase(src_vs->begin()); + } else { + src_vs->front().degree--; + } + + if (vd_src.degree == 0) { + continue; + } + + if (dest_vs->size() < size_t(1)) { + goto fail; + } + // We should never decrement below zero, but check just in case. + IGRAPH_ASSERT((*dest_vs)[0].degree - 1 >= 0); + + // Connect to the opposite partition + (*dest_vs)[0].degree--; + + VECTOR(edges)[2 * ec] = vd_src.vertex; + VECTOR(edges)[2 * ec + 1] = (*dest_vs)[0].vertex; + ec++; + } + } + IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, false)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + +fail: + IGRAPH_ERRORF("The given bidegree sequence cannot be realized as a bipartite %sgraph.", + IGRAPH_EINVAL, multiedges ? "multi" : "simple "); +} + +/** + * \function igraph_realize_bipartite_degree_sequence + * \brief Generates a bipartite graph with the given bidegree sequence. + * + * \experimental + * + * This function generates a bipartite graph with the given bidegree sequence, + * using a Havel-Hakimi-like construction algorithm. The order in which vertices + * are connected up is controlled by the \p method parameter. When using the + * \c IGRAPH_REALIZE_DEGSEQ_SMALLEST method, it is ensured that the graph will be + * connected if and only if the given bidegree sequence is potentially connected. + * + * + * The vertices of the graph will be ordered so that those having \p degrees1 + * come first, followed by \p degrees2. + * + * \param graph Pointer to an uninitialized graph object. + * \param degrees1 The degree sequence of the first partition. + * \param degrees2 The degree sequence of the second partition. + * \param allowed_edge_types The types of edges to allow in the graph. + * \clist + * \cli IGRAPH_SIMPLE_SW + * simple graph (i.e. no multi-edges allowed). + * \cli IGRAPH_MULTI_SW + * multi-edges are allowed + * \endclist + * \param method Controls the order in which vertices are selected for connection. + * Possible values: + * \clist + * \cli IGRAPH_REALIZE_DEGSEQ_SMALLEST + * The vertex with smallest remaining degree is selected first, from either + * partition. The result is usually a graph with high negative degree + * assortativity. This method is guaranteed to generate a connected graph, + * if one exists. + * \cli IGRAPH_REALIZE_DEGSEQ_LARGEST + * The vertex with the largest remaining degree is selected first, from + * either parition. 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. + * \endclist + * \return Error code. + * \sa \ref igraph_is_bigraphical() to test bigraphicality without generating a graph. + */ + +igraph_error_t igraph_realize_bipartite_degree_sequence( + igraph_t *graph, + const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, + const igraph_edge_type_sw_t allowed_edge_types, const igraph_realize_degseq_t method +) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + + igraph_integer_t ec = 0; // The number of edges added so far + igraph_integer_t n1 = igraph_vector_int_size(degrees1); + igraph_integer_t n2 = igraph_vector_int_size(degrees2); + igraph_vector_int_t edges; + igraph_integer_t ds1_sum; + igraph_integer_t ds2_sum; + igraph_bool_t multiedges; + igraph_bool_t largest; + std::vector vertices1; + std::vector vertices2; + + // Bipartite graphs can't have self loops, so we ignore those. + if (allowed_edge_types & IGRAPH_I_MULTI_EDGES_SW) { + // Multiedges allowed + multiedges = true; + } else { + // No multiedges + multiedges = false; + } + + switch (method) { + case IGRAPH_REALIZE_DEGSEQ_SMALLEST: + largest = false; + break; + case IGRAPH_REALIZE_DEGSEQ_LARGEST: + largest = true; + break; + case IGRAPH_REALIZE_DEGSEQ_INDEX: + return igraph_i_realize_undirected_bipartite_index(graph, degrees1, degrees2, multiedges); + default: + IGRAPH_ERROR("Invalid bipartite degree sequence realization method.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(degrees1, &ds1_sum)); + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(degrees2, &ds2_sum)); + + // Degree sequences of the two partitions must sum to the same value + if (ds1_sum != ds2_sum) { + goto fail; + } + + // If both degree sequences are empty, it's bigraphical + if (!(n1 == 0 && n2 == 0)) { + if (igraph_vector_int_min(degrees1) < 0 || igraph_vector_int_min(degrees2) < 0) { + goto fail; + } + } + + vertices1.reserve(n1); + vertices2.reserve(n2); + + for (igraph_integer_t i = 0; i < n1; i++) { + vertices1.push_back(vd_pair(i, VECTOR(*degrees1)[i])); + } + for (igraph_integer_t i = 0; i < n2; i++) { + vertices2.push_back(vd_pair(i + n1, VECTOR(*degrees2)[i])); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, ds1_sum + ds2_sum); + + + std::vector *src_vs; + std::vector *dest_vs; + + while (!vertices1.empty() && !vertices2.empty()) { + // Sort in non-increasing order. + // Note: for the smallest method, we can skip sorting the smaller ds, minor optimization. + // (i.e., we only need to sort the dest partition, since we always just remove the back of the min partition) + std::stable_sort(vertices1.begin(), vertices1.end(), degree_greater); + std::stable_sort(vertices2.begin(), vertices2.end(), degree_greater); + + vd_pair vd_src(-1, -1); + + if (!largest) { + vd_pair min1 = vertices1.back(); + vd_pair min2 = vertices2.back(); + if (min1.degree <= min2.degree) { + src_vs = &vertices1; + dest_vs = &vertices2; + } else { + src_vs = &vertices2; + dest_vs = &vertices1; + } + + vd_src = src_vs->back(); + + } else { + vd_pair max1 = vertices1.front(); + vd_pair max2 = vertices2.front(); + + if (max1.degree >= max2.degree) { + src_vs = &vertices1; + dest_vs = &vertices2; + } else { + src_vs = &vertices2; + dest_vs = &vertices1; + } + + vd_src = src_vs->front(); + } + + IGRAPH_ASSERT(vd_src.degree >= 0); + + if (!multiedges) { + // Remove the smallest element + if (!largest) { + src_vs->pop_back(); + } else { + // Remove the largest element. + src_vs->erase(src_vs->begin()); + } + + if (vd_src.degree == 0) { + continue; + } + if (dest_vs->size() < size_t(vd_src.degree)) { + goto fail; + } + for (igraph_integer_t i = 0; i < vd_src.degree; i++) { + // Decrement the degree of the delta largest vertices in the opposite partition + + if ((*dest_vs)[i].degree == 0) { + // Not enough non-zero remaining degree vertices in opposite partition. + // Not graphical. + goto fail; + } + + (*dest_vs)[i].degree--; + + VECTOR(edges)[2 * (ec + i)] = vd_src.vertex; + VECTOR(edges)[2 * (ec + i) + 1] = (*dest_vs)[i].vertex; + } + ec += vd_src.degree; + } + // If multiedges are allowed + else { + // The smallest degree is in the back, and we know it is in vertices1 + // If this is the last edge to be created from this vertex, we remove it. + if (!largest) { + if (src_vs->back().degree <= 1) { + src_vs->pop_back(); + } else { + // Otherwise we decrement its degrees by 1 for the edge we are about to create. + src_vs->back().degree--; + } + } else { + if (src_vs->front().degree <= 1) { + src_vs->erase(src_vs->begin()); + } else { + src_vs->front().degree--; + } + } + + if (vd_src.degree == 0) { + continue; + } + + if (dest_vs->size() < size_t(1)) { + goto fail; + } + // We should never decrement below zero, but check just in case. + IGRAPH_ASSERT((*dest_vs)[0].degree - 1 >= 0); + + // Connect to the opposite partition + (*dest_vs)[0].degree--; + + VECTOR(edges)[2 * ec] = vd_src.vertex; + VECTOR(edges)[2 * ec + 1] = (*dest_vs)[0].vertex; + ec++; + } + } + IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, IGRAPH_UNDIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + +fail: + IGRAPH_ERRORF("The given bidegree sequence cannot be realized as a bipartite %sgraph.", + IGRAPH_EINVAL, multiedges ? "multi" : "simple "); + + IGRAPH_HANDLE_EXCEPTIONS_END; +} diff --git a/src/misc/embedding.c b/src/misc/embedding.c new file mode 100644 index 0000000..3c4a314 --- /dev/null +++ b/src/misc/embedding.c @@ -0,0 +1,1201 @@ +/* -*- 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_interface.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "core/math.h" + +#include + +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 igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] += from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return IGRAPH_SUCCESS; +} + +/* Adjacency matrix, weighted, undirected. + Eigendecomposition is used. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/* Adjacency matrix, unweighted, directed. SVD. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t nei = 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] += VECTOR(*tmp)[nei]; + } + to[i] += VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; + } + + return IGRAPH_SUCCESS; +} + +/* Adjacency matrix, unweighted, directed. SVD, right eigenvectors */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] += from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return IGRAPH_SUCCESS; +} + +/* Adjacency matrix, weighted, directed. SVD. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/* Adjacency matrix, weighted, directed. SVD, right eigenvectors. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/* Laplacian D-A, unweighted, undirected. Eigendecomposition. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] -= from[nei]; + } + to[i] += VECTOR(*cvec)[i] * from[i]; + } + + return IGRAPH_SUCCESS; +} + +/* Laplacian D-A, weighted, undirected. Eigendecomposition. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/* Laplacian DAD, unweighted, undirected. Eigendecomposition. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t nei = 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/* Laplacian I-DAD, unweighted, undirected. Eigendecomposition. */ +static igraph_error_t igraph_i_lsembedding_idad(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_lsembedding_dad(to, from, n, extra); + for (int i = 0; i < n; i++) { + to[i] = from[i] - to[i]; + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_lsembedding_idadw(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + igraph_i_lsembedding_dadw(to, from, n, extra); + for (int i = 0; i < n; i++) { + to[i] = from[i] - to[i]; + } + + return IGRAPH_SUCCESS; +} + +/* Laplacian OAP, unweighted, directed. SVD. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t 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++) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/* Laplacian OAP, unweighted, directed. SVD, right eigenvectors. */ +static igraph_error_t 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; + igraph_integer_t 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++) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/* Laplacian OAP, weighted, directed. SVD. */ +static igraph_error_t 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; + igraph_integer_t i, j, nlen; + igraph_integer_t edge, nei; + igraph_real_t w; + + /* 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++) { + edge = VECTOR(*neis)[j]; + nei = IGRAPH_OTHER(graph, edge, i); + 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++) { + edge = VECTOR(*neis)[j]; + nei = IGRAPH_OTHER(graph, edge, i); + 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 IGRAPH_SUCCESS; +} + +/* Laplacian OAP, weighted, directed. SVD, right eigenvectors. */ +static igraph_error_t 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; + igraph_integer_t i, j, nlen; + igraph_integer_t edge, nei; + igraph_real_t w; + + /* 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++) { + edge = VECTOR(*neis)[j]; + nei = IGRAPH_OTHER(graph, edge, i); + 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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); + } + + if (vc > INT_MAX) { + IGRAPH_ERROR("Graph too large for ARPACK", IGRAPH_EOVERFLOW); + } + + if (no > INT_MAX) { + IGRAPH_ERROR("Too many eigenvectors requested from ARPACK", IGRAPH_EOVERFLOW); + } + + 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 IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&tmp, vc); + 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 = (int) vc; + options->start = 1; /* no random start vector */ + options->nev = (int) 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 = options->nev + 3; + if (options->ncv > options->n) { + options->ncv = options->n; + } + + /* We provide a random start vector to ARPACK on our own to ensure that + * we use igraph's RNG and not the one from ARPACK (which relies on LAPACK) */ + RNG_BEGIN(); + for (i = 0; i < vc; i++) { + MATRIX(*X, i, 0) = RNG_UNIF(-1, 1); + } + RNG_END(); + + 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), (int) 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 IGRAPH_SUCCESS; +} + +/** + * \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. Supply \c NULL to use the defaults. 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. + * + */ + +igraph_error_t 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; + } + + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + + 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 igraph_error_t 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_CHECK(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: { + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t 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_CHECK(igraph_strength(graph, °_in, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1, weights)); + IGRAPH_CHECK(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 IGRAPH_SUCCESS; +} + +/** + * \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. Supply \c NULL to use the defaults. 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. + */ + +igraph_error_t 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 (options == 0) { + options = igraph_arpack_options_get_default(); + } + + 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 + * \brief 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(). + */ + +igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { + + igraph_integer_t 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 IGRAPH_SUCCESS; + } + + 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++) { + igraph_integer_t 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 IGRAPH_SUCCESS; +} diff --git a/src/misc/feedback_arc_set.c b/src/misc/feedback_arc_set.c new file mode 100644 index 0000000..9a34b48 --- /dev/null +++ b/src/misc/feedback_arc_set.c @@ -0,0 +1,664 @@ +/* -*- 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_structural.h" +#include "misc/feedback_arc_set.h" + +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_vector_list.h" +#include "igraph_visitor.h" + +#include "internal/glpk_support.h" +#include "math/safe_intop.h" + +#include + +/** + * \ingroup structural + * \function igraph_feedback_arc_set + * \brief Feedback arc set of a graph using exact or heuristic methods. + * + * 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. + */ +igraph_error_t igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_int_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. + */ +igraph_error_t igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_vector_int_t *layering) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t edges; + + IGRAPH_VECTOR_INT_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_init_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_int_sort(&edges); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, -1)); /* guard element */ + + if (result) { + igraph_vector_int_clear(result); + for (igraph_integer_t i = 0, j = 0; i < no_of_edges; i++) { + if (i == VECTOR(edges)[j]) { + j++; + continue; + } + IGRAPH_CHECK(igraph_vector_int_push_back(result, i)); + } + } + + if (layering) { + igraph_vector_t degrees; + igraph_vector_int_t roots; + + IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&roots, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °rees, igraph_vss_all(), + IGRAPH_ALL, /* loops */ false, weights)); + IGRAPH_CHECK(igraph_vector_qsort_ind(°rees, &roots, IGRAPH_DESCENDING)); + + IGRAPH_CHECK(igraph_bfs(graph, + /* root = */ 0, + /* roots = */ &roots, + /* mode = */ IGRAPH_OUT, + /* unreachable = */ 0, + /* restricted = */ 0, + /* order = */ 0, + /* rank = */ 0, + /* parents = */ 0, + /* pred = */ 0, + /* succ = */ 0, + /* dist = */ layering, + /* callback = */ 0, + /* extra = */ 0)); + + igraph_vector_destroy(°rees); + igraph_vector_int_destroy(&roots); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Solves the feedback arc set problem using the heuristics of Eades et al. + */ +igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_vector_int_t *layers) { + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, k, v, eid, nodes_left; + igraph_dqueue_int_t sources, sinks; + igraph_vector_int_t neis; + igraph_vector_int_t indegrees, outdegrees; + igraph_vector_t instrengths, outstrengths; + igraph_integer_t *ordering; + igraph_integer_t order_next_pos = 0, order_next_neg = -1; + igraph_real_t diff, maxdiff; + + ordering = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(ordering, "Insufficient memory for finding feedback arc set."); + IGRAPH_FINALLY(igraph_free, ordering); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&indegrees, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdegrees, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&instrengths, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&outstrengths, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&sources, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &sources); + IGRAPH_CHECK(igraph_dqueue_int_init(&sinks, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &sinks); + + IGRAPH_CHECK(igraph_degree(graph, &indegrees, igraph_vss_all(), IGRAPH_IN, false)); + IGRAPH_CHECK(igraph_degree(graph, &outdegrees, igraph_vss_all(), IGRAPH_OUT, false)); + + if (weights) { + IGRAPH_CHECK(igraph_strength(graph, &instrengths, igraph_vss_all(), IGRAPH_IN, false, weights)); + IGRAPH_CHECK(igraph_strength(graph, &outstrengths, igraph_vss_all(), IGRAPH_OUT, false, weights)); + } else { + IGRAPH_CHECK(igraph_vector_resize(&instrengths, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(&outstrengths, no_of_nodes)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + VECTOR(instrengths)[i] = VECTOR(indegrees)[i]; + VECTOR(outstrengths)[i] = VECTOR(outdegrees)[i]; + } + } + + /* 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_CHECK(igraph_dqueue_int_push(&sources, i)); + } + } else if (VECTOR(outdegrees)[i] == 0) { + /* This is a sink */ + IGRAPH_CHECK(igraph_dqueue_int_push(&sinks, i)); + } + } + + /* While we have any nodes left... */ + while (nodes_left > 0) { + /* (1) Remove the sources one by one */ + while (!igraph_dqueue_int_empty(&sources)) { + i = igraph_dqueue_int_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, i, + IGRAPH_OUT)); + j = igraph_vector_int_size(&neis); + for (i = 0; i < j; i++) { + eid = 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_int_push(&sources, k)); + } + } + nodes_left--; + } + + /* (2) Remove the sinks one by one */ + while (!igraph_dqueue_int_empty(&sinks)) { + i = igraph_dqueue_int_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, i, + IGRAPH_IN)); + j = igraph_vector_int_size(&neis); + for (i = 0; i < j; i++) { + eid = 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_int_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, v, + IGRAPH_OUT)); + j = igraph_vector_int_size(&neis); + for (i = 0; i < j; i++) { + eid = 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_int_push(&sources, k)); + } + } + /* Remove incoming edges */ + IGRAPH_CHECK(igraph_incident(graph, &neis, v, + IGRAPH_IN)); + j = igraph_vector_int_size(&neis); + for (i = 0; i < j; i++) { + eid = 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_int_push(&sinks, k)); + } + } + + VECTOR(outdegrees)[v] = -1; + VECTOR(indegrees)[v] = -1; + nodes_left--; + } + } + + igraph_dqueue_int_destroy(&sinks); + igraph_dqueue_int_destroy(&sources); + igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&outstrengths); + igraph_vector_destroy(&instrengths); + igraph_vector_int_destroy(&outdegrees); + igraph_vector_int_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) { + igraph_vector_int_clear(result); + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i), to = IGRAPH_TO(graph, i); + if (from == to || ordering[from] > ordering[to]) { + IGRAPH_CHECK(igraph_vector_int_push_back(result, i)); + } + } + } + + /* If we have also requested a layering, return that as well */ + if (layers) { + igraph_vector_int_t ranks; + igraph_vector_int_t order_vec; + + IGRAPH_CHECK(igraph_vector_int_resize(layers, no_of_nodes)); + igraph_vector_int_null(layers); + + igraph_vector_int_view(&order_vec, ordering, no_of_nodes); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ranks, 0); + + IGRAPH_CHECK(igraph_vector_int_qsort_ind(&order_vec, &ranks, IGRAPH_ASCENDING)); + + for (i = 0; i < no_of_nodes; i++) { + igraph_integer_t from = VECTOR(ranks)[i]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, IGRAPH_OUT)); + k = igraph_vector_int_size(&neis); + for (j = 0; j < k; j++) { + igraph_integer_t to = 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_int_destroy(&neis); + igraph_vector_int_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. + */ +igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_int_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_int_t membership, *vec; + igraph_vector_int_t ordering, vertex_remapping; + igraph_vector_int_list_t vertices_by_components, edges_by_components; + igraph_integer_t i, j, k, l, m, n, from, to, no_of_rows, n_choose_2; + igraph_real_t weight; + glp_prob *ip; + glp_iocp parm; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ordering, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_remapping, no_of_vertices); + + igraph_vector_int_clear(result); + + /* Decompose the graph into connected components */ + IGRAPH_CHECK(igraph_connected_components(graph, &membership, 0, &no_of_components, IGRAPH_WEAK)); + + /* Construct vertex and edge lists for each of the components */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vertices_by_components, no_of_components); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edges_by_components, no_of_components); + for (i = 0; i < no_of_vertices; i++) { + j = VECTOR(membership)[i]; + vec = igraph_vector_int_list_get_ptr(&vertices_by_components, j); + IGRAPH_CHECK(igraph_vector_int_push_back(vec, i)); + } + for (i = 0; i < no_of_edges; i++) { + j = VECTOR(membership)[IGRAPH_FROM(graph, i)]; + vec = igraph_vector_int_list_get_ptr(&edges_by_components, j); + IGRAPH_CHECK(igraph_vector_int_push_back(vec, 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_int_t *vertices_in_comp = igraph_vector_int_list_get_ptr(&vertices_by_components, i); + igraph_vector_int_t *edges_in_comp = igraph_vector_int_list_get_ptr(&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 INT_MAX) { + IGRAPH_ERROR("Feedback arc set problem too large for GLPK.", IGRAPH_EOVERFLOW); + } + + if (n_choose_2 > 0) { + glp_add_cols(ip, (int) n_choose_2); + for (j = 1; j <= n_choose_2; j++) { + glp_set_col_kind(ip, (int) j, GLP_BV); + } + } + + /* Set up coefficients in the goal function */ + k = igraph_vector_int_size(edges_in_comp); + for (j = 0; j < k; j++) { + l = VECTOR(*edges_in_comp)[j]; + from = VECTOR(vertex_remapping)[IGRAPH_FROM(graph, l)]; + to = VECTOR(vertex_remapping)[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) { + { + /* Overflow-safe block for: + * no_of_rows = n * (n - 1) / 2 + n * (n - 1) * (n - 2) / 3 + */ + + /* res = n * (n - 1) * (n - 2) / 3 */ + igraph_integer_t mod = n % 3; + igraph_integer_t res = n / 3; /* same as (n - mod) / 3 */ + + mod = (mod + 1) % 3; + IGRAPH_SAFE_MULT(res, n - mod, &res); + mod = (mod + 1) % 3; + IGRAPH_SAFE_MULT(res, n - mod, &res); + + /* no_of_rows = n * (n - 1) / 2 + res */ + IGRAPH_SAFE_ADD(n_choose_2, res, &no_of_rows); + } + if (no_of_rows > INT_MAX) { + IGRAPH_ERROR("Feedback arc set problem too large for GLPK.", IGRAPH_EOVERFLOW); + } + glp_add_rows(ip, (int) no_of_rows); + 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_int_resize(&ordering, n)); + igraph_vector_int_null(&ordering); + j = 0; k = 1; + for (l = 1; l <= n_choose_2; 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_int_size(edges_in_comp); + for (j = 0; j < k; j++) { + l = VECTOR(*edges_in_comp)[j]; + from = VECTOR(vertex_remapping)[IGRAPH_FROM(graph, l)]; + to = VECTOR(vertex_remapping)[IGRAPH_TO(graph, l)]; + if (from == to || VECTOR(ordering)[from] < VECTOR(ordering)[to]) { + IGRAPH_CHECK(igraph_vector_int_push_back(result, l)); + } + } + + /* Clean up */ + glp_delete_prob(ip); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_list_destroy(&vertices_by_components); + igraph_vector_int_list_destroy(&edges_by_components); + igraph_vector_int_destroy(&vertex_remapping); + igraph_vector_int_destroy(&ordering); + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +#endif +} diff --git a/src/misc/feedback_arc_set.h b/src/misc/feedback_arc_set.h new file mode 100644 index 0000000..7d34eb0 --- /dev/null +++ b/src/misc/feedback_arc_set.h @@ -0,0 +1,46 @@ +/* -*- 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 + +igraph_error_t igraph_i_feedback_arc_set_eades( + const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_vector_int_t *layering +); +igraph_error_t igraph_i_feedback_arc_set_ip( + const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights); +igraph_error_t igraph_i_feedback_arc_set_undirected( + const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_vector_int_t *layering +); + +__END_DECLS + +#endif diff --git a/src/misc/graphicality.c b/src/misc/graphicality.c new file mode 100644 index 0000000..ba01d01 --- /dev/null +++ b/src/misc/graphicality.c @@ -0,0 +1,910 @@ +/* -*- 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" + +#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 igraph_error_t igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_int_t *degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_int_t *degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res); + +static igraph_error_t igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); + +static igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res); +static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t *degrees1, const igraph_vector_int_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), where n is the length of the degree sequence(s). + */ +igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, + const igraph_vector_int_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 { + /* Remaining 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 (igraph_vector_int_size(in_degrees) != igraph_vector_int_size(out_degrees)) { + IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); + } + + 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 { + /* Remaining 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), where n is the length of the larger degree sequence. + */ +igraph_error_t igraph_is_bigraphical(const igraph_vector_int_t *degrees1, + const igraph_vector_int_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 igraph_error_t igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_int_t *degrees, igraph_bool_t *res) { + igraph_integer_t sum_parity = 0; /* 0 if the degree sum is even, 1 if it is odd */ + igraph_integer_t n = igraph_vector_int_size(degrees); + igraph_integer_t i; + + for (i = 0; i < n; ++i) { + igraph_integer_t d = VECTOR(*degrees)[i]; + + if (d < 0) { + *res = false; + 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 igraph_error_t igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_int_t *degrees, igraph_bool_t *res) { + igraph_integer_t i; + igraph_integer_t n = igraph_vector_int_size(degrees); + igraph_integer_t dsum, dmax; + + /* Zero-length sequences are considered graphical. */ + if (n == 0) { + *res = true; + return IGRAPH_SUCCESS; + } + + dsum = 0; dmax = 0; + for (i = 0; i < n; ++i) { + igraph_integer_t d = VECTOR(*degrees)[i]; + + if (d < 0) { + *res = false; + 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 igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res) { + igraph_vector_int_t num_degs; + igraph_integer_t w, b, s, c, n, k, wd, kd; + + n = igraph_vector_int_size(degrees); + + /* Zero-length sequences are considered graphical. */ + if (n == 0) { + *res = true; + 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_VECTOR_INT_INIT_FINALLY(&num_degs, n+2); + + for (igraph_integer_t i = 0; i < n; ++i) { + igraph_integer_t degree = VECTOR(*degrees)[i]; + + /* Negative degrees are already checked in igraph_i_is_graphical_undirected_multi_loops() */ + if (degree > n+1) { + *res = false; + goto undirected_loopy_simple_finish; + } + + ++VECTOR(num_degs)[degree]; + } + + /* Convert num_degs to a cumulative sum array. */ + for (igraph_integer_t d = n; d >= 0; --d) { + VECTOR(num_degs)[d] += VECTOR(num_degs)[d+1]; + } + + wd = 0, kd = n+1; + *res = true; + w = n - 1; b = 0; s = 0; c = 0; + for (k = 0; k < n; k++) { + while (k >= VECTOR(num_degs)[kd]) { + --kd; + } + b += kd; + c += w; + while (w > k) { + while (wd + 1 <= n + 1 && w < VECTOR(num_degs)[wd + 1]) { + wd++; + } + if (wd > k + 1) break; + s += wd; + c -= (k + 1); + w--; + } + if (b > c + s + 2*(k + 1)) { + *res = false; + break; + } + if (w == k) { + break; + } + } + +undirected_loopy_simple_finish: + igraph_vector_int_destroy(&num_degs); + 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 igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res) { + igraph_vector_int_t num_degs; /* num_degs[d] is the # of vertices with degree d */ + const igraph_integer_t p = igraph_vector_int_size(degrees); + igraph_integer_t dmin, dmax, dsum; + igraph_integer_t n; /* number of non-zero degrees */ + igraph_integer_t k, sum_deg, sum_ni, sum_ini; + igraph_integer_t i, dk; + igraph_integer_t zverovich_bound; + + if (p == 0) { + *res = true; + 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) { + igraph_integer_t d = VECTOR(*degrees)[i]; + + if (d < 0 || d >= p) { + *res = false; + 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 = false; + goto finish; + } + + if (n == 0) { + *res = true; + 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 = true; + goto finish; + } + + k = 0; sum_deg = 0; sum_ni = 0; sum_ini = 0; + for (dk = dmax; dk >= dmin; --dk) { + igraph_integer_t run_size, v; + + if (dk < k+1) { + *res = true; + 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 = false; + goto finish; + } + } + } + + *res = true; + +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 igraph_error_t igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { + igraph_integer_t sumdiff; /* difference between sum of in- and out-degrees */ + igraph_integer_t n = igraph_vector_int_size(out_degrees); + igraph_integer_t i; + + IGRAPH_ASSERT(igraph_vector_int_size(in_degrees) == n); + + sumdiff = 0; + for (i = 0; i < n; ++i) { + igraph_integer_t dout = VECTOR(*out_degrees)[i]; + igraph_integer_t din = VECTOR(*in_degrees)[i]; + + if (dout < 0 || din < 0) { + *res = false; + 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 igraph_error_t igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { + igraph_integer_t i, sumin, sumout, dmax; + igraph_integer_t n = igraph_vector_int_size(out_degrees); + + IGRAPH_ASSERT(igraph_vector_int_size(in_degrees) == n); + + sumin = 0; sumout = 0; + dmax = 0; + for (i = 0; i < n; ++i) { + igraph_integer_t dout = VECTOR(*out_degrees)[i]; + igraph_integer_t din = VECTOR(*in_degrees)[i]; + igraph_integer_t d = dout + din; + + if (dout < 0 || din < 0) { + *res = false; + 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 igraph_error_t igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { + 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 + */ +static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { + igraph_vector_int_t in_degree_cumcounts, in_degree_counts; + igraph_vector_int_t sorted_in_degrees, sorted_out_degrees; + igraph_vector_int_t left_pq, right_pq; + igraph_integer_t lhs, rhs, left_pq_size, right_pq_size, left_i, right_i, left_sum, right_sum; + + /* 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; + } + + const igraph_integer_t vcount = igraph_vector_int_size(out_degrees); + if (vcount == 0) { + *res = true; + return IGRAPH_SUCCESS; + } + + + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_degree_cumcounts, vcount+1); + + /* Compute in_degree_cumcounts[d+1] to be the no. of in-degrees == d */ + for (igraph_integer_t v = 0; v < vcount; v++) { + igraph_integer_t indeg = VECTOR(*in_degrees)[v]; + igraph_integer_t outdeg = VECTOR(*out_degrees)[v]; + if (indeg >= vcount || outdeg >= vcount) { + *res = false; + igraph_vector_int_destroy(&in_degree_cumcounts); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + } + VECTOR(in_degree_cumcounts)[indeg + 1]++; + } + + /* Compute in_degree_cumcounts[d] to be the no. of in-degrees < d */ + for (igraph_integer_t indeg = 0; indeg < vcount; indeg++) { + VECTOR(in_degree_cumcounts)[indeg+1] += VECTOR(in_degree_cumcounts)[indeg]; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&sorted_out_degrees, vcount); + IGRAPH_VECTOR_INT_INIT_FINALLY(&sorted_in_degrees, vcount); + + /* In the following loop, in_degree_counts[d] keeps track of the number of vertices + * with in-degree d that were already placed. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_degree_counts, vcount); + + for (igraph_integer_t v = 0; v < vcount; v++) { + igraph_integer_t outdeg = VECTOR(*out_degrees)[v]; + igraph_integer_t indeg = VECTOR(*in_degrees)[v]; + igraph_integer_t idx = VECTOR(in_degree_cumcounts)[indeg] + VECTOR(in_degree_counts)[indeg]; + VECTOR(sorted_out_degrees)[vcount - idx - 1] = outdeg; + VECTOR(sorted_in_degrees)[vcount - idx - 1] = indeg; + VECTOR(in_degree_counts)[indeg]++; + } + + igraph_vector_int_destroy(&in_degree_counts); + igraph_vector_int_destroy(&in_degree_cumcounts); + IGRAPH_FINALLY_CLEAN(2); + + /* 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(sorted_in_degrees)[x]) +#define OUTDEGREE(x) (VECTOR(sorted_out_degrees)[x]) + + IGRAPH_VECTOR_INT_INIT_FINALLY(&left_pq, vcount); + IGRAPH_VECTOR_INT_INIT_FINALLY(&right_pq, vcount); + + left_pq_size = 0; + right_pq_size = vcount; + left_i = 0; + right_i = 0; + left_sum = 0; + right_sum = 0; + for (igraph_integer_t i = 0; i < vcount; i++) { + VECTOR(right_pq)[OUTDEGREE(i)]++; + } + + *res = true; + lhs = 0; + rhs = 0; + for (igraph_integer_t 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. However, this does not + * provide any noticeable benefits for the current implementation. */ + + if (OUTDEGREE(i) < i) { + left_sum += OUTDEGREE(i); + } + else { + VECTOR(left_pq)[OUTDEGREE(i)]++; + left_pq_size++; + } + while (left_i < i) { + while (VECTOR(left_pq)[left_i] > 0) { + VECTOR(left_pq)[left_i]--; + left_pq_size--; + left_sum += left_i; + } + left_i++; + } + + while (right_i < i + 1) { + while (VECTOR(right_pq)[right_i] > 0) { + VECTOR(right_pq)[right_i]--; + right_pq_size--; + right_sum += right_i; + } + right_i++; + } + if (OUTDEGREE(i) < i + 1) { + right_sum -= OUTDEGREE(i); + } + else { + VECTOR(right_pq)[OUTDEGREE(i)]--; + right_pq_size--; + } + + rhs = left_sum + i * left_pq_size + right_sum + (i + 1) * right_pq_size; + if (lhs > rhs) { + *res = false; + break; + } + } + +#undef INDEGREE +#undef OUTDEGREE + + igraph_vector_int_destroy(&sorted_in_degrees); + igraph_vector_int_destroy(&sorted_out_degrees); + igraph_vector_int_destroy(&left_pq); + igraph_vector_int_destroy(&right_pq); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + + +/***** Bipartite case *****/ + +/* Bipartite graph with multi-edges: + * - Degrees must be non-negative. + * - Sum of degrees must be the same in the two partitions. + */ +static igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res) { + igraph_integer_t i; + igraph_integer_t sum1, sum2; + igraph_integer_t n1 = igraph_vector_int_size(degrees1), n2 = igraph_vector_int_size(degrees2); + + sum1 = 0; + for (i = 0; i < n1; ++i) { + igraph_integer_t d = VECTOR(*degrees1)[i]; + + if (d < 0) { + *res = false; + return IGRAPH_SUCCESS; + } + + sum1 += d; + } + + sum2 = 0; + for (i = 0; i < n2; ++i) { + igraph_integer_t d = VECTOR(*degrees2)[i]; + + if (d < 0) { + *res = false; + 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 igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res) { + igraph_integer_t n1 = igraph_vector_int_size(degrees1), n2 = igraph_vector_int_size(degrees2); + igraph_vector_int_t deg_freq1, deg_freq2; + igraph_integer_t lhs_sum, partial_rhs_sum, partial_rhs_count; + igraph_integer_t a, b, k; + + if (n1 == 0 && n2 == 0) { + *res = true; + 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_int_t *tmp; + igraph_integer_t n; + + tmp = degrees1; + degrees1 = degrees2; + degrees2 = tmp; + + n = n1; + n1 = n2; + n2 = n; + } + + /* Counting sort the degrees. */ + /* Since the graph is simple, a vertex's degree can be at most the size of the other partition. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(°_freq1, n2+1); + IGRAPH_VECTOR_INT_INIT_FINALLY(°_freq2, n1+1); + + for (igraph_integer_t i = 0; i < n1; ++i) { + igraph_integer_t degree = VECTOR(*degrees1)[i]; + if (degree > n2) { + *res = false; + goto bigraphical_simple_done; + } + ++VECTOR(deg_freq1)[degree]; + } + for (igraph_integer_t i = 0; i < n2; ++i) { + igraph_integer_t degree = VECTOR(*degrees2)[i]; + if (degree > n1) { + *res = false; + goto bigraphical_simple_done; + } + ++VECTOR(deg_freq2)[degree]; + } + + /* + * 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). + * https://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. + * + * Additionally, 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). + */ + + /* 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 = true; /* be optimistic */ + lhs_sum = 0; + partial_rhs_sum = 0; /* the sum of those elements in the rhs which are <= (k+1) */ + partial_rhs_count = 0; /* number of elements in the rhs which are <= (k+1) */ + b = 0; /* index in deg_freq2 */ + k = -1; + for (a = n2; a >= 0; --a) { + igraph_integer_t acount = VECTOR(deg_freq1)[a]; + lhs_sum += a * acount; + k += acount; + + while (b <= k + 1) { + igraph_integer_t bcount = VECTOR(deg_freq2)[b]; + partial_rhs_sum += b * bcount; + partial_rhs_count += bcount; + + ++b; + } + + /* rhs_sum for a given k is partial_rhs_sum + (n2 - partial_rhs_count) * (k+1) */ + if (lhs_sum > partial_rhs_sum + (n2 - partial_rhs_count) * (k+1) ) { + *res = false; + break; + } + } + +bigraphical_simple_done: + igraph_vector_int_destroy(°_freq1); + igraph_vector_int_destroy(°_freq2); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/misc/matching.c b/src/misc/matching.c new file mode 100644 index 0000000..f030d6b --- /dev/null +++ b/src/misc/matching.c @@ -0,0 +1,1023 @@ +/* -*- 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 + */ +igraph_error_t igraph_is_matching(const igraph_t *graph, + const igraph_vector_bool_t *types, const igraph_vector_int_t *matching, + igraph_bool_t *result) { + igraph_integer_t i, j, no_of_nodes = igraph_vcount(graph); + igraph_bool_t conn; + + /* Checking match vector length */ + if (igraph_vector_int_size(matching) != no_of_nodes) { + *result = false; 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 = false; return IGRAPH_SUCCESS; + } + /* When i is unmatched, we're done */ + if (j == -1) { + continue; + } + /* Matches must be mutual */ + if (VECTOR(*matching)[j] != i) { + *result = false; return IGRAPH_SUCCESS; + } + /* Matched vertices must be connected */ + IGRAPH_CHECK(igraph_are_adjacent(graph, i, + j, &conn)); + if (!conn) { + /* Try the other direction -- for directed graphs */ + IGRAPH_CHECK(igraph_are_adjacent(graph, j, + i, &conn)); + if (!conn) { + *result = false; 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 = false; return IGRAPH_SUCCESS; + } + } + } + + *result = true; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_maximal_matching + * \brief 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 + */ +igraph_error_t igraph_is_maximal_matching(const igraph_t *graph, + const igraph_vector_bool_t *types, const igraph_vector_int_t *matching, + igraph_bool_t *result) { + + igraph_integer_t i, j, n, no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis; + igraph_bool_t valid; + + IGRAPH_CHECK(igraph_is_matching(graph, types, matching, &valid)); + if (!valid) { + *result = false; return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_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, i, + IGRAPH_ALL)); + n = igraph_vector_int_size(&neis); + for (j = 0; j < n; j++) { + if (VECTOR(*matching)[VECTOR(neis)[j]] == -1) { + if (types == 0 || + VECTOR(*types)[i] != VECTOR(*types)[VECTOR(neis)[j]]) { + valid = 0; break; + } + } + } + } + + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + + *result = valid; + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( + const igraph_t *graph, + const igraph_vector_bool_t *types, igraph_integer_t *matching_size, + igraph_vector_int_t *matching); + +static igraph_error_t 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_int_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 + */ +igraph_error_t 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_int_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 igraph_error_t igraph_i_maximum_bipartite_matching_unweighted_relabel( + const igraph_t* graph, + const igraph_vector_bool_t* types, igraph_vector_int_t* labels, + igraph_vector_int_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 igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( + const igraph_t *graph, + const igraph_vector_bool_t *types, igraph_integer_t *matching_size, + igraph_vector_int_t *matching) { + igraph_integer_t i, j, k, n, no_of_nodes = igraph_vcount(graph); + igraph_integer_t num_matched; /* number of matched vertex pairs */ + igraph_vector_int_t match; /* will store the matching */ + igraph_vector_int_t labels; /* will store the labels */ + igraph_vector_int_t neis; /* used to retrieve the neighbors of a node */ + igraph_dqueue_int_t q; /* a FIFO for push ordering */ + igraph_bool_t smaller_set; /* denotes which part of the bipartite graph is smaller */ + igraph_integer_t label_changed = 0; /* Counter to decide when to run a global relabeling */ + igraph_integer_t 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_int_init(&match, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &match); + IGRAPH_VECTOR_INT_INIT_FINALLY(&labels, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + + /* (2) Initially, every node is unmatched */ + igraph_vector_int_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, i, + IGRAPH_ALL)); + n = igraph_vector_int_size(&neis); + for (j = 0; j < n; j++) { + k = 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_int_push(&q, i)); + } + } + + /* (6) Main loop from the referenced tech report -- lines 4--13 */ + label_changed = 0; + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t v = igraph_dqueue_int_pop(&q); /* Line 13 */ + igraph_integer_t u = -1, label_u = 2 * no_of_nodes; + igraph_integer_t 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, v, + IGRAPH_ALL)); + n = igraph_vector_int_size(&neis); + for (i = 0; i < n; i++) { + if (VECTOR(labels)[VECTOR(neis)[i]] < label_u) { + u = VECTOR(neis)[i]; + label_u = 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_int_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_int_update(matching, &match)); + } + if (matching_size != 0) { + *matching_size = num_matched; + } + + /* Release everything */ + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&labels); + igraph_vector_int_destroy(&match); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted_relabel( + const igraph_t *graph, + const igraph_vector_bool_t *types, igraph_vector_int_t *labels, + igraph_vector_int_t *match, igraph_bool_t smaller_set) { + igraph_integer_t i, j, n, no_of_nodes = igraph_vcount(graph), matched_to; + igraph_dqueue_int_t q; + igraph_vector_int_t neis; + + debug("Running global relabeling.\n"); + + /* Set all the labels to no_of_nodes first */ + igraph_vector_int_fill(labels, no_of_nodes); + + /* Allocate vector for neighbors */ + IGRAPH_VECTOR_INT_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_int_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*types)[i] != smaller_set && VECTOR(*match)[i] == -1) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + VECTOR(*labels)[i] = 0; + } + } + + /* Run the BFS */ + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t v = igraph_dqueue_int_pop(&q); + igraph_integer_t w; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, + IGRAPH_ALL)); + + n = igraph_vector_int_size(&neis); + for (j = 0; j < n; j++) { + w = 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_int_push(&q, matched_to)); + VECTOR(*labels)[matched_to] = VECTOR(*labels)[w] + 1; + } + } + } + } + + igraph_dqueue_int_destroy(&q); + igraph_vector_int_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 igraph_error_t 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_int_t *matching, + const igraph_vector_t *weights, igraph_real_t eps) { + igraph_integer_t i, j, k, n, no_of_nodes, no_of_edges; + igraph_integer_t u, v, w, msize; + igraph_t newgraph; + igraph_vector_int_t match; /* will store the matching */ + igraph_vector_t slack; /* will store the slack on each edge */ + igraph_vector_int_t parent; /* parent vertices during a BFS */ + igraph_vector_int_t vec1, vec2; /* general temporary vectors */ + igraph_vector_t labels; /* will store the labels */ + igraph_dqueue_int_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 */ + igraph_integer_t smaller_set_size; /* size of the smaller set */ + igraph_integer_t 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_int_init(&match, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &match); + IGRAPH_CHECK(igraph_vector_init(&slack, no_of_edges)); + IGRAPH_FINALLY(igraph_vector_destroy, &slack); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec2, 0); + IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes); + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + IGRAPH_VECTOR_INT_INIT_FINALLY(&parent, no_of_nodes); + IGRAPH_CHECK(igraph_adjlist_init_empty(&tight_phantom_edges, + 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 = 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_int_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 = VECTOR(vec2)[j]; + v = VECTOR(vec2)[j + 1]; + VECTOR(slack)[i] = VECTOR(labels)[u] + VECTOR(labels)[v] - VECTOR(*weights)[i]; + if (IS_TIGHT(i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, u)); + IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, v)); + } + } + igraph_vector_int_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, + 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_int_clear(&vec1); + igraph_vector_int_clear(&vec2); + igraph_vector_int_fill(&parent, -1); + for (j = 0; j < smaller_set_size; j++) { + i = VECTOR(smaller_set)[j]; + if (UNMATCHED(i)) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + VECTOR(parent)[i] = i; + IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, i)); + } + } + +#ifdef MATCHING_DEBUG + debug("Matching:"); + igraph_vector_int_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_int_empty(&q)) { + v = igraph_dqueue_int_pop(&q); + + debug("Considering vertex %ld\n", 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 = 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 %" IGRAPH_PRId " via edge %" IGRAPH_PRId "\n", u, j); + VECTOR(parent)[u] = v; + IGRAPH_CHECK(igraph_vector_int_push_back(&vec2, u)); + w = 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_int_push(&q, w)); + VECTOR(parent)[w] = u; + } + IGRAPH_CHECK(igraph_vector_int_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 = 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)[v] + VECTOR(labels)[u]) > eps) { + continue; + } + debug(" Reached vertex %" IGRAPH_PRId " via tight phantom edge\n", u); + VECTOR(parent)[u] = v; + IGRAPH_CHECK(igraph_vector_int_push_back(&vec2, u)); + w = 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_int_push(&q, w)); + VECTOR(parent)[w] = u; + } + IGRAPH_CHECK(igraph_vector_int_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 = VECTOR(parent)[v]; + debug("Extending matching with alternating path ending in %ld.\n", v); + + while (u != v) { + w = VECTOR(match)[v]; + if (w != -1) { + VECTOR(match)[w] = -1; + } + VECTOR(match)[v] = u; + + VECTOR(match)[v] = u; + w = VECTOR(match)[u]; + if (w != -1) { + VECTOR(match)[w] = -1; + } + VECTOR(match)[u] = v; + + v = VECTOR(parent)[u]; + u = VECTOR(parent)[v]; + } + + msize++; + +#ifdef MATCHING_DEBUG + debug("New matching after update:"); + igraph_vector_int_print(&match); + debug("Matching size is now: %" IGRAPH_PRId "\n", msize); +#endif + continue; + } + +#ifdef MATCHING_DEBUG + debug("Vertices reachable from unmatched ones via tight edges:\n"); + igraph_vector_int_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_int_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 = i; + } + } + min_slack_2 = IGRAPH_INFINITY; + for (i = 0; i < n; i++) { + u = 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, min_slack_u, min_slack_v); + + n = igraph_vector_int_size(&vec1); + for (i = 0; i < n; i++) { + u = 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 %" IGRAPH_PRId "\n", 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 %" IGRAPH_PRId " -- %" IGRAPH_PRId " (ID=%" IGRAPH_PRId ")\n", u, v, VECTOR(*neis)[j]); + if (VECTOR(parent)[v] >= 0) { + /* v was reached, so we are not interested in it */ + debug(" %" IGRAPH_PRId " was reached, so we are not interested in it\n", v); + continue; + } + /* v is the ID of the edge from now on */ + v = 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 %" IGRAPH_PRId "--%" IGRAPH_PRId "\n", min_slack, min_slack_u, 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_int_size(&vec1); + for (i = 0; i < n; i++) { + u = 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 %" IGRAPH_PRId " (%" IGRAPH_PRId "--%" IGRAPH_PRId ") by %.4f\n", + VECTOR(*neis)[j], u, + IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); + VECTOR(slack)[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_int_size(&vec2); + for (i = 0; i < n; i++) { + u = 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 %" IGRAPH_PRId " (%" IGRAPH_PRId"--%" IGRAPH_PRId ") by %.4f\n", + VECTOR(*neis)[j], u, + IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); + VECTOR(slack)[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)[u] + VECTOR(labels)[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: %" IGRAPH_PRId " -- %" IGRAPH_PRId "\n", u, 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_int_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, 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_int_destroy(&parent); + igraph_dqueue_int_destroy(&q); + igraph_vector_destroy(&labels); + igraph_vector_int_destroy(&vec1); + igraph_vector_int_destroy(&vec2); + igraph_vector_destroy(&slack); + igraph_vector_int_destroy(&match); + IGRAPH_FINALLY_CLEAN(11); + + return IGRAPH_SUCCESS; +} + +#ifdef MATCHING_DEBUG + #undef MATCHING_DEBUG +#endif diff --git a/src/misc/microscopic_update.c b/src/misc/microscopic_update.c new file mode 100644 index 0000000..9802d61 --- /dev/null +++ b/src/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 igraph_error_t 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 */ + igraph_integer_t 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_EIT_GET(A); + S += VECTOR(*U)[e]; + IGRAPH_EIT_NEXT(A); + } + /* avoid division by zero later on */ + if (S == 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_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 = 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 igraph_error_t 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; + igraph_integer_t 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_VIT_GET(A); + S += VECTOR(*U)[v]; + IGRAPH_VIT_NEXT(A); + } + if (islocal) { + S += VECTOR(*U)[vid]; + } + /* avoid division by zero later on */ + if (S == 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_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 = 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 igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, + igraph_integer_t vid, + const igraph_vector_t *quantities, + const igraph_vector_int_t *strategies, + igraph_neimode_t mode, + igraph_bool_t *updates, + igraph_bool_t islocal) { + + igraph_integer_t nvert; + igraph_vector_int_t degv; + *updates = true; + + /* 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_vector_size(quantities)) { + IGRAPH_ERROR("Size of quantities vector different from number of vertices", + IGRAPH_EINVAL); + } + if (nvert != igraph_vector_int_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 = false; + } + /* graph has >= 2 vertices, but no edges */ + if (igraph_ecount(graph) < 1) { + *updates = false; + } + + /* 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_INT_INIT_FINALLY(°v, 1); + IGRAPH_CHECK(igraph_degree(graph, °v, igraph_vss_1(vid), + mode, IGRAPH_NO_LOOPS)); + if (VECTOR(degv)[0] < 1) { + *updates = false; + } + igraph_vector_int_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 optimum. 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 + */ + +igraph_error_t igraph_deterministic_optimal_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_optimal_t optimality, + const igraph_vector_t *quantities, + igraph_vector_int_t *strategies, + igraph_neimode_t mode) { + igraph_integer_t i, k, v; + igraph_real_t q; + igraph_vector_int_t adj; + igraph_bool_t updates; + + IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, + strategies, mode, &updates, + /*is local?*/ true)); + 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_INT_INIT_FINALLY(&adj, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode)); + IGRAPH_CHECK(igraph_vector_int_shuffle(&adj)); + /* maximum deterministic imitation */ + i = vid; + q = VECTOR(*quantities)[vid]; + if (optimality == IGRAPH_MAXIMUM) { + for (k = 0; k < igraph_vector_int_size(&adj); k++) { + v = VECTOR(adj)[k]; + if (VECTOR(*quantities)[v] > q) { + i = v; + q = VECTOR(*quantities)[v]; + } + } + } else { /* minimum deterministic imitation */ + for (k = 0; k < igraph_vector_int_size(&adj); k++) { + v = VECTOR(adj)[k]; + if (VECTOR(*quantities)[v] < q) { + i = v; + q = 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_int_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 + */ + +igraph_error_t igraph_moran_process(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_t *quantities, + igraph_vector_int_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_int_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; + igraph_integer_t 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?*/ false)); + 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_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?*/ false, + /*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_INT_INIT_FINALLY(°, 1); + while (!IGRAPH_VIT_END(vA)) { + u = 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?*/ true, + /*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_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_int_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 + */ + +igraph_error_t igraph_roulette_wheel_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_bool_t islocal, + const igraph_vector_t *quantities, + igraph_vector_int_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; + igraph_integer_t 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_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 + */ + +igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, + igraph_integer_t vid, + igraph_imitate_algorithm_t algo, + const igraph_vector_t *quantities, + igraph_vector_int_t *strategies, + igraph_neimode_t mode) { + igraph_bool_t updates; + igraph_integer_t u; + igraph_vector_int_t adj; + igraph_integer_t 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?*/ true)); + if (!updates) { + return IGRAPH_SUCCESS; /* nothing more to do */ + } + + /* immediate neighbours of v */ + IGRAPH_VECTOR_INT_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_int_push_back(&adj, vid)); + RNG_BEGIN(); + i = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); + RNG_END(); + u = 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 = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); + RNG_END(); + u = 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 = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); + RNG_END(); + u = VECTOR(adj)[i]; + if (VECTOR(*quantities)[u] < VECTOR(*quantities)[vid]) { + VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; + } + } + + /* clean up */ + igraph_vector_int_destroy(&adj); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/misc/mixing.c b/src/misc/mixing.c new file mode 100644 index 0000000..ef61a8d --- /dev/null +++ b/src/misc/mixing.c @@ -0,0 +1,957 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-2023 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_mixing.h" + +#include "igraph_interface.h" +#include "igraph_structural.h" + +/** + * \function igraph_assortativity_nominal + * \brief 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. + * + * + * The unnormalized version, computed when \p normalized is set to false, + * is identical to the modularity, and is defined as follows for + * directed networks: + * + * 1/m sum_ij (A_ij - k^out_i k^in_j / m) d(i,j), + * + * where \c m denotes the number of edges, \c A_ij is the adjacency matrix, + * k^out and k^in are the out- and in-degrees, + * and d(i,j) is one if vertices \c i and \c j are in the same + * category and zero otherwise. + * + * + * The normalized assortativity coefficient is obtained by dividing the + * previous expression by + * + * 1/m sum_ij (m - k^out_i k^in_j d(i,j) / m). + * + * It can take any value within the interval [-1, 1]. + * + * + * Undirected graphs are effectively treated as directed ones with all-reciprocal + * edges. Thus, self-loops are taken into account twice in undirected graphs. + * + * + * References: + * + * + * M. E. J. Newman: Mixing patterns in networks, + * Phys. Rev. E 67, 026126 (2003) + * https://doi.org/10.1103/PhysRevE.67.026126. + * See section II and equation (2) for the definition of the concept. + * + * + * For an educational overview of assortativity, see + * M. E. J. Newman, + * Networks: An Introduction, Oxford University Press (2010). + * https://doi.org/10.1093/acprof%3Aoso/9780199206650.001.0001. + * + * \param graph The input graph, it can be directed or undirected. + * \param types Integer vector giving the vertex categories. The types + * are represented by integers starting at 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. + * \param normalized Boolean, whether to compute the usual normalized + * assortativity. The unnormalized version is identical to + * modularity. Supply true here to compute the standard assortativity. + * \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() for computing the assortativity + * based on continuous vertex values instead of discrete categories. + * \ref igraph_modularity() to compute generalized modularity. + * \ref igraph_joint_type_distribution() to obtain the mixing matrix. + * + * \example examples/simple/igraph_assortativity_nominal.c + */ + +igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, + const igraph_vector_int_t *types, + igraph_real_t *res, + igraph_bool_t directed, + igraph_bool_t normalized) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_real_t no_of_edges_real = no_of_edges; /* for divisions */ + igraph_integer_t no_of_types; + igraph_vector_int_t ai, bi, eii; + igraph_real_t sumaibi = 0.0, sumeii = 0.0; + + if (igraph_vector_int_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_int_min(types) < 0) { + IGRAPH_ERROR("Vertex types must not be negative.", IGRAPH_EINVAL); + } + + directed = directed && igraph_is_directed(graph); + + no_of_types = igraph_vector_int_max(types) + 1; + IGRAPH_VECTOR_INT_INIT_FINALLY(&ai, no_of_types); + IGRAPH_VECTOR_INT_INIT_FINALLY(&bi, no_of_types); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eii, no_of_types); + + for (igraph_integer_t e = 0; e < no_of_edges; e++) { + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_integer_t from_type = VECTOR(*types)[from]; + igraph_integer_t to_type = 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 (igraph_integer_t i = 0; i < no_of_types; i++) { + sumaibi += (VECTOR(ai)[i] / no_of_edges_real) * (VECTOR(bi)[i] / no_of_edges_real); + sumeii += (VECTOR(eii)[i] / no_of_edges_real); + } + + if (!directed) { + sumaibi /= 4.0; + sumeii /= 2.0; + } + + if (normalized) { + *res = (sumeii - sumaibi) / (1.0 - sumaibi); + } else { + *res = (sumeii - sumaibi); + } + + igraph_vector_int_destroy(&eii); + igraph_vector_int_destroy(&bi); + igraph_vector_int_destroy(&ai); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_assortativity + * \brief Assortativity based on numeric properties of vertices. + * + * This function calculates the assortativity coefficient of a + * graph based on given values \c x_i for each vertex \c i. This type of + * assortativity coefficient equals the Pearson correlation of the values + * at the two ends of the edges. + * + * + * The unnormalized covariance of values, computed when \p normalized is + * set to false, is defined as follows in a directed graph: + * + * cov(x_out, x_in) = 1/m sum_ij (A_ij - k^out_i k^in_j / m) x_i x_j, + * + * where \c m denotes the number of edges, \c A_ij is the adjacency matrix, and + * k^out and k^in are the out- and in-degrees. + * \c x_out and \c x_in refer to the sets of vertex values at the start and end of + * the directed edges. + * + * + * The normalized covariance, i.e. Pearson correlation, is obtained by dividing + * the previous expression by + * sqrt(var(x_out)) sqrt(var(x_in)), where + * + * var(x_out) = 1/m sum_i k^out_i x_i^2 - (1/m sum_i k^out_i x_i^2)^2 + * + * var(x_in) = 1/m sum_j k^in_j x_j^2 - (1/m sum_j k^in_j x_j^2)^2 + * + * + * Undirected graphs are effectively treated as directed graphs where all edges + * are reciprocal. Therefore, self-loops are effectively considered twice in + * undirected graphs. + * + * + * References: + * + * + * M. E. J. Newman: Mixing patterns + * in networks, Phys. Rev. E 67, 026126 (2003) + * https://doi.org/10.1103/PhysRevE.67.026126. + * See section III and equation (21) for the definition, and equation (26) for + * performing the calculation in directed graphs with the degrees as values. + * + * + * M. E. J. Newman: Assortative mixing in networks, + * Phys. Rev. Lett. 89, 208701 (2002) + * https://doi.org/10.1103/PhysRevLett.89.208701. + * See equation (4) for performing the calculation in undirected + * graphs with the degrees as values. + * + * + * For an educational overview of the concept of assortativity, see + * M. E. J. Newman, + * Networks: An Introduction, Oxford University Press (2010). + * https://doi.org/10.1093/acprof%3Aoso/9780199206650.001.0001. + * + * \param graph The input graph, it can be directed or undirected. + * \param values The vertex values, these can be arbitrary numeric + * values. + * \param values_in A second value vector to be used for the incoming + * edges when calculating assortativity for a directed graph. + * Supply \c NULL 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 the 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. + * \param normalized Boolean, whether to compute the normalized + * covariance, i.e. Pearson correlation. Supply true here to + * compute the standard assortativity. + * \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 degrees. + */ + +igraph_error_t igraph_assortativity(const igraph_t *graph, + const igraph_vector_t *values, + const igraph_vector_t *values_in, + igraph_real_t *res, + igraph_bool_t directed, + igraph_bool_t normalized) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + + directed = directed && igraph_is_directed(graph); + + if (!directed && values_in) { + IGRAPH_WARNING("Incoming vertex values ignored when calculating undirected assortativity."); + } + + if (igraph_vector_size(values) != no_of_nodes) { + IGRAPH_ERROR("Invalid vertex values vector length.", IGRAPH_EINVAL); + } + + if (values_in && igraph_vector_size(values_in) != no_of_nodes) { + IGRAPH_ERROR("Invalid incoming vertex values vector length.", IGRAPH_EINVAL); + } + + if (!directed) { + igraph_real_t num1 = 0.0, num2 = 0.0, den1 = 0.0; + + for (igraph_integer_t e = 0; e < no_of_edges; e++) { + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_real_t from_value = VECTOR(*values)[from]; + igraph_real_t to_value = VECTOR(*values)[to]; + + num1 += from_value * to_value; + num2 += from_value + to_value; + if (normalized) { + den1 += from_value * from_value + to_value * to_value; + } + } + + num1 /= no_of_edges; + if (normalized) { + den1 /= no_of_edges * 2.0; + } + num2 /= no_of_edges * 2.0; + num2 = num2 * num2; + + if (normalized) { + *res = (num1 - num2) / (den1 - num2); + } else { + *res = (num1 - 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 (!values_in) { + values_in = values; + } + + for (igraph_integer_t e = 0; e < no_of_edges; e++) { + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_real_t from_value = VECTOR(*values)[from]; + igraph_real_t to_value = VECTOR(*values_in)[to]; + + num1 += from_value * to_value; + num2 += from_value; + num3 += to_value; + if (normalized) { + den1 += from_value * from_value; + den2 += to_value * to_value; + } + } + + num = num1 - num2 * num3 / no_of_edges; + if (normalized) { + den = sqrt(den1 - num2 * num2 / no_of_edges) * + sqrt(den2 - num3 * num3 / no_of_edges); + + *res = num / den; + } else { + *res = num / no_of_edges; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_assortativity_degree + * \brief 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. + * This function simply calls \ref igraph_assortativity() with + * the degrees as the vertex values and normalization enabled. + * In the directed case, it uses out-degrees as out-values and + * in-degrees as in-values. + * + * + * For regular graphs, i.e. graphs in which all vertices have the + * same degree, computing degree correlations is not meaningful, + * and this function returns NaN. + * + * \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 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, + * and \ref igraph_joint_degree_distribution() to get the complete + * joint degree distribution. + * + * \example examples/simple/igraph_assortativity_degree.c + */ + +igraph_error_t igraph_assortativity_degree(const igraph_t *graph, + igraph_real_t *res, + igraph_bool_t directed) { + + directed = directed && igraph_is_directed(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + /* This function uses igraph_strength() instead of igraph_degree() in order to obtain + * a vector of reals instead of a vector of integers. */ + if (directed) { + igraph_vector_t indegree, outdegree; + 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, NULL)); + IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, NULL)); + IGRAPH_CHECK(igraph_assortativity(graph, &outdegree, &indegree, res, /* directed */ true, /* normalized */ true)); + igraph_vector_destroy(&indegree); + igraph_vector_destroy(&outdegree); + IGRAPH_FINALLY_CLEAN(2); + } else { + igraph_vector_t degree; + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, NULL)); + IGRAPH_CHECK(igraph_assortativity(graph, °ree, 0, res, /* directed */ false, /* normalized */ true)); + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_joint_degree_matrix + * \brief The joint degree matrix of a graph. + * + * \experimental + * + * In graph theory, the joint degree matrix \c J_ij of a graph gives the number + * of edges, or sum of edge weights, between vertices of degree \c i and degree + * \c j. This function stores \c J_ij into jdm[i-1, j-1]. + * Each edge, including self-loops, is counted precisely once, both in undirected + * and directed graphs. + * + * + * sum_(i,j) J_ij is the total number of edges (or total edge weight) + * \c m in the graph, where (i,j) refers to ordered or unordered + * pairs in directed and undirected graphs, respectively. Thus J_ij / m + * is the probability that an edge chosen at random (with probability proportional + * to its weight) connects vertices with degrees \c i and \c j. + * + * + * Note that \c J_ij is similar, but not identical to the joint degree + * \em distribution, computed by \ref igraph_joint_degree_distribution(), + * which is defined for \em ordered (i, j) degree + * pairs even in the undirected case. When considering undirected graphs, the + * diagonal of the joint degree distribution is twice that of the joint + * degree matrix. + * + * + * References: + * + * + * Isabelle Stanton and Ali Pinar: + * Constructing and sampling graphs with a prescribed joint degree distribution. + * ACM J. Exp. Algorithmics 17, Article 3.5 (2012). + * https://doi.org/10.1145/2133803.2330086 + * + * \param graph A pointer to an initialized graph object. + * \param weights A vector containing the weights of the edges. If passing a + * \c NULL pointer, edges will be assumed to have unit weights, i.e. + * the matrix entries will be connection counts. + * \param jdm A pointer to an initialized matrix that will be resized. The values + * will be written here. + * \param max_out_degree Number of rows in the result, i.e. the largest (out-)degree + * to consider. If negative, the largest (out-)degree of the graph will + * be used. + * \param max_in_degree Number of columns in the result, i.e. the largest (in-)degree + * to consider. If negative, the largest (in-)degree of the graph will + * be used. + * \return Error code. + * + * \sa \ref igraph_joint_degree_distribution() to count ordered vertex pairs instead of + * edges, or to obtain a normalized matrix. + * + * Time complexity: O(E), where E is the number of edges in input graph. + */ + +igraph_error_t igraph_joint_degree_matrix( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_matrix_t *jdm, + igraph_integer_t max_out_degree, igraph_integer_t max_in_degree) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_eit_t eit; + igraph_integer_t eid; + igraph_integer_t v1id; + igraph_integer_t v2id; + igraph_integer_t v1deg; + igraph_integer_t v2deg; + + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (igraph_is_directed(graph)) { + igraph_vector_int_t out_degrees; + igraph_vector_int_t in_degrees; + + // Compute max degrees + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_degrees, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_degrees, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, &out_degrees, igraph_vss_all(), IGRAPH_OUT, true)); + IGRAPH_CHECK(igraph_degree(graph, &in_degrees, igraph_vss_all(), IGRAPH_IN, true)); + + if (max_out_degree < 0) { + max_out_degree = no_of_nodes > 0 ? igraph_vector_int_max(&out_degrees) : 0; + } + + if (max_in_degree < 0) { + max_in_degree = no_of_nodes > 0 ? igraph_vector_int_max(&in_degrees) : 0; + } + + IGRAPH_CHECK(igraph_matrix_resize(jdm, max_out_degree, max_in_degree)); + igraph_matrix_null(jdm); + + 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)) { + eid = IGRAPH_EIT_GET(eit); + v1id = IGRAPH_FROM(graph, eid); + v2id = IGRAPH_TO(graph, eid); + v1deg = VECTOR(out_degrees)[v1id]; + v2deg = VECTOR(in_degrees)[v2id]; + if (v1deg <= max_out_degree && v2deg <= max_in_degree) { + MATRIX(*jdm, v1deg-1, v2deg-1) += weights ? VECTOR(*weights)[eid] : 1; + } + } + + igraph_eit_destroy(&eit); + igraph_vector_int_destroy(&in_degrees); + igraph_vector_int_destroy(&out_degrees); + IGRAPH_FINALLY_CLEAN(3); + + } else { + igraph_vector_int_t degrees; + igraph_integer_t maxdeg; + + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, true)); + + // Compute max degree of the graph only if needed + if (max_out_degree < 0 || max_in_degree < 0) { + maxdeg = no_of_nodes > 0 ? igraph_vector_int_max(°rees) : 0; + } + + if (max_out_degree < 0) { + max_out_degree = maxdeg; + } + if (max_in_degree < 0) { + max_in_degree = maxdeg; + } + + IGRAPH_CHECK(igraph_matrix_resize(jdm, max_out_degree, max_in_degree)); + igraph_matrix_null(jdm); + + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + while (!IGRAPH_EIT_END(eit)) { + eid = IGRAPH_EIT_GET(eit); + v1id = IGRAPH_FROM(graph, eid); + v2id = IGRAPH_TO(graph, eid); + v1deg = VECTOR(degrees)[v1id]; + v2deg = VECTOR(degrees)[v2id]; + + // Undirected JDMs are symmetrical, needs to be accounted for this when indexing. + if (v1deg <= max_out_degree && v2deg <= max_in_degree) { + MATRIX(*jdm, v1deg-1, v2deg-1) += weights ? VECTOR(*weights)[eid] : 1; + } + // Do not double-count connections between same-degree vertices. + if (v1deg != v2deg && v2deg <= max_out_degree && v1deg <= max_in_degree) { + MATRIX(*jdm, v2deg-1, v1deg-1) += weights ? VECTOR(*weights)[eid] : 1; + } + + IGRAPH_EIT_NEXT(eit); + } + + igraph_eit_destroy(&eit); + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(2); + } + + return IGRAPH_SUCCESS; +} + +/** + * Common implementation for igraph_joint_type_distribution() and igraph_joint_degree_distribution() + * + * For the max_from/to_type parameters, negative values mean "automatic". These are used + * only with igraph_joint_degree_distribution(). + * + * check_types controls whether types should be validated to be non-negative. Validation + * is only necessary with igraph_joint_type_distribution() but not with igraph_joint_degree_distribution(). + * + * directed_neighbors must NOT be true when the graph is undirected. + */ +static igraph_error_t mixing_matrix( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_matrix_t *p, + const igraph_vector_int_t *from_types, const igraph_vector_int_t *to_types, + igraph_bool_t directed_neighbors, igraph_bool_t normalized, + igraph_integer_t max_from_type, igraph_integer_t max_to_type, + igraph_bool_t check_types) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t nrow, ncol; + igraph_real_t sum; + igraph_bool_t negative_weight; + + if (igraph_vector_int_size(from_types) != no_of_nodes) { + IGRAPH_ERROR("Length of 'from' type vector must agree with vertex count.", IGRAPH_EINVAL); + } + + if (igraph_vector_int_size(to_types) != no_of_nodes) { + IGRAPH_ERROR("Length of 'to' type vector must agree with vertex count.", IGRAPH_EINVAL); + } + + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (max_from_type < 0) { + if (no_of_nodes == 0) { + nrow = 0; + } else { + nrow = igraph_vector_int_max(from_types) + 1; + } + } else { + nrow = max_from_type + 1; + } + + if (max_to_type < 0) { + if (no_of_nodes == 0) { + ncol = 0; + } else if (to_types == from_types) { + /* Avoid computing the maximum again if target vertex types + * are the same as source vertex types. */ + ncol = nrow; + } else { + ncol = igraph_vector_int_max(to_types) + 1; + } + } else { + ncol = max_to_type + 1; + } + + if (check_types && no_of_nodes > 0) { + igraph_integer_t min; + + min = igraph_vector_int_min(from_types); + if (min < 0) { + IGRAPH_ERROR("Invalid source vertex type.", IGRAPH_EINVAL); + } + + if (to_types != from_types) { + min = igraph_vector_int_min(from_types); + if (min < 0) { + IGRAPH_ERROR("Invalid target vertex type.", IGRAPH_EINVAL); + } + } + } + + IGRAPH_CHECK(igraph_matrix_resize(p, nrow, ncol)); + igraph_matrix_null(p); + + sum = 0; + negative_weight = false; + for (igraph_integer_t eid=0; eid < no_of_edges; eid++) { + igraph_integer_t from = IGRAPH_FROM(graph, eid); + igraph_integer_t to = IGRAPH_TO(graph, eid); + igraph_integer_t from_type = VECTOR(*from_types)[from]; + igraph_integer_t to_type = VECTOR(*to_types)[to]; + igraph_real_t w = weights ? VECTOR(*weights)[eid] : 1; + + if (from_type >= nrow || to_type >= ncol) { + continue; + } + + MATRIX(*p, from_type, to_type) += w; + sum += w; + + if (! directed_neighbors) { + MATRIX(*p, to_type, from_type) += w; + sum += w; + } + + if (w < 0) { + negative_weight = true; + } + } + + if (normalized) { + if (negative_weight) { + /* When some edge weights are negative, they cannot be interpreted as sampling weights, + * and the sum of weights may be zero, potentially leading to Inf/NaN results. */ + IGRAPH_WARNING("Negative edge weights are present. Normalization may not be meaningful."); + } + if (no_of_edges > 0) { + /* Scale only when there are some edges, thus 'sum' can be non-zero. */ + igraph_matrix_scale(p, 1.0 / sum); + } + } + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_joint_degree_distribution + * \brief The joint degree distribution of a graph. + * + * \experimental + * + * Computes the joint degree distribution \c P_ij of a graph, used in the + * study of degree correlations. \c P_ij is the probability that a randomly + * chosen ordered pair of \em connected vertices have degrees \c i and \c j. + * + * + * In directed graphs, directionally connected u -> v pairs + * are considered. The joint degree distribution of an undirected graph is the + * same as that of the corresponding directed graph in which all connection are + * bidirectional, assuming that \p from_mode is \c IGRAPH_OUT, \p to_mode is + * \c IGRAPH_IN and \p directed_neighbors is true. + * + * + * When \p normalized is false, sum_ij P_ij gives the total + * number of connections in a directed graph, or twice that value in an + * undirected graph. The sum is taken over ordered (i,j) degree + * pairs. + * + * + * The joint degree distribution relates to other concepts used in the study of + * degree correlations. If \c P_ij is normalized then the degree correlation + * function k_nn(k) is obtained as + * + * + * k_nn(k) = (sum_j j P_kj) / (sum_j P_kj). + * + * + * The non-normalized degree assortativity is obtained as + * + * + * a = sum_ij i j (P_ij - q_i r_j), + * + * + * where q_i = sum_k P_ik and r_j = sum_k P_kj. + * + * + * Note that the joint degree distribution \c P_ij is similar, but not identical + * to the joint degree matrix \c J_ij computed by \ref igraph_joint_degree_matrix(). + * If the graph is undirected, then the diagonal entries of an unnormalized \c P_ij + * are double that of \c J_ij, as any undirected connection between same-degree vertices + * is counted in both directions. In contrast to \ref igraph_joint_degree_matrix(), + * this function returns matrices which include the row and column corresponding + * to zero degrees. In directed graphs, this row and column is not necessarily + * zero when \p from_mode is different from \c IGRAPH_OUT or \p to_mode is different + * from \c IGRAPH_IN. + * + * + * References: + * + * + * M. E. J. Newman: Mixing patterns in networks, + * Phys. Rev. E 67, 026126 (2003) + * https://doi.org/10.1103/PhysRevE.67.026126. + * + * \param graph A pointer to an initialized graph object. + * \param weights A vector containing the weights of the edges. If passing a + * \c NULL pointer, edges will be assumed to have unit weights. + * \param p A pointer to an initialized matrix that will be resized. The \c P_ij + * value will be written into p[i,j]. + * \param from_mode How to compute the degree of sources? Can be \c IGRAPH_OUT + * for out-degree, \c IGRAPH_IN for in-degree, or \c IGRAPH_ALL for total degree. + * Ignored in undirected graphs. + * \param to_mode How to compute the degree of sources? Can be \c IGRAPH_OUT + * for out-degree, \c IGRAPH_IN for in-degree, or \c IGRAPH_ALL for total degree. + * Ignored in undirected graphs. + * \param directed_neighbors Whether to consider u -> v connections + * to be directed. Undirected connections are treated as reciprocal directed ones, + * i.e. both u -> v and v -> u will be considered. + * Ignored in undirected graphs. + * \param normalized Whether to normalize the matrix so that entries sum to 1.0. + * If false, matrix entries will be connection counts. Normalization is not + * meaningful if some edge weights are negative. + * \param max_from_degree The largest source vertex degree to consider. If negative, + * the largest source degree will be used. The row count of the result matrix + * is one larger than this value. + * \param max_to_degree The largest target vertex degree to consider. If negative, + * the largest target degree will be used. The column count of the result matrix + * is one larger than this value. + * \return Error code. + * + * \sa \ref igraph_joint_degree_matrix() for computing the joint degree matrix; + * \ref igraph_assortativity_degree() and \ref igraph_assortativity() for + * degree correlations coefficients, and \ref igraph_degree_correlation_vector() + * for the degree correlation function. + * + * Time complexity: O(E), where E is the number of edges in the input graph. + */ +igraph_error_t igraph_joint_degree_distribution( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_matrix_t *p, + igraph_neimode_t from_mode, igraph_neimode_t to_mode, + igraph_bool_t directed_neighbors, + igraph_bool_t normalized, + igraph_integer_t max_from_degree, igraph_integer_t max_to_degree) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t *deg_from, *deg_to, deg_out, deg_in, deg_all; + + /* Make sure directionality parameters are consistent for undirected graphs. */ + if (! igraph_is_directed(graph)) { + from_mode = to_mode = IGRAPH_ALL; + directed_neighbors = false; + } + + igraph_bool_t have_out = from_mode == IGRAPH_OUT || to_mode == IGRAPH_OUT; + igraph_bool_t have_in = from_mode == IGRAPH_IN || to_mode == IGRAPH_IN; + igraph_bool_t have_all = from_mode == IGRAPH_ALL || to_mode == IGRAPH_ALL; + + if (have_out) { + IGRAPH_VECTOR_INT_INIT_FINALLY(°_out, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °_out, igraph_vss_all(), IGRAPH_OUT, /* loops */ true)); + } + + if (have_in) { + IGRAPH_VECTOR_INT_INIT_FINALLY(°_in, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °_in, igraph_vss_all(), IGRAPH_IN, /* loops */ true)); + } + + if (have_all) { + IGRAPH_VECTOR_INT_INIT_FINALLY(°_all, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °_all, igraph_vss_all(), IGRAPH_ALL, /* loops */ true)); + } + + switch (from_mode) { + case IGRAPH_OUT: deg_from = °_out; break; + case IGRAPH_IN: deg_from = °_in; break; + case IGRAPH_ALL: deg_from = °_all; break; + default: + IGRAPH_ERROR("Invalid 'from' degree mode.", IGRAPH_EINVMODE); + } + + switch (to_mode) { + case IGRAPH_OUT: deg_to = °_out; break; + case IGRAPH_IN: deg_to = °_in; break; + case IGRAPH_ALL: deg_to = °_all; break; + default: + IGRAPH_ERROR("Invalid 'to' degree mode.", IGRAPH_EINVMODE); + } + + IGRAPH_CHECK(mixing_matrix(graph, + weights, p, + deg_from, deg_to, + directed_neighbors, normalized, + max_from_degree, max_to_degree, + /*check_types=*/ false)); + + if (have_all) { + igraph_vector_int_destroy(°_all); + IGRAPH_FINALLY_CLEAN(1); + } + + if (have_in) { + igraph_vector_int_destroy(°_in); + IGRAPH_FINALLY_CLEAN(1); + } + + if (have_out) { + igraph_vector_int_destroy(°_out); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_joint_type_distribution + * \brief Mixing matrix for vertex categories. + * + * \experimental + * + * Computes the mixing matrix M_ij, i.e. the joint distribution of vertex types + * at the endpoints directed of edges. Categories are represented by non-negative integer + * indices, passed in \p from_types and \p to_types. The row and column counts of \p m + * will be one larger than the largest source and target type, respectively. Re-index type + * vectors using \ref igraph_reindex_membership() if they are not contiguous integers, + * to avoid producing a very large matrix. + * + * + * M_ij is proportional to the probability that a randomly chosen ordered pair of vertices + * have types \c i and \c j. + * + * + * When there is a single categorization of vertices, i.e. \p from_types and \p to_types + * are the same, M_ij is related to the modularity (\ref igraph_modularity()) and nominal + * assortativity (\ref igraph_assortativity_nominal()). Let a_i = sum_j M_ij and + * b_j = sum_i M_ij. If M_ij is normalized, i.e. sum_ij M_ij = 1, + * and the types represent membership in vertex partitions, then the modularity of the + * partitioning can be computed as + * + * + * Q = sum_ii M_ii - sum_i a_i b_i + * + * + * The normalized nominal assortativity is + * + * + * Q / (1 - sum_i a_i b_i) + * + * + * \ref igraph_joint_degree_distribution() is a special case of this function, with + * categories consisting vertices of the same degree. + * + * + * References: + * + * + * M. E. J. Newman: Mixing patterns in networks, + * Phys. Rev. E 67, 026126 (2003) + * https://doi.org/10.1103/PhysRevE.67.026126. + * + * \param graph The input graph. + * \param p The mixing matrix M_ij will be stored here. + * \param weights A vector containing the weights of the edges. If passing a + * \c NULL pointer, edges will be assumed to have unit weights. + * \param from_types Vertex types for source vertices. These must be non-negative integers. + * \param to_types Vertex types for target vertices. These must be non-negative integers. + * If \c NULL, it is assumed to be the same as \p from_types. + * \param directed Whether to treat edges are directed. Ignored for undirected graphs. + * \param normalized Whether to normalize the matrix so that entries sum to 1.0. + * If false, matrix entries will be connection counts. Normalization is not + * meaningful if some edge weights are negative. + * \return Error code. + * + * \sa \ref igraph_joint_degree_distribution() to compute the joint distribution + * of vertex degrees; \ref igraph_modularity() to compute the modularity of + * a vertex partitioning; \ref igraph_assortativity_nominal() to compute + * assortativity based on vertex categories. + * + * Time complexity: O(E), where E is the number of edges in the input graph. + */ +igraph_error_t igraph_joint_type_distribution( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_matrix_t *p, + const igraph_vector_int_t *from_types, const igraph_vector_int_t *to_types, + igraph_bool_t directed, igraph_bool_t normalized) { + + IGRAPH_ASSERT(from_types != NULL); + if (to_types == NULL) { + to_types = from_types; + } + if (! igraph_is_directed(graph)) { + directed = false; + } + return mixing_matrix(graph, weights, p, from_types, to_types, directed, normalized, -1, -1, true); +} diff --git a/src/misc/motifs.c b/src/misc/motifs.c new file mode 100644 index 0000000..07aa042 --- /dev/null +++ b/src/misc/motifs.c @@ -0,0 +1,1193 @@ +/* -*- 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/internal.h" + +/** + * Callback function for igraph_motifs_randesu that counts the motifs by + * isomorphism class in a histogram. + */ +static igraph_error_t igraph_i_motifs_randesu_update_hist( + const igraph_t *graph, + igraph_vector_int_t *vids, igraph_integer_t isoclass, void* extra) { + igraph_vector_t *hist = (igraph_vector_t*)extra; + IGRAPH_UNUSED(graph); IGRAPH_UNUSED(vids); + VECTOR(*hist)[isoclass]++; + return IGRAPH_SUCCESS; +} + +/** + * \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. In this case, 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; + * \ref igraph_graph_count() to find the number of graph on a given + * number of vertices, i.e. the length of the \p hist vector. + * + * Time complexity: TODO. + * + * \example examples/simple/igraph_motifs_randesu.c + */ +igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t size, const igraph_vector_t *cut_prob) { + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t 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 (%" IGRAPH_PRId ") 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) { + const int not_connected[] = { 0, 1, 2, 4, 5, 6, 9, 10, 11, 15, 22, 23, 27, + 28, 33, 34, 39, 62, 120 }; + size_t i, n = sizeof(not_connected) / sizeof(not_connected[0]); + 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 */ + const int not_connected[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 19 }; + size_t 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 */ + const 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}; + size_t 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 + */ + +igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_integer_t size, + const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback, + void* extra) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_adjlist_t allneis, alloutneis; + igraph_vector_int_t *neis; + igraph_integer_t father; + igraph_integer_t i, j, s; + igraph_integer_t motifs = 0; + IGRAPH_UNUSED(motifs); /* We mark it as unused to prevent warnings about unused-but-set-variables. */ + + igraph_vector_int_t vids; /* this is G */ + igraph_vector_int_t adjverts; /* this is V_E */ + igraph_stack_int_t stack; /* this is S */ + igraph_integer_t *added; + char *subg; + + const unsigned int *arr_idx, *arr_code; + unsigned int code = 0; + unsigned int mul, idx; + + igraph_bool_t terminate = false; + + 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 (%" IGRAPH_PRId ") must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Insufficient memory to find motifs."); + IGRAPH_FINALLY(igraph_free, added); + + subg = IGRAPH_CALLOC(no_of_nodes, char); + IGRAPH_CHECK_OOM(subg, "Insufficient memory to find motifs."); + 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_INT_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + + RNG_BEGIN(); + + for (father = 0; father < no_of_nodes; father++) { + igraph_integer_t level; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (VECTOR(*cut_prob)[0] == 1 || RNG_UNIF01() < VECTOR(*cut_prob)[0]) { + continue; + } + + /* init G */ + igraph_vector_int_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_int_push_back(&vids, father)); + subg[father] = 1; added[father] += 1; level += 1; + + /* init V_E */ + igraph_vector_int_clear(&adjverts); + neis = igraph_adjlist_get(&allneis, father); + s = igraph_vector_int_size(neis); + for (i = 0; i < s; i++) { + igraph_integer_t nei = VECTOR(*neis)[i]; + if (!added[nei] && nei > father) { + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); + } + added[nei] += 1; + } + + /* init S */ + igraph_stack_int_clear(&stack); + + while (level > 1 || !igraph_vector_int_empty(&adjverts)) { + igraph_real_t cp = VECTOR(*cut_prob)[level]; + + if (level == size - 1) { + s = igraph_vector_int_size(&adjverts) / 2; + for (i = 0; i < s; i++) { + igraph_integer_t k, s2; + igraph_integer_t last; + igraph_error_t ret; + + if (cp != 0 && RNG_UNIF01() < cp) { + continue; + } + motifs += 1; + + last = VECTOR(adjverts)[2 * i]; + IGRAPH_CHECK(igraph_vector_int_push_back(&vids, last)); + subg[last] = (char) size; + + code = 0; idx = 0; + for (k = 0; k < size; k++) { + igraph_integer_t from = VECTOR(vids)[k]; + neis = igraph_adjlist_get(&alloutneis, from); + s2 = igraph_vector_int_size(neis); + for (j = 0; j < s2; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (subg[nei] && k != subg[nei] - 1) { + idx = (unsigned char) (mul * k + (subg[nei] - 1)); + code |= arr_idx[idx]; + } + } + } + + IGRAPH_CHECK_CALLBACK( + callback(graph, &vids, arr_code[code], extra), + &ret + ); + + if (ret == IGRAPH_STOP) { + terminate = true; + break; + } + + igraph_vector_int_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_int_empty(&adjverts)) { + /* we might step down */ + igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); + igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); + + if (cp == 0 || RNG_UNIF01() > cp) { + /* yes, step down */ + IGRAPH_CHECK(igraph_vector_int_push_back(&vids, nei)); + subg[nei] = (char) level + 1; added[nei] += 1; level += 1; + + IGRAPH_CHECK(igraph_stack_int_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, level)); + + neis = igraph_adjlist_get(&allneis, nei); + s = igraph_vector_int_size(neis); + for (i = 0; i < s; i++) { + igraph_integer_t nei2 = VECTOR(*neis)[i]; + if (!added[nei2] && nei2 > father) { + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + } + added[nei2] += 1; + } + } + } else { + /* no, step back */ + igraph_integer_t nei, neifather; + while (!igraph_stack_int_empty(&stack) && + level == igraph_stack_int_top(&stack) - 1) { + igraph_stack_int_pop(&stack); + nei = igraph_stack_int_pop(&stack); + neifather = igraph_stack_int_pop(&stack); + igraph_vector_int_push_back(&adjverts, nei); + igraph_vector_int_push_back(&adjverts, neifather); + } + + nei = igraph_vector_int_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[ VECTOR(*neis)[i] ] -= 1; + } + while (!igraph_vector_int_empty(&adjverts) && + igraph_vector_int_tail(&adjverts) == nei) { + igraph_vector_int_pop_back(&adjverts); + igraph_vector_int_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[ VECTOR(*neis)[i] ] -= 1; + } + + } /* for father */ + + RNG_END(); + + IGRAPH_FREE(added); + IGRAPH_FREE(subg); + igraph_vector_int_destroy(&vids); + igraph_vector_int_destroy(&adjverts); + igraph_adjlist_destroy(&alloutneis); + igraph_adjlist_destroy(&allneis); + igraph_stack_int_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 on \p size 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 connected subgraphs, as there are too + * many of them. + * + * + * The estimate is made by taking a sample of vertices and counting all + * connected subgraphs 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. + * + * \param graph The graph object to study. + * \param est Pointer to an integer, the result will be stored here. + * \param size The size of the subgraphs 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. + */ + +igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, + igraph_integer_t size, const igraph_vector_t *cut_prob, + igraph_integer_t sample_size, + const igraph_vector_int_t *parsample) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis; + + igraph_vector_int_t vids; /* this is G */ + igraph_vector_int_t adjverts; /* this is V_E */ + igraph_stack_int_t stack; /* this is S */ + igraph_integer_t *added; + igraph_vector_int_t *sample; + igraph_integer_t sam; + igraph_integer_t i; + + if (size < 3) { + IGRAPH_ERRORF("Motif size must be at least 3, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, size); + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + + if (parsample && !igraph_vector_int_isininterval(parsample, 0, no_of_nodes-1)) { + IGRAPH_ERROR("Sample vertex ID out of range.", IGRAPH_EINVVID); + } + + if (no_of_nodes == 0) { + *est = 0; + return IGRAPH_SUCCESS; + } + + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Insufficient memory to count motifs."); + IGRAPH_FINALLY(igraph_free, added); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + if (parsample == NULL) { + sample = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(sample, "Insufficient memory to count motifs."); + IGRAPH_FINALLY(igraph_free, sample); + IGRAPH_VECTOR_INT_INIT_FINALLY(sample, 0); + IGRAPH_CHECK(igraph_random_sample(sample, 0, no_of_nodes - 1, sample_size)); + } else { + sample = (igraph_vector_int_t*) parsample; + sample_size = igraph_vector_int_size(sample); + } + + *est = 0; + + RNG_BEGIN(); + + for (sam = 0; sam < sample_size; sam++) { + igraph_integer_t father = VECTOR(*sample)[sam]; + igraph_integer_t level, s; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (VECTOR(*cut_prob)[0] == 1 || + RNG_UNIF01() < VECTOR(*cut_prob)[0]) { + continue; + } + + /* init G */ + igraph_vector_int_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_int_push_back(&vids, father)); + added[father] += 1; level += 1; + + /* init V_E */ + igraph_vector_int_clear(&adjverts); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); + for (i = 0; i < s; i++) { + igraph_integer_t nei = VECTOR(neis)[i]; + if (!added[nei] && nei > father) { + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); + } + added[nei] += 1; + } + + /* init S */ + igraph_stack_int_clear(&stack); + + while (level > 1 || !igraph_vector_int_empty(&adjverts)) { + igraph_real_t cp = VECTOR(*cut_prob)[level]; + + if (level == size - 1) { + s = igraph_vector_int_size(&adjverts) / 2; + for (i = 0; i < s; i++) { + if (cp != 0 && RNG_UNIF01() < cp) { + continue; + } + (*est) += 1; + } + } + + if (level < size - 1 && + !igraph_vector_int_empty(&adjverts)) { + /* We might step down */ + igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); + igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); + + if (cp == 0 || RNG_UNIF01() > cp) { + /* Yes, step down */ + IGRAPH_CHECK(igraph_vector_int_push_back(&vids, nei)); + added[nei] += 1; level += 1; + + IGRAPH_CHECK(igraph_stack_int_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, level)); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); + for (i = 0; i < s; i++) { + igraph_integer_t nei2 = VECTOR(neis)[i]; + if (!added[nei2] && nei2 > father) { + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + } + added[nei2] += 1; + } + } + } else { + /* no, step back */ + igraph_integer_t nei, neifather; + while (!igraph_stack_int_empty(&stack) && + level == igraph_stack_int_top(&stack) - 1) { + igraph_stack_int_pop(&stack); + nei = igraph_stack_int_pop(&stack); + neifather = igraph_stack_int_pop(&stack); + igraph_vector_int_push_back(&adjverts, nei); + igraph_vector_int_push_back(&adjverts, neifather); + } + + nei = igraph_vector_int_pop_back(&vids); + added[nei] -= 1; level -= 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); + for (i = 0; i < s; i++) { + added[ VECTOR(neis)[i] ] -= 1; + } + while (!igraph_vector_int_empty(&adjverts) && + igraph_vector_int_tail(&adjverts) == nei) { + igraph_vector_int_pop_back(&adjverts); + igraph_vector_int_pop_back(&adjverts); + } + } + + } /* while */ + + /* clear the added vector */ + added[father] -= 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); + for (i = 0; i < s; i++) { + added[ VECTOR(neis)[i] ] -= 1; + } + + } /* for father */ + + RNG_END(); + + (*est) *= ((igraph_real_t) no_of_nodes / sample_size); + + if (parsample == 0) { + igraph_vector_int_destroy(sample); + IGRAPH_FREE(sample); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_FREE(added); + igraph_vector_int_destroy(&vids); + igraph_vector_int_destroy(&adjverts); + igraph_stack_int_destroy(&stack); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(5); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_motifs_randesu_no + * \brief Count the total number of motifs in a graph. + * + * This function counts the total number of (weakly) connected + * induced subgraphs on \p size vertices, without assigning isomorphism + * classes to them. Arbitrarily large motif sizes are supported. + * + * \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. + */ + +igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, + igraph_integer_t size, const igraph_vector_t *cut_prob) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis; + igraph_vector_int_t vids; /* this is G */ + igraph_vector_int_t adjverts; /* this is V_E */ + igraph_stack_int_t stack; /* this is S */ + igraph_integer_t *added; + igraph_integer_t father; + igraph_integer_t i; + + if (size < 3) { + IGRAPH_ERRORF("Motif size must be at least 3, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, size); + } + + if (igraph_vector_size(cut_prob) != size) { + IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); + } + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Insufficient memory to count motifs."); + IGRAPH_FINALLY(igraph_free, added); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + *no = 0; + + RNG_BEGIN(); + + for (father = 0; father < no_of_nodes; father++) { + igraph_integer_t level, s; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (VECTOR(*cut_prob)[0] == 1 || + RNG_UNIF01() < VECTOR(*cut_prob)[0]) { + continue; + } + + /* init G */ + igraph_vector_int_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_int_push_back(&vids, father)); + added[father] += 1; level += 1; + + /* init V_E */ + igraph_vector_int_clear(&adjverts); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); + for (i = 0; i < s; i++) { + igraph_integer_t nei = VECTOR(neis)[i]; + if (!added[nei] && nei > father) { + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); + } + added[nei] += 1; + } + + /* init S */ + igraph_stack_int_clear(&stack); + + while (level > 1 || !igraph_vector_int_empty(&adjverts)) { + igraph_real_t cp = VECTOR(*cut_prob)[level]; + + if (level == size - 1) { + s = igraph_vector_int_size(&adjverts) / 2; + for (i = 0; i < s; i++) { + if (cp != 0 && RNG_UNIF01() < cp) { + continue; + } + (*no) += 1; + } + } + + if (level < size - 1 && + !igraph_vector_int_empty(&adjverts)) { + /* We might step down */ + igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); + igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); + + if (cp == 0 || RNG_UNIF01() > cp) { + /* Yes, step down */ + IGRAPH_CHECK(igraph_vector_int_push_back(&vids, nei)); + added[nei] += 1; level += 1; + + IGRAPH_CHECK(igraph_stack_int_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, level)); + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); + for (i = 0; i < s; i++) { + igraph_integer_t nei2 = VECTOR(neis)[i]; + if (!added[nei2] && nei2 > father) { + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + } + added[nei2] += 1; + } + } + } else { + /* no, step back */ + igraph_integer_t nei, neifather; + while (!igraph_stack_int_empty(&stack) && + level == igraph_stack_int_top(&stack) - 1) { + igraph_stack_int_pop(&stack); + nei = igraph_stack_int_pop(&stack); + neifather = igraph_stack_int_pop(&stack); + igraph_vector_int_push_back(&adjverts, nei); + igraph_vector_int_push_back(&adjverts, neifather); + } + + nei = igraph_vector_int_pop_back(&vids); + added[nei] -= 1; level -= 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); + for (i = 0; i < s; i++) { + added[ VECTOR(neis)[i] ] -= 1; + } + while (!igraph_vector_int_empty(&adjverts) && + igraph_vector_int_tail(&adjverts) == nei) { + igraph_vector_int_pop_back(&adjverts); + igraph_vector_int_pop_back(&adjverts); + } + } + + } /* while */ + + /* clear the added vector */ + added[father] -= 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); + for (i = 0; i < s; i++) { + added[ VECTOR(neis)[i] ] -= 1; + } + + } /* for father */ + + RNG_END(); + + IGRAPH_FREE(added); + igraph_vector_int_destroy(&vids); + igraph_vector_int_destroy(&adjverts); + igraph_stack_int_destroy(&stack); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(5); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_dyad_census + * \brief 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 a real, the number of mutual dyads is + * stored here. + * \param asym Pointer to a real, the number of asymmetric dyads + * is stored here. + * \param null Pointer to a real, the number of null dyads is + * stored here. + * \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. + */ +igraph_error_t igraph_dyad_census(const igraph_t *graph, igraph_real_t *mut, + igraph_real_t *asym, igraph_real_t *null) { + + /* This function operates with a floating point type instead of an + * integer type in order to avoid integer overflow, which is likely + * for 'null' in large graphs on 32-bit systems. */ + + igraph_real_t nonrec = 0, rec = 0; + igraph_vector_int_t inneis, outneis; + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outneis, 0); + + for (i = 0; i < vc; i++) { + igraph_integer_t ideg, odeg; + igraph_integer_t 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_int_size(&inneis); + odeg = igraph_vector_int_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_int_destroy(&inneis); + igraph_vector_int_destroy(&outneis); + IGRAPH_FINALLY_CLEAN(2); + + *mut = rec / 2; + *asym = nonrec / 2; + *null = 0.5 * vc * (vc - 1.0) - (*mut + *asym); + if (*null == 0.0) *null = 0.0; /* avoid returning -0.0 */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_real_t *res2, + igraph_real_t *res4) { + + igraph_integer_t vc = igraph_vcount(graph); + igraph_vector_int_t seen; + igraph_vector_int_t *neis, *neis2; + igraph_integer_t i, j, k, s, neilen, neilen2, ign; + igraph_adjlist_t adjlist; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&seen, vc); + 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++) { + igraph_integer_t nei = 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++) { + igraph_integer_t nei = 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++) { + igraph_integer_t nei2 = 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_int_destroy(&seen); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \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 based on the type of pairwise + * connections it contains, i.e. mutual, asymmetric or no connection. + * A triple can be in one of 16 states, commonly described using + * Davis and Leinhardt's "MAN labels". The \p res vector will + * contain the counts of these in the following order: + * + * \clist + * \cli  0: 003 + * A, B, C, the empty graph. + * \cli  1: 012 + * A->B, C, a graph with a single directed edge. + * \cli  2: 102 + * A<->B, C, a graph with a mutual connection between two vertices. + * \cli  3: 021D + * A<-B->C, the binary out-tree. + * \cli  4: 021U + * A->B<-C, the binary in-tree. + * \cli  5: 021C + * A->B->C, the directed line. + * \cli  6: 111D + * A<->B<-C. + * \cli  7: 111U + * A<->B->C. + * \cli  8: 030T + * A->B<-C, A->C. + * \cli  9: 030C + * A<-B<-C, A->C. + * \cli 10: 201 + * A<->B<->C. + * \cli 11: 120D + * A<-B->C, A<->C. + * \cli 12: 120U + * A->B<-C, A<->C. + * \cli 13: 120C + * A->B->C, A<->C. + * \cli 14: 210 + * A->B<->C, A<->C. + * \cli 15: 300 + * A<->B<->C, A<->C, the complete graph. + * \endclist + * + * + * This function is intended for directed graphs. If the input is undirected, + * a warning is shown, and undirected edges will be interpreted as mutual. + * + * + * 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(). + * + * + * References: + * + * + * 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. + * + * \param graph The input graph. + * \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. + */ + +igraph_error_t 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. All connections will be treated as mutual."); + } + + 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_i_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 IGRAPH_SUCCESS; +} diff --git a/src/misc/order_cycle.cpp b/src/misc/order_cycle.cpp new file mode 100644 index 0000000..e977db5 --- /dev/null +++ b/src/misc/order_cycle.cpp @@ -0,0 +1,100 @@ +/* + 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, see . +*/ + +#include "misc/order_cycle.h" + +#include "igraph_interface.h" + +#include "core/exceptions.h" + +#include +#include + +// Initialized to {-1, -1} +struct eid_pair_t : public std::pair { + eid_pair_t() : std::pair(-1, -1) { } +}; + +/** + * \function igraph_i_order_cycle + * \brief Reorders edges of a cycle in cycle order + * + * This function takes \p cycle, a vector of arbitrarily ordered edge IDs, + * representing a graph cycle. It produces a vector \p res containing the + * same IDs in cycle order. \p res must be initialized when calling this function. + */ +igraph_error_t igraph_i_order_cycle( + const igraph_t *graph, + const igraph_vector_int_t *cycle, + igraph_vector_int_t *res) { + + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + + igraph_integer_t n = igraph_vector_int_size(cycle); + IGRAPH_ASSERT(n > 0); + + std::map inclist; + for (igraph_integer_t i=0; i < n; ++i) { + igraph_integer_t eid = VECTOR(*cycle)[i]; + + { + igraph_integer_t from = IGRAPH_FROM(graph, eid); + auto &p = inclist[from]; + if (p.first < 0) { + p.first = eid; + } else { + IGRAPH_ASSERT(p.second < 0); + p.second = eid; + } + } + + { + igraph_integer_t to = IGRAPH_TO(graph, eid); + auto &p = inclist[to]; + if (p.first < 0) { + p.first = eid; + } else { + IGRAPH_ASSERT(p.second < 0); + p.second = eid; + } + } + } + + igraph_vector_int_clear(res); + IGRAPH_CHECK(igraph_vector_int_reserve(res, igraph_vector_int_size(cycle))); + igraph_integer_t current_e = VECTOR(*cycle)[0]; + igraph_integer_t current_v = IGRAPH_FROM(graph, current_e); + for (igraph_integer_t i=0; i < n; ++i) { + const auto &p = inclist.at(current_v); + igraph_vector_int_push_back(res, current_e); /* reserved */ + igraph_integer_t next_e = p.first; + if (next_e == current_e) { + next_e = p.second; + } + current_e = next_e; + igraph_integer_t next_v = IGRAPH_FROM(graph, current_e); + if (next_v == current_v) { + next_v = IGRAPH_TO(graph, current_e); + } + current_v = next_v; + } + + IGRAPH_HANDLE_EXCEPTIONS_END; + + return IGRAPH_SUCCESS; +} diff --git a/src/misc/order_cycle.h b/src/misc/order_cycle.h new file mode 100644 index 0000000..2ed87f5 --- /dev/null +++ b/src/misc/order_cycle.h @@ -0,0 +1,35 @@ +/* + 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, see . +*/ + +#ifndef IGRAPH_ORDER_CYCLE_H +#define IGRAPH_ORDER_CYCLE_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +igraph_error_t igraph_i_order_cycle( + const igraph_t *graph, + const igraph_vector_int_t *cycle, + igraph_vector_int_t *res); + +__END_DECLS + +#endif /* IGRAPH_ORDER_CYCLE_H */ diff --git a/src/misc/other.c b/src/misc/other.c new file mode 100644 index 0000000..8d22697 --- /dev/null +++ b/src/misc/other.c @@ -0,0 +1,378 @@ +/* -*- 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_interface.h" +#include "igraph_nongraph.h" +#include "igraph_paths.h" + +#include "core/interruption.h" + +/** + * \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. + */ + +igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, + igraph_integer_t binwidth) { + + double sum = 0; + igraph_integer_t i; + + /* Check */ + if (igraph_vector_size(data) < binwidth) { + IGRAPH_ERRORF("Data vector length (%" IGRAPH_PRId ") 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, (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)[ (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. + */ +igraph_error_t igraph_convex_hull( + const igraph_matrix_t *data, igraph_vector_int_t *resverts, + igraph_matrix_t *rescoords +) { + igraph_integer_t no_of_nodes; + igraph_integer_t i, pivot_idx = 0, last_idx, before_last_idx, next_idx, j; + igraph_vector_t angles; + igraph_vector_int_t order, stack; + igraph_real_t px, py, cp; + + no_of_nodes = igraph_matrix_nrow(data); + if (igraph_matrix_ncol(data) != 2) { + IGRAPH_ERROR("Only two-dimensional point sets are supports, matrix must have two columns.", IGRAPH_EINVAL); + } + if (no_of_nodes == 0) { + if (resverts) { + igraph_vector_int_clear(resverts); + } + if (rescoords) { + IGRAPH_CHECK(igraph_matrix_resize(rescoords, 0, 2)); + } + /**************************** this is an exit here *********/ + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&angles, no_of_nodes); + IGRAPH_VECTOR_INT_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_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_CHECK(igraph_vector_qsort_ind(&angles, &order, IGRAPH_ASCENDING)); + + /* Check if two points have the same angle. If so, keep only the point that + * is farthest from the pivot */ + j = 0; + last_idx = VECTOR(order)[0]; + pivot_idx = VECTOR(order)[no_of_nodes - 1]; + for (i = 1; i < no_of_nodes; i++) { + next_idx = 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_int_empty(&order)) { + next_idx = igraph_vector_int_tail(&order); + if (next_idx < 0) { + /* This vertex should be skipped; was excluded in an earlier step */ + igraph_vector_int_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)); + } + + if (cp < 0) { + /* We are turning into the right direction */ + igraph_vector_int_pop_back(&order); + IGRAPH_CHECK(igraph_vector_int_push_back(&stack, next_idx)); + before_last_idx = last_idx; + last_idx = next_idx; + j++; + } else { + /* No, skip back and try again in the next iteration */ + igraph_vector_int_pop_back(&stack); + j--; + last_idx = before_last_idx; + before_last_idx = (j >= 2) ? VECTOR(stack)[j - 2] : -1; + } + } + + /* Create result vector */ + if (resverts != 0) { + igraph_vector_int_clear(resverts); + IGRAPH_CHECK(igraph_vector_int_append(resverts, &stack)); + } + if (rescoords != 0) { + igraph_matrix_select_rows(data, rescoords, &stack); + } + + /* Free everything */ + igraph_vector_int_destroy(&order); + igraph_vector_int_destroy(&stack); + igraph_vector_destroy(&angles); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_expand_path_to_pairs + * \brief Helper function to convert a sequence of vertex IDs describing a path into a "pairs" vector. + * + * + * This function is useful when you have a sequence of vertex IDs in a graph and + * you would like to retrieve the IDs of the edges between them. The function + * duplicates all but the first and the last elements in the vector, effectively + * converting the path into a vector of vertex IDs that can be passed to + * \ref igraph_get_eids(). + * + * \param path the input vector. It will be modified in-place and it will be + * resized as needed. When the vector contains less than two vertex IDs, + * it will be cleared. + * \return Error code: \c IGRAPH_ENOMEM if there is not enough memory to expand + * the vector. + */ +igraph_error_t igraph_expand_path_to_pairs(igraph_vector_int_t* path) { + igraph_integer_t no_of_vertices = igraph_vector_int_size(path); + igraph_integer_t i, j, no_of_items = (no_of_vertices - 1) * 2; + + if (no_of_vertices <= 1) { + igraph_vector_int_clear(path); + } else { + IGRAPH_CHECK(igraph_vector_int_resize(path, no_of_items)); + + i = no_of_vertices - 1; + j = no_of_items - 1; + VECTOR(*path)[j] = VECTOR(*path)[i]; + while (i > 1) { + i--; j--; + VECTOR(*path)[j] = VECTOR(*path)[i]; + j--; + VECTOR(*path)[j] = VECTOR(*path)[i]; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vertex_path_from_edge_path + * \brief Converts a path of edge IDs to the traversed vertex IDs. + * + * + * This function is useful when you have a sequence of edge IDs representing a + * continuous path in a graph and you would like to obtain the vertex IDs that + * the path traverses. The function is used implicitly by several shortest path + * related functions to convert a path of edge IDs to the corresponding + * representation that describes the path in terms of vertex IDs instead. + * + * \param graph the graph that the edge IDs refer to + * \param start the start vertex of the path + * \param edge_path the sequence of edge IDs that describe the path + * \param vertex_path the sequence of vertex IDs traversed will be returned here + * \return Error code: \c IGRAPH_ENOMEM if there is not enough memory, + * \c IGRAPH_EINVAL if the edge path does not start at the given vertex + * or if there is at least one edge whose start vertex does not match + * the end vertex of the previous edge + */ +igraph_error_t igraph_vertex_path_from_edge_path( + const igraph_t *graph, igraph_integer_t start, + const igraph_vector_int_t *edge_path, igraph_vector_int_t *vertex_path, + igraph_neimode_t mode +) { + igraph_integer_t i, no_of_edges; + igraph_bool_t directed = igraph_is_directed(graph); + igraph_bool_t next_edge_ok; + igraph_integer_t next_start; + + igraph_vector_int_clear(vertex_path); + + no_of_edges = igraph_vector_int_size(edge_path); + IGRAPH_CHECK(igraph_vector_int_reserve(vertex_path, no_of_edges + 1)); + + if (!directed) { + mode = IGRAPH_ALL; + } + + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, VECTOR(*edge_path)[i]); + igraph_integer_t to = IGRAPH_TO(graph, VECTOR(*edge_path)[i]); + + igraph_vector_int_push_back(vertex_path, start); /* reserved */ + + switch (mode) { + case IGRAPH_OUT: + next_edge_ok = from == start; + next_start = to; + break; + + case IGRAPH_IN: + next_edge_ok = to == start; + next_start = from; + break; + + case IGRAPH_ALL: + if (from == start) { + next_edge_ok = true; + next_start = to; + } else if (to == start) { + next_edge_ok = true; + next_start = from; + } else { + next_edge_ok = false; + } + break; + + default: + IGRAPH_ERROR("Invalid neighborhood mode.", IGRAPH_EINVMODE); + } + + if (!next_edge_ok) { + IGRAPH_ERROR("Edge IDs do not form a continuous path.", IGRAPH_EINVAL); + } + + start = next_start; + } + + igraph_vector_int_push_back(vertex_path, start); /* reserved */ + + return IGRAPH_SUCCESS; +} diff --git a/src/misc/power_law_fit.c b/src/misc/power_law_fit.c new file mode 100644 index 0000000..e9e036c --- /dev/null +++ b/src/misc/power_law_fit.c @@ -0,0 +1,328 @@ +/* -*- 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 "plfit/plfit_error.h" +#include "plfit/plfit.h" + +#include + +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; +} + +static void igraph_i_plfit_prepare_continuous_options( + plfit_continuous_options_t* options, igraph_bool_t finite_size_correction +) { + plfit_continuous_options_init(options); + options->p_value_method = PLFIT_P_VALUE_SKIP; + options->xmin_method = PLFIT_STRATIFIED_SAMPLING; + options->finite_size_correction = (plfit_bool_t) finite_size_correction; +} + +static void igraph_i_plfit_prepare_discrete_options( + plfit_discrete_options_t* options, igraph_bool_t finite_size_correction +) { + plfit_discrete_options_init(options); + options->p_value_method = PLFIT_P_VALUE_SKIP; + options->finite_size_correction = (plfit_bool_t) finite_size_correction; +} + +/* Decides whether to use finite size correction for the given input data */ +static igraph_bool_t igraph_i_plfit_should_use_finite_size_correction(const igraph_vector_t* data) { + return igraph_vector_size(data) < 50; +} + +static igraph_error_t igraph_i_handle_plfit_error(int code) { + switch (code) { + case PLFIT_SUCCESS: + return IGRAPH_SUCCESS; + + 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); /* LCOV_EXCL_LINE */ + break; + + case PLFIT_OVERFLOW: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EOVERFLOW); /* LCOV_EXCL_LINE */ + break; + + case PLFIT_ENOMEM: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + + case PLFIT_EMAXITER: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_DIVERGED); /* LCOV_EXCL_LINE */ + break; + + default: + IGRAPH_ERRORF("Unknown error code returned from plfit (%d)", IGRAPH_FAILURE, code); + break; + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * https://doi.org/10.1137/070710111 + * + * \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. Note that the p-value of the fit is \em not + * calculated by default as it is time-consuming; you need to call + * \ref igraph_plfit_result_calculate_p_value() to calculate the + * p-value itself + * \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 + */ +igraph_error_t 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 ? false : true; + igraph_bool_t finite_size_correction; + + int retval; + size_t i, n; + + finite_size_correction = igraph_i_plfit_should_use_finite_size_correction(data); + n = (size_t) igraph_vector_size(data); + + if (discrete) { + /* Does the vector contain discrete values only? */ + for (i = 0; i < n; i++) { + if (trunc(VECTOR(*data)[i]) != VECTOR(*data)[i]) { + discrete = false; + break; + } + } + } + + RNG_BEGIN(); + + plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); + if (discrete) { + igraph_i_plfit_prepare_discrete_options(&disc_options, 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 { + igraph_i_plfit_prepare_continuous_options(&cont_options, 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(); + + IGRAPH_CHECK(igraph_i_handle_plfit_error(retval)); + + if (result) { + result->data = data; + result->continuous = !discrete; + result->alpha = plfit_result.alpha; + result->xmin = plfit_result.xmin; + result->L = plfit_result.L; + result->D = plfit_result.D; + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup nongraph + * \function igraph_plfit_result_calculate_p_value + * \brief Calculates the p-value of a fitted power-law model. + * + * + * The p-value is calculated by resampling the input data many times in a way + * that the part below the fitted \c x_min threshold is resampled from the + * input data itself, while the part above the fitted \c x_min threshold is + * drawn from the fitted power-law function. A Kolmogorov-Smirnov test is then + * performed for each resampled dataset and its test statistic is compared with the + * observed test statistic from the original dataset. The fraction of resampled + * datasets that have a \em higher test statistic is the returned p-value. + * + * + * Note that the precision of the returned p-value depends on the number of + * resampling attempts. The number of resampling trials is determined by + * 0.25 divided by the square of the required precision. For instance, a required + * precision of 0.01 means that 2500 samples will be drawn. + * + * + * If igraph is compiled with OpenMP support, this function will use parallel + * OpenMP threads for the resampling. Each OpenMP thread gets its own instance + * of a random number generator. However, since the scheduling of OpenMP threads + * is outside our control, we cannot guarantee how many resampling instances the + * threads are asked to execute, thus it may happen that the random number + * generators are used differently between runs. If you want to obtain + * reproducible results, seed igraph's master RNG appropriately, and force the + * number of OpenMP threads to 1 early in your program, either by calling + * omp_set_num_threads(1) or by setting the value of the \c OMP_NUM_THREADS + * environment variable to 1. + * + * \param model The fitted power-law model from the \ref igraph_power_law_fit() + * function + * \param result The calculated p-value is returned here + * \param precision The desired precision of the p-value. Higher values correspond + * to longer calculation time. + * @return igraph_error_t + */ +igraph_error_t igraph_plfit_result_calculate_p_value( + const igraph_plfit_result_t* model, igraph_real_t* result, igraph_real_t precision +) { + int retval; + plfit_continuous_options_t cont_options; + plfit_discrete_options_t disc_options; + plfit_result_t plfit_result; + plfit_error_handler_t* plfit_stored_error_handler; + igraph_bool_t finite_size_correction; + + IGRAPH_ASSERT(model != NULL); + + plfit_result.alpha = model->alpha; + plfit_result.xmin = model->xmin; + plfit_result.L = model->L; + plfit_result.D = model->D; + + finite_size_correction = igraph_i_plfit_should_use_finite_size_correction(model->data); + + RNG_BEGIN(); + + plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); + if (model->continuous) { + igraph_i_plfit_prepare_continuous_options(&cont_options, finite_size_correction); + cont_options.p_value_method = PLFIT_P_VALUE_EXACT; + cont_options.p_value_precision = precision; + retval = plfit_calculate_p_value_continuous( + VECTOR(*model->data), (size_t) igraph_vector_size(model->data), + &cont_options, /* xmin_fixed = */ 0, &plfit_result + ); + } else { + igraph_i_plfit_prepare_discrete_options(&disc_options, finite_size_correction); + disc_options.p_value_method = PLFIT_P_VALUE_EXACT; + disc_options.p_value_precision = precision; + retval = plfit_calculate_p_value_discrete( + VECTOR(*model->data), (size_t) igraph_vector_size(model->data), + &disc_options, /* xmin_fixed = */ 0, &plfit_result + ); + } + plfit_set_error_handler(plfit_stored_error_handler); + + RNG_END(); + + IGRAPH_CHECK(igraph_i_handle_plfit_error(retval)); + + if (result) { + *result = plfit_result.p; + } + + return IGRAPH_SUCCESS; +} diff --git a/src/misc/scan.c b/src/misc/scan.c new file mode 100644 index 0000000..6e96595 --- /dev/null +++ b/src/misc/scan.c @@ -0,0 +1,806 @@ +/* -*- 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_dqueue.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" + +/** + * \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. + * + */ + +igraph_error_t igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + return igraph_strength(graph, res, igraph_vss_all(), mode, /*loops=*/ 1, + weights); +} + +/* This removes loop, multiple edges and edges that point + "backwards" according to the rank vector. It works on + edge lists */ + +static igraph_error_t igraph_i_trans4_il_simplify(const igraph_t *graph, igraph_inclist_t *il, + const igraph_vector_int_t *rank) { + + igraph_integer_t i; + igraph_integer_t n = il->length; + igraph_vector_int_t mark; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, n); + + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = &il->incs[i]; + igraph_integer_t j, l = igraph_vector_int_size(v); + igraph_integer_t irank = VECTOR(*rank)[i]; + VECTOR(mark)[i] = i + 1; + for (j = 0; j < l; /* nothing */) { + igraph_integer_t edge = VECTOR(*v)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; + +} + +/* This one handles both weighted and unweighted cases */ + +static igraph_error_t igraph_i_local_scan_1_directed(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_inclist_t incs; + igraph_integer_t 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_FINALLY(&neis, no_of_nodes); + + 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 *edges1 = igraph_inclist_get(&incs, node); + igraph_integer_t edgeslen1 = igraph_vector_int_size(edges1); + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Mark neighbors and self */ + VECTOR(neis)[node] = node + 1; + for (i = 0; i < edgeslen1; i++) { + igraph_integer_t e = VECTOR(*edges1)[i]; + igraph_integer_t 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++) { + igraph_integer_t e2 = VECTOR(*edges1)[i]; + igraph_integer_t nei = IGRAPH_OTHER(graph, e2, node); + if (nei == node) { + break; + } + igraph_vector_int_t *edges2 = igraph_inclist_get(&incs, nei); + igraph_integer_t j, edgeslen2 = igraph_vector_int_size(edges2); + for (j = 0; j < edgeslen2; j++) { + igraph_integer_t e2 = VECTOR(*edges2)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_local_scan_1_directed_all(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_inclist_t incs; + igraph_integer_t i, node; + + igraph_vector_int_t neis; + + IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, no_of_nodes); + + 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 *edges1 = igraph_inclist_get(&incs, node); + igraph_integer_t edgeslen1 = igraph_vector_int_size(edges1); + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Mark neighbors. We also count the edges that are incident on 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++) { + igraph_integer_t e = VECTOR(*edges1)[i]; + igraph_integer_t nei = IGRAPH_OTHER(graph, e, node); + igraph_real_t w = weights ? VECTOR(*weights)[e] : 1; + VECTOR(neis)[nei] = node + 1; + VECTOR(*res)[node] += w; + } + + /* Explicitly unmark ego in case it had a loop edge */ + VECTOR(neis)[node] = 0; + + /* Crawl neighbors. We make sure that each neighbor of 'node' is + only crawled once. We count all qualifying edges of ego, and + then unmark ego to avoid double counting. */ + for (i = 0; i < edgeslen1; i++) { + igraph_integer_t e2 = VECTOR(*edges1)[i]; + igraph_integer_t nei = IGRAPH_OTHER(graph, e2, node); + igraph_vector_int_t *edges2; + igraph_integer_t 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++) { + igraph_integer_t e2 = VECTOR(*edges2)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + * + */ + +igraph_error_t 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 { + return igraph_local_scan_k_ecount(graph, 1, res, weights, mode); + } +} + +static igraph_error_t 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_int_t map2; + igraph_vector_t weights; + igraph_integer_t 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_INT_INIT_FINALLY(&map2, 0); + IGRAPH_CHECK(igraph_intersection(&is, us, them, /* edge_map1= */ 0, &map2)); + IGRAPH_FINALLY(igraph_destroy, &is); + + /* Rewrite the map as edge weights */ + m = igraph_vector_int_size(&map2); + IGRAPH_VECTOR_INIT_FINALLY(&weights, m); + for (i = 0; i < m; i++) { + VECTOR(weights)[i] = VECTOR(*weights_them)[ VECTOR(map2)[i] ]; + } + + IGRAPH_CHECK(igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, + /*weights=*/ &weights)); + + igraph_destroy(&is); + igraph_vector_int_destroy(&map2); + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + */ + +igraph_error_t 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_CHECK(igraph_intersection(&is, us, them, /*edge_map1=*/ 0, /*edge_map2=*/ 0)); + IGRAPH_FINALLY(igraph_destroy, &is); + + IGRAPH_CHECK(igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, /* weights = */ 0)); + + igraph_destroy(&is); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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) { + + igraph_integer_t no_of_nodes = igraph_vcount(us); + igraph_adjlist_t adj_us; + igraph_inclist_t incs_them; + igraph_vector_int_t neis; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&neis, no_of_nodes); + + 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); + igraph_integer_t len1_us = igraph_vector_int_size(neis_us); + igraph_integer_t len1_them = igraph_vector_int_size(edges1_them); + igraph_integer_t i; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Mark neighbors and self in us */ + VECTOR(neis)[node] = node + 1; + for (i = 0; i < len1_us; i++) { + igraph_integer_t nei = VECTOR(*neis_us)[i]; + VECTOR(neis)[nei] = node + 1; + } + + /* Crawl neighbors in them, first ego */ + for (i = 0; i < len1_them; i++) { + igraph_integer_t e = VECTOR(*edges1_them)[i]; + igraph_integer_t 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++) { + igraph_integer_t nei = VECTOR(*neis_us)[i]; + igraph_vector_int_t *edges2_them = igraph_inclist_get(&incs_them, nei); + igraph_integer_t j, len2_them = igraph_vector_int_size(edges2_them); + for (j = 0; j < len2_them; j++) { + igraph_integer_t e2 = VECTOR(*edges2_them)[j]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} + +/** + * \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. + * + */ + +igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_t k, + igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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 (%" IGRAPH_PRId ") in k-scan should equal " + "the number of edges of the graph (%" IGRAPH_PRId ").", + 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_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); + 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_CHECK(igraph_dqueue_int_push(&Q, node)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); + VECTOR(marked)[node] = node + 1; + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&Q); + igraph_integer_t dist = igraph_dqueue_int_pop(&Q) + 1; + igraph_vector_int_t *edges = igraph_inclist_get(&incs, act); + igraph_integer_t i, edgeslen = igraph_vector_int_size(edges); + for (i = 0; i < edgeslen; i++) { + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t 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_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(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 + * \brief Local THEM scan-statistics, edge count or 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. + */ + +igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, + igraph_integer_t k, igraph_vector_t *res, + const igraph_vector_t *weights_them, + igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(us); + igraph_integer_t 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("The number of vertices in the two graphs must " + "match in scan-k.", + IGRAPH_EINVAL); + } + if (igraph_is_directed(us) != igraph_is_directed(them)) { + IGRAPH_ERROR("Directedness in the two graphs must match " + "in scan-k", IGRAPH_EINVAL); + } + if (k < 0) { + IGRAPH_ERRORF("k must be non-negative in k-scan, got %" IGRAPH_PRId + ".", IGRAPH_EINVAL, k); + } + if (weights_them && + igraph_vector_size(weights_them) != igraph_ecount(them)) { + IGRAPH_ERRORF("The weight vector length (%" IGRAPH_PRId + ") must be equal to the number of edges (%" IGRAPH_PRId + ").", IGRAPH_EINVAL, igraph_vector_size(weights_them), + igraph_ecount(them)); + } + + 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_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); + 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)) { + igraph_integer_t act = igraph_dqueue_int_pop(&Q); + igraph_integer_t dist = igraph_dqueue_int_pop(&Q) + 1; + igraph_vector_int_t *edges = igraph_inclist_get(&incs_us, act); + igraph_integer_t i, edgeslen = igraph_vector_int_size(edges); + for (i = 0; i < edgeslen; i++) { + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t nei = IGRAPH_OTHER(us, edge, act); + if (dist <= k && VECTOR(marked)[nei] != node + 1) { + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, dist)); + VECTOR(marked)[nei] = node + 1; + IGRAPH_CHECK(igraph_stack_int_push(&ST, nei)); + } + } + } + + /* Now check the edges of all nodes in THEM */ + while (!igraph_stack_int_empty(&ST)) { + igraph_integer_t act = igraph_stack_int_pop(&ST); + igraph_vector_int_t *edges = igraph_inclist_get(&incs_them, act); + igraph_integer_t i, edgeslen = igraph_vector_int_size(edges); + for (i = 0; i < edgeslen; i++) { + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t 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 IGRAPH_SUCCESS; +} +/** + * \function igraph_local_scan_subset_ecount + * \brief Local scan-statistics of subgraphs induced by subsets of vertices. + * + * Count the number of edges, or sum the edge weights in + * induced subgraphs from vertices 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 subsets List of \type igraph_vector_int_t + * objects, the vertex subsets. + * \return Error code. + */ + +igraph_error_t igraph_local_scan_subset_ecount(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + const igraph_vector_int_list_t *subsets) { + + igraph_integer_t subset, no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_subsets = igraph_vector_int_list_size(subsets); + 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); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); + IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_OUT, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &incs); + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_subsets)); + igraph_vector_null(res); + + for (subset = 0; subset < no_of_subsets; subset++) { + igraph_vector_int_t *nei = igraph_vector_int_list_get_ptr(subsets, subset); + igraph_integer_t i, neilen = igraph_vector_int_size(nei); + for (i = 0; i < neilen; i++) { + igraph_integer_t 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] = subset + 1; + } + + for (i = 0; i < neilen; i++) { + igraph_integer_t vertex = VECTOR(*nei)[i]; + igraph_vector_int_t *edges = igraph_inclist_get(&incs, vertex); + igraph_integer_t j, edgeslen = igraph_vector_int_size(edges); + for (j = 0; j < edgeslen; j++) { + igraph_integer_t edge = VECTOR(*edges)[j]; + igraph_integer_t nei2 = IGRAPH_OTHER(graph, edge, vertex); + if (VECTOR(marked)[nei2] == subset + 1) { + igraph_real_t w = weights ? VECTOR(*weights)[edge] : 1; + VECTOR(*res)[subset] += w; + } + } + } + if (!directed) { + VECTOR(*res)[subset] /= 2.0; + } + } + + igraph_inclist_destroy(&incs); + igraph_vector_int_destroy(&marked); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_local_scan_neighborhood_ecount + * Local scan-statistics with pre-calculated neighborhoods + * + * Count the number of edges, or sum the edge weights in + * neighborhoods given as a parameter. + * + * \deprecated-by igraph_local_scan_subset_ecount 0.10.0 + * + * \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 \type igraph_vector_int_t + * objects, the neighborhoods, one for each vertex in the + * graph. + * \return Error code. + */ + +igraph_error_t igraph_local_scan_neighborhood_ecount(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + const igraph_vector_int_list_t *neighborhoods) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (igraph_vector_int_list_size(neighborhoods) != no_of_nodes) { + IGRAPH_ERROR("Invalid neighborhood list length in local scan.", + IGRAPH_EINVAL); + } + + return igraph_local_scan_subset_ecount(graph, res, weights, neighborhoods); +} diff --git a/src/misc/sir.c b/src/misc/sir.c new file mode 100644 index 0000000..0e63307 --- /dev/null +++ b/src/misc/sir.c @@ -0,0 +1,269 @@ +/* -*- 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" + +igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \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) { + igraph_integer_t 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 neighbors. 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|))). + */ + +igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, + igraph_real_t gamma, igraph_integer_t no_sim, + igraph_vector_ptr_t *result) { + + igraph_integer_t infected; + igraph_vector_int_t status; + igraph_adjlist_t adjlist; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, ns, ni, nr; + igraph_vector_int_t *neis; + igraph_psumtree_t tree; + igraph_real_t psum; + igraph_integer_t neilen; + igraph_bool_t simple; + + if (no_of_nodes == 0) { + IGRAPH_ERROR("Cannot run SIR model on empty graph.", IGRAPH_EINVAL); + } + 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); + } + if (igraph_is_directed(graph)) { + igraph_bool_t has_mutual; + IGRAPH_WARNING("Edge directions are ignored in SIR model."); + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + 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); /* LCOV_EXCL_LINE */ + } + 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++) { + igraph_integer_t nei = VECTOR(*neis)[i]; + IGRAPH_CHECK(igraph_psumtree_update(&tree, nei, beta)); + } + + while (ni > 0) { + igraph_real_t tt; + igraph_real_t r; + igraph_integer_t 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++) { + igraph_integer_t 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++) { + igraph_integer_t 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/misc/spanning_trees.c b/src/misc/spanning_trees.c new file mode 100644 index 0000000..bd83092 --- /dev/null +++ b/src/misc/spanning_trees.c @@ -0,0 +1,488 @@ +/* + IGraph library. + Copyright (C) 2011-2023 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_adjlist.h" +#include "igraph_bitset.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_operators.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +static igraph_error_t igraph_i_minimum_spanning_tree_unweighted( + const igraph_t *graph, igraph_vector_int_t *result); +static igraph_error_t igraph_i_minimum_spanning_tree_prim( + const igraph_t *graph, igraph_vector_int_t *result, const igraph_vector_t *weights); + +/** + * \ingroup structural + * \function igraph_minimum_spanning_tree + * \brief Calculates one minimum spanning tree of a graph. + * + * Finds a spanning tree of the graph. 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. + * + * + * Directed graphs are considered as undirected for this computation. + * + * + * This function is deterministic, i.e. it always returns the same + * spanning tree. See \ref igraph_random_spanning_tree() for the uniform + * random sampling of spanning trees of a graph. + * + * \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_from_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 + */ +igraph_error_t igraph_minimum_spanning_tree( + const igraph_t *graph, igraph_vector_int_t *res, const igraph_vector_t *weights +) { + if (weights == NULL) { + 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. Edge directions will be ignored. + * \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. + */ + +igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, + igraph_t *mst) { + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_nodes > 0 ? no_of_nodes - 1 : 0); + IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, &edges)); + IGRAPH_CHECK(igraph_subgraph_from_edges( + graph, mst, igraph_ess_vector(&edges), /* delete_vertices = */ false)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_minimum_spanning_tree_prim + * \brief Calculates one minimum spanning tree of a weighted graph. + * + * Finds a spanning tree or spanning forest for which the sum of edge + * weights is the smallest. This function uses Prim's method for carrying + * out the computation. + * + * + * Directed graphs are considered as undirected for this computation. + * + * + * Reference: + * + * + * Prim, R.C.: Shortest connection networks and some + * generalizations, Bell System Technical + * Journal, Vol. 36, + * 1957, 1389--1401. + * https://doi.org/10.1002/j.1538-7305.1957.tb01515.x + * + * \param graph The graph object. Edge directions will be ignored. + * \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 + */ + +igraph_error_t igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, + const igraph_vector_t *weights) { + igraph_vector_int_t edges; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, igraph_vcount(graph) - 1); + IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, &edges, weights)); + IGRAPH_CHECK(igraph_subgraph_from_edges( + graph, mst, igraph_ess_vector(&edges), /* delete_vertices = */ false)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_minimum_spanning_tree_unweighted(const igraph_t* graph, igraph_vector_int_t* res) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bitset_t already_added, added_edges; + + igraph_dqueue_int_t q; + igraph_vector_int_t eids; + + igraph_vector_int_clear(res); + + IGRAPH_BITSET_INIT_FINALLY(&added_edges, no_of_edges); + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + /* Perform a BFS */ + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (IGRAPH_BIT_TEST(already_added, i)) { + continue; + } + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_BIT_SET(already_added, i); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + while (! igraph_dqueue_int_empty(&q)) { + igraph_integer_t eids_size; + igraph_integer_t act_node = igraph_dqueue_int_pop(&q); + IGRAPH_CHECK(igraph_incident(graph, &eids, act_node, + IGRAPH_ALL)); + eids_size = igraph_vector_int_size(&eids); + for (igraph_integer_t j = 0; j < eids_size; j++) { + igraph_integer_t edge = VECTOR(eids)[j]; + if (! IGRAPH_BIT_TEST(added_edges, edge)) { + igraph_integer_t to = IGRAPH_OTHER(graph, edge, act_node); + if (! IGRAPH_BIT_TEST(already_added, to)) { + IGRAPH_BIT_SET(already_added, to); + IGRAPH_BIT_SET(added_edges, edge); + IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, to)); + } + } + } + } + } + + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&eids); + igraph_bitset_destroy(&already_added); + igraph_bitset_destroy(&added_edges); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_minimum_spanning_tree_prim( + const igraph_t* graph, igraph_vector_int_t* res, const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bitset_t already_added, added_edges; + + igraph_d_indheap_t heap; + const igraph_neimode_t mode = IGRAPH_ALL; + + igraph_vector_int_t adj; + + igraph_vector_int_clear(res); + + if (weights == NULL) { + return igraph_i_minimum_spanning_tree_unweighted(graph, res); + } + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Weight vector length does not match number of edges.", IGRAPH_EINVAL); + } + + if (igraph_vector_is_any_nan(weights)) { + IGRAPH_ERROR("Weigths must not contain NaN values.", IGRAPH_EINVAL); + } + + IGRAPH_BITSET_INIT_FINALLY(&added_edges, no_of_edges); + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); + + IGRAPH_CHECK(igraph_d_indheap_init(&heap, 0)); + IGRAPH_FINALLY(igraph_d_indheap_destroy, &heap); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&adj, 0); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t adj_size; + if (IGRAPH_BIT_TEST(already_added, i)) { + continue; + } + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_BIT_SET(already_added, i); + /* add all edges of the first vertex */ + IGRAPH_CHECK(igraph_incident(graph, &adj, i, mode)); + adj_size = igraph_vector_int_size(&adj); + for (igraph_integer_t j = 0; j < adj_size; j++) { + igraph_integer_t edgeno = VECTOR(adj)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edgeno, i); + if (! IGRAPH_BIT_TEST(already_added, neighbor)) { + IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], i, edgeno)); + } + } + + while (! igraph_d_indheap_empty(&heap)) { + /* Get minimal edge */ + igraph_integer_t from, edge; + igraph_d_indheap_max_index(&heap, &from, &edge); + + /* Erase it */ + igraph_d_indheap_delete_max(&heap); + + /* Is this edge already included? */ + if (! IGRAPH_BIT_TEST(added_edges, edge)) { + igraph_integer_t to = IGRAPH_OTHER(graph, edge, from); + + /* Does it point to a visited node? */ + if (! IGRAPH_BIT_TEST(already_added, to)) { + IGRAPH_BIT_SET(already_added, to); + IGRAPH_BIT_SET(added_edges, edge); + IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); + /* add all outgoing edges */ + IGRAPH_CHECK(igraph_incident(graph, &adj, to, mode)); + adj_size = igraph_vector_int_size(&adj); + for (igraph_integer_t j = 0; j < adj_size; j++) { + igraph_integer_t edgeno = VECTOR(adj)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edgeno, to); + if (! IGRAPH_BIT_TEST(already_added, neighbor)) { + 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_vector_int_destroy(&adj); + igraph_d_indheap_destroy(&heap); + igraph_bitset_destroy(&already_added); + igraph_bitset_destroy(&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 igraph_error_t igraph_i_lerw(const igraph_t *graph, igraph_vector_int_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_int_reserve(res, igraph_vector_int_size(res) + comp_size - 1)); + + VECTOR(*visited)[start] = true; + visited_count = 1; + + RNG_BEGIN(); + + while (visited_count < comp_size) { + igraph_integer_t 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_int_push_back(res, edge)); + VECTOR(*visited)[start] = true; + visited_count++; + } + + IGRAPH_ALLOW_INTERRUPTION(); + } + + RNG_END(); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_random_spanning_tree + * \brief Uniformly samples 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_from_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() + * + */ +igraph_error_t igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_int_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_int_clear(res); + + if (vid < 0) { /* generate random spanning forest: consider each component separately */ + igraph_vector_int_t membership, csize; + igraph_integer_t comp_count; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&csize, 0); + + IGRAPH_CHECK(igraph_connected_components(graph, &membership, &csize, &comp_count, IGRAPH_WEAK)); + + /* for each component ... */ + for (igraph_integer_t 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, VECTOR(csize)[i], &visited, &il)); + } + + igraph_vector_int_destroy(&membership); + igraph_vector_int_destroy(&csize); + IGRAPH_FINALLY_CLEAN(2); + } else { /* consider the component containing vid */ + igraph_vector_int_t comp_vertices; + igraph_integer_t comp_size; + + /* we measure the size of the component */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&comp_vertices, 0); + IGRAPH_CHECK(igraph_subcomponent(graph, &comp_vertices, vid, IGRAPH_ALL)); + comp_size = igraph_vector_int_size(&comp_vertices); + igraph_vector_int_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/operators/add_edge.c b/src/operators/add_edge.c new file mode 100644 index 0000000..1d082de --- /dev/null +++ b/src/operators/add_edge.c @@ -0,0 +1,63 @@ +/* -*- 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. + */ +igraph_error_t igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to) { + igraph_vector_int_t edges; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2); + + VECTOR(edges)[0] = from; + VECTOR(edges)[1] = to; + IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/operators/complementer.c b/src/operators/complementer.c new file mode 100644 index 0000000..f6a41c6 --- /dev/null +++ b/src/operators/complementer.c @@ -0,0 +1,102 @@ +/* -*- 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 Creates 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 + */ +igraph_error_t igraph_complementer(igraph_t *res, const igraph_t *graph, + igraph_bool_t loops) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t edges; + igraph_vector_int_t neis; + igraph_integer_t i, j; + igraph_integer_t zero = 0, *limit; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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, i, IGRAPH_OUT)); + if (loops) { + for (j = no_of_nodes - 1; j >= *limit; j--) { + if (igraph_vector_int_empty(&neis) || j > igraph_vector_int_tail(&neis)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + } else { + igraph_vector_int_pop_back(&neis); + } + } + } else { + for (j = no_of_nodes - 1; j >= *limit; j--) { + if (igraph_vector_int_empty(&neis) || j > igraph_vector_int_tail(&neis)) { + if (i != j) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + } + } else { + igraph_vector_int_pop_back(&neis); + } + } + } + } + + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&neis); + IGRAPH_I_ATTRIBUTE_DESTROY(res); + IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/true, /*vertex=*/true, /*edge=*/false); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} diff --git a/src/operators/compose.c b/src/operators/compose.c new file mode 100644 index 0000000..6c69f4b --- /dev/null +++ b/src/operators/compose.c @@ -0,0 +1,134 @@ +/* -*- 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 graph + * contains an (i,k) edge and the second graph a (k,j) edge. + * + * This is of course exactly the composition of two + * binary relations. + * + * The two graphs must have the same directedness, + * otherwise the function returns with an error. + * 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 + */ +igraph_error_t igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2) { + + igraph_integer_t no_of_nodes_left = igraph_vcount(g1); + igraph_integer_t no_of_nodes_right = igraph_vcount(g2); + igraph_integer_t no_of_nodes; + igraph_bool_t directed = igraph_is_directed(g1); + igraph_vector_int_t edges; + igraph_vector_int_t neis1, neis2; + igraph_integer_t 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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis2, 0); + + if (edge_map1) { + igraph_vector_int_clear(edge_map1); + } + if (edge_map2) { + igraph_vector_int_clear(edge_map2); + } + + for (i = 0; i < no_of_nodes_left; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_CHECK(igraph_incident(g1, &neis1, i, + IGRAPH_OUT)); + while (!igraph_vector_int_empty(&neis1)) { + igraph_integer_t con = igraph_vector_int_pop_back(&neis1); + igraph_integer_t v1 = IGRAPH_OTHER(g1, con, i); + if (v1 < no_of_nodes_right) { + IGRAPH_CHECK(igraph_incident(g2, &neis2, v1, + IGRAPH_OUT)); + } else { + continue; + } + while (!igraph_vector_int_empty(&neis2)) { + igraph_integer_t con2 = igraph_vector_int_pop_back(&neis2); + igraph_integer_t v2 = IGRAPH_OTHER(g2, con2, v1); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v2)); + if (edge_map1) { + IGRAPH_CHECK(igraph_vector_int_push_back(edge_map1, con)); + } + if (edge_map2) { + IGRAPH_CHECK(igraph_vector_int_push_back(edge_map2, con2)); + } + } + } + } + + igraph_vector_int_destroy(&neis1); + igraph_vector_int_destroy(&neis2); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} diff --git a/src/operators/connect_neighborhood.c b/src/operators/connect_neighborhood.c new file mode 100644 index 0000000..ce56ffe --- /dev/null +++ b/src/operators/connect_neighborhood.c @@ -0,0 +1,322 @@ +/* -*- 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_adjlist.h" +#include "igraph_error.h" +#include "igraph_operators.h" + +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "graph/attributes.h" + +/** + * \function igraph_connect_neighborhood + * \brief Connects each 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). + * + * + * 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. It will be modified in-place. + * \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_graph_power() to compute the kth power of a graph; + * \ref igraph_square_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. + */ +igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, + igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vector_int_t edges; + igraph_integer_t i, j, in; + igraph_integer_t *added; + igraph_vector_int_t neis; + + if (order < 0) { + IGRAPH_ERRORF("Order must not be negative, found %" IGRAPH_PRId ".", + IGRAPH_EINVAL, order); + } + + if (order < 2) { + IGRAPH_WARNING("Order smaller than two, graph will be unchanged."); + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Cannot connect neighborhood."); + + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + for (i = 0; i < no_of_nodes; i++) { + added[i] = i + 1; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, mode)); + in = igraph_vector_int_size(&neis); + if (order > 1) { + for (j = 0; j < in; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 1)); + } + } + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_size(&neis); + + if (actdist < order - 1) { + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + if (mode != IGRAPH_ALL || i < nei) { + if (mode == IGRAPH_IN) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + } else { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + } + } + } + } + } else { + for (j = 0; j < n; j++) { + igraph_integer_t nei = 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_int_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + } else { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + } + } + } + } + } + + } /* while q not empty */ + } /* for i < no_of_nodes */ + + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&q); + igraph_free(added); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_graph_power + * \brief The kth power of a graph. + * + * \experimental + * + * The kth power of a graph G is a simple graph where vertex \c u is connected to + * \c v by a single edge if \c v is reachable from \c u in G within at most k steps. + * By convention, the zeroth power of a graph has no edges. The first power is + * identical to the original graph, except that multiple edges and self-loops + * are removed. + * + * + * Graph power is usually defined only for undirected graphs. igraph extends the concept + * to directed graphs. To ignore edge directions in the input, set the \p directed + * parameter to \c false. In this case, the result will be an undirected graph. + * + * + * Graph and vertex attributes are preserved, but edge attributes are discarded. + * + * \param graph The input graph. + * \param res The graph power of the given \p order. + * \param order Non-negative integer, the power to raise the graph to. + * In other words, vertices within a distance \p order will be connected. + * \param directed Logical, whether to take edge directions into account. + * \return Error code. + * + * \sa \ref igraph_connect_neighborhood() to connect each vertex to its + * neighborhood, modifying a graph in-place. + * + * 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. + */ +igraph_error_t igraph_graph_power(const igraph_t *graph, igraph_t *res, + igraph_integer_t order, igraph_bool_t directed) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t edges; + igraph_adjlist_t al; + igraph_bool_t dir = igraph_is_directed(graph) && directed; + igraph_neimode_t mode = dir ? IGRAPH_OUT : IGRAPH_ALL; + + if (order < 0) { + IGRAPH_ERRORF("Order must not be negative, found %" IGRAPH_PRId ".", + IGRAPH_EINVAL, order); + } + + IGRAPH_CHECK(igraph_empty(res, no_of_nodes, dir)); + IGRAPH_I_ATTRIBUTE_DESTROY(res); + IGRAPH_I_ATTRIBUTE_COPY(res, graph, /* graph */ true, /* vertex */ true, /* edge */ false); + if (order == 0) { + return IGRAPH_SUCCESS; + } + + /* Initialize res with a copy of the graph, but with multi-edges and self-loops removed. + * Also convert the graph to undirected if this is requested. */ + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + + /* Reserve initial space for no_of_edges. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges); + igraph_vector_int_clear(&edges); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *tmp = igraph_adjlist_get(&al, i); + for (igraph_integer_t j = 0; j < igraph_vector_int_size(tmp); j++) { + if (dir || i < VECTOR(*tmp)[j]) { + igraph_vector_int_push_back(&edges, i); + igraph_vector_int_push_back(&edges, VECTOR(*tmp)[j]); + } + } + } + + if (order > 1) { + /* order > 1, so add more edges. */ + + igraph_integer_t d_i, d_actnode; + igraph_integer_t *added; + const igraph_vector_int_t *neis; + igraph_dqueue_int_t q; + + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Insufficient memory for graph power."); + IGRAPH_FINALLY(igraph_free, added); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + added[i] = i + 1; + neis = igraph_adjlist_get(&al, i); + d_i = igraph_vector_int_size(neis); + + for (igraph_integer_t j = 0; j < d_i; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 1)); + } + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + + neis = igraph_adjlist_get(&al, actnode); + d_actnode = igraph_vector_int_size(neis); + + if (actdist < order - 1) { + for (igraph_integer_t j = 0; j < d_actnode; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + if (dir || i < nei) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + } + } + } + } else { + for (igraph_integer_t j = 0; j < d_actnode; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (dir || i < nei) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + } + } + } + } + + } /* while q not empty */ + } /* for i < no_of_nodes */ + + igraph_dqueue_int_destroy(&q); + igraph_free(added); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_add_edges(res, &edges, 0)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/contract.c b/src/operators/contract.c new file mode 100644 index 0000000..bb965d5 --- /dev/null +++ b/src/operators/contract.c @@ -0,0 +1,150 @@ +/* -*- 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 "graph/attributes.h" + +/** + * \function igraph_contract_vertices + * \brief Replace multiple vertices with a single one. + * + * This function modifies the graph by merging several vertices + * into one. The vertices in the modified graph correspond + * to groups of vertices in the input graph. No edges are removed, + * thus the modified graph will typically have self-loops + * (corresponding to in-group edges) and multi-edges + * (corresponding to multiple connections between two groups). + * Use \ref igraph_simplify() to eliminate self-loops and + * merge multi-edges. + * + * \param graph The input graph. It will be modified in-place. + * \param mapping A vector giving the mapping. For each + * vertex in the original graph, it should contain + * its desired ID in the result graph. In order not to create + * "orphan vertices" that have no corresponding vertices + * in the original graph, ensure that the IDs are consecutive + * integers starting from zero. + * \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. + * + * \example examples/simple/igraph_contract_vertices.c + */ + +igraph_error_t igraph_contract_vertices(igraph_t *graph, + const igraph_vector_int_t *mapping, + const igraph_attribute_combination_t *vertex_comb) { + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t vattr = vertex_comb && igraph_has_attribute_table(); + igraph_t res; + igraph_integer_t last; + igraph_integer_t no_new_vertices; + + if (igraph_vector_int_size(mapping) != no_of_nodes) { + IGRAPH_ERRORF("Mapping vector length (%" IGRAPH_PRId ") " + "not equal to number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(mapping), no_of_nodes); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + + if (no_of_nodes > 0) { + last = igraph_vector_int_max(mapping); + } else { + /* Ensure that no_new_vertices will be zero + * when the input graph has no vertices. */ + last = -1; + } + + for (igraph_integer_t edge = 0; edge < no_of_edges; edge++) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + + igraph_integer_t nfrom = VECTOR(*mapping)[from]; + igraph_integer_t nto = VECTOR(*mapping)[to]; + + igraph_vector_int_push_back(&edges, nfrom); + igraph_vector_int_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, no_new_vertices, + igraph_is_directed(graph))); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FINALLY(igraph_destroy, &res); + + IGRAPH_I_ATTRIBUTE_DESTROY(&res); + IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ true, /*vertex=*/ false, /*edge=*/ true); + + if (vattr) { + igraph_vector_int_list_t merges; + igraph_vector_int_t sizes; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&merges, no_new_vertices); + IGRAPH_VECTOR_INT_INIT_FINALLY(&sizes, no_new_vertices); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t to = VECTOR(*mapping)[i]; + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&merges, to); + VECTOR(sizes)[to] += 1; + IGRAPH_CHECK(igraph_vector_int_push_back(v, i)); + } + + IGRAPH_CHECK(igraph_i_attribute_combine_vertices(graph, &res, + &merges, + vertex_comb)); + + igraph_vector_int_destroy(&sizes); + igraph_vector_int_list_destroy(&merges); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_FINALLY_CLEAN(1); + igraph_destroy(graph); + *graph = res; + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/difference.c b/src/operators/difference.c new file mode 100644 index 0000000..f9c5786 --- /dev/null +++ b/src/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 Calculates 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 + */ +igraph_error_t 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. */ + + igraph_integer_t no_of_nodes_orig = igraph_vcount(orig); + igraph_integer_t no_of_nodes_sub = igraph_vcount(sub); + igraph_integer_t no_of_nodes = no_of_nodes_orig; + igraph_integer_t smaller_nodes; + igraph_bool_t directed = igraph_is_directed(orig); + igraph_vector_int_t edges; + igraph_vector_int_t edge_ids; + igraph_vector_int_t *nei1, *nei2; + igraph_inclist_t inc_orig, inc_sub; + igraph_integer_t i; + igraph_integer_t v1, v2; + + if (directed != igraph_is_directed(sub)) { + IGRAPH_ERROR("Cannot subtract directed and undirected graphs.", + IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_ids, 0); + IGRAPH_VECTOR_INT_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++) { + igraph_integer_t 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 = VECTOR(*nei1)[n1]; + e2 = 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_int_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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 = VECTOR(*nei1)[n1]; + v1 = IGRAPH_OTHER(orig, e1, i); + if (directed || v1 >= i) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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++) { + igraph_integer_t n1, e1; + nei1 = igraph_inclist_get(&inc_orig, i); + n1 = igraph_vector_int_size(nei1) - 1; + while (n1 >= 0) { + e1 = VECTOR(*nei1)[n1]; + v1 = IGRAPH_OTHER(orig, e1, i); + if (directed || v1 >= i) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + /* Attributes */ + if (orig->attr) { + IGRAPH_I_ATTRIBUTE_DESTROY(res); + IGRAPH_I_ATTRIBUTE_COPY(res, orig, /*graph=*/ true, /*vertex=*/ true, /*edge=*/ false); + IGRAPH_CHECK(igraph_i_attribute_permute_edges(orig, res, &edge_ids)); + } + + igraph_vector_int_destroy(&edge_ids); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/disjoint_union.c b/src/operators/disjoint_union.c new file mode 100644 index 0000000..f6e31ab --- /dev/null +++ b/src/operators/disjoint_union.c @@ -0,0 +1,196 @@ +/* -*- 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 "math/safe_intop.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. + * + * + * The vertex and edge ordering of the graphs will be preserved. + * In other words, the vertex and edge IDs of the first graph map to + * identical values in the new graph, while the vertex and edge IDs + * of the second graph map to IDs incremented by the vertex and edge + * count of the first graph. + * + * + * 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 + */ +igraph_error_t igraph_disjoint_union(igraph_t *res, + const igraph_t *left, + const igraph_t *right) { + + const igraph_integer_t no_of_nodes_left = igraph_vcount(left); + const igraph_integer_t no_of_nodes_right = igraph_vcount(right); + const igraph_integer_t no_of_edges_left = igraph_ecount(left); + const igraph_integer_t no_of_edges_right = igraph_ecount(right); + igraph_integer_t no_of_nodes; /* vertex count of the result */ + igraph_integer_t no_of_edges2; /* twice the edge count of the result */ + igraph_vector_int_t edges; + igraph_bool_t directed_left = igraph_is_directed(left); + igraph_integer_t from, to; + + if (directed_left != igraph_is_directed(right)) { + IGRAPH_ERROR("Cannot create disjoint union of directed and undirected graphs.", + IGRAPH_EINVAL); + } + + /* The edge count of an existing graph object is always safe to multiply by 2. */ + IGRAPH_SAFE_ADD(no_of_nodes_left, no_of_nodes_right, &no_of_nodes); + IGRAPH_SAFE_ADD(2*no_of_edges_left, 2*no_of_edges_right, &no_of_edges2); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + for (igraph_integer_t i = 0; i < no_of_edges_left; i++) { + from = IGRAPH_FROM(left, i); + to = IGRAPH_TO(left, i); + igraph_vector_int_push_back(&edges, from); /* reserved */ + igraph_vector_int_push_back(&edges, to); /* reserved */ + } + for (igraph_integer_t i = 0; i < no_of_edges_right; i++) { + from = IGRAPH_FROM(right, i); + to = IGRAPH_TO(right, i); + igraph_vector_int_push_back(&edges, from + no_of_nodes_left); /* reserved */ + igraph_vector_int_push_back(&edges, to + no_of_nodes_left); /* reserved */ + } + + IGRAPH_CHECK(igraph_create(res, &edges, + no_of_nodes, + directed_left)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_disjoint_union_many + * \brief The disjoint 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. + * + * + * The vertex and edge ordering of the input graphs is preserved in + * the output graph. + * + * + * 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. + */ +igraph_error_t igraph_disjoint_union_many(igraph_t *res, + const igraph_vector_ptr_t *graphs) { + igraph_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); + igraph_bool_t directed = true; + igraph_vector_int_t edges; + igraph_integer_t no_of_edges2 = 0; /* twice the edge count of the result */ + igraph_integer_t shift = 0; + igraph_t *graph; + igraph_integer_t from, to; + + if (no_of_graphs != 0) { + graph = VECTOR(*graphs)[0]; + directed = igraph_is_directed(graph); + for (igraph_integer_t i = 0; i < no_of_graphs; i++) { + graph = VECTOR(*graphs)[i]; + IGRAPH_SAFE_ADD(no_of_edges2, 2*igraph_ecount(graph), &no_of_edges2); + if (directed != igraph_is_directed(graph)) { + IGRAPH_ERROR("Cannot create disjoint union of directed and undirected graphs.", + IGRAPH_EINVAL); + } + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + + for (igraph_integer_t i = 0; i < no_of_graphs; i++) { + igraph_integer_t ec; + graph = VECTOR(*graphs)[i]; + ec = igraph_ecount(graph); + for (igraph_integer_t j = 0; j < ec; j++) { + from = IGRAPH_FROM(graph, j); + to = IGRAPH_TO(graph, j); + igraph_vector_int_push_back(&edges, from + shift); /* reserved */ + igraph_vector_int_push_back(&edges, to + shift); /* reserved */ + } + IGRAPH_SAFE_ADD(shift, igraph_vcount(graph), &shift); + } + + IGRAPH_CHECK(igraph_create(res, &edges, shift, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/intersection.c b/src/operators/intersection.c new file mode 100644 index 0000000..006e390 --- /dev/null +++ b/src/operators/intersection.c @@ -0,0 +1,287 @@ +/* -*- 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_qsort.h" +#include "igraph_vector_list.h" + +#include "operators/misc_internal.h" + +/** + * \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. + * + * + * The directedness of the operand graphs must be the same. + * + * + * Edge multiplicities are handled by taking the \em smaller of the two + * multiplicities in the input graphs. In other words, if the first graph + * has N edges between a vertex pair (u, v) and the second graph has M edges, + * the result graph will have min(N, M) edges between them. + * + * \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 vector. + * 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. For the edges that + * are not in the intersection, -1 is stored. + * \param edge_map2 Null pointer, or an initialized vector. The same + * as \p edge_map1, but for the \p right input graph. For the edges that + * are not in the intersection, -1 is stored. + * \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 + */ +igraph_error_t igraph_intersection( + igraph_t *res, + const igraph_t *left, const igraph_t *right, + igraph_vector_int_t *edge_map1, igraph_vector_int_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. + * + * + * The directedness of the argument graphs must be the same. + * If the graph list has length zero, the result will be a \em directed + * graph with no vertices. + * + * + * Edge multiplicities are handled by taking the \em minimum multiplicity of the + * all multiplicities for the same vertex pair (u, v) in the input graphs; this + * will be the multiplicity of (u, v) in the result graph. + * + * \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 + * list of integer vectors, 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_int_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). + */ +igraph_error_t igraph_intersection_many( + igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_int_list_t *edgemaps +) { + + igraph_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); + igraph_integer_t no_of_nodes = 0; + igraph_bool_t directed = true; + igraph_vector_int_t edges; + igraph_vector_int_list_t edge_vects, order_vects; + igraph_integer_t i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1; + igraph_vector_int_t no_edges; + igraph_bool_t allne = no_of_graphs > 0; + igraph_bool_t allsame = false; + igraph_integer_t 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 create intersection of directed and undirected graphs.", + IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_init(&no_edges, no_of_graphs)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &no_edges); + + /* Calculate number of nodes, query number of edges */ + for (i = 0; i < no_of_graphs; i++) { + igraph_integer_t 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) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edgemaps, no_of_graphs)); + for (i = 0; i < no_of_graphs; i++) { + igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(edgemaps, i); + IGRAPH_CHECK(igraph_vector_int_resize(v, VECTOR(no_edges)[i])); + igraph_vector_int_fill(v, -1); + } + } + + /* Allocate memory for the edge lists and their index vectors */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edge_vects, no_of_graphs); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&order_vects, no_of_graphs); + + /* Query and sort the edge lists */ + for (i = 0; i < no_of_graphs; i++) { + igraph_integer_t k, j, n = VECTOR(no_edges)[i]; + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, i); + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, i); + IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], ev, /*bycol=*/ false)); + if (!directed) { + for (k = 0, j = 0; k < n; k++, j += 2) { + if (VECTOR(*ev)[j] > VECTOR(*ev)[j + 1]) { + igraph_integer_t tmp = VECTOR(*ev)[j]; + VECTOR(*ev)[j] = VECTOR(*ev)[j + 1]; + VECTOR(*ev)[j + 1] = tmp; + } + } + } + IGRAPH_CHECK(igraph_vector_int_resize(order, n)); + for (k = 0; k < n; k++) { + VECTOR(*order)[k] = k; + } + igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), ev, + 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 = IGRAPH_INTEGER_MAX, tailto = IGRAPH_INTEGER_MAX; j < no_of_graphs; j++) { + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_integer_t from = VECTOR(*ev)[2 * edge]; + igraph_integer_t 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 = true; j < no_of_graphs; j++) { + igraph_integer_t from = -1, to = -1; + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + while (true) { + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + from = VECTOR(*ev)[2 * edge]; + to = VECTOR(*ev)[2 * edge + 1]; + if (from > tailfrom || (from == tailfrom && to > tailto)) { + igraph_vector_int_pop_back(order); + if (igraph_vector_int_empty(order)) { + allne = false; + break; + } + } else { + break; + } + } + if (from != tailfrom || to != tailto) { + allsame = false; + } + } + + /* Add the edge, if the smallest tail element was present + in all graphs. */ + if (allsame) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailto)); + } + + /* Drop edges matching the smallest tail elements + from the order vectors, build edge maps */ + if (allne) { + for (j = 0; j < no_of_graphs; j++) { + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + igraph_integer_t from = VECTOR(*ev)[2 * edge]; + igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; + if (from == tailfrom && to == tailto) { + igraph_vector_int_pop_back(order); + if (igraph_vector_int_empty(order)) { + allne = false; + } + if (edgemaps && allsame) { + igraph_vector_int_t *map = igraph_vector_int_list_get_ptr(edgemaps, j); + VECTOR(*map)[edge] = idx; + } + } + } + if (allsame) { + idx++; + } + } + + } /* while allne */ + + igraph_vector_int_list_destroy(&order_vects); + igraph_vector_int_list_destroy(&edge_vects); + igraph_vector_int_destroy(&no_edges); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/join.c b/src/operators/join.c new file mode 100644 index 0000000..57bb2d9 --- /dev/null +++ b/src/operators/join.c @@ -0,0 +1,109 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2024 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_operators.h" +#include "igraph_interface.h" + +#include "math/safe_intop.h" + +/** + * \function igraph_join + * \brief Creates the join of two disjoint graphs. + * + * \experimental + * + * 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. Finally, the vertces from the + * first graph will have edges added to each vertex from the second. + * 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|+|V1|*|V2| edges. + * + * + * The vertex ordering of the graphs will be preserved. + * In other words, the vertex IDs of the first graph map to + * identical values in the new graph, while the vertex IDs + * of the second graph map to IDs incremented by the vertex + * count of the first graph. The new edges will be grouped with the + * other edges that share a from vertex. + * + * + * Both graphs need to have the same directedness, i.e. either both + * directed or both undirected. If both graphs are directed, then for each + * vertex v, u in graphs G1, G2 we add edges (v, u), (u, v) to maintain + * completeness. + * + * + * 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 be stored here. + * \param left The first graph. + * \param right The second graph. + * \return Error code. + * + * Time complexity: O(|V1|*|V2|+|E1|+|E2|). + * + */ +igraph_error_t igraph_join(igraph_t *res, + const igraph_t *left, + const igraph_t *right) { + + igraph_integer_t no_of_nodes_left = igraph_vcount(left); + igraph_integer_t no_of_nodes_right = igraph_vcount(right); + igraph_integer_t no_of_new_edges; + igraph_vector_int_t new_edges; + igraph_bool_t directed_left = igraph_is_directed(left); + igraph_integer_t i; + igraph_integer_t j; + + if (directed_left != igraph_is_directed(right)) { + IGRAPH_ERROR("Cannot create join of directed and undirected graphs.", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_disjoint_union(res,left,right)); + IGRAPH_SAFE_MULT(no_of_nodes_left, no_of_nodes_right ,&no_of_new_edges); + IGRAPH_SAFE_MULT(no_of_new_edges, 2 ,&no_of_new_edges); + if (directed_left) { + IGRAPH_SAFE_MULT(no_of_new_edges, 2 ,&no_of_new_edges); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&new_edges, no_of_new_edges)); + + for(i = 0; i < no_of_nodes_left; i++) { + for(j = 0; j < no_of_nodes_right; j++) { + igraph_vector_int_push_back(&new_edges, i); /* reserved */ + igraph_vector_int_push_back(&new_edges, j + no_of_nodes_left); /* reserved */ + if (directed_left) { + igraph_vector_int_push_back(&new_edges, j + no_of_nodes_left); /* reserved */ + igraph_vector_int_push_back(&new_edges, i); /* reserved */ + } + } + } + + IGRAPH_CHECK(igraph_add_edges(res, &new_edges, NULL)); + + igraph_vector_int_destroy(&new_edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/misc_internal.c b/src/operators/misc_internal.c new file mode 100644 index 0000000..f57d88d --- /dev/null +++ b/src/operators/misc_internal.c @@ -0,0 +1,240 @@ +/* -*- 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_qsort.h" + +int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2) { + igraph_vector_int_t *edgelist = edges; + igraph_integer_t edge1 = (*(const igraph_integer_t*) e1) * 2; + igraph_integer_t edge2 = (*(const igraph_integer_t*) e2) * 2; + igraph_integer_t from1 = VECTOR(*edgelist)[edge1]; + igraph_integer_t from2 = VECTOR(*edgelist)[edge2]; + if (from1 < from2) { + return -1; + } else if (from1 > from2) { + return 1; + } else { + igraph_integer_t to1 = VECTOR(*edgelist)[edge1 + 1]; + igraph_integer_t to2 = VECTOR(*edgelist)[edge2 + 1]; + if (to1 < to2) { + return -1; + } else if (to1 > to2) { + return 1; + } else { + return 0; + } + } +} + +igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, + const igraph_t *left, const igraph_t *right, + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2) { + + igraph_integer_t no_of_nodes_left = igraph_vcount(left); + igraph_integer_t no_of_nodes_right = igraph_vcount(right); + igraph_integer_t no_of_nodes; + igraph_integer_t no_edges_left = igraph_ecount(left); + igraph_integer_t no_edges_right = igraph_ecount(right); + igraph_bool_t directed = igraph_is_directed(left); + igraph_vector_int_t edges; + igraph_vector_int_t edges1, edges2; + igraph_vector_int_t order1, order2; + igraph_integer_t i, j, eptr = 0; + igraph_integer_t 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 create union or intersection of directed and undirected graph.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges1, no_edges_left * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges2, no_edges_right * 2); + IGRAPH_CHECK(igraph_vector_int_init(&order1, no_edges_left)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order1); + IGRAPH_CHECK(igraph_vector_int_init(&order2, no_edges_right)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order2); + + if (edge_map1) { + switch (mode) { + case IGRAPH_MERGE_MODE_UNION: + IGRAPH_CHECK(igraph_vector_int_resize(edge_map1, no_edges_left)); + break; + case IGRAPH_MERGE_MODE_INTERSECTION: + igraph_vector_int_clear(edge_map1); + break; + default: + IGRAPH_FATAL("Invalid merge mode."); + } + } + if (edge_map2) { + switch (mode) { + case IGRAPH_MERGE_MODE_UNION: + IGRAPH_CHECK(igraph_vector_int_resize(edge_map2, no_edges_right)); + break; + case IGRAPH_MERGE_MODE_INTERSECTION: + igraph_vector_int_clear(edge_map2); + break; + default: + IGRAPH_FATAL("Invalid merge mode."); + } + } + + 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, the larger (non-smaller) vertex ID is in the + second column. */ + + IGRAPH_CHECK(igraph_get_edgelist(left, &edges1, /*bycol=*/ false)); + IGRAPH_CHECK(igraph_get_edgelist(right, &edges2, /*bycol=*/ false)); + if (!directed) { + for (i = 0, j = 0; i < no_edges_left; i++, j += 2) { + if (VECTOR(edges1)[j] > VECTOR(edges1)[j + 1]) { + igraph_integer_t 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]) { + igraph_integer_t 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_int_push_back(&edges, from1)); + IGRAPH_CHECK(igraph_vector_int_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_int_push_back(&edges, from2)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to2)); + if (edge_map2) { + VECTOR(*edge_map2)[edge2] = eptr; + } + eptr++; + } + INC2(); + } else { + /* Edge from both */ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from1)); + IGRAPH_CHECK(igraph_vector_int_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_int_push_back(edge_map1, edge1)); + } + if (edge_map2) { + IGRAPH_CHECK(igraph_vector_int_push_back(edge_map2, edge2)); + } + } + eptr++; + INC1(); + INC2(); + } + CONT(); + } + +#undef INC1 +#undef INC2 + + igraph_vector_int_destroy(&order2); + igraph_vector_int_destroy(&order1); + igraph_vector_int_destroy(&edges2); + igraph_vector_int_destroy(&edges1); + IGRAPH_FINALLY_CLEAN(4); + + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/misc_internal.h b/src/operators/misc_internal.h new file mode 100644 index 0000000..2dc7cc7 --- /dev/null +++ b/src/operators/misc_internal.h @@ -0,0 +1,47 @@ +/* -*- 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_error.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" + +__BEGIN_DECLS + +typedef enum { + IGRAPH_MERGE_MODE_UNION = 1, + IGRAPH_MERGE_MODE_INTERSECTION = 2 +} igraph_i_merge_mode_t; + +IGRAPH_FUNCATTR_PURE int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2); +igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, + const igraph_t *left, const igraph_t *right, + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); + +__END_DECLS + +#endif diff --git a/src/operators/permute.c b/src/operators/permute.c new file mode 100644 index 0000000..3fcfb3b --- /dev/null +++ b/src/operators/permute.c @@ -0,0 +1,129 @@ +/* -*- 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" + +/** + * \brief Inverts a permutation. + * + * Produces the inverse of \p permutation into \p inverse and at the same time it checks + * that the permutation vector is valid, i.e. all indices are within range and there are + * no duplicate entries. + * + * \param permutation A permutation vector containing 0-based integer indices. + * \param inverse An initialized vector. The inverse of \p permutation will be stored here. + * \return Error code. + */ +static igraph_error_t igraph_i_invert_permutation(const igraph_vector_int_t *permutation, igraph_vector_int_t *inverse) { + const igraph_integer_t n = igraph_vector_int_size(permutation); + + IGRAPH_CHECK(igraph_vector_int_resize(inverse, n)); + igraph_vector_int_fill(inverse, -1); + + for (igraph_integer_t i=0; i < n; i++) { + igraph_integer_t j = VECTOR(*permutation)[i]; + if (j < 0 || j >= n) { + IGRAPH_ERROR("Invalid index in permutation vector.", IGRAPH_EINVAL); + } + if (VECTOR(*inverse)[j] != -1) { + /* This element of 'inverse' has already been set, 'j' is a duplicate value. */ + IGRAPH_ERROR("Duplicate entry in permutation vector.", IGRAPH_EINVAL); + } + VECTOR(*inverse)[j] = i; + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_permute_vertices + * \brief 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. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in terms of the number of + * vertices and edges. + */ +igraph_error_t igraph_permute_vertices(const igraph_t *graph, igraph_t *res, + const igraph_vector_int_t *permutation) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t edges; + igraph_vector_int_t index; + igraph_integer_t p; + + if (igraph_vector_int_size(permutation) != no_of_nodes) { + IGRAPH_ERROR("Permute vertices: invalid permutation vector size.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_nodes); + + /* Also checks that 'permutation' is valid: */ + IGRAPH_CHECK(igraph_i_invert_permutation(permutation, &index)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + + p = 0; + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + VECTOR(edges)[p++] = VECTOR(*permutation)[ IGRAPH_FROM(graph, i) ]; + VECTOR(edges)[p++] = VECTOR(*permutation)[ IGRAPH_TO(graph, i) ]; + } + + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); + IGRAPH_FINALLY(igraph_destroy, res); + + /* Attributes */ + if (graph->attr) { + igraph_vector_int_t vtypes; + IGRAPH_I_ATTRIBUTE_DESTROY(res); + IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/1, /*vertex=*/0, /*edge=*/1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); + IGRAPH_CHECK(igraph_i_attribute_get_info(graph, 0, 0, 0, &vtypes, 0, 0)); + if (igraph_vector_int_size(&vtypes) != 0) { + IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, &index)); + } + igraph_vector_int_destroy(&vtypes); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&index); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(3); /* +1 for res */ + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/reverse.c b/src/operators/reverse.c new file mode 100644 index 0000000..6f7801f --- /dev/null +++ b/src/operators/reverse.c @@ -0,0 +1,102 @@ +/* + 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 +*/ + +#include "igraph_operators.h" + +#include "igraph_constructors.h" +#include "igraph_conversion.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_vector.h" + +#include "graph/attributes.h" +#include "graph/internal.h" + +/** + * \function igraph_reverse_edges + * \brief Reverses some edges of a directed graph. + * + * This function reverses some edges of a directed graph. The modification is done in place. + * All attributes, as well as the ordering of edges and vertices are preserved. + * + * + * Note that is rarely necessary to reverse \em all edges, as almost all functions that + * handle directed graphs take a \c mode argument that can be set to \c IGRAPH_IN to + * effectively treat edges as reversed. + * + * \param graph The graph whose edges will be reversed. + * \param es The edges to be reversed. + * Pass igraph_ess_all(IGRAPH_EDGEORDER_ID) to reverse all edges. + * \return Error code. + * + * Time complexity: O(1) if all edges are reversed, otherwise + * O(|E|) where |E| is the number of edges in the graph. + */ +igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t edges; + igraph_eit_t eit; + igraph_t new_graph; + + /* Nothing to do on undirected graph. */ + if (! igraph_is_directed(graph)) { + return IGRAPH_SUCCESS; + } + + /* Use fast method when all edges are to be reversed. */ + if (igraph_es_is_all(&eids)) { + return igraph_i_reverse(graph); + } + + /* Convert graph to edge list. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2*no_of_edges); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, /* bycol= */ false)); + + /* Reverse the edges. */ + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &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 tmp = VECTOR(edges)[2*eid]; + VECTOR(edges)[2*eid] = VECTOR(edges)[2*eid + 1]; + VECTOR(edges)[2*eid + 1] = tmp; + } + + /* Re-create graph from edge list and transfer attributes. */ + IGRAPH_CHECK(igraph_create(&new_graph, &edges, no_of_nodes, IGRAPH_DIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &new_graph); + + IGRAPH_I_ATTRIBUTE_DESTROY(&new_graph); + IGRAPH_I_ATTRIBUTE_COPY(&new_graph, graph, true, true, true); /* does IGRAPH_CHECK */ + + igraph_eit_destroy(&eit); + igraph_vector_int_destroy(&edges); + igraph_destroy(graph); + IGRAPH_FINALLY_CLEAN(3); + + *graph = new_graph; + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/rewire.c b/src/operators/rewire.c new file mode 100644 index 0000000..299c1bc --- /dev/null +++ b/src/operators/rewire.c @@ -0,0 +1,264 @@ +/* -*- 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. */ +igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, igraph_bool_t use_adjlist) { + const igraph_integer_t no_of_edges = igraph_ecount(graph); + char message[256]; + igraph_integer_t a, b, c, d, dummy, num_swaps, num_successful_swaps; + igraph_vector_int_t eids; + igraph_vector_int_t edgevec, alledges; + const igraph_bool_t directed = igraph_is_directed(graph); + const igraph_bool_t loops = (mode & IGRAPH_REWIRING_SIMPLE_LOOPS); + igraph_bool_t ok; + igraph_es_t es; + igraph_adjlist_t al; + + if (no_of_edges < 2) { + /* There are no possible rewirings, return with the same graph. */ + return IGRAPH_SUCCESS; + } + + RNG_BEGIN(); + + IGRAPH_VECTOR_INT_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_INT_INIT_FINALLY(&alledges, no_of_edges * 2); + igraph_get_edgelist(graph, &alledges, /*bycol=*/ false); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgevec, 4); + es = igraph_ess_vector(&eids); + } + + /* We count both successful and unsuccessful rewiring trials. + * This is necessary for uniform sampling. */ + + 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 = true; + + /* 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)[VECTOR(eids)[0] * 2]; + b = VECTOR(alledges)[VECTOR(eids)[0] * 2 + 1]; + c = VECTOR(alledges)[VECTOR(eids)[1] * 2]; + d = VECTOR(alledges)[VECTOR(eids)[1] * 2 + 1]; + } else { + IGRAPH_CHECK(igraph_edge(graph, VECTOR(eids)[0], &a, &b)); + IGRAPH_CHECK(igraph_edge(graph, 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)[VECTOR(eids)[1] * 2] = c; + VECTOR(alledges)[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 = false; + } else { + /* Check whether they are suitable for rewiring */ + if (a == c || b == d) { + /* Swapping would have no effect */ + ok = false; + } 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 = false; + } + } else { + IGRAPH_CHECK(igraph_are_adjacent(graph, a, d, &ok)); + ok = !ok; + } + } + if (ok) { + if (use_adjlist) { + if (igraph_adjlist_has_edge(&al, c, b, directed)) { + ok = false; + } + } else { + IGRAPH_CHECK(igraph_are_adjacent(graph, c, b, &ok)); + ok = !ok; + } + } + + /* If we are still okay, we can perform the rewiring */ + if (ok) { + /* printf("Deleting: %" IGRAPH_PRId " -> %" IGRAPH_PRId ", %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", + a, b, c, 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)[VECTOR(eids)[0] * 2 + 1] = d; + VECTOR(alledges)[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: %" IGRAPH_PRId " -> %" IGRAPH_PRId ", %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", + a, d, c, b); */ + IGRAPH_CHECK(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_int_destroy(&alledges); + igraph_adjlist_destroy(&al); + } else { + igraph_vector_int_destroy(&edgevec); + } + + igraph_vector_int_destroy(&eids); + IGRAPH_FINALLY_CLEAN(use_adjlist ? 3 : 2); + + RNG_END(); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_rewire + * \brief Randomly rewires a graph while preserving its degree sequence. + * + * This function generates a new graph based on the original one by randomly + * "rewriting" edges while preserving the original graph's degree sequence. + * 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. All graph attributes will be lost. + * + * + * The rewiring is performed with degree-preserving edge switches: + * Two arbitrary edges are picked uniformly at random, namely + * (a, b) and (c, d), then they are replaced + * by (a, d) and (b, c) if this preserves the + * constraints specified by \p mode. + * + * \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 + * This method does not create or destroy self-loops, and does + * not create multi-edges. + * \cli IGRAPH_REWIRING_SIMPLE_LOOPS + * This method allows the creation or destruction of self-loops. + * Self-loops are created by switching edges that have a single + * common endpoint. + * \endclist + * + * \return Error code: + * \clist + * \cli IGRAPH_EINVMODE + * Invalid rewiring mode. + * \cli IGRAPH_ENOMEM + * Not enough memory for temporary data. + * \endclist + * + * Time complexity: TODO. + */ +igraph_error_t 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/operators/rewire_edges.c b/src/operators/rewire_edges.c new file mode 100644 index 0000000..6c5b856 --- /dev/null +++ b/src/operators/rewire_edges.c @@ -0,0 +1,395 @@ +/* -*- 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 igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, + igraph_vector_int_t *edges) { + + igraph_integer_t no_verts = igraph_vcount(graph); + igraph_integer_t no_edges = igraph_ecount(graph); + igraph_vector_int_t eorder, tmp; + igraph_vector_int_t first, next, prev, marked; + igraph_integer_t i, to_rewire, last_other = -1; + + /* Create our special graph representation */ + +# define ADD_STUB(vertex, stub) do { \ + if (VECTOR(first)[(vertex)]) { \ + VECTOR(prev)[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 { \ + igraph_integer_t xxx_ =VECTOR(first)[(vertex)]; \ + while (xxx_) { \ + igraph_integer_t o= 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_INT_INIT_FINALLY(&eorder, no_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, no_edges); + for (i = 0; i < no_edges; i++) { + igraph_integer_t idx1 = 2 * i, idx2 = idx1 + 1; + igraph_integer_t from = VECTOR(*edges)[idx1]; + igraph_integer_t to = VECTOR(*edges)[idx2]; + VECTOR(tmp)[i] = from; + ADD_STUB(from, idx1); + ADD_STUB(to, idx2); + } + IGRAPH_CHECK(igraph_vector_int_order1(&tmp, &eorder, no_verts)); + igraph_vector_int_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 = (igraph_integer_t) RNG_GEOM(prob); + while (to_rewire < no_edges) { + igraph_integer_t stub = 2 * VECTOR(eorder)[to_rewire] + 1; + igraph_integer_t v = VECTOR(*edges)[stub]; + igraph_integer_t ostub = stub - 1; + igraph_integer_t other = VECTOR(*edges)[ostub]; + igraph_integer_t pot; + if (last_other != other) { + MARK_NEIGHBORS(other); + } + /* Do the rewiring */ + do { + if (loops) { + pot = RNG_INTEGER(0, no_verts - 1); + } else { + pot = 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_INT_INIT_FINALLY(&tmp, no_edges); + for (i = 0; i < no_edges; i++) { + VECTOR(tmp)[i] = VECTOR(*edges)[2 * i + 1]; + } + IGRAPH_CHECK(igraph_vector_int_order1(&tmp, &eorder, no_verts)); + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + /* Rewire the stubs, part II */ + + igraph_vector_int_null(&marked); + last_other = -1; + + to_rewire = (igraph_integer_t) RNG_GEOM(prob); + while (to_rewire < no_edges) { + igraph_integer_t stub = (2 * VECTOR(eorder)[to_rewire]); + igraph_integer_t v = VECTOR(*edges)[stub]; + igraph_integer_t ostub = stub + 1; + igraph_integer_t other = VECTOR(*edges)[ostub]; + igraph_integer_t pot; + if (last_other != other) { + MARK_NEIGHBORS(other); + } + /* Do the rewiring */ + do { + if (loops) { + pot = RNG_INTEGER(0, no_verts - 1); + } else { + pot = 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_int_destroy(&eorder); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +#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|). + */ +igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, + igraph_bool_t loops, igraph_bool_t multiple) { + + igraph_t newgraph; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t endpoints = no_of_edges * 2; + igraph_integer_t to_rewire; + igraph_vector_int_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_INT_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 = RNG_GEOM(prob); + while (to_rewire < endpoints) { + if (loops) { + VECTOR(edges)[to_rewire] = RNG_INTEGER(0, no_of_nodes - 1); + } else { + igraph_integer_t opos = to_rewire % 2 ? to_rewire - 1 : to_rewire + 1; + igraph_integer_t nei = VECTOR(edges)[opos]; + igraph_integer_t 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, no_of_nodes, + igraph_is_directed(graph))); + igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \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|). + */ +igraph_error_t 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; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t to_rewire; + igraph_integer_t offset = 0; + igraph_vector_int_t edges; + + IGRAPH_VECTOR_INT_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 { + igraph_integer_t nei = VECTOR(edges)[2 * to_rewire + (1 - offset)]; + igraph_integer_t 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, no_of_nodes, + igraph_is_directed(graph))); + igraph_vector_int_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 IGRAPH_SUCCESS; +} diff --git a/src/operators/rewire_internal.h b/src/operators/rewire_internal.h new file mode 100644 index 0000000..b82b10e --- /dev/null +++ b/src/operators/rewire_internal.h @@ -0,0 +1,15 @@ +#ifndef IGRAPH_OPERATORS_REWIRE_INTERNAL_H +#define IGRAPH_OPERATORS_REWIRE_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_interface.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_rewire( + igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, + igraph_bool_t use_adjlist); + +__END_DECLS + +#endif diff --git a/src/operators/simplify.c b/src/operators/simplify.c new file mode 100644 index 0000000..4412669 --- /dev/null +++ b/src/operators/simplify.c @@ -0,0 +1,210 @@ +/* -*- 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. + * + * This function merges parallel edges and removes self-loops, according + * to the \p multiple and \p loops parameters. Note that this function + * may change the edge order, even if the input was already a simple graph. + * + * \param graph The graph object. + * \param remove_multiple Logical, if true, multiple edges will be removed. + * \param remove_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 unaffected. 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 + */ + +igraph_error_t igraph_simplify(igraph_t *graph, + igraph_bool_t remove_multiple, igraph_bool_t remove_loops, + const igraph_attribute_combination_t *edge_comb) { + + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t edge; + igraph_bool_t attr = edge_comb && igraph_has_attribute_table(); + igraph_integer_t from, to, pfrom = -1, pto = -2; + igraph_t res; + igraph_es_t es; + igraph_eit_t eit; + igraph_vector_int_t mergeinto; + igraph_integer_t actedge; + + /* if we already know there are no multi-edges, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI)) { + remove_multiple = false; + } + + /* if we already know there are no loops, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + remove_loops = false; + } + + if (!remove_multiple && !remove_loops) + /* nothing to do */ + { + return IGRAPH_SUCCESS; + } + + if (!remove_multiple) { + igraph_vector_int_t edges_to_delete; + + /* removing loop edges only, this is simple. No need to combine anything + * and the whole process can be done in-place */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges_to_delete, 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_int_push_back(&edges_to_delete, edge)); + } + IGRAPH_EIT_NEXT(eit); + } + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + IGRAPH_FINALLY_CLEAN(2); + + if (igraph_vector_int_size(&edges_to_delete) > 0) { + IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_vector(&edges_to_delete))); + } + + igraph_vector_int_destroy(&edges_to_delete); + IGRAPH_FINALLY_CLEAN(1); + + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, false); + + return IGRAPH_SUCCESS; + } + + if (attr) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_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 (remove_loops && from == to) { + /* Loop edge to be removed */ + if (attr) { + VECTOR(mergeinto)[edge] = -1; + } + } else if (remove_multiple && from == pfrom && to == pto) { + /* Multiple edge to be contracted */ + if (attr) { + VECTOR(mergeinto)[edge] = actedge; + } + } else { + /* Edge to be kept */ + igraph_vector_int_push_back(&edges, from); /* reserved */ + igraph_vector_int_push_back(&edges, to); /* reserved */ + 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, no_of_nodes, igraph_is_directed(graph))); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FINALLY(igraph_destroy, &res); + + IGRAPH_I_ATTRIBUTE_DESTROY(&res); + IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ true, /*vertex=*/ true, /*edge=*/ false); + + 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.vecs, edge_comb)); + + igraph_fixed_vectorlist_destroy(&vl); + igraph_vector_int_destroy(&mergeinto); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_FINALLY_CLEAN(1); + igraph_destroy(graph); + *graph = res; + + /* The cache must be set as the very last step, only after all functions that can + * potentially return with an error have finished. */ + + if (remove_loops) { + /* Loop edges were removed so we know for sure that there aren't any + * loop edges now */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, false); + } + + if (remove_multiple) { + /* Multi-edges were removed so we know for sure that there aren't any + * multi-edges now */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MULTI, false); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/subgraph.c b/src/operators/subgraph.c new file mode 100644 index 0000000..9885351 --- /dev/null +++ b/src/operators/subgraph.c @@ -0,0 +1,629 @@ +/* + IGraph library. + Copyright (C) 2006-2024 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_operators.h" + +#include "igraph_bitset.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "core/interruption.h" +#include "core/set.h" +#include "graph/attributes.h" +#include "graph/internal.h" +#include "operators/subgraph.h" + +/** + * Subgraph creation, old version: it copies the graph and then deletes + * unneeded vertices. + */ +static igraph_error_t igraph_i_induced_subgraph_copy_and_delete( + const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, + igraph_vector_int_t *map, igraph_vector_int_t *invmap) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_new_nodes_estimate; + igraph_vector_int_t delete; + igraph_bitset_t remain; + igraph_vit_t vit; + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&delete, 0); + IGRAPH_BITSET_INIT_FINALLY(&remain, no_of_nodes); + + /* Calculate how many nodes there will be in the new graph. The result is + * a lower bound only as 'vit' may contain the same vertex more than once. */ + no_of_new_nodes_estimate = no_of_nodes - IGRAPH_VIT_SIZE(vit); + if (no_of_new_nodes_estimate < 0) { + no_of_new_nodes_estimate = 0; + } + + IGRAPH_CHECK(igraph_vector_int_reserve(&delete, no_of_new_nodes_estimate)); + + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + IGRAPH_BIT_SET(remain, IGRAPH_VIT_GET(vit)); + } + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + if (! IGRAPH_BIT_TEST(remain, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); + } + } + + igraph_bitset_destroy(&remain); + IGRAPH_FINALLY_CLEAN(1); + + 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_int_destroy(&delete); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * 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 igraph_error_t igraph_i_induced_subgraph_create_from_scratch( + const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, + igraph_vector_int_t *map, igraph_vector_int_t *invmap, + igraph_bool_t map_is_prepared) { + + const igraph_bool_t directed = igraph_is_directed(graph); + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_new_nodes = 0; + igraph_integer_t n; + igraph_integer_t to; + igraph_integer_t eid; + igraph_vector_int_t vids_old2new, vids_new2old; + igraph_vector_int_t eids_new2old; + igraph_vector_int_t vids_vec; + igraph_vector_int_t nei_edges; + igraph_vector_int_t new_edges; + igraph_vit_t vit; + igraph_vector_int_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_INT_INIT_FINALLY(&eids_new2old, 0); + if (invmap) { + my_vids_new2old = invmap; + igraph_vector_int_clear(my_vids_new2old); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_new2old, 0); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nei_edges, 0); + if (map) { + my_vids_old2new = map; + if (!map_is_prepared) { + IGRAPH_CHECK(igraph_vector_int_resize(map, no_of_nodes)); + igraph_vector_int_null(map); + } + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_vec, 0); + + 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. + */ + IGRAPH_CHECK(igraph_vit_as_vector(&vit, &vids_vec)); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + igraph_vector_int_sort(&vids_vec); + n = igraph_vector_int_size(&vids_vec); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t vid = VECTOR(vids_vec)[i]; + + /* Cater for duplicate vertex IDs in the input vertex selector; we use + * the first occurrence of each vertex ID and ignore the rest */ + if (VECTOR(*my_vids_old2new)[vid] == 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(my_vids_new2old, vid)); + no_of_new_nodes++; + VECTOR(*my_vids_old2new)[vid] = no_of_new_nodes; + } + } + igraph_vector_int_destroy(&vids_vec); + IGRAPH_FINALLY_CLEAN(1); + + /* Create the new edge list */ + for (igraph_integer_t i = 0; i < no_of_new_nodes; i++) { + igraph_integer_t old_vid = VECTOR(*my_vids_new2old)[i]; + igraph_integer_t new_vid = i; + igraph_bool_t skip_loop_edge; + + IGRAPH_CHECK(igraph_incident(graph, &nei_edges, old_vid, IGRAPH_OUT)); + n = igraph_vector_int_size(&nei_edges); + + if (directed) { + /* directed graph; this is easier */ + for (igraph_integer_t j = 0; j < n; j++) { + eid = VECTOR(nei_edges)[j]; + + to = VECTOR(*my_vids_old2new)[ IGRAPH_TO(graph, eid) ]; + if (!to) { + continue; + } + + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, new_vid)); + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, to - 1)); + IGRAPH_CHECK(igraph_vector_int_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 (igraph_integer_t j = 0; j < n; j++) { + eid = VECTOR(nei_edges)[j]; + + if (IGRAPH_FROM(graph, eid) != old_vid) { + /* avoid processing edges twice */ + continue; + } + + to = VECTOR(*my_vids_old2new)[ 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_int_push_back(&new_edges, new_vid)); + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&eids_new2old, eid)); + } + } + } + + /* Get rid of some vectors that are not needed anymore */ + if (!map) { + igraph_vector_int_destroy(&vids_old2new); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_int_destroy(&nei_edges); + IGRAPH_FINALLY_CLEAN(1); + + /* Create the new graph */ + IGRAPH_CHECK(igraph_create(res, &new_edges, no_of_new_nodes, directed)); + IGRAPH_I_ATTRIBUTE_DESTROY(res); + + /* Now we can also get rid of the new_edges vector */ + igraph_vector_int_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 = */ true, /* va = */ false, /* ea = */ false)); + + /* 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)); + + /* Get rid of the remaining stuff */ + if (!invmap) { + igraph_vector_int_destroy(my_vids_new2old); + IGRAPH_FINALLY_CLEAN(1); + } + igraph_vector_int_destroy(&eids_new2old); + IGRAPH_FINALLY_CLEAN(2); /* 1 + 1 since we don't need to destroy res */ + + return IGRAPH_SUCCESS; +} + +/** + * \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 vertex IDs are always contiguos integers starting + * at zero, the IDs in the created subgraph will be different from the IDs in + * the original graph. To get the mappings between them, use + * \ref igraph_induced_subgraph_map() + * + * \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. A vertex + * may appear more than once in the selector, but it will be considered + * only once (i.e. it is not possible to duplicate a vertex by adding + * its ID more than once to the selector). The order in which the + * vertices appear in the vertex selector is ignored; the returned + * subgraph will always contain the vertices of the original graph in + * increasing order of vertex IDs. + * \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_induced_subgraph_map() to also retrieve the vertex ID + * mapping between the graph and the extracted subgraph; + * \ref igraph_delete_vertices() to delete the specified set of + * vertices from a graph, the opposite of this function. + */ +igraph_error_t 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= */ NULL, /* invmap= */ NULL); +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +igraph_error_t igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *res, + const igraph_vs_t vids, + igraph_subgraph_implementation_t impl, + igraph_vector_int_t *map, + igraph_vector_int_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); + } +} + +/** + * \ingroup structural + * \function igraph_induced_subgraph_map + * \brief Creates an induced subraph and returns the mapping from the original. + * + * This function collects the specified vertices and all edges between + * them to a new graph. As vertex IDs are always contiguos integers starting + * at zero, the IDs in the created subgraph will be different from the IDs in + * the original graph. The mapping between the vertex IDs in the graph and the + * extracted subgraphs are returned in \p map and \p invmap. + * + * \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 be + * used 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. + * \param map Returns a map of the vertices in \p graph to the vertices + * in \p res. A 0 indicates a vertex is not mapped. An \c i + 1 at + * position \c j indicates the vertex \c j in \p graph is mapped + * to vertex i in \p res. + * \param invmap Returns a map of the vertices in \p res to the vertices + * in \p graph. An i at position \c j indicates the vertex \c i + * in \p graph is mapped to vertex j in \p res. + * + * \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. + */ +igraph_error_t igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, + const igraph_vs_t vids, + igraph_subgraph_implementation_t impl, + igraph_vector_int_t *map, + igraph_vector_int_t *invmap) { + return igraph_i_induced_subgraph_map(graph, res, vids, impl, + map, invmap, + /* map_is_prepared = */ false); +} + +/** + * \function igraph_induced_subgraph_edges + * \brief The edges contained within an induced subgraph. + * + * This function finds the IDs of those edges which connect vertices from + * a given list, passed in the \p vids parameter. + * + * \param graph The graph. + * \param vids A vertex selector specifying the vertices that make up the subgraph. + * \param edges Integer vector. The IDs of edges within the subgraph induces by + * \p vids will be stored here. + * \return Error code. + * + * Time complexity: O(mv log(nv)) where nv is the number of vertices in \p vids + * and mv is the sum of degrees of vertices in \p vids. + */ +igraph_error_t igraph_induced_subgraph_edges(const igraph_t *graph, igraph_vs_t vids, igraph_vector_int_t *edges) { + /* TODO: When the size of \p vids is large, is it faster to use a boolean vector instead of a set + * to test membership within \p vids? Benchmark to find out at what size it is worth switching + * to the alternative implementation. + */ + igraph_vit_t vit; + igraph_set_t vids_set; + igraph_vector_int_t incedges; + + if (igraph_vs_is_all(&vids)) { + IGRAPH_CHECK(igraph_vector_int_range(edges, 0, igraph_ecount(graph))); + return IGRAPH_SUCCESS; + } + + igraph_vector_int_clear(edges); + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + IGRAPH_SET_INIT_FINALLY(&vids_set, IGRAPH_VIT_SIZE(vit)); + for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + IGRAPH_CHECK(igraph_set_add(&vids_set, IGRAPH_VIT_GET(vit))); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&incedges, 0); + + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + igraph_integer_t v = IGRAPH_VIT_GET(vit); + IGRAPH_CHECK(igraph_i_incident(graph, &incedges, v, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); + + igraph_integer_t d = igraph_vector_int_size(&incedges); + for (igraph_integer_t i=0; i < d; i++) { + igraph_integer_t e = VECTOR(incedges)[i]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + /* The v <= u check avoids adding non-loop edges twice. + * Loop edges only appear once due to the use of + * IGRAPH_LOOPS_ONCE in igraph_i_incident() */ + if (v <= u && igraph_set_contains(&vids_set, u)) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, e)); + } + } + } + + IGRAPH_FINALLY_CLEAN(3); + igraph_vector_int_destroy(&incedges); + igraph_set_destroy(&vids_set); + igraph_vit_destroy(&vit); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_subgraph_edges + * \brief Creates a subgraph with the specified edges and their endpoints (deprecated alias). + * + * \deprecated-by igraph_subgraph_from_edges 0.10.3 + */ +igraph_error_t igraph_subgraph_edges( + const igraph_t *graph, igraph_t *res, const igraph_es_t eids, + igraph_bool_t delete_vertices +) { + return igraph_subgraph_from_edges(graph, res, eids, delete_vertices); +} + +/** + * \ingroup structural + * \function igraph_subgraph_from_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 edge IDs in a graph are always contiguous integers starting at + * zero, the edge IDs in the extracted subgraph will be different from those + * in the original graph. Vertex IDs will also be reassigned if + * \p delete_vertices is set to \c true. Attributes are preserved. + * + * \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. + */ + +igraph_error_t igraph_subgraph_from_edges( + const igraph_t *graph, igraph_t *res, const igraph_es_t eids, + igraph_bool_t delete_vertices +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_edges_to_delete_estimate; + igraph_vector_int_t delete = IGRAPH_VECTOR_NULL; + igraph_bitset_t vremain, eremain; + igraph_eit_t eit; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&delete, 0); + IGRAPH_BITSET_INIT_FINALLY(&vremain, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&eremain, no_of_edges); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + /* Calculate how many edges there will be in the new graph. The result is + * a lower bound only as 'eit' may contain the same edge more than once. */ + no_of_edges_to_delete_estimate = no_of_edges - IGRAPH_EIT_SIZE(eit); + if (no_of_edges_to_delete_estimate < 0) { + no_of_edges_to_delete_estimate = 0; + } + + IGRAPH_CHECK(igraph_vector_int_reserve(&delete, no_of_edges_to_delete_estimate)); + + /* 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 eid = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, eid), to = IGRAPH_TO(graph, eid); + IGRAPH_BIT_SET(eremain, eid); + IGRAPH_BIT_SET(vremain, from); + IGRAPH_BIT_SET(vremain, to); + } + + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + /* Collect the edge IDs to be deleted */ + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + if (! IGRAPH_BIT_TEST(eremain, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); + } + } + + igraph_bitset_destroy(&eremain); + IGRAPH_FINALLY_CLEAN(1); + + /* Delete the unnecessary edges */ + 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_int_clear(&delete); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + if (! IGRAPH_BIT_TEST(vremain, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); + } + } + } + + igraph_bitset_destroy(&vremain); + IGRAPH_FINALLY_CLEAN(1); + + /* Delete the unnecessary vertices */ + if (delete_vertices) { + IGRAPH_CHECK(igraph_delete_vertices(res, igraph_vss_vector(&delete))); + } + + igraph_vector_int_destroy(&delete); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/operators/subgraph.h b/src/operators/subgraph.h new file mode 100644 index 0000000..d293f7b --- /dev/null +++ b/src/operators/subgraph.h @@ -0,0 +1,42 @@ +/* -*- 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_decls.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_iterators.h" + +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_induced_subgraph_map( + const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, + igraph_subgraph_implementation_t impl, igraph_vector_int_t *map, + igraph_vector_int_t *invmap, igraph_bool_t map_is_prepared +); + +__END_DECLS + +#endif diff --git a/src/operators/union.c b/src/operators/union.c new file mode 100644 index 0000000..71876bd --- /dev/null +++ b/src/operators/union.c @@ -0,0 +1,248 @@ +/* -*- 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_qsort.h" +#include "igraph_vector_list.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. + * + * + * The directedness of the operand graphs must be the same. + * + * + * Edge multiplicities are handled by taking the \em larger of the two + * multiplicities in the input graphs. In other words, if the first graph + * has N edges between a vertex pair (u, v) and the second graph has M edges, + * the result graph will have max(N, M) edges between them. + * + * \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 + */ +igraph_error_t igraph_union( + igraph_t *res, + const igraph_t *left, const igraph_t *right, + igraph_vector_int_t *edge_map1, igraph_vector_int_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 number of vertices in the result graph will be the maximum + * number of vertices in the argument graphs. + * + * + * The directedness of the argument graphs must be the same. + * If the graph list has length zero, the result will be a \em directed + * graph with no vertices. + * + * + * Edge multiplicities are handled by taking the \em maximum multiplicity of the + * all multiplicities for the same vertex pair (u, v) in the input graphs; this + * will be the multiplicity of (u, v) in the result graph. + * + * \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 + * list of integer vectors, 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_int_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. + */ +igraph_error_t igraph_union_many( + igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_int_list_t *edgemaps +) { + + igraph_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); + igraph_integer_t no_of_nodes = 0; + igraph_bool_t directed = true; + igraph_vector_int_t edges; + igraph_vector_int_list_t edge_vects, order_vects; + igraph_vector_int_t no_edges; + igraph_integer_t i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1; + igraph_integer_t 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 create union of directed and undirected graphs.", + IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_init(&no_edges, no_of_graphs)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &no_edges); + + /* Calculate number of nodes, query number of edges */ + for (i = 0; i < no_of_graphs; i++) { + igraph_integer_t 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) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edgemaps, no_of_graphs)); + for (i = 0; i < no_of_graphs; i++) { + igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(edgemaps, i); + IGRAPH_CHECK(igraph_vector_int_resize(v, VECTOR(no_edges)[i])); + } + } + + /* Allocate memory for the edge lists and their index vectors */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edge_vects, no_of_graphs); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&order_vects, no_of_graphs); + + /* Query and sort the edge lists */ + for (i = 0; i < no_of_graphs; i++) { + igraph_integer_t k, j, n = VECTOR(no_edges)[i]; + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, i); + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, i); + IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], ev, /*bycol=*/ false)); + if (!directed) { + for (k = 0, j = 0; k < n; k++, j += 2) { + if (VECTOR(*ev)[j] > VECTOR(*ev)[j + 1]) { + igraph_integer_t tmp = VECTOR(*ev)[j]; + VECTOR(*ev)[j] = VECTOR(*ev)[j + 1]; + VECTOR(*ev)[j + 1] = tmp; + } + } + } + IGRAPH_CHECK(igraph_vector_int_resize(order, n)); + for (k = 0; k < n; k++) { + VECTOR(*order)[k] = k; + } + igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), ev, + igraph_i_order_edgelist_cmp); + } + + while (tailfrom >= 0) { + + /* Get the largest tail element */ + tailfrom = tailto = -1; + for (j = 0; j < no_of_graphs; j++) { + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + if (!igraph_vector_int_empty(order)) { + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_integer_t from = VECTOR(*ev)[2 * edge]; + igraph_integer_t 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_int_push_back(&edges, tailfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailto)); + + /* update edge lists, we just modify the 'order' vectors */ + for (j = 0; j < no_of_graphs; j++) { + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + if (!igraph_vector_int_empty(order)) { + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_integer_t from = VECTOR(*ev)[2 * edge]; + igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; + if (from == tailfrom && to == tailto) { + igraph_vector_int_pop_back(order); + if (edgemaps) { + igraph_vector_int_t *map = igraph_vector_int_list_get_ptr(edgemaps, j); + VECTOR(*map)[edge] = idx; + } + } + } + } + idx++; + + } + + igraph_vector_int_list_destroy(&order_vects); + igraph_vector_int_list_destroy(&edge_vects); + igraph_vector_int_destroy(&no_edges); + IGRAPH_FINALLY_CLEAN(3); + + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/all_shortest_paths.c b/src/paths/all_shortest_paths.c new file mode 100644 index 0000000..4cfa219 --- /dev/null +++ b/src/paths/all_shortest_paths.c @@ -0,0 +1,338 @@ +/* -*- 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_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "core/interruption.h" + +#include /* memset */ + +/** + * \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. Every edge is considered separately, + * therefore in graphs with multi-edges, this function may produce + * a very large number of results. + * + * \param graph The graph object. + * \param vertices The result, the IDs of the vertices along the paths. + * This is a list of integer vectors where each element is an + * \ref igraph_vector_int_t object. 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. The list will be resized as needed. 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. Each vector object contains the edges + * 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. The list will be resized as needed. Supply a null pointer here + * if you don't need these vectors. + * \param nrgeo Pointer to an initialized \ref igraph_vector_int_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. + */ + +igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_vector_int_t *nrgeo, + igraph_integer_t from, const igraph_vs_t to, + igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t *geodist; + igraph_vector_int_list_t paths; + igraph_vector_int_list_t path_edge; + igraph_dqueue_int_t q; + igraph_vector_int_t *vptr; + igraph_vector_int_t *vptr_e; + igraph_vector_int_t neis; + igraph_vector_int_t ptrlist; + igraph_vector_int_t ptrhead; + igraph_integer_t n; + igraph_integer_t to_reach, reached = 0, maxdist = 0; + + igraph_vit_t vit; + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("Index of source vertex is out of range.", 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_VECTOR_INT_LIST_INIT_FINALLY(&paths, 0); + /* path_edge will store the shortest paths during the search */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&path_edge, 0); + /* neis is a temporary vector holding the neighbors of the + * node being examined */ + IGRAPH_VECTOR_INT_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_INT_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_INT_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, igraph_integer_t); + IGRAPH_CHECK_OOM(geodist, "Insufficient memory for calculating shortest paths."); + 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_DQUEUE_INT_INIT_FINALLY(&q, 100); + + if (nrgeo) { + IGRAPH_CHECK(igraph_vector_int_resize(nrgeo, no_of_nodes)); + igraph_vector_int_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[ IGRAPH_VIT_GET(vit) ] == 0) { + geodist[ IGRAPH_VIT_GET(vit) ] = -1; + } else { + to_reach--; /* this node was given multiple times */ + } + } + + if (geodist[ from ] < 0) { + reached++; + } + + /* from -> from */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&paths, &vptr)); + IGRAPH_CHECK(igraph_vector_int_push_back(vptr, from)); + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&path_edge, &vptr_e)); + + geodist[from] = 1; + VECTOR(ptrhead)[from] = 1; + IGRAPH_CHECK(igraph_vector_int_push_back(&ptrlist, 0)); + if (nrgeo) { + VECTOR(*nrgeo)[from] = 1; + } + + /* Init queue */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, from)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_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 */ + IGRAPH_ASSERT(maxdist >= 0); + break; + } + } + + /* If we need the edge-paths, we need to use igraph_incident() followed by an + * IGRAPH_OTHER() macro in the main loop. This is going to be slower than + * using igraph_neighbors() due to branch mispredictions in IGRAPH_OTHER(), so we + * use igraph_incident() only if the user needs the edge-paths */ + if (edges) { + IGRAPH_CHECK(igraph_incident(graph, &neis, actnode, mode)); + } else { + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + } + + n = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t neighbor; + igraph_integer_t parentptr; + + if (edges) { + /* user needs the edge-paths, so 'neis' contains edge IDs, we need to resolve + * the next edge ID into a vertex ID */ + neighbor = IGRAPH_OTHER(graph, VECTOR(neis)[j], actnode); + } else { + /* user does not need the edge-paths, so 'neis' contains vertex IDs */ + neighbor = VECTOR(neis)[j]; + } + + 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_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_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 */ + parentptr = VECTOR(ptrhead)[actnode]; + while (parentptr != 0) { + /* allocate a new igraph_vector_int_t at the end of paths */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&paths, &vptr)); + IGRAPH_CHECK(igraph_vector_int_update(vptr, igraph_vector_int_list_get_ptr(&paths, parentptr - 1))); + IGRAPH_CHECK(igraph_vector_int_push_back(vptr, neighbor)); + + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&path_edge, &vptr_e)); + if (actnode != from) { + /* If the previous vertex was the source then there is no edge to add*/ + IGRAPH_CHECK(igraph_vector_int_update(vptr_e, igraph_vector_int_list_get_ptr(&path_edge, parentptr - 1))); + } + IGRAPH_CHECK(igraph_vector_int_push_back(vptr_e, VECTOR(neis)[j])); + + IGRAPH_CHECK(igraph_vector_int_push_back(&ptrlist, VECTOR(ptrhead)[neighbor])); + VECTOR(ptrhead)[neighbor] = igraph_vector_int_size(&ptrlist); + + parentptr = VECTOR(ptrlist)[parentptr - 1]; + } + } + } + + igraph_dqueue_int_destroy(&q); + IGRAPH_FINALLY_CLEAN(1); + + /* mark the nodes for which we need the result */ + memset(geodist, 0, sizeof(geodist[0]) * (size_t) no_of_nodes); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + geodist[ IGRAPH_VIT_GET(vit) ] = 1; + } + + if (vertices) { + igraph_vector_int_list_clear(vertices); + } + if (edges) { + igraph_vector_int_list_clear(edges); + } + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t parentptr = VECTOR(ptrhead)[i]; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* do we need the paths leading to vertex i? */ + if (geodist[i] > 0) { + /* yes, transfer them to the result vector */ + while (parentptr != 0) { + /* Given two vector lists, list1 and list2, an efficient way to transfer + * a vector from list1 to the end of list2 is to extend list2 with an + * empty vector, then swap that empty vector with the given element of + * list1. This approach avoids creating a full copy of the vector. */ + if (vertices) { + igraph_vector_int_t *p; + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &p)); + igraph_vector_int_swap(p, igraph_vector_int_list_get_ptr(&paths, parentptr - 1)); + } + if (edges) { + igraph_vector_int_t *p; + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &p)); + igraph_vector_int_swap(p, igraph_vector_int_list_get_ptr(&path_edge, parentptr - 1)); + } + parentptr = VECTOR(ptrlist)[parentptr - 1]; + } + } + } + + IGRAPH_FREE(geodist); + igraph_vector_int_destroy(&ptrlist); + igraph_vector_int_destroy(&ptrhead); + igraph_vector_int_destroy(&neis); + igraph_vector_int_list_destroy(&paths); + igraph_vector_int_list_destroy(&path_edge); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/astar.c b/src/paths/astar.c new file mode 100644 index 0000000..c3d5f55 --- /dev/null +++ b/src/paths/astar.c @@ -0,0 +1,275 @@ +/* + 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 + +*/ + +#include "igraph_paths.h" +#include "igraph_interface.h" +#include "igraph_adjlist.h" +#include "igraph_memory.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +static igraph_error_t null_heuristic( + igraph_real_t *result, igraph_integer_t from, igraph_integer_t to, + void *extra +) { + IGRAPH_UNUSED(from); + IGRAPH_UNUSED(to); + IGRAPH_UNUSED(extra); + + *result = 0; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_shortest_path_astar + * \brief A* gives the shortest path from one vertex to another, with heuristic. + * + * \experimental + * + * Calculates a shortest path from a single source vertex to a single + * target, using the A* algorithm. A* tries to find a shortest path by + * starting at \p from and moving to vertices that lie on a path with + * the lowest estimated length. This length estimate is the sum of two + * numbers: the distance from the source (\p from) to the intermediate vertex, + * and the value returned by the heuristic function. The heuristic function + * provides an estimate the distance between intermediate candidate + * vertices and the target vertex \p to. The A* algorithm is guaranteed + * to give the correct shortest path (if one exists) only if the heuristic + * does not overestimate distances, i.e. if the heuristic function is + * \em admissible. + * + * \param graph The input graph, it can be directed or undirected. + * \param vertices Pointer to an initialized vector or the \c NULL + * pointer. If not \c NULL, then the vertex IDs along + * the path are stored here, including the source and target + * vertices. + * \param edges Pointer to an initialized vector or the \c NULL + * pointer. If not \c NULL, 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 Optional edge weights. Supply \c NULL for unweighted graphs. + * All edge weights must be non-negative. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. Edges with positive infinite weights are ignored. + * \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. + * \param heuristic A function that provides distance estimates to the + * target vertex. See \ref igraph_astar_heuristic_func_t for + * more information. + * \param extra This is passed on to the heuristic function. + * \return Error code. + * + * Time complexity: In the worst case, O(|E|log|V|+|V|), where + * |V| is the number of vertices and + * |E| is the number of edges in the graph. + * The running time depends on the accuracy of the distance estimates + * returned by the heuristic function. Assuming that the heuristic + * is admissible, the better the estimates, the shortert the running + * time. + */ + +igraph_error_t igraph_get_shortest_path_astar(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_astar_heuristic_func_t *heuristic, + void *extra) +{ + igraph_real_t heur_res; + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_vector_t dists; + igraph_integer_t *parent_eids; + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("Starting vertex out of range.", IGRAPH_EINVVID); + } + + if (to < 0 || to >= no_of_nodes) { + IGRAPH_ERROR("End vertex out of range.", IGRAPH_EINVVID); + } + + if (!heuristic) { + heuristic = null_heuristic; + } + + if (weights) { /* If there are no weights, they are treated as 1. */ + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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, found weight of %g.", IGRAPH_EINVAL, min); + } + else if (isnan(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_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + /* dists[v] is the length of the shortest path found so far between 'from' and 'v'. */ + IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes); + igraph_vector_fill(&dists, IGRAPH_INFINITY); + + /* parent_eids[v] is the 1 + the ID of v's inbound edge in the shortest path tree. + * A value of 0 indicates unreachable vertices. */ + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest paths with A* algorithm."); + IGRAPH_FINALLY(igraph_free, parent_eids); + + VECTOR(dists)[from] = 0.0; + IGRAPH_CHECK(heuristic(&heur_res, from, to, extra)); + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, from, -heur_res)); + + igraph_bool_t found = false; + while (!igraph_2wheap_empty(&Q)) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* The from -> u -> to distance estimate is the sum of the + * from -> u distance and the u -> to distance estimate + * obtained from the heuristic. + * + * We use an indexed heap to process 'u' vertices in order + * of the smallest from -> u -> to distance estimate. Since + * we only have a maximum heap available, we store negated values + * in order to obtain smallest values first. The value taken off + * the heap is ignored, we just want the index of 'u'. */ + + igraph_integer_t u; + igraph_2wheap_delete_max_index(&Q, &u); + + /* Reached the target vertex, the search can be stopped. */ + if (u == to) { + found = true; + break; + } + + /* Now we check all neighbors 'u' for a path with a shorter actual (not estimated) + * length than what was found so far. */ + + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, u); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + igraph_integer_t nlen = igraph_vector_int_size(neis); + for (igraph_integer_t i = 0; i < nlen; i++) { + igraph_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t v = IGRAPH_OTHER(graph, edge, u); + igraph_real_t altdist; /* candidate from -> v distance */ + if (weights) { + igraph_real_t weight = VECTOR(*weights)[edge]; + if (weight == IGRAPH_INFINITY) { + continue; + } + altdist = VECTOR(dists)[u] + weight; + } else { + altdist = VECTOR(dists)[u] + 1; + } + igraph_real_t curdist = VECTOR(dists)[v]; + if (curdist == IGRAPH_INFINITY) { + /* This is the first finite from -> v distance we found. + * Here we rely on infinite weight edges having been skipped, see TODO above. */ + VECTOR(dists)[v] = altdist; + parent_eids[v] = edge + 1; + IGRAPH_CHECK(heuristic(&heur_res, v, to, extra)); + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, v, -(altdist + heur_res))); + } else if (altdist < curdist) { + /* This is a shorter from -> v path than what was found before. */ + VECTOR(dists)[v] = altdist; + parent_eids[v] = edge + 1; + IGRAPH_CHECK(heuristic(&heur_res, v, to, extra)); + igraph_2wheap_modify(&Q, v, -(altdist + heur_res)); + } + } + } /* !igraph_2wheap_empty(&Q) */ + + if (!found) { + IGRAPH_WARNING("Couldn't reach the target vertex."); + } + + /* Reconstruct the shortest paths based on vertex and/or edge IDs */ + if (vertices || edges) { + igraph_integer_t size, act, edge; + + if (vertices) { + igraph_vector_int_clear(vertices); + } + if (edges) { + igraph_vector_int_clear(edges); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + size = 0; + act = to; + while (parent_eids[act]) { + size++; + edge = parent_eids[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vertices && (size > 0 || to == from)) { + IGRAPH_CHECK(igraph_vector_int_resize(vertices, size + 1)); + VECTOR(*vertices)[size] = to; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_resize(edges, size)); + } + act = to; + while (parent_eids[act]) { + edge = parent_eids[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + size--; + if (vertices) { + VECTOR(*vertices)[size] = act; + } + if (edges) { + VECTOR(*edges)[size] = edge; + } + } + } + + + IGRAPH_FREE(parent_eids); + igraph_vector_destroy(&dists); + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/bellman_ford.c b/src/paths/bellman_ford.c new file mode 100644 index 0000000..692aeb9 --- /dev/null +++ b/src/paths/bellman_ford.c @@ -0,0 +1,600 @@ +/* -*- 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_bitset.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_memory.h" + +#include "core/interruption.h" + +/** + * \function igraph_distances_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_distances_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. + * \param weights The edge weights. There must not 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_distances() 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_distances() for a faster unweighted version + * or \ref igraph_distances_dijkstra() if you do not have negative + * edge weights. + * + * \example examples/simple/bellman_ford.c + */ +igraph_error_t igraph_distances_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_lazy_inclist_t inclist; + igraph_integer_t i; + igraph_integer_t no_of_from, no_of_to; + igraph_dqueue_int_t Q; + igraph_bitset_t clean_vertices; + igraph_vector_int_t num_queued; + igraph_vit_t fromvit, tovit; + igraph_bool_t all_to; + igraph_vector_t dist; + int counter = 0; + + /* + - 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_distances(graph, res, from, to, mode); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + if (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_INT_INIT_FINALLY(&Q, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&clean_vertices, no_of_nodes); + IGRAPH_VECTOR_INT_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); + + /* No need to check here whether the vertices in 'to' are unique because + * the loop below uses a temporary distance vector that is then copied + * into the result matrix at the end of the outer loop iteration, and + * this is safe even if 'to' contains the same vertex multiple times */ + } + + 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++) { + igraph_integer_t source = IGRAPH_VIT_GET(fromvit); + + igraph_vector_fill(&dist, IGRAPH_INFINITY); + VECTOR(dist)[source] = 0; + igraph_bitset_null(&clean_vertices); + igraph_vector_int_null(&num_queued); + + /* Fill the queue with vertices to be checked */ + for (igraph_integer_t j = 0; j < no_of_nodes; j++) { + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, j)); + } + + while (!igraph_dqueue_int_empty(&Q)) { + if (++counter >= 10000) { + counter = 0; + IGRAPH_ALLOW_INTERRUPTION(); + } + + igraph_integer_t j = igraph_dqueue_int_pop(&Q); + IGRAPH_BIT_SET(clean_vertices, j); + VECTOR(num_queued)[j] += 1; + if (VECTOR(num_queued)[j] > no_of_nodes) { + IGRAPH_ERROR("Negative loop in graph while calculating distances with Bellman-Ford algorithm.", + IGRAPH_ENEGLOOP); + } + + /* If we cannot get to j in finite time yet, there is no need to relax + * its edges */ + if (VECTOR(dist)[j] == IGRAPH_INFINITY) { + continue; + } + + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, j); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + igraph_integer_t nlen = igraph_vector_int_size(neis); + for (igraph_integer_t k = 0; k < nlen; k++) { + igraph_integer_t nei = VECTOR(*neis)[k]; + igraph_integer_t target = IGRAPH_OTHER(graph, nei, j); + igraph_real_t altdist = VECTOR(dist)[j] + VECTOR(*weights)[nei]; + if (VECTOR(dist)[target] > altdist) { + /* relax the edge */ + VECTOR(dist)[target] = altdist; + if (IGRAPH_BIT_TEST(clean_vertices, target)) { + IGRAPH_BIT_CLEAR(clean_vertices, target); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); + } + } + } + } + + /* Copy it to the result */ + if (all_to) { + igraph_matrix_set_row(res, &dist, i); + } else { + igraph_integer_t j; + for (IGRAPH_VIT_RESET(tovit), j = 0; !IGRAPH_VIT_END(tovit); + IGRAPH_VIT_NEXT(tovit), j++) { + igraph_integer_t 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_int_destroy(&Q); + igraph_bitset_destroy(&clean_vertices); + igraph_vector_int_destroy(&num_queued); + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_shortest_paths_bellman_ford + * \brief Weighted shortest path lengths between vertices, allowing negative weights (deprecated). + * + * \deprecated-by igraph_distances_bellman_ford 0.10.0 + */ +igraph_error_t 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) { + return igraph_distances_bellman_ford(graph, res, from, to, weights, mode); +} + +/** + * \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 + * using the Bellman-Ford algorithm, whihc can handle negative weights. When + * there is more than one shortest path between two vertices, only one of them + * is returned. When there are no negative weights, + * \ref igraph_get_shortest_paths_dijkstra() is likely to be faster. + * + * \param graph The input graph, can be directed. + * \param vertices The result, the IDs of the vertices along the paths. + * This is a list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 weights The edge weights. There must not 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_get_shortest_paths() is called. + * Edges with positive infinite weights are ignored. + * \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 parents A pointer to an initialized igraph vector or null. + * If not null, a vector containing the parent of each vertex in + * the single source shortest path tree is returned here. The + * parent of vertex i in the tree is the vertex from which vertex i + * was reached. The parent of the start vertex (in the \c from + * argument) is -1. If the parent is -2, 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 + * \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_distances_bellman_ford() to compute only shortest path + * lengths, but not the paths themselves; \ref igraph_get_shortest_paths() for + * a faster unweighted version or \ref igraph_get_shortest_paths_dijkstra() + * if you do not have negative edge weights. + */ + +igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t *parent_eids; + igraph_lazy_inclist_t inclist; + igraph_integer_t i, j, k; + igraph_dqueue_int_t Q; + igraph_bitset_t clean_vertices; + igraph_vector_int_t num_queued; + igraph_vit_t tovit; + igraph_vector_t dist; + int counter = 0; + + if (!weights) { + return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, + parents, inbound_edges); + } + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must match number of edges.", IGRAPH_EINVAL); + } + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&clean_vertices, no_of_nodes); + IGRAPH_VECTOR_INT_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_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(tovit))); + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(tovit))); + } + + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest paths with Bellman-Ford."); + IGRAPH_FINALLY(igraph_free, parent_eids); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + igraph_vector_fill(&dist, IGRAPH_INFINITY); + VECTOR(dist)[from] = 0; + + /* Fill the queue with vertices to be checked */ + for (j = 0; j < no_of_nodes; j++) { + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, j)); + } + + while (!igraph_dqueue_int_empty(&Q)) { + if (++counter >= 10000) { + counter = 0; + IGRAPH_ALLOW_INTERRUPTION(); + } + + j = igraph_dqueue_int_pop(&Q); + IGRAPH_BIT_SET(clean_vertices, j); + VECTOR(num_queued)[j] += 1; + if (VECTOR(num_queued)[j] > no_of_nodes) { + IGRAPH_ERROR("Negative loop in graph while calculating distances with Bellman-Ford algorithm.", + IGRAPH_ENEGLOOP); + } + + /* If we cannot get to j in finite time yet, there is no need to relax its edges */ + if (VECTOR(dist)[j] == IGRAPH_INFINITY) { + continue; + } + + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, j); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + igraph_integer_t nlen = igraph_vector_int_size(neis); + for (k = 0; k < nlen; k++) { + igraph_integer_t nei = VECTOR(*neis)[k]; + igraph_integer_t target = IGRAPH_OTHER(graph, nei, j); + igraph_real_t weight = VECTOR(*weights)[nei]; + igraph_real_t altdist = VECTOR(dist)[j] + weight; + + if (isnan(weight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + + /* infinite weights are handled correctly here; if an edge has + * infinite weight, altdist will also be infinite so the condition + * will never be true as if the edge was ignored */ + + if (VECTOR(dist)[target] > altdist) { + /* relax the edge */ + VECTOR(dist)[target] = altdist; + parent_eids[target] = nei + 1; + if (IGRAPH_BIT_TEST(clean_vertices, target)) { + IGRAPH_BIT_CLEAR(clean_vertices, target); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); + } + } + } + } + + /* Create `parents' if needed */ + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (i == from) { + /* i is the start vertex */ + VECTOR(*parents)[i] = -1; + } else if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*parents)[i] = -2; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); + } + } + } + + /* Create `inbound_edges' if needed */ + if (inbound_edges) { + IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*inbound_edges)[i] = -1; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*inbound_edges)[i] = parent_eids[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++) { + igraph_integer_t node = IGRAPH_VIT_GET(tovit); + igraph_integer_t size, act, edge; + igraph_vector_int_t *vvec = 0, *evec = 0; + if (vertices) { + vvec = igraph_vector_int_list_get_ptr(vertices, i); + igraph_vector_int_clear(vvec); + } + if (edges) { + evec = igraph_vector_int_list_get_ptr(edges, i); + igraph_vector_int_clear(evec); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + size = 0; + act = node; + while (parent_eids[act]) { + size++; + edge = parent_eids[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vvec && (size > 0 || node == from)) { + IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); + VECTOR(*vvec)[size] = node; + } + if (evec) { + IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); + } + act = node; + while (parent_eids[act]) { + edge = parent_eids[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(parent_eids); + igraph_dqueue_int_destroy(&Q); + igraph_bitset_destroy(&clean_vertices); + igraph_vector_int_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 (Bellman-Ford). + * + * Finds a weighted shortest path from a single source vertex to + * a single target using the 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 initialized 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 must not 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. + */ + +igraph_error_t igraph_get_shortest_path_bellman_ford(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + igraph_vector_int_list_t vertices2, *vp = &vertices2; + igraph_vector_int_list_t edges2, *ep = &edges2; + + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); + } else { + vp = NULL; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); + } else { + ep = NULL; + } + + IGRAPH_CHECK(igraph_get_shortest_paths_bellman_ford(graph, vp, ep, + from, igraph_vss_1(to), + weights, mode, NULL, NULL)); + + /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the + result to the output parameter. */ + if (edges) { + IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); + igraph_vector_int_list_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); + igraph_vector_int_list_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/dijkstra.c b/src/paths/dijkstra.c new file mode 100644 index 0000000..3f5fa24 --- /dev/null +++ b/src/paths/dijkstra.c @@ -0,0 +1,1219 @@ +/* -*- 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_interface.h" +#include "igraph_memory.h" +#include "igraph_nongraph.h" +#include "igraph_stack.h" +#include "igraph_vector_ptr.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +#include /* memset */ + +/** + * \function igraph_distances_dijkstra_cutoff + * \brief Weighted shortest path lengths between vertices, with cutoff. + * + * \experimental + * + * This function is similar to \ref igraph_distances_dijkstra(), but + * paths longer than \p cutoff will not be considered. + * + * \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 \p to argument. + * Vertices that are not reachable within distance \p cutoff will + * be assigned 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_distances() is called. Edges with positive infinite + * weights are ignored. + * \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 cutoff The maximal length of paths that will be considered. + * When the distance of two vertices is greater than this value, + * it will be returned as \c IGRAPH_INFINITY. Negative cutoffs are + * treated as infinity. + * \return Error code. + * + * Time complexity: at most O(s |E| log|V| + |V|), where |V| is the number of + * vertices, |E| the number of edges and s the number of sources. The + * \p cutoff parameter will limit the number of edges traversed from each + * source vertex, which reduces the computation time. + * + * \sa \ref igraph_distances_cutoff() for a (slightly) faster unweighted + * version. + * + * \example examples/simple/distances.c + */ +igraph_error_t igraph_distances_dijkstra_cutoff(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_real_t cutoff) { + + /* 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. + + Tricks: + - The opposite of the distance is stored in the heap, as it is a + maximum heap and we need a minimum heap. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_vit_t fromvit, tovit; + igraph_integer_t no_of_from, no_of_to; + igraph_lazy_inclist_t inclist; + igraph_integer_t i, j; + igraph_bool_t all_to; + igraph_vector_int_t indexv; + + if (!weights) { + return igraph_distances_cutoff(graph, res, from, to, mode, cutoff); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); + } else if (isnan(min)) { + IGRAPH_ERROR("Weights 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_INT_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); + + /* We need to check whether the vertices in 'tovit' are unique; this is + * because the inner while loop of the main algorithm updates the + * distance matrix whenever a shorter path is encountered from the + * source vertex 'i' to a target vertex, and we need to be able to + * map a target vertex to its column in the distance matrix. The mapping + * is constructed by the loop below */ + for (i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit)) { + igraph_integer_t v = IGRAPH_VIT_GET(tovit); + if (VECTOR(indexv)[v]) { + IGRAPH_ERROR("Target vertex list must not have any duplicates.", + IGRAPH_EINVAL); + } + VECTOR(indexv)[v] = ++i; + } + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); + igraph_matrix_fill(res, IGRAPH_INFINITY); + + for (IGRAPH_VIT_RESET(fromvit), i = 0; + !IGRAPH_VIT_END(fromvit); + IGRAPH_VIT_NEXT(fromvit), i++) { + + igraph_integer_t reached = 0; + igraph_integer_t source = IGRAPH_VIT_GET(fromvit); + + igraph_2wheap_clear(&Q); + + /* Many systems distinguish between +0.0 and -0.0. + * Since we store negative distances in the heap, + * we must insert -0.0 in order to get +0.0 as the + * final distance result. */ + igraph_2wheap_push_with_index(&Q, source, -0.0); + + while (!igraph_2wheap_empty(&Q)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); + igraph_vector_int_t *neis; + igraph_integer_t nlen; + + if (cutoff >= 0 && mindist > cutoff) { + continue; + } + + if (all_to) { + MATRIX(*res, i, minnei) = mindist; + } else { + if (VECTOR(indexv)[minnei]) { + MATRIX(*res, i, VECTOR(indexv)[minnei] - 1) = mindist; + 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, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_real_t weight = VECTOR(*weights)[edge]; + + /* Optimization: do not follow infinite-weight edges. */ + if (weight == IGRAPH_INFINITY) { + continue; + } + + igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + weight; + + if (! igraph_2wheap_has_elem(&Q, tto)) { + /* This is the first non-infinite distance */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); + } else if (igraph_2wheap_has_active(&Q, tto)) { + igraph_real_t curdist = -igraph_2wheap_get(&Q, tto); + if (altdist < curdist) { + /* This is a shorter path */ + igraph_2wheap_modify(&Q, tto, -altdist); + } + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + } /* !IGRAPH_VIT_END(fromvit) */ + + if (!all_to) { + igraph_vit_destroy(&tovit); + igraph_vector_int_destroy(&indexv); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + igraph_vit_destroy(&fromvit); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_distances_dijkstra + * \brief Weighted shortest path lengths between vertices. + * + * This function implements Dijkstra's algorithm, which can find + * the weighted shortest path lengths from a source vertex to all + * other vertices. This function allows specifying a set of source + * and target vertices. The algorithm is run independently for each + * source and the results are retained only for the specified targets. + * This implementation uses a binary heap for efficiency. + * + * \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 \p to argument. + * Unreachable vertices have 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_distances() 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|V|+|V|), where |V| is the number of + * vertices, |E| the number of edges and s the number of sources. + * + * \sa \ref igraph_distances() for a (slightly) faster unweighted + * version or \ref igraph_distances_bellman_ford() for a weighted + * variant that works in the presence of negative edge weights (but no + * negative loops) + * + * \example examples/simple/distances.c + */ +igraph_error_t igraph_distances_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) { + return igraph_distances_dijkstra_cutoff(graph, res, from, to, weights, mode, -1); +} + +/** + * \function igraph_shortest_paths_dijkstra + * \brief Weighted shortest path lengths between vertices (deprecated). + * + * \deprecated-by igraph_distances_dijkstra 0.10.0 + */ +igraph_error_t 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) { + return igraph_distances_dijkstra(graph, res, from, to, weights, mode); +} + +/** + * \ingroup structural + * \function igraph_get_shortest_paths_dijkstra + * \brief Weighted shortest paths from a vertex. + * + * Finds weighted shortest paths from a single source vertex to the specified + * sets of target vertices using Dijkstra's algorithm. If there is more than + * one path with the smallest weight between two vertices, this function gives + * only one of them. To find all such paths, use + * \ref igraph_get_all_shortest_paths_dijkstra(). + * + * \param graph The graph object. + * \param vertices The result, the IDs of the vertices along the paths. + * This is a list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 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 parents A pointer to an initialized igraph vector or null. + * If not null, a vector containing the parent of each vertex in + * the single source shortest path tree is returned here. The + * parent of vertex i in the tree is the vertex from which vertex i + * was reached. The parent of the start vertex (in the \c from + * argument) is -1. If the parent is -2, 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 + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|E|log|V|+|V|), where |V| is the number of + * vertices and |E| is the number of edges + * + * \sa \ref igraph_distances_dijkstra() if you only need the path length but + * not the paths themselves; \ref igraph_get_shortest_paths() if all edge + * weights are equal; \ref igraph_get_all_shortest_paths() to find all + * shortest paths between (source, target) pairs; + * \ref igraph_get_shortest_paths_bellman_ford() if some edge weighted are + * negative. + * + * \example examples/simple/igraph_get_shortest_paths_dijkstra.c + */ +igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_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 isfinite() might involve a function call + and we want to spare that. So we store distance+1.0 instead of + distance, and zero denotes infinity. + - `parent_eids' 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. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vit_t vit; + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_vector_t dists; + igraph_integer_t *parent_eids; + igraph_bool_t *is_target; + igraph_integer_t i, to_reach; + + if (!weights) { + return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, + parents, inbound_edges); + } + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); + } + else if (isnan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(vit))); + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edges, 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(&dists, no_of_nodes); + igraph_vector_fill(&dists, -1.0); + + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest paths with Dijkstra's algorithm."); + IGRAPH_FINALLY(igraph_free, parent_eids); + + is_target = IGRAPH_CALLOC(no_of_nodes, igraph_bool_t); + IGRAPH_CHECK_OOM(is_target, "Insufficient memory for shortest paths with Dijkstra's algorithm."); + 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[ IGRAPH_VIT_GET(vit) ]) { + is_target[ IGRAPH_VIT_GET(vit) ] = true; + } else { + to_reach--; /* this node was given multiple times */ + } + } + + VECTOR(dists)[from] = 0.0; /* zero distance */ + parent_eids[from] = 0; + igraph_2wheap_push_with_index(&Q, from, 0); + + while (!igraph_2wheap_empty(&Q) && to_reach > 0) { + igraph_integer_t 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] = false; + to_reach--; + } + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(&inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (i = 0; i < nlen; i++) { + igraph_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t 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; + parent_eids[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; + parent_eids[tto] = edge + 1; + igraph_2wheap_modify(&Q, tto, -altdist); + } + } + } /* !igraph_2wheap_empty(&Q) */ + + if (to_reach > 0) { + IGRAPH_WARNING("Couldn't reach some vertices."); + } + + /* Create `parents' if needed */ + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (i == from) { + /* i is the start vertex */ + VECTOR(*parents)[i] = -1; + } else if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*parents)[i] = -2; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); + } + } + } + + /* Create `inbound_edges' if needed */ + if (inbound_edges) { + IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*inbound_edges)[i] = -1; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*inbound_edges)[i] = parent_eids[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++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t size, act, edge; + igraph_vector_int_t *vvec = 0, *evec = 0; + if (vertices) { + vvec = igraph_vector_int_list_get_ptr(vertices, i); + igraph_vector_int_clear(vvec); + } + if (edges) { + evec = igraph_vector_int_list_get_ptr(edges, i); + igraph_vector_int_clear(evec); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + size = 0; + act = node; + while (parent_eids[act]) { + size++; + edge = parent_eids[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vvec && (size > 0 || node == from)) { + IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); + VECTOR(*vvec)[size] = node; + } + if (evec) { + IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); + } + act = node; + while (parent_eids[act]) { + edge = parent_eids[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(parent_eids); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_shortest_path_dijkstra + * \brief Weighted shortest path from one vertex to another one (Dijkstra). + * + * Finds a weighted shortest path from a single source vertex to + * a single target, using Dijkstra's algorithm. If more than one + * shortest path exists, an arbitrary one is returned. + * + * + * 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 initialized 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|V|+|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. + */ + +igraph_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + igraph_vector_int_list_t vertices2, *vp = &vertices2; + igraph_vector_int_list_t edges2, *ep = &edges2; + + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); + } else { + vp = NULL; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); + } else { + ep = NULL; + } + + IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph, vp, ep, + from, igraph_vss_1(to), + weights, mode, NULL, NULL)); + + /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the + result to the output parameter. */ + if (edges) { + IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); + igraph_vector_int_list_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); + igraph_vector_int_list_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_get_all_shortest_paths_dijkstra + * \brief All weighted shortest paths (geodesics) from a vertex. + * + * \param graph The graph object. + * \param vertices Pointer to an initialized integer vector list or NULL. + * If not NULL, then 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 edges Pointer to an initialized integer vector list or NULL. If + * not NULL, then each vector object contains the edges 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_int_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 an invalid vertex ID + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|E|log|V|+|V|), where |V| is the number of + * vertices and |E| is the number of edges + * + * \sa \ref igraph_distances_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 + */ +igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_vector_int_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. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vit_t vit; + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_vector_t dists; + igraph_vector_int_t index; + igraph_vector_int_t order; + igraph_vector_ptr_t parents, parents_edge; + + unsigned char *is_target; /* uses more than two discrete values, can't be 'bool' */ + igraph_integer_t i, n, to_reach; + igraph_bool_t free_vertices = false; + int cmp_result; + const double eps = IGRAPH_SHORTEST_PATH_EPSILON; + + if (!weights) { + return igraph_get_all_shortest_paths(graph, vertices, edges, nrgeo, from, to, mode); + } + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); + } + + if (vertices == NULL && nrgeo == NULL && edges == NULL) { + return IGRAPH_SUCCESS; + } + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length does not match number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + igraph_real_t min = igraph_vector_min(weights); + if (min < 0) { + IGRAPH_ERRORF("Edge weights must not be negative, got %g.", IGRAPH_EINVAL, min); + } + else if (isnan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); + } + } + + /* parents stores a vector for each vertex, listing the parent vertices + * of each vertex in the traversal. Right now we do not use an + * igraph_vector_int_list_t because that would pre-initialize vectors + * for all the nodes even if the traversal would involve only a small part + * of the graph */ + 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_vector_destroy); + + /* parents_edge stores a vector for each vertex, listing the parent edges + * of each vertex in the traversal */ + IGRAPH_CHECK(igraph_vector_ptr_init(&parents_edge, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &parents_edge); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&parents_edge, igraph_vector_destroy); + + for (i = 0; i < no_of_nodes; i++) { + igraph_vector_int_t *parent_vec, *parent_edge_vec; + + parent_vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(parent_vec, "Insufficient memory for all shortest paths with Dijkstra's algorithm."); + IGRAPH_FINALLY(igraph_free, parent_vec); + IGRAPH_CHECK(igraph_vector_int_init(parent_vec, 0)); + VECTOR(parents)[i] = parent_vec; + IGRAPH_FINALLY_CLEAN(1); + + parent_edge_vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(parent_edge_vec, "Insufficient memory for all shortest paths with Dijkstra's algorithm."); + IGRAPH_FINALLY(igraph_free, parent_edge_vec); + IGRAPH_CHECK(igraph_vector_int_init(parent_edge_vec, 0)); + VECTOR(parents_edge)[i] = parent_edge_vec; + IGRAPH_FINALLY_CLEAN(1); + } + + /* 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_INT_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); + IGRAPH_CHECK_OOM(is_target, "Insufficient memory for all shortest paths with Dijkstra's algorithm."); + 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[ IGRAPH_VIT_GET(vit) ]) { + is_target[ IGRAPH_VIT_GET(vit) ] = 1; + } else { + to_reach--; /* this node was given multiple times */ + } + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + VECTOR(dists)[from] = 0.0; /* zero distance */ + igraph_2wheap_push_with_index(&Q, from, 0.0); + + while (!igraph_2wheap_empty(&Q) && to_reach > 0) { + igraph_integer_t 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--; + } + + /* Mark that we have reached this vertex */ + IGRAPH_CHECK(igraph_vector_int_push_back(&order, minnei)); + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(&inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (i = 0; i < nlen; i++) { + igraph_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; + igraph_real_t curdist = VECTOR(dists)[tto]; + igraph_vector_int_t *parent_vec, *parent_edge_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_int_t*)VECTOR(parents)[tto]; + IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); + parent_edge_vec = (igraph_vector_int_t*)VECTOR(parents_edge)[tto]; + IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); + + 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_int_t*) VECTOR(parents)[tto]; + IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); + parent_edge_vec = (igraph_vector_int_t*) VECTOR(parents_edge)[tto]; + IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); + } else if (cmp_result > 0 /* altdist < curdist */) { + /* This is a shorter path */ + VECTOR(dists)[tto] = altdist; + + parent_vec = (igraph_vector_int_t*)VECTOR(parents)[tto]; + igraph_vector_int_clear(parent_vec); + IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); + parent_edge_vec = (igraph_vector_int_t*)VECTOR(parents_edge)[tto]; + igraph_vector_int_clear(parent_edge_vec); + IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); + + igraph_2wheap_modify(&Q, tto, -altdist); + } + } + } /* !igraph_2wheap_empty(&Q) */ + + if (to_reach > 0) { + IGRAPH_WARNING("Couldn't reach some of the requested target 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_int_print(&order); + + printf("Parent vertices:\n"); + for (i = 0; i < no_of_nodes; i++) { + if (igraph_vector_int_size(VECTOR(parents)[i]) > 0) { + printf("[%ld]: ", i); + igraph_vector_int_print(VECTOR(parents)[i]); + } + } + */ + + if (nrgeo) { + IGRAPH_CHECK(igraph_vector_int_resize(nrgeo, no_of_nodes)); + igraph_vector_int_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)[from] = 1; + n = igraph_vector_int_size(&order); + for (i = 1; i < n; i++) { + igraph_integer_t node, j, k; + igraph_vector_int_t *parent_vec; + + node = VECTOR(order)[i]; + /* now, take the parent vertices */ + parent_vec = (igraph_vector_int_t*)VECTOR(parents)[node]; + k = igraph_vector_int_size(parent_vec); + for (j = 0; j < k; j++) { + VECTOR(*nrgeo)[node] += VECTOR(*nrgeo)[VECTOR(*parent_vec)[j]]; + } + } + } + + if (vertices || edges) { + igraph_vector_int_t *path, *parent_vec, *parent_edge_vec; + igraph_vector_t *paths_index; + igraph_stack_int_t stack; + igraph_integer_t 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_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_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 = IGRAPH_VIT_GET(vit); + if (!is_target[i]) { + is_target[i] = 1; + IGRAPH_CHECK(igraph_stack_int_push(&stack, i)); + } + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + while (!igraph_stack_int_empty(&stack)) { + /* For each parent of node i, get its parents */ + igraph_integer_t el = igraph_stack_int_pop(&stack); + parent_vec = (igraph_vector_int_t*) VECTOR(parents)[el]; + i = igraph_vector_int_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 = VECTOR(*parent_vec)[j]; + if (!is_target[n]) { + is_target[n] = 2; + IGRAPH_CHECK(igraph_stack_int_push(&stack, n)); + } + } + } + igraph_stack_int_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 vertices 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_int_size(&order); + igraph_vector_null(paths_index); + + if (edges) { + igraph_vector_int_list_clear(edges); + } + + if (vertices) { + igraph_vector_int_list_clear(vertices); + } else { + /* If the 'vertices' vector doesn't exist, then create one, in order + * for the algorithm to work. */ + vertices = IGRAPH_CALLOC(1, igraph_vector_int_list_t); + IGRAPH_CHECK_OOM(vertices, "Insufficient memory for all shortest paths with Dijkstra's algorithm."); + IGRAPH_FINALLY(igraph_free, vertices); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(vertices, 0); + free_vertices = true; + } + + /* by definition, the shortest path leading to the starting vertex + * consists of the vertex itself only */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &path)); + IGRAPH_CHECK(igraph_vector_int_push_back(path, from)); + + if (edges) { + /* the shortest path from the source to itself is empty */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &path)); + } + VECTOR(*paths_index)[from] = 1; + + for (i = 1; i < n; i++) { + igraph_integer_t m, path_count; + igraph_vector_int_t *parent_path, *parent_path_edge; + + node = 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_int_list_size(vertices); + VECTOR(*paths_index)[node] = path_count + 1; + + /* now, take the parent vertices */ + parent_vec = (igraph_vector_int_t*) VECTOR(parents)[node]; + parent_edge_vec = (igraph_vector_int_t*) VECTOR(parents_edge)[node]; + m = igraph_vector_int_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 */ + igraph_integer_t parent_node = VECTOR(*parent_vec)[j]; + igraph_integer_t parent_edge = VECTOR(*parent_edge_vec)[j]; + igraph_integer_t parent_path_idx = VECTOR(*paths_index)[parent_node] - 1; + /* + printf(" Considering parent: %ld\n", parent_node); + printf(" Paths to parent start at index %ld in vertices\n", parent_path_idx); + */ + IGRAPH_ASSERT(parent_path_idx >= 0); + for (; parent_path_idx < path_count; parent_path_idx++) { + parent_path = igraph_vector_int_list_get_ptr(vertices, parent_path_idx); + if (igraph_vector_int_tail(parent_path) != parent_node) { + break; + } + + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &path)); + + /* We need to re-read parent_path because the previous push_back_new() + * call might have reallocated the entire vector list */ + parent_path = igraph_vector_int_list_get_ptr(vertices, parent_path_idx); + IGRAPH_CHECK(igraph_vector_int_update(path, parent_path)); + IGRAPH_CHECK(igraph_vector_int_push_back(path, node)); + + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &path)); + if (parent_node != from) { + parent_path_edge = igraph_vector_int_list_get_ptr(edges, parent_path_idx); + IGRAPH_CHECK(igraph_vector_int_update(path, parent_path_edge)); + } + IGRAPH_CHECK(igraph_vector_int_push_back(path, parent_edge)); + } + } + } + } + + /* free those paths from the result vector that we won't need */ + n = igraph_vector_int_list_size(vertices); + i = 0; + while (i < n) { + igraph_integer_t tmp; + path = igraph_vector_int_list_get_ptr(vertices, i); + tmp = igraph_vector_int_tail(path); + if (is_target[tmp] == 1) { + /* we need this path, keep it */ + i++; + } else { + /* we don't need this path, free it */ + igraph_vector_int_list_discard_fast(vertices, i); + if (edges) { + igraph_vector_int_list_discard_fast(edges, i); + } + n--; + } + } + + /* sort the remaining paths by the target vertices */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, 0); + igraph_vector_int_list_sort_ind(vertices, &index, igraph_vector_int_colex_cmp); + IGRAPH_CHECK(igraph_vector_int_list_permute(vertices, &index)); + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_permute(edges, &index)); + } + igraph_vector_int_destroy(&index); + IGRAPH_FINALLY_CLEAN(1); + } + + /* free the allocated memory */ + if (free_vertices) { + igraph_vector_int_list_destroy(vertices); + IGRAPH_FREE(vertices); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_int_destroy(&order); + IGRAPH_FREE(is_target); + igraph_vector_destroy(&dists); + igraph_vector_ptr_destroy_all(&parents); + igraph_vector_ptr_destroy_all(&parents_edge); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/distances.c b/src/paths/distances.c new file mode 100644 index 0000000..180a74b --- /dev/null +++ b/src/paths/distances.c @@ -0,0 +1,1051 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + 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 . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_datatype.h" +#include "igraph_dqueue.h" +#include "igraph_iterators.h" +#include "igraph_interface.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" +#include "igraph_vector.h" + +#include "core/interruption.h" +#include "core/indheap.h" + +/* When vid_ecc is not NULL, only one vertex ID should be passed in vids. + * vid_ecc will then return the id of the vertex farthest from the one in + * vids. If unconn == false and not all other vertices were reachable from + * the single given vertex, -1 is returned in vid_ecc. */ +static igraph_error_t igraph_i_eccentricity(const igraph_t *graph, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_lazy_adjlist_t *adjlist, + igraph_integer_t *vid_ecc, + igraph_bool_t unconn) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vit_t vit; + igraph_vector_int_t counted; + igraph_integer_t i, mark = 1; + igraph_integer_t min_degree = 0; + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&counted, no_of_nodes); + + 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++) { + + igraph_integer_t source; + igraph_integer_t nodes_reached = 1; + source = IGRAPH_VIT_GET(vit); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + VECTOR(counted)[source] = mark; + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&q); + igraph_integer_t dist = igraph_dqueue_int_pop(&q); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(adjlist, act); + igraph_integer_t j, n; + + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (VECTOR(counted)[nei] != mark) { + VECTOR(counted)[nei] = mark; + nodes_reached++; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, dist + 1)); + } + } + if (vid_ecc) { + /* Return the vertex ID of the vertex which has the lowest + * degree of the vertices most distant from the starting + * vertex. Assumes there is only 1 vid in vids. Used for + * pseudo_diameter calculations. */ + if (dist > VECTOR(*res)[i] || (dist == VECTOR(*res)[i] && n < min_degree)) { + VECTOR(*res)[i] = dist; + *vid_ecc = act; + min_degree = n; + } + } else if (dist > VECTOR(*res)[i]) { + VECTOR(*res)[i] = dist; + } + } /* while !igraph_dqueue_int_empty(dqueue) */ + + if (nodes_reached != no_of_nodes && !unconn && vid_ecc) { + *vid_ecc = -1; + break; + } + } /* for IGRAPH_VIT_NEXT(vit) */ + + igraph_vector_int_destroy(&counted); + igraph_vit_destroy(&vit); + igraph_dqueue_int_destroy(&q); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * This function finds the weighted eccentricity and returns it via \p ecc. + * It's used for igraph_pseudo_diameter_dijkstra() and igraph_eccentricity_dijkstra(). + * \p vid_ecc returns the vertex id of the ecc with the greatest + * distance from \p vid_start. If two vertices have the same greatest distance, + * the one with the lowest degree is chosen. + * When the graph is not (strongly) connected and \p unconn is false, then \p ecc + * wil be set to infinity, and \p vid_ecc to -1; + */ +static igraph_error_t igraph_i_eccentricity_dijkstra( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *ecc, + igraph_integer_t vid_start, + igraph_integer_t *vid_ecc, + igraph_bool_t unconn, + igraph_lazy_inclist_t *inclist) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_2wheap_t Q; + igraph_vector_t vec_dist; + igraph_integer_t i; + igraph_real_t degree_ecc, dist; + igraph_integer_t degree_i; + igraph_vector_int_t *neis; + + IGRAPH_VECTOR_INIT_FINALLY(&vec_dist, no_of_nodes); + igraph_vector_fill(&vec_dist, IGRAPH_INFINITY); + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, vid_start, -1.0); + + while (!igraph_2wheap_empty(&Q)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); + igraph_integer_t nlen; + + VECTOR(vec_dist)[minnei] = mindist - 1.0; + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (i = 0; i < nlen; i++) { + igraph_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t 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 (altdist == IGRAPH_INFINITY) { + /* Ignore edges with positive infinite weights */ + } else 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_2wheap_modify(&Q, tto, -altdist); + } + } + } + + *ecc = 0; + *vid_ecc = vid_start; + degree_ecc = 0; + + for (i = 0; i < no_of_nodes; i++) { + if (i == vid_start) { + continue; + } + dist = VECTOR(vec_dist)[i]; + + /* inclist is used to ignore multiple edges when finding the degree */ + neis = igraph_lazy_inclist_get(inclist, i); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + degree_i = igraph_vector_int_size(neis); + + if (dist > *ecc) { + if (!isfinite(dist)) { + if (!unconn) { + *ecc = IGRAPH_INFINITY; + *vid_ecc = -1; + break; + } + } else { + *ecc = dist; + *vid_ecc = i; + degree_ecc = degree_i; + } + } else if (dist == *ecc) { + if (degree_i < degree_ecc) { + degree_ecc = degree_i; + *vid_ecc = i; + } + } + } + igraph_2wheap_destroy(&Q); + igraph_vector_destroy(&vec_dist); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +/** + * \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 + */ + +igraph_error_t igraph_eccentricity(const igraph_t *graph, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode) { + igraph_lazy_adjlist_t adjlist; + + 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_i_eccentricity(graph, res, vids, &adjlist, + /*vid_ecc*/ NULL, /*unconn*/ 1)); + igraph_lazy_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_eccentricity_dijkstra + * \brief Eccentricity of some vertices, using weighted edges. + * + * 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 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_eccentricity() is called. Edges with positive + * infinite weights are ignored. + * \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| |E| log|V| + |V|), where |V| is the number of + * vertices, |E| the number of edges. + * + */ + +igraph_error_t igraph_eccentricity_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode) { + igraph_lazy_inclist_t inclist; + igraph_vit_t vit; + igraph_integer_t dump; + igraph_real_t ecc; + igraph_integer_t no_of_edges = igraph_ecount(graph); + + if (weights == NULL) { + return igraph_eccentricity(graph, res, vids, mode); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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 (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, + IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_vector_resize(res, 0)); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + + for (IGRAPH_VIT_RESET(vit); + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit)) { + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc, IGRAPH_VIT_GET(vit), /*vid_ecc*/ &dump, /*unconn*/ 1, &inclist)); + IGRAPH_CHECK(igraph_vector_push_back(res, ecc)); + } + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \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_radius_dijkstra() for the weighted version, + * \ref igraph_diameter() for the maximum eccentricity, + * \ref igraph_eccentricity() for the eccentricities of all vertices. + * + * \example examples/simple/igraph_radius.c + */ + +igraph_error_t igraph_radius(const igraph_t *graph, igraph_real_t *radius, + igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (no_of_nodes == 0) { + *radius = IGRAPH_NAN; + } else { + igraph_vector_t ecc; + IGRAPH_VECTOR_INIT_FINALLY(&ecc, igraph_vcount(graph)); + IGRAPH_CHECK(igraph_eccentricity(graph, &ecc, igraph_vss_all(), + mode)); + *radius = igraph_vector_min(&ecc); + igraph_vector_destroy(&ecc); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_radius_dijkstra + * \brief Radius of a graph, using weighted edges. + * + * \experimental + * + * 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 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_radius() is called. Edges with positive + * infinite weights are ignored. + * \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| |E| log|V| + |V|), where |V| is the number of + * vertices, |E| the number of edges. + * + * \sa \ref igraph_radius() for the unweighted version, + * \ref igraph_diameter_dijkstra() for the maximum weighted eccentricity, + * \ref igraph_eccentricity_dijkstra() for weighted eccentricities of + * all vertices. + */ + +igraph_error_t igraph_radius_dijkstra(const igraph_t *graph, const igraph_vector_t *weights, + igraph_real_t *radius, igraph_neimode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (weights == NULL) { + return igraph_radius(graph, radius, mode); + } + + if (no_of_nodes == 0) { + *radius = IGRAPH_NAN; + } else { + igraph_vector_t ecc; + IGRAPH_VECTOR_INIT_FINALLY(&ecc, igraph_vcount(graph)); + IGRAPH_CHECK(igraph_eccentricity_dijkstra(graph, weights, &ecc, igraph_vss_all(), mode)); + *radius = igraph_vector_min(&ecc); + igraph_vector_destroy(&ecc); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_pseudo_diameter + * \brief Approximation and lower bound of diameter. + * + * This algorithm finds a pseudo-peripheral vertex and returns its + * eccentricity. This value can be used as an approximation + * and lower bound of the diameter of a graph. + * + * + * A pseudo-peripheral vertex is a vertex v, such that for every + * vertex u which is as far away from v as possible, v is also as + * far away from u as possible. The process of finding one depends + * on where the search starts, and for a disconnected graph the + * maximum diameter found will be that of the component \p vid_start + * is in. + * + * \param graph The input graph, if it is directed, its edge directions + * are ignored. + * \param diameter Pointer to a real variable, the result is stored + * here. + * \param vid_start Id of the starting vertex. If this is negative, a + * random starting vertex is chosen. + * \param from Pointer to an integer, if not \c NULL it will be set to the + * source vertex of the diameter path. If \p unconn is \c false, and + * a disconnected graph is detected, this is set to -1. + * \param to Pointer to an integer, if not \c NULL it will be set to the + * target vertex of the diameter path. If \p unconn is \c false, and + * a disconnected graph is detected, this is set to -1. + * \param directed Boolean, whether to consider directed + * paths. Ignored for undirected graphs. + * \param unconn What to do if the graph is not connected. If + * \c true the longest geodesic within a component + * will be returned, otherwise \c IGRAPH_INFINITY is returned. + * \return Error code. + * + * Time complexity: O(|V||E|)), where |V| is the number of + * vertices and |E| is the number of edges. + * + * \sa \ref igraph_eccentricity(), \ref igraph_diameter(). + * + */ +igraph_error_t igraph_pseudo_diameter(const igraph_t *graph, + igraph_real_t *diameter, + igraph_integer_t vid_start, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_bool_t directed, + igraph_bool_t unconn) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_real_t ecc_v; + igraph_real_t ecc_u; + igraph_integer_t vid_ecc; + igraph_integer_t ito, ifrom; + igraph_bool_t inf = false; + + if (vid_start >= no_of_nodes) { + IGRAPH_ERROR("Starting vertex ID for pseudo-diameter out of range.", IGRAPH_EINVAL); + } + + /* We will reach here when vid_start < 0 and the graph has no vertices. */ + if (no_of_nodes == 0) { + if (diameter) { + *diameter = IGRAPH_NAN; + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + return IGRAPH_SUCCESS; + } + + if (vid_start < 0) { + RNG_BEGIN(); + vid_start = RNG_INTEGER(0, no_of_nodes - 1); + RNG_END(); + } + + if (!igraph_is_directed(graph) || !directed) { + igraph_lazy_adjlist_t adjlist; + igraph_vector_t ecc_vec; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, + IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + ifrom = vid_start; + IGRAPH_VECTOR_INIT_FINALLY(&ecc_vec, no_of_nodes); + + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_vec, igraph_vss_1(vid_start), + &adjlist, &vid_ecc, unconn)); + ecc_u = VECTOR(ecc_vec)[0]; + + if (!unconn && vid_ecc == -1) { + inf = true; + } else { + while (true) { + IGRAPH_ALLOW_INTERRUPTION(); + + ito = vid_ecc; + + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_vec, igraph_vss_1(vid_ecc), + &adjlist, &vid_ecc, 1)); + + ecc_v = VECTOR(ecc_vec)[0]; + + if (ecc_u < ecc_v) { + ecc_u = ecc_v; + ifrom = ito; + } else { + break; + } + } + } + igraph_vector_destroy(&ecc_vec); + igraph_lazy_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(2); + } else { + igraph_vector_t ecc_out; + igraph_vector_t ecc_in; + igraph_integer_t vid_ecc_in; + igraph_integer_t vid_ecc_out; + igraph_integer_t vid_end; + igraph_bool_t direction; + igraph_lazy_adjlist_t adjlist_in; + igraph_lazy_adjlist_t adjlist_out; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist_in, IGRAPH_IN, + IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist_in); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist_out, IGRAPH_OUT, + IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist_out); + + IGRAPH_VECTOR_INIT_FINALLY(&ecc_in, igraph_vcount(graph)); + IGRAPH_VECTOR_INIT_FINALLY(&ecc_out, igraph_vcount(graph)); + + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_out, igraph_vss_1(vid_start), + &adjlist_out, &vid_ecc_out, unconn)); + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_in, igraph_vss_1(vid_start), + &adjlist_in, &vid_ecc_in, unconn)); + + /* A directed graph is strongly connected iff all vertices are reachable + * from vid_start both when moving along or moving opposite the edge directions. */ + if (!unconn && (vid_ecc_out == -1 || vid_ecc_in == -1)) { + inf = true; + } else { + if (VECTOR(ecc_out)[0] > VECTOR(ecc_in)[0]) { + vid_ecc = vid_ecc_out; + ecc_u = VECTOR(ecc_out)[0]; + } else { + vid_ecc = vid_ecc_in; + ecc_u = VECTOR(ecc_in)[0]; + } + + while (1) { + IGRAPH_ALLOW_INTERRUPTION(); + + vid_end = vid_ecc; + + /* TODO: In the undirected case, we break ties between vertices at the + * same distance based on their degree. In te directed case, should we + * use in-, out- or total degree? */ + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_out, igraph_vss_1(vid_ecc), + &adjlist_out, &vid_ecc_out, 1)); + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_in, igraph_vss_1(vid_ecc), + &adjlist_in, &vid_ecc_in, 1)); + + if (VECTOR(ecc_out)[0] > VECTOR(ecc_in)[0]) { + vid_ecc = vid_ecc_out; + ecc_v = VECTOR(ecc_out)[0]; + direction = 1; + } else { + vid_ecc = vid_ecc_in; + ecc_v = VECTOR(ecc_in)[0]; + direction = 0; + } + + if (ecc_u < ecc_v) { + ecc_u = ecc_v; + vid_start = vid_end; + } else { + break; + } + } + + if (direction) { + ifrom = vid_end; + ito = vid_start; + } else { + ifrom = vid_start; + ito = vid_end; + } + + } + igraph_vector_destroy(&ecc_out); + igraph_vector_destroy(&ecc_in); + igraph_lazy_adjlist_destroy(&adjlist_in); + igraph_lazy_adjlist_destroy(&adjlist_out); + IGRAPH_FINALLY_CLEAN(4); + } + + if (inf) { + if (diameter) { + *diameter = IGRAPH_INFINITY; + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + } else { + if (diameter) { + *diameter = ecc_u; + } + if (from) { + *from = ifrom; + } + if (to) { + *to = ito; + } + } + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_pseudo_diameter_dijkstra + * \brief Approximation and lower bound of the diameter of a weighted graph. + * + * This algorithm finds a pseudo-peripheral vertex and returns its + * weighted eccentricity. This value can be used as an approximation + * and lower bound of the diameter of a graph. + * + * + * A pseudo-peripheral vertex is a vertex v, such that for every + * vertex u which is as far away from v as possible, v is also as + * far away from u as possible. The process of finding one depends + * on where the search starts, and for a disconnected graph the + * maximum diameter found will be that of the component \p vid_start + * is in. + * + * + * If the graph has no vertices, \c IGRAPH_NAN is returned. + * + * \param graph The input graph, can be directed or undirected. + * \param weights The edge weights of the graph. Can be \c NULL for an + * unweighted graph. All weights should be non-negative. Edges with + * positive infinite weights are ignored. + * \param diameter This will contain the weighted pseudo-diameter. + * \param vid_start Id of the starting vertex. If this is negative, a + * random starting vertex is chosen. + * \param from If not \c NULL this 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 to If not \c NULL this 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 directed Boolean, whether to consider directed + * paths. Ignored for undirected graphs. + * \param unconn What to do if the graph is not connected. If + * \c true the longest geodesic within a component + * will be returned, otherwise \c IGRAPH_INFINITY is + * returned. + * \return Error code. + * + * Time complexity: O(|V||E|*log|E|), |V| is the number of vertices, + * |E| is the number of edges. + * + * \sa \ref igraph_diameter_dijkstra() + */ +igraph_error_t igraph_pseudo_diameter_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *diameter, + igraph_integer_t vid_start, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_bool_t directed, + igraph_bool_t unconn) { + + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_real_t ecc_v; + igraph_real_t ecc_u; + igraph_integer_t vid_ecc; + igraph_integer_t ito, ifrom; + igraph_bool_t inf = false; + + if (vid_start >= no_of_nodes) { + IGRAPH_ERROR("Starting vertex ID for pseudo-diameter out of range.", IGRAPH_EINVAL); + } + + if (!weights) { + return igraph_pseudo_diameter(graph, diameter, vid_start, from, to, directed, unconn); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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 (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + /* We will reach here when vid_start < 0 and the graph has no vertices. */ + if (no_of_nodes == 0) { + if (diameter) { + *diameter = IGRAPH_NAN; + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + return IGRAPH_SUCCESS; + } + + if (vid_start < 0) { + RNG_BEGIN(); + vid_start = RNG_INTEGER(0, no_of_nodes - 1); + RNG_END(); + } + + if (!igraph_is_directed(graph) || !directed) { + igraph_lazy_inclist_t inclist; + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + ifrom = vid_start; + + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_u, vid_start, &vid_ecc, unconn, &inclist)); + + inf = !isfinite(ecc_u); + + if (!inf) { + while (1) { + IGRAPH_ALLOW_INTERRUPTION(); + + ito = vid_ecc; + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_v, vid_ecc, &vid_ecc, unconn, &inclist)); + + if (ecc_u < ecc_v) { + ecc_u = ecc_v; + ifrom = ito; + } else { + break; + } + } + } + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_real_t ecc_out; + igraph_real_t ecc_in; + igraph_integer_t vid_ecc_in; + igraph_integer_t vid_ecc_out; + igraph_integer_t vid_end; + igraph_bool_t direction; + igraph_lazy_inclist_t inclist_out; + igraph_lazy_inclist_t inclist_in; + + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist_out, IGRAPH_OUT, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist_out); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist_in, IGRAPH_IN, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist_in); + + + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_out, vid_start, &vid_ecc_out, unconn, &inclist_out)); + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_in, vid_start, &vid_ecc_in, unconn, &inclist_in)); + + /* A directed graph is strongly connected iff all vertices are reachable + * from vid_start both when moving along or moving opposite the edge directions. */ + if (!unconn && (vid_ecc_out == -1 || vid_ecc_in == -1)) { + inf = true; + } else { + if (ecc_out > ecc_in) { + vid_ecc = vid_ecc_out; + ecc_u = ecc_out; + } else { + vid_ecc = vid_ecc_in; + ecc_u = ecc_in; + } + + while (1) { + IGRAPH_ALLOW_INTERRUPTION(); + + vid_end = vid_ecc; + + /* TODO: In the undirected case, we break ties between vertices at the + * same distance based on their degree. In te directed case, should we + * use in-, out- or total degree? */ + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_out, vid_ecc, &vid_ecc_out, unconn, &inclist_out)); + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_in, vid_ecc, &vid_ecc_in, unconn, &inclist_in)); + + if (ecc_out > ecc_in) { + vid_ecc = vid_ecc_out; + ecc_v = ecc_out; + direction = 1; + } else { + vid_ecc = vid_ecc_in; + ecc_v = ecc_in; + direction = 0; + } + + if (ecc_u < ecc_v) { + ecc_u = ecc_v; + vid_start = vid_end; + } else { + break; + } + } + + if (direction) { + ifrom = vid_end; + ito = vid_start; + } else { + ifrom = vid_start; + ito = vid_end; + } + } + igraph_lazy_inclist_destroy(&inclist_out); + igraph_lazy_inclist_destroy(&inclist_in); + IGRAPH_FINALLY_CLEAN(2); + } + + if (inf) { + if (diameter) { + *diameter = IGRAPH_INFINITY; + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + } else { + if (diameter) { + *diameter = ecc_u; + } + if (from) { + *from = ifrom; + } + if (to) { + *to = ito; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_graph_center + * \brief Central vertices of a graph. + * + * The central vertices of a graph are calculated by finding the vertices + * with the minimum eccentricity. This concept is typically applied to + * (strongly) connected graphs. In disconnected graphs, the smallest + * eccentricity is taken across all components. + * + * \param graph The input graph, it can be directed or undirected. + * \param res Pointer to an initialized vector, 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_graph_center_dijkstra(), + * \ref igraph_eccentricity(), \ref igraph_radius() + * + */ +igraph_error_t igraph_graph_center( + const igraph_t *graph, igraph_vector_int_t *res, igraph_neimode_t mode +) { + + igraph_vector_t ecc; + + igraph_vector_int_clear(res); + if (igraph_vcount(graph) == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&ecc, 0); + IGRAPH_CHECK(igraph_eccentricity(graph, &ecc, igraph_vss_all(), mode)); + + /* igraph_eccentricity() does not return infinity or NaN, and the null graph + * case was handled above, therefore calling vector_min() is safe. */ + igraph_real_t min_eccentricity = igraph_vector_min(&ecc); + igraph_integer_t n = igraph_vector_size(&ecc); + for (igraph_integer_t i = 0; i < n; i++) { + if (VECTOR(ecc)[i] == min_eccentricity) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, i)); + } + } + + igraph_vector_destroy(&ecc); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_graph_center_dijkstra + * \brief Central vertices of a graph, using weighted edges. + * + * \experimental + * + * The central vertices of a graph are calculated by finding the vertices + * with the minimum eccentricity. This function takes edge weights into + * account and uses Dijkstra's algorithm for the shortest path calculation. + * The concept of the graph center is typically applied to + * (strongly) connected graphs. In disconnected graphs, the smallest + * eccentricity is taken across all components. + * + * \param graph The input graph, it can be directed or undirected. + * \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_graph_center() is called. Edges with positive + * infinite weights are ignored. + * \param res Pointer to an initialized vector, 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| |E| log|V| + |V|), where |V| is the number of + * vertices, |E| the number of edges. + * + * \sa \ref igraph_graph_center(), + * \ref igraph_eccentricity_dijkstra(), \ref igraph_radius_dijkstra() + * + */ +igraph_error_t igraph_graph_center_dijkstra( + const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_int_t *res, igraph_neimode_t mode +) { + + igraph_vector_t ecc; + const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; + + if (weights == NULL) { + return igraph_graph_center(graph, res, mode); + } + + igraph_vector_int_clear(res); + if (igraph_vcount(graph) == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&ecc, 0); + IGRAPH_CHECK(igraph_eccentricity_dijkstra(graph, weights, &ecc, igraph_vss_all(), mode)); + + /* igraph_eccentricity_dijkstra() does not return infinity or NaN, and the null graph + * case was handled above, therefore calling vector_min() is safe. */ + igraph_real_t min_eccentricity = igraph_vector_min(&ecc); + igraph_integer_t n = igraph_vector_size(&ecc); + for (igraph_integer_t i = 0; i < n; i++) { + if (igraph_cmp_epsilon(VECTOR(ecc)[i], min_eccentricity, eps) == 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, i)); + } + } + + igraph_vector_destroy(&ecc); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/eulerian.c b/src/paths/eulerian.c new file mode 100644 index 0000000..11cf63f --- /dev/null +++ b/src/paths/eulerian.c @@ -0,0 +1,683 @@ +/* -*- 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_bitset.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 igraph_error_t 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_int_t degree; + igraph_vector_int_t csize; + /* boolean vector to mark singletons: */ + igraph_vector_int_t nonsingleton; + igraph_integer_t i, n, vsize; + igraph_integer_t cluster_count; + /* number of self-looping singletons: */ + igraph_integer_t es; + /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ + igraph_integer_t 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 = true; + *has_cycle = true; + 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_INT_INIT_FINALLY(&csize, 0); + IGRAPH_CHECK(igraph_connected_components(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + cluster_count = 0; + vsize = igraph_vector_int_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 = false; + *has_cycle = false; + igraph_vector_int_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + } + } + } + + igraph_vector_int_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + /* the graph is connected except for singletons */ + /* find singletons (including those with self-loops) */ + IGRAPH_VECTOR_INT_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_INT_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++) { + igraph_integer_t deg = 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 = false; + *has_cycle = false; + igraph_vector_int_destroy(&nonsingleton); + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; + } + } + + igraph_vector_int_destroy(&nonsingleton); + IGRAPH_FINALLY_CLEAN(1); + + /* this is the usual algorithm on the connected part of the graph */ + if (odd > 2) { + *has_path = false; + *has_cycle = false; + } else if (odd == 2) { + *has_path = true; + *has_cycle = false; + } else { + *has_path = true; + *has_cycle = true; + } + + /* 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 && VECTOR(degree)[i] > 0) || (!*has_cycle && VECTOR(degree)[i] %2 == 1)) { + *start_of_path = i; + break; + } + } + + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t 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; + igraph_integer_t i, vsize; + igraph_integer_t cluster_count; + igraph_vector_int_t out_degree, in_degree; + igraph_vector_int_t csize; + /* boolean vector to mark singletons: */ + igraph_vector_int_t nonsingleton; + /* number of self-looping singletons: */ + igraph_integer_t es; + /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ + igraph_integer_t 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 = true; + *has_cycle = true; + 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_INT_INIT_FINALLY(&csize, 0); + + IGRAPH_CHECK(igraph_connected_components(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + cluster_count = 0; + vsize = igraph_vector_int_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 = false; + *has_cycle = false; + igraph_vector_int_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; + } + } + } + + igraph_vector_int_destroy(&csize); + IGRAPH_FINALLY_CLEAN(1); + + /* the graph is weakly connected except for singletons */ + /* find the singletons (including those with self-loops) */ + IGRAPH_VECTOR_INT_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_INT_INIT_FINALLY(&out_degree, 0); + IGRAPH_CHECK(igraph_degree(graph, &out_degree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); + + IGRAPH_VECTOR_INT_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++) { + igraph_integer_t degin = VECTOR(in_degree)[i]; + igraph_integer_t 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 = false; + *has_cycle = false; + igraph_vector_int_destroy(&nonsingleton); + igraph_vector_int_destroy(&in_degree); + igraph_vector_int_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 = false; + *has_cycle = false; + igraph_vector_int_destroy(&nonsingleton); + igraph_vector_int_destroy(&in_degree); + igraph_vector_int_destroy(&out_degree); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; + } + } + + *has_path = true; + /* perfect edge balance -> strong connectivity */ + *has_cycle = (incoming_excess == 0) && (outgoing_excess == 0); + /* either way, the start was set already */ + + igraph_vector_int_destroy(&nonsingleton); + igraph_vector_int_destroy(&in_degree); + igraph_vector_int_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. + * Must not be \c NULL. + * \param has_cycle Pointer to a Boolean, will be set to true if an Eulerian cycle exists. + * Must not be \c NULL. + * \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. + * + */ + +igraph_error_t 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 igraph_error_t igraph_i_eulerian_path_undirected( + const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res, + igraph_integer_t start_of_path) { + + igraph_integer_t curr; + igraph_integer_t n, m; + igraph_inclist_t il; + igraph_stack_int_t path, tracker, edge_tracker, edge_path; + igraph_bitset_t visited_list; + igraph_vector_int_t degree; + + n = igraph_vcount(graph); + m = igraph_ecount(graph); + + if (edge_res) { + igraph_vector_int_clear(edge_res); + } + + if (vertex_res) { + igraph_vector_int_clear(vertex_res); + } + + if (m == 0 || n == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_STACK_INT_INIT_FINALLY(&path, n); + IGRAPH_STACK_INT_INIT_FINALLY(&tracker, n); + IGRAPH_STACK_INT_INIT_FINALLY(&edge_path, n); + IGRAPH_STACK_INT_INIT_FINALLY(&edge_tracker, n); + IGRAPH_BITSET_INIT_FINALLY(&visited_list, m); + + IGRAPH_CHECK(igraph_stack_int_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_int_empty(&tracker)) { + + if (VECTOR(degree)[curr] != 0) { + igraph_vector_int_t *incedges; + igraph_integer_t nc, edge = -1; + igraph_integer_t j, next; + IGRAPH_CHECK(igraph_stack_int_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 = VECTOR(*incedges)[j]; + if (!IGRAPH_BIT_TEST(visited_list, edge)) { + break; + } + } + + next = IGRAPH_OTHER(graph, edge, curr); + + IGRAPH_CHECK(igraph_stack_int_push(&edge_tracker, edge)); + + /* remove edge here */ + VECTOR(degree)[curr]--; + VECTOR(degree)[next]--; + IGRAPH_BIT_SET(visited_list, edge); + + curr = next; + } else { /* back track to find remaining circuit */ + igraph_integer_t curr_e; + IGRAPH_CHECK(igraph_stack_int_push(&path, curr)); + curr = igraph_stack_int_pop(&tracker); + if (!igraph_stack_int_empty(&edge_tracker)) { + curr_e = igraph_stack_int_pop(&edge_tracker); + IGRAPH_CHECK(igraph_stack_int_push(&edge_path, curr_e)); + } + } + } + + if (edge_res) { + IGRAPH_CHECK(igraph_vector_int_reserve(edge_res, m)); + while (!igraph_stack_int_empty(&edge_path)) { + IGRAPH_CHECK(igraph_vector_int_push_back(edge_res, igraph_stack_int_pop(&edge_path))); + } + } + if (vertex_res) { + IGRAPH_CHECK(igraph_vector_int_reserve(vertex_res, m+1)); + while (!igraph_stack_int_empty(&path)) { + IGRAPH_CHECK(igraph_vector_int_push_back(vertex_res, igraph_stack_int_pop(&path))); + } + } + + igraph_stack_int_destroy(&path); + igraph_stack_int_destroy(&tracker); + igraph_stack_int_destroy(&edge_path); + igraph_stack_int_destroy(&edge_tracker); + igraph_bitset_destroy(&visited_list); + igraph_inclist_destroy(&il); + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/* solution adapted from https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/ */ +static igraph_error_t igraph_i_eulerian_path_directed( + const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res, + igraph_integer_t start_of_path) { + + igraph_integer_t curr; + igraph_integer_t n, m; + igraph_inclist_t il; + igraph_stack_int_t path, tracker, edge_tracker, edge_path; + igraph_bitset_t visited_list; + igraph_vector_int_t remaining_out_edges; + + n = igraph_vcount(graph); + m = igraph_ecount(graph); + + if (edge_res) { + igraph_vector_int_clear(edge_res); + } + + if (vertex_res) { + igraph_vector_int_clear(vertex_res); + } + + if (m == 0 || n == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_STACK_INT_INIT_FINALLY(&path, n); + IGRAPH_STACK_INT_INIT_FINALLY(&tracker, n); + IGRAPH_STACK_INT_INIT_FINALLY(&edge_path, n); + IGRAPH_STACK_INT_INIT_FINALLY(&edge_tracker, n); + IGRAPH_BITSET_INIT_FINALLY(&visited_list, m); + + IGRAPH_CHECK(igraph_stack_int_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_INT_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_int_empty(&tracker)) { + + if (VECTOR(remaining_out_edges)[curr] != 0) { + igraph_vector_int_t *incedges; + igraph_integer_t nc, edge = -1; + igraph_integer_t j, next; + IGRAPH_CHECK(igraph_stack_int_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 = VECTOR(*incedges)[j]; + if (!IGRAPH_BIT_TEST(visited_list, edge)) { + break; + } + } + + next = IGRAPH_TO(graph, edge); + + IGRAPH_CHECK(igraph_stack_int_push(&edge_tracker, edge)); + + /* remove edge here */ + VECTOR(remaining_out_edges)[curr]--; + IGRAPH_BIT_SET(visited_list, edge); + + curr = next; + } else { /* back track to find remaining circuit */ + igraph_integer_t curr_e; + IGRAPH_CHECK(igraph_stack_int_push(&path, curr)); + curr = igraph_stack_int_pop(&tracker); + if (!igraph_stack_int_empty(&edge_tracker)) { + curr_e = igraph_stack_int_pop(&edge_tracker); + IGRAPH_CHECK(igraph_stack_int_push(&edge_path, curr_e)); + } + } + } + + if (edge_res) { + IGRAPH_CHECK(igraph_vector_int_reserve(edge_res, m)); + while (!igraph_stack_int_empty(&edge_path)) { + IGRAPH_CHECK(igraph_vector_int_push_back(edge_res, igraph_stack_int_pop(&edge_path))); + } + } + if (vertex_res) { + IGRAPH_CHECK(igraph_vector_int_reserve(vertex_res, m+1)); + while (!igraph_stack_int_empty(&path)) { + IGRAPH_CHECK(igraph_vector_int_push_back(vertex_res, igraph_stack_int_pop(&path))); + } + } + + igraph_stack_int_destroy(&path); + igraph_stack_int_destroy(&tracker); + igraph_stack_int_destroy(&edge_path); + igraph_stack_int_destroy(&edge_tracker); + igraph_bitset_destroy(&visited_list); + igraph_inclist_destroy(&il); + igraph_vector_int_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. + * + * + * If the graph has no edges, a zero-length cycle is returned. + * + * + * 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. The first and + * last vertex in the vector will be the same. 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_ENOSOL + * graph does not have an Eulerian cycle. + * \endclist + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + * + */ + +igraph_error_t igraph_eulerian_cycle( + const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_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_ENOSOL); + } + + 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_ENOSOL); + } + + 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. + * + * + * If the graph has no edges, a zero-length path is returned. + * + * + * 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_ENOSOL + * graph does not have an Eulerian path. + * \endclist + * + * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. + * + */ + +igraph_error_t igraph_eulerian_path( + const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_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_ENOSOL); + } + 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_ENOSOL); + } + + IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/floyd_warshall.c b/src/paths/floyd_warshall.c new file mode 100644 index 0000000..19abae6 --- /dev/null +++ b/src/paths/floyd_warshall.c @@ -0,0 +1,363 @@ +/* + IGraph library. + Copyright (C) 2022-2023 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_interface.h" +#include "igraph_stack.h" + +#include "core/interruption.h" +#include "internal/utils.h" + +static igraph_error_t distances_floyd_warshall_original(igraph_matrix_t *res) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(res); + + for (igraph_integer_t k = 0; k < no_of_nodes; k++) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Iteration order matters for performance! + * First j, then i, because matrices are stored as column-major. */ + for (igraph_integer_t j = 0; j < no_of_nodes; j++) { + igraph_real_t dkj = MATRIX(*res, k, j); + if (dkj == IGRAPH_INFINITY) { + continue; + } + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_real_t di = MATRIX(*res, i, k) + dkj; + igraph_real_t dd = MATRIX(*res, i, j); + if (di < dd) { + MATRIX(*res, i, j) = di; + } + if (i == j && MATRIX(*res, i, i) < 0) { + IGRAPH_ERROR("Negative cycle found while calculating distances with Floyd-Warshall.", + IGRAPH_ENEGLOOP); + } + } + } + } + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t distances_floyd_warshall_tree(igraph_matrix_t *res) { + + /* This is the "Tree" algorithm of Brodnik et al. + * A difference from the paper is that instead of using the OUT_k tree of shortest + * paths _starting_ in k, we use the IN_k tree of shortest paths _ending_ in k. + * This makes it easier to iterate through matrices in column-major order, + * i.e. storage order, thus increasing performance. */ + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(res); + + /* successors[v][u] is the second vertex on the shortest path from v to u, + i.e. the parent of v in the IN_u tree. */ + igraph_matrix_int_t successors; + IGRAPH_MATRIX_INT_INIT_FINALLY(&successors, no_of_nodes, no_of_nodes); + + /* children[children_start[u] + i] is the i-th child of u in a tree of shortest paths + rooted at k, and ending in k, in the main loop below (IN_k). There are no_of_nodes-1 + child vertices in total, as the root vertex is excluded. This is essentially a contiguously + stored adjacency list representation of IN_k. */ + igraph_vector_int_t children; + IGRAPH_VECTOR_INT_INIT_FINALLY(&children, no_of_nodes-1); + + /* children_start[u] indicates where the children of u are stored in children[]. + These are effectively the cumulative sums of no_of_children[], with the first + element being 0. The last element, children_start[no_of_nodes], is equal to the + total number of children in the tree, i.e. no_of_nodes-1. */ + igraph_vector_int_t children_start; + IGRAPH_VECTOR_INT_INIT_FINALLY(&children_start, no_of_nodes+1); + + /* no_of_children[u] is the number of children that u has in IN_k in the main loop below. */ + igraph_vector_int_t no_of_children; + IGRAPH_VECTOR_INT_INIT_FINALLY(&no_of_children, no_of_nodes); + + /* dfs_traversal and dfs_skip arrays for running time optimization, + see "Practical improvement" in Section 3.1 of the paper */ + igraph_vector_int_t dfs_traversal; + IGRAPH_VECTOR_INT_INIT_FINALLY(&dfs_traversal, no_of_nodes); + igraph_vector_int_t dfs_skip; + IGRAPH_VECTOR_INT_INIT_FINALLY(&dfs_skip, no_of_nodes); + + igraph_stack_int_t stack; + IGRAPH_STACK_INT_INIT_FINALLY(&stack, no_of_nodes); + + for (igraph_integer_t u = 0; u < no_of_nodes; u++) { + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + MATRIX(successors, v, u) = u; + } + } + + for (igraph_integer_t k = 0; k < no_of_nodes; k++) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Count the children of each node in the shortest path tree, assuming that at + this point all elements of no_of_children[] are zeros. */ + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + if (v == k) continue; + igraph_integer_t parent = MATRIX(successors, v, k); + VECTOR(no_of_children)[parent]++; + } + + /* Note: we do not use igraph_vector_int_cumsum() here as that function produces + an output vector of the same length as the input vector. Here we need an output + one longer, with a 0 being prepended to what vector_cumsum() would produce. */ + igraph_integer_t cumsum = 0; + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + VECTOR(children_start)[v] = cumsum; + cumsum += VECTOR(no_of_children)[v]; + } + VECTOR(children_start)[no_of_nodes] = cumsum; + + /* Constructing the tree IN_k (as in the paper) and representing it + as a contiguously stored adjacency list. The entries of the no_of_children + vector as re-used as an index of where to insert child node indices. + At the end of the calculation, all elements of no_of_children[] will be zeros, + making this vector ready for the next iteration of the outer loop. */ + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + if (v == k) continue; + igraph_integer_t parent = MATRIX(successors, v, k); + VECTOR(no_of_children)[parent]--; + VECTOR(children)[ VECTOR(children_start)[parent] + VECTOR(no_of_children)[parent] ] = v; + } + + /* constructing dfs-traversal and dfs-skip arrays for the IN_k tree */ + IGRAPH_CHECK(igraph_stack_int_push(&stack, k)); + igraph_integer_t counter = 0; + while (!igraph_stack_int_empty(&stack)) { + igraph_integer_t parent = igraph_stack_int_pop(&stack); + if (parent >= 0) { + VECTOR(dfs_traversal)[counter] = parent; + counter++; + /* a negative marker -parent - 1 that is popped right after + all the descendants of the parent were processed */ + IGRAPH_CHECK(igraph_stack_int_push(&stack, -parent - 1)); + for (igraph_integer_t l = VECTOR(children_start)[parent]; l < VECTOR(children_start)[parent + 1]; l++) { + IGRAPH_CHECK(igraph_stack_int_push(&stack, VECTOR(children)[l])); + } + } else { + VECTOR(dfs_skip)[-(parent + 1)] = counter; + } + } + + /* main inner loop */ + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_real_t dki = MATRIX(*res, k, i); + if (dki == IGRAPH_INFINITY || i == k) { + continue; + } + igraph_integer_t counter = 1; + while (counter < no_of_nodes) { + igraph_integer_t j = VECTOR(dfs_traversal)[counter]; + igraph_real_t di = MATRIX(*res, j, k) + dki; + igraph_real_t dd = MATRIX(*res, j, i); + if (di < dd) { + MATRIX(*res, j, i) = di; + MATRIX(successors, j, i) = MATRIX(successors, j, k); + counter++; + } else { + counter = VECTOR(dfs_skip)[j]; + } + if (i == j && MATRIX(*res, i, i) < 0) { + IGRAPH_ERROR("Negative cycle found while calculating distances with Floyd-Warshall.", + IGRAPH_ENEGLOOP); + } + } + } + } + + igraph_stack_int_destroy(&stack); + igraph_vector_int_destroy(&dfs_traversal); + igraph_vector_int_destroy(&dfs_skip); + igraph_vector_int_destroy(&no_of_children); + igraph_vector_int_destroy(&children_start); + igraph_vector_int_destroy(&children); + igraph_matrix_int_destroy(&successors); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_distances_floyd_warshall + * \brief Weighted all-pairs shortest path lengths with the Floyd-Warshall algorithm. + * + * \experimental + * + * The Floyd-Warshall algorithm computes weighted shortest path lengths between + * all pairs of vertices at the same time. It is useful with very dense weighted graphs, + * as its running time is primarily determined by the vertex count, and is not sensitive + * to the graph density. In sparse graphs, other methods such as the Dijkstra or + * Bellman-Ford algorithms will perform significantly better. + * + * + * In addition to the original Floyd-Warshall algorithm, igraph contains implementations + * of variants that offer better asymptotic complexity as well as better practical + * running times for most instances. See the reference below for more information. + * + * + * Note that internally this function always computes the distance matrix + * for all pairs of vertices. The \p from and \p to parameters only serve + * to subset this matrix, but do not affect the time or memory taken by the + * calculation. + * + * + * Reference: + * + * + * Brodnik, A., Grgurovič, M., Požar, R.: + * Modifications of the Floyd-Warshall algorithm with nearly quadratic expected-time, + * Ars Mathematica Contemporanea, vol. 22, issue 1, p. #P1.01 (2021). + * https://doi.org/10.26493/1855-3974.2467.497 + * + * \param graph The graph object. + * \param res An intialized matrix, the distances will be stored here. + * \param from The source vertices. + * \param to The target vertices. + * \param weights The edge weights. If \c NULL, all weights are assumed to be 1. + * Negative weights are allowed, but the graph must not contain negative cycles. + * Edges with positive infinite weights are ignored. + * \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 method The type of the algorithm used. + * \clist + * \cli IGRAPH_FLOYD_WARSHALL_AUTOMATIC + * tried to select the best performing variant for the current graph; + * presently this option always uses the "Tree" method. + * \cli IGRAPH_FLOYD_WARSHALL_ORIGINAL + * the basic Floyd-Warshall algorithm. + * \cli IGRAPH_FLOYD_WARSHALL_TREE + * the "Tree" speedup of Brodnik et al., faster than the original algorithm + * in most cases. + * \endclist + * \return Error code. \c IGRAPH_ENEGLOOP is returned if a negative-weight + * cycle is found. + * + * \sa \ref igraph_distances(), \ref igraph_distances_dijkstra(), + * \ref igraph_distances_bellman_ford(), \ref igraph_distances_johnson() + * + * Time complexity: + * The original variant has complexity O(|V|^3 + |E|). + * The "Tree" variant has expected-case complexity of O(|V|^2 log^2 |V|) + * according to Brodnik et al., while its worst-time complexity remains O(|V|^3). + * Here |V| denotes the number of vertices and |E| is the number of edges. + */ +igraph_error_t igraph_distances_floyd_warshall( + const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t from, igraph_vs_t to, + const igraph_vector_t *weights, igraph_neimode_t mode, + const igraph_floyd_warshall_algorithm_t method) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t in = false, out = false; + + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + switch (mode) { + case IGRAPH_ALL: + in = out = true; + break; + case IGRAPH_OUT: + out = true; + break; + case IGRAPH_IN: + in = true; + break; + default: + IGRAPH_ERROR("Invalid mode for Floyd-Warshall shortest path calculation.", IGRAPH_EINVMODE); + } + + if (weights && igraph_vector_is_any_nan(weights)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_fill(res, IGRAPH_INFINITY); + + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + MATRIX(*res, v, v) = 0; + } + + for (igraph_integer_t e = 0; e < no_of_edges; e++) { + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_real_t w = weights ? VECTOR(*weights)[e] : 1; + + if (w < 0) { + if (mode == IGRAPH_ALL) { + IGRAPH_ERRORF("Negative edge weight (%g) found in undirected graph " + "while calculating distances with Floyd-Warshall.", + IGRAPH_ENEGLOOP, w); + } else if (to == from) { + IGRAPH_ERRORF("Self-loop with negative weight (%g) found " + "while calculating distances with Floyd-Warshall.", + IGRAPH_ENEGLOOP, w); + } + } else if (w == IGRAPH_INFINITY) { + /* Ignore edges with infinite weight */ + continue; + } + + if (out && MATRIX(*res, from, to) > w) { + MATRIX(*res, from, to) = w; + } + if (in && MATRIX(*res, to, from) > w) { + MATRIX(*res, to, from) = w; + } + } + + /* If there are zero or one vertices, nothing needs to be done. + * This is special-cased so that at later stages we can rely on no_of_nodes - 1 >= 0. */ + if (no_of_nodes <= 1) { + return IGRAPH_SUCCESS; + } + + switch (method) { + case IGRAPH_FLOYD_WARSHALL_ORIGINAL: + IGRAPH_CHECK(distances_floyd_warshall_original(res)); + break; + case IGRAPH_FLOYD_WARSHALL_AUTOMATIC: + case IGRAPH_FLOYD_WARSHALL_TREE: + IGRAPH_CHECK(distances_floyd_warshall_tree(res)); + break; + default: + IGRAPH_ERROR("Invalid method.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_matrix_subset_vertices(res, graph, from, to)); + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/histogram.c b/src/paths/histogram.c new file mode 100644 index 0000000..1f5d9e9 --- /dev/null +++ b/src/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_distances() + */ + +igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, + igraph_real_t *unconnected, igraph_bool_t directed) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, n; + igraph_vector_int_t already_added; + igraph_integer_t nodes_reached; + + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_neimode_t dirmode; + igraph_adjlist_t allneis; + igraph_real_t unconn = 0; + igraph_integer_t ressize; + + if (directed) { + dirmode = IGRAPH_OUT; + } else { + dirmode = IGRAPH_ALL; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&already_added, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + igraph_vector_clear(res); + ressize = 0; + + for (i = 0; i < no_of_nodes; i++) { + nodes_reached = 1; /* itself */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_int_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_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + igraph_integer_t neighbor = 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_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + } + } /* while !igraph_dqueue_int_empty */ + + unconn += (no_of_nodes - nodes_reached); + + } /* for i + * If no edge weights are supplied, then the unweighted version, \ref igraph_distances() + * is called. If none of the supplied edge weights are negative, then Dijkstra's algorithm + * is used by calling \ref igraph_distances_dijkstra(). + * + * + * Note that Johnson's algorithm applies only to directed graphs. This function rejects + * undirected graphs with \em any negative edge weights, even when the \p from and \p to + * vertices are all in connected components that are free of negative weights. + * + * + * References: + * + * + * Donald B. Johnson: Efficient Algorithms for Shortest Paths in Sparse Networks. + * J. ACM 24, 1 (1977), 1–13. + * https://doi.org/10.1145/321992.321993 + * + * \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_distances() will + * be called. Edges with positive infinite weights are ignored. + * \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_distances() for a faster unweighted version, + * \ref igraph_distances_dijkstra() if you do not have negative + * edge weights, \ref igraph_distances_bellman_ford() if you only + * need to calculate shortest paths from a couple of sources. + */ +igraph_error_t igraph_distances_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_t newgraph; + igraph_vector_int_t edges; + igraph_vector_t newweights; + igraph_matrix_t bfres; + igraph_integer_t i, ptr; + igraph_integer_t nr, nc; + igraph_vit_t fromvit; + igraph_integer_t no_edges_reserved; + + /* If no weights, then we can just run the unweighted version */ + if (!weights) { + return igraph_distances(graph, res, from, to, IGRAPH_OUT); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + /* If no edges, then we can just run the unweighted version */ + if (no_of_edges == 0) { + return igraph_distances(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 (isnan(min_weight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + if (min_weight >= 0) { + return igraph_distances_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, no_of_nodes + 1, igraph_is_directed(graph))); + IGRAPH_FINALLY(igraph_destroy, &newgraph); + + IGRAPH_SAFE_MULT(no_of_nodes, 2, &no_edges_reserved); + IGRAPH_SAFE_ADD(no_edges_reserved, no_of_edges * 2, &no_edges_reserved); + + /* Add a new node to the graph, plus edges from it to all the others. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_edges_reserved); + igraph_get_edgelist(graph, &edges, /*bycol=*/ 0); /* reserved */ + igraph_vector_int_resize(&edges, no_edges_reserved); /* reserved */ + 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_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_vector_reserve(&newweights, no_of_edges + no_of_nodes)); + igraph_vector_update(&newweights, weights); /* reserved */ + igraph_vector_resize(&newweights, no_of_edges + no_of_nodes); /* reserved */ + for (i = no_of_edges; i < no_of_edges + no_of_nodes; i++) { + VECTOR(newweights)[i] = 0; + } + + /* Run Bellman-Ford algorithm on the new graph, starting from the + new vertex. */ + + IGRAPH_CHECK(igraph_distances_bellman_ford(&newgraph, &bfres, + igraph_vss_1(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); /* reserved */ + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t ffrom = IGRAPH_FROM(graph, i); + igraph_integer_t tto = IGRAPH_TO(graph, i); + VECTOR(newweights)[i] += MATRIX(bfres, 0, ffrom) - MATRIX(bfres, 0, tto); + + /* If a weight becomes slightly negative due to roundoff errors, + snap it to exact zero. */ + if (VECTOR(newweights)[i] < 0) VECTOR(newweights)[i] = 0; + } + + /* Run Dijkstra's algorithm on the new weights */ + IGRAPH_CHECK(igraph_distances_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)) { + igraph_integer_t v1 = IGRAPH_VIT_GET(fromvit); + if (igraph_vs_is_all(&to)) { + igraph_integer_t 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 { + igraph_integer_t 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)) { + igraph_integer_t 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; +} + +/** + * \function igraph_shortest_paths_johnson + * \brief Weighted shortest path lengths between vertices, using Johnson's algorithm (deprecated). + * + * \deprecated-by igraph_distances_johnson 0.10.0 + */ +igraph_error_t 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) { + return igraph_distances_johnson(graph, res, from, to, weights); +} diff --git a/src/paths/random_walk.c b/src/paths/random_walk.c new file mode 100644 index 0000000..60556b4 --- /dev/null +++ b/src/paths/random_walk.c @@ -0,0 +1,396 @@ +/* -*- 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 "igraph_vector_ptr.h" + +#include "core/interruption.h" + +/** + * This function performs a random walk with a given length on a graph, + * from the given start vertex. + * It's used for igraph_random_walk when the given graph is unweighted, + * and only vertex IDs of the vertices on the walk are needed (edge IDs are not needed). + * \param vertices An allocated vector, the result is stored here as + * a list of vertex IDs. It will be resized as needed. + * It includes the starting vertex id as well. + */ +static igraph_error_t igraph_i_random_walk_adjlist(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_integer_t start, + igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck) { + igraph_integer_t i; + igraph_lazy_adjlist_t adj; + + if (vertices == NULL) { + /* Nothing to do */ + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adj, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adj); + + IGRAPH_CHECK(igraph_vector_int_resize(vertices, steps + 1)); + + RNG_BEGIN(); + + VECTOR(*vertices)[0] = start; + for (i = 1; i <= steps; i++) { + igraph_vector_int_t *neis; + igraph_integer_t nn; + neis = igraph_lazy_adjlist_get(&adj, start); + + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + + nn = igraph_vector_int_size(neis); + if (IGRAPH_UNLIKELY(nn == 0)) { + igraph_vector_int_resize(vertices, i); /* shrinks */ + if (stuck == IGRAPH_RANDOM_WALK_STUCK_RETURN) { + break; + } else { + IGRAPH_ERROR("Random walk got stuck.", IGRAPH_ERWSTUCK); + } + } + start = VECTOR(*vertices)[i] = VECTOR(*neis)[RNG_INTEGER(0, nn - 1)]; + + IGRAPH_ALLOW_INTERRUPTION(); + } + + RNG_END(); + + igraph_lazy_adjlist_destroy(&adj); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Used as item destructor for 'cdfs' in igraph_i_random_walk_inclist(). */ +static void vec_destr(igraph_vector_t *vec) { + if (vec != NULL) { + igraph_vector_destroy(vec); + } +} + + +/** + * This function performs a random walk with a given length on a graph, + * from the given start vertex. + * It's used for igraph_random_walk: + * - when weights are used or when edge IDs of the traversed edges + * and/or vertex IDs of the visited vertices are requested. + * \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 vertices An allocated vector, the result is stored here as + * a list of vertex IDs. It will be resized as needed. + * It includes the starting vertex id as well. + * \param edges An initialized vector, the indices of traversed + * edges are stored here. It will be resized as needed. + */ +static igraph_error_t igraph_i_random_walk_inclist( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + 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 i, next; + igraph_vector_t weight_temp; + igraph_lazy_inclist_t il; + igraph_vector_ptr_t cdfs; /* cumulative distribution vectors for each node, used for weighted choice */ + + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_resize(vertices, steps + 1)); /* size: steps + 1 because vertices includes start vertex */ + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_resize(edges, steps)); + } + + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &il); + + IGRAPH_VECTOR_INIT_FINALLY(&weight_temp, 0); + + /* cdf vectors will be computed lazily; that's why we are still using + * igraph_vector_ptr_t as it does not require us to pre-initialize all + * the vectors in the vector list */ + 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(); + + if (vertices) { + VECTOR(*vertices)[0] = start; + } + for (i = 0; i < steps; ++i) { + igraph_integer_t degree, edge, idx; + igraph_vector_int_t *inc_edges = igraph_lazy_inclist_get(&il, start); + + IGRAPH_CHECK_OOM(inc_edges, "Failed to query incident edges."); + degree = igraph_vector_int_size(inc_edges); + + /* are we stuck? */ + if (IGRAPH_UNLIKELY(degree == 0)) { + /* can't fail since size is reduced, skip IGRAPH_CHECK */ + if (vertices) { + igraph_vector_int_resize(vertices, i + 1); /* size: i + 1 because vertices includes start vertex */ + } + if (edges) { + igraph_vector_int_resize(edges, i); + } + 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)) { + igraph_integer_t j; + + *cd = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_CHECK_OOM(*cd, "Insufficient memory for random walk."); + 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(*inc_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 { + idx = RNG_INTEGER(0, degree - 1); + } + + edge = VECTOR(*inc_edges)[idx]; + if (edges) { + VECTOR(*edges)[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: + next = IGRAPH_TO(graph, edge); + break; + case IGRAPH_IN: + next = IGRAPH_FROM(graph, edge); + break; + case IGRAPH_ALL: + next = IGRAPH_OTHER(graph, edge, start); + break; + } + + if (vertices) { + VECTOR(*vertices)[i + 1] = next; /* index i + 1 because vertices includes start vertex at position 0 */ + } + start = next; + + IGRAPH_ALLOW_INTERRUPTION(); + } + + RNG_END(); + + igraph_vector_ptr_destroy_all(&cdfs); + igraph_vector_destroy(&weight_temp); + igraph_lazy_inclist_destroy(&il); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_random_walk + * \brief Performs 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 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 \c NULL, all edges are considered to have equal weight. + * \param vertices An allocated vector, the result is stored here as + * a list of vertex IDs. It will be resized as needed. + * It includes the vertex IDs of starting and ending vertices. + * Length of the vertices vector: \p steps + 1 + * \param edges An initialized vector, the indices of traversed + * edges are stored here. It will be resized as needed. + * Length of the edges vector: \p steps + * \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. + * \p steps is the number of edges to traverse during the walk. + * \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 \c IGRAPH_ERWSTUCK error is reported. + * In both cases, \p vertices and \p edges are truncated to contain + * the actual interrupted walk. + * \return Error code: \c IGRAPH_ERWSTUCK if the walk got stuck. + * + * Time complexity: + * O(l + d) for unweighted graphs and + * O(l * log(k) + d) for weighted graphs, + * where \c l is the length of the walk, \c d is the total degree of the visited nodes + * and \c k is the average degree of vertices of the given graph. + */ + + +igraph_error_t igraph_random_walk(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + 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); + + if (!(mode == IGRAPH_ALL || mode == IGRAPH_IN || mode == IGRAPH_OUT)) { + IGRAPH_ERROR("Invalid mode parameter.", IGRAPH_EINVMODE); + } + + if (start < 0 || start >= vc) { + IGRAPH_ERRORF("Starting vertex must be between 0 and the " + "number of vertices in the graph (%" IGRAPH_PRId + "), got %" IGRAPH_PRId ".", IGRAPH_EINVAL, + vc, start); + } + if (steps < 0) { + IGRAPH_ERRORF("Number of steps should be non-negative, got %" + IGRAPH_PRId ".", IGRAPH_EINVAL, steps); + } + + 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 (isnan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); + } + } + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (edges || weights) { + return igraph_i_random_walk_inclist(graph, weights, vertices, edges, + start, mode, steps, stuck); + } else { + return igraph_i_random_walk_adjlist(graph, vertices, + start, mode, steps, stuck); + } +} + + +/** + * \function igraph_random_edge_walk + * \brief Performs a random walk on a graph and returns 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 \c IGRAPH_ERWSTUCK error is reported. In both cases, + * \p edgewalk is truncated to contain the actual interrupted walk. + * + * \return Error code. + * + * \deprecated-by igraph_random_walk 0.10.0 + */ +igraph_error_t igraph_random_edge_walk( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *edgewalk, + igraph_integer_t start, igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck) { + + return igraph_random_walk(graph, weights, NULL, edgewalk, + start, mode, steps, stuck); +} diff --git a/src/paths/shortest_paths.c b/src/paths/shortest_paths.c new file mode 100644 index 0000000..0a87088 --- /dev/null +++ b/src/paths/shortest_paths.c @@ -0,0 +1,1680 @@ +/* -*- 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 igraph_error_t 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 */) +{ + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t source, j, n; + igraph_integer_t *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_int_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_adjlist_t allneis; + + *res = 0; + already_added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for average path length."); + IGRAPH_FINALLY(igraph_free, already_added); + + IGRAPH_DQUEUE_INT_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_int_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + already_added[source] = source + 1; + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + igraph_integer_t neighbor = 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_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + } + } /* while !igraph_dqueue_int_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_int_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 igraph_error_t 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 isfinite() might involve a function call + and we want to spare that. -1 will denote infinity instead. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_integer_t 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 (%" IGRAPH_PRId ") does not match the number of edges (%" IGRAPH_PRId ").", + 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 (isnan(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)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); + igraph_vector_int_t *neis; + igraph_integer_t 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, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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 (altdist == IGRAPH_INFINITY) { + /* Ignore edges with positive infinite weight */ + } else 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_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 + */ + +igraph_error_t 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. Edges with positive + * infinite weight are ignored. + * \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 + */ + +igraph_error_t 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. Edges with positive infinite weights are + * ignored. + * \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. + * + */ + +igraph_error_t 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 igraph_error_t igraph_i_local_efficiency_unweighted( + const igraph_t *graph, + const igraph_adjlist_t *adjlist, + igraph_dqueue_int_t *q, + igraph_integer_t *already_counted, + igraph_vector_int_t *vertex_neis, + igraph_vector_char_t *nei_mask, + igraph_real_t *res, + igraph_integer_t vertex, + igraph_neimode_t mode) +{ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t vertex_neis_size; + igraph_integer_t neighbor_count; /* unlike 'vertex_neis_size', 'neighbor_count' does not count self-loops and multi-edges */ + igraph_integer_t i, j; + + igraph_dqueue_int_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(already_counted[0])); + + IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); + vertex_neis_size = igraph_vector_int_size(vertex_neis); + + igraph_vector_char_null(nei_mask); + neighbor_count = 0; + for (i=0; i < vertex_neis_size; ++i) { + igraph_integer_t 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) { + igraph_integer_t source = VECTOR(*vertex_neis)[i]; + igraph_integer_t 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_int_push(q, source)); + IGRAPH_CHECK(igraph_dqueue_int_push(q, 0)); + already_counted[source] = i + 1; + + while (!igraph_dqueue_int_empty(q)) { + igraph_vector_int_t *act_neis; + igraph_integer_t act_neis_size; + igraph_integer_t act = igraph_dqueue_int_pop(q); + igraph_integer_t actdist = igraph_dqueue_int_pop(q); + + if (act != source && VECTOR(*nei_mask)[act]) { + *res += 1.0 / actdist; + reached++; + if (reached == neighbor_count) { + igraph_dqueue_int_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++) { + igraph_integer_t neighbor = VECTOR(*act_neis)[j]; + + if (neighbor == vertex || already_counted[neighbor] == i + 1) + continue; + + already_counted[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(q, actdist + 1)); + } + } + } + + *res /= neighbor_count * (neighbor_count - 1.0); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_local_efficiency_dijkstra( + const igraph_t *graph, + igraph_lazy_inclist_t *inclist, + igraph_2wheap_t *Q, + igraph_vector_int_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 isfinite() might involve a function call + and we want to spare that. -1 will denote infinity instead. + */ + + igraph_integer_t i, j; + igraph_integer_t vertex_neis_size; + igraph_integer_t 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_int_size(vertex_neis); + + igraph_vector_char_null(nei_mask); + neighbor_count = 0; + for (i=0; i < vertex_neis_size; ++i) { + igraph_integer_t 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) { + igraph_integer_t source = VECTOR(*vertex_neis)[i]; + igraph_integer_t 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)) { + igraph_integer_t minnei = igraph_2wheap_max_index(Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(Q); + igraph_vector_int_t *neis; + igraph_integer_t 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, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + igraph_real_t altdist, curdist; + igraph_bool_t active, has; + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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_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. Edges with positive + * infinite weights are ignored. + * \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() + * + */ + +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t nodes_to_calc; /* no. of vertices includes in computation */ + igraph_vit_t vit; + igraph_vector_int_t vertex_neis; + igraph_vector_char_t nei_mask; + igraph_integer_t 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_INT_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 */ + { + igraph_integer_t *already_counted; + igraph_adjlist_t adjlist; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + + already_counted = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(already_counted, "Insufficient memory for local efficiency calculation."); + 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_INT_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_int_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_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); + } + else if (isnan(min)) { + IGRAPH_ERROR("Weights 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_int_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. Edges + * with positive infinite weight are ignored. + * \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() + * + */ + +igraph_error_t 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_integer_t 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, + * i.e. the maximum eccentricity of the graph's vertices. + * This function computes both the diameter, as well as a 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 res Pointer to a real number, if not \c NULL then it will contain + * the diameter (the actual distance). + * \param from 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 to 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 vertex_path Pointer to an initialized vector. If not \c NULL the actual + * longest geodesic path in terms of vertices will be stored here. The vector will be + * resized as needed. + * \param edge_path Pointer to an initialized vector. If not \c NULL the actual + * longest geodesic path in terms of edges 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() for the weighted version, + * \ref igraph_radius() for the minimum eccentricity. + * + * \example examples/simple/igraph_diameter.c + */ + +igraph_error_t igraph_diameter(const igraph_t *graph, igraph_real_t *res, + igraph_integer_t *from, igraph_integer_t *to, + igraph_vector_int_t *vertex_path, igraph_vector_int_t *edge_path, + igraph_bool_t directed, igraph_bool_t unconn) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, n; + igraph_integer_t *already_added; + igraph_integer_t nodes_reached; + /* from/to are initialized to 0 because in a singleton graph, or in an edgeless graph + * with unconn = true, the diameter path will be considered to consist of vertex 0 only. */ + igraph_integer_t ifrom = 0, ito = 0; + igraph_real_t ires = 0; + + igraph_dqueue_int_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 (res) { + *res = IGRAPH_NAN; + } + if (vertex_path) { + igraph_vector_int_clear(vertex_path); + } + if (edge_path) { + igraph_vector_int_clear(edge_path); + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + return IGRAPH_SUCCESS; + } + + if (directed) { + dirmode = IGRAPH_OUT; + } else { + dirmode = IGRAPH_ALL; + } + already_added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for diameter calculation."); + IGRAPH_FINALLY(igraph_free, already_added); + + IGRAPH_DQUEUE_INT_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_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + already_added[i] = i + 1; + + IGRAPH_PROGRESS("Diameter: ", 100.0 * i / no_of_nodes, NULL); + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + if (actdist > ires) { + ires = actdist; + ifrom = i; + ito = actnode; + } + + neis = igraph_adjlist_get(&allneis, actnode); + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (already_added[neighbor] == i + 1) { + continue; + } + already_added[neighbor] = i + 1; + nodes_reached++; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + } + } /* while !igraph_dqueue_int_empty */ + + /* not connected, return IGRAPH_INFINITY */ + if (nodes_reached != no_of_nodes && !unconn) { + ires = IGRAPH_INFINITY; + ifrom = -1; + ito = -1; + break; + } + } /* for i 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 (isnan(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)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); + igraph_vector_int_t *neis; + igraph_integer_t nlen; + + if (mindist > ires) { + ires = mindist; ifrom = source; ito = 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++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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_2wheap_modify(&Q, tto, -altdist); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + /* not connected, return infinity */ + if (nodes_reached != no_of_nodes && !unconn) { + ires = IGRAPH_INFINITY; + ifrom = ito = -1; + break; + } + + } /* source < no_of_nodes */ + + /* Compensate for the +1 that we have added to distances */ + ires -= 1; + + igraph_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_PROGRESS("Weighted diameter: ", 100.0, NULL); + + if (res) { + *res = ires; + } + if (from) { + *from = ifrom; + } + if (to) { + *to = ito; + } + if ((vertex_path) || (edge_path)) { + if (!isfinite(ires)) { + if (vertex_path){ + igraph_vector_int_clear(vertex_path); + } + if (edge_path) { + igraph_vector_int_clear(edge_path); + } + } else { + IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, + /*vertices=*/ vertex_path, /*edges=*/ edge_path, + ifrom, ito, + weights, dirmode)); + } + } + return IGRAPH_SUCCESS; +} + +/** + * Temporarily removes all edges incident on the vertex with the given ID from + * the graph by setting the weights of these edges to infinity. + * + * \param graph the graph + * \param weights the weights of the edges of the graph + * \param vid the ID of the vertex to remove + * \param edges_removed vector that records the IDs of the edges that were + * "removed" (i.e. their weights were set to infinity) + * \param eids temporary vector that is used to retrieve the IDs of the + * incident edges, to make this function free of memory allocations + */ +static igraph_error_t igraph_i_semidelete_vertex( + const igraph_t *graph, igraph_vector_t *weights, + igraph_integer_t vid, igraph_vector_int_t *edges_removed, + igraph_vector_int_t *eids +) { + igraph_integer_t j, n; + + IGRAPH_CHECK(igraph_incident(graph, eids, vid, IGRAPH_ALL)); + + n = igraph_vector_int_size(eids); + for (j = 0; j < n; j++) { + igraph_integer_t eid = VECTOR(*eids)[j]; + IGRAPH_CHECK(igraph_vector_int_push_back(edges_removed, eid)); + VECTOR(*weights)[eid] = IGRAPH_INFINITY; + } + + return IGRAPH_SUCCESS; +} + +static igraph_bool_t igraph_i_has_edge_with_infinite_weight( + const igraph_vector_int_t* path, const igraph_vector_t* weights +) { + igraph_integer_t i, n; + + n = weights ? igraph_vector_int_size(path) : 0; + for (i = 0; i < n; i++) { + igraph_integer_t edge = VECTOR(*path)[i]; + if (!isfinite(VECTOR(*weights)[edge])) { + return true; + } + } + + return false; +} + +static igraph_real_t igraph_i_get_total_weight_of_path( + igraph_vector_int_t* path, const igraph_vector_t* weights +) { + igraph_integer_t i, n = igraph_vector_int_size(path); + igraph_real_t result; + + if (weights) { + result = 0; + for (i = 0; i < n; i++) { + igraph_integer_t edge = VECTOR(*path)[i]; + result += VECTOR(*weights)[edge]; + } + } else { + result = n; + } + + return result; +} + +/** + * \function igraph_get_k_shortest_paths + * \brief k shortest paths between two vertices. + * + * This function returns the \p k shortest paths between two vertices, in order of + * increasing lengths. + * + * + * Reference: + * + * + * Yen, Jin Y.: + * An algorithm for finding shortest routes from all source nodes to a given + * destination in general networks. + * Quarterly of Applied Mathematics. 27 (4): 526–530. (1970) + * https://doi.org/10.1090/qam/253822 + * + * \param graph The graph object. + * \param weights The edge weights of the graph. Can be \c NULL for an + * unweighted graph. Infinite weights will be treated as missing + * edges. + * \param vertex_paths Pointer to an initialized list of integer vectors, the result + * will be stored here in \ref igraph_vector_int_t objects. Each vector + * object contains the vertex IDs along the kth shortest path + * between \p from and \p to, where \c k is the vector list index. May + * be \c NULL if the vertex paths are not needed. + * \param edge_paths Pointer to an initialized list of integer vectors, the result + * will be stored here in \ref igraph_vector_int_t objects. Each vector + * object contains the edge IDs along the kth shortest path + * between \p from and \p to, where \c k is the vector list index. May be + * \c NULL if the edge paths are not needed. + * \param k The number of paths. + * \param from The ID of the vertex from which the paths are calculated. + * \param to The ID of the vertex to which the paths are calculated. + * \param mode The type of paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * The outgoing paths of \p from are calculated. + * \cli IGRAPH_IN + * The incoming paths of \p from 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 or \p to is an invalid vertex id. + * \cli IGRAPH_EINVMODE + * Invalid mode argument. + * \cli IGRAPH_EINVAL + * Invalid argument. + * \endclist + * + * \sa \ref igraph_get_all_simple_paths(), \ref igraph_get_shortest_paths(), + * \ref igraph_get_shortest_paths_dijkstra() + * + * Time complexity: k |V| (|V| log|V| + |E|), where |V| is the number of vertices, + * and |E| is the number of edges. + */ +igraph_error_t igraph_get_k_shortest_paths( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_int_list_t *vertex_paths, + igraph_vector_int_list_t *edge_paths, + igraph_integer_t k, igraph_integer_t from, igraph_integer_t to, + igraph_neimode_t mode +) { + igraph_vector_int_list_t paths_pot; /* potential shortest paths */ + igraph_integer_t vertex_spur; + igraph_vector_int_t path_spur, path_root, path_total, path_shortest; + igraph_integer_t nr_edges_root, i_path_current, i_path, edge_path_root, vertex_root_del; + igraph_integer_t i, n; + igraph_vector_t current_weights; + igraph_vector_int_t edges_removed; + igraph_integer_t nr_edges = igraph_ecount(graph); + igraph_bool_t infinite_path, already_in_potential_paths; + igraph_vector_int_t *path_0; + igraph_vector_int_t eids; + igraph_real_t path_weight, shortest_path_weight; + igraph_integer_t edge_paths_owned = 0; + + if (!igraph_is_directed(graph) && (mode == IGRAPH_IN || mode == IGRAPH_OUT)) { + mode = IGRAPH_ALL; + } + + if (vertex_paths) { + igraph_vector_int_list_clear(vertex_paths); + } + + if (!edge_paths) { + /* We will need our own instance */ + edge_paths = IGRAPH_CALLOC(1, igraph_vector_int_list_t); + IGRAPH_CHECK_OOM(edge_paths, "Cannot allocate vector for storing edge paths."); + IGRAPH_FINALLY(igraph_free, edge_paths); + edge_paths_owned = 1; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(edge_paths, 0); + edge_paths_owned = 2; + } + + igraph_vector_int_list_clear(edge_paths); + + if (k == 0) { + goto cleanup; + } + + IGRAPH_CHECK(igraph_vector_int_list_resize(edge_paths, 1)); + path_0 = igraph_vector_int_list_get_ptr(edge_paths, 0); + + IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, + NULL, + path_0, + from, + to, + weights, + mode)); + + /* Check if there's a path. */ + infinite_path = igraph_i_has_edge_with_infinite_weight(path_0, weights); + if (infinite_path || (from != to && igraph_vector_int_size(path_0) == 0)) { + /* No path found. */ + igraph_vector_int_list_clear(edge_paths); + goto cleanup; + } + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&paths_pot, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&path_spur, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&path_root, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&path_total, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges_removed, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); + IGRAPH_VECTOR_INIT_FINALLY(¤t_weights, nr_edges); + + /* If weights are NULL we use a uniform weight vector where each edge has + * a weight of 1. Later on, we replace the weights of removed edges with + * infinities. Note that we work on a copy of the weight vector so the + * original vector remains intact. + */ + if (weights) { + igraph_vector_update(¤t_weights, weights); + } else { + igraph_vector_fill(¤t_weights, 1); + } + + for (i_path_current = 1; i_path_current < k; i_path_current++) { + igraph_vector_int_t *path_previous = igraph_vector_int_list_tail_ptr(edge_paths); + igraph_integer_t path_previous_length = igraph_vector_int_size(path_previous); + for (nr_edges_root = 0; nr_edges_root < path_previous_length; nr_edges_root++) { + /* Determine spur node. */ + if (mode == IGRAPH_OUT) { + vertex_spur = IGRAPH_FROM(graph, VECTOR(*path_previous)[nr_edges_root]); + } else if (mode == IGRAPH_IN) { + vertex_spur = IGRAPH_TO(graph, VECTOR(*path_previous)[nr_edges_root]); + } else { + igraph_integer_t eid = VECTOR(*path_previous)[nr_edges_root]; + igraph_integer_t vertex_spur_1 = IGRAPH_FROM(graph, eid); + igraph_integer_t vertex_spur_2 = IGRAPH_TO(graph, eid); + igraph_integer_t vertex_spur_3; + igraph_integer_t vertex_spur_4; + if (nr_edges_root < path_previous_length-1) { + igraph_integer_t eid_next = VECTOR(*path_previous)[nr_edges_root + 1]; + vertex_spur_3 = IGRAPH_FROM(graph, eid_next); + vertex_spur_4 = IGRAPH_TO(graph, eid_next); + } else { + vertex_spur_3 = vertex_spur_4 = to; + } + if (vertex_spur_1 == vertex_spur_3 || vertex_spur_1 == vertex_spur_4) { + vertex_spur = vertex_spur_2; + } else { + vertex_spur = vertex_spur_1; + } + } + + /* Determine root path. */ + IGRAPH_CHECK(igraph_vector_int_resize(&path_root, nr_edges_root)); + for (i = 0; i < nr_edges_root; i++) { + VECTOR(path_root)[i] = VECTOR(*path_previous)[i]; + } + + /* Remove edges that are part of the previous shortest paths which share the same root path. */ + for (i_path = 0; i_path < i_path_current; i_path++) { + igraph_vector_int_t *path_check = igraph_vector_int_list_get_ptr(edge_paths, i_path); + igraph_bool_t equal = true; + for (i = 0; i < nr_edges_root; i++) { + if (VECTOR(path_root)[i] != VECTOR(*path_check)[i]) { + equal = false; + break; + } + } + if (equal) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges_removed, VECTOR(*path_check)[nr_edges_root])); + VECTOR(current_weights)[VECTOR(*path_check)[nr_edges_root]] = IGRAPH_INFINITY; + } + } + + /* pseudocode: for each node rootPathNode in rootPath except spurNode: + * remove rootPathNode from Graph; + */ + for (edge_path_root = 0; edge_path_root < nr_edges_root; edge_path_root++) { + if (mode == IGRAPH_OUT) { + vertex_root_del = IGRAPH_FROM(graph, VECTOR(path_root)[edge_path_root]); + } else if (mode == IGRAPH_IN) { + vertex_root_del = IGRAPH_TO(graph, VECTOR(path_root)[edge_path_root]); + } else { + igraph_integer_t eid = VECTOR(*path_previous)[edge_path_root]; + igraph_integer_t eid_next = VECTOR(*path_previous)[edge_path_root + 1]; + igraph_integer_t vertex_root_del_1 = IGRAPH_FROM(graph, eid); + igraph_integer_t vertex_root_del_2 = IGRAPH_TO(graph, eid); + igraph_integer_t vertex_root_del_3 = IGRAPH_FROM(graph, eid_next); + igraph_integer_t vertex_root_del_4 = IGRAPH_TO(graph, eid_next); + if (vertex_root_del_1 == vertex_root_del_3 || vertex_root_del_1 == vertex_root_del_4) { + vertex_root_del = vertex_root_del_2; + } else { + vertex_root_del = vertex_root_del_1; + } + } + /* Remove vertex by setting incident edges to infinity */ + IGRAPH_CHECK(igraph_i_semidelete_vertex( + graph, ¤t_weights, vertex_root_del, &edges_removed, + &eids + )); + } + + /* Determine spur path */ + IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, + NULL, + &path_spur, + vertex_spur, + to, + ¤t_weights, + mode)); + infinite_path = igraph_i_has_edge_with_infinite_weight(&path_spur, ¤t_weights); + + /* Add total (root + spur) path to potential paths if it's not in there yet. */ + if (!infinite_path) { + IGRAPH_CHECK(igraph_vector_int_update(&path_total, &path_root)); + IGRAPH_CHECK(igraph_vector_int_append(&path_total, &path_spur)); + + already_in_potential_paths = false; + n = igraph_vector_int_list_size(&paths_pot); + for (i = 0; i < n; i++) { + if (igraph_vector_int_all_e(&path_total, igraph_vector_int_list_get_ptr(&paths_pot, i))) { + already_in_potential_paths = true; + break; + } + } + + if (!already_in_potential_paths) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(&paths_pot, &path_total)); + } + } + + /* Cleanup */ + n = igraph_vector_int_size(&edges_removed); + for (i = 0; i < n; i++) { + VECTOR(current_weights)[VECTOR(edges_removed)[i]] = + weights ? VECTOR(*weights)[VECTOR(edges_removed)[i]] : 1; + } + igraph_vector_int_clear(&edges_removed); + } + + /* Add shortest potential path to shortest paths */ + n = igraph_vector_int_list_size(&paths_pot); + if (n == 0) { + break; + } + + shortest_path_weight = igraph_i_get_total_weight_of_path( + igraph_vector_int_list_get_ptr(&paths_pot, 0), weights + ); + i_path = 0; + for (i = 1; i < n; i++) { + path_weight = igraph_i_get_total_weight_of_path( + igraph_vector_int_list_get_ptr(&paths_pot, i), weights + ); + if (path_weight < shortest_path_weight) { + i_path = i; + shortest_path_weight = path_weight; + } + } + + IGRAPH_CHECK(igraph_vector_int_list_remove_fast(&paths_pot, i_path, &path_shortest)); + IGRAPH_CHECK(igraph_vector_int_list_push_back(edge_paths, &path_shortest)); + } + + igraph_vector_destroy(¤t_weights); + igraph_vector_int_destroy(&eids); + igraph_vector_int_destroy(&edges_removed); + igraph_vector_int_destroy(&path_total); + igraph_vector_int_destroy(&path_root); + igraph_vector_int_destroy(&path_spur); + igraph_vector_int_list_destroy(&paths_pot); + IGRAPH_FINALLY_CLEAN(7); + + if (vertex_paths) { + igraph_integer_t no_of_edge_paths = igraph_vector_int_list_size(edge_paths); + + IGRAPH_CHECK(igraph_vector_int_list_resize(vertex_paths, no_of_edge_paths)); + for (i = 0; i < no_of_edge_paths; i++) { + igraph_vector_int_t* edge_path = igraph_vector_int_list_get_ptr(edge_paths, i); + igraph_vector_int_t* vertex_path = igraph_vector_int_list_get_ptr(vertex_paths, i); + IGRAPH_CHECK(igraph_vertex_path_from_edge_path(graph, from, edge_path, vertex_path, mode)); + } + } + +cleanup: + if (edge_paths_owned >= 2) { + igraph_vector_int_list_destroy(edge_paths); + IGRAPH_FINALLY_CLEAN(1); + } + if (edge_paths_owned >= 1) { + igraph_free(edge_paths); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/simple_paths.c b/src/paths/simple_paths.c new file mode 100644 index 0000000..1383775 --- /dev/null +++ b/src/paths/simple_paths.c @@ -0,0 +1,167 @@ +/* + IGraph library. + Copyright (C) 2014-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, see . +*/ + + +#include "igraph_paths.h" + +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_adjlist.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 when the + * graph has many cycles. Consider using the \p cutoff + * parameter when you do not need long paths. + * + * \param graph The input graph. + * \param res Initialized integer vector. The paths are + * returned here in terms of their vertices, 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. + * + * \sa \ref igraph_get_k_shortest_paths() + * + * Time complexity: O(n!) in the worst case, n is the number of + * vertices. + */ + +igraph_error_t 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_lazy_adjlist_t adjlist; + igraph_vector_int_t stack, dist; /* used as a stack, but represented as a vector, + in order to be appendable to other vectors */ + igraph_vector_bool_t markto, added; + igraph_vector_int_t nptr; + int iter = 0; + + if (from < 0 || from >= no_nodes) { + IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); + } + + if (!toall) { + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&markto, no_nodes); + 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) ] = true; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + } + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&added, no_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&stack, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&dist, 100); + IGRAPH_CHECK(igraph_lazy_adjlist_init( + graph, &adjlist, mode, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE + )); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nptr, no_nodes); + + 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] = true; + while (!igraph_vector_int_empty(&stack)) { + igraph_integer_t act = igraph_vector_int_tail(&stack); + igraph_integer_t curdist = igraph_vector_int_tail(&dist); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, act); + igraph_integer_t n; + igraph_integer_t *ptr = igraph_vector_int_get_ptr(&nptr, act); + igraph_bool_t any; + igraph_bool_t within_dist; + igraph_integer_t nei; + + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + + n = igraph_vector_int_size(neis); + + within_dist = (curdist < cutoff || cutoff < 0); + if (within_dist) { + /* Search for a neighbor that was not yet visited */ + any = false; + while (!any && (*ptr) < n) { + nei = 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] = true; + /* 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 */ + igraph_integer_t up = igraph_vector_int_pop_back(&stack); + igraph_vector_int_pop_back(&dist); + VECTOR(added)[up] = false; + VECTOR(nptr)[up] = 0; + } + + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 13); + } + + igraph_vector_int_destroy(&nptr); + igraph_lazy_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&dist); + igraph_vector_int_destroy(&stack); + igraph_vector_bool_destroy(&added); + IGRAPH_FINALLY_CLEAN(5); + + if (!toall) { + igraph_vector_bool_destroy(&markto); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/sparsifier.c b/src/paths/sparsifier.c new file mode 100644 index 0000000..b8b9b98 --- /dev/null +++ b/src/paths/sparsifier.c @@ -0,0 +1,462 @@ +/* + 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 . +*/ + + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_bitset.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "core/interruption.h" + +/* + * This internal function gets the adjacency and incidence list representation + * of the current residual graph, the weight vector, the current assignment of + * the vertices to clusters, whether the i-th cluster is sampled, and the + * index of a single node v. The function updates the given lightest_eid vector + * such that the i-th element contains the ID of the lightest edge that leads + * from node v to cluster i. Similarly, the lightest_weight vector is updated + * to contain the weights of these edges. + * + * When the is_cluster_sampled vector is provided, the + * nearest_neighboring_sampled_cluster pointer is also updated to the index of + * the cluster that has the smallest weight among the _sampled_ ones. + * + * As a pre-condition, this function requires the lightest_eid vector to be + * filled with -1 and the lightest_weight vector to be filled with infinity. + * This is _not_ checked within the function. + * + * Use the igraph_i_clean_lightest_edges_to_clusters() function to clear these vectors + * after you are done with them. Avoid using igraph_vector_fill() because that + * one is O(|V|), while igraph_i_clean_lightest_edge_vector() is O(d) where d + * is the degree of the vertex. + */ +static igraph_error_t igraph_i_collect_lightest_edges_to_clusters( + const igraph_adjlist_t *adjlist, + const igraph_inclist_t *inclist, + const igraph_vector_t *weights, + const igraph_vector_int_t *clustering, + const igraph_bitset_t *is_cluster_sampled, + igraph_integer_t v, + igraph_vector_int_t *lightest_eid, + igraph_vector_t *lightest_weight, + igraph_vector_int_t *dirty_vids, + igraph_integer_t *nearest_neighboring_sampled_cluster +) { + // This internal function gets the residual graph, the clustering, the sampled clustering and + // the vector and return the lightest edge to each neighboring cluster and the index of the lightest + // sampled cluster (if any) + + igraph_real_t lightest_weight_to_sampled = INFINITY; + igraph_vector_int_t* adjacent_nodes = igraph_adjlist_get(adjlist, v); + igraph_vector_int_t* incident_edges = igraph_inclist_get(inclist, v); + igraph_integer_t i, nlen = igraph_vector_int_size(incident_edges); + + for (i = 0; i < nlen; i++) { + igraph_integer_t neighbor_node = VECTOR(*adjacent_nodes)[i]; + igraph_integer_t edge = VECTOR(*incident_edges)[i]; + igraph_integer_t neighbor_cluster = VECTOR(*clustering)[neighbor_node]; + igraph_real_t weight = weights ? VECTOR(*weights)[edge] : 1; + + // If the weight of the edge being considered is smaller than the weight + // of the lightest edge found so far that connects v to the same + // cluster, remember the new minimum. + if (VECTOR(*lightest_weight)[neighbor_cluster] > weight) { + VECTOR(*lightest_weight)[neighbor_cluster] = weight; + VECTOR(*lightest_eid)[neighbor_cluster] = edge; + + IGRAPH_CHECK(igraph_vector_int_push_back(dirty_vids, neighbor_cluster)); + + // Also, if this cluster happens to be a sampled cluster, also update + // the variables that store which is the lightest edge that connects + // v to any of the sampled clusters. + if (is_cluster_sampled) { + if ((IGRAPH_BIT_TEST(*is_cluster_sampled, neighbor_cluster)) && (lightest_weight_to_sampled > weight)) { + lightest_weight_to_sampled = weight; + *nearest_neighboring_sampled_cluster = neighbor_cluster; + } + } + } + } + + return IGRAPH_SUCCESS; +} + +static void igraph_i_clear_lightest_edges_to_clusters( + igraph_vector_int_t *dirty_vids, + igraph_vector_int_t *lightest_eid, + igraph_vector_t *lightest_weight +) { + igraph_integer_t i, n = igraph_vector_int_size(dirty_vids); + for (i = 0; i < n; i++) { + igraph_integer_t vid = VECTOR(*dirty_vids)[i]; + VECTOR(*lightest_weight)[vid] = INFINITY; + VECTOR(*lightest_eid)[vid] = -1; + } + + igraph_vector_int_clear(dirty_vids); +} + +/** + * \ingroup structural + * \function igraph_spanner + * \brief Calculates a spanner of a graph with a given stretch factor. + * + * A spanner of a graph G = (V,E) with a stretch \c t is a + * subgraph H = (V,Es) such that \c Es is a subset of \c E + * and the distance between any pair of nodes in \c H is at most \c t + * times the distance in \c G. The returned graph is always a spanner of + * the given graph with the specified stretch. For weighted graphs the + * number of edges in the spanner is O(k n^(1 + 1 / k)), where + * \c k is k = (t + 1) / 2, \c m is the number of edges + * and \c n is the number of nodes in \c G. For unweighted graphs the number + * of edges is O(n^(1 + 1 / k) + kn). + * + * + * This function is based on the algorithm of Baswana and Sen: "A Simple and + * Linear Time Randomized Algorithm for Computing Sparse Spanners in + * Weighted Graphs". https://doi.org/10.1002/rsa.20130 + * + * \param graph An undirected connected graph object. If the graph + * is directed, the directions of the edges will be ignored. + * \param spanner An initialized vector, the IDs of the edges that constitute + * the calculated spanner will be returned here. Use + * \ref igraph_subgraph_from_edges() to extract the spanner as a separate + * graph object. + * \param stretch The stretch factor \c t of the spanner. + * \param weights The edge weights or \c NULL. + * + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \endclist + * + * Time complexity: The algorithm is a randomized Las Vegas algorithm. The expected + * running time is O(km) where k is the value mentioned above and m is the number + * of edges. + */ +igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanner, + igraph_real_t stretch, const igraph_vector_t *weights) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, v, nlen, neighbor, cluster; + igraph_real_t sample_prob, k = (stretch + 1) / 2, weight, lightest_sampled_weight; + igraph_vector_int_t clustering, lightest_eid; + igraph_vector_t lightest_weight; + igraph_bitset_t is_cluster_sampled; + igraph_bitset_t is_edge_in_spanner; + igraph_vector_int_t new_clustering; + igraph_vector_int_t dirty_vids; + igraph_vector_int_t *adjacent_vertices; + igraph_vector_int_t *incident_edges; + igraph_adjlist_t adjlist; + igraph_inclist_t inclist; + igraph_integer_t edge; + igraph_integer_t index; + + if (spanner == NULL) { + return IGRAPH_SUCCESS; + } + + /* Test validity of stretch factor */ + if (stretch < 1) { + IGRAPH_ERROR("Stretch factor must be at least 1.", IGRAPH_EINVAL); + } + + /* Test validity of weights vector */ + if (weights) { + 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 (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + } + + // Clear the vector that will contain the IDs of the edges in the spanner + igraph_vector_int_clear(spanner); + + // Create an incidence list representation of the graph and also create the + // corresponding adjacency list. The residual graph will not be constructed + // explicitly; it will only exist in terms of the incidence and the adjacency + // lists, maintained in parallel as the edges are removed from the residual + // graph. + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + IGRAPH_CHECK(igraph_adjlist_init_from_inclist(graph, &adjlist, &inclist)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + // Phase 1: forming the clusters + // Create a vector which maps the nodes to the centers of the corresponding + // clusters. At the beginning each node is its own cluster center. + IGRAPH_CHECK(igraph_vector_int_init_range(&clustering, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &clustering); + + // A mapping vector which indicates the neighboring edge with the smallest + // weight for each cluster central, for a single vertex of interest. + // Preconditions needed by igraph_i_collect_lightest_edges_to_clusters() + // are enforced here. + IGRAPH_VECTOR_INT_INIT_FINALLY(&lightest_eid, no_of_nodes); + igraph_vector_int_fill(&lightest_eid, -1); + + // A mapping vector which indicated the minimum weight to each neighboring + // cluster, for a single vertex of interest. + // Preconditions needed by igraph_i_collect_lightest_edges_to_clusters() + // are enforced here. + IGRAPH_VECTOR_INIT_FINALLY(&lightest_weight, no_of_nodes); + igraph_vector_fill(&lightest_weight, IGRAPH_INFINITY); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_clustering, no_of_nodes); + + // A boolean vector whose i-th element is 1 if the i-th vertex is a cluster + // center that is sampled in the current iteration, 0 otherwise + IGRAPH_BITSET_INIT_FINALLY(&is_cluster_sampled, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&is_edge_in_spanner, no_of_edges); + + // Temporary vector used by igraph_i_collect_lightest_edges_to_clusters() + // to keep track of the nodes that it has written to + IGRAPH_VECTOR_INT_INIT_FINALLY(&dirty_vids, 0); + + sample_prob = pow(no_of_nodes, -1 / k); + +#define ADD_EDGE_TO_SPANNER \ + if (!IGRAPH_BIT_TEST(is_edge_in_spanner, edge)) { \ + IGRAPH_BIT_SET(is_edge_in_spanner, edge); \ + IGRAPH_CHECK(igraph_vector_int_push_back(spanner, edge)); \ + } + + igraph_vector_fill(&lightest_weight, INFINITY); + + for (i = 0; i < k - 1; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_vector_int_fill(&new_clustering, -1); + igraph_bitset_null(&is_cluster_sampled); + + // Step 1: sample cluster centers + RNG_BEGIN(); + for (j = 0; j < no_of_nodes; j++) { + if (VECTOR(clustering)[j] == j && RNG_UNIF01() < sample_prob) { + IGRAPH_BIT_SET(is_cluster_sampled, j); + } + } + RNG_END(); + + // Step 2 and 3 + for (v = 0; v < no_of_nodes; v++) { + // If v is inside a cluster and the cluster of v is sampled, then continue + cluster = VECTOR(clustering)[v]; + if (cluster != -1 && IGRAPH_BIT_TEST(is_cluster_sampled, cluster)) { + VECTOR(new_clustering)[v] = cluster; + continue; + } + + // Step 2: find the lightest edge that connects vertex v to its + // neighboring sampled clusters + igraph_integer_t nearest_neighboring_sampled_cluster = -1; + IGRAPH_CHECK(igraph_i_collect_lightest_edges_to_clusters( + &adjlist, + &inclist, + weights, + &clustering, + &is_cluster_sampled, + v, + &lightest_eid, + &lightest_weight, + &dirty_vids, + &nearest_neighboring_sampled_cluster + )); + + // Step 3: add edges to spanner + if (nearest_neighboring_sampled_cluster == -1) { + // Case 3(a) from the paper: v is not adjacent to any of the + // sampled clusters. + + // Add lightest edge which connects vertex v to each neighboring + // cluster (none of which are sampled) + for (j = 0; j < no_of_nodes; j++) { + edge = VECTOR(lightest_eid)[j]; + if (edge != -1) { + ADD_EDGE_TO_SPANNER; + } + } + + // Remove all edges incident on v from the graph. Note that each + // edge being removed occurs twice in the adjacency / incidence + // lists + adjacent_vertices = igraph_adjlist_get(&adjlist, v); + incident_edges = igraph_inclist_get(&inclist, v); + nlen = igraph_vector_int_size(incident_edges); + for (j = 0; j < nlen; j++) { + neighbor = VECTOR(*adjacent_vertices)[j]; + if (neighbor == v) { + /* should not happen as we did not ask for loop edges in + * the adjacency / incidence lists, but let's be defensive */ + continue; + } + + if (igraph_vector_int_search( + igraph_inclist_get(&inclist, neighbor), + 0, + VECTOR(*incident_edges)[j], + &index + )) { + igraph_vector_int_remove_fast(igraph_adjlist_get(&adjlist, neighbor), index); + igraph_vector_int_remove_fast(igraph_inclist_get(&inclist, neighbor), index); + } + } + igraph_vector_int_clear(adjacent_vertices); + igraph_vector_int_clear(incident_edges); + } else { + // Case 3(b) from the paper: v is adjacent to at least one of + // the sampled clusters + + // add the edge connecting to the lightest sampled cluster + edge = VECTOR(lightest_eid)[nearest_neighboring_sampled_cluster]; + ADD_EDGE_TO_SPANNER; + + // 'lightest_sampled_weight' is the weight of the lightest edge connecting v to + // one of the sampled clusters. This is where v will belong in + // the new clustering. + lightest_sampled_weight = VECTOR(lightest_weight)[nearest_neighboring_sampled_cluster]; + VECTOR(new_clustering)[v] = nearest_neighboring_sampled_cluster; + + // Add to the spanner light edges with weight less than 'lightest_sampled_weight' + for (j = 0; j < no_of_nodes; j++) { + if (VECTOR(lightest_weight)[j] < lightest_sampled_weight) { + edge = VECTOR(lightest_eid)[j]; + ADD_EDGE_TO_SPANNER; + } + } + + // Remove edges to centers with edge weight less than 'lightest_sampled_weight' + adjacent_vertices = igraph_adjlist_get(&adjlist, v); + incident_edges = igraph_inclist_get(&inclist, v); + nlen = igraph_vector_int_size(incident_edges); + for (j = 0; j < nlen; j++) { + neighbor = VECTOR(*adjacent_vertices)[j]; + if (neighbor == v) { + /* should not happen as we did not ask for loop edges in + * the adjacency / incidence lists, but let's be defensive */ + continue; + } + + cluster = VECTOR(clustering)[neighbor]; + weight = VECTOR(lightest_weight)[cluster]; + if ((cluster == nearest_neighboring_sampled_cluster) || (weight < lightest_sampled_weight)) { + edge = VECTOR(*incident_edges)[j]; + + if (igraph_vector_int_search( + igraph_inclist_get(&inclist, neighbor), 0, edge, &index + )) { + igraph_vector_int_remove_fast(igraph_adjlist_get(&adjlist, neighbor), index); + igraph_vector_int_remove_fast(igraph_inclist_get(&inclist, neighbor), index); + } + + igraph_vector_int_remove_fast(adjacent_vertices, j); + igraph_vector_int_remove_fast(incident_edges, j); + + j--; + nlen--; + } + } + } + + // We don't need lightest_eids and lightest_weights any more so + // clear them in O(d) time + igraph_i_clear_lightest_edges_to_clusters( + &dirty_vids, &lightest_eid, &lightest_weight + ); + } + + // Commit the new clustering + igraph_vector_int_update(&clustering, &new_clustering); /* reserved */ + + // Remove intra-cluster edges + for (v = 0; v < no_of_nodes; v++) { + adjacent_vertices = igraph_adjlist_get(&adjlist, v); + incident_edges = igraph_inclist_get(&inclist, v); + nlen = igraph_vector_int_size(incident_edges); + for (j = 0; j < nlen; j++) { + neighbor = VECTOR(*adjacent_vertices)[j]; + edge = VECTOR(*incident_edges)[j]; + + if (VECTOR(clustering)[neighbor] == VECTOR(clustering)[v]) { + /* We don't need to bother with removing the other copy + * of the edge from the incidence lists (and the corresponding + * vertices from the adjacency lists) because we will find + * them anyway as we are iterating over all nodes */ + igraph_vector_int_remove_fast(adjacent_vertices, j); + igraph_vector_int_remove_fast(incident_edges, j); + j--; + nlen--; + } + } + } + } + + // Phase 2: vertex_clustering joining + for (v = 0; v < no_of_nodes; v++) { + if (VECTOR(clustering)[v] != -1) { + IGRAPH_CHECK(igraph_i_collect_lightest_edges_to_clusters( + &adjlist, + &inclist, + weights, + &clustering, + /* is_cluster_sampled = */ NULL, + v, + &lightest_eid, + &lightest_weight, + &dirty_vids, + NULL + )); + for (j = 0; j < no_of_nodes; j++) { + edge = VECTOR(lightest_eid)[j]; + if (edge != -1) { + ADD_EDGE_TO_SPANNER; + } + } + igraph_i_clear_lightest_edges_to_clusters(&dirty_vids, &lightest_eid, &lightest_weight); + } + } + + // Free memory + igraph_vector_int_destroy(&dirty_vids); + igraph_bitset_destroy(&is_edge_in_spanner); + igraph_bitset_destroy(&is_cluster_sampled); + igraph_vector_int_destroy(&new_clustering); + igraph_vector_destroy(&lightest_weight); + igraph_vector_int_destroy(&lightest_eid); + igraph_vector_int_destroy(&clustering); + igraph_adjlist_destroy(&adjlist); + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/unweighted.c b/src/paths/unweighted.c new file mode 100644 index 0000000..2bf3aff --- /dev/null +++ b/src/paths/unweighted.c @@ -0,0 +1,619 @@ +/* -*- 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_distances_cutoff + * \brief Length of the shortest paths between vertices, with cutoff. + * + * \experimental + * + * This function is similar to \ref igraph_distances(), but + * paths longer than \p cutoff will not be considered. + * + * \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 \p from + * argument, and its number of columns is the number of + * vertices in the \p to argument. One row of the matrix shows the + * distances from/to a given vertex to the ones in \p to. + * For the unreachable vertices \c IGRAPH_INFINITY is returned. + * \param from The source vertices._d + * \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 + * \param cutoff The maximal length of paths that will be considered. + * When the distance of two vertices is greater than this value, + * it will be returned as \c IGRAPH_INFINITY. Negative cutoffs are + * treated as infinity. + * \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(s |E| + |V|), where s is the number of source vertices to use, + * and |V| and |E| are the number of vertices and edges in the graph. + * + * \sa \ref igraph_distances_dijkstra_cutoff() for the weighted version with non-negative + * weights. + * + * \example examples/simple/distances.c + */ +igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t from, const igraph_vs_t to, + igraph_neimode_t mode, igraph_real_t cutoff) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_from, no_of_to; + igraph_integer_t *already_counted; + igraph_adjlist_t adjlist; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_int_t *neis; + igraph_bool_t all_to; + + igraph_integer_t i, j; + igraph_vit_t fromvit, tovit; + igraph_vector_int_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, igraph_integer_t); + IGRAPH_CHECK_OOM(already_counted, "Insufficient memory for graph distance calculation."); + IGRAPH_FINALLY(igraph_free, already_counted); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + all_to = igraph_vs_is_all(&to); + if (all_to) { + no_of_to = no_of_nodes; + } else { + IGRAPH_VECTOR_INT_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)) { + igraph_integer_t v = IGRAPH_VIT_GET(tovit); + if (VECTOR(indexv)[v]) { + IGRAPH_ERROR("Target vertex list must not have any duplicates.", + IGRAPH_EINVAL); + } + VECTOR(indexv)[v] = ++i; + } + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); + igraph_matrix_fill(res, IGRAPH_INFINITY); + + for (IGRAPH_VIT_RESET(fromvit), i = 0; + !IGRAPH_VIT_END(fromvit); + IGRAPH_VIT_NEXT(fromvit), i++) { + igraph_integer_t reached = 0; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, IGRAPH_VIT_GET(fromvit))); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + already_counted[ IGRAPH_VIT_GET(fromvit) ] = i + 1; + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + + if (cutoff >= 0 && actdist > cutoff) { + continue; + } + + if (all_to) { + MATRIX(*res, i, act) = actdist; + } else { + if (VECTOR(indexv)[act]) { + MATRIX(*res, i, VECTOR(indexv)[act] - 1) = actdist; + reached++; + if (reached == no_of_to) { + igraph_dqueue_int_clear(&q); + break; + } + } + } + + neis = igraph_adjlist_get(&adjlist, act); + igraph_integer_t nei_count = igraph_vector_int_size(neis); + for (j = 0; j < nei_count; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (already_counted[neighbor] == i + 1) { + continue; + } + already_counted[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + } + } + } + + /* Clean */ + if (!all_to) { + igraph_vit_destroy(&tovit); + igraph_vector_int_destroy(&indexv); + IGRAPH_FINALLY_CLEAN(2); + } + + IGRAPH_FREE(already_counted); + igraph_dqueue_int_destroy(&q); + igraph_vit_destroy(&fromvit); + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_distances + * \brief 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 \p from + * argument, and its number of columns is the number of + * vertices in the \p to argument. One row of the matrix shows the + * distances from/to a given vertex to the ones in \p to. + * For the unreachable vertices \c 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_distances_dijkstra() for the weighted version with non-negative + * weights, \ref igraph_distances_bellman_ford() if you also have negative + * weights. + * + * \example examples/simple/distances.c + */ +igraph_error_t igraph_distances(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t from, const igraph_vs_t to, + igraph_neimode_t mode) { + return igraph_distances_cutoff(graph, res, from, to, mode, -1); +} + +/** + * \function igraph_shortest_paths + * \brief Length of the shortest paths between vertices. + * + * \deprecated-by igraph_distances 0.10.0 + */ +igraph_error_t 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) { + return igraph_distances(graph, res, from, to, mode); +} + +/** + * \ingroup structural + * \function igraph_get_shortest_paths + * \brief Shortest paths from a vertex. + * + * Finds unweighted shortest paths from a single source vertex to the specified + * sets of target vertices. If there is more than one geodesic between two vertices, + * this function gives only one of them. Use \ref igraph_get_all_shortest_paths() + * to find \em all shortest paths. + * + * \param graph The graph object. + * \param vertices The result, the IDs of the vertices along the paths. + * This is a list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 parents A pointer to an initialized igraph vector or \c NULL. + * If not \c NULL, a vector containing the parent of each vertex in + * the single source shortest path tree is returned here. The + * parent of vertex \c i in the tree is the vertex from which vertex \c i + * was reached. The parent of the start vertex (in the \p from + * argument) is -1. If the parent is -2, 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 + * \p to are reached. + * \param inbound_edges A pointer to an initialized igraph vector or \c NULL. + * If not \c 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 \c i in the tree is the edge via which vertex \c 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 + * \p 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 + * \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_distances() if you only need the path lengths but + * not the paths themselves; \ref igraph_get_shortest_paths_dijkstra() + * for the weighted version; \ref igraph_get_all_shortest_paths() to + * return all shortest paths between (source, target) pairs. + * + * \example examples/simple/igraph_get_shortest_paths.c + */ +igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, const igraph_vs_t to, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges) { + + /* TODO: use inclist_t if to is long (longer than 1?) */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t *parent_eids; + + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + + igraph_integer_t i, j, vsize; + igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; + + igraph_vit_t vit; + + igraph_integer_t to_reach; + igraph_integer_t reached = 0; + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("Index of source vertex is out of range.", 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_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(vit))); + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(vit))); + } + + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest path calculation."); + IGRAPH_FINALLY(igraph_free, parent_eids); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_DQUEUE_INT_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 (parent_eids[ IGRAPH_VIT_GET(vit) ] == 0) { + parent_eids[ IGRAPH_VIT_GET(vit) ] = -1; + } else { + to_reach--; /* this node was given multiple times */ + } + } + + /* Meaning of parent_eids[i]: + * + * - If parent_eids[i] < 0, it means that vertex i has to be reached and has not + * been reached yet. + * + * - If parent_eids[i] = 0, it means that vertex i does not have to be reached and + * it has not been reached yet. + * + * - If parent_eids[i] = 1, it means that vertex i is the start vertex. + * + * - Otherwise, parent_eids[i] is the ID of the edge from which vertex i was + * reached plus 2. + */ + + IGRAPH_CHECK(igraph_dqueue_int_push(&q, from + 1)); + if (parent_eids[ from ] < 0) { + reached++; + } + parent_eids[ from ] = 1; + + while (!igraph_dqueue_int_empty(&q) && reached < to_reach) { + igraph_integer_t act = igraph_dqueue_int_pop(&q) - 1; + + IGRAPH_CHECK(igraph_incident(graph, &tmp, act, mode)); + vsize = igraph_vector_int_size(&tmp); + for (j = 0; j < vsize; j++) { + igraph_integer_t edge = VECTOR(tmp)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, act); + if (parent_eids[neighbor] > 0) { + continue; + } else if (parent_eids[neighbor] < 0) { + reached++; + } + parent_eids[neighbor] = edge + 2; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor + 1)); + } + } + + if (reached < to_reach) { + IGRAPH_WARNING("Couldn't reach some vertices"); + } + + /* Create `parents' if needed */ + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*parents)[i] = -2; + } else if (parent_eids[i] == 1) { + /* i is the start vertex */ + VECTOR(*parents)[i] = -1; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 2 */ + VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 2, i); + } + } + } + + /* Create `inbound_edges' if needed */ + if (inbound_edges) { + IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (parent_eids[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 = parent_eids[i] - 2 */ + VECTOR(*inbound_edges)[i] = parent_eids[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++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_vector_int_t *vvec = 0, *evec = 0; + if (vertices) { + vvec = igraph_vector_int_list_get_ptr(vertices, j); + igraph_vector_int_clear(vvec); + } + if (edges) { + evec = igraph_vector_int_list_get_ptr(edges, j); + igraph_vector_int_clear(evec); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + if (parent_eids[node] > 0) { + igraph_integer_t act = node; + igraph_integer_t size = 0; + igraph_integer_t edge; + while (parent_eids[act] > 1) { + size++; + edge = parent_eids[act] - 2; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vvec) { + IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); + VECTOR(*vvec)[size] = node; + } + if (evec) { + IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); + } + act = node; + while (parent_eids[act] > 1) { + size--; + edge = parent_eids[act] - 2; + act = IGRAPH_OTHER(graph, edge, act); + if (vvec) { + VECTOR(*vvec)[size] = act; + } + if (evec) { + VECTOR(*evec)[size] = edge; + } + } + } + } + } + + /* Clean */ + IGRAPH_FREE(parent_eids); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&tmp); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + +/** + * \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 is more than one shortest + * path 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 initialized 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. + */ + +igraph_error_t igraph_get_shortest_path(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + igraph_neimode_t mode) { + + igraph_vector_int_list_t vertices2, *vp = &vertices2; + igraph_vector_int_list_t edges2, *ep = &edges2; + + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); + } else { + vp = NULL; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); + } else { + ep = NULL; + } + + IGRAPH_CHECK(igraph_get_shortest_paths(graph, vp, ep, from, + igraph_vss_1(to), mode, NULL, NULL)); + + /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the + result to the output parameter. */ + if (edges) { + IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); + igraph_vector_int_list_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); + igraph_vector_int_list_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/voronoi.c b/src/paths/voronoi.c new file mode 100644 index 0000000..146a556 --- /dev/null +++ b/src/paths/voronoi.c @@ -0,0 +1,425 @@ +/* + 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, see . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +static igraph_error_t igraph_i_voronoi( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_t *mindist, + const igraph_vector_int_t *generators, + igraph_neimode_t mode, + igraph_voronoi_tiebreaker_t tiebreaker) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_generators = igraph_vector_int_size(generators); + igraph_adjlist_t al; + igraph_dqueue_int_t q; + + igraph_vector_int_t already_counted; + + /* tie_count[vid] is the number of generators that vid is an equal distance from. + * This value is needed to pick one of these generators uniformly at random + * without needing to store all of them. */ + igraph_vector_int_t tie_count; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&already_counted, no_of_nodes); + + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&tie_count, no_of_nodes); + RNG_BEGIN(); + } + + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_fill(membership, -1); + + IGRAPH_CHECK(igraph_vector_resize(mindist, no_of_nodes)); + igraph_vector_fill(mindist, IGRAPH_INFINITY); + + /* Loop through all generator points and compute shortest paths to all other vertices. + * As we go, we keep track of the shortest distance from any generator to each vertex + * in 'mindist'. If we find that the distance from the current generator to a vertex + * is shorter than what was recorded so far in 'mindist', we update 'mindist' and + * assign that vertex to the current generator. + */ + for (igraph_integer_t i=0; i < no_of_generators; i++) { + igraph_integer_t g = VECTOR(*generators)[i]; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* BFS-based unweighted shortest path implementation */ + + igraph_dqueue_int_clear(&q); + + VECTOR(already_counted)[g] = i+1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, g)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t vid = igraph_dqueue_int_pop(&q); + igraph_integer_t dist = igraph_dqueue_int_pop(&q); + + /* Attention! This must be igraph_real_t, not igraph_integer_t + * because later it will be compared with another igraph_real_t + * whose value may be infinite. */ + igraph_real_t md = VECTOR(*mindist)[vid]; + + if (dist > md) { + /* This vertex is reachable at a shorter distance from + * another generator. Thus all its descendants in the shortest + * path tree are also reachable at a shorter distance from the + * other generator than from the current one. Therefore + * we do not need to search further from here. */ + continue; + } else if (dist < md) { + /* This vertex is closest to the current generator so far. + * Assign it to the current partition. */ + VECTOR(*mindist)[vid] = dist; + VECTOR(*membership)[vid] = i; + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + VECTOR(tie_count)[vid] = 1; + } + } else { /* md == dist, we have a tie */ + switch (tiebreaker) { + case IGRAPH_VORONOI_FIRST: + /* Never replace existing generator assignment. */ + break; + case IGRAPH_VORONOI_LAST: + /* Always replace existing generator assignment. */ + VECTOR(*membership)[vid] = i; + break; + case IGRAPH_VORONOI_RANDOM: + /* We replace the membership assignment with probability 1/k upon + * encountering the kth same-distance generator. This ensures + * that one of these generators is selected uniformly at random. */ + VECTOR(tie_count)[vid]++; + if (RNG_UNIF01() < 1.0 / VECTOR(tie_count)[vid]) { + VECTOR(*membership)[vid] = i; + } + break; + } + } + + igraph_vector_int_t *neis = igraph_adjlist_get(&al, vid); + igraph_integer_t nei_count = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nei_count; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (VECTOR(already_counted)[neighbor] == i + 1) { + continue; + } + VECTOR(already_counted)[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, dist + 1)); + } + } + } + + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + RNG_END(); + igraph_vector_int_destroy(&tie_count); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&already_counted); + igraph_dqueue_int_destroy(&q); + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_voronoi_dijkstra( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_t *mindist, + const igraph_vector_int_t *generators, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_voronoi_tiebreaker_t tiebreaker) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_generators = igraph_vector_int_size(generators); + igraph_inclist_t il; + igraph_2wheap_t q; + + /* tie_count[vid] is the number of generators that vid is an equal distance from. + * We use this value to be able to randomly select one of the generators. */ + igraph_vector_int_t tie_count; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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 (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + IGRAPH_CHECK(igraph_2wheap_init(&q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &q); + + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&tie_count, no_of_nodes); + RNG_BEGIN(); + } + + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_fill(membership, -1); + + IGRAPH_CHECK(igraph_vector_resize(mindist, no_of_nodes)); + igraph_vector_fill(mindist, IGRAPH_INFINITY); + + /* Loop through all generator points and compute shortest paths to all other vertices. + * As we go, we keep track of the shortest distance from any generator to each vertex + * in 'mindist'. If we find that the distance from the current generator to a vertex + * is shorter than what was recorded so far in 'mindist', we update 'mindist' and + * assign that vertex to the current generator. + */ + for (igraph_integer_t i=0; i < no_of_generators; i++) { + igraph_integer_t g = VECTOR(*generators)[i]; + + /* Weighted shortest path implementation using Dijkstra's algorithm */ + + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_2wheap_clear(&q); + + /* We store negative distances in the maximum heap. Since some systems + * distinguish between -0.0 and +0.0, we need -0.0 to ensure +0.0 as + * the final result. */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&q, g, -0.0)); + + while (!igraph_2wheap_empty(&q)) { + igraph_integer_t vid = igraph_2wheap_max_index(&q); + igraph_real_t dist = -igraph_2wheap_deactivate_max(&q); + + igraph_real_t md = VECTOR(*mindist)[vid]; + + int cmp_result = igraph_cmp_epsilon(dist, md, IGRAPH_SHORTEST_PATH_EPSILON); + if (cmp_result > 0) { /* dist > md */ + /* This vertex is reachable at a shorter distance from + * another generator. Thus all its descendants in the shortest + * path tree are also reachable at a shorter distance from the + * other generator than from the current one. Therefore + * we do not need to search further from here. */ + continue; + } else if (cmp_result < 0) { /* dist < md */ + /* This vertex is closest to the current generator so far. + * Assign it to the current partition. */ + VECTOR(*mindist)[vid] = dist; + VECTOR(*membership)[vid] = i; + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + VECTOR(tie_count)[vid] = 1; + } + } else { /* md == dist, we have a tie */ + switch (tiebreaker) { + case IGRAPH_VORONOI_FIRST: + /* Never replace existing generator assignment. */ + break; + case IGRAPH_VORONOI_LAST: + /* Always replace existing generator assignment. */ + VECTOR(*membership)[vid] = i; + break; + case IGRAPH_VORONOI_RANDOM: + /* We replace the membership assignment with probability 1/k upon + * encountering the kth same-distance generator. This ensures + * that one of these generators is selected uniformly at random. */ + VECTOR(tie_count)[vid]++; + if (RNG_UNIF01() < 1.0 / VECTOR(tie_count)[vid]) { + VECTOR(*membership)[vid] = i; + } + break; + } + } + + igraph_vector_int_t *inc_edges = igraph_inclist_get(&il, vid); + igraph_integer_t inc_count = igraph_vector_int_size(inc_edges); + for (igraph_integer_t j=0; j < inc_count; j++) { + igraph_integer_t edge = VECTOR(*inc_edges)[j]; + igraph_real_t weight = VECTOR(*weights)[edge]; + + /* Optimization: do not follow infinite-weight edges. */ + if (weight == IGRAPH_INFINITY) { + continue; + } + + igraph_integer_t to = IGRAPH_OTHER(graph, edge, vid); + igraph_real_t altdist = dist + weight; + + if (! igraph_2wheap_has_elem(&q, to)) { + /* This is the first non-infinite distance */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&q, to, -altdist)); + } else if (igraph_2wheap_has_active(&q, to)) { + igraph_real_t curdist = -igraph_2wheap_get(&q, to); + if (altdist < curdist) { + /* This is a shorter path */ + igraph_2wheap_modify(&q, to, -altdist); + } + } + } + } /* !igraph_2wheap_empty(&q) */ + } + + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + RNG_END(); + igraph_vector_int_destroy(&tie_count); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_2wheap_destroy(&q); + igraph_inclist_destroy(&il); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_voronoi + * \brief Voronoi partitioning of a graph. + * + * \experimental + * + * To obtain a Voronoi partitioning of a graph, we start with a set of generator + * vertices, which will define the partitions. Each vertex is assigned to the generator + * vertex from (or to) which it is closest. + * + * + * This function uses a BFS search for unweighted graphs and Dijkstra's algorithm + * for weights ones. + * + * \param graph The graph to partition. + * \param membership If not \c NULL, the Voronoi partition of each vertex + * will be stored here. membership[v] will be set to the index + * in \p generators of the generator vertex that \c v belongs to. For vertices + * that are not reachable from any generator, -1 is returned. + * \param distances If not \c NULL, the distance of each vertex to its respective + * generator will be stored here. For vertices which are not reachable from + * any generator, \c IGRAPH_INFINITY is returned. + * \param generators Vertex IDs of the generator vertices. + * \param weights The edge weights, interpreted as lengths in the shortest + * path calculation. All weights must be non-negative. + * \param mode In directed graphs, whether to compute distances \em from + * generator vertices to other vertices (\c IGRAPH_OUT), \em to generator + * vertices from other vertices (\c IGRAPH_IN), or ignore edge directions + * entirely (\c IGRAPH_ALL). + * \param tiebreaker Controls which generator vertex to assign a vertex to + * when it is at equal distance from/to multiple generator vertices. + * \clist + * \cli IGRAPH_VORONOI_FIRST assign the vertex to the first generator vertex. + * \cli IGRAPH_VORONOI_LAST assign the vertex to the last generator vertex. + * \cli IGRAPH_VORONOI_RANDOM assign the vertex to a random generator vertex. + * \endclist + * Note that \c IGRAPH_VORONOI_RANDOM does not guarantee that all partitions + * will be contiguous. For example, if 1 and 2 are chosen as generators for the + * graph 1-3, 2-3, 3-4, then 3 and 4 are at equal distance from + * both generators. If 3 is assigned to 2 but 4 is assigned to 1, then the + * partition {1, 4} will not induce a connected subgraph. + * \return Error code. + * + * Time complexity: In weighted graphs, O((log |S|) |E| (log |V|) + |V|), and in + * unweighted graphs O((log |S|) |E| + |V|), where |S| is the number of generator + * vertices, and |V| and |E| are the number of vertices and edges in the graph. + * + * \sa \ref igraph_distances(), \ref igraph_distances_dijkstra(). + */ +igraph_error_t igraph_voronoi( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_t *distances, + const igraph_vector_int_t *generators, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_voronoi_tiebreaker_t tiebreaker) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t *pmembership; + igraph_vector_int_t imembership; + igraph_vector_t *pdistances; + igraph_vector_t idistances; + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (tiebreaker != IGRAPH_VORONOI_FIRST && + tiebreaker != IGRAPH_VORONOI_LAST && + tiebreaker != IGRAPH_VORONOI_RANDOM) { + IGRAPH_ERROR("Invalid tiebreaker specification during Voronoi partitioning.", IGRAPH_EINVAL); + } + + if (! igraph_vector_int_isininterval(generators, 0, igraph_vcount(graph)-1)) { + IGRAPH_ERROR("Invalid vertex ID given as Voronoi generator.", IGRAPH_EINVVID); + } + + if (membership) { + pmembership = membership; + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&imembership, no_of_nodes); + pmembership = &imembership; + } + + if (distances) { + pdistances = distances; + } else { + IGRAPH_VECTOR_INIT_FINALLY(&idistances, no_of_nodes); + pdistances = &idistances; + } + + if (weights) { + IGRAPH_CHECK(igraph_i_voronoi_dijkstra(graph, pmembership, pdistances, generators, weights, mode, tiebreaker)); + } else { + IGRAPH_CHECK(igraph_i_voronoi(graph, pmembership, pdistances, generators, mode, tiebreaker)); + } + + if (! distances) { + igraph_vector_destroy(&idistances); + IGRAPH_FINALLY_CLEAN(1); + } + + if (! membership) { + igraph_vector_int_destroy(&imembership); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/paths/widest_paths.c b/src/paths/widest_paths.c new file mode 100644 index 0000000..faf6d5d --- /dev/null +++ b/src/paths/widest_paths.c @@ -0,0 +1,731 @@ +/* -*- 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_interface.h" +#include "igraph_memory.h" + +#include "core/indheap.h" +#include "core/interruption.h" +#include "internal/utils.h" + +/** + * \function igraph_get_widest_paths + * \brief Widest paths from a single vertex. + * + * Calculates the widest paths from a single node to all other specified nodes, + * using a modified Dijkstra's algorithm. If there is more than one path with + * the largest width 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * Supply a null pointer here if you don't need these vectors. + * \param from The id of the vertex from/to which the widest paths are + * calculated. + * \param to Vertex sequence with the IDs of the vertices to/from which the + * widest paths will be calculated. A vertex might be given multiple + * times. + * \param weights The edge weights. Edge weights can be negative. If this + * is a null pointer or if any edge weight is NaN, then an error + * is returned. Edges with positive infinite weight are ignored. + * \param mode The type of widest 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 parents A pointer to an initialized igraph vector or null. + * If not null, a vector containing the parent of each vertex in + * the single source widest path tree is returned here. The + * parent of vertex i in the tree is the vertex from which vertex i + * was reached. The parent of the start vertex (in the \c from + * argument) is -1. If the parent is -2, 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 widest 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 + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|E|log|E|+|V|), where |V| is the number of + * vertices in the graph and |E| is the number of edges + * + * \sa \ref igraph_widest_path_widths_dijkstra() or + * \ref igraph_widest_path_widths_floyd_warshall() if you only need the + * widths of the paths but not the paths themselves. + */ +igraph_error_t igraph_get_widest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges) { + + /* Implementation details: This is a Dijkstra algorithm with a + binary heap, modified to support widest paths. The heap is indexed, + so it stores both the widest path to a node, as well as it's index. We + use a 2 way heap so that we can query indexes directly in the heap. + + To adapt a Dijkstra to handle widest path, instead of prioritising candidate + nodes with the minimum distance, we prioritise those with the maximum + width instead. When adding a node into our set of 'completed' nodes, we + update all neighbouring nodes with a width that is equal to the min of the + width to the current node and the width of the edge. + + We denote the widest path from a node to itself as infinity, and the widest + path from a node to a node it cannot reach as negative infinity. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vit_t vit; + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_vector_t widths; + igraph_integer_t *parent_eids; + bool *is_target; + igraph_integer_t i, to_reach; + + if (!weights) { + IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (igraph_vector_is_any_nan(weights)) { + 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_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(vit))); + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edges, 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(&widths, no_of_nodes); + igraph_vector_fill(&widths, IGRAPH_NEGINFINITY); + + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for widest paths."); + IGRAPH_FINALLY(igraph_free, parent_eids); + + is_target = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(is_target, "Insufficient memory for widest paths."); + 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[ IGRAPH_VIT_GET(vit) ]) { + is_target[ IGRAPH_VIT_GET(vit) ] = true; + } else { + to_reach--; /* this node was given multiple times */ + } + } + + VECTOR(widths)[from] = IGRAPH_POSINFINITY; + parent_eids[from] = 0; + igraph_2wheap_push_with_index(&Q, from, IGRAPH_POSINFINITY); + + while (!igraph_2wheap_empty(&Q) && to_reach > 0) { + igraph_integer_t nlen, maxnei = igraph_2wheap_max_index(&Q); + igraph_real_t maxwidth = igraph_2wheap_delete_max(&Q); + igraph_vector_int_t *neis; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (is_target[maxnei]) { + is_target[maxnei] = false; + to_reach--; + } + + /* Now check all neighbors of 'maxnei' for a wider path */ + neis = igraph_lazy_inclist_get(&inclist, maxnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (i = 0; i < nlen; i++) { + igraph_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t tto = IGRAPH_OTHER(graph, edge, maxnei); + igraph_real_t edgewidth = VECTOR(*weights)[edge]; + igraph_real_t altwidth = maxwidth < edgewidth ? maxwidth : edgewidth; + igraph_real_t curwidth = VECTOR(widths)[tto]; + if (edgewidth == IGRAPH_INFINITY) { + /* Ignore edges with infinite weight */ + } else if (curwidth < 0) { + /* This is the first assigning a width to this vertex */ + VECTOR(widths)[tto] = altwidth; + parent_eids[tto] = edge + 1; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, altwidth)); + } else if (altwidth > curwidth) { + /* This is a wider path */ + VECTOR(widths)[tto] = altwidth; + parent_eids[tto] = edge + 1; + igraph_2wheap_modify(&Q, tto, altwidth); + } + } + } /* !igraph_2wheap_empty(&Q) */ + + + if (to_reach > 0) { + IGRAPH_WARNING("Couldn't reach some vertices."); + } + + /* Create `parents' if needed */ + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (i == from) { + /* i is the start vertex */ + VECTOR(*parents)[i] = -1; + } else if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*parents)[i] = -2; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); + } + } + } + + /* Create `inbound_edges' if needed */ + if (inbound_edges) { + IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*inbound_edges)[i] = -1; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*inbound_edges)[i] = parent_eids[i] - 1; + } + } + } + /* Reconstruct the widest 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++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t size, act, edge; + igraph_vector_int_t *vvec = 0, *evec = 0; + + if (vertices) { + vvec = igraph_vector_int_list_get_ptr(vertices, i); + igraph_vector_int_clear(vvec); + } + if (edges) { + evec = igraph_vector_int_list_get_ptr(edges, i); + igraph_vector_int_clear(evec); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + size = 0; + act = node; + while (parent_eids[act]) { + size++; + edge = parent_eids[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vvec && (size > 0 || node == from)) { + IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); + VECTOR(*vvec)[size] = node; + } + if (evec) { + IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); + } + act = node; + while (parent_eids[act]) { + edge = parent_eids[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(&widths); + IGRAPH_FREE(is_target); + IGRAPH_FREE(parent_eids); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_widest_path + * \brief Widest path from one vertex to another one. + * + * Calculates a single widest 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_widest_paths(). + * + * \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 initialized 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. Edge weights can be negative. If this + * is a null pointer or if any edge weight is NaN, then an error + * is returned. Edges with positive infinite weight are ignored. + * \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_widest_paths() for the version with + * more target vertices. + */ +igraph_error_t igraph_get_widest_path(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + igraph_vector_int_list_t vertices2, *vp = &vertices2; + igraph_vector_int_list_t edges2, *ep = &edges2; + + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); + } else { + vp = NULL; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); + } else { + ep = NULL; + } + + IGRAPH_CHECK(igraph_get_widest_paths(graph, vp, ep, + from, igraph_vss_1(to), + weights, mode, 0, 0)); + + if (edges) { + IGRAPH_CHECK(igraph_vector_int_update(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); + igraph_vector_int_list_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_update(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); + igraph_vector_int_list_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_widest_path_widths_floyd_warshall + * \brief Widths of widest paths between vertices. + * + * This function implements a modified Floyd-Warshall algorithm, + * to find the widest path widths between a set of source and target + * vertices. It is primarily useful for all-pairs path widths in very dense + * graphs, as its running time is manily determined by the vertex count, + * and is not sensitive to the graph density. In sparse graphs, other methods + * such as the Dijkstra algorithm, will perform better. + * + * + * Note that internally this function always computes the path width matrix + * for all pairs of vertices. The \p from and \p to parameters only serve + * to subset this matrix, but do not affect the time taken by the + * calculation. + * + * \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 widths from a single source, to the + * vertices given in the \c to argument. + * Unreachable vertices have width \c IGRAPH_NEGINFINITY, and vertices + * have a width of \c IGRAPH_POSINFINITY to themselves. + * \param from The source vertices. + * \param to The target vertices. + * \param weights The edge weights. Edge weights can be negative. If this + * is a null pointer or if any edge weight is NaN, then an error + * is returned. Edges with positive infinite weight are ignored. + * \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(|V|^3), where |V| is the number of vertices in the graph. + * + * \sa \ref igraph_widest_path_widths_dijkstra() for a variant that runs faster + * on sparse graphs. + */ +igraph_error_t igraph_widest_path_widths_floyd_warshall(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 a modified Floyd Warshall algorithm + which computes the widest path between every pair of nodes. The key + difference between this and the regular Floyd Warshall is that instead + of updating the distance between two nodes to be the minimum of itself + and the distance through an intermediate node, we instead set the width + to be the maximum of itself and the width through the intermediate node. + + We denote the widest path from a node to itself as infinity, and the widest + path from a node to a node it cannot reach as negative infinity. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t in = false, out = false; + + if (! weights) { + IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (igraph_vector_is_any_nan(weights)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + switch (mode) { + case IGRAPH_ALL: + in = out = true; + break; + case IGRAPH_OUT: + out = true; + break; + case IGRAPH_IN: + in = true; + break; + default: + IGRAPH_ERROR("Invalid mode for Floyd-Warshall shortest path calculation.", IGRAPH_EINVMODE); + } + + /* Fill out adjacency matrix */ + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_fill(res, IGRAPH_NEGINFINITY); + for (igraph_integer_t i=0; i < no_of_nodes; i++) { + MATRIX(*res, i, i) = IGRAPH_POSINFINITY; + } + + for (igraph_integer_t edge=0; edge < no_of_edges; edge++) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + igraph_real_t w = VECTOR(*weights)[edge]; + + if (w == IGRAPH_INFINITY) { + /* Ignore edges with infinite weight */ + continue; + } + + if (out && MATRIX(*res, from, to) < w) MATRIX(*res, from, to) = w; + if (in && MATRIX(*res, to, from) < w) MATRIX(*res, to, from) = w; + } + + /* Run modified Floyd Warshall */ + for (igraph_integer_t k = 0; k < no_of_nodes; k++) { + /* Iterate in column-major order for better performance */ + for (igraph_integer_t j = 0; j < no_of_nodes; j++) { + igraph_real_t width_kj = MATRIX(*res, k, j); + if (j == k || width_kj == IGRAPH_NEGINFINITY) continue; + + IGRAPH_ALLOW_INTERRUPTION(); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (i == j || i == k) continue; + + /* alternative_width := min(A(i,k), A(k,j)) + A(i,j) := max(A(i,j), alternative_width) */ + + igraph_real_t altwidth = MATRIX(*res, i, k); + if (width_kj < altwidth) { + altwidth = width_kj; + } + if (altwidth > MATRIX(*res, i, j)) { + MATRIX(*res, i, j) = altwidth; + } + } + } + } + + IGRAPH_CHECK(igraph_i_matrix_subset_vertices(res, graph, from, to)); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_widest_path_widths_dijkstra + * \brief Widths of widest paths between vertices. + * + * This function implements a modified Dijkstra's algorithm, which + * can find the widest path widths from a source vertex to all + * other vertices. This function allows specifying a set of source + * and target vertices. The algorithm is run independently for each + * source and the results are retained only for the specified targets. + * This implementation uses a binary heap for efficiency. + * + * \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 widths from a single source, to the + * vertices given in the \c to argument. + * Unreachable vertices have width \c IGRAPH_NEGINFINITY, and vertices + * have a width of \c IGRAPH_POSINFINITY to themselves. + * \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. Edge weights can be negative. If this + * is a null pointer or if any edge weight is NaN, then an error + * is returned. Edges with positive infinite weight are ignored. + * \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 in the graph, |E| the number of edges and s the number of sources. + * + * \sa \ref igraph_widest_path_widths_floyd_warshall() for a variant that runs faster + * on dense graphs. + */ +igraph_error_t igraph_widest_path_widths_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 a Dijkstra algorithm with a + binary heap, modified to support widest paths. The heap is indexed, + so it stores both the widest path to a node, as well as it's index. We + use a 2 way heap so that we can query indexes directly in the heap. + + To adapt a Dijkstra to handle widest path, instead of prioritising candidate + nodes with the minimum distance, we prioritise those with the maximum + width instead. When adding a node into our set of 'completed' nodes, we + update all neighbouring nodes with a width that is equal to the min of the + width to the current node and the width of the edge. + + We denote the widest path from a node to itself as infinity, and the widest + path from a node to a node it cannot reach as negative infinity. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_vit_t fromvit, tovit; + igraph_integer_t no_of_from, no_of_to; + igraph_lazy_inclist_t inclist; + igraph_integer_t i, j; + igraph_bool_t all_to; + igraph_vector_int_t indexv; + + if (!weights) { + IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (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_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_INT_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)) { + igraph_integer_t 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, IGRAPH_NEGINFINITY); + + for (IGRAPH_VIT_RESET(fromvit), i = 0; + !IGRAPH_VIT_END(fromvit); + IGRAPH_VIT_NEXT(fromvit), i++) { + + igraph_integer_t reached = 0; + igraph_integer_t source = IGRAPH_VIT_GET(fromvit); + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, source, IGRAPH_POSINFINITY); + + while (!igraph_2wheap_empty(&Q)) { + igraph_integer_t maxnei = igraph_2wheap_max_index(&Q); + igraph_real_t maxwidth = igraph_2wheap_deactivate_max(&Q); + igraph_vector_int_t *neis; + igraph_integer_t nlen; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (all_to) { + MATRIX(*res, i, maxnei) = maxwidth; + } else { + if (VECTOR(indexv)[maxnei]) { + MATRIX(*res, i, VECTOR(indexv)[maxnei] - 1) = maxwidth; + reached++; + if (reached == no_of_to) { + igraph_2wheap_clear(&Q); + break; + } + } + } + + /* Now check all neighbors of 'maxnei' for a wider path*/ + neis = igraph_lazy_inclist_get(&inclist, maxnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t tto = IGRAPH_OTHER(graph, edge, maxnei); + igraph_real_t edgewidth = VECTOR(*weights)[edge]; + igraph_real_t altwidth = maxwidth < edgewidth ? maxwidth : edgewidth; + igraph_bool_t active = igraph_2wheap_has_active(&Q, tto); + igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto); + igraph_real_t curwidth = active ? igraph_2wheap_get(&Q, tto) : IGRAPH_POSINFINITY; + if (edgewidth == IGRAPH_INFINITY) { + /* Ignore edges with infinite weight */ + } else if (!has) { + /* This is the first time assigning a width to this vertex */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, altwidth)); + } else if (altwidth > curwidth) { + /* This is a wider path */ + igraph_2wheap_modify(&Q, tto, altwidth); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + } /* !IGRAPH_VIT_END(fromvit) */ + + if (!all_to) { + igraph_vit_destroy(&tovit); + igraph_vector_int_destroy(&indexv); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + igraph_vit_destroy(&fromvit); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/basic_properties.c b/src/properties/basic_properties.c new file mode 100644 index 0000000..7b69cba --- /dev/null +++ b/src/properties/basic_properties.c @@ -0,0 +1,381 @@ +/* -*- 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 connection 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. This function does not check whether the graph has + * parallel edges. The result it returns for such graphs is not meaningful. + * + * \param graph The input graph object. It must not have parallel edges. + * \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 \c 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 \c false then the result is only correct if the graph does not + * contain loops. + * \return Error code. + * + * Time complexity: O(1). + */ +igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t loops) { + + igraph_real_t no_of_nodes = (igraph_real_t) igraph_vcount(graph); + igraph_real_t no_of_edges = (igraph_real_t) igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + + if (no_of_nodes == 0) { + *res = IGRAPH_NAN; + return IGRAPH_SUCCESS; + } + + 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_mean_degree + * \brief The mean degree of a graph. + * + * \experimental + * + * This is a convenience function that computes the average of all vertex + * degrees. In directed graphs, the average of out-degrees and in-degrees is + * the same; this is the number that is returned. For the null graph, which + * has no vertices, NaN is returned. + * + * \param graph The input graph object. + * \param res Pointer to a real number, the result will be stored here. + * \param loops Whether to consider self-loops during the calculation. + * \return Error code. + * + * Time complexity: O(1) if self-loops are considered, + * O(|E|) where |E| is the number of edges if self-loops are ignored. + */ +igraph_error_t igraph_mean_degree(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t loops) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + + if (no_of_nodes == 0) { + *res = IGRAPH_NAN; + return IGRAPH_SUCCESS; + } + + if (! loops) { + igraph_integer_t loop_count; + IGRAPH_CHECK(igraph_count_loops(graph, &loop_count)); + no_of_edges -= loop_count; + } + + *res = (directed ? 1.0 : 2.0) * (igraph_real_t) no_of_edges / (igraph_real_t) no_of_nodes; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_diversity + * \brief 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. + * + */ +igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_t *res, const igraph_vs_t vids) { + + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t k, i; + igraph_vector_int_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 (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_VECTOR_INT_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; + igraph_integer_t v = IGRAPH_VIT_GET(vit); + + IGRAPH_CHECK(igraph_incident(graph, &incident, v, /*mode=*/ IGRAPH_ALL)); + k = igraph_vector_int_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)[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_int_destroy(&incident); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_reciprocity + * \brief Calculates the reciprocity of a directed graph. + * + * In a directed graph, the measure of reciprocity defines the proportion of + * mutual connections. It is most commonly defined as the probability that the + * opposite counterpart of a randomly chosen directed edge is also included in + * the graph. In adjacency matrix notation: + * 1 - (sum_ij |A_ij - A_ji|) / (2 sum_ij A_ij). + * In multigraphs, each parallel edge between two vertices must have its own + * separate reciprocal edge, in accordance with the above formula. This measure + * is calculated if the \p mode argument is \c IGRAPH_RECIPROCITY_DEFAULT. + * + * + * For directed graphs with no edges, NaN is returned. + * For undirected graphs, 1 is returned unconditionally. + * + * + * Prior to igraph version 0.6, another measure was implemented, defined as the + * probability of having mutual connections 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 self-loops when counting edges. + * Self-loops are considered as a mutual connection. + * \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 + */ +igraph_error_t 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_int_t inneis, outneis; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (mode != IGRAPH_RECIPROCITY_DEFAULT && + mode != IGRAPH_RECIPROCITY_RATIO) { + IGRAPH_ERROR("Invalid reciprocity type.", IGRAPH_EINVAL); + } + + /* Undirected graphs have reciprocity 1.0 by definition. */ + if (!igraph_is_directed(graph)) { + *res = 1.0; + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outneis, 0); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t ip, op, indeg, outdeg; + IGRAPH_CHECK(igraph_neighbors(graph, &inneis, i, IGRAPH_IN)); + IGRAPH_CHECK(igraph_neighbors(graph, &outneis, i, IGRAPH_OUT)); + + indeg = igraph_vector_int_size(&inneis); + outdeg = igraph_vector_int_size(&outneis); + + ip = op = 0; + while (ip < indeg && op < outdeg) { + 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 += (indeg - ip) + (outdeg - op); + } + + /* If we found non-loop mutual connections, we can set the cache. */ + if (rec - (ignore_loops ? 0 : loops) > 0) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MUTUAL, true); + } + + 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_int_destroy(&inneis); + igraph_vector_int_destroy(&outneis); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/complete.c b/src/properties/complete.c new file mode 100644 index 0000000..efad9e3 --- /dev/null +++ b/src/properties/complete.c @@ -0,0 +1,279 @@ +/* + IGraph library. + Copyright (C) 2024 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_interface.h" +#include "igraph_structural.h" + +#include "core/interruption.h" +#include "graph/internal.h" + +/** + * \ingroup structural + * \function igraph_is_complete + * \brief Decides whether the graph is complete. + * + * \experimental + * + * A graph is considered complete if all pairs of different vertices are + * adjacent. + * + * + * The null graph and the singleton graph are considered complete. + * + * \param graph The graph object to analyze. + * \param res Pointer to a logical variable, the result will be stored here. + * + * \return Error code. + * + * Time complexity: O(|V| + |E|) at worst. + */ + +igraph_error_t igraph_is_complete(const igraph_t *graph, igraph_bool_t *res) { + + const igraph_integer_t vcount = igraph_vcount(graph); + const igraph_integer_t ecount = igraph_ecount(graph); + igraph_integer_t complete_ecount; + igraph_bool_t simple, directed = igraph_is_directed(graph); + igraph_vector_int_t neighbours; + int iter = 0; + + /* If the graph is the null graph or the singleton graph, return early */ + if (vcount == 0 || vcount == 1) { + *res = true; + return IGRAPH_SUCCESS; + } + + /* Compute the amount of edges a complete graph of vcount vertices would + have */ + + /* Depends on whether the graph is directed */ + + /* We have to take care of integer overflowing */ + +#if IGRAPH_INTEGER_SIZE == 32 + if (directed) { + /* Highest x s.t. x² - x < 2^31 - 1 */ + if (vcount > 46341) { + *res = false; + return IGRAPH_SUCCESS; + } else { + complete_ecount = vcount * (vcount - 1); + } + } else { + /* Highest x s.t. (x² - x) / 2 < 2^31 - 1 */ + if (vcount > 65536) { + *res = false; + return IGRAPH_SUCCESS; + } else { + complete_ecount = vcount % 2 == 0 ? + (vcount / 2) * (vcount - 1) : + vcount * ((vcount - 1) / 2); + } + } +#elif IGRAPH_INTEGER_SIZE == 64 + if (directed) { + /* Highest x s.t. x² - x < 2^63 - 1 */ + if (vcount > 3037000500) { + *res = false; + return IGRAPH_SUCCESS; + } else { + complete_ecount = vcount * (vcount - 1); + } + } else { + /* Highest x s.t. (x² - x) / 2 < 2^63 - 1 */ + if (vcount > 4294967296) { + *res = false; + return IGRAPH_SUCCESS; + } else { + complete_ecount = vcount % 2 == 0 ? + (vcount / 2) * (vcount - 1) : + vcount * ((vcount - 1) / 2); + } + } +#else + /* If values other than 32 or 64 become allowed, + * this code will need to be updated. */ +# error "Unexpected IGRAPH_INTEGER_SIZE value." +#endif + + /* If the amount of edges is strictly lower than what it should be for a + complete graph, return early */ + + if (ecount < complete_ecount) { + *res = false; + return IGRAPH_SUCCESS; + } + + /* If the graph is simple, compare and conclude */ + IGRAPH_CHECK(igraph_is_simple(graph, &simple)); + + if (simple) { + *res = (ecount == complete_ecount); + return IGRAPH_SUCCESS; + } + + /* Allocate memory for vector of size v */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbours, vcount); + + for (igraph_integer_t i = 0; i < vcount; ++i) { + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); + + IGRAPH_CHECK(igraph_i_neighbors(graph, &neighbours, i, IGRAPH_OUT, + IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + + if ((igraph_vector_int_size(&neighbours) < vcount - 1)) { + *res = false; + goto cleanup; + } + } + + /* If we arrive here, we have found no neighbour vector of size strictly + less than vcount - 1. The graph is therefore complete */ + + *res = true; + +cleanup: + + igraph_vector_int_destroy(&neighbours); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/* Test for cliques or independent sets, depending on whether independent_set == true. */ +static igraph_error_t is_clique(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t directed, igraph_bool_t *res, + igraph_bool_t independent_set) { + igraph_vector_int_t vids; + igraph_integer_t n; /* clique size */ + igraph_bool_t result = true; /* be optimistic */ + int iter = 0; + + /* The following implementation is optimized for testing for small cliques + * in large graphs. */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_CHECK(igraph_vs_as_vector(graph, candidate, &vids)); + + n = igraph_vector_int_size(&vids); + + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t u = VECTOR(vids)[i]; + for (igraph_integer_t j = directed ? 0 : i+1; j < n; j++) { + igraph_integer_t v = VECTOR(vids)[j]; + /* Compare u and v for equality instead of i and j in case + * the vertex list contained duplicates. */ + if (u != v) { + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, u, v, directed, false)); + if (independent_set) { + if (eid != -1) { + result = false; + goto done; + } + } else { + if (eid == -1) { + result = false; + goto done; + } + } + } + } + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); + } + +done: + + *res = result; + + igraph_vector_int_destroy(&vids); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_is_clique + * \brief Does a set of vertices form a clique? + * + * \experimental + * + * Tests if all pairs within a set of vertices are adjacent, i.e. whether they + * form a clique. An empty set and singleton set are considered to be a clique. + * + * \param graph The input graph. + * \param candidate The vertex set to test for being a clique. + * \param directed Whether to take edge directions into account in directed graphs. + * \param res The result will be stored here. + * \return Error code. + * + * \sa \ref igraph_is_complete(), \ref igraph_is_independent_vertex_set() + * + * Time complexity: O(n^2 log(d)) where n is the number of vertices in the + * candidate set and d is the typical vertex degree. + */ +igraph_error_t igraph_is_clique(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t directed, igraph_bool_t *res) { + + if (! igraph_is_directed(graph)) { + directed = false; + } + + if (igraph_is_directed(graph) == directed && igraph_vs_is_all(&candidate)) { + return igraph_is_complete(graph, res); + } + + return is_clique(graph, candidate, directed, res, /* independent_set */ false); +} + +/** + * \ingroup structural + * \function igraph_is_independent_vertex_set + * \brief Does a set of vertices form an independent set? + * + * \experimental + * + * Tests if no pairs within a set of vertices are adjacenct, i.e. whether they + * form a an independent set. An empty set and singleton set are both considered + * to be an independent set. + * + * \param graph The input graph. + * \param candidate The vertex set to test for being an independent set. + * \param res The result will be stored here. + * \return Error code. + * + * \sa \ref igraph_is_clique() + * + * Time complexity: O(n^2 log(d)) where n is the number of vertices in the + * candidate set and d is the typical vertex degree. + */ +igraph_error_t igraph_is_independent_vertex_set(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t *res) { + + /* Note: igraph_count_loops() already makes use of the cache. */ + if (igraph_vs_is_all(&candidate)) { + igraph_integer_t loop_count; + igraph_count_loops(graph, &loop_count); + *res = (igraph_ecount(graph) - loop_count) == 0; + return IGRAPH_SUCCESS; + } + + return is_clique(graph, candidate, /* directed */ false, res, /* independent_set */ true); +} diff --git a/src/properties/constraint.c b/src/properties/constraint.c new file mode 100644 index 0000000..878eb25 --- /dev/null +++ b/src/properties/constraint.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_centrality.h" + +#include "igraph_interface.h" +#include "igraph_structural.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). + */ +igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, + igraph_vs_t vids, const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vit_t vit; + igraph_integer_t nodes_to_calc; + igraph_integer_t a, b, c, i, j, q, vsize, vsize2; + igraph_integer_t edge, edge2; + + igraph_vector_t contrib; + igraph_vector_t degree; + igraph_vector_int_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_INT_INIT_FINALLY(&ineis_in, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ineis_out, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&jneis_in, 0); + IGRAPH_VECTOR_INT_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); + + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS, weights)); + + 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, i, + IGRAPH_IN)); + IGRAPH_CHECK(igraph_incident(graph, &ineis_out, i, + IGRAPH_OUT)); + + /* NaN for isolates */ + if (igraph_vector_int_size(&ineis_in) == 0 && + igraph_vector_int_size(&ineis_out) == 0) { + VECTOR(*res)[a] = IGRAPH_NAN; + } + + /* zero their contribution */ + vsize = igraph_vector_int_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = VECTOR(ineis_in)[b]; + j = IGRAPH_OTHER(graph, edge, i); + VECTOR(contrib)[j] = 0.0; + } + vsize = igraph_vector_int_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = VECTOR(ineis_out)[b]; + j = IGRAPH_OTHER(graph, edge, i); + VECTOR(contrib)[j] = 0.0; + } + + /* add the direct contributions, in-neighbors and out-neighbors */ + vsize = igraph_vector_int_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = VECTOR(ineis_in)[b]; + j = IGRAPH_OTHER(graph, edge, i); + if (i != j) { /* excluding loops */ + if (weights) { + VECTOR(contrib)[j] += + VECTOR(*weights)[edge] / VECTOR(degree)[i]; + } else { + VECTOR(contrib)[j] += 1.0 / VECTOR(degree)[i]; + } + } + } + if (igraph_is_directed(graph)) { + vsize = igraph_vector_int_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = VECTOR(ineis_out)[b]; + j = IGRAPH_OTHER(graph, edge, i); + if (i != j) { + if (weights) { + VECTOR(contrib)[j] += + VECTOR(*weights)[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_int_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = VECTOR(ineis_in)[b]; + j = IGRAPH_OTHER(graph, edge, i); + if (i == j) { + continue; + } + IGRAPH_CHECK(igraph_incident(graph, &jneis_in, j, + IGRAPH_IN)); + IGRAPH_CHECK(igraph_incident(graph, &jneis_out, j, + IGRAPH_OUT)); + vsize2 = igraph_vector_int_size(&jneis_in); + for (c = 0; c < vsize2; c++) { + edge2 = VECTOR(jneis_in)[c]; + q = IGRAPH_OTHER(graph, edge2, j); + if (j != q) { + if (weights) { + VECTOR(contrib)[q] += + VECTOR(*weights)[edge] * + VECTOR(*weights)[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_int_size(&jneis_out); + for (c = 0; c < vsize2; c++) { + edge2 = VECTOR(jneis_out)[c]; + q = IGRAPH_OTHER(graph, edge2, j); + if (j != q) { + if (weights) { + VECTOR(contrib)[q] += + VECTOR(*weights)[edge] * + VECTOR(*weights)[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_int_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = VECTOR(ineis_out)[b]; + j = IGRAPH_OTHER(graph, edge, i); + if (i == j) { + continue; + } + IGRAPH_CHECK(igraph_incident(graph, &jneis_in, j, + IGRAPH_IN)); + IGRAPH_CHECK(igraph_incident(graph, &jneis_out, j, + IGRAPH_OUT)); + vsize2 = igraph_vector_int_size(&jneis_in); + for (c = 0; c < vsize2; c++) { + edge2 = VECTOR(jneis_in)[c]; + q = IGRAPH_OTHER(graph, edge2, j); + if (j != q) { + if (weights) { + VECTOR(contrib)[q] += + VECTOR(*weights)[edge] * + VECTOR(*weights)[edge2] / + VECTOR(degree)[i] / VECTOR(degree)[j]; + } else { + VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; + } + } + } + vsize2 = igraph_vector_int_size(&jneis_out); + for (c = 0; c < vsize2; c++) { + edge2 = VECTOR(jneis_out)[c]; + q = IGRAPH_OTHER(graph, edge2, j); + if (j != q) { + if (weights) { + VECTOR(contrib)[q] += + VECTOR(*weights)[edge] * + VECTOR(*weights)[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_int_size(&ineis_in); + for (b = 0; b < vsize; b++) { + edge = VECTOR(ineis_in)[b]; + j = 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_int_size(&ineis_out); + for (b = 0; b < vsize; b++) { + edge = VECTOR(ineis_out)[b]; + j = 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_int_destroy(&jneis_out); + igraph_vector_int_destroy(&jneis_in); + igraph_vector_int_destroy(&ineis_out); + igraph_vector_int_destroy(&ineis_in); + igraph_vector_destroy(°ree); + igraph_vector_destroy(&contrib); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/convergence_degree.c b/src/properties/convergence_degree.c new file mode 100644 index 0000000..661818f --- /dev/null +++ b/src/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. + */ +igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, + igraph_vector_t *ins, igraph_vector_t *outs) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, k, n; + igraph_integer_t *geodist; + igraph_vector_int_t *eids; + igraph_vector_t *ins_p, *outs_p, ins_v, outs_v; + igraph_dqueue_int_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_int_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_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, igraph_integer_t); + if (geodist == 0) { + IGRAPH_ERROR("Cannot calculate convergence degrees", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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_int_clear(&q); + memset(geodist, 0, sizeof(geodist[0]) * (size_t) no_of_nodes); + geodist[i] = 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + IGRAPH_ALLOW_INTERRUPTION(); + eids = igraph_inclist_get(&inclist, actnode); + n = igraph_vector_int_size(eids); + for (j = 0; j < n; j++) { + igraph_integer_t 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)[VECTOR(*eids)[j]] += 1; + } else { + VECTOR(*outs_p)[VECTOR(*eids)[j]] += 1; + } + } else { + vec[VECTOR(*eids)[j]] += 1; + } + } else if (geodist[neighbor] - 1 < actdist + 1) { + continue; + } + } else { + /* we haven't seen this node yet */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_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)[VECTOR(*eids)[j]] += 1; + } else { + VECTOR(*outs_p)[VECTOR(*eids)[j]] += 1; + } + } else { + vec[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_int_destroy(&q); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/dag.c b/src/properties/dag.c new file mode 100644 index 0000000..afad5ef --- /dev/null +++ b/src/properties/dag.c @@ -0,0 +1,315 @@ +/* -*- 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 + */ +igraph_error_t igraph_topological_sorting( + const igraph_t* graph, igraph_vector_int_t *res, igraph_neimode_t mode) { + + /* Note: This function ignores self-loops, there it cannot + * use the IGRAPH_PROP_IS_DAG property cache entry. */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degrees; + igraph_vector_int_t neis; + igraph_dqueue_int_t sources; + igraph_neimode_t deg_mode; + igraph_integer_t 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 for topological sorting.", IGRAPH_EINVMODE); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&sources, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &sources); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), deg_mode, 0)); + + igraph_vector_int_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_int_push(&sources, i)); + } + } + + /* Take all nodes with no incoming vertices and remove them */ + while (!igraph_dqueue_int_empty(&sources)) { + node = igraph_dqueue_int_pop(&sources); + /* Add the node to the result vector */ + IGRAPH_CHECK(igraph_vector_int_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, node, mode)); + j = igraph_vector_int_size(&neis); + for (i = 0; i < j; i++) { + VECTOR(degrees)[ VECTOR(neis)[i] ]--; + if (VECTOR(degrees)[ VECTOR(neis)[i] ] == 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&sources, VECTOR(neis)[i])); + } + } + } + + igraph_vector_int_destroy(°rees); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&sources); + IGRAPH_FINALLY_CLEAN(3); + + if (igraph_vector_int_size(res) < no_of_nodes) { + IGRAPH_ERROR("The graph has cycles; " + "topological sorting is only possible in acyclic graphs.", + IGRAPH_EINVAL); + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * + * + * This function returns false for undirected graphs. + * + * + * The return value of this function is cached in the graph itself; calling + * the function multiple times with no modifications to the graph in between + * will return a cached value in O(1) time. + * + * \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. + */ +igraph_error_t igraph_is_dag(const igraph_t* graph, igraph_bool_t *res) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degrees; + igraph_vector_int_t neis; + igraph_dqueue_int_t sources; + + if (!igraph_is_directed(graph)) { + *res = false; + return IGRAPH_SUCCESS; + } + + IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_IS_DAG, res); + + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&sources, 0); + + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_IN, /* loops */ true)); + + igraph_integer_t vertices_left = no_of_nodes; + + /* Do we have nodes with no incoming edges? */ + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (VECTOR(degrees)[i] == 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&sources, i)); + } + } + + /* Take all nodes with no incoming edges and remove them */ + while (!igraph_dqueue_int_empty(&sources)) { + igraph_integer_t node = igraph_dqueue_int_pop(&sources); + /* 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, node, IGRAPH_OUT)); + igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t nei = VECTOR(neis)[i]; + if (nei == node) { + /* Found a self-loop, graph is not a DAG */ + *res = false; + goto finalize; + } + VECTOR(degrees)[nei]--; + if (VECTOR(degrees)[nei] == 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&sources, nei)); + } + } + } + + IGRAPH_ASSERT(vertices_left >= 0); + *res = (vertices_left == 0); + +finalize: + igraph_vector_int_destroy(°rees); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&sources); + IGRAPH_FINALLY_CLEAN(3); + + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_DAG, *res); + + 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. + */ +igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *closure) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t deg; + igraph_vector_int_t new_edges; + igraph_vector_int_t ancestors; + igraph_integer_t root; + igraph_vector_int_t neighbors; + igraph_stack_int_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_INT_INIT_FINALLY(&new_edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(°, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ancestors, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 0); + IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_int_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_int_push(&path, root)); + + while (!igraph_stack_int_empty(&path)) { + igraph_integer_t node = igraph_stack_int_top(&path); + if (node == STAR) { + /* Leaving a node */ + igraph_integer_t j, n; + igraph_stack_int_pop(&path); + node = igraph_stack_int_pop(&path); + if (!VECTOR(done)[node]) { + igraph_vector_int_pop_back(&ancestors); + VECTOR(done)[node] = true; + } + n = igraph_vector_int_size(&ancestors); + for (j = 0; j < n; j++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, node)); + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, + VECTOR(ancestors)[j])); + } + } else { + /* Getting into a node */ + igraph_integer_t n, j; + if (!VECTOR(done)[node]) { + IGRAPH_CHECK(igraph_vector_int_push_back(&ancestors, node)); + } + IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, + node, IGRAPH_IN)); + n = igraph_vector_int_size(&neighbors); + IGRAPH_CHECK(igraph_stack_int_push(&path, STAR)); + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neighbors)[j]; + IGRAPH_CHECK(igraph_stack_int_push(&path, nei)); + } + } + } + } + +#undef STAR + + igraph_vector_bool_destroy(&done); + igraph_stack_int_destroy(&path); + igraph_vector_int_destroy(&neighbors); + igraph_vector_int_destroy(&ancestors); + igraph_vector_int_destroy(°); + IGRAPH_FINALLY_CLEAN(5); + + IGRAPH_CHECK(igraph_create(closure, &new_edges, no_of_nodes, + IGRAPH_DIRECTED)); + + igraph_vector_int_destroy(&new_edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/degrees.c b/src/properties/degrees.c new file mode 100644 index 0000000..5131fd8 --- /dev/null +++ b/src/properties/degrees.c @@ -0,0 +1,750 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2005-2023 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_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 \p loops is \c 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_degree() to retrieve the degrees for several vertices. + */ +igraph_error_t igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, + igraph_vs_t vids, igraph_neimode_t mode, + igraph_bool_t loops) { + + igraph_vector_int_t tmp; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + + IGRAPH_CHECK(igraph_degree(graph, &tmp, vids, mode, loops)); + if (igraph_vector_int_size(&tmp) == 0) { + *res = 0; + } else { + *res = igraph_vector_int_max(&tmp); + } + + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis, edge_neis; + igraph_integer_t no_vids; + igraph_vit_t vit; + igraph_vector_t my_knn_v, *my_knn = knn; + igraph_vector_t strength; + igraph_vector_int_t deg; + igraph_integer_t maxdeg; + igraph_vector_t deghist; + + 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_INT_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_INT_INIT_FINALLY(&neis, maxdeg); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_neis, maxdeg); + igraph_vector_int_clear(&neis); + igraph_vector_int_clear(&edge_neis); + + if (knnk) { + IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg)); + igraph_vector_null(knnk); + IGRAPH_VECTOR_INIT_FINALLY(°hist, maxdeg); + } + + for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_real_t sum = 0.0; + igraph_integer_t v = IGRAPH_VIT_GET(vit); + igraph_integer_t nv; + igraph_real_t str = VECTOR(strength)[v]; + /* Get neighbours and incident edges */ + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, mode)); + IGRAPH_CHECK(igraph_incident(graph, &edge_neis, v, mode)); + nv = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < nv; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + igraph_integer_t e = VECTOR(edge_neis)[j]; + igraph_real_t w = VECTOR(*weights)[e]; + sum += w * VECTOR(deg)[nei]; + } + if (str != 0.0) { + VECTOR(*my_knn)[i] = sum / str; + } else { + VECTOR(*my_knn)[i] = IGRAPH_NAN; + } + if (knnk && nv > 0) { + VECTOR(*knnk)[nv - 1] += sum; + VECTOR(deghist)[nv - 1] += str; + } + } + + igraph_vector_int_destroy(&edge_neis); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(2); + + if (knnk) { + for (igraph_integer_t i = 0; i < maxdeg; i++) { + igraph_real_t dh = VECTOR(deghist)[i]; + if (dh != 0) { + VECTOR(*knnk)[i] /= dh; + } else { + VECTOR(*knnk)[i] = IGRAPH_NAN; + } + } + + igraph_vector_destroy(°hist); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_destroy(&strength); + igraph_vector_int_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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_avg_nearest_neighbor_degree + * \brief 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. + * This is equation (6) in the reference below. + * + * + * When only the k_nn(k) degree correlation function is needed, + * \ref igraph_degree_correlation_vector() can be used as well. This function provides + * more flexible control over how degree at each end of directed edges are computed. + * + * + * 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. This is sometimes referred to as the k_nn(k) + * degree correlation function. The first (zeroth) element is for degree + * one vertices, etc. The calculation is done based only on the vertices + * \p vids. 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. + * + * \sa \ref igraph_degree_correlation_vector() for computing only the degree correlation function, + * with more flexible control over degree computations. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \example examples/simple/igraph_avg_nearest_neighbor_degree.c + */ +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis; + igraph_integer_t no_vids; + igraph_vit_t vit; + igraph_vector_t my_knn_v, *my_knn = knn; + igraph_vector_int_t deg; + igraph_integer_t maxdeg; + igraph_vector_int_t deghist; + + 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_INT_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_INT_INIT_FINALLY(&neis, maxdeg); + igraph_vector_int_clear(&neis); + + if (knnk) { + IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg)); + igraph_vector_null(knnk); + IGRAPH_VECTOR_INT_INIT_FINALLY(°hist, maxdeg); + } + + for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_real_t sum = 0.0; + igraph_integer_t v = IGRAPH_VIT_GET(vit); + igraph_integer_t nv; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, mode)); + nv = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < nv; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + sum += VECTOR(deg)[nei]; + } + if (nv != 0) { + VECTOR(*my_knn)[i] = sum / nv; + } else { + VECTOR(*my_knn)[i] = IGRAPH_NAN; + } + if (knnk && nv > 0) { + VECTOR(*knnk)[nv - 1] += VECTOR(*my_knn)[i]; + VECTOR(deghist)[nv - 1] += 1; + } + } + + if (knnk) { + for (igraph_integer_t i = 0; i < maxdeg; i++) { + igraph_integer_t dh = VECTOR(deghist)[i]; + if (dh != 0) { + VECTOR(*knnk)[i] /= dh; + } else { + VECTOR(*knnk)[i] = IGRAPH_NAN; + } + } + igraph_vector_int_destroy(°hist); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(°); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(3); + + if (!knn) { + igraph_vector_destroy(&my_knn_v); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_degree_correlation_vector + * \brief Degree correlation function. + * + * \experimental + * + * Computes the degree correlation function k_nn(k), defined as the + * mean degree of the targets of directed edges whose source has degree \c k. + * The averaging is done over all directed edges. The \p from_mode and \p to_mode + * parameters control how the source and target vertex degrees are computed. + * This way the out-in, out-out, in-in and in-out degree correlation functions + * can all be computed. + * + * + * In undirected graphs, edges are treated as if they were a pair of reciprocal directed + * ones. + * + * + * If P_ij is the joint degree distribution of the graph, computable with + * \ref igraph_joint_degree_distribution(), then + * k_nn(k) = (sum_j j P_kj) / (sum_j P_kj). + * + * + * The function \ref igraph_avg_nearest_neighbor_degree(), whose main purpose is to + * calculate the average neighbor degree for each vertex separately, can also compute + * k_nn(k). It differs from this function in that it can take a subset + * of vertices to base the calculation on, but it does not allow the same fine-grained + * control over how degrees are computed. + * + * + * References: + * + * + * R. Pastor-Satorras, A. Vazquez, A. Vespignani: + * Dynamical and Correlation Properties of the Internet, + * Phys. Rev. Lett., vol. 87, pp. 258701 (2001). + * https://doi.org/10.1103/PhysRevLett.87.258701 + * + * + * A. Vazquez, R. Pastor-Satorras, A. Vespignani: + * Large-scale topological and dynamical properties of the Internet, + * Phys. Rev. E, vol. 65, pp. 066130 (2002). + * https://doi.org/10.1103/PhysRevE.65.066130 + * + * + * 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. + * \param weights An optional weight vector. If not \c NULL, weighted averages will be computed. + * \param knnk An initialized vector, the result will be written here. + * knnk[d] will contain the mean degree of vertices connected to + * by vertices of degree \c d. Note that in contrast to + * \ref igraph_avg_nearest_neighbor_degree(), d=0 is also + * included. + * \param from_mode How to compute the degree of sources? Can be \c IGRAPH_OUT + * for out-degree, \c IGRAPH_IN for in-degree, or \c IGRAPH_ALL for total degree. + * Ignored in undirected graphs. + * \param to_mode How to compute the degree of sources? Can be \c IGRAPH_OUT + * for out-degree, \c IGRAPH_IN for in-degree, or \c IGRAPH_ALL for total degree. + * Ignored in undirected graphs. + * \param directed_neighbors Whether to consider u -> v connections + * to be directed. Undirected connections are treated as reciprocal directed ones, + * i.e. both u -> v and v -> u will be considered. + * Ignored in undirected graphs. + * \return Error code. + * + * \sa \ref igraph_avg_nearest_neighbor_degree() for computing the average neighbour + * degree of a set of vertices, \ref igraph_joint_degree_distribution() to get the + * complete joint degree distribution, and \ref igraph_assortativity_degree() + * to compute the degree assortativity. + * + * Time complexity: O(|E| + |V|) + */ +igraph_error_t igraph_degree_correlation_vector( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_t *knnk, + igraph_neimode_t from_mode, igraph_neimode_t to_mode, + igraph_bool_t directed_neighbors) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t maxdeg; + igraph_vector_t weight_sums; + igraph_vector_int_t *deg_from, *deg_to, deg_out, deg_in, deg_all; + + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (! igraph_is_directed(graph)) { + from_mode = to_mode = IGRAPH_ALL; + directed_neighbors = false; + } + + igraph_bool_t have_out = from_mode == IGRAPH_OUT || to_mode == IGRAPH_OUT; + igraph_bool_t have_in = from_mode == IGRAPH_IN || to_mode == IGRAPH_IN; + igraph_bool_t have_all = from_mode == IGRAPH_ALL || to_mode == IGRAPH_ALL; + + if (have_out) { + IGRAPH_VECTOR_INT_INIT_FINALLY(°_out, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °_out, igraph_vss_all(), IGRAPH_OUT, /* loops */ true)); + } + + if (have_in) { + IGRAPH_VECTOR_INT_INIT_FINALLY(°_in, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °_in, igraph_vss_all(), IGRAPH_IN, /* loops */ true)); + } + + if (have_all) { + IGRAPH_VECTOR_INT_INIT_FINALLY(°_all, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °_all, igraph_vss_all(), IGRAPH_ALL, /* loops */ true)); + } + + switch (from_mode) { + case IGRAPH_OUT: deg_from = °_out; break; + case IGRAPH_IN: deg_from = °_in; break; + case IGRAPH_ALL: deg_from = °_all; break; + default: + IGRAPH_ERROR("Invalid 'from' mode.", IGRAPH_EINVMODE); + } + + switch (to_mode) { + case IGRAPH_OUT: deg_to = °_out; break; + case IGRAPH_IN: deg_to = °_in; break; + case IGRAPH_ALL: deg_to = °_all; break; + default: + IGRAPH_ERROR("Invalid 'to' mode.", IGRAPH_EINVMODE); + } + + maxdeg = no_of_edges > 0 ? igraph_vector_int_max(deg_from) : 0; + + IGRAPH_VECTOR_INIT_FINALLY(&weight_sums, maxdeg+1); + + IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg+1)); + igraph_vector_null(knnk); + + for (igraph_integer_t eid=0; eid < no_of_edges; eid++) { + igraph_integer_t from = IGRAPH_FROM(graph, eid); + igraph_integer_t to = IGRAPH_TO(graph, eid); + igraph_integer_t fromdeg = VECTOR(*deg_from)[from]; + igraph_integer_t todeg = VECTOR(*deg_to)[to]; + igraph_real_t w = weights ? VECTOR(*weights)[eid] : 1; + + VECTOR(weight_sums)[fromdeg] += w; + VECTOR(*knnk)[fromdeg] += w * todeg; + + /* Treat undirected edges as reciprocal directed ones */ + if (! directed_neighbors) { + VECTOR(weight_sums)[todeg] += w; + VECTOR(*knnk)[todeg] += w * fromdeg; + } + } + + IGRAPH_CHECK(igraph_vector_div(knnk, &weight_sums)); + + igraph_vector_destroy(&weight_sums); + IGRAPH_FINALLY_CLEAN(1); + + /* In reverse order of initialization: */ + + if (have_all) { + igraph_vector_int_destroy(°_all); + IGRAPH_FINALLY_CLEAN(1); + } + + if (have_in) { + igraph_vector_int_destroy(°_in); + IGRAPH_FINALLY_CLEAN(1); + } + + if (have_out) { + igraph_vector_int_destroy(°_out); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_i_strength_all( + const igraph_t *graph, igraph_vector_t *res, + igraph_neimode_t mode, igraph_bool_t loops, + const igraph_vector_t *weights) { + + // When calculating strength for all vertices, iterating over edges is faster + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (loops) { + if (mode & IGRAPH_OUT) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + VECTOR(*res)[IGRAPH_FROM(graph, edge)] += VECTOR(*weights)[edge]; + } + } + if (mode & IGRAPH_IN) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + VECTOR(*res)[IGRAPH_TO(graph, edge)] += VECTOR(*weights)[edge]; + } + } + } else { + if (mode & IGRAPH_OUT) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + if (from != IGRAPH_TO(graph, edge)) { + VECTOR(*res)[from] += VECTOR(*weights)[edge]; + } + } + } + if (mode & IGRAPH_IN) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + igraph_integer_t to = IGRAPH_TO(graph, edge); + if (IGRAPH_FROM(graph, edge) != to) { + VECTOR(*res)[to] += VECTOR(*weights)[edge]; + } + } + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_strength + * \brief Strength of the vertices, also called weighted vertex degree. + * + * 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). + * This parameter is ignored for undirected graphs. + * \param loops A logical scalar, whether to count loop edges as well. + * \param weights A vector giving the edge weights. If this is a \c 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. + */ +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vit_t vit; + igraph_integer_t no_vids; + igraph_vector_int_t degrees; + igraph_vector_int_t neis; + + if (! weights) { + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + IGRAPH_CHECK(igraph_degree(graph, °rees, vids, mode, loops)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + VECTOR(*res)[i] = VECTOR(degrees)[i]; + } + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + } + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode for vertex strength calculation.", IGRAPH_EINVMODE); + } + + if (igraph_vs_is_all(&vids)) { + return igraph_i_strength_all(graph, res, mode, loops, weights); + } + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + no_vids = IGRAPH_VIT_SIZE(vit); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&neis, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(res, no_vids)); + igraph_vector_null(res); + + if (loops) { + for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + IGRAPH_CHECK(igraph_incident(graph, &neis, IGRAPH_VIT_GET(vit), mode)); + const igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t edge = VECTOR(neis)[j]; + VECTOR(*res)[i] += VECTOR(*weights)[edge]; + } + } + } else { + for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + IGRAPH_CHECK(igraph_incident(graph, &neis, IGRAPH_VIT_GET(vit), mode)); + const igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t edge = VECTOR(neis)[j]; + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + if (from != to) { + VECTOR(*res)[i] += VECTOR(*weights)[edge]; + } + } + } + } + + igraph_vit_destroy(&vit); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \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 \ref igraph_vs_all() or \ref 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. + * + */ +igraph_error_t igraph_sort_vertex_ids_by_degree(const igraph_t *graph, + igraph_vector_int_t *outvids, + igraph_vs_t vids, + igraph_neimode_t mode, + igraph_bool_t loops, + igraph_order_t order, + igraph_bool_t only_indices) { + igraph_integer_t i, n; + igraph_vector_int_t degrees; + igraph_vector_int_t vs_vec; + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, 0); + IGRAPH_CHECK(igraph_degree(graph, °rees, vids, mode, loops)); + IGRAPH_CHECK(igraph_vector_int_qsort_ind(°rees, outvids, order)); + if (only_indices || igraph_vs_is_all(&vids) ) { + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&vs_vec, 0); + IGRAPH_CHECK(igraph_vs_as_vector(graph, vids, &vs_vec)); + n = igraph_vector_int_size(outvids); + for (i = 0; i < n; i++) { + VECTOR(*outvids)[i] = VECTOR(vs_vec)[VECTOR(*outvids)[i]]; + } + igraph_vector_int_destroy(&vs_vec); + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(2); + } + return IGRAPH_SUCCESS; +} diff --git a/src/properties/ecc.c b/src/properties/ecc.c new file mode 100644 index 0000000..5701596 --- /dev/null +++ b/src/properties/ecc.c @@ -0,0 +1,387 @@ +/* + 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, see . +*/ + +#include "igraph_transitivity.h" + +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_adjlist.h" + +#include "core/interruption.h" + +/* This implementation of ECC relies on (lazy_)adjlist_get() producing sorted + * neighbor lists and (lazy_)adjlist_init() being called with IGRAPH_NO_LOOPS + * and IGRAPH_NO_MULTIPLE to prevent duplicate entries. + */ + +/* Optimized for the case when computing ECC for all edges. */ +static igraph_error_t igraph_i_ecc3_1( + const igraph_t *graph, igraph_vector_t *res, const igraph_es_t eids, + igraph_bool_t offset, igraph_bool_t normalize) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degree; + igraph_adjlist_t al; + igraph_eit_t eit; + const igraph_real_t c = offset ? 1.0 : 0.0; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (igraph_integer_t i=0; + ! IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), i++) { + + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); + + igraph_real_t z; /* number of triangles the edge participates in */ + igraph_real_t s; /* max number of triangles the edge could be part of */ + + IGRAPH_ALLOW_INTERRUPTION(); + + if (v1 == v2) { + /* A self-loop isn't, and cannot be part of any triangles. */ + z = 0.0; + s = 0.0; + } else { + const igraph_vector_int_t *a1 = igraph_adjlist_get(&al, v1), *a2 = igraph_adjlist_get(&al, v2); + igraph_integer_t d1 = VECTOR(degree)[v1], d2 = VECTOR(degree)[v2]; + + z = igraph_vector_int_intersection_size_sorted(a1, a2); + s = (d1 < d2 ? d1 : d2) - 1.0; + } + + VECTOR(*res)[i] = z + c; + if (normalize) VECTOR(*res)[i] /= s; + } + + igraph_eit_destroy(&eit); + igraph_vector_int_destroy(°ree); + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Optimized for computing ECC for a small subset of edges. */ +static igraph_error_t igraph_i_ecc3_2( + const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { + + igraph_lazy_adjlist_t al; + igraph_eit_t eit; + const igraph_real_t c = offset ? 1.0 : 0.0; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (igraph_integer_t i=0; + ! IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), i++) { + + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); + + igraph_real_t z; /* number of triangles the edge participates in */ + igraph_real_t s; /* max number of triangles the edge could be part of */ + + IGRAPH_ALLOW_INTERRUPTION(); + + if (v1 == v2) { + /* A self-loop isn't, and cannot be part of any triangles. */ + z = 0.0; + s = 0.0; + } else { + igraph_vector_int_t *a1 = igraph_lazy_adjlist_get(&al, v1); + igraph_vector_int_t *a2 = igraph_lazy_adjlist_get(&al, v2); + + igraph_integer_t d1, d2; + IGRAPH_CHECK(igraph_degree_1(graph, &d1, v1, IGRAPH_ALL, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree_1(graph, &d2, v2, IGRAPH_ALL, IGRAPH_LOOPS)); + + z = igraph_vector_int_intersection_size_sorted(a1, a2); + s = (d1 < d2 ? d1 : d2) - 1.0; + } + + VECTOR(*res)[i] = z + c; + if (normalize) VECTOR(*res)[i] /= s; + } + + igraph_eit_destroy(&eit); + igraph_lazy_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/* Optimized for the case when computing ECC for all edges. */ +static igraph_error_t igraph_i_ecc4_1( + const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degree; + igraph_adjlist_t al; + igraph_eit_t eit; + igraph_real_t c = offset ? 1.0 : 0.0; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (igraph_integer_t i=0; + ! IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), i++) { + + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); + + igraph_real_t z; /* number of 4-cycles the edge participates in */ + igraph_real_t s; /* max number of 4-cycles the edge could be part of */ + + IGRAPH_ALLOW_INTERRUPTION(); + + if (v1 == v2) { + z = 0.0; + s = 0.0; + } else { + /* ensure that v1 is the vertex with the smaller degree */ + if (VECTOR(degree)[v1] > VECTOR(degree)[v2]) { + igraph_integer_t tmp = v1; + v1 = v2; + v2 = tmp; + } + + z = 0.0; + const igraph_vector_int_t *a1 = igraph_adjlist_get(&al, v1); + const igraph_integer_t n = igraph_vector_int_size(a1); + for (igraph_integer_t j=0; j < n; j++) { + igraph_integer_t v3 = VECTOR(*a1)[j]; + + /* It is not possible that v3 == v1 because self-loops have been removed from the adjlist. */ + + if (v3 == v2) continue; + + const igraph_vector_int_t *a2 = igraph_adjlist_get(&al, v2), *a3 = igraph_adjlist_get(&al, v3); + + z += igraph_vector_int_intersection_size_sorted(a2, a3) - 1.0; + } + + igraph_integer_t d1 = VECTOR(degree)[v1], d2 = VECTOR(degree)[v2]; + s = (d1 - 1.0) * (d2 - 1.0); + } + + VECTOR(*res)[i] = z + c; + if (normalize) VECTOR(*res)[i] /= s; + } + + igraph_eit_destroy(&eit); + igraph_vector_int_destroy(°ree); + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Optimized for computing ECC for a small subset of edges. */ +static igraph_error_t igraph_i_ecc4_2( + const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { + + igraph_lazy_adjlist_t al; + igraph_eit_t eit; + igraph_real_t c = offset ? 1.0 : 0.0; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (igraph_integer_t i=0; + ! IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), i++) { + + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); + + igraph_real_t z; /* number of 4-cycles the edge participates in */ + igraph_real_t s; /* max number of 4-cycles the edge could be part of */ + + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_integer_t d1, d2; + IGRAPH_CHECK(igraph_degree_1(graph, &d1, v1, IGRAPH_ALL, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree_1(graph, &d2, v2, IGRAPH_ALL, IGRAPH_LOOPS)); + + if (v1 == v2) { + z = 0.0; + s = 0.0; + } else { + /* ensure that v1 is the vertex with the smaller degree */ + if (d1 > d2) { + igraph_integer_t tmp = v1; + v1 = v2; + v2 = tmp; + + tmp = d1; + d1 = d2; + d2 = tmp; + } + + z = 0.0; + + igraph_vector_int_t *a1 = igraph_lazy_adjlist_get(&al, v1); + + const igraph_integer_t n = igraph_vector_int_size(a1); + for (igraph_integer_t j=0; j < n; j++) { + igraph_integer_t v3 = VECTOR(*a1)[j]; + + /* It is not possible that v3 == v1 because self-loops have been removed from the adjlist. */ + + if (v3 == v2) continue; + + igraph_vector_int_t *a2 = igraph_lazy_adjlist_get(&al, v2); + igraph_vector_int_t *a3 = igraph_lazy_adjlist_get(&al, v3); + + z += igraph_vector_int_intersection_size_sorted(a2, a3) - 1.0; + } + + s = (d1 - 1.0) * (d2 - 1.0); + } + + VECTOR(*res)[i] = z + c; + if (normalize) VECTOR(*res)[i] /= s; + } + + igraph_eit_destroy(&eit); + igraph_lazy_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_ecc + * \brief Edge clustering coefficient of some edges. + * + * \experimental + * + * The edge clustering coefficient C^(k)_ij of an edge (i, j) + * is defined based on the number of k-cycles the edge participates in, + * z^(k)_ij, and the largest number of such cycles it could + * participate in given the degrees of its endpoints, s^(k)_ij. + * The original definition given in the reference below is: + * + * + * C^(k)_ij = (z^(k)_ij + 1) / s^(k)_ij + * + * + * For k=3, s^(k)_ij = min(d_i - 1, d_j - 1), + * where \c d_i and \c d_j are the edge endpoint degrees. + * For k=4, s^(k)_ij = (d_i - 1) (d_j - 1). + * + * + * The \p normalize and \p offset parameters allow for skipping normalization + * by s^(k) and offsetting the cycle count z^(k) + * by one in the numerator of C^(k). Set both to \c true to + * compute the original definition of this metric. + * + * + * This function ignores edge multiplicities when listing k-cycles + * (i.e. z^(k)), but not when computing the maximum number of + * cycles an edge can participate in (s^(k)). + * + * + * Reference: + * + * + * F. Radicchi, C. Castellano, F. Cecconi, V. Loreto, and D. Parisi, + * PNAS 101, 2658 (2004). + * https://doi.org/10.1073/pnas.0400054101 + * + * \param graph The input graph. + * \param res Initialized vector, the result will be stored here. + * \param eids The edges for which the edge clustering coefficient will be computed. + * \param k Size of cycles to use in calculation. Must be at least 3. Currently + * only values of 3 and 4 are supported. + * \param offset Boolean, whether to add one to cycle counts. When \c false, + * z^(k) is used instead of z^(k) + 1. In this case + * the maximum value of the normalized metric is 1. For k=3 this + * is achieved for all edges in a complete graph. + * \param normalize Boolean, whether to normalize cycle counts by the maximum + * possible count s^(k) given the degrees. + * \return Error code. + * + * Time complexity: When \p k is 3, O(|V| d log d + |E| d). + * When \p k is 4, O(|V| d log d + |E| d^2). d denotes the degree of vertices. + */ +igraph_error_t igraph_ecc(const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_integer_t k, + igraph_bool_t offset, igraph_bool_t normalize) { + + if (k < 3) { + IGRAPH_ERRORF("Cycle size for edge clustering coefficient must be at least 3, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, k); + } + + switch (k) { + case 3: + if (igraph_es_is_all(&eids)) { + return igraph_i_ecc3_1(graph, res, eids, offset, normalize); + } else { + return igraph_i_ecc3_2(graph, res, eids, offset, normalize); + } + case 4: + if (igraph_es_is_all(&eids)) { + return igraph_i_ecc4_1(graph, res, eids, offset, normalize); + } else { + return igraph_i_ecc4_2(graph, res, eids, offset, normalize); + } + default: + IGRAPH_ERROR("Edge clustering coefficient calculation is only implemented for cycle sizes 3 and 4.", + IGRAPH_UNIMPLEMENTED); + } +} diff --git a/src/properties/girth.c b/src/properties/girth.c new file mode 100644 index 0000000..28ee98b --- /dev/null +++ b/src/properties/girth.c @@ -0,0 +1,217 @@ +/* -*- 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" + +/** + * \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, i.e. cycles of length 1 or 2 are + * not considered. + * + * + * For graphs that contain no cycles, and only for such graphs, + * infinity is returned. + * + * + * The first implementation of this function was done by Keith Briggs, + * thanks Keith. + * + * + * Reference: + * + * + * 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. + * https://doi.org/10.1145/800105.803390 + * + * \param graph The input graph. Edge directions will be ignored. + * \param girth Pointer to an \c igraph_real_t, 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 + */ +igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, + igraph_vector_int_t *circle) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_lazy_adjlist_t adjlist; + igraph_integer_t mincirc = IGRAPH_INTEGER_MAX, minvertex = 0; + igraph_integer_t node; + igraph_bool_t triangle = false; + igraph_vector_int_t *neis; + igraph_vector_int_t level; + igraph_integer_t stoplevel = no_of_nodes + 1; + igraph_bool_t anycircle = false; + igraph_integer_t 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_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&level, no_of_nodes); + + 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_int_clear(&q); + igraph_vector_int_null(&level); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + VECTOR(level)[node] = 1; + + IGRAPH_ALLOW_INTERRUPTION(); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actlevel = VECTOR(level)[actnode]; + igraph_integer_t i, n; + + if (actlevel >= stoplevel) { + break; + } + + neis = igraph_lazy_adjlist_get(&adjlist, actnode); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + + n = igraph_vector_int_size(neis); + for (i = 0; i < n; i++) { + igraph_integer_t nei = VECTOR(*neis)[i]; + igraph_integer_t 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_int_push(&q, nei); + VECTOR(level)[nei] = actlevel + 1; + } + } + + } /* while q !empty */ + } /* node */ + + if (girth) { + if (mincirc == IGRAPH_INTEGER_MAX) { + *girth = IGRAPH_INFINITY; + } else { + *girth = mincirc; + } + } + + if (mincirc == IGRAPH_INTEGER_MAX) { + mincirc = 0; + } + + /* Store the actual circle, if needed */ + if (circle) { + IGRAPH_CHECK(igraph_vector_int_resize(circle, mincirc)); + if (mincirc != 0) { + igraph_integer_t i, n, idx = 0; + igraph_dqueue_int_clear(&q); + igraph_vector_int_null(&level); /* used for father pointers */ +#define FATHER(x) (VECTOR(level)[(x)]) + IGRAPH_CHECK(igraph_dqueue_int_push(&q, minvertex)); + FATHER(minvertex) = minvertex; + while (FATHER(t1) == 0 || FATHER(t2) == 0) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + neis = igraph_lazy_adjlist_get(&adjlist, actnode); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + n = igraph_vector_int_size(neis); + for (i = 0; i < n; i++) { + igraph_integer_t nei = VECTOR(*neis)[i]; + if (FATHER(nei) == 0) { + FATHER(nei) = actnode + 1; + igraph_dqueue_int_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_int_destroy(&level); + igraph_dqueue_int_destroy(&q); + igraph_lazy_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/loops.c b/src/properties/loops.c new file mode 100644 index 0000000..18b5ac1 --- /dev/null +++ b/src/properties/loops.c @@ -0,0 +1,164 @@ +/* -*- 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. + * + * + * The return value of this function is cached in the graph itself; calling + * the function multiple times with no modifications to the graph in between + * will return a cached value in O(1) time. + * + * \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_is_loop.c + */ +igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { + igraph_integer_t i, m = igraph_ecount(graph); + + IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_HAS_LOOP, res); + + *res = false; + + for (i = 0; i < m; i++) { + if (IGRAPH_FROM(graph, i) == IGRAPH_TO(graph, i)) { + *res = true; + break; + } + } + + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, *res); + + return IGRAPH_SUCCESS; +} + +/** + * \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 + */ +igraph_error_t igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, + igraph_es_t es) { + igraph_eit_t eit; + igraph_bool_t found_loop = false; + + 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))); + + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + ! igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + igraph_vector_bool_null(res); + goto done; + } + + for (igraph_integer_t i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { + igraph_integer_t e = IGRAPH_EIT_GET(eit); + igraph_bool_t is_loop = (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)); + VECTOR(*res)[i] = is_loop; + if (is_loop) { + found_loop = true; + } + } + + if (found_loop) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, true); + } else if (igraph_es_is_all(&es)) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, false); + } + +done: + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_count_loops + * \brief Counts the self-loops in the graph. + * + * \experimental + * + * Counts loop edges, i.e. edges whose two endpoints coincide. + * + * \param graph The input graph. + * \param res Pointer to an integer, the number of self-loops will be stored here. + * \return Error code. + * + * Time complexity: O(|E|), linear in the number of edges. + * + * \example examples/simple/igraph_is_loop.c + */ +igraph_error_t igraph_count_loops(const igraph_t *graph, igraph_integer_t *loop_count) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t count; + + /* Nothing to do if we know that there are no loops. */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + *loop_count = 0; + return IGRAPH_SUCCESS; + } + + count = 0; + for (igraph_integer_t e=0; e < no_of_edges; e++) { + if (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)) { + count++; + } + } + + /* We already checked for loops, so take the opportunity to set the cache. */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, count > 0); + + *loop_count = count; + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/multiplicity.c b/src/properties/multiplicity.c new file mode 100644 index 0000000..9647356 --- /dev/null +++ b/src/properties/multiplicity.c @@ -0,0 +1,536 @@ +/* -*- 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|). + */ +igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t ec = igraph_ecount(graph); + + /* Is it already known whether the graph has loops or multi-edges? */ + igraph_bool_t known_loop, known_multi; + + /* If it is known, does the graph have them? */ + igraph_bool_t has_loop, has_multi; + + known_loop = igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP); + if (known_loop) { + has_loop = igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP); + if (has_loop) { + *res = false; + return IGRAPH_SUCCESS; + } + } + + known_multi = igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI); + if (known_multi) { + has_multi = igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI); + if (has_multi) { + *res = false; + return IGRAPH_SUCCESS; + } + } + + if (known_loop && known_multi) { + if (!has_loop && !has_multi) { + *res = true; + return IGRAPH_SUCCESS; + } + } + + /* Up to now, these variables were used to store the cache status. + * From here on, we re-use them to store the outcome of explicit + * checks. */ + + known_loop = known_multi = false; + has_loop = has_multi = false; /* be optimistic */ + + if (vc == 0 || ec == 0) { + *res = true; + known_loop = known_multi = true; + } else { + igraph_vector_int_t neis; + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + for (igraph_integer_t i = 0; i < vc; i++) { + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT)); + const igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < n; j++) { + if (VECTOR(neis)[j] == i) { + known_loop = true; has_loop = true; break; + } + /* Attention: If the graph is undirected, self-loops appears + * twice in the neighbour list. This does not mean that there + * are multi-edges. We do not need to worry about this as loop + * are already caught above. */ + if (j > 0 && VECTOR(neis)[j - 1] == VECTOR(neis)[j]) { + known_multi = true; has_multi = true; break; + } + } + } + *res = !has_loop && !has_multi; + if (*res) { + known_multi = known_loop = true; + } + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + } + + if (known_loop) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, has_loop); + } + + if (known_multi) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MULTI, has_multi); + } + + return IGRAPH_SUCCESS; +} + + +/** + * \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. + * + * + * The return value of this function is cached in the graph itself; calling + * the function multiple times with no modifications to the graph in between + * will return a cached value in O(1) time. + * + * \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 + */ +igraph_error_t igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) { + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t ec = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + + IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_HAS_MULTI, res); + + if (vc == 0 || ec == 0) { + *res = false; + } else { + igraph_vector_int_t neis; + igraph_integer_t i, j, n; + igraph_bool_t found = false; + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + for (i = 0; i < vc && !found; i++) { + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, + IGRAPH_OUT)); + n = igraph_vector_int_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 = true; break; + } else if (VECTOR(neis)[j - 1] != i) { + /* Undirected, but not a loop edge */ + found = true; break; + } else if (j < n - 1 && VECTOR(neis)[j] == VECTOR(neis)[j + 1]) { + /* Undirected, loop edge, multiple times */ + found = true; break; + } + } + } + } + *res = found; + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MULTI, *res); + + return IGRAPH_SUCCESS; +} + +/** + * \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 + */ +igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, + igraph_es_t es) { + igraph_eit_t eit; + igraph_integer_t 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)) { + igraph_integer_t e = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, from); + + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + VECTOR(*res)[i] = false; + + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + igraph_integer_t e2 = VECTOR(*neis)[j]; + igraph_integer_t to2 = IGRAPH_OTHER(graph, e2, from); + if (to2 == to && e2 < e) { + VECTOR(*res)[i] = true; + } + } + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_count_multiple + * \brief The multiplicity of some edges in a graph. + * + * An edge is called a multiple edge when there is one or more other + * edge between the same two vertices. The multiplicity of an edge + * is the number of edges between its endpoints. + * + * \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_count_multiple_1() if you only need the multiplicity of a + * single edge; \ref igraph_is_multiple() if you are only interested in whether + * the graph has at least one edge with multiplicity greater than one; + * \ref igraph_simplify() to ensure that the graph has no multiple edges. + * + * 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. + */ +igraph_error_t igraph_count_multiple(const igraph_t *graph, igraph_vector_int_t *res, igraph_es_t es) { + igraph_eit_t eit; + igraph_integer_t i, j, n; + igraph_lazy_adjlist_t adjlist; + + IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_vector_int_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { + igraph_integer_t e = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, from); + + IGRAPH_CHECK_OOM(neis, "Failed to query adjacent vertices."); + + VECTOR(*res)[i] = 0; + + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + if (VECTOR(*neis)[j] == to) { + VECTOR(*res)[i]++; + } + } + } + + igraph_lazy_adjlist_destroy(&adjlist); + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_count_multiple_1 + * \brief The multiplicity of a single edge in a graph. + * + * \param graph The input graph. + * \param res Pointer to an iteger, the result will be stored here. + * \param eid The ID of the edge to check. + * \return Error code. + * + * \sa \ref igraph_count_multiple() if you need the multiplicity of multiple + * edges; \ref igraph_is_multiple() if you are only interested in whether the + * graph has at least one edge with multiplicity greater than one; + * \ref igraph_simplify() to ensure that the graph has no multiple edges. + * + * Time complexity: O(d), where d is the out-degree of the tail of the edge. + */ +igraph_error_t igraph_count_multiple_1(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t eid) +{ + igraph_integer_t i, n, count; + igraph_integer_t from = IGRAPH_FROM(graph, eid); + igraph_integer_t to = IGRAPH_TO(graph, eid); + igraph_vector_int_t vids; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vids, from, IGRAPH_OUT)); + + count = 0; + n = igraph_vector_int_size(&vids); + for (i = 0; i < n; i++) { + if (VECTOR(vids)[i] == to) { + count++; + } + } + + igraph_vector_int_destroy(&vids); + IGRAPH_FINALLY_CLEAN(1); + + *res = count; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_is_mutual + * \brief Check whether some edges of a directed graph are mutual. + * + * An (A,B) non-loop directed edge is mutual if the graph contains + * the (B,A) edge too. Whether directed self-loops are considered mutual + * is controlled by the \p loops parameter. + * + * + * 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. + * + * \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 + * \ref igraph_ess_all() to check all edges. + * \param loops Boolean, whether to consider directed self-loops + * to be mutual. + * \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. + */ +igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es, igraph_bool_t loops) { + + igraph_eit_t eit; + igraph_lazy_adjlist_t adjlist; + igraph_integer_t 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, true); + 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)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + + if (from == to) { + VECTOR(*res)[i] = loops; + continue; /* no need to do binsearch for self-loops */ + } + + /* Check whether there is a to->from edge, search for from in the + out-list of to */ + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, to); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + 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; +} + + +/** + * \function igraph_has_mutual + * \brief Check whether a directed graph has any mutual edges. + * + * An (A,B) non-loop directed edge is mutual if the graph contains + * the (B,A) edge too. Whether directed self-loops are considered mutual + * is controlled by the \p loops parameter. + * + * + * In undirected graphs, all edges are considered mutual by definition. + * Thus for undirected graph, this function returns false only when there + * are no edges. + * + * + * To check whether a graph is an oriented graph, use this function in + * conjunction with \ref igraph_is_directed(). + * + * \param graph The input graph. + * \param res Pointer to a boolean, the result will be stored here. + * \param loops Boolean, whether to consider directed self-loops + * to be mutual. + * \return Error code. + * + * Time complexity: O(|E| log(d)) where d is the maximum in-degree. + */ +igraph_error_t igraph_has_mutual(const igraph_t *graph, igraph_bool_t *res, igraph_bool_t loops) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_lazy_adjlist_t adjlist; + + if (! igraph_is_directed(graph)) { + /* In undirected graphs, all edges are considered mutual, so we just check + * if there are any edges. */ + *res = no_of_edges > 0; + return IGRAPH_SUCCESS; + } + + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MUTUAL)) { + if (igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MUTUAL)) { + /* we know that the graph has at least one mutual non-loop edge + * (because the cache only stores non-loop edges) */ + *res = true; + return IGRAPH_SUCCESS; + } else if (loops) { + /* no non-loop mutual edges, but maybe we have loops? */ + return igraph_has_loop(graph, res); + } else { + /* no non-loop mutual edges, and loops are not to be treated as mutual */ + *res = false; + 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); + + *res = false; /* assume no mutual edges */ + for (igraph_integer_t edge=0; edge < no_of_edges; edge++) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + + if (from == to) { + if (loops) { + *res = true; + break; + } + continue; /* no need to do binsearch for self-loops */ + } + + /* Check whether there is a to->from edge, search for from in the + out-list of to */ + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, to); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + if (igraph_vector_int_binsearch2(neis, from)) { + *res = true; + break; + } + } + + igraph_lazy_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + /* cache the result if loops are not treated as mutual */ + if (!loops) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MUTUAL, *res); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/neighborhood.c b/src/properties/neighborhood.c new file mode 100644 index 0000000..98fbbe1 --- /dev/null +++ b/src/properties/neighborhood.c @@ -0,0 +1,439 @@ +/* -*- 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. + */ +igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, + igraph_integer_t mindist) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vit_t vit; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_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, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size."); + IGRAPH_FINALLY(igraph_free, added); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_vector_int_resize(res, IGRAPH_VIT_SIZE(vit))); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t size = mindist == 0 ? 1 : 0; + added[node] = i + 1; + igraph_dqueue_int_clear(&q); + if (order > 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + } + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_size(&neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_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++) { + igraph_integer_t nei = 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_int_destroy(&neis); + igraph_vit_destroy(&vit); + igraph_dqueue_int_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 list of integer vectors. The result of the + * calculation will be stored here. The list 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 \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. + */ +igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, igraph_integer_t mindist) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vit_t vit; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_t neis; + igraph_vector_int_t tmp; + + 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, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size."); + IGRAPH_FINALLY(igraph_free, added); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_CHECK(igraph_vector_int_list_reserve(res, IGRAPH_VIT_SIZE(vit))); + igraph_vector_int_list_clear(res); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + added[node] = i + 1; + igraph_vector_int_clear(&tmp); + if (mindist == 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); + } + if (order > 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + } + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_size(&neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } else { + /* we just count them but don't add them to q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } + + } /* while q not empty */ + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &tmp)); + } + + igraph_vector_int_destroy(&tmp); + igraph_vector_int_destroy(&neis); + igraph_vit_destroy(&vit); + igraph_dqueue_int_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 list of graphs, the result will be stored + * here. Each item in the list is an \c igraph_t object. The list 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 \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. + */ +igraph_error_t igraph_neighborhood_graphs(const igraph_t *graph, igraph_graph_list_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, + igraph_integer_t mindist) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vit_t vit; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_t neis; + igraph_vector_int_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, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size"); + IGRAPH_FINALLY(igraph_free, added); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + + igraph_graph_list_clear(res); + + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + added[node] = i + 1; + igraph_vector_int_clear(&tmp); + if (mindist == 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); + } + if (order > 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + } + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_size(&neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } else { + /* we just count them but don't add them to q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } + + } /* while q not empty */ + + if (igraph_vector_int_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)); + } + + IGRAPH_FINALLY(igraph_destroy, &newg); + IGRAPH_CHECK(igraph_graph_list_push_back(res, &newg)); + IGRAPH_FINALLY_CLEAN(1); /* ownership of `newg' taken by `res' */ + } + + igraph_vector_int_destroy(&tmp); + igraph_vector_int_destroy(&neis); + igraph_vit_destroy(&vit); + igraph_dqueue_int_destroy(&q); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/perfect.c b/src/properties/perfect.c new file mode 100644 index 0000000..de1b426 --- /dev/null +++ b/src/properties/perfect.c @@ -0,0 +1,191 @@ +/* -*- 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, see . +*/ + +#include "igraph_structural.h" + +#include "igraph_bipartite.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_operators.h" +#include "igraph_topology.h" + +#include "core/interruption.h" + +/** + * \function igraph_is_perfect + * \brief Checks if the graph is perfect. + * + * A perfect graph is an undirected graph in which the chromatic number of every induced + * subgraph equals the order of the largest clique of that subgraph. + * The chromatic number of a graph G is the smallest number of colors needed to + * color the vertices of G so that no two adjacent vertices share the same color. + * + * + * Warning: This function may create the complement of the graph internally, + * which consumes a lot of memory. For moderately sized graphs, consider + * decomposing them into biconnected components and running the check separately + * on each component. + * + * + * This implementation is based on the strong perfect graph theorem which was + * conjectured by Claude Berge and proved by Maria Chudnovsky, Neil Robertson, + * Paul Seymour, and Robin Thomas. + * + * \param graph The input graph. It is expected to be undirected and simple. + * \param perfect Pointer to an integer, the result will be stored here. + * \return Error code. + * + * Time complexity: worst case exponenital, often faster in practice. + */ +igraph_error_t igraph_is_perfect(const igraph_t *graph, igraph_bool_t *perfect) { + + igraph_bool_t is_bipartite, is_chordal, iso, is_simple; + igraph_real_t girth, comp_girth; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t start; + igraph_integer_t cycle_len; + igraph_t comp_graph, cycle; + + // If the graph is directed return error. + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("The concept of perfect graphs is only defined for undirected graphs.", IGRAPH_EINVAL); + } + + // If the graph isn't simple then return an error. + IGRAPH_CHECK(igraph_is_simple(graph, &is_simple)); + if (!is_simple) { + IGRAPH_ERROR("Perfect graph testing is implemented for simple graphs only. Simplify the graph.", IGRAPH_EINVAL); + } + + // All graphs with less than 5 vertices are perfect. + if (no_of_nodes < 5) { + *perfect = true; + return IGRAPH_SUCCESS; + } + + // Graphs with less than 5 edges or a complement with less than 5 edges + // are also perfect. The following check handles most 5-vertex graphs, + // but its usefulness quickly diminishes, with only 0.3% of unlabelled + // 8-vertex graphs handled. + // In order to avoid bad results due to integer overflow with large graphs, + // we limit this check for small graphs only. + if ( no_of_nodes < 10000 && + (no_of_edges < 5 || no_of_edges > (no_of_nodes - 1) * no_of_nodes / 2 - 5)) { + *perfect = true; + return IGRAPH_SUCCESS; + } + + // Chordal and bipartite graph types are perfect. + // Possibly more optimizations found here: http://www.or.uni-bonn.de/~hougardy/paper/ClassesOfPerfectGraphs.pdf + IGRAPH_CHECK(igraph_is_bipartite(graph, &is_bipartite, NULL)); + if (is_bipartite) { + *perfect = true; + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_is_chordal(graph, NULL, NULL, &is_chordal, NULL, NULL)); + if (is_chordal) { + *perfect = true; + return IGRAPH_SUCCESS; + } + + // The weak perfect graph theorem: + // A graph is perfect iff its complement is perfect. + IGRAPH_CHECK(igraph_complementer(&comp_graph, graph, 0)); + IGRAPH_FINALLY(igraph_destroy, &comp_graph); + + IGRAPH_CHECK(igraph_is_bipartite(&comp_graph, &is_bipartite, NULL)); + if (is_bipartite) { + *perfect = true; + goto clean1; + } + + IGRAPH_CHECK(igraph_is_chordal(&comp_graph, NULL, NULL, &is_chordal, NULL, NULL)); + if (is_chordal) { + *perfect = true; + goto clean1; + } + + // Since igraph_is_bipartite also catches trees, at this point the girth + // of the graph and its complementer (to be stored in girth and comp_girth) + // are both guaranteed to be finite. + + // If the girth (or the smallest circle in the graph) is bigger than 3 and have odd number of vertices then + // the graph isn't perfect. + IGRAPH_CHECK(igraph_girth(graph, &girth, NULL)); + if ((girth > 3) && (((igraph_integer_t)girth) % 2 == 1)) { + *perfect = false; + goto clean1; + } + + IGRAPH_CHECK(igraph_girth(&comp_graph, &comp_girth, NULL)); + if ((comp_girth > 3) && (((igraph_integer_t)comp_girth) % 2 == 1)) { + *perfect = false; + goto clean1; + } + + // At this point girth and comp_girth are both at least 3. + + // Strong perfect graph theorem: + // A graph is perfect iff neither it or its complement contains an induced odd cycle of length >= 5 + // (i.e. an odd hole). TODO: Find a more efficient way to check for odd holes. + start = (igraph_integer_t) (girth < comp_girth ? girth : comp_girth); + start = start % 2 == 0 ? start + 1 : start + 2; + for (cycle_len = start; cycle_len <= no_of_nodes ; cycle_len += 2) { + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_ring(&cycle, cycle_len, IGRAPH_UNDIRECTED, /* mutual */ 0, /* circular */ 1)); + IGRAPH_FINALLY(igraph_destroy, &cycle); + + if (cycle_len > girth) { + IGRAPH_CHECK(igraph_subisomorphic_lad(&cycle, graph, NULL, &iso, NULL, NULL, /* induced */ 1, 0)); + if (iso) { + *perfect = false; + goto clean2; + } + } + + if (cycle_len > comp_girth) { + IGRAPH_CHECK(igraph_subisomorphic_lad(&cycle, &comp_graph, NULL, &iso, NULL, NULL, /* induced */ 1, 0)); + if (iso) { + *perfect = false; + goto clean2; + } + } + + igraph_destroy(&cycle); + IGRAPH_FINALLY_CLEAN(1); + } + + *perfect = true; + +clean1: + /* normal exit route */ + igraph_destroy(&comp_graph); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + +clean2: + /* exit route if we also have a cycle to destroy */ + igraph_destroy(&cycle); + IGRAPH_FINALLY_CLEAN(1); + goto clean1; +} diff --git a/src/properties/properties_internal.h b/src/properties/properties_internal.h new file mode 100644 index 0000000..b12b1b4 --- /dev/null +++ b/src/properties/properties_internal.h @@ -0,0 +1,34 @@ +/* -*- 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_decls.h" +#include "igraph_iterators.h" + +__BEGIN_DECLS + +igraph_error_t igraph_i_trans4_al_simplify(igraph_adjlist_t *al, + const igraph_vector_int_t *rank); + +__END_DECLS + +#endif diff --git a/src/properties/spectral.c b/src/properties/spectral.c new file mode 100644 index 0000000..e362cef --- /dev/null +++ b/src/properties/spectral.c @@ -0,0 +1,431 @@ +/* -*- 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 "math/safe_intop.h" + +#include + +static igraph_error_t igraph_i_laplacian_validate_weights( + const igraph_t* graph, const igraph_vector_t* weights +) { + igraph_integer_t no_of_edges; + + if (weights == NULL) { + return IGRAPH_SUCCESS; + } + + no_of_edges = igraph_ecount(graph); + + 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 non-negative.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_laplacian + * \brief Returns the Laplacian matrix of a graph. + * + * The Laplacian matrix \c L of a graph is defined as + * L_ij = - A_ij when i != j and + * L_ii = d_i - A_ii. Here \c A denotes the (possibly weighted) + * adjacency matrix and d_i is the degree (or strength, if weighted) + * of vertex \c i. In directed graphs, the \p mode parameter controls whether to use + * out- or in-degrees. Correspondingly, the rows or columns will sum to zero. + * In undirected graphs, A_ii is taken to be \em twice the number + * (or total weight) of self-loops, ensuring that d_i = \sum_j A_ij. + * Thus, the Laplacian of an undirected graph is the same as the Laplacian + * of a directed one obtained by replacing each undirected edge with two reciprocal + * directed ones. + * + * + * More compactly, L = D - A where the \c D is a diagonal matrix + * containing the degrees. The Laplacian matrix can also be normalized, with several + * conventional normalization methods. See \ref igraph_laplacian_normalization_t for + * the methods available in igraph. + * + * + * 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. + * \param mode Controls whether to use out- or in-degrees in directed graphs. + * If set to \c IGRAPH_ALL, edge directions will be ignored. + * \param normalization The normalization method to use when calculating the + * Laplacian matrix. See \ref igraph_laplacian_normalization_t for + * possible values. + * \param weights An optional vector containing non-negative 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|^2), |V| is the number of vertices in the graph. + * + * \example examples/simple/igraph_get_laplacian.c + */ + +igraph_error_t igraph_get_laplacian( + const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, + igraph_laplacian_normalization_t normalization, + const igraph_vector_t *weights +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + igraph_vector_t degree; + igraph_integer_t i; + + IGRAPH_ASSERT(res != NULL); + + IGRAPH_CHECK(igraph_i_laplacian_validate_weights(graph, weights)); + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_null(res); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), mode, IGRAPH_LOOPS, weights)); + + /* Value of 'mode' is validated in igraph_strength() call above. */ + if (! directed) { + mode = IGRAPH_ALL; + } else if (mode == IGRAPH_ALL) { + directed = 0; + } + + for (i = 0; i < no_of_nodes; i++) { + switch (normalization) { + case IGRAPH_LAPLACIAN_UNNORMALIZED: + MATRIX(*res, i, i) = VECTOR(degree)[i]; + break; + + case IGRAPH_LAPLACIAN_SYMMETRIC: + if (VECTOR(degree)[i] > 0) { + MATRIX(*res, i, i) = 1; + VECTOR(degree)[i] = 1.0 / sqrt(VECTOR(degree)[i]); + } + break; + + case IGRAPH_LAPLACIAN_LEFT: + case IGRAPH_LAPLACIAN_RIGHT: + if (VECTOR(degree)[i] > 0) { + MATRIX(*res, i, i) = 1; + VECTOR(degree)[i] = 1.0 / VECTOR(degree)[i]; + } + break; + + default: + IGRAPH_ERROR("Invalid Laplacian normalization method.", IGRAPH_EINVAL); + } + } + + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_real_t weight = weights ? VECTOR(*weights)[i] : 1.0; + igraph_real_t norm; + + switch (normalization) { + case IGRAPH_LAPLACIAN_UNNORMALIZED: + MATRIX(*res, from, to) -= weight; + if (!directed) { + MATRIX(*res, to, from) -= weight; + } + break; + + case IGRAPH_LAPLACIAN_SYMMETRIC: + norm = VECTOR(degree)[from] * VECTOR(degree)[to]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero %s-%s, " + "cannot perform symmetric normalization of Laplacian with '%s' mode.", + IGRAPH_EINVAL, + mode == IGRAPH_OUT ? "out" : "in", weights ? "strength" : "degree", mode == IGRAPH_OUT ? "out" : "in"); + } + weight *= norm; + MATRIX(*res, from, to) -= weight; + if (!directed) { + MATRIX(*res, to, from) -= weight; + } + break; + + case IGRAPH_LAPLACIAN_LEFT: + norm = VECTOR(degree)[from]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero in-%s, " + "cannot perform left stochastic normalization of Laplacian with 'in' mode.", + IGRAPH_EINVAL, + weights ? "strength" : "degree"); + } + MATRIX(*res, from, to) -= weight * norm; + if (!directed) { + /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ + MATRIX(*res, to, from) -= weight * VECTOR(degree)[to]; + } + break; + + case IGRAPH_LAPLACIAN_RIGHT: + norm = VECTOR(degree)[to]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero out-%s, " + "cannot perform right stochastic normalization of Laplacian with 'out' mode.", + IGRAPH_EINVAL, + weights ? "strength" : "degree"); + } + MATRIX(*res, from, to) -= weight * norm; + if (!directed) { + /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ + MATRIX(*res, to, from) -= weight * VECTOR(degree)[from]; + } + break; + } + } + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_get_laplacian_sparse + * \brief Returns the Laplacian of a graph in a sparse matrix format. + * + * See \ref igraph_get_laplacian() for the definition of the Laplacian matrix. + * + * + * The first version of this function was written by Vincent Matossian. + * + * \param graph Pointer to the graph to convert. + * \param sparseres Pointer to an initialized sparse matrix object, the + * result is stored here. + * \param mode Controls whether to use out- or in-degrees in directed graphs. + * If set to \c IGRAPH_ALL, edge directions will be ignored. + * \param normalization The normalization method to use when calculating the + * Laplacian matrix. See \ref igraph_laplacian_normalization_t for + * possible values. + * \param weights An optional vector containing non-negative 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(|E|), |E| is the number of edges in the graph. + * + * \example examples/simple/igraph_get_laplacian_sparse.c + */ + +igraph_error_t igraph_get_laplacian_sparse( + const igraph_t *graph, igraph_sparsemat_t *sparseres, igraph_neimode_t mode, + igraph_laplacian_normalization_t normalization, + const igraph_vector_t *weights +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + igraph_vector_t degree; + igraph_integer_t i; + igraph_integer_t nz; + + if (directed) { + IGRAPH_SAFE_ADD(no_of_edges, no_of_nodes, &nz); + } else { + IGRAPH_SAFE_ADD(no_of_edges * 2, no_of_nodes, &nz); + } + + IGRAPH_ASSERT(sparseres != NULL); + + IGRAPH_CHECK(igraph_i_laplacian_validate_weights(graph, weights)); + + IGRAPH_CHECK(igraph_sparsemat_resize(sparseres, no_of_nodes, no_of_nodes, nz)); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), mode, IGRAPH_LOOPS, weights)); + + for (i = 0; i < no_of_nodes; i++) { + switch (normalization) { + case IGRAPH_LAPLACIAN_UNNORMALIZED: + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, VECTOR(degree)[i])); + break; + + case IGRAPH_LAPLACIAN_SYMMETRIC: + if (VECTOR(degree)[i] > 0) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, 1)); + VECTOR(degree)[i] = 1.0 / sqrt(VECTOR(degree)[i]); + } + break; + + case IGRAPH_LAPLACIAN_LEFT: + case IGRAPH_LAPLACIAN_RIGHT: + if (VECTOR(degree)[i] > 0) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, 1)); + VECTOR(degree)[i] = 1.0 / VECTOR(degree)[i]; + } + break; + + default: + IGRAPH_ERROR("Invalid Laplacian normalization method.", IGRAPH_EINVAL); + } + } + + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_real_t weight = weights ? VECTOR(*weights)[i] : 1.0; + igraph_real_t norm; + + switch (normalization) { + case IGRAPH_LAPLACIAN_UNNORMALIZED: + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight)); + if (!directed) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight)); + } + break; + + case IGRAPH_LAPLACIAN_SYMMETRIC: + norm = VECTOR(degree)[from] * VECTOR(degree)[to]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero %s-%s, " + "cannot perform symmetric normalization of Laplacian with '%s' mode.", + IGRAPH_EINVAL, + mode == IGRAPH_OUT ? "out" : "in", weights ? "strength" : "degree", mode == IGRAPH_OUT ? "out" : "in"); + } + weight *= norm; + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight)); + if (!directed) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight)); + } + break; + + case IGRAPH_LAPLACIAN_LEFT: + norm = VECTOR(degree)[from]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero in-%s, " + "cannot perform left stochastic normalization of Laplacian with 'in' mode.", + IGRAPH_EINVAL, + weights ? "strength" : "degree"); + } + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight * norm)); + if (!directed) { + /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight * VECTOR(degree)[to])); + } + break; + + case IGRAPH_LAPLACIAN_RIGHT: + norm = VECTOR(degree)[to]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero out-%s, " + "cannot perform right stochastic normalization of Laplacian with 'out' mode.", + IGRAPH_EINVAL, + weights ? "strength" : "degree"); + } + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight * norm)); + if (!directed) { + /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight * VECTOR(degree)[from])); + } + break; + } + } + + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_laplacian + * \brief Returns the Laplacian matrix of a graph (deprecated). + * + * This function produces the Laplacian matrix of a graph in either dense or + * sparse format. When \p normalized is set to true, the type of normalization + * used depends on the directnedness of the graph: symmetric normalization + * is used for undirected graphs and left stochastic normalization for + * directed graphs. + * + * \param graph Pointer to the graph to convert. + * \param res Pointer to an initialized matrix object or \c NULL. The dense matrix + * result will be stored here. + * \param sparseres Pointer to an initialized sparse matrix object or \c NULL. + * The sparse matrix result will be stored here. + * \param mode Controls whether to use out- or in-degrees in directed graphs. + * If set to \c IGRAPH_ALL, edge directions will be ignored. + * \param normalized Boolean, whether to normalize the result. + * \param weights An optional vector containing non-negative edge weights, + * to calculate the weighted Laplacian matrix. Set it to a null pointer to + * calculate the unweighted Laplacian. + * \return Error code. + * + * \deprecated-by igraph_get_laplacian 0.10.0 + */ + +igraph_error_t igraph_laplacian( + const igraph_t *graph, igraph_matrix_t *res, igraph_sparsemat_t *sparseres, + igraph_bool_t normalized, const igraph_vector_t *weights +) { + igraph_laplacian_normalization_t norm_method = IGRAPH_LAPLACIAN_UNNORMALIZED; + + if (!res && !sparseres) { + IGRAPH_ERROR("Laplacian: specify at least one of 'res' or 'sparseres'", + IGRAPH_EINVAL); + } + + if (normalized) { + if (igraph_is_directed(graph)) { + norm_method = IGRAPH_LAPLACIAN_LEFT; + } else { + norm_method = IGRAPH_LAPLACIAN_SYMMETRIC; + } + } + + if (res) { + IGRAPH_CHECK(igraph_get_laplacian(graph, res, IGRAPH_OUT, norm_method, weights)); + } + + if (sparseres) { + IGRAPH_CHECK(igraph_get_laplacian_sparse(graph, sparseres, IGRAPH_OUT, norm_method, weights)); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/trees.c b/src/properties/trees.c new file mode 100644 index 0000000..b076c9a --- /dev/null +++ b/src/properties/trees.c @@ -0,0 +1,746 @@ +/* -*- 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_topology.h" + +#include "igraph_bitset.h" +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_stack.h" + +/** + * \function igraph_unfold_tree + * \brief 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. + * + */ +igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, + igraph_neimode_t mode, const igraph_vector_int_t *roots, + igraph_vector_int_t *vertex_index) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_roots = igraph_vector_int_size(roots); + igraph_integer_t tree_vertex_count = no_of_nodes; + + igraph_vector_int_t edges; + igraph_bitset_t seen_vertices; + igraph_bitset_t seen_edges; + + igraph_dqueue_int_t Q; + igraph_vector_int_t neis; + + igraph_integer_t v_ptr = no_of_nodes; + + if (! igraph_vector_int_isininterval(roots, 0, no_of_nodes-1)) { + IGRAPH_ERROR("All roots should be vertices of the graph.", IGRAPH_EINVVID); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_BITSET_INIT_FINALLY(&seen_vertices, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&seen_edges, no_of_edges); + + if (vertex_index) { + IGRAPH_CHECK(igraph_vector_int_range(vertex_index, 0, no_of_nodes)); + } + + for (igraph_integer_t r = 0; r < no_of_roots; r++) { + + igraph_integer_t root = VECTOR(*roots)[r]; + IGRAPH_BIT_SET(seen_vertices, root); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, root)); + + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); + + IGRAPH_CHECK(igraph_incident(graph, &neis, actnode, mode)); + + igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t i = 0; i < n; i++) { + + igraph_integer_t edge = VECTOR(neis)[i]; + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, actnode); + + if (! IGRAPH_BIT_TEST(seen_edges, edge)) { + + IGRAPH_BIT_SET(seen_edges, edge); + + if (! IGRAPH_BIT_TEST(seen_vertices, nei)) { + + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + + IGRAPH_BIT_SET(seen_vertices, nei); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + + } else { + + tree_vertex_count++; + if (vertex_index) { + IGRAPH_CHECK(igraph_vector_int_push_back(vertex_index, nei)); + } + + if (from == nei) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v_ptr++)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + } else { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v_ptr++)); + } + } + } + + } /* for i + * In the directed case, an 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 connected, and therefore not 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 \ref igraph_is_connected() + * + * \example examples/simple/igraph_kary_tree.c + */ +igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode) { + igraph_bool_t is_tree = false; + igraph_bool_t treat_as_undirected = !igraph_is_directed(graph) || mode == IGRAPH_ALL; + igraph_integer_t iroot = 0; + igraph_integer_t visited_count; + igraph_integer_t vcount, ecount; + + vcount = igraph_vcount(graph); + ecount = igraph_ecount(graph); + + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED)) { + igraph_bool_t weakly_connected = igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED); + + if (weakly_connected) { + /* For undirected graphs and for directed graphs with mode == IGRAPH_ALL, + * we can return early if we know from the cache that the graph is weakly + * connected and is a forest. We can do this even if the user wants the + * root vertex because we always return zero as the root vertex for + * undirected graphs */ + if (treat_as_undirected && + igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) && + igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST) + ) { + is_tree = true; + iroot = 0; + goto success; + } + } else /* ! weakly_connected */ { + /* If the graph is not weakly connected, then it is neither an undirected + * not a directed tree. There is no root, so we can return early. */ + is_tree = false; + goto success; + } + } + + /* A tree must have precisely vcount-1 edges. */ + /* By convention, the zero-vertex graph will not be considered a tree. */ + if (ecount != vcount - 1) { + is_tree = false; + goto success; + } + + /* The single-vertex graph is a tree, provided it has no edges (checked in the previous if (..)) */ + if (vcount == 1) { + is_tree = true; + iroot = 0; + goto 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. + */ + + is_tree = true; /* assume success */ + + switch (mode) { + case IGRAPH_ALL: + iroot = 0; + break; + + case IGRAPH_IN: + case IGRAPH_OUT: { + igraph_vector_int_t degree; + igraph_integer_t i; + + IGRAPH_CHECK(igraph_vector_int_init(°ree, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, °ree); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), mode == IGRAPH_IN ? IGRAPH_OUT : IGRAPH_IN, IGRAPH_LOOPS)); + + 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. + */ + is_tree = false; + break; + } + } + + /* If no suitable root is found, the graph is not a tree. */ + if (is_tree && i == vcount) { + is_tree = false; + } else { + iroot = i; + } + + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + } + + break; + default: + IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVMODE); + } + + /* if no suitable root was found, skip visiting vertices */ + if (is_tree) { + IGRAPH_CHECK(igraph_i_is_tree_visitor(graph, iroot, mode, &visited_count)); + is_tree = visited_count == vcount; + } + +success: + if (res) { + *res = is_tree; + } + + if (root) { + *root = iroot; + } + + if (is_tree) { + /* A graph that is a directed tree is also an undirected tree. + * An undirected tree is weakly connected and is a forest, + * so we can cache this. */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, true); + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true); + } + + return IGRAPH_SUCCESS; +} + +/* igraph_is_forest() -- check if a graph is a forest */ + +/* Verify that the graph has no cycles and count the number of reachable vertices. + * This function performs a DFS starting from 'root'. + * If it finds a cycle, it sets *res to false, otherwise it does not change it. + * *visited_count will be incremented by the number of vertices reachable from 'root', + * including 'root' itself. + */ +static igraph_error_t igraph_i_is_forest_visitor( + const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, + igraph_bitset_t *visited, igraph_stack_int_t *stack, igraph_vector_int_t *neis, + igraph_integer_t *visited_count, igraph_bool_t *res) +{ + igraph_integer_t i; + + igraph_stack_int_clear(stack); + + /* push the root onto the stack */ + IGRAPH_CHECK(igraph_stack_int_push(stack, root)); + + while (! igraph_stack_int_empty(stack)) { + igraph_integer_t u; + igraph_integer_t ncount; + + /* Take a vertex from stack and check if it is already visited. + * If yes, then we found a cycle: the graph is not a forest. + * Otherwise mark it as visited and continue. + */ + u = igraph_stack_int_pop(stack); + if (IGRAPH_LIKELY(! IGRAPH_BIT_TEST(*visited, u))) { + IGRAPH_BIT_SET(*visited, u); + *visited_count += 1; + } + else { + *res = false; + break; + } + + /* Vertex discovery: Register all its neighbours for future processing */ + IGRAPH_CHECK(igraph_neighbors(graph, neis, u, mode)); + ncount = igraph_vector_int_size(neis); + + for (i = 0; i < ncount; ++i) { + igraph_integer_t v = VECTOR(*neis)[i]; + + if (mode == IGRAPH_ALL) { + /* In the undirected case, we avoid returning to the predecessor + * vertex of 'v' in the DFS tree by skipping visited vertices. + * + * Note that in order to succcessfully detect a cycle, a vertex + * within that cycle must end up on the stack more than once. + * Does skipping visited vertices preclude this sometimes? + * No, because any visited vertex can only be accessed through + * an already discovered vertex (i.e. one that has already been + * pushed onto the stack). + */ + if (IGRAPH_LIKELY(! IGRAPH_BIT_TEST(*visited, v))) { + IGRAPH_CHECK(igraph_stack_int_push(stack, v)); + } + /* To check for a self-loop in undirected graph */ + else if (v == u) { + *res = false; + break; + } + } + else { + IGRAPH_CHECK(igraph_stack_int_push(stack, v)); + } + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_is_forest( + const igraph_t *graph, igraph_bool_t *res, + igraph_vector_int_t *roots, igraph_neimode_t mode +); + +/** + * \ingroup structural + * \function igraph_is_forest + * \brief Decides whether the graph is a forest. + * + * An undirected graph is a forest if it has no cycles. Equivalently, + * a graph is a forest if all connected components are trees. + * + * + * In the directed case, an additional requirement is that edges in each + * tree are oriented away from the root (out-trees or arborescences) or all edges + * are oriented towards the root (in-trees or anti-arborescences). + * This test can be controlled using the \p mode parameter. + * + * + * By convention, the null graph (i.e. the graph with no vertices) is considered to be a forest. + * + * + * The \p res return value of this function is cached in the graph itself if + * \p mode is set to \c IGRAPH_ALL or if the graph is undirected. Calling the + * function multiple times with no modifications to the graph in between + * will return a cached value in O(1) time if the roots are not requested. + * + * \param graph The graph object to analyze. + * \param res Pointer to a logical variable. If not \c NULL, then the result will be stored + * here. + * \param roots If not \c NULL, the root nodes will be stored here. When \p mode + * is \c IGRAPH_ALL or the graph is undirected, any one vertex from each + * component can be the root. When \p mode is \c IGRAPH_OUT + * or \c IGRAPH_IN, all the vertices with zero in- or out-degree, + * respectively are considered as root nodes. + * \param mode For a directed graph this specifies whether to test for an + * out-forest, an in-forest 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_EINVMODE: invalid mode argument. + * + * Time complexity: At most O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + */ +igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool_t *res, + igraph_vector_int_t *roots, igraph_neimode_t mode) { + + const igraph_bool_t treat_as_undirected = !igraph_is_directed(graph) || mode == IGRAPH_ALL; + + if (!roots && !res) { + return IGRAPH_SUCCESS; + } + + /* Note on cache use: + * + * The IGRAPH_PROP_IS_FOREST cached property is equivalent to this function's + * result ONLY in the undirected case. Keep in mind that a graph that is not + * a directed forest may still be an undirected forest, i.e. may still be free + * of undirected cycles. Example: 1->2<-3->4. + */ + + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST)) { + const igraph_bool_t no_undirected_cycles = igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST); + if (treat_as_undirected && res && ! roots) { + /* If the graph is treated as undirected and no roots are requested, + * we can directly use the cached IGRAPH_PROP_IS_FOREST value. */ + *res = no_undirected_cycles; + return IGRAPH_SUCCESS; + } else { + /* Otherwise we can use negative cached values (i.e. "false"): + * - A graph with undirected cycles cannot be a directed forest. + * - If the graph is not a forest, we don't need to look for roots. + */ + if (! no_undirected_cycles) { + if (res) { *res = false; } + if (roots) { igraph_vector_int_clear(roots); } + return IGRAPH_SUCCESS; + } + } + } + + IGRAPH_CHECK(igraph_i_is_forest(graph, res, roots, mode)); + + /* At this point we know whether the graph is an (undirected or directed) forest + * as we have at least one of 'res' or 'roots'. The case when both are NULL was + * caught above. */ + igraph_bool_t is_forest; + if (res != NULL) { + is_forest = *res; + } else /* roots != NULL */ { + is_forest = igraph_vcount(graph) == 0 || !igraph_vector_int_empty(roots); + } + if (is_forest) { + /* If the graph is a directed forest, then it has no undirected cycles. + * We can enter positive results in the cache unconditionally. */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, true); + } else if (treat_as_undirected) { + /* However, if the graph is not a directed forest, it might still be + * an undirected forest. We can only enter negative results in the cache + * when edge directions were ignored, but NOT in the directed case. */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, false); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_is_forest( + const igraph_t *graph, igraph_bool_t *res, + igraph_vector_int_t *roots, igraph_neimode_t mode +) { + igraph_bitset_t visited; + igraph_vector_int_t neis; + igraph_stack_int_t stack; + igraph_integer_t visited_count = 0; + igraph_integer_t vcount, ecount; + igraph_integer_t v; + igraph_bool_t result; + + vcount = igraph_vcount(graph); + ecount = igraph_ecount(graph); + + if (roots) { + igraph_vector_int_clear(roots); + } + + /* Any graph with 0 edges is a forest. */ + if (ecount == 0) { + if (res) { + *res = true; + } + if (roots) { + for (v = 0; v < vcount; v++) { + IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); + } + } + return IGRAPH_SUCCESS; + } + + /* A forest can have at most vcount-1 edges. */ + if (ecount > vcount - 1) { + if (res) { + *res = false; + } + return IGRAPH_SUCCESS; + } + + /* Ignore mode for undirected graphs. */ + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + result = true; /* assume success */ + + IGRAPH_BITSET_INIT_FINALLY(&visited, vcount); + + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + /* The main algorithm: + * + * Undirected Graph:- We add each unvisited vertex to the roots vector, and + * mark all other vertices that are reachable from it as visited. + * + * Directed Graph:- For each tree, the root is the node with no + * incoming/outgoing connections, depending on 'mode'. We add each vertex + * with zero degree to the roots vector and mark all other vertices that are + * reachable from it as visited. + * + * If all the vertices are visited exactly once, then the graph is a forest. + */ + + switch (mode) { + case IGRAPH_ALL: + { + for (v = 0; v < vcount; ++v) { + if (!result) { + break; + } + if (! IGRAPH_BIT_TEST(visited, v)) { + if (roots) { + IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); + } + IGRAPH_CHECK(igraph_i_is_forest_visitor( + graph, v, mode, + &visited, &stack, &neis, + &visited_count, &result)); + } + } + break; + } + + case IGRAPH_IN: + case IGRAPH_OUT: + { + igraph_vector_int_t degree; + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_REVERSE_MODE(mode), /* loops = */ 1)); + + for (v = 0; v < vcount; ++v) { + /* In an out-tree, roots have in-degree 0, + * and all other vertices have in-degree 1. */ + if (VECTOR(degree)[v] > 1 || !result) { + result = false; + break; + } + if (VECTOR(degree)[v] == 0) { + if (roots) { + IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); + } + IGRAPH_CHECK(igraph_i_is_forest_visitor( + graph, v, mode, + &visited, &stack, &neis, + &visited_count, &result)); + } + } + + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + break; + } + + default: + IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVMODE); + } + + if (result) { + /* In a forest, all vertices are reachable from the roots. */ + result = (visited_count == vcount); + } + + if (res) { + *res = result; + } + + /* If the graph is not a forest then the root vector will be empty. */ + if (!result && roots) { + igraph_vector_int_clear(roots); + } + + igraph_vector_int_destroy(&neis); + igraph_stack_int_destroy(&stack); + igraph_bitset_destroy(&visited); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_is_acyclic + * \brief Checks whether a graph is acyclic or not. + * + * This function checks whether a graph is acyclic or not. + * + * \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. + */ +igraph_error_t igraph_is_acyclic(const igraph_t *graph, igraph_bool_t *res) { + if (igraph_is_directed(graph)) { + /* igraph_is_dag is cached */ + return igraph_is_dag(graph, res); + } else { + /* igraph_is_forest is cached if mode == IGRAPH_ALL and we don't need + * the roots */ + return igraph_is_forest(graph, res, NULL, IGRAPH_ALL); + } +} diff --git a/src/properties/triangles.c b/src/properties/triangles.c new file mode 100644 index 0000000..c306911 --- /dev/null +++ b/src/properties/triangles.c @@ -0,0 +1,896 @@ +/* -*- 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. + */ + +igraph_error_t 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 (!isnan(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; +} + +static igraph_error_t 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; +} + +static igraph_error_t igraph_transitivity_local_undirected2(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vit_t vit; + igraph_integer_t nodes_to_calc, affected_nodes; + igraph_integer_t maxdegree = 0; + igraph_integer_t i, j, k, nn; + igraph_lazy_adjlist_t adjlist; + igraph_vector_int_t degree; + igraph_vector_t indexv, avids, rank, triangles; + igraph_vector_int_t order; + igraph_integer_t *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++) { + igraph_integer_t v = IGRAPH_VIT_GET(vit); + igraph_vector_int_t *neis2; + igraph_integer_t 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, v); + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + + neilen = igraph_vector_int_size(neis2); + for (j = 0; j < neilen; j++) { + igraph_integer_t nei = 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_INT_INIT_FINALLY(&order, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, affected_nodes); + for (i = 0; i < affected_nodes; i++) { + igraph_integer_t v = VECTOR(avids)[i]; + igraph_vector_int_t *neis2; + igraph_integer_t deg; + neis2 = igraph_lazy_adjlist_get(&adjlist, v); + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + VECTOR(degree)[i] = deg = igraph_vector_int_size(neis2); + if (deg > maxdegree) { + maxdegree = deg; + } + } + IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree + 1)); + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + IGRAPH_VECTOR_INIT_FINALLY(&rank, affected_nodes); + for (i = 0; i < affected_nodes; i++) { + VECTOR(rank)[ VECTOR(order)[i] ] = affected_nodes - i - 1; + } + + neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + if (neis == 0) { + IGRAPH_ERROR("Insufficient memory for local transitivity calculation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, neis); + + IGRAPH_VECTOR_INIT_FINALLY(&triangles, affected_nodes); + for (nn = affected_nodes - 1; nn >= 0; nn--) { + igraph_integer_t node = VECTOR(avids) [ VECTOR(order)[nn] ]; + igraph_vector_int_t *neis1, *neis2; + igraph_integer_t neilen1, neilen2; + igraph_integer_t nodeindex = VECTOR(indexv)[node]; + igraph_integer_t noderank = VECTOR(rank) [nodeindex - 1]; + + IGRAPH_ALLOW_INTERRUPTION(); + + neis1 = igraph_lazy_adjlist_get(&adjlist, node); + IGRAPH_CHECK_OOM(neis1, "Failed to query neighbors."); + + neilen1 = igraph_vector_int_size(neis1); + for (i = 0; i < neilen1; i++) { + igraph_integer_t nei = VECTOR(*neis1)[i]; + neis[nei] = node + 1; + } + for (i = 0; i < neilen1; i++) { + igraph_integer_t nei = VECTOR(*neis1)[i]; + igraph_integer_t neiindex = VECTOR(indexv)[nei]; + igraph_integer_t neirank = 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, nei); + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + neilen2 = igraph_vector_int_size(neis2); + for (j = 0; j < neilen2; j++) { + igraph_integer_t nei2 = VECTOR(*neis2)[j]; + igraph_integer_t nei2index = VECTOR(indexv)[nei2]; + igraph_integer_t nei2rank = 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)) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t idx = VECTOR(indexv)[node] - 1; + igraph_vector_int_t *neis2 = igraph_lazy_adjlist_get(&adjlist, node); + igraph_integer_t deg; + + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + + 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_int_destroy(&order); + igraph_vector_destroy(&avids); + igraph_vector_destroy(&indexv); + igraph_lazy_adjlist_destroy(&adjlist); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(8); + + return IGRAPH_SUCCESS; +} + +/* This removes loop, multiple edges and edges that point + "backwards" according to the rank vector. */ +/* Note: Also used in scan.c */ +igraph_error_t igraph_i_trans4_al_simplify(igraph_adjlist_t *al, + const igraph_vector_int_t *rank) { + igraph_integer_t i; + igraph_integer_t n = al->length; + igraph_vector_int_t mark; + + IGRAPH_CHECK(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]; + igraph_integer_t j, l = igraph_vector_int_size(v); + igraph_integer_t irank = VECTOR(*rank)[i]; + VECTOR(mark)[i] = i + 1; + for (j = 0; j < l; /* nothing */) { + igraph_integer_t e = 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 IGRAPH_SUCCESS; +} + +static igraph_error_t 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_transitivity_local_undirected + * \brief The local transitivity (clustering coefficient) of some vertices. + * + * 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. + */ + +igraph_error_t 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; + igraph_integer_t 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 igraph_error_t igraph_adjacent_triangles1(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids) { +# include "properties/triangles_template1.h" + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_adjacent_triangles4(const igraph_t *graph, + igraph_vector_t *res) { +# include "properties/triangles_template.h" + return IGRAPH_SUCCESS; +} + +/** + * \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. + */ + +igraph_error_t 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. + * + * + * The triangles are reported as a long list of vertex ID triplets. Use + * the \c int variant of \ref igraph_matrix_view_from_vector() to create a + * matrix view into the vector where each triangle is stored in a column of the + * matrix (see the example). + * + * \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. + * + * \example examples/simple/igraph_list_triangles.c + */ + +igraph_error_t 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 + */ + +igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, + igraph_real_t *res, + igraph_transitivity_mode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_real_t triples = 0, triangles = 0; + igraph_integer_t node, nn; + igraph_integer_t maxdegree; + igraph_integer_t *neis; + igraph_vector_int_t order; + igraph_vector_t rank; + igraph_vector_int_t degree; + + igraph_adjlist_t allneis; + igraph_vector_int_t *neis1, *neis2; + igraph_integer_t i, j, neilen1, neilen2; + + if (no_of_nodes == 0) { + *res = mode == IGRAPH_TRANSITIVITY_ZERO ? 0.0 : IGRAPH_NAN; + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + maxdegree = igraph_vector_int_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); + + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(rank)[ VECTOR(order)[i] ] = no_of_nodes - i - 1; + } + + IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); + + neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + if (! neis) { + IGRAPH_ERROR("Insufficient memory for undirected global transitivity.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, neis); + + 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); + triples += (igraph_real_t)neilen1 * (neilen1 - 1); + /* Mark the neighbors of 'node' */ + for (i = 0; i < neilen1; i++) { + igraph_integer_t nei = VECTOR(*neis1)[i]; + neis[nei] = node + 1; + } + for (i = 0; i < neilen1; i++) { + igraph_integer_t nei = 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++) { + igraph_integer_t nei2 = VECTOR(*neis2)[j]; + if (neis[nei2] == node + 1) { + triangles += 1.0; + } + } + } + } + } + + IGRAPH_FREE(neis); + igraph_adjlist_destroy(&allneis); + igraph_vector_destroy(&rank); + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(4); + + if (triples == 0 && mode == IGRAPH_TRANSITIVITY_ZERO) { + *res = 0; + } else { + *res = triangles / triples * 2.0; + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vit_t vit; + igraph_integer_t nodes_to_calc; + igraph_vector_int_t *adj1, *adj2; + igraph_vector_int_t neis; + igraph_vector_t actw; + igraph_lazy_inclist_t incident; + igraph_integer_t 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_int_init(&neis, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t adjlen1, adjlen2, j, k; + igraph_real_t triples, triangles; + + IGRAPH_ALLOW_INTERRUPTION(); + + adj1 = igraph_lazy_inclist_get(&incident, node); + IGRAPH_CHECK_OOM(adj1, "Failed to query incident edges."); + adjlen1 = igraph_vector_int_size(adj1); + /* Mark the neighbors of the node */ + for (j = 0; j < adjlen1; j++) { + igraph_integer_t edge = VECTOR(*adj1)[j]; + igraph_integer_t 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++) { + igraph_integer_t edge1 = VECTOR(*adj1)[j]; + igraph_real_t weight1 = VECTOR(*weights)[edge1]; + igraph_integer_t v = IGRAPH_OTHER(graph, edge1, node); + adj2 = igraph_lazy_inclist_get(&incident, v); + IGRAPH_CHECK_OOM(adj2, "Failed to query incident edges."); + adjlen2 = igraph_vector_int_size(adj2); + for (k = 0; k < adjlen2; k++) { + igraph_integer_t edge2 = VECTOR(*adj2)[k]; + igraph_integer_t 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_int_destroy(&neis); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_transitivity_barrat4( + const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + igraph_transitivity_mode_t mode) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t order; + igraph_vector_int_t degree; + igraph_vector_t strength; + igraph_vector_t rank; + igraph_integer_t maxdegree; + igraph_inclist_t incident; + igraph_vector_int_t neis; + igraph_vector_int_t *adj1, *adj2; + igraph_vector_t actw; + igraph_integer_t 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_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes); + + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_LOOPS)); + maxdegree = igraph_vector_int_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); + + IGRAPH_CHECK(igraph_strength(graph, &strength, 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)[ 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_int_init(&neis, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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--) { + igraph_integer_t adjlen1, adjlen2; + igraph_real_t triples; + igraph_integer_t node = VECTOR(order)[nn]; + + IGRAPH_ALLOW_INTERRUPTION(); + + adj1 = igraph_inclist_get(&incident, node); + adjlen1 = igraph_vector_int_size(adj1); + triples = VECTOR(strength)[node] * (adjlen1 - 1) / 2.0; + /* Mark the neighbors of the node */ + for (i = 0; i < adjlen1; i++) { + igraph_integer_t edge = VECTOR(*adj1)[i]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, node); + VECTOR(neis)[nei] = node + 1; + VECTOR(actw)[nei] = VECTOR(*weights)[edge]; + } + + for (i = 0; i < adjlen1; i++) { + igraph_integer_t edge1 = VECTOR(*adj1)[i]; + igraph_real_t weight1 = VECTOR(*weights)[edge1]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge1, node); + igraph_integer_t 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++) { + igraph_integer_t edge2 = VECTOR(*adj2)[j]; + igraph_real_t weight2 = VECTOR(*weights)[edge2]; + igraph_integer_t 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_int_destroy(&neis); + igraph_inclist_destroy(&incident); + igraph_vector_destroy(&rank); + igraph_vector_int_destroy(°ree); + igraph_vector_destroy(&strength); + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_transitivity_barrat + * \brief Weighted local transitivity of some vertices, 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 equation (5) in Alain Barrat, Marc Barthelemy, Romualdo + * Pastor-Satorras, Alessandro Vespignani: The architecture of complex + * weighted networks, Proc. Natl. Acad. Sci. USA 101, 3747 (2004) at + * https://doi.org/10.1073/pnas.0400087101 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. + */ + +igraph_error_t 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) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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 (%" IGRAPH_PRId ") not equal to " + "number of edges (%" IGRAPH_PRId ").", 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_is_directed(graph)) { + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + IGRAPH_CHECK(igraph_has_mutual(graph, &has_multiple, false)); + } + if (has_multiple) { + IGRAPH_ERROR("Barrat's weighted transitivity measure works only if the graph has no multi-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, weights, mode); + } else { + return igraph_i_transitivity_barrat1(graph, res, vids, weights, mode); + } +} diff --git a/src/properties/triangles_template.h b/src/properties/triangles_template.h new file mode 100644 index 0000000..8b83da7 --- /dev/null +++ b/src/properties/triangles_template.h @@ -0,0 +1,141 @@ +/* -*- 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 + +igraph_integer_t no_of_nodes = igraph_vcount(graph); +igraph_integer_t node, i, j, nn; +igraph_adjlist_t allneis; +igraph_vector_int_t *neis1, *neis2; +igraph_integer_t neilen1, neilen2; +igraph_integer_t *neis; +igraph_integer_t maxdegree; + +#ifdef TRANSIT_TRIEDGES +igraph_integer_t deg1; +#endif + +igraph_vector_int_t order; +igraph_vector_int_t rank; +igraph_vector_int_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_FINALLY(&order, no_of_nodes); +IGRAPH_VECTOR_INT_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 = igraph_vector_int_max(°ree) + 1; +IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); +IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); +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, igraph_integer_t); +if (neis == 0) { + IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ +} +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 = VECTOR(degree)[node]; +#endif + + /* Mark the neighbors of the node */ + for (i = 0; i < neilen1; i++) { + neis[ VECTOR(*neis1)[i] ] = node + 1; + } + + for (i = 0; i < neilen1; i++) { + igraph_integer_t nei = VECTOR(*neis1)[i]; + neis2 = igraph_adjlist_get(&allneis, nei); + neilen2 = igraph_vector_int_size(neis2); + for (j = 0; j < neilen2; j++) { + igraph_integer_t nei2 = 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_int_destroy(°ree); +igraph_vector_int_destroy(&order); +IGRAPH_FINALLY_CLEAN(5); + +#ifdef TRANSIT_TRIEDGES +#undef TRANSIT_TRIEDGES +#endif diff --git a/src/properties/triangles_template1.h b/src/properties/triangles_template1.h new file mode 100644 index 0000000..25329ea --- /dev/null +++ b/src/properties/triangles_template1.h @@ -0,0 +1,97 @@ +/* -*- 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 + +*/ + +igraph_integer_t no_of_nodes = igraph_vcount(graph); +igraph_vit_t vit; +igraph_integer_t nodes_to_calc; +igraph_vector_int_t *neis1, *neis2; +igraph_real_t triangles; +igraph_integer_t i, j, k; +igraph_integer_t neilen1, neilen2; +igraph_integer_t *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, igraph_integer_t); +if (neis == 0) { + IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ +} +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++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + + IGRAPH_ALLOW_INTERRUPTION(); + + neis1 = igraph_lazy_adjlist_get(&adjlist, node); + IGRAPH_CHECK_OOM(neis1, "Failed to query neighbors."); + neilen1 = igraph_vector_int_size(neis1); + for (j = 0; j < neilen1; j++) { + neis[ VECTOR(*neis1)[j] ] = i + 1; + } + triangles = 0; + + for (j = 0; j < neilen1; j++) { + igraph_integer_t v = VECTOR(*neis1)[j]; + neis2 = igraph_lazy_adjlist_get(&adjlist, v); + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + neilen2 = igraph_vector_int_size(neis2); + for (k = 0; k < neilen2; k++) { + igraph_integer_t v2 = 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/random/random.c b/src/random/random.c new file mode 100644 index 0000000..459af9c --- /dev/null +++ b/src/random/random.c @@ -0,0 +1,2240 @@ +/* -*- 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 "core/interruption.h" +#include "core/math.h" +#include "math/safe_intop.h" +#include "random/random_internal.h" + +#include "config.h" /* IGRAPH_THREAD_LOCAL, HAVE___UINT128_T, HAVE__UMUL128 */ + +#if defined(HAVE__UMUL128) || defined(HAVE___UMULH) +#include /* _umul128() or __umulh() are defined in intrin.h */ +#endif + +#include +#include +#include /* DBL_MANT_DIG */ + +/** + * \section about_rngs + * + *
+ * About random numbers in igraph + * + * + * Some algorithms in igraph, such as sampling from random graph models, + * require random number generators (RNGs). igraph includes a flexible + * RNG framework that allows hooking up arbitrary random number generators, + * and comes with several ready-to-use generators. This framework is used + * in igraph's high-level interfaces to integrate with the host language's + * own RNG. + * + *
+ * + */ + +/** + * \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 + * + *
+ * + *
+ */ + +/* ------------------------------------ */ + +/** + * \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. + */ + +extern IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default; /* defined in rng_pcg32.c */ + +/** + * \function igraph_rng_set_default + * \brief Set the default igraph random number generator. + * + * This function \em copies the internal structure of the given \type igraph_rng_t + * object to igraph's internal default RNG structure. The structure itself + * contains two pointers only, one to the "methods" of the RNG and one to the + * memory buffer holding the internal state of the RNG. This means that if you + * keep on generating random numbers from the RNG after setting it as the + * default, it will affect the state of the default RNG as well because the two + * share the same state pointer. However, do \em not expect + * \ref igraph_rng_default() to return the same pointer as the one you passed + * in here - the state is shared, but the entire structure is not. + * + * \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 \ref igraph_rng_set_default() + */ + +igraph_rng_t *igraph_rng_default(void) { + return &igraph_i_rng_default; +} + +/* ------------------------------------ */ + +static igraph_uint_t igraph_i_rng_get_random_bits(igraph_rng_t *rng, uint8_t bits); +static uint64_t igraph_i_rng_get_random_bits_uint64(igraph_rng_t *rng, uint8_t bits); + +static igraph_uint_t igraph_i_rng_get_uint(igraph_rng_t *rng); +static igraph_uint_t igraph_i_rng_get_uint_bounded(igraph_rng_t *rng, igraph_uint_t range); + +static uint32_t igraph_i_rng_get_uint32(igraph_rng_t *rng); +static uint32_t igraph_i_rng_get_uint32_bounded(igraph_rng_t *rng, uint32_t range); + +#if IGRAPH_INTEGER_SIZE == 64 +static uint64_t igraph_i_rng_get_uint64(igraph_rng_t *rng); +static uint64_t igraph_i_rng_get_uint64_bounded(igraph_rng_t *rng, uint64_t range); +#endif + +static double igraph_i_norm_rand(igraph_rng_t *rng); +static double igraph_i_exp_rand(igraph_rng_t *rng); +static double igraph_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp); +static double igraph_i_rexp(igraph_rng_t *rng, double rate); +static double igraph_i_rgamma(igraph_rng_t *rng, double shape, double scale); +static double igraph_i_rpois(igraph_rng_t *rng, double rate); + +/** + * \function igraph_rng_init + * \brief Initializes 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, such as \ref igraph_rngtype_mt19937, + * \ref igraph_rngtype_glibc2, \ref igraph_rngtype_pcg32 or + * \ref igraph_rngtype_pcg64. + * \return Error code. + */ + +igraph_error_t 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 Deallocates 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 Seeds 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. + */ +igraph_error_t igraph_rng_seed(igraph_rng_t *rng, igraph_uint_t seed) { + const igraph_rng_type_t *type = rng->type; + IGRAPH_CHECK(type->seed(rng->state, seed)); + rng->is_seeded = true; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_rng_bits + * \brief The number of random bits that a random number generator can produces in a single round. + * + * \param rng The RNG. + * \return The number of random bits that can be generated in a single round + * with the RNG. + * + * Time complexity: O(1). + */ +igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng) { + return rng->type->bits; +} + +/** + * \function igraph_rng_max + * \brief The maximum possible integer for a random number generator. + * + * Note that this number is only for informational purposes; it returns the + * maximum possible integer that can be generated with the RNG with a single + * call to its internals. It is derived directly from the number of random + * \em bits that the RNG can generate in a single round. When this is smaller + * than what would be needed by other RNG functions like \ref igraph_rng_get_integer(), + * igraph will call the RNG multiple times to generate more random bits. + * + * \param rng The RNG. + * \return The largest possible integer that can be generated in a single round + * with the RNG. + * + * Time complexity: O(1). + */ + +igraph_uint_t igraph_rng_max(const igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; +#if IGRAPH_INTEGER_SIZE == 64 + return (type->bits >= 64) ? 0xFFFFFFFFFFFFFFFFULL : ((1ULL << type->bits) - 1); +#else + return (type->bits >= 32) ? 0xFFFFFFFFUL : ((1ULL << type->bits) - 1); +#endif +} + +/** + * \function igraph_rng_name + * \brief 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. + * + * Time complexity: O(1). + */ + +const char *igraph_rng_name(const igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; + return type->name; +} + +/** + * Generates a given number of random bits, possibly invoking the underlying + * RNG multiple times if needed, and returns the result in an \c igraph_uint_t . + * + * \param rng The RNG. + * \param bits The number of random bits needed. Must be smaller than or equal + * to the size of the \c igraph_uint_t data type. Passing a value larger + * than the size of \c igraph_uint_t will throw away random bits except + * the last few that are needed to fill an \c igraph_uint_t . + * \return The random bits, packed into the low bits of an \c igraph_uint_t . + * The upper, unused bits of \c igraph_uint_t will be set to zero. + */ +static igraph_uint_t igraph_i_rng_get_random_bits(igraph_rng_t *rng, uint8_t bits) { + const igraph_rng_type_t *type = rng->type; + igraph_integer_t rng_bitwidth = igraph_rng_bits(rng); + igraph_uint_t result; + + if (rng_bitwidth >= bits) { + /* keep the high bits as RNGs sometimes tend to have lower entropy in + * low bits than in high bits */ + result = type->get(rng->state) >> (rng_bitwidth - bits); + } else { + result = 0; + do { + result = (result << rng_bitwidth) + type->get(rng->state); + bits -= rng_bitwidth; + } while (bits > rng_bitwidth); + + /* and now the last piece */ + result = (result << bits) + (type->get(rng->state) >> (rng_bitwidth - bits)); + } + + return result; +} + +/** + * Generates a given number of random bits, possibly invoking the underlying + * RNG multiple times if needed, and returns the result in an \c uint64_t . + * + * Prefer \c igraph_i_rng_get_random_bits() if you know that you need at most + * 32 bits due to the type of the return value. This function might perform + * worse on 32-bit platforms because the result is always 64 bits. + * + * \param rng The RNG. + * \param bits The number of random bits needed. Must be smaller than or equal + * to the size of the \c uint64_t data type. Passing a value larger + * than the size of \c uint64_t will throw away random bits except + * the last few that are needed to fill an \c uint64_t . + * \return The random bits, packed into the low bits of an \c uint64_t . + * The upper, unused bits of \c uint64_t will be set to zero. + */ +static uint64_t igraph_i_rng_get_random_bits_uint64(igraph_rng_t *rng, uint8_t bits) { + const igraph_rng_type_t *type = rng->type; + igraph_integer_t rng_bitwidth = igraph_rng_bits(rng); + uint64_t result; + + if (rng_bitwidth >= bits) { + /* keep the high bits as RNGs sometimes tend to have lower entropy in + * low bits than in high bits */ + result = type->get(rng->state) >> (rng_bitwidth - bits); + } else { + result = 0; + do { + result = (result << rng_bitwidth) + type->get(rng->state); + bits -= rng_bitwidth; + } while (bits > rng_bitwidth); + + /* and now the last piece */ + result = (result << bits) + (type->get(rng->state) >> (rng_bitwidth - bits)); + } + + return result; +} + +/** + * Generates a random integer in the full range of the \c igraph_uint_t + * data type. + * + * \param rng The RNG. + * \return The random integer. + */ +static igraph_uint_t igraph_i_rng_get_uint(igraph_rng_t *rng) { + return igraph_i_rng_get_random_bits(rng, sizeof(igraph_uint_t) * 8); +} + +/** + * Generates a random integer in the full range of the \c uint32_t + * data type. + * + * \param rng The RNG. + * \return The random integer. + */ +static uint32_t igraph_i_rng_get_uint32(igraph_rng_t *rng) { + return igraph_i_rng_get_random_bits(rng, 32); +} + +/** + * Generates a random integer in the range [0; range) (upper bound exclusive), + * restricted to at most 32 bits. + * + * \param rng The RNG. + * \param range The upper bound (exclusive). + * \return The random integer. + */ +static uint32_t igraph_i_rng_get_uint32_bounded(igraph_rng_t *rng, uint32_t range) { + /* Debiased integer multiplication -- Lemire's method + * from https://www.pcg-random.org/posts/bounded-rands.html */ + uint32_t x, l, t = (-range) % range; + uint64_t m; + do { + x = igraph_i_rng_get_uint32(rng); + m = (uint64_t)(x) * (uint64_t)(range); + l = (uint32_t)m; + } while (l < t); + return m >> 32; +} + +#if IGRAPH_INTEGER_SIZE == 64 +/** + * Generates a random integer in the full range of the \c uint64_t + * data type. + * + * \param rng The RNG. + * \param range The upper bound (inclusive). + * \return The random integer. + */ +static uint64_t igraph_i_rng_get_uint64(igraph_rng_t *rng) { + return igraph_i_rng_get_random_bits(rng, 64); +} + +#if !defined(HAVE___UINT128_T) +static uint64_t igraph_i_umul128(uint64_t a, uint64_t b, uint64_t *hi) { +#if defined(HAVE__UMUL128) + /* MSVC has _umul128() on x64 but not on arm64 */ + return _umul128(a, b, hi); +#elif defined(HAVE___UMULH) + /* MSVC has __umulh() on arm64 */ + *hi = __umulh(a, b); + return a*b; +#else + /* Portable but slow fallback implementation of unsigned + * 64-bit multiplication obtaining a 128-bit result. + * Based on https://stackoverflow.com/a/28904636/695132 + */ + + uint64_t a_lo = (uint32_t) a; + uint64_t a_hi = a >> 32; + uint64_t b_lo = (uint32_t) b; + uint64_t b_hi = b >> 32; + + uint64_t a_x_b_hi = a_hi * b_hi; + uint64_t a_x_b_mid = a_hi * b_lo; + uint64_t b_x_a_mid = b_hi * a_lo; + uint64_t a_x_b_lo = a_lo * b_lo; + + uint64_t carry_bit = ((uint64_t) (uint32_t) a_x_b_mid + + (uint64_t) (uint32_t) b_x_a_mid + + (a_x_b_lo >> 32) ) >> 32; + + *hi = a_x_b_hi + + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + + carry_bit; + + return a*b; +#endif +} +#endif /* !defined(HAVE___UINT128_T) */ + +/** + * Generates a random integer in the range [0; range) (upper bound exclusive), + * restricted to at most 64 bits. + * + * \param rng The RNG. + * \param range The upper bound (exclusive). + * \return The random integer. + */ +static uint64_t igraph_i_rng_get_uint64_bounded(igraph_rng_t *rng, uint64_t range) { + /* Debiased integer multiplication -- Lemire's method + * from https://www.pcg-random.org/posts/bounded-rands.html */ + uint64_t x, l, t = (-range) % range; +#if defined(HAVE___UINT128_T) + /* gcc and clang have __uint128_t */ + __uint128_t m; + do { + x = igraph_i_rng_get_uint64(rng); + m = (__uint128_t)(x) * (__uint128_t)(range); + l = (uint64_t)m; + } while (l < t); + return m >> 64; +#else + uint64_t hi; + do { + x = igraph_i_rng_get_uint64(rng); + l = igraph_i_umul128(x, range, &hi); + } while (l < t); + return hi; +#endif +} + +#endif /* IGRAPH_INTEGER_SIZE == 64 */ + +/** + * Generates a random integer in the range [0; range) (upper bound exclusive). + * + * \param rng The RNG. + * \param range The upper bound (exclusive). + * \return The random integer. + */ +static igraph_uint_t igraph_i_rng_get_uint_bounded(igraph_rng_t *rng, igraph_uint_t range) { + /* We must make this function behave the same way for range < 2^32 so igraph + * behaves the same way on 32-bit and 64-bit platforms as long as we stick + * to integers less than 2^32. This is to ensure that the unit tests are + * consistent */ + +#if IGRAPH_INTEGER_SIZE == 32 + return igraph_i_rng_get_uint32_bounded(rng, range); +#else + if (range <= UINT32_MAX) { + return igraph_i_rng_get_uint32_bounded(rng, range); + } else { + return igraph_i_rng_get_uint64_bounded(rng, range); + } +#endif +} + +/** + * \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: O(log2(h-l) / bits) where bits is the value of + * \ref igraph_rng_bits(rng). + */ + +igraph_integer_t igraph_rng_get_integer( + igraph_rng_t *rng, igraph_integer_t l, igraph_integer_t h +) { + const igraph_rng_type_t *type = rng->type; + igraph_uint_t range; + + assert(h >= l); + + if (h == l) { + return l; + } + + if (type->get_int) { + return type->get_int(rng->state, l, h); + } + + if (IGRAPH_UNLIKELY(l == IGRAPH_INTEGER_MIN && h == IGRAPH_INTEGER_MAX)) { + /* Full uint range is needed, we can just grab a random number from + * the uint range and cast it to a signed integer */ + return (igraph_integer_t) igraph_i_rng_get_uint(rng); + } else if (l >= 0 || h < 0) { + /* this is okay, (h - l) will not overflow an igraph_integer_t */ + range = (igraph_uint_t)(h - l) + 1; + } else { + /* (h - l) could potentially overflow so we need to play it safe. If we + * are here, l < 0 and h >= 0 so we can cast -l into an igraph_uint_t + * safely and do the subtraction that way */ + range = ((igraph_uint_t)(h)) + ((igraph_uint_t)(-l)) + 1; + } + + return l + igraph_i_rng_get_uint_bounded(rng, range); +} + +/** + * \function igraph_rng_get_normal + * \brief Samples from a normal distribution. + * + * Generates random variates from a normal distribution with probability + * density + * + *
+ * exp( -(x - m)^2 / (2 s^2) ). + * + * \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 The 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_i_norm_rand(rng) * s + m; + } +} + +/** + * \function igraph_rng_get_unif + * \brief Samples real numbers from a given interval. + * + * Generates uniformly distributed real numbers from the [l, h) + * half-open 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) { + assert(h >= l); + + if (l == h) return h; + + /* Ensure that 'h' is never produced due to numerical roundoff errors, except when l == h. */ + igraph_real_t r; + do { + r = igraph_rng_get_unif01(rng) * (h - l) + l; + } while (IGRAPH_UNLIKELY(r == h)); + return r; +} + +/** + * \function igraph_rng_get_unif01 + * \brief Samples uniformly from the unit interval. + * + * Generates uniformly distributed real numbers from the [0, 1) + * half-open 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 { + /* We extract 52 random bits from a 64-bit uint and fill that directly + * into the mantissa of a double, bit-by-bit, clear the sign bit and + * set the exponent to 2^0. This way we get a 52-bit random double + * between 1 (inclusive) and 2 (exclusive), uniformly distributed. + * Then we subtract 1 to arrive at the [0; 1) interval. This is fast + * but we lose one bit of precision as there are 2^53 possible doubles + * between 0 and 1. */ + union { + uint64_t as_uint64_t; + double as_double; + } value; + value.as_uint64_t = + (igraph_i_rng_get_random_bits_uint64(rng, 52) & 0xFFFFFFFFFFFFFull) | 0x3FF0000000000000ull; + return value.as_double - 1.0; + } +} + +/** + * \function igraph_rng_get_geom + * \brief Samples from a geometric distribution. + * + * Generates random variates from a geometric distribution. The number \c k is + * generated with probability + * + * + * (1 - p)^k p, k = 0, 1, 2, .... + * + * \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 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 (!isfinite(p) || p <= 0 || p > 1) { + return IGRAPH_NAN; + } + if (type->get_geom) { + return type->get_geom(rng->state, p); + } else { + return igraph_rng_get_pois(rng, igraph_i_exp_rand(rng) * ((1 - p) / p)); + } +} + +/** + * \function igraph_rng_get_binom + * \brief Samples from a binomial distribution. + * + * Generates random variates from a binomial distribution. The number \c k is generated + * with probability + * + * + * (n \choose k) p^k (1-p)^(n-k), k = 0, 1, ..., n. + * + * \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 RNG. + */ + +igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, igraph_integer_t 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_i_rbinom(rng, n, p); + } +} + +/** + * \function igraph_rng_get_gamma + * \brief Samples from a gamma distribution. + * + * Generates random variates from a gamma distribution with probability + * density proportional to + * + * + * x^(shape-1) exp(-x / scale). + * + * \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 the 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_i_rgamma(rng, shape, scale); + } +} + +/** + * \function igraph_rng_get_exp + * \brief Samples from an exponential distribution. + * + * Generates random variates from an exponential distribution with probability + * density proportional to + * + * + * exp(-rate x). + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param rate Rate parameter. + * \return The generated sample. + * + * Time complexity: depends on the RNG. + */ + +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_i_rexp(rng, rate); + } +} + +/** + * \function igraph_rng_get_pois + * \brief Samples from a Poisson distribution. + * + * Generates random variates from a Poisson distribution. The number \c k is generated + * with probability + * + * + * rate^k * exp(-rate) / k!, k = 0, 1, 2, .... + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param rate The rate parameter of the Poisson distribution. Must not be negative. + * \return The generated geometrically distributed random number. + * + * Time complexity: depends on the RNG. + */ + +igraph_real_t igraph_rng_get_pois(igraph_rng_t *rng, igraph_real_t rate) { + const igraph_rng_type_t *type = rng->type; + if (isnan(rate) || rate < 0) { + return IGRAPH_NAN; + } else if (rate == 0) { + return 0; + } else if (type->get_pois) { + return type->get_pois(rng->state, rate); + } else { + return igraph_i_rpois(rng, rate); + } +} + + +/** + * \ingroup internal + * + * This function appends the rest of the needed random numbers to the + * result vector. It is Algoirthm A in Vitter's paper. + */ + +static void igraph_i_random_sample_alga(igraph_vector_int_t *res, + igraph_integer_t l, igraph_integer_t h, + igraph_integer_t length) { + /* Vitter: Variables V, quot, Nreal, and top are of type real */ + + igraph_integer_t N = h - l + 1; + igraph_integer_t n = length; + + igraph_real_t top = N - n; + igraph_real_t Nreal = N; + igraph_integer_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_int_push_back(res, l); /* allocated */ + Nreal = -1.0 + Nreal; n = -1 + n; + } + + S = trunc(round(Nreal) * RNG_UNIF01()); + l += S + 1; + igraph_vector_int_push_back(res, l); /* allocated */ +} + +/** + * \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. + * + * + * Reference: + * + * + * J. S. Vitter. An efficient algorithm for sequential random sampling. + * ACM Transactions on Mathematical Software, 13(1):58--67, 1987. + * https://doi.org/10.1145/23002.23003 + * + * \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. + * \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. + * \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). + * + * \example examples/simple/igraph_random_sample.c + */ + +igraph_error_t igraph_random_sample(igraph_vector_int_t *res, igraph_integer_t l, igraph_integer_t h, + igraph_integer_t length) { + igraph_integer_t N; /* := h - l + 1 */ + IGRAPH_SAFE_ADD(h, -l, &N); + IGRAPH_SAFE_ADD(N, 1, &N); + + igraph_integer_t n = length; + + 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_integer_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_integer_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_int_resize(res, 1)); + VECTOR(*res)[0] = l; + return IGRAPH_SUCCESS; + } + if (length == 0) { + igraph_vector_int_clear(res); + return IGRAPH_SUCCESS; + } + if (length == N) { + IGRAPH_CHECK(igraph_vector_int_resize(res, length)); + for (igraph_integer_t i = 0; i < length; i++) { + VECTOR(*res)[i] = l++; + } + return IGRAPH_SUCCESS; + } + + igraph_vector_int_clear(res); + IGRAPH_CHECK(igraph_vector_int_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_int_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) { + igraph_i_random_sample_alga(res, l + 1, h, n); + } else { + S = floor(N * Vprime); + l += S + 1; + igraph_vector_int_push_back(res, l); /* allocated */ + } + + RNG_END(); + + return IGRAPH_SUCCESS; +} + +static void igraph_i_random_sample_alga_real(igraph_vector_t *res, + igraph_real_t l, igraph_real_t h, + igraph_real_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 = trunc(round(Nreal) * RNG_UNIF01()); + l += S + 1; + igraph_vector_push_back(res, l); /* allocated */ +} + +/** + * \ingroup nongraph + * \function igraph_random_sample_real + * \brief Generates an increasing random sequence of integers (igraph_real_t version). + * + * This function is the 'real' version of \ref igraph_random_sample(), and was added + * so \ref igraph_erdos_renyi_game_gnm() and related functions can use a random sample + * of doubles instead of integers to prevent overflows on systems with 32-bit + * \type igraph_integer_t. + * + * \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. + */ + +igraph_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, + igraph_real_t h, igraph_integer_t length) { + /* This function is the 'real' version of igraph_random_sample, and was added + * so erdos_renyi_game_gnm can use a random sample of doubles instead of integers + * to prevent overflows on systems with 32-bits igraph_integer_t. + */ + igraph_real_t N = h - l + 1; + igraph_real_t n = length; + + 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; + int iter = 0; + + /* 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); + } + + /* ensure that we work in the range where igraph_real_t can represent integers exactly */ + if (h > IGRAPH_MAX_EXACT_REAL || l < -IGRAPH_MAX_EXACT_REAL || N > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Sampling interval too large.", IGRAPH_EOVERFLOW); + } + + /* 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) { + IGRAPH_CHECK(igraph_vector_resize(res, length)); + for (igraph_integer_t 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 (++iter >= (1 << 14)) { + iter = 0; + IGRAPH_ALLOW_INTERRUPTION(); + } + } + + if (n > 1) { + igraph_i_random_sample_alga_real(res, l + 1, h, n); + } else { + S = floor(N * Vprime); + l += S + 1; + igraph_vector_push_back(res, l); /* allocated */ + } + + RNG_END(); + + return IGRAPH_SUCCESS; +} + +/* + * 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 + * + */ + +/* The ISNAN macro is used in some of the code borrowed from R below. */ +#define ISNAN isnan + +/* Indicates that we use systems which support NaN values. */ +#define IEEE_754 1 + +/* 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; } + +#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; \ + } + +static double igraph_i_qnorm5(double p, double mu, double sigma, igraph_bool_t lower_tail, igraph_bool_t 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 igraph_integer_t imax2(igraph_integer_t x, igraph_integer_t y) { + return (x < y) ? y : x; +} + +static igraph_integer_t imin2(igraph_integer_t x, igraph_integer_t y) { + return (x < y) ? x : y; +} + +static double igraph_i_norm_rand(igraph_rng_t *rng) { + double r; + + /* Use the inversion method based on uniform variates from (0, 1). + * We exclude 0.0 as it would lead to generating -infinity. + * It is assumed that unif01() provides sufficient accuracy. + * A resolution of 2^-32 may not be sufficient. igraph's default + * implementaton provides an accuracy of 2^-52. + */ + do { + r = igraph_rng_get_unif01(rng); + } while (r == 0.0); + + return igraph_i_qnorm5(r, 0.0, 1.0, true, false); +} + +/* + * The following function is igraph code (not R / Mathlib). + * + * We use simple inverse transform sampling, with the assumption that the + * quality/resolution of uniform variates is high (52 bits in the default + * implementation). The quantile function is -log(1 - r) but given that + * r is sampled uniformly form the unit interval, -log(r) is equivalent. + * r = 0 is disallowed as it would yield infinity. + */ + +static double igraph_i_exp_rand(igraph_rng_t *rng) { + igraph_real_t r = igraph_rng_get_unif01(rng); + if (r == 0.0) r = 1.0; /* sample from (0, 1] instead of [0, 1) */ + return -log(r); +} + +/* + * 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_i_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; + static IGRAPH_THREAD_LOCAL igraph_integer_t 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 (!isfinite(mu) || mu < 0) { + 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, (igraph_integer_t) 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_i_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_i_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 + copysign(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 + +/* This is from nmath/rbinom.c */ + +#define repeat for(;;) + +static double igraph_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp) { + + 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 igraph_integer_t nsave = -1; + static IGRAPH_THREAD_LOCAL igraph_integer_t 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; + igraph_integer_t i, ix, k; + + if (!isfinite(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; + /* Note (igraph): Original code used a cast to (int) for rounding. However, + * the max npq = n*p*(1-p) value is 0.25*n, thus 2.195 * sqrt(npq) may be + * as large as 1.0975 * sqrt(n). This is not representable on a 32-bit signed + * integer when n is a 64-bit signed integer. Thus we use trunc() instead. */ + p1 = trunc(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 = imaxabs(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_i_rexp(igraph_rng_t *rng, double rate) { + igraph_real_t scale = 1.0 / rate; + if (!isfinite(scale) || scale <= 0.0) { + if (scale == 0.0) { + return 0.0; + } + return IGRAPH_NAN; + } + return scale * igraph_i_exp_rand(rng); +} + +/* 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_i_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: */ + static IGRAPH_THREAD_LOCAL double aa = 0.; + static IGRAPH_THREAD_LOCAL double aaa = 0.; + static IGRAPH_THREAD_LOCAL double s, s2, d; /* no. 1 (step 1) */ + static IGRAPH_THREAD_LOCAL double q0, b, si, c;/* no. 2 (step 4) */ + + double e, p, q, r, t, u, v, w, x, ret_val; + + if (!isfinite(a) || !isfinite(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_i_exp_rand(rng) >= (1.0 - a) * log(x)) { + break; + } + } else { + x = exp(log(p) / a); + if (igraph_i_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_i_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_i_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; +} + +igraph_error_t 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_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)); + + for (igraph_integer_t i = 0; i < len; i++) { + VECTOR(*result)[i] = igraph_rng_get_gamma(rng, VECTOR(*alpha)[i], 1.0); + sum += VECTOR(*result)[i]; + } + for (igraph_integer_t i = 0; i < len; i++) { + VECTOR(*result)[i] /= sum; + } + + return IGRAPH_SUCCESS; +} diff --git a/src/random/random_internal.h b/src/random/random_internal.h new file mode 100644 index 0000000..25110da --- /dev/null +++ b/src/random/random_internal.h @@ -0,0 +1,38 @@ +/* -*- 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_RANDOM_INTERNAL_H +#define IGRAPH_RANDOM_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +igraph_error_t igraph_random_sample_real( + igraph_vector_t *res, igraph_real_t l, igraph_real_t h, + igraph_integer_t length); + +__END_DECLS + +#endif diff --git a/src/random/rng_glibc2.c b/src/random/rng_glibc2.c new file mode 100644 index 0000000..968f5d4 --- /dev/null +++ b/src/random/rng_glibc2.c @@ -0,0 +1,144 @@ +/* + 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, see . +*/ + +#include "igraph_random.h" + +#include "igraph_memory.h" +#include "igraph_types.h" + +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; + + /* The original implementation used x[*i] += x[*j] here. Considering that + * x is signed, this is undefined behaviour according to the C standard. + * Therefore, we temporarily cast to unsigned long int to achieve what the + * original intention was */ + x[*i] = ((unsigned long int)x[*i]) + ((unsigned long int)x[*j]); + k = (x[*i] >> 1) & 0x7FFFFFFF; + + (*i)++; + if (*i == n) { + *i = 0; + } + + (*j)++ ; + if (*j == n) { + *j = 0; + } + + return k; +} + +static igraph_uint_t 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); +} + +/* 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] = s ; + } +} + +static igraph_error_t igraph_rng_glibc2_seed(void *vstate, igraph_uint_t 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, (unsigned long) seed); + + state->i = 3; + state->j = 0; + + for (i = 0; i < 10 * 31; i++) { + igraph_rng_glibc2_get(state); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_rng_glibc2_init(void **state) { + igraph_i_rng_glibc2_state_t *st; + + st = IGRAPH_CALLOC(1, igraph_i_rng_glibc2_state_t); + IGRAPH_CHECK_OOM(st, "Cannot initialize GNU libc 2 RNG."); + (*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", + /* bits= */ 31, + /* init= */ igraph_rng_glibc2_init, + /* destroy= */ igraph_rng_glibc2_destroy, + /* seed= */ igraph_rng_glibc2_seed, + /* get= */ igraph_rng_glibc2_get, + /* get_int= */ NULL, + /* get_real= */ NULL, + /* get_norm= */ NULL, + /* get_geom= */ NULL, + /* get_binom= */ NULL, + /* get_exp= */ NULL, + /* get_gamma= */ NULL, + /* get_pois= */ NULL +}; diff --git a/src/random/rng_mt19937.c b/src/random/rng_mt19937.c new file mode 100644 index 0000000..adff540 --- /dev/null +++ b/src/random/rng_mt19937.c @@ -0,0 +1,179 @@ +/* + 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, see . +*/ + +#include "igraph_random.h" + +#include "igraph_memory.h" +#include "igraph_types.h" + +#include /* memset() */ +#include + + +#define N 624 /* Period parameters */ +#define M 397 + +/* most significant w-r bits */ +static const uint32_t UPPER_MASK = UINT32_C(0x80000000); + +/* least significant r bits */ +static const uint32_t LOWER_MASK = UINT32_C(0x7fffffff); + +typedef struct { + uint32_t mt[N]; + int mti; +} igraph_i_rng_mt19937_state_t; + +static igraph_uint_t igraph_rng_mt19937_get(void *vstate) { + igraph_i_rng_mt19937_state_t *state = vstate; + + uint32_t k; + uint32_t *const mt = state->mt; + +#define MAGIC(y) (((y) & 0x1) ? UINT32_C(0x9908b0df) : 0) + + if (state->mti >= N) { + /* generate N words at one time */ + int kk; + + for (kk = 0; kk < N - M; kk++) { + uint32_t y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y); + } + for (; kk < N - 1; kk++) { + uint32_t y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y); + } + + { + uint32_t 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) & UINT32_C(0x9d2c5680); + k ^= (k << 15) & UINT32_C(0xefc60000); + k ^= (k >> 18); + + state->mti++; + + return k; +} + +static igraph_error_t igraph_rng_mt19937_seed(void *vstate, igraph_uint_t 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 & UINT32_C(0xffffffff); + + for (i = 1; i < N; i++) { + /* See Knuth's "Art of Computer Programming" Vol. 2, 3rd + Ed. p.106 for multiplier. */ + state->mt[i] = + (UINT32_C(1812433253) * (state->mt[i - 1] ^ (state->mt[i - 1] >> 30)) + + (uint32_t) i); + state->mt[i] &= UINT32_C(0xffffffff); + } + + state->mti = i; + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_rng_mt19937_init(void **state) { + igraph_i_rng_mt19937_state_t *st; + + st = IGRAPH_CALLOC(1, igraph_i_rng_mt19937_state_t); + IGRAPH_CHECK_OOM(st, "Cannot initialize MT19937 RNG."); + (*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 igraph_rng_mt19937_seed() 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", + /* bits= */ 32, + /* init= */ igraph_rng_mt19937_init, + /* destroy= */ igraph_rng_mt19937_destroy, + /* seed= */ igraph_rng_mt19937_seed, + /* get= */ igraph_rng_mt19937_get, + /* get_int= */ NULL, + /* get_real= */ NULL, + /* get_norm= */ NULL, + /* get_geom= */ NULL, + /* get_binom= */ NULL, + /* get_exp= */ NULL, + /* get_gamma= */ NULL, + /* get_pois= */ NULL +}; + +#undef N +#undef M diff --git a/src/random/rng_pcg32.c b/src/random/rng_pcg32.c new file mode 100644 index 0000000..d821b38 --- /dev/null +++ b/src/random/rng_pcg32.c @@ -0,0 +1,124 @@ +/* + 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, see . +*/ + +#include "igraph_random.h" + +#include "igraph_memory.h" +#include "igraph_types.h" + +#include "pcg/pcg_variants.h" + +#include "config.h" /* IGRAPH_THREAD_LOCAL */ + +/* The original implementation of the 32-bit PCG random number generator in this + * file was obtained from https://github.com/imneme/pcg-c + * + * PCG is dual-licensed under Apache-2.0 and MIT Licenses. MIT is compatible + * with igraph's GPLv2 license. License notices for PCG are to be found in the + * pcg_variants.h header + */ + +static const pcg32_random_t pcg32_initializer = PCG32_INITIALIZER; + +static igraph_uint_t igraph_rng_pcg32_get(void *vstate) { + pcg32_random_t *state = (pcg32_random_t*) vstate; + return pcg32_random_r(state); +} + +static igraph_error_t igraph_rng_pcg32_seed(void *vstate, igraph_uint_t seed) { + pcg32_random_t *state = (pcg32_random_t*) vstate; + + /* PCG32 is seeded by a 64-bit state and a 64-bit sequence number (well, only + * 63 bits are used from the sequence number, though). Since the unified + * igraph RNG seeding interface provides a single igraph_uint_t as the seed, + * we use the seed to fill in the sequence number and use the state from + * PCG32_INITIALIZER */ + if (seed == 0) { + /* If you feel the temptation to unify the two branches by running + * seed = pcg32_initializer.inc >> 1, don't. + * seed is an igraph_uint_t, so it can be 32-bit or 64-bit. + * pcg32_initializer.inc is always 64-bit. + */ + pcg32_srandom_r(state, pcg32_initializer.state, pcg32_initializer.inc >> 1); + } else { + pcg32_srandom_r(state, pcg32_initializer.state, seed); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_rng_pcg32_init(void **state) { + pcg32_random_t *st; + + st = IGRAPH_CALLOC(1, pcg32_random_t); + IGRAPH_CHECK_OOM(st, "Cannot initialize PCG32 RNG."); + (*state) = st; + + igraph_rng_pcg32_seed(st, 0); + + return IGRAPH_SUCCESS; +} + +static void igraph_rng_pcg32_destroy(void *vstate) { + pcg32_random_t *state = (pcg32_random_t*) vstate; + IGRAPH_FREE(state); +} + +/** + * \var igraph_rngtype_pcg32 + * \brief The PCG random number generator (32-bit version). + * + * This is an implementation of the PCG random number generator; see + * https://www.pcg-random.org for more details. This implementation returns + * 32 random bits in a single iteration. + * + * + * The generator was ported from the original source code published by the + * authors at https://github.com/imneme/pcg-c. + */ + +const igraph_rng_type_t igraph_rngtype_pcg32 = { + /* name= */ "PCG32", + /* bits= */ 32, + /* init= */ igraph_rng_pcg32_init, + /* destroy= */ igraph_rng_pcg32_destroy, + /* seed= */ igraph_rng_pcg32_seed, + /* get= */ igraph_rng_pcg32_get, + /* get_int= */ NULL, + /* get_real= */ NULL, + /* get_norm= */ NULL, + /* get_geom= */ NULL, + /* get_binom= */ NULL, + /* get_exp= */ NULL, + /* get_gamma= */ NULL, + /* get_pois= */ NULL +}; + +/***** Default RNG, used upon igraph startup *****/ + +#define addr(a) (&a) + +static pcg32_random_t igraph_i_rng_default_state = PCG32_INITIALIZER; + +IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = { + addr(igraph_rngtype_pcg32), + addr(igraph_i_rng_default_state), + /* is_seeded = */ true +}; + +#undef addr diff --git a/src/random/rng_pcg64.c b/src/random/rng_pcg64.c new file mode 100644 index 0000000..c460b4a --- /dev/null +++ b/src/random/rng_pcg64.c @@ -0,0 +1,138 @@ +/* + 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, see . +*/ + +#include "igraph_random.h" + +#include "igraph_memory.h" +#include "igraph_types.h" + +#include "config.h" + +/* The original implementation of the 64-bit PCG random number generator in this + * file was obtained from https://github.com/imneme/pcg-c + * + * PCG is dual-licensed under Apache-2.0 and MIT Licenses. MIT is compatible + * with igraph's GPLv2 license. License notices for PCG are to be found in the + * pcg_variants.h header + */ + +#if IGRAPH_INTEGER_SIZE == 64 && defined(HAVE___UINT128_T) + +#include "pcg/pcg_variants.h" + +static const pcg64_random_t pcg64_initializer = PCG64_INITIALIZER; + +static igraph_uint_t igraph_rng_pcg64_get(void *vstate) { + pcg64_random_t *state = (pcg64_random_t*) vstate; + return pcg64_random_r(state); +} + +static igraph_error_t igraph_rng_pcg64_seed(void *vstate, igraph_uint_t seed) { + pcg64_random_t *state = (pcg64_random_t*) vstate; + + if (seed == 0) { + seed = (pcg64_initializer.inc >> 1); + } + + /* PCG64 is seeded by a 128-bit state and a 128-bit sequence number (well, only + * 63 bits are used from the sequence number, though). Since the unified + * igraph RNG seeding interface provides a single igraph_uint_t as the seed, + * we use the seed to fill in the sequence number and use the state from + * PCG64_INITIALIZER */ + pcg64_srandom_r(state, pcg64_initializer.state, seed); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_rng_pcg64_init(void **state) { + pcg64_random_t *st; + + st = IGRAPH_CALLOC(1, pcg64_random_t); + IGRAPH_CHECK_OOM(st, "Cannot initialize PCG64 RNG."); + (*state) = st; + + igraph_rng_pcg64_seed(st, 0); + + return IGRAPH_SUCCESS; +} + +static void igraph_rng_pcg64_destroy(void *vstate) { + pcg64_random_t *state = (pcg64_random_t*) vstate; + IGRAPH_FREE(state); +} + +#else + +/* Dummy implementation if the compiler does not support __uint128_t */ + +static igraph_uint_t igraph_rng_pcg64_get(void *vstate) { + IGRAPH_UNUSED(vstate); + return 0; +} + +static igraph_error_t igraph_rng_pcg64_seed(void *vstate, igraph_uint_t seed) { + IGRAPH_UNUSED(vstate); IGRAPH_UNUSED(seed); + IGRAPH_ERROR("64-bit PCG generator needs __uint128_t.", IGRAPH_UNIMPLEMENTED); +} + +static igraph_error_t igraph_rng_pcg64_init(void **state) { + IGRAPH_UNUSED(state); + IGRAPH_ERROR("64-bit PCG generator needs __uint128_t.", IGRAPH_UNIMPLEMENTED); +} + +static void igraph_rng_pcg64_destroy(void *vstate) { + IGRAPH_UNUSED(vstate); +} + +#endif + +/** + * \var igraph_rngtype_pcg64 + * \brief The PCG random number generator (64-bit version). + * + * This is an implementation of the PCG random number generator; see + * https://www.pcg-random.org for more details. This implementation returns + * 64 random bits in a single iteration. It is only available on 64-bit plaforms + * with compilers that provide the __uint128_t type. + * + * + * PCG64 typically provides better performance than PCG32 when sampling floating + * point numbers or very large integers, as it can provide twice as many random + * bits in a single generation round. + * + * + * The generator was ported from the original source code published by the + * authors at https://github.com/imneme/pcg-c. + */ + +const igraph_rng_type_t igraph_rngtype_pcg64 = { + /* name= */ "PCG64", + /* bits= */ 64, + /* init= */ igraph_rng_pcg64_init, + /* destroy= */ igraph_rng_pcg64_destroy, + /* seed= */ igraph_rng_pcg64_seed, + /* get= */ igraph_rng_pcg64_get, + /* get_int= */ NULL, + /* get_real= */ NULL, + /* get_norm= */ NULL, + /* get_geom= */ NULL, + /* get_binom= */ NULL, + /* get_exp= */ NULL, + /* get_gamma= */ NULL, + /* get_pois= */ NULL +}; diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..f0bf439 --- /dev/null +++ b/src/version.c @@ -0,0 +1,57 @@ +/* + IGraph library. + Copyright (C) 2008-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, see . +*/ + +#include "igraph_version.h" + +#include + +static const char *igraph_version_string = IGRAPH_VERSION; + +/** + * \function igraph_version + * \brief 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.9.11" or "0.10.0". This + * string must 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.9.11" this is 0. + * \param minor If not a null pointer, then it is set to the minor + * igraph version. E.g. for version "0.9.11" this is 11. + * \param subminor If not a null pointer, then it is set to the + * subminor igraph version. E.g. for version "0.9.11" this is 11. + * + * \example examples/simple/igraph_version.c + */ + +void igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor) { + int i1, i2, i3; + int *p1 = major ? major : &i1; + int *p2 = minor ? minor : &i2; + int *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); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..ca1cc56 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,1049 @@ +include(test_helpers) +include(benchmark_helpers) + +add_library(test_utilities OBJECT unit/test_utilities.c) +target_link_libraries(test_utilities PRIVATE igraph) +use_all_warnings(test_utilities) + +# Add a compatibility alias to the "test" target so it can also be invoked as +# "make check" - for people who have it in their muscle memories from autotools +add_custom_target(build_tests) +add_custom_target( + check + COMMAND ${CMAKE_CTEST_COMMAND} --progress --output-on-failure -C $ + COMMENT "Executing unit tests..." + USES_TERMINAL +) +add_dependencies(check build_tests) + +# Add a custom target for benchmarks and another one for building them +add_custom_target(build_benchmarks) +add_custom_target( + benchmark + COMMAND true + COMMENT "Running benchmarks..." +) +add_dependencies(benchmark build_benchmarks) + +# Some newer gcc version have --enable-new-dtags on by default. This then leads +# to using RUNPATH instead of RPATH. Since RUNPATH is only considered after +# LD_LIBRARY_PATH, if another version of igraph is installed somewhere it will +# be linked to that library. +include(CheckLinkerFlag) +check_linker_flag(C "-Wl,--enable-new-dtags" HAVE_ENABLE_NEW_DTAGS) +if (HAVE_ENABLE_NEW_DTAGS AND BUILD_SHARED_LIBS) + message(STATUS "Disabling new dtags for testing to use RPATH to ensure the correct library is found.") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-new-dtags") +endif() + +# tutorial examples and other snippets from the documentation +add_examples( + FOLDER examples/tutorial NAMES + tutorial1 + tutorial2 + tutorial3 +) + +# version.at +add_examples( + FOLDER examples/simple NAMES + igraph_version +) + +# types.at +add_examples( + FOLDER examples/simple NAMES + dqueue + igraph_sparsemat + igraph_sparsemat3 + igraph_sparsemat4 + igraph_sparsemat6 + igraph_sparsemat7 + igraph_sparsemat8 + igraph_strvector + igraph_vector_int_list_sort +) + +add_legacy_tests( + FOLDER tests/unit NAMES + bitset + heap + igraph_array + igraph_complex + igraph_psumtree + igraph_sparsemat5 + igraph_sparsemat9 + igraph_sparsemat_droptol + igraph_sparsemat_fkeep + igraph_sparsemat_getelements_sorted + igraph_sparsemat_is_symmetric + igraph_sparsemat_iterator_idx + igraph_sparsemat_minmax + igraph_sparsemat_nonzero_storage + igraph_sparsemat_normalize + igraph_sparsemat_view + igraph_sparsemat_which_minmax + igraph_strvector + igraph_vector_floor + igraph_vector_lex_cmp + matrix + matrix2 + matrix3 + matrix_complex + stack + strvector_set_len_remove_print + vector + vector2 + vector3 + vector4 + vector_list + vector_ptr + vector_ptr_sort_ind + vector_qsort_ind +) + +if ((NOT BUILD_SHARED_LIBS) OR (NOT BLAS_IS_VENDORED AND NOT ARPACK_IS_VENDORED)) + add_legacy_tests( + FOLDER tests/unit NAMES + igraph_sparsemat2 # Uses ARPACK and BLAS functions which are not publicly available when building with internal ARPACK/BLAS + ) +endif() + +add_legacy_tests( + FOLDER tests/unit NAMES + 2wheap + cutheap + d_indheap + marked_queue + gen2wheap + set + trie +) + +# basic.at +add_examples( + FOLDER examples/simple NAMES + creation + igraph_copy + igraph_degree + igraph_delete_edges + igraph_delete_vertices + igraph_get_eid + igraph_get_eids + igraph_is_directed + igraph_neighbors +) + +add_legacy_tests( + FOLDER tests/unit NAMES + adj + igraph_add_edges + igraph_add_vertices + igraph_degree + igraph_delete_edges + igraph_delete_vertices + igraph_edges + igraph_empty + igraph_get_eid + igraph_is_same_graph + igraph_i_incident + igraph_i_neighbors + igraph_neighbors +) + +# iterators.at +add_examples( + FOLDER examples/simple NAMES + igraph_es_pairs + igraph_vs_nonadj + igraph_vs_range + igraph_vs_vector +) + +add_legacy_tests( + FOLDER tests/unit NAMES + edge_selectors + igraph_es_path + igraph_es_all_between + vertex_selectors +) + +# structure_generators.at +add_examples( + FOLDER examples/simple NAMES + igraph_adjacency + igraph_atlas + igraph_barabasi_game + igraph_barabasi_game2 + igraph_create + igraph_degree_sequence_game + igraph_erdos_renyi_game_gnm + igraph_erdos_renyi_game_gnp + igraph_full + igraph_grg_game + igraph_kary_tree + igraph_lcf + igraph_realize_degree_sequence + igraph_regular_tree + igraph_ring + igraph_small + igraph_star + igraph_symmetric_tree + igraph_weighted_adjacency +) + +add_legacy_tests( + FOLDER tests/unit NAMES + constructor-failure + erdos_renyi_game_gnm + erdos_renyi_game_gnp + full + igraph_adjacency + igraph_atlas + igraph_barabasi_aging_game + igraph_barabasi_game + igraph_bipartite_game + igraph_callaway_traits_game + igraph_chung_lu_game + igraph_circulant + igraph_cited_type_game + igraph_citing_cited_type_game + igraph_correlated_game + igraph_correlated_pair_game + igraph_create + igraph_degree_sequence_game + igraph_dot_product_game + igraph_establishment_game + igraph_extended_chordal_ring + igraph_forest_fire_game + igraph_from_prufer + igraph_full_citation + igraph_full_multipartite + igraph_generalized_petersen + igraph_grg_game + igraph_growing_random_game + igraph_hsbm_game + igraph_hsbm_list_game + igraph_k_regular_game + igraph_lastcit_game + igraph_linegraph + igraph_kautz + igraph_preference_game + igraph_perfect + igraph_realize_degree_sequence + igraph_recent_degree_aging_game + igraph_recent_degree_game + igraph_sbm_game + igraph_simple_interconnected_islands_game + igraph_square_lattice + igraph_static_power_law_game + igraph_tree_from_parent_vector + igraph_trussness + igraph_turan + igraph_wheel + igraph_weighted_adjacency + kary_tree + symmetric_tree + tree_game + ring + watts_strogatz_game +) + +# structural_properties.at +add_examples( + FOLDER examples/simple NAMES + bellman_ford + distances + igraph_assortativity_degree + igraph_assortativity_nominal + igraph_average_path_length + igraph_cocitation + igraph_diameter + igraph_eccentricity + igraph_feedback_arc_set + igraph_feedback_arc_set_ip + igraph_get_all_shortest_paths_dijkstra + igraph_get_shortest_paths + igraph_get_shortest_paths_dijkstra + igraph_girth + igraph_has_multiple + igraph_is_loop + igraph_is_multiple + igraph_list_triangles + igraph_avg_nearest_neighbor_degree + igraph_minimum_spanning_tree + igraph_pagerank + igraph_radius + igraph_reciprocity + igraph_similarity + igraph_simplify + igraph_topological_sorting + igraph_transitivity +) + +add_legacy_tests( + FOLDER tests/unit NAMES + all_shortest_paths + assortativity + components + coreness + efficiency + global_transitivity + harmonic_centrality + hub_and_authority + igraph_adjacent_triangles + igraph_are_connected + igraph_average_path_length + igraph_average_path_length_dijkstra + igraph_betweenness + igraph_betweenness_subset + igraph_closeness + igraph_constraint + igraph_convergence_degree + igraph_count_multiple + igraph_density + igraph_diameter + igraph_diameter_dijkstra + igraph_diversity + igraph_distances_floyd_warshall + igraph_distances_floyd_warshall_speedup + igraph_distances_johnson + igraph_ecc + igraph_eccentricity + igraph_eccentricity_dijkstra + igraph_edge_betweenness + igraph_edge_betweenness_subset + igraph_feedback_arc_set_undirected + igraph_get_all_simple_paths + igraph_get_all_shortest_paths_dijkstra + igraph_get_k_shortest_paths + igraph_get_shortest_paths2 + igraph_get_shortest_path_astar + igraph_get_shortest_path_bellman_ford + igraph_get_shortest_paths_bellman_ford + igraph_get_shortest_paths_dijkstra + igraph_graph_center + igraph_has_mutual + igraph_is_bipartite + igraph_is_connected + igraph_is_chordal + igraph_is_clique + igraph_is_complete + igraph_is_dag + igraph_is_mutual + igraph_is_tree + igraph_is_forest + igraph_is_forest2 + igraph_joint_degree_distribution + igraph_joint_type_distribution + igraph_is_acyclic + igraph_list_triangles + igraph_local_scan_k_ecount + igraph_local_scan_k_ecount_them + igraph_local_scan_subset_ecount + igraph_local_transitivity + igraph_mean_degree + igraph_neighborhood + igraph_neighborhood_graphs + igraph_neighborhood_size + igraph_pagerank + igraph_path_length_hist + igraph_pseudo_diameter + igraph_pseudo_diameter_dijkstra + igraph_random_walk + igraph_rewire # Uses internal igraph_i_rewire + igraph_similarity + igraph_transitive_closure + igraph_transitivity_avglocal_undirected + igraph_transitivity_barrat + igraph_unfold_tree + igraph_voronoi + igraph_widest_paths + igraph_spanner + knn + random_spanning_tree + reachability + single_target_shortest_path + topological_sorting +) + +add_legacy_tests( + FOLDER tests/regression NAMES + bug_1760 + bug_1814 + bug_1970 + bug_2150 + bug_2497 + bug_2506 + bug_2517 +) + +# components.at +add_examples( + FOLDER examples/simple NAMES + igraph_biconnected_components + igraph_decompose + igraph_is_biconnected +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_biconnected_components + igraph_bridges + igraph_decompose_strong + igraph_is_biconnected + igraph_subcomponent +) + +# layout.at +add_examples( + FOLDER examples/simple NAMES + igraph_layout_reingold_tilford +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_layout_drl + igraph_layout_drl_3d + igraph_layout_bipartite + igraph_layout_fruchterman_reingold + igraph_layout_fruchterman_reingold_3d + igraph_layout_gem + igraph_layout_graphopt + igraph_layout_grid + igraph_layout_kamada_kawai + igraph_layout_lgl + igraph_layout_mds + igraph_layout_merge2 + igraph_layout_merge3 + igraph_layout_random_3d + igraph_layout_reingold_tilford_circular + igraph_layout_reingold_tilford_extended + igraph_layout_sphere + igraph_layout_star + igraph_layout_sugiyama + igraph_layout_umap +) +add_legacy_tests( + FOLDER tests/regression NAMES + igraph_layout_kamada_kawai_3d_bug_1462 + igraph_layout_reingold_tilford_bug_879 +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_i_layout_sphere + igraph_layout_davidson_harel # Uses igraph_i_layout_segments_intersect and igraph_i_layout_point_segment_dist2 + igraph_layout_merge # Uses igraph_i_layout_merge functions + igraph_i_umap_fit_ab +) + +# visitors.at +add_examples( + FOLDER examples/simple NAMES + igraph_bfs + igraph_bfs_callback + igraph_bfs_simple +) +add_legacy_tests( + FOLDER tests/unit NAMES + bfs + bfs_simple +) + +# topology.at +add_examples( + FOLDER examples/simple NAMES + igraph_isomorphic_vf2 + igraph_subisomorphic_lad +) + +add_legacy_tests( + FOLDER tests/unit NAMES + simplify_and_colorize + bliss_automorphisms + igraph_get_isomorphisms_vf2 + igraph_get_subisomorphisms_vf2 + igraph_isomorphic + igraph_isomorphic_vf2 + igraph_subisomorphic + igraph_subisomorphic_lad + igraph_isomorphic_bliss + isomorphism_test + isoclasses + isoclasses2 + VF2-compat +) + +# coloring.at +add_examples( + FOLDER examples/simple NAMES + igraph_coloring +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_coloring +) + + +# motifs.at +add_examples( + FOLDER examples/simple NAMES + igraph_motifs_randesu +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_dyad_census + igraph_motifs_randesu + igraph_motifs_randesu_estimate + igraph_motifs_randesu_no + triad_census +) + +# foreign.at +add_examples( + FOLDER examples/simple NAMES + dot + foreign + gml + graphml + igraph_read_graph_dl + igraph_read_graph_graphdb + igraph_read_graph_lgl + igraph_write_graph_lgl + igraph_write_graph_pajek + safelocale +) + +add_legacy_tests( + FOLDER tests/unit NAMES + foreign_empty + gml + igraph_read_graph_graphdb + igraph_read_graph_graphml + igraph_write_graph_leda + igraph_write_graph_dimacs_flow + igraph_write_graph_dot + lineendings + ncol + pajek + pajek2 + pajek_bipartite + pajek_bipartite2 + pajek_signed +) + +# other.at +add_examples( + FOLDER examples/simple NAMES + igraph_power_law_fit +) +# igraph_power_law_fit() output is only deterministic when running with 1 thread +set_property(TEST example::igraph_power_law_fit APPEND PROPERTY ENVIRONMENT "OMP_NUM_THREADS=1") + +add_legacy_tests( + FOLDER tests/unit NAMES + all_almost_e + cmp_epsilon + igraph_almost_equals + igraph_convex_hull + igraph_power_law_fit + overflow + prop_caching + zapsmall +) +# igraph_power_law_fit() output is only deterministic when running with 1 thread +set_property(TEST test::igraph_power_law_fit APPEND PROPERTY ENVIRONMENT "OMP_NUM_THREADS=1") + +# operators.at +add_examples( + FOLDER examples/simple NAMES + igraph_complementer + igraph_compose + igraph_contract_vertices + igraph_difference + igraph_disjoint_union + igraph_join + igraph_intersection + igraph_union +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_contract_vertices + igraph_connect_neighborhood + igraph_disjoint_union + igraph_graph_power + igraph_induced_subgraph + igraph_induced_subgraph_map + igraph_induced_subgraph_edges + igraph_intersection + igraph_permute_vertices + igraph_reverse_edges + igraph_rewire_directed_edges + igraph_union +) + +# conversion.at +add_examples( + FOLDER examples/simple NAMES + adjlist + igraph_get_laplacian + igraph_get_laplacian_sparse + igraph_to_undirected +) + +add_legacy_tests( + FOLDER tests/unit NAMES + adjlist + igraph_adjlist_init_complementer + igraph_adjlist_simplify + igraph_get_adjacency + igraph_get_adjacency_sparse + igraph_get_laplacian + igraph_get_stochastic + igraph_get_stochastic_sparse + igraph_to_directed + igraph_to_prufer + inclist +) + +# flow.at +add_examples( + FOLDER examples/simple NAMES + dominator_tree + even_tarjan + flow + flow2 + igraph_all_st_mincuts + igraph_mincut +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_all_st_mincuts + igraph_dominator_tree + igraph_st_mincut_value + igraph_vertex_disjoint_paths + igraph_adhesion + igraph_cohesion + igraph_maxflow + igraph_residual_graph + igraph_edge_disjoint_paths + igraph_st_edge_connectivity + igraph_st_mincut + igraph_st_vertex_connectivity +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_all_st_cuts # Uses igraph_marked_queue, which is internal. +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_gomory_hu_tree +) + +# community.at +add_examples( + FOLDER examples/simple NAMES + igraph_community_edge_betweenness + igraph_community_fastgreedy + igraph_community_label_propagation + igraph_community_leading_eigenvector + igraph_community_leiden + igraph_community_multilevel + igraph_community_optimal_modularity + walktrap +) + +add_legacy_tests( + FOLDER tests/unit NAMES + community_indexing + community_leiden + community_label_propagation + community_label_propagation2 + community_label_propagation3 + community_walktrap + graphlets + igraph_community_eb_get_merges + igraph_community_edge_betweenness + igraph_community_fastgreedy + igraph_community_fluid_communities + igraph_community_infomap + igraph_community_leading_eigenvector2 + igraph_community_voronoi + igraph_compare_communities + igraph_le_community_to_membership + igraph_modularity + igraph_modularity_matrix + igraph_split_join_distance + levc-stress + null_communities + spinglass +) +add_legacy_tests( + FOLDER tests/regression NAMES + bug-1149658 +) + +# use a higher test timeout for the Infomap algorithm +set_tests_properties("test::igraph_community_infomap" PROPERTIES TIMEOUT 150) + +# cliques.at +add_examples( + FOLDER examples/simple NAMES + igraph_cliques + igraph_independent_sets + igraph_maximal_cliques +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_clique_size_hist + igraph_maximal_cliques + igraph_maximal_cliques2 + igraph_maximal_cliques3 + igraph_maximal_cliques4 + igraph_maximal_cliques_file + igraph_weighted_cliques + maximal_cliques_callback + maximal_cliques_hist +) + +# eigen.at +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_eigen_matrix + igraph_eigen_matrix2 + igraph_eigen_matrix3 + igraph_eigen_matrix4 + igraph_eigen_matrix_symmetric + igraph_eigen_matrix_symmetric_arpack +) + +# attributes.at +add_examples( + FOLDER examples/simple NAMES + cattributes + cattributes2 + cattributes3 + cattributes4 + igraph_attribute_combination +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_attribute_combination_remove + cattributes5 + cattributes6 +) +add_legacy_tests( + FOLDER tests/regression NAMES + cattr_bool_bug + cattr_bool_bug2 +) + +# arpack.at +add_examples( + FOLDER examples/simple NAMES + blas + blas_dgemm + eigenvector_centrality + igraph_lapack_dgeev + igraph_lapack_dgeevx + igraph_lapack_dgesv + igraph_lapack_dsyevr +) + +add_legacy_tests( + FOLDER tests/unit NAMES + dgemv + igraph_arpack_rnsolve + igraph_arpack_unpack_complex + igraph_blas_dgemm + igraph_eigenvector_centrality + igraph_lapack_dgeev + igraph_lapack_dgeevx + igraph_lapack_dgehrd + igraph_lapack_dgetrf + igraph_lapack_dgetrs + igraph_lapack_dsyevr +) + +# bipartite.at +add_examples( + FOLDER examples/simple NAMES + igraph_bipartite_create + igraph_bipartite_projection +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_bipartite_create + igraph_bipartite_projection + igraph_biadjacency + igraph_get_biadjacency +) + +# centralization.at +add_examples( + FOLDER examples/simple NAMES + centralization +) + +add_legacy_tests( + FOLDER tests/unit NAMES + centralization +) + +# eulerian.at +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_is_eulerian + igraph_eulerian_cycle + igraph_eulerian_path +) + +# separators.at +add_examples( + FOLDER examples/simple NAMES + cohesive_blocks + igraph_is_minimal_separator + igraph_is_separator + igraph_minimal_separators + igraph_minimum_size_separators +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_is_separator +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_cohesive_blocks + igraph_minimum_size_separators +) + +add_legacy_tests( + FOLDER tests/regression NAMES + bug-1033045 +) + +# hrg.at +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_hrg + igraph_hrg2 + igraph_hrg3 + igraph_hrg_create +) + +# microscopic.at +add_examples( + FOLDER examples/simple NAMES + igraph_deterministic_optimal_imitation + igraph_roulette_wheel_imitation + igraph_stochastic_imitation +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_deterministic_optimal_imitation + igraph_moran_process + igraph_roulette_wheel_imitation + igraph_stochastic_imitation +) + +# mt.at -- only if we have pthreads +if(CMAKE_USE_PTHREADS_INIT) + add_legacy_tests( + FOLDER tests/unit NAMES tls1 + LIBRARIES Threads::Threads + ) + + # tls2 should be added only if we use vendored ARPACK because a non-vendored + # ARPACK is not guaranteed to be thread-safe + if(ARPACK_IS_VENDORED AND NOT BUILD_SHARED_LIBS) + add_legacy_tests( + FOLDER tests/unit NAMES tls2 + LIBRARIES Threads::Threads + ) + endif() +endif() + +# random.at +add_examples( + FOLDER examples/simple NAMES + igraph_fisher_yates_shuffle + igraph_random_sample + random_seed +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_random_sample + igraph_rng_get_integer + random_sampling + rng_reproducibility + rng_init_destroy_max_bits_name_set_default +) + +# qsort.at +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_qsort + igraph_qsort_r +) + +# matching.at +add_examples( + FOLDER examples/simple NAMES + igraph_maximum_bipartite_matching +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_maximum_bipartite_matching +) + +# embedding.at +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_adjacency_spectral_embedding +) + +# graphicality + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_is_graphical + igraph_is_bigraphical +) + +# cycle bases +add_legacy_tests( + FOLDER tests/unit NAMES + cycle_bases +) + +# handlers + +add_legacy_tests( + FOLDER tests/unit NAMES + fatal_handler + igraph_progress_handler_stderr + igraph_set_progress_handler +) + +# error output + +add_legacy_tests( + FOLDER tests/unit NAMES + error_macros +) + +# GLPK + +add_legacy_tests( + FOLDER tests/unit NAMES + glpk_error +) + +# regression and fuzzing tests + +add_legacy_tests( + FOLDER tests/regression NAMES + igraph_read_graph_gml_invalid_inputs + igraph_read_graph_graphml_invalid_inputs + igraph_read_graph_pajek_invalid_inputs +) + +# non-graph + +add_legacy_tests( + FOLDER tests/unit NAMES + expand_path_to_pairs + igraph_running_mean + igraph_solve_lsap +) + +# memory allocation + +add_legacy_tests( + FOLDER tests/unit NAMES + zero_allocs +) + +# simulation + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_sir +) + + +# benchmarks +add_benchmarks( + NAMES + connectivity + erdos_renyi + graphicality + igraph_average_path_length_unweighted + igraph_betweenness + igraph_betweenness_weighted + igraph_cliques + igraph_closeness_weighted + igraph_coloring + igraph_decompose + igraph_degree + igraph_degree_sequence_game + igraph_distances + igraph_ecc + igraph_induced_subgraph_edges + igraph_layout_umap + igraph_matrix_transpose + igraph_maximal_cliques + igraph_neighborhood + igraph_pagerank + igraph_pagerank_weighted + igraph_power_law_fit + igraph_qsort + igraph_random_walk + igraph_strength + igraph_transitivity + igraph_tree_game + igraph_vertex_connectivity + igraph_voronoi + inc_vs_adj + lad + intersection +) + +# dot product +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_sample_dirichlet + igraph_sample_sphere +) + +# lattices +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_triangular_lattice + igraph_hexagonal_lattice +) + +add_legacy_tests( + FOLDER tests/unit NAMES + jdm +) + +add_legacy_tests( + FOLDER tests/unit NAMES + igraph_realize_bipartite_degree_sequence +) diff --git a/tests/benchmarks/bench.h b/tests/benchmarks/bench.h new file mode 100644 index 0000000..a0030d9 --- /dev/null +++ b/tests/benchmarks/bench.h @@ -0,0 +1,62 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013-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_BENCH_H +#define IGRAPH_BENCH_H + +#include /* getrusage */ +#include /* gettimeofday */ +#include /* sleep */ + +static inline void igraph_get_cpu_time(double *data) { + + struct rusage self; + struct timeval real; + gettimeofday(&real, NULL); + getrusage(RUSAGE_SELF, &self); + data[0] = (double) real.tv_sec + 1e-6 * real.tv_usec; /* real */ + data[1] = (double) self.ru_utime.tv_sec + 1e-6 * self.ru_utime.tv_usec; /* user */ + data[2] = (double) self.ru_stime.tv_sec + 1e-6 * self.ru_stime.tv_usec; /* system */ +} + +#define BENCH_INIT() \ + do { \ + printf("\n|> Benchmark file: %s\n", IGRAPH_FILE_BASENAME); \ + sleep(1); \ + } while (0) + +#define REPEAT(CODE, N) \ + do { \ + igraph_integer_t rep_i; \ + for (rep_i=0; rep_i < N; ++rep_i) { CODE; } \ + } while (0) + +#define BENCH(NAME, ...) do { \ + double start[3], stop[3]; \ + double r, u, s; \ + igraph_get_cpu_time(start); \ + { __VA_ARGS__; } \ + igraph_get_cpu_time(stop); \ + r = 1e-3 * round(1e3 * (stop[0] - start[0])); \ + u = 1e-3 * round(1e3 * (stop[1] - start[1])); \ + s = 1e-3 * round(1e3 * (stop[2] - start[2])); \ + printf("| %-80s %5.3gs %5.3gs %5.3gs\n", NAME, r, u, s); \ + } while (0) + +#endif diff --git a/tests/benchmarks/connectivity.c b/tests/benchmarks/connectivity.c new file mode 100644 index 0000000..76b867b --- /dev/null +++ b/tests/benchmarks/connectivity.c @@ -0,0 +1,92 @@ +#include + +#include "bench.h" + +/* Benchmarks related to biconnected components. */ + +void run_bench(igraph_integer_t vcount, igraph_real_t meandeg, igraph_integer_t rep) { + igraph_t g; + igraph_bool_t b; + igraph_vector_int_t ivec; + char msg[255]; + + igraph_erdos_renyi_game_gnm(&g, vcount, round(meandeg * vcount / 2), IGRAPH_DIRECTED, IGRAPH_LOOPS); + igraph_vector_int_init(&ivec, igraph_vcount(&g)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 1 Weakly connected? vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, 10*rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_is_connected(&g, &b, IGRAPH_WEAK), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 2 Weak components. vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_connected_components(&g, &ivec, NULL, NULL, IGRAPH_WEAK), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 3 Strongly connected? vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, 10*rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_is_connected(&g, &b, IGRAPH_STRONG), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 4 Strong components. vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_connected_components(&g, &ivec, NULL, NULL, IGRAPH_STRONG), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 5 Biconnected? vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_is_biconnected(&g, &b), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 6 Bridges. vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_bridges(&g, &ivec), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 7 Articulation pts. vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_articulation_points(&g, &ivec), rep)); + + igraph_vector_int_destroy(&ivec); + igraph_destroy(&g); + + printf("\n"); + +} + +int main(void) { + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + // Note that whether these random graphs end up being connected + // with high probability depends not only on their mean degree, + // but also their vertex count. + + run_bench(10000, 0.5, 100); // no giant component + run_bench(10000, 3, 100); // not weakly connected + run_bench(10000, 10, 100); // not strongly connected + run_bench(10000, 20, 100); // strongly connnected + + run_bench(100000, 0.5, 100); // no giant component + run_bench(100000, 3, 100); // not weakly connected + run_bench(100000, 10, 100); // not strongly connected + run_bench(100000, 20, 100); // strongly connnected + + return 0; +} diff --git a/tests/benchmarks/erdos_renyi.c b/tests/benchmarks/erdos_renyi.c new file mode 100644 index 0000000..3c58181 --- /dev/null +++ b/tests/benchmarks/erdos_renyi.c @@ -0,0 +1,118 @@ +#include + +#include "bench.h" + +void gnp(igraph_integer_t n, igraph_real_t p, igraph_bool_t directed, igraph_bool_t loops) { + igraph_t g; + igraph_erdos_renyi_game_gnp(&g, n, p, directed, loops); + igraph_destroy(&g); +} + +void gnm(igraph_integer_t n, igraph_real_t meandeg, igraph_bool_t directed, igraph_bool_t loops) { + igraph_t g; + igraph_erdos_renyi_game_gnm(&g, n, round(directed ? n*meandeg : 0.5*n*meandeg), directed, loops); + igraph_destroy(&g); +} + +void chung_lu(const igraph_vector_t *outdeg, const igraph_vector_t *indeg, igraph_bool_t loops) { + igraph_t g; + igraph_chung_lu_game(&g, outdeg, indeg, loops, IGRAPH_CHUNG_LU_ORIGINAL); + igraph_destroy(&g); +} + +void run_bench(igraph_integer_t vcount, igraph_real_t meandeg, igraph_integer_t rep) { + igraph_t g; + igraph_real_t p = meandeg / vcount; + igraph_vector_t outdeg, indeg; + char msg[255], msg2[255]; + + snprintf(msg2, sizeof(msg2) / sizeof(msg2[0]), + "vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_vector_init(&outdeg, 0); + igraph_vector_init(&indeg, 0); + + igraph_erdos_renyi_game_gnp(&g, vcount, p, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_strength(&g, &outdeg, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, NULL); + igraph_destroy(&g); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 1 G(n,p) undirected, no loops, %s", msg2); + BENCH(msg, REPEAT(gnp(vcount, p, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 2 G(n,m) undirected, no loops, %s", msg2); + BENCH(msg, REPEAT(gnm(vcount, meandeg, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 3 Chung-Lu undirected, no loops, %s", msg2); + BENCH(msg, REPEAT(chung_lu(&outdeg, NULL, IGRAPH_NO_LOOPS), rep)); + + printf("\n"); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 4 G(n,p) undirected, loops, %s", msg2); + BENCH(msg, REPEAT(gnp(vcount, p, IGRAPH_UNDIRECTED, IGRAPH_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 5 G(n,m) undirected, loops, %s", msg2); + BENCH(msg, REPEAT(gnm(vcount, meandeg, IGRAPH_UNDIRECTED, IGRAPH_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 6 Chung-Lu undirected, loops, %s", msg2); + BENCH(msg, REPEAT(chung_lu(&outdeg, NULL, IGRAPH_LOOPS), rep)); + + printf("\n"); + + igraph_erdos_renyi_game_gnp(&g, vcount, p, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_strength(&g, &outdeg, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, NULL); + igraph_strength(&g, &indeg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS, NULL); + igraph_destroy(&g); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 7 G(n,p) directed, no loops, %s", msg2); + BENCH(msg, REPEAT(gnp(vcount, p, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 8 G(n,m) directed, no loops, %s", msg2); + BENCH(msg, REPEAT(gnm(vcount, meandeg, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 9 Chung-Lu directed, no loops, %s", msg2); + BENCH(msg, REPEAT(chung_lu(&outdeg, &indeg, IGRAPH_NO_LOOPS), rep)); + + printf("\n"); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + "10 G(n,p) directed, loops, %s", msg2); + BENCH(msg, REPEAT(gnp(vcount, p, IGRAPH_DIRECTED, IGRAPH_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + "11 G(n,m) directed, loops, %s", msg2); + BENCH(msg, REPEAT(gnm(vcount, meandeg, IGRAPH_DIRECTED, IGRAPH_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + "12 Chung-Lu directed, loops, %s", msg2); + BENCH(msg, REPEAT(chung_lu(&outdeg, &indeg, IGRAPH_LOOPS), rep)); + + printf("\n\n"); + + igraph_vector_destroy(&indeg); + igraph_vector_destroy(&outdeg); +} + +int main(void) { + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + run_bench(100, 3, 10000); + run_bench(10000, 3, 100); + run_bench(1000000, 3, 1); + + run_bench(10000, 1, 1000); + run_bench(10000, 100, 10); + + return 0; +} diff --git a/tests/benchmarks/graphicality.c b/tests/benchmarks/graphicality.c new file mode 100644 index 0000000..bd00ed9 --- /dev/null +++ b/tests/benchmarks/graphicality.c @@ -0,0 +1,127 @@ + +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +#define BENCH_UNDIR(N, REP) \ + do { \ + igraph_barabasi_game(&graph, N, 1, 3, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); \ + igraph_degree(&graph, °, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); \ + igraph_destroy(&graph); \ + \ + BENCH("Undirected simple, PA, n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(°, NULL, IGRAPH_SIMPLE_SW, &graphical), REP); \ + ); \ + \ + BENCH("Undirected loops, PA, n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(°, NULL, IGRAPH_LOOPS_SW, &graphical), REP); \ + ); \ + \ + BENCH("Undirected multi, PA, n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(°, NULL, IGRAPH_MULTI_SW, &graphical), REP); \ + ); \ + \ + BENCH("Undirected multi, loops, PA, n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(°, NULL, IGRAPH_MULTI_SW | IGRAPH_LOOPS_SW, &graphical), 100); \ + ); \ + \ + igraph_erdos_renyi_game_gnp(&graph, N, 12.0/N, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); \ + igraph_degree(&graph, °, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); \ + igraph_destroy(&graph); \ + \ + BENCH("Undirected simple, G(n,p), n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(°, NULL, IGRAPH_SIMPLE_SW, &graphical), REP); \ + ); \ + \ + BENCH("Undirected loops, G(n,p), n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(°, NULL, IGRAPH_LOOPS_SW, &graphical), REP); \ + ); \ + \ + BENCH("Undirected multi, G(n,p), n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(°, NULL, IGRAPH_MULTI_SW, &graphical), REP); \ + ); \ + \ + BENCH("Undirected multi, loops, G(n,p), n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(°, NULL, IGRAPH_MULTI_SW | IGRAPH_LOOPS_SW, &graphical), 100); \ + ); \ + \ + } while (0) + + +#define BENCH_DIR(N, REP) \ +do { \ + igraph_barabasi_game(&graph, N, 1, 3, NULL, true, 0, IGRAPH_DIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); \ + igraph_degree(&graph, &outdeg, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); \ + igraph_degree(&graph, &indeg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); \ + igraph_destroy(&graph); \ + \ + BENCH("Directed simple, PA, n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(&outdeg, &indeg, IGRAPH_SIMPLE_SW, &graphical), REP); \ + ); \ + \ + BENCH("Directed loops, PA, n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(&outdeg, &indeg, IGRAPH_LOOPS_SW, &graphical), REP); \ + ); \ + \ + BENCH("Directed multi, PA, n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(&outdeg, &indeg, IGRAPH_MULTI_SW, &graphical), REP); \ + ); \ + \ + BENCH("Directed multi, loops, PA, n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(&outdeg, &indeg, IGRAPH_MULTI_SW | IGRAPH_LOOPS_SW, &graphical), 100); \ + ); \ + \ + igraph_erdos_renyi_game_gnp(&graph, N, 12.0/N, IGRAPH_DIRECTED, IGRAPH_LOOPS); \ + igraph_degree(&graph, &outdeg, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); \ + igraph_degree(&graph, &indeg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); \ + igraph_destroy(&graph); \ + \ + BENCH("Directed simple, G(n,p), n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(&outdeg, &indeg, IGRAPH_SIMPLE_SW, &graphical), REP); \ + ); \ + \ + BENCH("Directed loops, G(n,p), n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(&outdeg, &indeg, IGRAPH_LOOPS_SW, &graphical), REP); \ + ); \ + \ + BENCH("Directed multi, G(n,p), n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(&outdeg, &indeg, IGRAPH_MULTI_SW, &graphical), REP); \ + ); \ + \ + BENCH("Directed multi, loops, G(n,p), n = " TOSTR(N) ", " TOSTR(REP) "x", \ + REPEAT(igraph_is_graphical(&outdeg, &indeg, IGRAPH_MULTI_SW | IGRAPH_LOOPS_SW, &graphical), 100); \ + ); \ + \ +} while (0) + + +int main(void) { + igraph_t graph; + igraph_vector_int_t deg, outdeg, indeg; + igraph_bool_t graphical; + + BENCH_INIT(); + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_int_init(°, 0); + igraph_vector_int_init(&outdeg, 0); + igraph_vector_int_init(&indeg, 0); + + BENCH_UNDIR(50, 2000000); + BENCH_DIR(50, 2000000); + printf("\n"); + BENCH_UNDIR(1000, 100000); + BENCH_DIR(1000, 100000); + printf("\n"); + BENCH_UNDIR(1000000, 100); + BENCH_DIR(1000000, 100); + + igraph_vector_int_destroy(°); + igraph_vector_int_destroy(&outdeg); + igraph_vector_int_destroy(&indeg); + + return 0; +} diff --git a/tests/benchmarks/igraph_average_path_length_unweighted.c b/tests/benchmarks/igraph_average_path_length_unweighted.c new file mode 100644 index 0000000..2e8c9ae --- /dev/null +++ b/tests/benchmarks/igraph_average_path_length_unweighted.c @@ -0,0 +1,78 @@ + +#include + +#include "bench.h" + +int main(void) { + igraph_t graph; + igraph_real_t avglen; + igraph_matrix_t mat; + + BENCH_INIT(); + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_matrix_init(&mat, 0, 0); + + igraph_kautz(&graph, 4, 5); + igraph_matrix_resize(&mat, igraph_vcount(&graph), igraph_vcount(&graph)); /* preallocate matrix */ + + BENCH(" 1 Kautz(4, 5) average_path_length directed", + igraph_average_path_length(&graph, &avglen, NULL, IGRAPH_DIRECTED, 1); + ); + BENCH(" 2 Kautz(4, 5) distances directed", + igraph_distances(&graph, &mat, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT); + ); + BENCH(" 3 Kautz(4, 5) average_path_length undirected", + igraph_average_path_length(&graph, &avglen, NULL, IGRAPH_UNDIRECTED, 1); + ); + BENCH(" 4 Kautz(4, 5) distances undirected", + igraph_distances(&graph, &mat, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL); + ); + + igraph_destroy(&graph); + + { + igraph_vector_int_t dims; + igraph_vector_bool_t periodic; + igraph_vector_int_init_int(&dims, 3, 15, 15, 15); + igraph_vector_bool_init_int(&periodic, 3, 1, 1, 1); + igraph_square_lattice(&graph, &dims, 1, IGRAPH_UNDIRECTED, 0, &periodic); + igraph_vector_int_destroy(&dims); + igraph_vector_bool_destroy(&periodic); + igraph_rewire(&graph, 100, IGRAPH_REWIRING_SIMPLE); + igraph_matrix_resize(&mat, igraph_vcount(&graph), igraph_vcount(&graph)); /* preallocate matrix */ + } + + BENCH(" 5 Rewired 15x15x15 lattice average_path_length", + igraph_average_path_length(&graph, &avglen, NULL, IGRAPH_UNDIRECTED, 1); + ); + BENCH(" 6 Rewired 15x15x15 lattice distances undirected", + igraph_distances(&graph, &mat, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL); + ); + + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 10000, 12000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + igraph_matrix_resize(&mat, igraph_vcount(&graph), igraph_vcount(&graph)); /* preallocate matrix */ + + BENCH(" 7 Erdos-Renyi n=10000 m=12000 average_path_length directed", + igraph_average_path_length(&graph, &avglen, NULL, IGRAPH_DIRECTED, 1); + ); + BENCH(" 8 Erdos-Renyi n=10000 m=12000 distances directed", + igraph_distances(&graph, &mat, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT); + ); + + /* The undirected computation will be much slower on this graph, as the largest weakly connected + * component is much larger. */ + BENCH(" 9 Erdos-Renyi n=10000 m=12000 average_path_length undirected", + igraph_average_path_length(&graph, &avglen, NULL, IGRAPH_UNDIRECTED, 1); + ); + BENCH("10 Erdos-Renyi n=10000 m=12000 distances undirected", + igraph_distances(&graph, &mat, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL); + ); + + igraph_destroy(&graph); + igraph_matrix_destroy(&mat); + + return 0; +} diff --git a/tests/benchmarks/igraph_betweenness.c b/tests/benchmarks/igraph_betweenness.c new file mode 100644 index 0000000..dd92668 --- /dev/null +++ b/tests/benchmarks/igraph_betweenness.c @@ -0,0 +1,66 @@ +#include + +#include "bench.h" + +int main(void) { + igraph_t graph; + igraph_vector_t betweenness; + + BENCH_INIT(); + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_init(&betweenness, 0); + + /* Kautz and De Bruijn graphs are connected, therefore there should not be a dramatic difference + * in the performance of the directed and undirected calculations. */ + + igraph_kautz(&graph, 4, 5); + BENCH(" 1 Betweenness, Kautz(4,5), directed", + igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, NULL)); + BENCH(" 2 Betweenness, Kautz(4,5), undirected", + igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL)); + igraph_destroy(&graph); + + igraph_de_bruijn(&graph, 6, 5); + BENCH(" 3 Betweenness, DeBruijn(6,5), directed", + igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, NULL)); + igraph_destroy(&graph); + + { + igraph_vector_int_t dims; + + igraph_vector_int_init_int_end(&dims, -1, 8, 8, 8, 8, -1); + igraph_square_lattice(&graph, &dims, 1, IGRAPH_UNDIRECTED, /* mutual */ 0, /* periodic */ 0); + BENCH(" 4 Betweenness, Grid(8,8,8,8), undirected", + igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL)); + igraph_destroy(&graph); + igraph_vector_int_destroy(&dims); + + igraph_vector_int_init_int_end(&dims, -1, 10, 10, 10, 10, -1); + igraph_square_lattice(&graph, &dims, 1, IGRAPH_UNDIRECTED, /* mutual */ 0, /* periodic */ 0); + BENCH(" 5 Betweenness, Grid(10,10,10,10), cutoff 5", + igraph_betweenness_cutoff(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL, 5)); + BENCH(" 6 Betweenness, Grid(10,10,10,10), cutoff 8", + igraph_betweenness_cutoff(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL, 8)); + igraph_destroy(&graph); + igraph_vector_int_destroy(&dims); + } + + igraph_barabasi_game(&graph, 8000, 1, 1, NULL, 1, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); + BENCH(" 7 Betweenness, Barabasi n=8000 m=1, undirected", + igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL)); + BENCH(" 8 Betweenness, Barabasi n=8000 m=1, undirected, cutoff 6", + igraph_betweenness_cutoff(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL, 6)); + igraph_destroy(&graph); + + igraph_barabasi_game(&graph, 30000, 1, 5, NULL, 1, 0, IGRAPH_DIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); + BENCH(" 9 Betweenness, Barabasi n=30000 m=5, directed", + igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, NULL)); + BENCH("10 Betweenness, Barabasi n=30000 m=5, directed, cutoff 5", + igraph_betweenness_cutoff(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, NULL, 5)); + igraph_destroy(&graph); + + igraph_vector_destroy(&betweenness); + + return 0; +} diff --git a/tests/benchmarks/igraph_betweenness_weighted.c b/tests/benchmarks/igraph_betweenness_weighted.c new file mode 100644 index 0000000..8f602bd --- /dev/null +++ b/tests/benchmarks/igraph_betweenness_weighted.c @@ -0,0 +1,146 @@ +#include + +#include "bench.h" + +void rand_weight_vec(igraph_vector_t *vec, const igraph_t *graph) { + igraph_integer_t i, n = igraph_ecount(graph); + igraph_vector_resize(vec, n); + for (i=0; i < n; ++i) { + VECTOR(*vec)[i] = RNG_UNIF(1, 10); + } +} + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +int main(void) { + igraph_t graph; + igraph_vector_t betweenness, weight; + + /* These betweenness benchmarks are identical to the weighted closeness ones. */ + + /* This benchmark compares directed/undirected and weighted/unweighted calculations + * on the same graphs. */ + + BENCH_INIT(); + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_init(&betweenness, 0); + igraph_vector_init(&weight, 0); + + igraph_kautz(&graph, 4, 3); + + /* Kautz and De Bruijn graphs are connected, therefore there should not be a dramatic difference + * in the performance of the directed and undirected calculations. */ + +#define NAME "Kautz(4,3)" +#define REP 100 + + BENCH(" 1 Betweenness, unweighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, NULL), REP) + ); + BENCH(" 2 Betweenness, unweighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL), REP) + ); + + rand_weight_vec(&weight, &graph); + + BENCH(" 3 Betweenness, weighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, &weight), REP) + ); + BENCH(" 4 Betweenness, weighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, &weight), REP) + ); + + igraph_destroy(&graph); + +#undef NAME +#undef REP + +#define NAME "DeBruijn(5,5)" +#define REP 1 + + igraph_de_bruijn(&graph, 5, 5); + + BENCH(" 5 Betweenness, unweighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, NULL), REP) + ); + BENCH(" 6 Betweenness, unweighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL), REP) + ); + + rand_weight_vec(&weight, &graph); + + BENCH(" 7 Betweenness, weighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, &weight), REP) + ); + BENCH(" 8 Betweenness, weighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, &weight), REP) + ); + + igraph_destroy(&graph); + +#undef NAME +#undef REP + + /* Choose the parameters of the Erdős-Rényi model so that the graph will have a large strongly connected + * giant component. With 3000 vertices and 10000 edges, it is likely to contain over 90% of vertices. + * If the graph does not have a giant component, then the directed betweenness calculation will be very + * fast, and not directly comparable to the undirected one. */ + +#define NAME "GNM(3000,10000)" +#define REP 1 + + igraph_erdos_renyi_game_gnm(&graph, 3000, 10000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + + BENCH(" 9 Betweenness, unweighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, NULL), REP) + ); + BENCH("10 Betweenness, unweighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL), REP) + ); + + rand_weight_vec(&weight, &graph); + + BENCH("11 Betweenness, weighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, &weight), REP) + ); + BENCH("12 Betweenness, weighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, &weight), REP) + ); + + igraph_destroy(&graph); + +#undef NAME +#undef REP + + /* Benchmark a much denser Erdős-Rényi graph as well. */ + +#define NAME "GNM(3000,30000)" +#define REP 1 + + igraph_erdos_renyi_game_gnm(&graph, 3000, 30000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + + BENCH("13 Betweenness, unweighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, NULL), REP) + ); + BENCH("14 Betweenness, unweighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL), REP) + ); + + rand_weight_vec(&weight, &graph); + + BENCH("15 Betweenness, weighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_DIRECTED, &weight), REP) + ); + BENCH("16 Betweenness, weighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_betweenness(&graph, &betweenness, igraph_vss_all(), IGRAPH_UNDIRECTED, &weight), REP) + ); + + igraph_destroy(&graph); + + igraph_vector_destroy(&weight); + igraph_vector_destroy(&betweenness); + + return 0; +} diff --git a/tests/benchmarks/igraph_cliques.c b/tests/benchmarks/igraph_cliques.c new file mode 100644 index 0000000..c62933a --- /dev/null +++ b/tests/benchmarks/igraph_cliques.c @@ -0,0 +1,37 @@ + +#include + +#include "bench.h" + +int main(void) { + igraph_t g; + igraph_vector_int_list_t res; + igraph_integer_t res_int; + + igraph_rng_seed(igraph_rng_default(), 42); + BENCH_INIT(); + + igraph_vector_int_list_init(&res, 0); + + igraph_erdos_renyi_game_gnm(&g, 100, 3000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + BENCH(" 1 Cliques in random graph with 100 vertices and 3000 edges", + igraph_cliques(&g, &res, /* min_size= */ 0, /* max_size= */ 0); + ); + igraph_destroy(&g); + igraph_vector_int_list_clear(&res); + + igraph_erdos_renyi_game_gnm(&g, 200, 10000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + BENCH(" 2 Cliques in random graph with 200 vertices and 10000 edges, up to size 5", + igraph_cliques(&g, &res, /* min_size= */ 0, /* max_size= */ 5); + ); + igraph_vector_int_list_clear(&res); + BENCH(" 3 Clique number of the same graph with 200 vertices and 10000 edges", + igraph_clique_number(&g, &res_int); + ); + igraph_vector_int_list_clear(&res); + igraph_destroy(&g); + + igraph_vector_int_list_destroy(&res); + + return 0; +} diff --git a/tests/benchmarks/igraph_closeness_weighted.c b/tests/benchmarks/igraph_closeness_weighted.c new file mode 100644 index 0000000..14295e6 --- /dev/null +++ b/tests/benchmarks/igraph_closeness_weighted.c @@ -0,0 +1,146 @@ +#include + +#include "bench.h" + +void rand_weight_vec(igraph_vector_t *vec, const igraph_t *graph) { + igraph_integer_t i, n = igraph_ecount(graph); + igraph_vector_resize(vec, n); + for (i=0; i < n; ++i) { + VECTOR(*vec)[i] = RNG_UNIF(1, 10); + } +} + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +int main(void) { + igraph_t graph; + igraph_vector_t closeness, weight; + + /* These closeness benchmarks are identical to the weighted betweenness ones. */ + + /* This benchmark compares directed/undirected and weighted/unweighted calculations + * on the same graphs. */ + + BENCH_INIT(); + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_init(&closeness, 0); + igraph_vector_init(&weight, 0); + + igraph_kautz(&graph, 4, 3); + + /* Kautz and De Bruijn graphs are connected, therefore there should not be a dramatic difference + * in the performance of the directed and undirected calculations. */ + +#define NAME "Kautz(4,3)" +#define REP 100 + + BENCH(" 1 Closeness, unweighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_OUT, NULL, 1), REP) + ); + BENCH(" 2 Closeness, unweighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, NULL, 1), REP) + ); + + rand_weight_vec(&weight, &graph); + + BENCH(" 3 Closeness, weighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_OUT, &weight, 1), REP) + ); + BENCH(" 4 Closeness, weighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, &weight, 1), REP) + ); + + igraph_destroy(&graph); + +#undef NAME +#undef REP + +#define NAME "DeBruijn(5,5)" +#define REP 1 + + igraph_de_bruijn(&graph, 5, 5); + + BENCH(" 5 Closeness, unweighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_OUT, NULL, 1), REP) + ); + BENCH(" 6 Closeness, unweighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, NULL, 1), REP) + ); + + rand_weight_vec(&weight, &graph); + + BENCH(" 7 Closeness, weighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_OUT, &weight, 1), REP) + ); + BENCH(" 8 Closeness, weighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, &weight, 1), REP) + ); + + igraph_destroy(&graph); + +#undef NAME +#undef REP + + /* Choose the parameters of the Erdős-Rényi model so that the graph will have a large strongly connected + * giant component. With 3000 vertices and 10000 edges, it is likely to contain over 90% of vertices. + * If the graph does not have a giant component, then the directed betweenness calculation will be very + * fast, and not directly comparable to the undirected one. */ + +#define NAME "GNM(3000,10000)" +#define REP 1 + + igraph_erdos_renyi_game_gnm(&graph, 3000, 10000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + + BENCH(" 9 Closeness, unweighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_OUT, NULL, 1), REP) + ); + BENCH("10 Closeness, unweighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, NULL, 1), REP) + ); + + rand_weight_vec(&weight, &graph); + + BENCH("11 Closeness, weighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_OUT, &weight, 1), REP) + ); + BENCH("12 Closeness, weighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, &weight, 1), REP) + ); + + igraph_destroy(&graph); + +#undef NAME +#undef REP + + /* Benchmark a much denser Erdős-Rényi graph as well. */ + +#define NAME "GNM(3000,30000)" +#define REP 1 + + igraph_erdos_renyi_game_gnm(&graph, 3000, 30000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + + BENCH("13 Closeness, unweighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_OUT, NULL, 1), REP) + ); + BENCH("14 Closeness, unweighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, NULL, 1), REP) + ); + + rand_weight_vec(&weight, &graph); + + BENCH("15 Closeness, weighted, " NAME ", directed, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_OUT, &weight, 1), REP) + ); + BENCH("16 Closeness, weighted, " NAME ", undirected, " TOSTR(REP) "x", + REPEAT(igraph_closeness(&graph, &closeness, NULL, NULL, igraph_vss_all(), IGRAPH_ALL, &weight, 1), REP) + ); + + igraph_destroy(&graph); + + igraph_vector_destroy(&weight); + igraph_vector_destroy(&closeness); + + return 0; +} diff --git a/tests/benchmarks/igraph_coloring.c b/tests/benchmarks/igraph_coloring.c new file mode 100644 index 0000000..eea5433 --- /dev/null +++ b/tests/benchmarks/igraph_coloring.c @@ -0,0 +1,39 @@ + +#include + +#include "bench.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t colors; + + igraph_rng_seed(igraph_rng_default(), 42); + BENCH_INIT(); + + igraph_vector_int_init(&colors, 0); + + igraph_erdos_renyi_game_gnm(&g, 50000, 1000000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + BENCH(" 1 COLORED_NEIGHBORS, random graph with 50,000 vertices and 2,000,000 edges", + igraph_vertex_coloring_greedy(&g, &colors, IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS) + ); + BENCH(" 2 DSATUR, random graph with 50,000 vertices and 2,000,000 edges", + igraph_vertex_coloring_greedy(&g, &colors, IGRAPH_COLORING_GREEDY_DSATUR) + ); + igraph_destroy(&g); + + printf("\n"); + + igraph_barabasi_game(&g, 100000, 1, 15, NULL, 0, 0, 0, IGRAPH_BARABASI_PSUMTREE, NULL); + BENCH(" 1 COLORED_NEIGHBORS, pref. attach. graph n=100,000 m=15", + igraph_vertex_coloring_greedy(&g, &colors, IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS) + ); + BENCH(" 2 DSATUR, pref. attach. graph n=100,000 m=15", + igraph_vertex_coloring_greedy(&g, &colors, IGRAPH_COLORING_GREEDY_DSATUR) + ); + igraph_destroy(&g); + + igraph_vector_int_destroy(&colors); + + + return 0; +} diff --git a/tests/benchmarks/igraph_decompose.c b/tests/benchmarks/igraph_decompose.c new file mode 100644 index 0000000..3fe5b75 --- /dev/null +++ b/tests/benchmarks/igraph_decompose.c @@ -0,0 +1,39 @@ + +#include + +#include "bench.h" + +int main(void) { + igraph_t g; + igraph_graph_list_t res; + + igraph_rng_seed(igraph_rng_default(), 42); + BENCH_INIT(); + + igraph_graph_list_init(&res, 0); + + igraph_empty(&g, 1000, IGRAPH_UNDIRECTED); + BENCH(" 1 Decompose graph with 1000 isolated vertices", + igraph_decompose(&g, &res, IGRAPH_WEAK, -1, -1); + ); + igraph_destroy(&g); + igraph_graph_list_clear(&res); + + igraph_empty(&g, 10000, IGRAPH_UNDIRECTED); + BENCH(" 2 Decompose graph with 10000 isolated vertices", + igraph_decompose(&g, &res, IGRAPH_WEAK, -1, -1); + ); + igraph_destroy(&g); + igraph_graph_list_clear(&res); + + igraph_empty(&g, 100000, IGRAPH_UNDIRECTED); + BENCH(" 3 Decompose graph with 100000 isolated vertices", + igraph_decompose(&g, &res, IGRAPH_WEAK, -1, -1); + ); + igraph_destroy(&g); + igraph_graph_list_clear(&res); + + igraph_graph_list_destroy(&res); + + return 0; +} diff --git a/tests/benchmarks/igraph_degree.c b/tests/benchmarks/igraph_degree.c new file mode 100644 index 0000000..d364c32 --- /dev/null +++ b/tests/benchmarks/igraph_degree.c @@ -0,0 +1,106 @@ + +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +/* Calls igraph_degree_1() separately for each vertex. */ +void deg1(const igraph_t *g, igraph_vector_int_t *res, igraph_neimode_t mode, igraph_bool_t loops) { + igraph_integer_t n = igraph_vcount(g); + igraph_vector_int_resize(res, n); + for (igraph_integer_t i=0; i < n; i++) { + igraph_degree_1(g, &VECTOR(*res)[i], i, mode, loops); + } +} + +/* Calls igraph_degree() separately for each vertex, pre-allocates work vector. */ +void degv(const igraph_t *g, igraph_vector_int_t *res, igraph_neimode_t mode, igraph_bool_t loops) { + igraph_integer_t n = igraph_vcount(g); + igraph_vector_int_t work; + + igraph_vector_int_resize(res, n); + igraph_vector_int_init(&work, 1); + for (igraph_integer_t i=0; i < n; i++) { + igraph_degree(g, &work, igraph_vss_1(i), mode, loops); + } + igraph_vector_int_destroy(&work); +} + +/* Calls igraph_degree() separately for each vertex, allocates a new work vector each time. */ +void degv2(const igraph_t *g, igraph_vector_int_t *res, igraph_neimode_t mode, igraph_bool_t loops) { + igraph_integer_t n = igraph_vcount(g); + + for (igraph_integer_t i=0; i < n; i++) { + igraph_vector_int_t work; + igraph_vector_int_init(&work, 1); + igraph_degree(g, &work, igraph_vss_1(i), mode, loops); + igraph_vector_int_destroy(&work); + } +} + +int main(void) { + igraph_t g; + igraph_vector_int_t degs; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + igraph_barabasi_game(&g, 100000, 1, 10, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + igraph_vector_int_init(°s, igraph_vcount(&g)); + +#define REP 1000 + + printf("Count loops, O(1) per degree, fast methods only.\n"); + + BENCH(" 1 igraph_degree(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(igraph_degree(&g, °s, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS), REP); + ); + BENCH(" 2 deg1(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(deg1(&g, °s, IGRAPH_ALL, IGRAPH_LOOPS), REP); + ); + +#undef REP + +#define REP 100 + + printf("\nCount loops, O(1) per degree.\n"); + + BENCH(" 1 igraph_degree(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(igraph_degree(&g, °s, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS), REP); + ); + BENCH(" 2 deg1(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(deg1(&g, °s, IGRAPH_ALL, IGRAPH_LOOPS), REP); + ); + BENCH(" 3 degv(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(degv(&g, °s, IGRAPH_ALL, IGRAPH_LOOPS), REP); + ); + BENCH(" 4 degv2(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(degv2(&g, °s, IGRAPH_ALL, IGRAPH_LOOPS), REP); + ); + +#undef REP + +#define REP 100 + + printf("\nDo not count loops, O(d) per degree.\n"); + + BENCH(" 1 igraph_degree(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(igraph_degree(&g, °s, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS), REP); + ); + BENCH(" 2 deg1(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(deg1(&g, °s, IGRAPH_ALL, IGRAPH_NO_LOOPS), REP); + ); + BENCH(" 3 degv(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(degv(&g, °s, IGRAPH_ALL, IGRAPH_NO_LOOPS), REP); + ); + BENCH(" 4 degv2(), preferential attachment n=100000, m=10, " TOSTR(REP) "x", + REPEAT(degv2(&g, °s, IGRAPH_ALL, IGRAPH_NO_LOOPS), REP); + ); + + igraph_vector_int_destroy(°s); + igraph_destroy(&g); + + return 0; +} diff --git a/tests/benchmarks/igraph_degree_sequence_game.c b/tests/benchmarks/igraph_degree_sequence_game.c new file mode 100644 index 0000000..96f2427 --- /dev/null +++ b/tests/benchmarks/igraph_degree_sequence_game.c @@ -0,0 +1,103 @@ +#include + +#include "bench.h" + + +int main(void) { + igraph_t g, template; + igraph_vector_int_t degrees, outdeg, indeg; + + BENCH_INIT(); + igraph_rng_seed(igraph_rng_default(), 42); + + /* This benchmark indirectly tests the performance of igraph_set_t at the + * moment because igraph_degree_sequence_game() (especially the + * IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE mode) heavily relies on sets. + * This might change in future versions so the name of the benchmark + * reflects the name of the function that is tested directly */ + + igraph_vector_int_init(°rees, 1000); + igraph_vector_int_fill(°rees, 7); + BENCH(" 1 Degseq of undirected k-regular, N=1000, k=7, CONFIGURATION_SIMPLE", + igraph_degree_sequence_game(&g, °rees, /* indeg = */ NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) + ); + igraph_destroy(&g); + igraph_vector_int_destroy(°rees); + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_vector_int_init(°rees, 0); + igraph_barabasi_game(&template, 1000, /* power = */ 1, /* m = */ 1, + /* outseq = */ NULL, /* outpref = */ true, /* A = */ 0, + IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE, /* start_from = */ NULL + ); + igraph_degree(&template, °rees, igraph_vss_all(), IGRAPH_ALL, 1); + BENCH(" 2 Degseq of undirected BA, N=1000, m=1, CONFIGURATION_SIMPLE", + igraph_degree_sequence_game(&g, °rees, /* indeg = */ NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) + ); + igraph_destroy(&g); + igraph_destroy(&template); + igraph_vector_int_destroy(°rees); + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_vector_int_init(°rees, 0); + igraph_erdos_renyi_game_gnm(&template, 200, 600, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_degree(&template, °rees, igraph_vss_all(), IGRAPH_ALL, 1); + BENCH(" 3 Degseq of undirected G(n,m), N=150, m=450, CONFIGURATION_SIMPLE", + igraph_degree_sequence_game(&g, °rees, /* indeg = */ NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) + ); + igraph_destroy(&g); + igraph_destroy(&template); + igraph_vector_int_destroy(°rees); + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_vector_int_init(°rees, 0); + igraph_grg_game(&template, 10000, 0.013, /* torus = */ false, /* x = */ NULL, /* y = */ NULL); + igraph_degree(&template, °rees, igraph_vss_all(), IGRAPH_ALL, true); + BENCH(" 4 Degseq of GRG, N=10000, r=0.013, CONFIGURATION_SIMPLE", + igraph_degree_sequence_game(&g, °rees, /* indeg = */ NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) + ); + igraph_destroy(&g); + igraph_destroy(&template); + igraph_vector_int_destroy(°rees); + + igraph_vector_int_init(°rees, 1000); + igraph_vector_int_fill(°rees, 5); + BENCH(" 5 Degseq of directed k-regular, N=1000, k=5, CONFIGURATION_SIMPLE", + igraph_degree_sequence_game(&g, °rees, °rees, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) + ); + igraph_destroy(&g); + igraph_vector_int_destroy(°rees); + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_vector_int_init(&outdeg, 0); + igraph_vector_int_init(&indeg, 0); + igraph_barabasi_game(&template, 5000, /* power = */ 1, /* m = */ 2, + /* outseq = */ NULL, /* outpref = */ true, /* A = */ 0, + IGRAPH_DIRECTED, IGRAPH_BARABASI_PSUMTREE, /* start_from = */ NULL + ); + igraph_degree(&template, &outdeg, igraph_vss_all(), IGRAPH_OUT, true); + igraph_degree(&template, &indeg, igraph_vss_all(), IGRAPH_IN, true); + BENCH(" 6 Degseq of directed BA, N=500, m=2, CONFIGURATION_SIMPLE", + igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) + ); + igraph_destroy(&g); + igraph_destroy(&template); + igraph_vector_int_destroy(&indeg); + igraph_vector_int_destroy(&outdeg); + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_vector_int_init(&outdeg, 0); + igraph_vector_int_init(&indeg, 0); + igraph_erdos_renyi_game_gnm(&template, 15000, 45000, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_degree(&template, &outdeg, igraph_vss_all(), IGRAPH_OUT, true); + igraph_degree(&template, &indeg, igraph_vss_all(), IGRAPH_IN, true); + BENCH(" 7 Degseq of directed G(n,m), N=15000, m=45000, CONFIGURATION_SIMPLE", + igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) + ); + igraph_destroy(&g); + igraph_destroy(&template); + igraph_vector_int_destroy(&indeg); + igraph_vector_int_destroy(&outdeg); + + return 0; +} diff --git a/tests/benchmarks/igraph_distances.c b/tests/benchmarks/igraph_distances.c new file mode 100644 index 0000000..50d8420 --- /dev/null +++ b/tests/benchmarks/igraph_distances.c @@ -0,0 +1,211 @@ + +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +int main(void) { + igraph_t g; + igraph_matrix_t res; + igraph_vector_t weights; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + igraph_matrix_init(&res, 0, 0); + igraph_vector_init(&weights, 0); + +#define VCOUNT 100 +#define DENS 0.5 +#define REP 200 + + igraph_erdos_renyi_game_gnp(&g, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_matrix_resize(&res, igraph_vcount(&g), igraph_vcount(&g)); + igraph_vector_resize(&weights, igraph_ecount(&g)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < igraph_ecount(&g); i++) { + VECTOR(weights)[i] = RNG_EXP(1); + } + RNG_END(); + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted, " TOSTR(REP) "x", + REPEAT(igraph_distances(&g, &res, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT), REP); + ); + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Dijkstra, " TOSTR(REP) "x", + REPEAT(igraph_distances_dijkstra(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT), REP) + ); + BENCH(" 3 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted Floyd-Warshall, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH(" 4 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted Floyd-Warshall-tree-speedup, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + BENCH(" 5 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH(" 6 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall-tree-speedup, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < trunc(0.005 * igraph_ecount(&g)); i++) { + /* For reproducibility, do not write two RNG_...() calls on the same line + * as the C language does not guarantee any evaluation order between them. */ + igraph_real_t w = RNG_UNIF(-0.05, 0); + VECTOR(weights)[RNG_INTEGER(0, igraph_ecount(&g)-1)] = w; + } + RNG_END(); + + BENCH(" 7 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Bellman-Ford (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_bellman_ford(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT), REP) + ); + BENCH(" 8 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Johnson (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_johnson(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights), REP) + ); + BENCH(" 9 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH("10 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall-tree-speedup (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + printf("\n"); + +#define VCOUNT 500 +#define DENS 0.1 +#define REP 10 + + igraph_erdos_renyi_game_gnp(&g, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_matrix_resize(&res, igraph_vcount(&g), igraph_vcount(&g)); + igraph_vector_resize(&weights, igraph_ecount(&g)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < igraph_ecount(&g); i++) { + VECTOR(weights)[i] = RNG_EXP(1); + } + RNG_END(); + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted, " TOSTR(REP) "x", + REPEAT(igraph_distances(&g, &res, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT), REP); + ); + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Dijkstra, " TOSTR(REP) "x", + REPEAT(igraph_distances_dijkstra(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT), REP) + ); + BENCH(" 3 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted Floyd-Warshall, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH(" 4 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted Floyd-Warshall-tree-speedup, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + BENCH(" 5 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH(" 6 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall-tree-speedup, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < trunc(0.002 * igraph_ecount(&g)); i++) { + /* For reproducibility, do not write two RNG_...() calls on the same line + * as the C language does not guarantee any evaluation order between them. */ + igraph_real_t w = RNG_UNIF(-0.02, 0); + VECTOR(weights)[RNG_INTEGER(0, igraph_ecount(&g)-1)] = w; + } + RNG_END(); + + BENCH(" 7 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Bellman-Ford (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_bellman_ford(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT), REP) + ); + BENCH(" 8 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Johnson (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_johnson(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights), REP) + ); + BENCH(" 9 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH(" 10 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall-tree-speedup (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + printf("\n"); + +#define VCOUNT 1500 +#define DENS 0.02 +#define REP 1 + + igraph_erdos_renyi_game_gnp(&g, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_matrix_resize(&res, igraph_vcount(&g), igraph_vcount(&g)); + igraph_vector_resize(&weights, igraph_ecount(&g)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < igraph_ecount(&g); i++) { + VECTOR(weights)[i] = RNG_EXP(1); + } + RNG_END(); + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted, " TOSTR(REP) "x", + REPEAT(igraph_distances(&g, &res, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT), REP); + ); + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Dijkstra, " TOSTR(REP) "x", + REPEAT(igraph_distances_dijkstra(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT), REP) + ); + BENCH(" 3 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted Floyd-Warshall, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH(" 4 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted Floyd-Warshall-tree-speedup, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + BENCH(" 5 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH(" 6 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall-tree-speedup, " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < trunc(0.01 * igraph_ecount(&g)); i++) { + /* For reproducibility, do not write two RNG_...() calls on the same line + * as the C language does not guarantee any evaluation order between them. */ + igraph_real_t w = RNG_UNIF(-0.02, 0); + VECTOR(weights)[RNG_INTEGER(0, igraph_ecount(&g)-1)] = w; + } + RNG_END(); + + BENCH(" 7 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Bellman-Ford (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_bellman_ford(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT), REP) + ); + BENCH(" 8 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Johnson (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_johnson(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights), REP) + ); + BENCH(" 9 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) + ); + BENCH(" 10 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall-tree-speedup (negative), " TOSTR(REP) "x", + REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) + ); + + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + igraph_vector_destroy(&weights); + igraph_matrix_destroy(&res); + + return 0; +} diff --git a/tests/benchmarks/igraph_ecc.c b/tests/benchmarks/igraph_ecc.c new file mode 100644 index 0000000..61f3318 --- /dev/null +++ b/tests/benchmarks/igraph_ecc.c @@ -0,0 +1,158 @@ + +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +#define BENCH_BLOCK() \ + BENCH(" 1 vc=" TOSTR(VCOUNT) ", ec=" TOSTR(ECOUNT) ", k=3, all, " TOSTR(REP) "x", \ + REPEAT(igraph_ecc(&g, &ecc, igraph_ess_all(IGRAPH_EDGEORDER_ID), 3, false, true), REP); \ + ); \ + BENCH(" 2 vc=" TOSTR(VCOUNT) ", ec=" TOSTR(ECOUNT) ", k=3, subset: all, " TOSTR(REP) "x", \ + REPEAT(igraph_ecc(&g, &ecc, igraph_ess_range(0, igraph_ecount(&g)), 3, false, true), REP); \ + ); \ + \ + igraph_random_sample(&eids, 0, igraph_ecount(&g)-1, SS); \ + \ + BENCH(" 3 vc=" TOSTR(VCOUNT) ", ec=" TOSTR(ECOUNT) ", k=3, subset: " TOSTR(SS) ", " TOSTR(SREP) "x", \ + REPEAT(igraph_ecc(&g, &ecc, igraph_ess_vector(&eids), 3, false, true), SREP); \ + ); \ + \ + BENCH(" 4 vc=" TOSTR(VCOUNT) ", ec=" TOSTR(ECOUNT) ", k=4, all, " TOSTR(REP) "x", \ + REPEAT(igraph_ecc(&g, &ecc, igraph_ess_all(IGRAPH_EDGEORDER_ID), 4, false, true), REP); \ + ); \ + \ + BENCH(" 4 vc=" TOSTR(VCOUNT) ", ec=" TOSTR(ECOUNT) ", k=4, subset: all, " TOSTR(REP) "x", \ + REPEAT(igraph_ecc(&g, &ecc, igraph_ess_range(0, igraph_ecount(&g)), 4, false, true), REP); \ + ); \ + BENCH(" 5 vc=" TOSTR(VCOUNT) ", ec=" TOSTR(ECOUNT) ", k=4, subset: " TOSTR(SS) ", " TOSTR(SREP) "x", \ + REPEAT(igraph_ecc(&g, &ecc, igraph_ess_vector(&eids), 4, false, true), SREP); \ + ); + +int main(void) { + igraph_t g; + igraph_vector_t ecc; + igraph_vector_int_t eids; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + igraph_vector_init(&ecc, 0); + igraph_vector_int_init(&eids, 0); + + printf("Erdos-Renyi GNM:\n\n"); + +#define VCOUNT 100 +#define ECOUNT 5000 +#define REP 100 +#define SS 100 +#define SREP 1000 + + igraph_erdos_renyi_game_gnm(&g, VCOUNT, ECOUNT, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_resize(&ecc, igraph_ecount(&g)); + + BENCH_BLOCK() + + igraph_destroy(&g); + +#undef VCOUNT +#undef ECOUNT +#undef REP +#undef SS +#undef SREP + + printf("\n"); + +#define VCOUNT 1000 +#define ECOUNT 5000 +#define REP 100 +#define SS 100 +#define SREP 1000 + + igraph_erdos_renyi_game_gnm(&g, VCOUNT, ECOUNT, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_resize(&ecc, igraph_ecount(&g)); + + BENCH_BLOCK() + + igraph_destroy(&g); + +#undef VCOUNT +#undef ECOUNT +#undef REP +#undef SS +#undef SREP + + printf("\n"); + +#define VCOUNT 10000 +#define ECOUNT 10000 +#define REP 100 +#define SS 100 +#define SREP 1000 + + igraph_erdos_renyi_game_gnm(&g, VCOUNT, ECOUNT, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_resize(&ecc, igraph_ecount(&g)); + + BENCH_BLOCK() + + igraph_destroy(&g); + +#undef VCOUNT +#undef ECOUNT +#undef REP +#undef SS +#undef SREP + + printf("\n"); + +#define VCOUNT 10000 +#define ECOUNT 50000 +#define REP 100 +#define SS 100 +#define SREP 1000 + + igraph_erdos_renyi_game_gnm(&g, VCOUNT, ECOUNT, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_resize(&ecc, igraph_ecount(&g)); + + BENCH_BLOCK() + + igraph_destroy(&g); + +#undef VCOUNT +#undef ECOUNT +#undef REP +#undef SS +#undef SREP + + printf("\n"); + +#define VCOUNT 10000 +#define ECOUNT 50000 +#define REP 100 +#define SS 100 +#define SREP 1000 + + printf("Barabasi:\n\n"); + + igraph_barabasi_game(&g, + VCOUNT, 1, ECOUNT / VCOUNT, NULL, true, 0, + IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + igraph_vector_resize(&ecc, igraph_ecount(&g)); + + BENCH_BLOCK() + + igraph_destroy(&g); + +#undef VCOUNT +#undef ECOUNT +#undef REP +#undef SS +#undef SREP + + igraph_vector_int_destroy(&eids); + igraph_vector_destroy(&ecc); + + return 0; +} diff --git a/tests/benchmarks/igraph_induced_subgraph_edges.c b/tests/benchmarks/igraph_induced_subgraph_edges.c new file mode 100644 index 0000000..26b7282 --- /dev/null +++ b/tests/benchmarks/igraph_induced_subgraph_edges.c @@ -0,0 +1,77 @@ +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) +#define BARABASI_GRAPH_VERTEX_COUNT 100000 + +void unit_test_subgraph_edges( + const igraph_t *graph, igraph_vector_int_t *vertices, + igraph_integer_t induced_subgraph_vertice_count, igraph_integer_t benchmark_count, + const char* bench_message +) { + igraph_vector_int_t edges; + igraph_vector_int_init(&edges, 0); + + BENCH(bench_message, + REPEAT( + igraph_induced_subgraph_edges(graph, igraph_vss_range(0, induced_subgraph_vertice_count), &edges), + benchmark_count + ) + ); + + igraph_vector_int_destroy(&edges); +} + +void bench_induced_subgraph_edges(void) { + igraph_t g; + igraph_vector_int_t vertices; + + igraph_vector_int_init_range(&vertices, 0, BARABASI_GRAPH_VERTEX_COUNT); + igraph_vector_int_shuffle(&vertices); + + igraph_barabasi_game(&g, BARABASI_GRAPH_VERTEX_COUNT, 1, 100, NULL, true, 0, + IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + + printf("induced_subgraph_edges() for BA graph\n"); + +#define REP 10 + +#define INDUCED_SUBGRAPH_VERTEX_COUNT 100 + unit_test_subgraph_edges(&g, &vertices, INDUCED_SUBGRAPH_VERTEX_COUNT, REP, + "n=" TOSTR(BARABASI_GRAPH_VERTEX_COUNT) ", " + "m=100, " + "e=" TOSTR(INDUCED_SUBGRAPH_VERTEX_COUNT) ", " + TOSTR(REP) " x" + ); +#undef INDUCED_SUBGRAPH_VERTEX_COUNT + +#define INDUCED_SUBGRAPH_VERTEX_COUNT 1000 + unit_test_subgraph_edges(&g, &vertices, INDUCED_SUBGRAPH_VERTEX_COUNT, REP, + "n=" TOSTR(BARABASI_GRAPH_VERTEX_COUNT) ", " + "m=100, " + "e=" TOSTR(INDUCED_SUBGRAPH_VERTEX_COUNT) ", " + TOSTR(REP) " x" + ); +#undef INDUCED_SUBGRAPH_VERTEX_COUNT + +#define INDUCED_SUBGRAPH_VERTEX_COUNT 10000 + unit_test_subgraph_edges(&g, &vertices, INDUCED_SUBGRAPH_VERTEX_COUNT, REP, + "n=" TOSTR(BARABASI_GRAPH_VERTEX_COUNT) ", " + "m=100, " + "e=" TOSTR(INDUCED_SUBGRAPH_VERTEX_COUNT) ", " + TOSTR(REP) " x" + ); +#undef INDUCED_SUBGRAPH_VERTEX_COUNT + +#undef REP +} + +int main(void) { + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + bench_induced_subgraph_edges(); + + return 0; +} diff --git a/tests/benchmarks/igraph_layout_umap.c b/tests/benchmarks/igraph_layout_umap.c new file mode 100644 index 0000000..73d4cec --- /dev/null +++ b/tests/benchmarks/igraph_layout_umap.c @@ -0,0 +1,70 @@ + +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +int main(void) { + igraph_t graph; + igraph_vector_t distances; + igraph_matrix_t layout; + + igraph_rng_seed(igraph_rng_default(), 42); + BENCH_INIT(); + + igraph_matrix_init(&layout, 0, 0); + + + igraph_small(&graph, 12, IGRAPH_UNDIRECTED, + 0,1, 0,2, 0,3, 1,2, 1,3, 2,3, + 3,4, 4,5, 5,6, + 6,7, 7,8, 6,8, 7,9, 6,9, 8,9, 7,10, 8,10, 9,10, 10,11, 9,11, 8,11, 7,11, + -1); + igraph_vector_init_real(&distances, + igraph_ecount(&graph), + 0.1, 0.09, 0.12, 0.09, 0.1, 0.1, + 0.9, 0.9, 0.9, + 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.08, 0.05, 0.1, 0.08, 0.12, 0.09, 0.11 + ); + +#define EPOCHS 5000 +#define REP 40 + + BENCH("Small graph, epochs: " TOSTR(EPOCHS) ", repetitions: " TOSTR(REP), REPEAT(igraph_layout_umap(&graph, &layout, 0, &distances, 0.01, EPOCHS, 0), REP); + ); + +#undef EPOCHS +#undef REP +#define EPOCHS 500 +#define REP 400 + + BENCH("Small graph, epochs: " TOSTR(EPOCHS) ", repetitions: " TOSTR(REP), REPEAT(igraph_layout_umap(&graph, &layout, 0, &distances, 0.01, EPOCHS, 0) , REP); + ); + +#undef EPOCHS +#undef REP +#define EPOCHS 60 +#define REP 1 +#define VCOUNT 10000 +#define DENS 0.001 + + igraph_destroy(&graph); + igraph_erdos_renyi_game_gnp(&graph, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_resize(&distances, igraph_ecount(&graph)); + RNG_BEGIN(); + for (igraph_integer_t i=0; i < igraph_ecount(&graph); i++) { + VECTOR(distances)[i] = RNG_UNIF(0.05, 0.15); + } + RNG_END(); + + BENCH("Larger graph, epochs: " TOSTR(EPOCHS) ", repetitions: " TOSTR(REP), REPEAT(igraph_layout_umap(&graph, &layout, 0, &distances, 0.01, EPOCHS, 0) , REP); + ); + + + igraph_matrix_destroy(&layout); + igraph_destroy(&graph); + igraph_vector_destroy(&distances); + return 0; +} diff --git a/tests/benchmarks/igraph_matrix_transpose.c b/tests/benchmarks/igraph_matrix_transpose.c new file mode 100644 index 0000000..f54bfce --- /dev/null +++ b/tests/benchmarks/igraph_matrix_transpose.c @@ -0,0 +1,40 @@ + +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +void bench(int m, int n, int rep) { + igraph_matrix_t mat; + + igraph_matrix_init(&mat, m, n); + RNG_BEGIN(); + for (igraph_integer_t j=0; j < n; j++) { + for (igraph_integer_t i=0; i < m; i++) { + MATRIX(mat, i, j) = RNG_UNIF(-1, 1); + } + } + RNG_END(); + + char name[200]; + sprintf(name, "Transpose %5d x %5d, %dx", m, n, rep); + BENCH( name, REPEAT(igraph_matrix_transpose(&mat), rep) ); + + igraph_matrix_destroy(&mat); +} + +int main(void) { + bench(30, 30, 100000); + bench(100, 100, 10000); + bench(1000, 1000, 100); + bench(1024, 1024, 100); /* naive implementation has bad cache behaviour with power of 2 sizes */ + bench(1023, 1025, 100); /* non-symmetric */ + bench(3000, 3000, 10); + /* skinny non-symmetric: */ + bench(100, 10000, 100); + bench(10000, 100, 100); + + return 0; +} diff --git a/tests/benchmarks/igraph_maximal_cliques.c b/tests/benchmarks/igraph_maximal_cliques.c new file mode 100644 index 0000000..b1e99ca --- /dev/null +++ b/tests/benchmarks/igraph_maximal_cliques.c @@ -0,0 +1,58 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 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 + 02110-1301 USA + +*/ + +#include + +#include "bench.h" + +int main(void) { + + igraph_t g; + igraph_integer_t toremovev[] = { + 2609, 2098, 14517, 7540, 19560, 8855, + 5939, 14947, 441, 16976, 19642, 4188, + 15447, 11837, 2333, 7309, 18539, 14099, + 14264, 9240 + }; + igraph_vector_int_t toremove; + igraph_vector_int_list_t res; + + BENCH_INIT(); + + igraph_vector_int_view(&toremove, toremovev, sizeof(toremovev) / sizeof(toremovev[0])); + igraph_full(&g, 200, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_delete_edges(&g, igraph_ess_vector(&toremove)); + + igraph_vector_int_list_init(&res, 0); + + BENCH(" 1 Maximal cliques of almost complete graph", + igraph_maximal_cliques(&g, &res, /* min_size= */ 0, + /* max_size= */ 0); + ); + + igraph_destroy(&g); + + igraph_vector_int_list_destroy(&res); + + return 0; +} diff --git a/tests/benchmarks/igraph_neighborhood.c b/tests/benchmarks/igraph_neighborhood.c new file mode 100644 index 0000000..a658b89 --- /dev/null +++ b/tests/benchmarks/igraph_neighborhood.c @@ -0,0 +1,474 @@ +/* + 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 . +*/ + +#include + +#include "bench.h" + +typedef struct igraph_lazy_adjlist2_t { + const igraph_t *graph; + igraph_integer_t length; + igraph_integer_t data_length; + igraph_vector_int_t *adjs; + igraph_integer_t *data; + igraph_integer_t next_data; + igraph_neimode_t mode; + igraph_loops_t loops; + igraph_multiple_t multiple; +} igraph_lazy_adjlist2_t; +igraph_error_t igraph_lazy_adjlist2_init(const igraph_t *graph, + igraph_lazy_adjlist2_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->data_length = igraph_ecount(graph) * 2; + al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); + al->data = IGRAPH_CALLOC(al->data_length, igraph_integer_t); + al->next_data = 0; + + if (al->adjs == NULL || al->data == NULL) { + IGRAPH_ERROR("Cannot create lazy adjacency list view", IGRAPH_ENOMEM); + } + + return IGRAPH_SUCCESS; +} + +void igraph_lazy_adjlist2_destroy(igraph_lazy_adjlist2_t *al) { + IGRAPH_FREE(al->adjs); + IGRAPH_FREE(al->data); +} + +igraph_vector_int_t *igraph_i_lazy_adjlist2_get_real(igraph_lazy_adjlist2_t *al, + igraph_integer_t pno) { + igraph_integer_t no = pno; + igraph_error_t ret; + + if (al->adjs[no].stor_begin == NULL) { + igraph_vector_int_view(&al->adjs[no], al->data + al->next_data, al->data_length - al->next_data); + /* igraph_neighbors calls a resize. Since we're handling our own + * vectors, we can't have our stor_begin be realloced. A realloc should only + * happen when the vector is too small to handle the neighbors, which + * should never happen, because the stor_end is set to one past the + * end of the latest integer. + */ + ret = igraph_neighbors(al->graph, &al->adjs[no], no, al->mode); + if (ret != IGRAPH_SUCCESS) { + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return NULL; + } + al->next_data += igraph_vector_int_size(&(al->adjs[no])); + } + + return &al->adjs[no]; +} + +igraph_error_t igraph_neighborhood_adjl2(const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, igraph_integer_t mindist) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vit_t vit; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_t *neis; + igraph_vector_int_t tmp; + igraph_lazy_adjlist2_t adjlist2; + + 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, igraph_integer_t); + if (added == 0) { + IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_CHECK(igraph_vector_int_list_reserve(res, IGRAPH_VIT_SIZE(vit))); + igraph_vector_int_list_clear(res); + + if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) { + IGRAPH_CHECK(igraph_lazy_adjlist2_init(graph, &adjlist2, mode, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + } else { + IGRAPH_CHECK(igraph_lazy_adjlist2_init(graph, &adjlist2, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + } + IGRAPH_FINALLY(igraph_lazy_adjlist2_destroy, &adjlist2); + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + added[node] = i + 1; + igraph_vector_int_clear(&tmp); + if (mindist == 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); + } + if (order > 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + } + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + neis = igraph_i_lazy_adjlist2_get_real(&adjlist2, actnode); + //IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_size(neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } else { + /* we just count them but don't add them to q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } + + } /* while q not empty */ + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &tmp)); + } + + igraph_lazy_adjlist2_destroy(&adjlist2); + igraph_vector_int_destroy(&tmp); + igraph_vit_destroy(&vit); + igraph_dqueue_int_destroy(&q); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_neighborhood_adjl(const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, igraph_integer_t mindist) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vit_t vit; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_t *neis; + igraph_vector_int_t tmp; + igraph_lazy_adjlist_t adjlist; + + 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, igraph_integer_t); + if (added == 0) { + IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_CHECK(igraph_vector_int_list_reserve(res, IGRAPH_VIT_SIZE(vit))); + igraph_vector_int_list_clear(res); + + if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) { + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + } else { + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + } + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + added[node] = i + 1; + igraph_vector_int_clear(&tmp); + if (mindist == 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); + } + if (order > 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + } + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + neis = igraph_lazy_adjlist_get(&adjlist, actnode); + //IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_size(neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } else { + /* we just count them but don't add them to q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } + + } /* while q not empty */ + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &tmp)); + } + + igraph_lazy_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&tmp); + igraph_vit_destroy(&vit); + igraph_dqueue_int_destroy(&q); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +igraph_error_t igraph_neighborhood_adj(const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_vs_t vids, igraph_integer_t order, + igraph_neimode_t mode, igraph_integer_t mindist) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vit_t vit; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_t *neis; + igraph_vector_int_t tmp; + igraph_adjlist_t adjlist; + + 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, igraph_integer_t); + if (added == 0) { + IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, added); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_CHECK(igraph_vector_int_list_reserve(res, IGRAPH_VIT_SIZE(vit))); + igraph_vector_int_list_clear(res); + + if (!igraph_is_directed(graph) || mode == IGRAPH_ALL) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + } else { + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + } + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + added[node] = i + 1; + igraph_vector_int_clear(&tmp); + if (mindist == 0) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); + } + if (order > 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + } + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + neis = igraph_adjlist_get(&adjlist, actnode); + //IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_size(neis); + + if (actdist < order - 1) { + /* we add them to the q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } else { + /* we just count them but don't add them to q */ + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (added[nei] != i + 1) { + added[nei] = i + 1; + if (actdist + 1 >= mindist) { + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + } + } + } + } + + } /* while q not empty */ + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &tmp)); + } + + igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&tmp); + igraph_vit_destroy(&vit); + igraph_dqueue_int_destroy(&q); + IGRAPH_FREE(added); + IGRAPH_FINALLY_CLEAN(5); + + return IGRAPH_SUCCESS; +} + +void do_benchmark(igraph_t *g, igraph_vs_t vids, igraph_integer_t repeat) +{ + igraph_vector_int_list_t result_orig; + igraph_vector_int_list_t result_adj; + igraph_vector_int_list_t result_adjl; + igraph_vector_int_list_t result_adjl2; + + igraph_vector_int_list_init(&result_orig, 0); + igraph_vector_int_list_init(&result_adj, 0); + igraph_vector_int_list_init(&result_adjl, 0); + igraph_vector_int_list_init(&result_adjl2, 0); + + for (igraph_integer_t order = 1; order <= 3; order++) { + printf("order %" IGRAPH_PRId ":\n", order); + BENCH("Original function:", + REPEAT(igraph_neighborhood(g, &result_orig, vids, order, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0), repeat)); + + BENCH("Using a lazy adjlist:", + REPEAT(igraph_neighborhood_adjl(g, &result_adjl, vids, order, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0), repeat)); + + BENCH("Using lazy adjlist 2:", + REPEAT(igraph_neighborhood_adjl2(g, &result_adjl2, vids, order, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0), repeat)); + + BENCH("Using adjlist:", + REPEAT(igraph_neighborhood_adj(g, &result_adj, vids, order, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0), repeat)); + for (igraph_integer_t i = 0; i <= order; i++) { + IGRAPH_ASSERT(!igraph_vector_int_lex_cmp(&VECTOR(result_orig)[i], &VECTOR(result_adj)[i])); + IGRAPH_ASSERT(!igraph_vector_int_lex_cmp(&VECTOR(result_adj)[i], &VECTOR(result_adjl)[i])); + IGRAPH_ASSERT(!igraph_vector_int_lex_cmp(&VECTOR(result_adjl)[i], &VECTOR(result_adjl2)[i])); + } + } + igraph_vector_int_list_destroy(&result_orig); + igraph_vector_int_list_destroy(&result_adj); + igraph_vector_int_list_destroy(&result_adjl); + igraph_vector_int_list_destroy(&result_adjl2); +} + +int main(void) { + igraph_t g_full, g_ring, g_er; + igraph_vs_t vids_all, vids_50, vids_5000, vids_200; + + igraph_vs_all(&vids_all); + igraph_vs_range(&vids_50, 0, 50); + igraph_vs_range(&vids_5000, 0, 5000); + igraph_vs_range(&vids_200, 0, 200); + + igraph_full(&g_full, 500, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_ring(&g_ring, 50000, IGRAPH_UNDIRECTED, /* mutual */ 0, /*circular*/ 0); + igraph_erdos_renyi_game_gnm(&g_er, 2000, 20000, IGRAPH_UNDIRECTED, /*loops*/ 0); + + printf("Select all vertices:\n\n"); + printf("Full graph:\n"); + do_benchmark(&g_full, vids_all, 10); + printf("\nRing graph:\n"); + do_benchmark(&g_ring, vids_all, 40); + printf("\nRandom graph:\n"); + do_benchmark(&g_er, vids_all, 15); + + printf("\n\nSelect 10%% of vertices:\n\n"); + printf("Full graph:\n"); + do_benchmark(&g_full, vids_50, 100); + printf("\nRing graph:\n"); + do_benchmark(&g_ring, vids_5000, 400); + printf("\nRandom graph:\n"); + do_benchmark(&g_er, vids_200, 150); + + igraph_destroy(&g_full); + igraph_destroy(&g_ring); + igraph_destroy(&g_er); + igraph_vs_destroy(&vids_all); + igraph_vs_destroy(&vids_50); + igraph_vs_destroy(&vids_5000); + igraph_vs_destroy(&vids_200); +} diff --git a/tests/benchmarks/igraph_pagerank.c b/tests/benchmarks/igraph_pagerank.c new file mode 100644 index 0000000..7f5bbec --- /dev/null +++ b/tests/benchmarks/igraph_pagerank.c @@ -0,0 +1,101 @@ +#include + +#include "bench.h" + +int main(void) { + igraph_t graph; + igraph_vector_t res; + igraph_arpack_options_t arpack_opts; + + BENCH_INIT(); + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_arpack_options_init(&arpack_opts); + + igraph_vector_init(&res, 0); + + igraph_barabasi_game(&graph, 100000, 1, 4, NULL, 1, 0, IGRAPH_DIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); + BENCH(" 1 PageRank, Barabasi n=100000 m=4, PRPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 10) + ); + BENCH(" 2 PageRank, Barabasi n=100000 m=4, ARPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 10) + ); + igraph_destroy(&graph); + + igraph_barabasi_game(&graph, 100000, 1, 10, NULL, 1, 0, IGRAPH_DIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); + BENCH(" 3 PageRank, Barabasi n=100000 m=10, PRPACK, 5x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 5) + ); + BENCH(" 4 PageRank, Barabasi n=100000 m=10, ARPACK, 5x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 5) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 100, 1000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + BENCH(" 5 PageRank, GNM(100,1000), PRPACK, 1000x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 1000) + ); + BENCH(" 6 PageRank, GNM(100,1000), ARPACK, 1000x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 1000) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 200, 4000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + BENCH(" 7 PageRank, GNM(200,4000), PRPACK, 1000x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 1000) + ); + BENCH(" 8 PageRank, GNM(200,4000), ARPACK, 1000x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 1000) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 10000, 20000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + BENCH(" 9 PageRank, GNM(10000,20000), PRPACK, 100x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 100) + ); + BENCH("10 PageRank, GNM(10000,20000), ARPACK, 100x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 100) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 100000, 100000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + BENCH("11 PageRank, GNM(100000,100000), PRPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 10) + ); + BENCH("12 PageRank, GNM(100000,100000), ARPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 10) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 100000, 500000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + BENCH("13 PageRank, GNM(100000,500000), PRPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 10) + ); + BENCH("14 PageRank, GNM(100000,500000), ARPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 10) + ); + igraph_destroy(&graph); + + igraph_kautz(&graph, 6, 6); + BENCH("13 PageRank, Kautz(6,6), PRPACK, 1x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 1) + ); + BENCH("14 PageRank, Kautz(6,6), ARPACK, 1x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 1) + ); + igraph_destroy(&graph); + + igraph_de_bruijn(&graph, 7, 7); + BENCH("13 PageRank, DeBruijn(7,7), PRPACK, 1x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, NULL), 1) + ); + BENCH("14 PageRank, DeBruijn(7,7), ARPACK, 1x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, NULL, &arpack_opts), 1) + ); + igraph_destroy(&graph); + + igraph_vector_destroy(&res); + + return 0; +} diff --git a/tests/benchmarks/igraph_pagerank_weighted.c b/tests/benchmarks/igraph_pagerank_weighted.c new file mode 100644 index 0000000..6837711 --- /dev/null +++ b/tests/benchmarks/igraph_pagerank_weighted.c @@ -0,0 +1,120 @@ +#include + +#include "bench.h" + +void rand_weight_vec(igraph_vector_t *vec, const igraph_t *graph) { + igraph_integer_t i, n = igraph_ecount(graph); + igraph_vector_resize(vec, n); + for (i=0; i < n; ++i) { + VECTOR(*vec)[i] = RNG_UNIF(1, 10); + } +} + +int main(void) { + igraph_t graph; + igraph_vector_t res, weights; + igraph_arpack_options_t arpack_opts; + + BENCH_INIT(); + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_arpack_options_init(&arpack_opts); + + igraph_vector_init(&res, 0); + igraph_vector_init(&weights, 0); + + igraph_barabasi_game(&graph, 100000, 1, 4, NULL, 1, 0, IGRAPH_DIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); + rand_weight_vec(&weights, &graph); + BENCH(" 1 PageRank weighted, Barabasi n=100000 m=4, PRPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 10) + ); + BENCH(" 2 PageRank weighted, Barabasi n=100000 m=4, ARPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 10) + ); + igraph_destroy(&graph); + + igraph_barabasi_game(&graph, 100000, 1, 10, NULL, 1, 0, IGRAPH_DIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); + rand_weight_vec(&weights, &graph); + BENCH(" 3 PageRank weighted, Barabasi n=100000 m=10, PRPACK, 5x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 5) + ); + BENCH(" 4 PageRank weighted, Barabasi n=100000 m=10, ARPACK, 5x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 5) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 100, 1000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + rand_weight_vec(&weights, &graph); + BENCH(" 5 PageRank weighted, GNM(100,1000), PRPACK, 1000x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 1000) + ); + BENCH(" 6 PageRank weighted, GNM(100,1000), ARPACK, 1000x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 1000) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 200, 4000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + rand_weight_vec(&weights, &graph); + BENCH(" 7 PageRank weighted, GNM(200,4000), PRPACK, 1000x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 1000) + ); + BENCH(" 8 PageRank weighted, GNM(200,4000), ARPACK, 1000x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 1000) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 10000, 20000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + rand_weight_vec(&weights, &graph); + BENCH(" 9 PageRank weighted, GNM(10000,20000), PRPACK, 100x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 100) + ); + BENCH("10 PageRank weighted, GNM(10000,20000), ARPACK, 100x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 100) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 100000, 100000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + rand_weight_vec(&weights, &graph); + BENCH("11 PageRank weighted, GNM(100000,100000), PRPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 10) + ); + BENCH("12 PageRank weighted, GNM(100000,100000), ARPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 10) + ); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 100000, 500000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + rand_weight_vec(&weights, &graph); + BENCH("13 PageRank weighted, GNM(100000,500000), PRPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 10) + ); + BENCH("14 PageRank weighted, GNM(100000,500000), ARPACK, 10x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 10) + ); + igraph_destroy(&graph); + + igraph_kautz(&graph, 6, 6); + rand_weight_vec(&weights, &graph); + BENCH("13 PageRank weighted, Kautz(6,6), PRPACK, 1x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 1) + ); + BENCH("14 PageRank weighted, Kautz(6,6), ARPACK, 1x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 1) + ); + igraph_destroy(&graph); + + igraph_de_bruijn(&graph, 7, 7); + rand_weight_vec(&weights, &graph); + BENCH("13 PageRank weighted, DeBruijn(7,7), PRPACK, 1x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_PRPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, NULL), 1) + ); + BENCH("14 PageRank weighted, DeBruijn(7,7), ARPACK, 1x", + REPEAT(igraph_pagerank(&graph, IGRAPH_PAGERANK_ALGO_ARPACK, &res, NULL, igraph_vss_all(), IGRAPH_DIRECTED, 0.85, &weights, &arpack_opts), 1) + ); + igraph_destroy(&graph); + + igraph_vector_destroy(&weights); + igraph_vector_destroy(&res); + + return 0; +} diff --git a/tests/benchmarks/igraph_power_law_fit.c b/tests/benchmarks/igraph_power_law_fit.c new file mode 100644 index 0000000..8cb9eb9 --- /dev/null +++ b/tests/benchmarks/igraph_power_law_fit.c @@ -0,0 +1,154 @@ +#include +#include + +#include "bench.h" + +igraph_vector_t data; + +double rpareto(double xmin, double alpha) { + /* 1-u is used in the base here because we want to avoid the case of + * sampling zero */ + return pow(1 - RNG_UNIF01(), -1.0 / alpha) * xmin; +} + +double rzeta(igraph_integer_t xmin, double alpha) { + double u, v, t; + igraph_integer_t 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; + + xmin = (igraph_integer_t) 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 = RNG_UNIF01(); + v = RNG_UNIF01(); + /* 1-u is used in the base here because we want to avoid the case of + * having zero in x */ + x = (igraph_integer_t) 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 generate_continuous(double xmin, double alpha, size_t num_samples) { + IGRAPH_CHECK(igraph_vector_resize(&data, num_samples)); + + RNG_BEGIN(); + for (size_t i = 0; i < num_samples; i++) { + VECTOR(data)[i] = rpareto(xmin, alpha); + } + RNG_END(); + + return IGRAPH_SUCCESS; +} + +int generate_discrete(double xmin, double alpha, size_t num_samples) { + IGRAPH_CHECK(igraph_vector_resize(&data, num_samples)); + + RNG_BEGIN(); + for (size_t i = 0; i < num_samples; i++) { + VECTOR(data)[i] = rzeta(xmin, alpha); + } + RNG_END(); + + return IGRAPH_SUCCESS; +} + +int fit_continuous(double known_xmin) { + igraph_plfit_result_t result; + IGRAPH_CHECK(igraph_power_law_fit(&data, &result, known_xmin, /* force_continuous = */ 1)); + return IGRAPH_SUCCESS; +} + +int fit_discrete(double known_xmin) { + igraph_plfit_result_t result; + IGRAPH_CHECK(igraph_power_law_fit(&data, &result, known_xmin, 0)); + return IGRAPH_SUCCESS; +} + +int main(void) { + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_init(&data, 0); + + BENCH_INIT(); + + generate_continuous(1, 3, 100000); + BENCH(" 1 Continuous, xmin = 1, alpha = 3, samples = 100K, fitting alpha only", fit_continuous(1)); + + generate_continuous(1, 3, 200000); + BENCH(" 2 Continuous, xmin = 1, alpha = 3, samples = 200K, fitting alpha only", fit_continuous(1)); + + generate_continuous(1, 3, 500000); + BENCH(" 3 Continuous, xmin = 1, alpha = 3, samples = 500K, fitting alpha only", fit_continuous(1)); + + generate_continuous(1, 3, 5000); + BENCH(" 4 Continuous, xmin = 1, alpha = 3, samples = 5K, fitting xmin and alpha", fit_continuous(-1)); + + generate_continuous(1, 3, 10000); + BENCH(" 5 Continuous, xmin = 1, alpha = 3, samples = 10K, fitting xmin and alpha", fit_continuous(-1)); + + generate_continuous(1, 3, 15000); + BENCH(" 6 Continuous, xmin = 1, alpha = 3, samples = 15K, fitting xmin and alpha", fit_continuous(-1)); + + generate_discrete(3, 3, 1000000); + BENCH(" 7 Discrete, xmin = 3, alpha = 3, samples = 1M, fitting alpha only", fit_discrete(3)); + + generate_discrete(3, 3, 5000000); + BENCH(" 8 Discrete, xmin = 3, alpha = 3, samples = 5M, fitting alpha only", fit_discrete(3)); + + generate_discrete(3, 3, 10000000); + BENCH(" 9 Discrete, xmin = 3, alpha = 3, samples = 10M, fitting alpha only", fit_discrete(3)); + + generate_discrete(3, 3, 1000000); + BENCH("10 Discrete, xmin = 3, alpha = 3, samples = 1M, fitting xmin and alpha", fit_discrete(-1)); + + generate_discrete(3, 3, 5000000); + BENCH("11 Discrete, xmin = 3, alpha = 3, samples = 5M, fitting xmin and alpha", fit_discrete(-1)); + + generate_discrete(3, 3, 10000000); + BENCH("12 Discrete, xmin = 3, alpha = 3, samples = 10M, fitting xmin and alpha", fit_discrete(-1)); + + igraph_vector_destroy(&data); + + return 0; +} diff --git a/tests/benchmarks/igraph_qsort.c b/tests/benchmarks/igraph_qsort.c new file mode 100644 index 0000000..a5a2da8 --- /dev/null +++ b/tests/benchmarks/igraph_qsort.c @@ -0,0 +1,49 @@ +#include + +#include "bench.h" + +/* This program benchmarks igraph_qsort() indirectly through vector_sort() */ + +int main(void) { + igraph_vector_int_t vec; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + + igraph_vector_int_init(&vec, 0); + + RNG_BEGIN(); + +#define N 10000000 + + igraph_vector_int_resize(&vec, N); + for (igraph_integer_t i=0; i < N; i++) { + VECTOR(vec)[i] = RNG_INTEGER(0, N-1); + } + BENCH("Sort vector of length " IGRAPH_I_STRINGIFY(N), igraph_vector_int_sort(&vec)); + +#undef N +#define N 1000000 + + igraph_vector_int_resize(&vec, N); + for (igraph_integer_t i=0; i < N; i++) { + VECTOR(vec)[i] = RNG_INTEGER(0, N-1); + } + BENCH("Sort vector of length " IGRAPH_I_STRINGIFY(N), igraph_vector_int_sort(&vec)); + +#undef N +#define N 100000 + + igraph_vector_int_resize(&vec, N); + for (igraph_integer_t i=0; i < N; i++) { + VECTOR(vec)[i] = RNG_INTEGER(0, N-1); + } + BENCH("Sort vector of length " IGRAPH_I_STRINGIFY(N), igraph_vector_int_sort(&vec)); + + RNG_END(); + + igraph_vector_int_destroy(&vec); + + return 0; +} diff --git a/tests/benchmarks/igraph_random_walk.c b/tests/benchmarks/igraph_random_walk.c new file mode 100644 index 0000000..a755013 --- /dev/null +++ b/tests/benchmarks/igraph_random_walk.c @@ -0,0 +1,183 @@ +#include +#include "bench.h" + +int main(void) { + igraph_t graph; + igraph_vector_int_t vertices; + igraph_vector_int_t edges; + igraph_vector_t weights; + igraph_integer_t ec, i; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + igraph_vector_int_init(&vertices, 0); + igraph_vector_int_init(&edges, 0); + igraph_vector_init(&weights, 0); + + /* create a small graph, and a compatible weight vector */ + igraph_de_bruijn(&graph, 3, 2); /* 9 vertices, 27 edges, average degree: 6 */ + ec = igraph_ecount(&graph); + + igraph_vector_resize(&weights, ec); + for (i = 0; i < ec; ++i) { + VECTOR(weights)[i] = igraph_rng_get_unif01(igraph_rng_default()); + } + + /* Only edges */ + + BENCH(" 1 Random edge walk, directed, unweighted, small graph ", + igraph_random_walk(&graph, NULL, NULL, &edges, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 2 Random edge walk, directed, weighted, small graph ", + igraph_random_walk(&graph, &weights, NULL, &edges, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 3 Random edge walk, undirected, unweighted, small graph ", + igraph_random_walk(&graph, NULL, NULL, &edges, 0, IGRAPH_ALL, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 4 Random edge walk, undirected, weighted, small graph ", + igraph_random_walk(&graph, &weights, NULL, &edges, 0, IGRAPH_ALL, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + /* Only vertices */ + + BENCH(" 5 Random vertex walk, directed, unweighted, small graph ", + igraph_random_walk(&graph, NULL, &vertices, NULL, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 6 Random vertex walk, directed, weighted, small graph ", + igraph_random_walk(&graph, &weights, &vertices, NULL, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 7 Random vertex walk, undirected, unweighted, small graph ", + igraph_random_walk(&graph, NULL, &vertices, NULL, 0, IGRAPH_ALL, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 8 Random vertex walk, undirected, weighted, small graph ", + igraph_random_walk(&graph, &weights, &vertices, NULL, 0, IGRAPH_ALL, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + /* Both edges and vertices */ + + BENCH(" 9 Random walk, directed, unweighted, small graph ", + igraph_random_walk(&graph, NULL, &vertices, &edges, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 10 Random walk, directed, weighted, small graph ", + igraph_random_walk(&graph, &weights, &vertices, &edges, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + igraph_destroy(&graph); + + /* create a big graph, and a compatible weight vector */ + igraph_de_bruijn(&graph, 8, 5); /* 32768 vertices, 262144 edges, average degree: 16 */ + ec = igraph_ecount(&graph); + + igraph_vector_resize(&weights, ec); + for (i = 0; i < ec; ++i) { + VECTOR(weights)[i] = igraph_rng_get_unif01(igraph_rng_default()); + } + + /* Only edges */ + + BENCH(" 11 Random edge walk, directed, unweighted, large graph ", + igraph_random_walk(&graph, NULL, NULL, &edges, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 12 Random edge walk, directed, weighted, large graph ", + igraph_random_walk(&graph, &weights, NULL, &edges, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 13 Random edge walk, undirected, unweighted, large graph ", + igraph_random_walk(&graph, NULL, NULL, &edges, 0, IGRAPH_ALL, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 14 Random edge walk, undirected, weighted, large graph ", + igraph_random_walk(&graph, &weights, NULL, &edges, 0, IGRAPH_ALL, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + /* Only vertices */ + + BENCH(" 15 Random vertex walk, directed, unweighted, large graph ", + igraph_random_walk(&graph, NULL, &vertices, NULL, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 16 Random vertex walk, directed, weighted, large graph ", + igraph_random_walk(&graph, &weights, &vertices, NULL, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 17 Random vertex walk, undirected, unweighted, large graph ", + igraph_random_walk(&graph, NULL, &vertices, NULL, 0, IGRAPH_ALL, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 18 Random vertex walk, undirected, weighted, large graph ", + igraph_random_walk(&graph, &weights, &vertices, NULL, 0, IGRAPH_ALL, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + /* Both edges and vertices */ + + BENCH(" 19 Random walk, directed, unweighted, large graph ", + igraph_random_walk(&graph, NULL, &vertices, &edges, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + BENCH(" 20 Random walk, directed, weighted, large graph ", + igraph_random_walk(&graph, &weights, &vertices, &edges, 0, IGRAPH_OUT, 50000000, IGRAPH_RANDOM_WALK_STUCK_RETURN) + ); + + /* Only edges */ + + BENCH(" 21 Short edge walk, directed, unweighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, NULL, NULL, &edges, 0, IGRAPH_OUT, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + BENCH(" 22 Short edge walk, directed, weighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, &weights, NULL, &edges, 0, IGRAPH_OUT, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + BENCH(" 23 Short edge walk, undirected, unweighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, NULL, NULL, &edges, 0, IGRAPH_ALL, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + BENCH(" 24 Short edge walk, undirected, weighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, &weights, NULL, &edges, 0, IGRAPH_ALL, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + /* Only vertices */ + + BENCH(" 25 Short vertex walk, directed, unweighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, NULL, &vertices, NULL, 0, IGRAPH_OUT, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + BENCH(" 26 Short vertex walk, directed, weighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, &weights, &vertices, NULL, 0, IGRAPH_OUT, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + BENCH(" 27 Short vertex walk, undirected, unweighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, NULL, &vertices, NULL, 0, IGRAPH_ALL, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + BENCH(" 28 Short vertex walk, undirected, weighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, &weights, &vertices, NULL, 0, IGRAPH_ALL, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + /* Both edges and vertices */ + + BENCH(" 29 Short random walk, directed, unweighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, NULL, &vertices, &edges, 0, IGRAPH_OUT, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + BENCH(" 30 Short random walk, directed, weighted, large graph, x 100", + REPEAT(igraph_random_walk(&graph, &weights, &vertices, &edges, 0, IGRAPH_OUT, 10000, IGRAPH_RANDOM_WALK_STUCK_RETURN), 100) + ); + + igraph_destroy(&graph); + + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&vertices); + igraph_vector_int_destroy(&edges); + + return 0; +} diff --git a/tests/benchmarks/igraph_strength.c b/tests/benchmarks/igraph_strength.c new file mode 100644 index 0000000..73095cb --- /dev/null +++ b/tests/benchmarks/igraph_strength.c @@ -0,0 +1,127 @@ +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +void rand_weight_vec(igraph_vector_t *vec, const igraph_t *graph) { + igraph_integer_t i, n = igraph_ecount(graph); + igraph_vector_resize(vec, n); + for (i=0; i < n; ++i) { + VECTOR(*vec)[i] = RNG_UNIF(1, 10); + } +} + +int main(void) { + + igraph_t g; + igraph_vector_t strength, weights; + + igraph_rng_seed(igraph_rng_default(), 54); + BENCH_INIT(); + + igraph_vector_init(&strength, 0); + igraph_vector_init(&weights, 0); + + /* igraph_strength() uses an optimized, cache friendly code path when given + * a vertex selector for which igraph_vs_is_all() returns true (this is + * currently igraph_vs_all()). We pass both igraph_vss_all() and + * igraph_vss_range(0, igraph_vcount(graph)) as vertex selectors + * to compare the performance of the two code paths. + * + * NOTE: While currently igraph_vs_is_all() does not return true for + * a range-type vertex selector, this may change in the future. + * An altrenative is to to use igraph_vs_vector(), with the vector + * initialized to a range. + */ + +#define N 1000 +#define M 10 +#define REP 10000 + + igraph_barabasi_game(&g, N, 1, M, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + rand_weight_vec(&weights, &g); + + BENCH(" 1a igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + BENCH(" 1b igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_range(0, igraph_vcount(&g)), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + printf("\n"); + + igraph_destroy(&g); + +#undef N +#undef M +#undef REP + +#define N 10000 +#define M 10 +#define REP 1000 + + igraph_barabasi_game(&g, N, 1, M, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + rand_weight_vec(&weights, &g); + + BENCH(" 2a igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + BENCH(" 2b igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_range(0, igraph_vcount(&g)), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + printf("\n"); + + igraph_destroy(&g); + +#undef N +#undef M +#undef REP + +#define N 100000 +#define M 10 +#define REP 100 + + igraph_barabasi_game(&g, N, 1, M, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + rand_weight_vec(&weights, &g); + + BENCH(" 3a igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + BENCH(" 3b igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_range(0, igraph_vcount(&g)), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + printf("\n"); + + igraph_destroy(&g); + +#undef N +#undef M +#undef REP + +#define N 100000 +#define M 100 +#define REP 10 + + igraph_barabasi_game(&g, N, 1, M, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + rand_weight_vec(&weights, &g); + + BENCH(" 4a igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + BENCH(" 4b igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_range(0, igraph_vcount(&g)), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + printf("\n"); + + igraph_destroy(&g); + +#undef N +#undef M +#undef REP + + igraph_vector_destroy(&weights); + igraph_vector_destroy(&strength); + + return 0; +} diff --git a/tests/benchmarks/igraph_transitivity.c b/tests/benchmarks/igraph_transitivity.c new file mode 100644 index 0000000..a645ecd --- /dev/null +++ b/tests/benchmarks/igraph_transitivity.c @@ -0,0 +1,158 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 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 + 02110-1301 USA + +*/ + +#include + +#include "bench.h" + +#define N 6000 +#define M 2000000 + +int main(void) { + + igraph_t g; + igraph_vector_t trans; + igraph_vs_t all_vertices; + igraph_real_t avg_trans, global_trans; + + igraph_rng_seed(igraph_rng_default(), 42); + BENCH_INIT(); + + igraph_erdos_renyi_game_gnm(&g, N, M, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_init(&trans, igraph_vcount(&g)); + igraph_vs_range(&all_vertices, 0, igraph_vcount(&g)); + + BENCH(" 1 Local transitivity, all vertices method, GNM", + igraph_transitivity_local_undirected(&g, &trans, igraph_vss_all(), + IGRAPH_TRANSITIVITY_NAN); + ); + + BENCH(" 2 Local transitivity, subset method, GNM", + igraph_transitivity_local_undirected(&g, &trans, all_vertices, + IGRAPH_TRANSITIVITY_NAN); + ); + + BENCH(" 3 Average local transitivity GNM", + igraph_transitivity_avglocal_undirected(&g, &avg_trans, IGRAPH_TRANSITIVITY_NAN); + ); + + BENCH(" 4 Global transitivity GNM", + igraph_transitivity_undirected(&g, &global_trans, IGRAPH_TRANSITIVITY_NAN); + ); + + igraph_vs_destroy(&all_vertices); + igraph_destroy(&g); + + igraph_barabasi_game(&g, N, /*power=*/ 1, M / N, /*outseq=*/ 0, + /*outpref=*/ 0, /*A=*/ 1, IGRAPH_UNDIRECTED, + IGRAPH_BARABASI_PSUMTREE, /*start_from=*/ 0); + igraph_vector_resize(&trans, igraph_vcount(&g)); + igraph_vs_range(&all_vertices, 0, igraph_vcount(&g)); + + BENCH(" 5 Local transitivity, all vertices method, Barabasi", + igraph_transitivity_local_undirected(&g, &trans, igraph_vss_all(), + IGRAPH_TRANSITIVITY_NAN); + ); + + BENCH(" 6 Local transitivity, subset method, Barabasi", + igraph_transitivity_local_undirected(&g, &trans, all_vertices, + IGRAPH_TRANSITIVITY_NAN); + ); + + BENCH(" 7 Average local transitivity, Barabasi", + igraph_transitivity_avglocal_undirected(&g, &avg_trans, IGRAPH_TRANSITIVITY_NAN); + ); + + BENCH(" 8 Global transitivity, Barabasi", + igraph_transitivity_undirected(&g, &global_trans, IGRAPH_TRANSITIVITY_NAN); + ); + + igraph_vs_destroy(&all_vertices); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnm(&g, 500, 2000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_resize(&trans, igraph_vcount(&g)); + igraph_vs_range(&all_vertices, 0, igraph_vcount(&g)); + +#define REPS 1000 + + BENCH(" 9 Local transitivity, all vertices method, small GNM", + REPEAT(igraph_transitivity_local_undirected(&g, &trans, igraph_vss_all(), + IGRAPH_TRANSITIVITY_NAN), REPS); + ); + + BENCH("10 Local transitivity, subset method, small GNM", + REPEAT(igraph_transitivity_local_undirected(&g, &trans, all_vertices, + IGRAPH_TRANSITIVITY_NAN), REPS); + ); + + BENCH("11 Average local transitivity, small GNM", + REPEAT(igraph_transitivity_avglocal_undirected(&g, &avg_trans, IGRAPH_TRANSITIVITY_NAN), + REPS); + ); + + BENCH("12 Global transitivity, small GNM", + REPEAT(igraph_transitivity_undirected(&g, &global_trans, IGRAPH_TRANSITIVITY_NAN), + REPS); + ); + + igraph_vs_destroy(&all_vertices); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnm(&g, 50, 300, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_resize(&trans, igraph_vcount(&g)); + igraph_vs_range(&all_vertices, 0, igraph_vcount(&g)); + +#undef REPS + +#define REPS 10000 + + BENCH("13 Local transitivity, all vertices method, tiny GNM", + REPEAT(igraph_transitivity_local_undirected(&g, &trans, igraph_vss_all(), + IGRAPH_TRANSITIVITY_NAN), REPS); + ); + + BENCH("14 Local transitivity, subset method, tiny GNM", + REPEAT(igraph_transitivity_local_undirected(&g, &trans, all_vertices, + IGRAPH_TRANSITIVITY_NAN), REPS); + ); + + BENCH("15 Average local transitivity, tiny GNM", + REPEAT(igraph_transitivity_avglocal_undirected(&g, &avg_trans, IGRAPH_TRANSITIVITY_NAN), + REPS); + ); + + BENCH("16 Global transitivity, tiny GNM", + REPEAT(igraph_transitivity_undirected(&g, &global_trans, IGRAPH_TRANSITIVITY_NAN), + REPS); + ); + + igraph_vs_destroy(&all_vertices); + igraph_destroy(&g); + +#undef REPS + + igraph_vector_destroy(&trans); + + return 0; +} diff --git a/tests/benchmarks/igraph_tree_game.c b/tests/benchmarks/igraph_tree_game.c new file mode 100644 index 0000000..80b8e37 --- /dev/null +++ b/tests/benchmarks/igraph_tree_game.c @@ -0,0 +1,90 @@ + +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +int main(void) { + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + +#define VCOUNT 100 +#define REP 100000 + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", Prufer, " TOSTR(REP) "x", + REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); + ); + igraph_destroy(&g); + + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", LERW, " TOSTR(REP) "x", + REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); + ); + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + printf("\n"); + +#define VCOUNT 1000 +#define REP 10000 + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", Prufer, " TOSTR(REP) "x", + REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); + ); + igraph_destroy(&g); + + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", LERW, " TOSTR(REP) "x", + REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); + ); + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + printf("\n"); + +#define VCOUNT 10000 +#define REP 1000 + + BENCH(" 3 vcount=" TOSTR(VCOUNT) ", Prufer, " TOSTR(REP) "x", + REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); + ); + igraph_destroy(&g); + + BENCH(" 4 vcount=" TOSTR(VCOUNT) ", LERW, " TOSTR(REP) "x", + REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); + ); + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + printf("\n"); + +#define VCOUNT 100000 +#define REP 100 + + BENCH(" 3 vcount=" TOSTR(VCOUNT) ", Prufer, " TOSTR(REP) "x", + REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); + ); + igraph_destroy(&g); + + BENCH(" 4 vcount=" TOSTR(VCOUNT) ", LERW, " TOSTR(REP) "x", + REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); + ); + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + return 0; +} diff --git a/tests/benchmarks/igraph_vertex_connectivity.c b/tests/benchmarks/igraph_vertex_connectivity.c new file mode 100644 index 0000000..0d024aa --- /dev/null +++ b/tests/benchmarks/igraph_vertex_connectivity.c @@ -0,0 +1,60 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2023 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 "bench.h" + +int main(void) { + + igraph_t g; + igraph_integer_t vconn; + + igraph_rng_seed(igraph_rng_default(), 54); + BENCH_INIT(); + + igraph_grg_game(&g, 100, 0.2, /* torus = */ false, /* x = */ NULL, /* y = */ NULL); + + BENCH(" 1 Vertex connectivity of geometric random graph, n=100, r=0.2", + igraph_vertex_connectivity(&g, &vconn, /* checks = */ false); + ); + + igraph_destroy(&g); + + igraph_grg_game(&g, 200, 0.141, /* torus = */ false, /* x = */ NULL, /* y = */ NULL); + + BENCH(" 2 Vertex connectivity of geometric random graph, n=200, r=0.141", + igraph_vertex_connectivity(&g, &vconn, /* checks = */ false); + ); + + igraph_destroy(&g); + + igraph_grg_game(&g, 400, 0.1, /* torus = */ false, /* x = */ NULL, /* y = */ NULL); + + BENCH(" 3 Vertex connectivity of geometric random graph, n=400, r=0.1", + igraph_vertex_connectivity(&g, &vconn, /* checks = */ false); + ); + + igraph_destroy(&g); + + return 0; +} diff --git a/tests/benchmarks/igraph_voronoi.c b/tests/benchmarks/igraph_voronoi.c new file mode 100644 index 0000000..f1adf31 --- /dev/null +++ b/tests/benchmarks/igraph_voronoi.c @@ -0,0 +1,156 @@ + +#include + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +int main(void) { + igraph_t g; + igraph_vector_int_t membership; + igraph_vector_t distances; + igraph_vector_t weights; + igraph_vector_int_t generators; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + igraph_vector_int_init(&membership, 0); + igraph_vector_init(&distances, 0); + igraph_vector_init(&weights, 0); + igraph_vector_int_init(&generators, 0); + +#define VCOUNT 100 +#define DENS 0.5 +#define REP 1000 + + igraph_erdos_renyi_game_gnp(&g, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_int_resize(&membership, igraph_vcount(&g)); + igraph_vector_resize(&distances, igraph_vcount(&g)); + igraph_vector_resize(&weights, igraph_ecount(&g)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < igraph_ecount(&g); i++) { + VECTOR(weights)[i] = RNG_EXP(1); + } + RNG_END(); + + igraph_random_sample(&generators, 0, igraph_vcount(&g)-1, 20); + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted, " TOSTR(REP) "x", + REPEAT(igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM), REP); + ); + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Dijkstra, " TOSTR(REP) "x", + REPEAT(igraph_voronoi(&g, &membership, &distances, &generators, &weights, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM), REP); + ); + + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + printf("\n"); + +#define VCOUNT 500 +#define DENS 0.1 +#define REP 1000 + + igraph_erdos_renyi_game_gnp(&g, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_int_resize(&membership, igraph_vcount(&g)); + igraph_vector_resize(&distances, igraph_vcount(&g)); + igraph_vector_resize(&weights, igraph_ecount(&g)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < igraph_ecount(&g); i++) { + VECTOR(weights)[i] = RNG_EXP(1); + } + RNG_END(); + + igraph_random_sample(&generators, 0, igraph_vcount(&g)-1, 20); + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted, " TOSTR(REP) "x", + REPEAT(igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM), REP); + ); + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Dijkstra, " TOSTR(REP) "x", + REPEAT(igraph_voronoi(&g, &membership, &distances, &generators, &weights, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM), REP); + ); + + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + printf("\n"); + +#define VCOUNT 5000 +#define DENS 0.01 +#define REP 100 + + igraph_erdos_renyi_game_gnp(&g, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_int_resize(&membership, igraph_vcount(&g)); + igraph_vector_resize(&distances, igraph_vcount(&g)); + igraph_vector_resize(&weights, igraph_ecount(&g)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < igraph_ecount(&g); i++) { + VECTOR(weights)[i] = RNG_EXP(1); + } + RNG_END(); + + igraph_random_sample(&generators, 0, igraph_vcount(&g)-1, 20); + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted, " TOSTR(REP) "x", + REPEAT(igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM), REP); + ); + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Dijkstra, " TOSTR(REP) "x", + REPEAT(igraph_voronoi(&g, &membership, &distances, &generators, &weights, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM), REP); + ); + + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + printf("\n"); + +#define VCOUNT 100000 +#define DENS 0.0001 +#define REP 1 + + igraph_erdos_renyi_game_gnp(&g, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_int_resize(&membership, igraph_vcount(&g)); + igraph_vector_resize(&distances, igraph_vcount(&g)); + igraph_vector_resize(&weights, igraph_ecount(&g)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < igraph_ecount(&g); i++) { + VECTOR(weights)[i] = RNG_EXP(1); + } + RNG_END(); + + igraph_random_sample(&generators, 0, igraph_vcount(&g)-1, 100); + + BENCH(" 1 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Unweighted, " TOSTR(REP) "x", + REPEAT(igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM), REP); + ); + BENCH(" 2 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Dijkstra, " TOSTR(REP) "x", + REPEAT(igraph_voronoi(&g, &membership, &distances, &generators, &weights, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM), REP); + ); + + igraph_destroy(&g); + +#undef VCOUNT +#undef DENS +#undef REP + + igraph_vector_int_destroy(&generators); + igraph_vector_destroy(&weights); + igraph_vector_destroy(&distances); + igraph_vector_int_destroy(&membership); + + return 0; +} diff --git a/tests/benchmarks/inc_vs_adj.c b/tests/benchmarks/inc_vs_adj.c new file mode 100644 index 0000000..714f89b --- /dev/null +++ b/tests/benchmarks/inc_vs_adj.c @@ -0,0 +1,456 @@ +/* + IGraph library. + Copyright (C) 2013-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, see . +*/ + +#include + +#include "bench.h" + +typedef struct igraph_incadjlist_inter_t { + igraph_integer_t length; + igraph_vector_int_t *incadjs; +} igraph_incadjlist_inter_t; + +#define igraph_incadjlist_inter_get(il,no) (&(il)->incadjs[(igraph_integer_t)(no)]) +#define igraph_incadjlist_sep_get_inc(il,no) (&(il)->incs[(igraph_integer_t)(no)]) +#define igraph_incadjlist_sep_get_adj(il,no) (&(il)->adjs[(igraph_integer_t)(no)]) + +void igraph_incadjlist_inter_destroy(igraph_incadjlist_inter_t *il) { + igraph_integer_t i; + for (i = 0; i < il->length; i++) { + /* This works if some igraph_vector_int_t's contain NULL, + because igraph_vector_int_destroy can handle this. */ + igraph_vector_int_destroy(&il->incadjs[i]); + } + IGRAPH_FREE(il->incadjs); +} + +igraph_error_t igraph_incadjlist_inter_init(const igraph_t *graph, + igraph_incadjlist_inter_t *il, + igraph_neimode_t mode) { + igraph_integer_t i, j, n; + igraph_vector_int_t tmp; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create incadjlist.", IGRAPH_EINVMODE); + } + + igraph_vector_int_init(&tmp, 0); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp); + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + il->length = igraph_vcount(graph); + il->incadjs = IGRAPH_CALLOC(il->length, igraph_vector_int_t); + if (il->incadjs == 0) { + IGRAPH_ERROR("Cannot create incadjlist.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_incadjlist_inter_destroy, il); + + for (i = 0; i < il->length; i++) { + //IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_incident(graph, &tmp, i, mode)); + + n = igraph_vector_int_size(&tmp); + IGRAPH_CHECK(igraph_vector_int_init(&il->incadjs[i], n * 2)); + + for (j = 0; j < n; j++) { + VECTOR(il->incadjs[i])[j * 2] = VECTOR(tmp)[j]; + VECTOR(il->incadjs[i])[j * 2 + 1] = + IGRAPH_OTHER(graph, VECTOR(tmp)[j], i); + } + } + + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); /* + igraph_incadjlist_inter_destroy */ + + return IGRAPH_SUCCESS; +} + + +typedef struct igraph_incadjlist_sep_t { + igraph_integer_t length; + igraph_vector_int_t *incs; + igraph_vector_int_t *adjs; +} igraph_incadjlist_sep_t; + +void igraph_incadjlist_sep_destroy(igraph_incadjlist_sep_t *il) { + igraph_integer_t i; + for (i = 0; i < il->length; i++) { + /* This works if some igraph_vector_int_t's contain NULL, + because igraph_vector_int_destroy can handle this. */ + igraph_vector_int_destroy(&il->incs[i]); + igraph_vector_int_destroy(&il->adjs[i]); + } + IGRAPH_FREE(il->incs); + IGRAPH_FREE(il->adjs); +} + +igraph_error_t igraph_incadjlist_sep_init(const igraph_t *graph, + igraph_incadjlist_sep_t *il, + igraph_neimode_t mode) { + igraph_integer_t i, j, n; + igraph_vector_int_t tmp; + + if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Cannot create incadjlist.", IGRAPH_EINVMODE); + } + + igraph_vector_int_init(&tmp, 0); + IGRAPH_FINALLY(igraph_vector_int_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 incadjlist.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + il->adjs = IGRAPH_CALLOC(il->length, igraph_vector_int_t); + if (il->adjs == 0) { + IGRAPH_FREE(il->incs); + IGRAPH_ERROR("Cannot create incadjlist.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + IGRAPH_FINALLY(igraph_incadjlist_sep_destroy, il); + + for (i = 0; i < il->length; i++) { + //IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_incident(graph, &tmp, i, mode)); + + n = igraph_vector_int_size(&tmp); + IGRAPH_CHECK(igraph_vector_int_init(&il->incs[i], n)); + IGRAPH_CHECK(igraph_vector_int_init(&il->adjs[i], n)); + + for (j = 0; j < n; j++) { + VECTOR(il->incs[i])[j] = VECTOR(tmp)[j]; + VECTOR(il->adjs[i])[j] = + IGRAPH_OTHER(graph, VECTOR(tmp)[j], i); + } + + } + + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); /* + igraph_incadjlist_sep_destroy */ + + return IGRAPH_SUCCESS; +} + +/* In the below tests, the 'dummy' variable is used to prevent the compilers + * from optimizing away the entire side-effects-free function. We use XOR + * instead of e.g. addition in order to prevent triggering undefined behaviour. */ + +igraph_integer_t test_direct(igraph_t *g) +{ + igraph_integer_t dummy = 0; + igraph_integer_t vcount = igraph_vcount(g); + igraph_integer_t ecount = igraph_ecount(g); + + igraph_integer_t ei = 0; + igraph_integer_t eo = 0; + for (igraph_integer_t i = 0; i < vcount; i++) { + while (ei < ecount && VECTOR(g->from)[VECTOR(g->oi)[ei]] == i) { + igraph_integer_t neighbor = VECTOR(g->to)[VECTOR(g->oi)[ei]]; + dummy ^= neighbor ^ VECTOR(g->oi)[ei]; + ei++; + } + while (eo < ecount && VECTOR(g->to)[VECTOR(g->ii)[eo]] == i) { + igraph_integer_t neighbor = VECTOR(g->from)[VECTOR(g->ii)[eo]]; + dummy ^= neighbor ^ VECTOR(g->ii)[eo] ; + eo++; + } + } + return dummy; +} + +igraph_integer_t test_adj(igraph_t *g, igraph_adjlist_t *adj) +{ + igraph_integer_t dummy = 0; + igraph_integer_t vcount = igraph_vcount(g); + + for (igraph_integer_t i = 0; i < vcount; i++) { + igraph_vector_int_t *neis = igraph_adjlist_get(adj, i); + igraph_integer_t nneis = igraph_vector_int_size(neis); + for (int j = 0; j < nneis; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + dummy ^= neighbor; + } + } + return dummy; +} + +igraph_integer_t test_inc_adj(igraph_t *g, igraph_inclist_t *inc, igraph_adjlist_t *adj) +{ + igraph_integer_t dummy = 0; + igraph_integer_t vcount = igraph_vcount(g); + + for (igraph_integer_t i = 0; i < vcount; i++) { + igraph_vector_int_t *adjs = igraph_adjlist_get(adj, i); + igraph_vector_int_t *incs = igraph_inclist_get(inc, i); + igraph_integer_t nneis = igraph_vector_int_size(adjs); + for (int j = 0; j < nneis; j++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t neighbor = VECTOR(*adjs)[j]; + dummy ^= neighbor ^ edge; + } + } + return dummy; +} + +igraph_integer_t test_inc_other(igraph_t *g, igraph_inclist_t *inc) +{ + igraph_integer_t dummy = 0; + igraph_integer_t vcount = igraph_vcount(g); + + for (igraph_integer_t i = 0; i < vcount; i++) { + igraph_vector_int_t *neis = igraph_inclist_get(inc, i); + igraph_integer_t nneis = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nneis; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(g, edge, i); + dummy ^= neighbor ^ edge; + } + } + return dummy; +} + +igraph_integer_t test_incadj_sep(igraph_t *g, igraph_incadjlist_sep_t *inc) +{ + igraph_integer_t dummy = 0; + igraph_integer_t vcount = igraph_vcount(g); + + for (igraph_integer_t i = 0; i < vcount; i++) { + igraph_vector_int_t *incs = igraph_incadjlist_sep_get_inc(inc, i); + igraph_vector_int_t *adjs = igraph_incadjlist_sep_get_adj(inc, i); + igraph_integer_t nneis = igraph_vector_int_size(incs); + for (igraph_integer_t j = 0; j < nneis; j++) { + igraph_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t neighbor = VECTOR(*adjs)[j]; + dummy ^= neighbor ^ edge; + } + } + return dummy; +} + +igraph_integer_t test_incadj_inter(igraph_t *g, igraph_incadjlist_inter_t *inc) +{ + igraph_integer_t dummy = 0; + igraph_integer_t vcount = igraph_vcount(g); + + for (igraph_integer_t i = 0; i < vcount; i++) { + igraph_vector_int_t *ias = igraph_incadjlist_inter_get(inc, i); + igraph_integer_t nneis = igraph_vector_int_size(ias) / 2; + for (igraph_integer_t j = 0; j < nneis; j++) { + igraph_integer_t edge = VECTOR(*ias)[j * 2]; + igraph_integer_t neighbor = VECTOR(*ias)[j * 2 + 1]; + dummy ^= neighbor ^ edge; + } + } + return dummy; +} + +igraph_integer_t test_inc_to(igraph_t *g, igraph_inclist_t *inc) +{ + igraph_integer_t dummy = 0; + igraph_integer_t vcount = igraph_vcount(g); + + for (igraph_integer_t i = 0; i < vcount; i++) { + igraph_vector_int_t *neis = igraph_inclist_get(inc, i); + igraph_integer_t nneis = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nneis; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t neighbor = IGRAPH_TO(g, edge); + dummy ^= neighbor ^ edge; + } + } + return dummy; +} + +igraph_integer_t test_inc_nop(igraph_t *g, igraph_inclist_t *inc) +{ + igraph_integer_t dummy = 0; + igraph_integer_t vcount = igraph_vcount(g); + + for (igraph_integer_t i = 0; i < vcount; i++) { + igraph_vector_int_t *neis = igraph_inclist_get(inc, i); + igraph_integer_t nneis = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nneis; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + dummy ^= edge; + } + } + return dummy; +} + +/* Used to prevent optimizing away the result. + * This must be a global variable to prevent "variable set but not used" + * warnings from some compilers. */ +volatile igraph_integer_t result; + +void do_benchmarks(char *name, igraph_t *g, int repeat) { + igraph_adjlist_t adj; + igraph_inclist_t inc; + igraph_incadjlist_inter_t incadj_inter; + igraph_incadjlist_sep_t incadj_sep; + + /* The init() call is in a REPEAT loop, so we include destroy() as well to avoid a memory leak. + * This is representative of real use cases, where init/destroy should always come in pairs. */ + printf("%s", name); + BENCH("1 init/destroy adjlist.", + REPEAT( + do { + igraph_adjlist_init(g, &adj, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + igraph_adjlist_destroy(&adj); + } while (0), + repeat); + ); + + printf("%s", name); + BENCH("2 init/destroy inclist.", + REPEAT( + do { + igraph_inclist_init(g, &inc, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + igraph_inclist_destroy(&inc); + } while (0), + repeat); + ); + + printf("%s", name); + BENCH("3 init/destroy adjlist, remove loops and multiple (which aren't present).", + REPEAT( + do { + igraph_adjlist_init(g, &adj, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + igraph_adjlist_destroy(&adj); + } while (0), + repeat); + ); + + printf("%s", name); + BENCH("4 init/destroy inclist, remove loops (which aren't present).", + REPEAT( + do { + igraph_inclist_init(g, &inc, IGRAPH_ALL, IGRAPH_NO_LOOPS); + igraph_inclist_destroy(&inc); + } while (0), + repeat); + ); + + /* Initialize adjlist / inclist for the following benchmarks. */ + igraph_adjlist_init(g, &adj, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + igraph_inclist_init(g, &inc, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("%s", name); + BENCH("5 go over vertices (multiple times) using adjlist.", + REPEAT(result = test_adj(g, &adj), repeat); + ); + + printf("%s", name); + BENCH("6 go over vertices (multiple times) using inclist, IGRAPH_OTHER.", + REPEAT(result = test_inc_other(g, &inc), repeat); + ); + + printf("%s", name); + BENCH("7 go over vertices (multiple times) using inclist, IGRAPH_TO.", + REPEAT(result = test_inc_to(g, &inc), repeat); + ); + + printf("%s", name); + BENCH("8 go over edges using inclist, don't retrieve vertex.", + REPEAT(result = test_inc_nop(g, &inc), repeat); + ); + + printf("%s", name); + BENCH("9 go over edges and vertices using adjlist and inclist.", + REPEAT(result = test_inc_adj(g, &inc, &adj), repeat); + ); + + igraph_adjlist_destroy(&adj); + igraph_inclist_destroy(&inc); + + printf("%s", name); + BENCH("10 go over edges and vertices using graph internals directly.", + REPEAT(result = test_direct(g), repeat); + ); + + printf("%s", name); + BENCH("11 init/destroy interleaved incadjlist.", + REPEAT( + do { + igraph_incadjlist_inter_init(g, &incadj_inter, IGRAPH_ALL); + igraph_incadjlist_inter_destroy(&incadj_inter); + } while (0), + repeat); + ); + + igraph_incadjlist_inter_init(g, &incadj_inter, IGRAPH_ALL); + + printf("%s", name); + BENCH("12 go over edges and vertices using interleaved incadjlist.", + REPEAT(result = test_incadj_inter(g, &incadj_inter), repeat); + ); + + igraph_incadjlist_inter_destroy(&incadj_inter); + + printf("%s", name); + BENCH("13 init/destroy incadjlist with two vectors.", + REPEAT( + do { + igraph_incadjlist_sep_init(g, &incadj_sep, IGRAPH_ALL); + igraph_incadjlist_sep_destroy(&incadj_sep); + } while (0), repeat); + ); + + igraph_incadjlist_sep_init(g, &incadj_sep, IGRAPH_ALL); + + printf("%s", name); + BENCH("14 go over edges and vertices using incadjlist.", + REPEAT(result = test_incadj_sep(g, &incadj_sep), repeat); + ); + + igraph_incadjlist_sep_destroy(&incadj_sep); +} + +int main(void) { + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 42); + BENCH_INIT(); + + printf("Full graph tests:\n"); + igraph_full(&g, 10000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + do_benchmarks(" fg - ", &g, 1); + igraph_destroy(&g); + + printf("\nRandom graph tests:\n"); + igraph_erdos_renyi_game_gnm(&g, 10000, 49994999, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + do_benchmarks(" rg - ", &g, 1); + igraph_destroy(&g); + + printf("\nSmall graph tests:\n"); + igraph_full(&g, 1000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + do_benchmarks(" sg - ", &g, 100); + igraph_destroy(&g); + + return 0; +} diff --git a/tests/benchmarks/intersection.c b/tests/benchmarks/intersection.c new file mode 100644 index 0000000..c08a7f5 --- /dev/null +++ b/tests/benchmarks/intersection.c @@ -0,0 +1,69 @@ + +#include + +#include "bench.h" + +void rand_vec(igraph_vector_int_t *v, igraph_integer_t n, igraph_integer_t k) { + igraph_vector_int_resize(v, n); + for (igraph_integer_t i=0; i < n; i++) { + VECTOR(*v)[i] = RNG_INTEGER(0, k); + } +} + +void run_bench(int i, int n, int r) { + igraph_vector_int_t a, b; + igraph_integer_t na = n, nb = r*n; + int rep = 300000000 / nb; + char msg[255]; + + igraph_vector_int_init(&a, na); + igraph_vector_int_init(&b, nb); + + rand_vec(&a, na, nb); + igraph_vector_int_sort(&a); + rand_vec(&b, nb, nb); + igraph_vector_int_sort(&b); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + "%2d n = %5d, r = %3d, %dx", i, n, r, rep); + + /* 'volatile' is needed to prevent the compiler from optimizing + * away multiple calls to the function with the same parameters within REPEAT. + * This would normally happen due to the use of IGRAPH_FUNCATTR_PURE. + * ATTENTION! 'volatile', when used this way, may not prevent this optimization + * with future compiler versions. */ + volatile igraph_integer_t res; + BENCH(msg, REPEAT(res = igraph_vector_int_intersection_size_sorted(&a, &b), rep)); + + igraph_vector_int_destroy(&a); + igraph_vector_int_destroy(&b); +} + +int main(void) { + int i = 0; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + +#define BENCHSET(n) \ + run_bench(++i, n, 1); \ + run_bench(++i, n, 3); \ + run_bench(++i, n, 10); \ + run_bench(++i, n, 30); \ + run_bench(++i, n, 100); \ + printf("\n"); + + BENCHSET(1); + BENCHSET(3); + BENCHSET(10); + BENCHSET(30); + BENCHSET(100); + BENCHSET(300); + BENCHSET(1000); + BENCHSET(3000); + BENCHSET(10000); + BENCHSET(30000); + BENCHSET(100000); + + return 0; +} diff --git a/tests/benchmarks/lad.c b/tests/benchmarks/lad.c new file mode 100644 index 0000000..11a5675 --- /dev/null +++ b/tests/benchmarks/lad.c @@ -0,0 +1,50 @@ + +#include + +#include "bench.h" + +void match(const igraph_t *graph, + const igraph_t patt[], igraph_integer_t n, + igraph_vector_int_list_t *maps) { + + igraph_vector_int_list_clear(maps); + for (igraph_integer_t i=0; i < n; i++) { + igraph_subisomorphic_lad(&patt[i], graph, NULL, NULL, NULL, maps, false, 0); + } +} + +#define NP 8 + +int main(void) { + igraph_t graph; + igraph_t patt[NP]; + igraph_vector_int_list_t maps; + + BENCH_INIT(); + + igraph_vector_int_list_init(&maps, 0); + + for (igraph_integer_t i=0; i < NP; i++) { + igraph_ring(&patt[i], i+1, IGRAPH_DIRECTED, false, true); + } + + igraph_kautz(&graph, 3, 3); + BENCH("1 Kautz(3,3) 10x", REPEAT(match(&graph, patt, NP, &maps), 10)); + igraph_destroy(&graph); + + igraph_kautz(&graph, 3, 4); + BENCH("2 Kautz(3,4) 3x", REPEAT(match(&graph, patt, NP, &maps), 3)); + igraph_destroy(&graph); + + igraph_kautz(&graph, 4, 3); + BENCH("3 Kautz(4,3) 1x", REPEAT(match(&graph, patt, NP, &maps), 1)); + igraph_destroy(&graph); + + for (igraph_integer_t i=0; i < NP; i++) { + igraph_destroy(&patt[i]); + } + + igraph_vector_int_list_destroy(&maps); + + return 0; +} diff --git a/tests/regression/bug-1033045.c b/tests/regression/bug-1033045.c new file mode 100644 index 0000000..e69c2b0 --- /dev/null +++ b/tests/regression/bug-1033045.c @@ -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 +*/ + +#include + +#include "../unit/test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t separators; + + igraph_small(&graph, 0, /*directed=*/ 0, + 0, 1, 0, 2, 1, 3, 1, 4, 2, 3, 2, 5, 3, 4, 3, 5, 4, 6, 5, 6, -1); + igraph_vector_int_list_init(&separators, 0); + + igraph_all_minimal_st_separators(&graph, &separators); + + print_vector_int_list(&separators); + igraph_vector_int_list_destroy(&separators); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug-1033045.out b/tests/regression/bug-1033045.out new file mode 100644 index 0000000..04769a5 --- /dev/null +++ b/tests/regression/bug-1033045.out @@ -0,0 +1,11 @@ +{ + 0: ( 1 2 ) + 1: ( 0 3 4 ) + 2: ( 0 3 5 ) + 3: ( 4 5 ) + 4: ( 1 3 6 ) + 5: ( 2 3 6 ) + 6: ( 2 3 4 ) + 7: ( 1 3 5 ) + 8: ( 0 3 6 ) +} diff --git a/tests/regression/bug-1149658.c b/tests/regression/bug-1149658.c new file mode 100644 index 0000000..48fe0cf --- /dev/null +++ b/tests/regression/bug-1149658.c @@ -0,0 +1,48 @@ +/* -*- 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 + +#include "../unit/test_utilities.h" + +int main(void) { + + igraph_t graph; + igraph_vector_t mod; + + igraph_empty(&graph, 25, IGRAPH_UNDIRECTED); + igraph_vector_init(&mod, 0); + igraph_community_multilevel(&graph, /*weights=*/ 0, /*resolution=*/ 1, + /*membership=*/ 0, /*memberships=*/ 0, &mod); + + if (igraph_vector_size(&mod) != 1 || + !isnan(VECTOR(mod)[0])) { + return 1; + } + + igraph_vector_destroy(&mod); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug_1760.c b/tests/regression/bug_1760.c new file mode 100644 index 0000000..fd5bf13 --- /dev/null +++ b/tests/regression/bug_1760.c @@ -0,0 +1,159 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "../unit/test_utilities.h" + +/* Regression test for https://github.com/igraph/igraph/issues/1760 */ + +int test_unweighted(const igraph_t* g, igraph_integer_t from, const igraph_vs_t* to) { + igraph_vector_int_list_t vpath, epath; + igraph_integer_t num_paths; + igraph_vector_int_t parents; + igraph_vector_int_t inbound_edges; + + printf("Unweighted case\n"); + printf("---------------\n\n"); + + IGRAPH_CHECK(igraph_vs_size(g, to, &num_paths)); + IGRAPH_CHECK(igraph_vector_int_list_init(&vpath, 0)); + IGRAPH_CHECK(igraph_vector_int_list_init(&epath, 0)); + IGRAPH_CHECK(igraph_vector_int_init(&parents, 0)); + IGRAPH_CHECK(igraph_vector_int_init(&inbound_edges, 0)); + + IGRAPH_CHECK(igraph_get_shortest_paths( + g, &vpath, &epath, from, *to, IGRAPH_IN, + &parents, &inbound_edges + )); + + printf("Vertices:\n"); + print_vector_int_list(&vpath); + printf("\n"); + + printf("Edges:\n"); + print_vector_int_list(&epath); + printf("\n"); + + printf("Parents:\n"); + print_vector_int(&parents); + printf("\n"); + + printf("Inbound edges:\n"); + print_vector_int(&inbound_edges); + printf("\n"); + + igraph_vector_int_destroy(&inbound_edges); + igraph_vector_int_destroy(&parents); + igraph_vector_int_list_destroy(&epath); + igraph_vector_int_list_destroy(&vpath); + + return IGRAPH_SUCCESS; +} + +int test_weighted( + const igraph_t* g, const igraph_vector_t* weights, igraph_integer_t from, + const igraph_vs_t* to, igraph_bool_t use_bellman_ford +) { + igraph_vector_int_list_t vpath, epath; + igraph_integer_t num_paths; + igraph_vector_int_t parents; + igraph_vector_int_t inbound_edges; + + printf("Weighted case\n"); + printf("-------------\n\n"); + + printf("Algorithm: %s\n\n", use_bellman_ford ? "Bellman-Ford" : "Dijkstra"); + + IGRAPH_CHECK(igraph_vs_size(g, to, &num_paths)); + IGRAPH_CHECK(igraph_vector_int_list_init(&vpath, 0)); + IGRAPH_CHECK(igraph_vector_int_list_init(&epath, 0)); + IGRAPH_CHECK(igraph_vector_int_init(&parents, 0)); + IGRAPH_CHECK(igraph_vector_int_init(&inbound_edges, 0)); + + if (use_bellman_ford) { + IGRAPH_CHECK(igraph_get_shortest_paths_bellman_ford( + g, &vpath, &epath, from, *to, weights, IGRAPH_IN, + &parents, &inbound_edges + )); + } else { + IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra( + g, &vpath, &epath, from, *to, weights, IGRAPH_IN, + &parents, &inbound_edges + )); + } + + printf("Vertices:\n"); + print_vector_int_list(&vpath); + printf("\n"); + + printf("Edges:\n"); + print_vector_int_list(&epath); + printf("\n"); + + printf("Parents:\n"); + print_vector_int(&parents); + printf("\n"); + + printf("Inbound edges:\n"); + print_vector_int(&inbound_edges); + printf("\n"); + + igraph_vector_int_destroy(&inbound_edges); + igraph_vector_int_destroy(&parents); + igraph_vector_int_list_destroy(&epath); + igraph_vector_int_list_destroy(&vpath); + + return IGRAPH_SUCCESS; +} + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_vs_t to; + + igraph_set_warning_handler(igraph_warning_handler_ignore); + + igraph_small(&g, 4, /* directed = */ 1, 0, 1, 1, 2, 1, 3, -1); + igraph_vs_vector_small(&to, 0, 3, -1); + igraph_vector_init(&weights, 3); + VECTOR(weights)[0] = 1; + VECTOR(weights)[1] = 2; + VECTOR(weights)[2] = 2; + + /* Test unweighted case */ + if (test_unweighted(&g, 2, &to)) { + return 1; + } + + /* Test weighted case */ + if (test_weighted(&g, &weights, 2, &to, /* use_bellman_ford = */ 0)) { + return 2; + } + if (test_weighted(&g, &weights, 2, &to, /* use_bellman_ford = */ 1)) { + return 3; + } + + igraph_vector_destroy(&weights); + igraph_vs_destroy(&to); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug_1760.out b/tests/regression/bug_1760.out new file mode 100644 index 0000000..d1722c2 --- /dev/null +++ b/tests/regression/bug_1760.out @@ -0,0 +1,67 @@ +Unweighted case +--------------- + +Vertices: +{ + 0: ( 2 1 0 ) + 1: ( ) +} + +Edges: +{ + 0: ( 1 0 ) + 1: ( ) +} + +Parents: +( 1 2 -1 -2 ) + +Inbound edges: +( 0 1 -1 -1 ) + +Weighted case +------------- + +Algorithm: Dijkstra + +Vertices: +{ + 0: ( 2 1 0 ) + 1: ( ) +} + +Edges: +{ + 0: ( 1 0 ) + 1: ( ) +} + +Parents: +( 1 2 -1 -2 ) + +Inbound edges: +( 0 1 -1 -1 ) + +Weighted case +------------- + +Algorithm: Bellman-Ford + +Vertices: +{ + 0: ( 2 1 0 ) + 1: ( ) +} + +Edges: +{ + 0: ( 1 0 ) + 1: ( ) +} + +Parents: +( 1 2 -1 -2 ) + +Inbound edges: +( 0 1 -1 -1 ) + diff --git a/tests/regression/bug_1814.c b/tests/regression/bug_1814.c new file mode 100644 index 0000000..04be9f0 --- /dev/null +++ b/tests/regression/bug_1814.c @@ -0,0 +1,75 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "../unit/test_utilities.h" + +/* Regression test for https://github.com/igraph/igraph/issues/1814 */ + +void test_igraph_to_undirected(igraph_to_undirected_t mode) { + igraph_t g; + igraph_attribute_combination_t comb; + + igraph_small(&g, 4, IGRAPH_DIRECTED, + 0,1, 0,1, 1,0, + 1,1, 1,1, + -1); + + SETEAN(&g, "weight", 0, 1); + SETEAN(&g, "weight", 1, 2); + SETEAN(&g, "weight", 2, 3); + SETEAN(&g, "weight", 3, 4); + SETEAN(&g, "weight", 4, 2); + + igraph_attribute_combination( + &comb, "weight", IGRAPH_ATTRIBUTE_COMBINE_SUM, + IGRAPH_NO_MORE_ATTRIBUTES + ); + igraph_to_undirected(&g, mode, &comb); + igraph_attribute_combination_destroy(&comb); + + igraph_write_graph_gml(&g, stdout, IGRAPH_WRITE_GML_DEFAULT_SW, 0, ""); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); +} + +int main(void) { + igraph_set_attribute_table(&igraph_cattribute_table); + + printf("to_undirected(COLLAPSE)\n"); + printf("=======================\n\n"); + test_igraph_to_undirected(IGRAPH_TO_UNDIRECTED_COLLAPSE); + printf("\n"); + + printf("to_undirected(MUTUAL)\n"); + printf("=====================\n\n"); + test_igraph_to_undirected(IGRAPH_TO_UNDIRECTED_MUTUAL); + printf("\n"); + + printf("to_undirected(EACH)\n"); + printf("===================\n\n"); + test_igraph_to_undirected(IGRAPH_TO_UNDIRECTED_EACH); + printf("\n"); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug_1814.out b/tests/regression/bug_1814.out new file mode 100644 index 0000000..89cb99a --- /dev/null +++ b/tests/regression/bug_1814.out @@ -0,0 +1,135 @@ +to_undirected(COLLAPSE) +======================= + +Version 1 +graph +[ + directed 0 + node + [ + id 0 + ] + node + [ + id 1 + ] + node + [ + id 2 + ] + node + [ + id 3 + ] + edge + [ + source 1 + target 0 + weight 6 + ] + edge + [ + source 1 + target 1 + weight 6 + ] +] + +to_undirected(MUTUAL) +===================== + +Version 1 +graph +[ + directed 0 + node + [ + id 0 + ] + node + [ + id 1 + ] + node + [ + id 2 + ] + node + [ + id 3 + ] + edge + [ + source 1 + target 0 + weight 5 + ] + edge + [ + source 1 + target 1 + weight 2 + ] + edge + [ + source 1 + target 1 + weight 4 + ] +] + +to_undirected(EACH) +=================== + +Version 1 +graph +[ + directed 0 + node + [ + id 0 + ] + node + [ + id 1 + ] + node + [ + id 2 + ] + node + [ + id 3 + ] + edge + [ + source 1 + target 0 + weight 1 + ] + edge + [ + source 1 + target 0 + weight 2 + ] + edge + [ + source 1 + target 0 + weight 3 + ] + edge + [ + source 1 + target 1 + weight 4 + ] + edge + [ + source 1 + target 1 + weight 2 + ] +] + diff --git a/tests/regression/bug_1970.c b/tests/regression/bug_1970.c new file mode 100644 index 0000000..026f7ac --- /dev/null +++ b/tests/regression/bug_1970.c @@ -0,0 +1,44 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "../unit/test_utilities.h" + +int main(void) { + FILE *file; + igraph_t graph; + igraph_error_t err; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_set_error_handler(igraph_error_handler_printignore); + + file = fopen("bug_1970.graphml", "r"); + IGRAPH_ASSERT(file != NULL); + + err = igraph_read_graph_graphml(&graph, file, 0); + fclose(file); + IGRAPH_ASSERT(err != IGRAPH_SUCCESS); + + /* graph creation should have failed, no need to destroy 'graph' */ + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug_1970.graphml b/tests/regression/bug_1970.graphml new file mode 100644 index 0000000..f2f4a8c --- /dev/null +++ b/tests/regression/bug_1970.graphml @@ -0,0 +1 @@ + diff --git a/tests/regression/bug_2150.c b/tests/regression/bug_2150.c new file mode 100644 index 0000000..50bde54 --- /dev/null +++ b/tests/regression/bug_2150.c @@ -0,0 +1,47 @@ +/* -*- 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 + +*/ + +#include + +#include "../unit/test_utilities.h" + +/* Regression test for https://github.com/igraph/igraph/issues/2150 */ + +int main(void) { + igraph_t graph; + igraph_vector_t w; + igraph_vector_int_list_t res; + + igraph_full(&graph, 3, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_init_int(&w, 3, 1, 2, 3); + igraph_vector_int_list_init(&res, 0); + + igraph_weighted_cliques(&graph, &w, &res, 1, 3, false); + + igraph_vector_int_list_destroy(&res); + igraph_vector_destroy(&w); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug_2497.c b/tests/regression/bug_2497.c new file mode 100644 index 0000000..7038818 --- /dev/null +++ b/tests/regression/bug_2497.c @@ -0,0 +1,48 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "../unit/test_utilities.h" + +int main(void) { + FILE *file; + igraph_t graph; + + igraph_set_attribute_table(&igraph_cattribute_table); + igraph_set_error_handler(igraph_error_handler_ignore); + igraph_set_warning_handler(igraph_warning_handler_ignore); + + file = fopen("bug_2497.gml", "r"); + IGRAPH_ASSERT(file != NULL); + + IGRAPH_ASSERT(IGRAPH_FINALLY_STACK_EMPTY); + IGRAPH_ASSERT(igraph_read_graph_gml(&graph, file) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(IGRAPH_FINALLY_STACK_EMPTY); + + fclose(file); + + igraph_write_graph_gml(&graph, stdout, IGRAPH_WRITE_GML_DEFAULT_SW, NULL, ""); + IGRAPH_ASSERT(IGRAPH_FINALLY_STACK_EMPTY); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug_2497.gml b/tests/regression/bug_2497.gml new file mode 100644 index 0000000..5014ac6 --- /dev/null +++ b/tests/regression/bug_2497.gml @@ -0,0 +1,4 @@ +graph [ + node [ id 2 ] + node [ ] +] diff --git a/tests/regression/bug_2497.out b/tests/regression/bug_2497.out new file mode 100644 index 0000000..e6a1cd4 --- /dev/null +++ b/tests/regression/bug_2497.out @@ -0,0 +1,13 @@ +Version 1 +graph +[ + directed 0 + node + [ + id 0 + ] + node + [ + id 1 + ] +] diff --git a/tests/regression/bug_2506.c b/tests/regression/bug_2506.c new file mode 100644 index 0000000..5803178 --- /dev/null +++ b/tests/regression/bug_2506.c @@ -0,0 +1,68 @@ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +#include "../unit/test_utilities.h" + +int main(void) { + igraph_t g; + igraph_error_t result; + FILE *ifile; + + igraph_set_error_handler(igraph_error_handler_ignore); + + igraph_set_attribute_table(&igraph_cattribute_table); + + ifile = fopen("bug_2506_1.graphml", "r"); + IGRAPH_ASSERT(ifile != NULL); + + result = igraph_read_graph_graphml(&g, ifile, 0); + fclose(ifile); + + if (result == IGRAPH_UNIMPLEMENTED) { + /* maybe it is simply disabled at compile-time */ + return 77; + } + + IGRAPH_ASSERT(result == IGRAPH_PARSEERROR); + IGRAPH_ASSERT(IGRAPH_FINALLY_STACK_EMPTY); + + ifile = fopen("bug_2506_2.graphml", "r"); + IGRAPH_ASSERT(ifile != NULL); + + result = igraph_read_graph_graphml(&g, ifile, 0); + fclose(ifile); + + IGRAPH_ASSERT(result == IGRAPH_PARSEERROR); + IGRAPH_ASSERT(IGRAPH_FINALLY_STACK_EMPTY); + + ifile = fopen("bug_2506_3.graphml", "r"); + IGRAPH_ASSERT(ifile != NULL); + + result = igraph_read_graph_graphml(&g, ifile, 0); + fclose(ifile); + + IGRAPH_ASSERT(result == IGRAPH_PARSEERROR); + IGRAPH_ASSERT(IGRAPH_FINALLY_STACK_EMPTY); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug_2506_1.graphml b/tests/regression/bug_2506_1.graphml new file mode 100644 index 0000000..5ab8579 --- /dev/null +++ b/tests/regression/bug_2506_1.graphml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/regression/bug_2506_2.graphml b/tests/regression/bug_2506_2.graphml new file mode 100644 index 0000000..0e31209 --- /dev/null +++ b/tests/regression/bug_2506_2.graphml @@ -0,0 +1 @@ + diff --git a/tests/regression/bug_2506_3.graphml b/tests/regression/bug_2506_3.graphml new file mode 100644 index 0000000..a2297e0 --- /dev/null +++ b/tests/regression/bug_2506_3.graphml @@ -0,0 +1 @@ + diff --git a/tests/regression/bug_2517.c b/tests/regression/bug_2517.c new file mode 100644 index 0000000..0ecfeb2 --- /dev/null +++ b/tests/regression/bug_2517.c @@ -0,0 +1,44 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "../unit/test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_list_t result; + + igraph_vector_int_list_init(&result, 0); + + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0, 1, -1); + igraph_all_minimal_st_separators(&g, &result); + print_vector_int_list(&result); + igraph_destroy(&g); + + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0, 1, -1); + igraph_all_minimal_st_separators(&g, &result); + print_vector_int_list(&result); + igraph_destroy(&g); + + igraph_vector_int_list_destroy(&result); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/bug_2517.out b/tests/regression/bug_2517.out new file mode 100644 index 0000000..8a0e2c0 --- /dev/null +++ b/tests/regression/bug_2517.out @@ -0,0 +1,4 @@ +{ +} +{ +} diff --git a/tests/regression/cattr_bool_bug.c b/tests/regression/cattr_bool_bug.c new file mode 100644 index 0000000..7b8e102 --- /dev/null +++ b/tests/regression/cattr_bool_bug.c @@ -0,0 +1,73 @@ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +#include "../unit/test_utilities.h" + +void check_attr(igraph_t *graph) { + IGRAPH_ASSERT(igraph_cattribute_has_attr(graph, IGRAPH_ATTRIBUTE_GRAPH, "name")); + IGRAPH_ASSERT(igraph_cattribute_has_attr(graph, IGRAPH_ATTRIBUTE_GRAPH, "type")); + IGRAPH_ASSERT(igraph_cattribute_has_attr(graph, IGRAPH_ATTRIBUTE_GRAPH, "p")); + IGRAPH_ASSERT(igraph_cattribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, "name")); + IGRAPH_ASSERT(igraph_cattribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, "weight")); +} + +int main(void) { + + igraph_t graph; + igraph_error_handler_t* oldhandler; + int result; + FILE *ifile = fopen("cattr_bool_bug.graphml", "r"); + + if (!ifile) { + printf("Cannot open input file\n"); + return 1; + } + + igraph_set_attribute_table(&igraph_cattribute_table); + + oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); + if ((result = igraph_read_graph_graphml(&graph, ifile, 0))) { + /* Maybe it is simply disabled at compile-time. If so, skip test. */ + if (result == IGRAPH_UNIMPLEMENTED) { + return 77; + } + printf("Failed to read GraphML file\n"); + return 1; + } + igraph_set_error_handler(oldhandler); + + fclose(ifile); + + printf("Checkng attributes of original graph\n"); + check_attr(&graph); + + printf("Checkng attributes of directed version\n"); + igraph_to_directed(&graph, IGRAPH_TO_DIRECTED_ARBITRARY); + check_attr(&graph); + + IGRAPH_ASSERT(! GAB(&graph, "loops")); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/cattr_bool_bug.graphml b/tests/regression/cattr_bool_bug.graphml new file mode 100644 index 0000000..8ed39fe --- /dev/null +++ b/tests/regression/cattr_bool_bug.graphml @@ -0,0 +1,76 @@ + + + + + + + + + + + Erdos renyi (gnp) graph + gnp + false + 0.2 + + n0 + + + n1 + + + n2 + + + n3 + + + n4 + + + n5 + + + n6 + + + n7 + + + n8 + + + n9 + + + 10 + + + 11 + + + 12 + + + 13 + + + 14 + + + 15 + + + 16 + + + 17 + + + 18 + + + diff --git a/tests/regression/cattr_bool_bug2.c b/tests/regression/cattr_bool_bug2.c new file mode 100644 index 0000000..5a4cc82 --- /dev/null +++ b/tests/regression/cattr_bool_bug2.c @@ -0,0 +1,63 @@ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +#include "../unit/test_utilities.h" + +#define FILENAME "mybool.graphml.xml" + +int main(void) { + + igraph_t graph; + igraph_error_handler_t* oldhandler; + int result; + FILE* ifile = fopen("cattr_bool_bug2.graphml", "r"); + + if (!ifile) { + printf("Cannot open input file\n"); + return 1; + } + + igraph_set_attribute_table(&igraph_cattribute_table); + + oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); + if ((result = igraph_read_graph_graphml(&graph, ifile, 0))) { + /* maybe it is simply disabled at compile-time */ + if (result == IGRAPH_UNIMPLEMENTED) { + return 77; + } + printf("Failed to read GraphML file\n"); + return 1; + } + igraph_set_error_handler(oldhandler); + + fclose(ifile); + + IGRAPH_ASSERT(igraph_cattribute_has_attr(&graph, IGRAPH_ATTRIBUTE_GRAPH, "mybool")); + + /* Boolean attribute value is expected to be true */ + IGRAPH_ASSERT(igraph_cattribute_GAB(&graph, "mybool")); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/cattr_bool_bug2.graphml b/tests/regression/cattr_bool_bug2.graphml new file mode 100644 index 0000000..92c78f6 --- /dev/null +++ b/tests/regression/cattr_bool_bug2.graphml @@ -0,0 +1,7 @@ + + + + + True + + diff --git a/tests/regression/igraph_layout_kamada_kawai_3d_bug_1462.c b/tests/regression/igraph_layout_kamada_kawai_3d_bug_1462.c new file mode 100644 index 0000000..578b27b --- /dev/null +++ b/tests/regression/igraph_layout_kamada_kawai_3d_bug_1462.c @@ -0,0 +1,66 @@ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +#include "../unit/test_utilities.h" + +void snap_to_zero(igraph_real_t* value) { + if (fabs(*value) < 1e-5) { + *value = 0.0; + } +} + +int main(void) { + igraph_t graph; + igraph_matrix_t layout; + igraph_integer_t i; + + if (igraph_empty(&graph, 2, 0)) { + return 1; + } + + if (igraph_add_edge(&graph, 0, 1)) { + return 2; + } + + if (igraph_matrix_init(&layout, 0, 0)) { + return 3; + } + + if (igraph_layout_kamada_kawai_3d(&graph, &layout, 0, 200, 0, 2, 0, 0, 0, 0, 0, 0, 0)) { + return 4; + } + + /* Snap numbers close to zero in the layout; there are false failures on + * MinGW if we don't do so */ + for (i = 0; i < 2; i++) { + snap_to_zero(&MATRIX(layout, i, 0)); + snap_to_zero(&MATRIX(layout, i, 1)); + snap_to_zero(&MATRIX(layout, i, 2)); + } + print_matrix_format(&layout, stdout, "%.2f"); + + igraph_matrix_destroy(&layout); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/igraph_layout_kamada_kawai_3d_bug_1462.out b/tests/regression/igraph_layout_kamada_kawai_3d_bug_1462.out new file mode 100644 index 0000000..f50fbcb --- /dev/null +++ b/tests/regression/igraph_layout_kamada_kawai_3d_bug_1462.out @@ -0,0 +1,2 @@ +[ 0.00 0.00 -0.91 + 0.00 0.00 0.51 ] diff --git a/tests/regression/igraph_layout_reingold_tilford_bug_879.c b/tests/regression/igraph_layout_reingold_tilford_bug_879.c new file mode 100644 index 0000000..4024528 --- /dev/null +++ b/tests/regression/igraph_layout_reingold_tilford_bug_879.c @@ -0,0 +1,56 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA +*/ + +#include + +#include "../unit/test_utilities.h" + +int main(void) { + + igraph_t g; + FILE *f; + igraph_matrix_t coords; + igraph_vector_int_t roots; + igraph_integer_t i, n; + + f = fopen("igraph_layout_reingold_tilford_bug_879.in", "r"); + IGRAPH_ASSERT(f != NULL); + igraph_read_graph_edgelist(&g, f, 0, IGRAPH_UNDIRECTED); + igraph_matrix_init(&coords, 0, 0); + igraph_vector_int_init(&roots, 0); + igraph_vector_int_push_back(&roots, 0); + + igraph_layout_reingold_tilford(&g, &coords, IGRAPH_OUT, &roots, 0); + + n = igraph_vcount(&g); + for (i = 0; i < n; i++) { + printf("%6.3f %6.3f\n", MATRIX(coords, i, 0), MATRIX(coords, i, 1)); + } + + igraph_matrix_destroy(&coords); + igraph_vector_int_destroy(&roots); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/igraph_layout_reingold_tilford_bug_879.in b/tests/regression/igraph_layout_reingold_tilford_bug_879.in new file mode 100644 index 0000000..6ea94ee --- /dev/null +++ b/tests/regression/igraph_layout_reingold_tilford_bug_879.in @@ -0,0 +1,15 @@ +0 1 +0 2 +0 3 +1 4 +2 5 +2 6 +3 7 +4 8 +4 9 +4 10 +4 11 +4 12 +7 13 +7 14 +7 15 diff --git a/tests/regression/igraph_layout_reingold_tilford_bug_879.out b/tests/regression/igraph_layout_reingold_tilford_bug_879.out new file mode 100644 index 0000000..b916553 --- /dev/null +++ b/tests/regression/igraph_layout_reingold_tilford_bug_879.out @@ -0,0 +1,16 @@ + 0.000 0.000 +-1.833 1.000 +-0.333 1.000 + 2.167 1.000 +-1.833 2.000 +-0.833 2.000 + 0.167 2.000 + 2.167 2.000 +-3.833 3.000 +-2.833 3.000 +-1.833 3.000 +-0.833 3.000 + 0.167 3.000 + 1.167 3.000 + 2.167 3.000 + 3.167 3.000 diff --git a/tests/regression/igraph_read_graph_gml_invalid_inputs.c b/tests/regression/igraph_read_graph_gml_invalid_inputs.c new file mode 100644 index 0000000..dabf855 --- /dev/null +++ b/tests/regression/igraph_read_graph_gml_invalid_inputs.c @@ -0,0 +1,74 @@ +/* + IGraph library. + Copyright (C) 2021-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 +*/ + +#include +#include + +#include "../unit/test_utilities.h" + +int test_file(const char* fname) { + FILE *ifile; + igraph_t g; + int retval; + + ifile = fopen(fname, "r"); + if (ifile == 0) { + return 1; + } + + retval = igraph_read_graph_gml(&g, ifile); + if (!retval) { + /* input was accepted, this is a bug; attempt to clean up after + * ourselves nevertheless */ + igraph_destroy(&g); + fclose(ifile); + return 2; + } + + fclose(ifile); + + return 0; +} + +#undef RUN_TEST +#define RUN_TEST(fname) { \ + index++; \ + if (test_file(fname)) { \ + return index; \ + } \ + VERIFY_FINALLY_STACK(); \ +} + +int main(void) { + int index = 0; + + /* We do not care about errors; all we care about is that the library + * should not segfault and should not accept invalid input either */ + igraph_set_error_handler(igraph_error_handler_ignore); + + RUN_TEST("invalid1.gml"); + RUN_TEST("invalid2.gml"); + /* invalid3.gml was removed, parser now supports it */ + RUN_TEST("invalid4.gml"); + RUN_TEST("invalid5.gml"); + RUN_TEST("invalid6.gml"); + + return 0; +} diff --git a/tests/regression/igraph_read_graph_graphml_invalid_inputs.c b/tests/regression/igraph_read_graph_graphml_invalid_inputs.c new file mode 100644 index 0000000..077961c --- /dev/null +++ b/tests/regression/igraph_read_graph_graphml_invalid_inputs.c @@ -0,0 +1,80 @@ +/* + 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 +*/ + +#include +#include + +#include "../unit/test_utilities.h" + +int test_file(const char* fname, igraph_bool_t should_parse) { + FILE *ifile; + igraph_t g; + igraph_error_t retval; + + ifile = fopen(fname, "r"); + if (ifile == 0) { + printf("Cannot open input file: %s\n", fname); + return 1; + } + + retval = igraph_read_graph_graphml(&g, ifile, 0); + if (retval == IGRAPH_SUCCESS) { + /* destroy the graph if we managed to parse it */ + igraph_destroy(&g); + } + + fclose(ifile); + + if (!should_parse && retval == IGRAPH_SUCCESS) { + /* input was accepted but it should not have been, this is a bug */ + return 2; + } else { + return 0; + } +} + +#undef RUN_TEST +#define RUN_TEST(fname, should_parse) { \ + index++; \ + if (test_file(fname, should_parse)) { \ + return index; \ + } \ + VERIFY_FINALLY_STACK(); \ +} + +int main(void) { + int index = 0; + + /* We do not care about errors; all we care about is that the library + * should not segfault, should not accept invalid input and should not + * print anything to stdout or stderr while parsing, apart from warnings */ + igraph_set_error_handler(igraph_error_handler_ignore); + igraph_set_warning_handler(igraph_warning_handler_ignore); + + RUN_TEST("invalid1.graphml", /* should_parse = */ 0); + RUN_TEST("invalid2.graphml", /* should_parse = */ 1); + RUN_TEST("invalid3.graphml", /* should_parse = */ 0); + RUN_TEST("invalid4.graphml", /* should_parse = */ 0); + RUN_TEST("invalid5.graphml", /* should_parse = */ 0); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/regression/igraph_read_graph_graphml_invalid_inputs.out b/tests/regression/igraph_read_graph_graphml_invalid_inputs.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/regression/igraph_read_graph_pajek_invalid_inputs.c b/tests/regression/igraph_read_graph_pajek_invalid_inputs.c new file mode 100644 index 0000000..45e0bc4 --- /dev/null +++ b/tests/regression/igraph_read_graph_pajek_invalid_inputs.c @@ -0,0 +1,70 @@ +/* + 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 +*/ + +#include +#include + +int test_file(const char* fname) { + FILE *ifile; + igraph_t g; + int retval; + + ifile = fopen(fname, "r"); + if (ifile == 0) { + return 1; + } + + retval = igraph_read_graph_pajek(&g, ifile); + if (!retval) { + /* input was accepted, this is a bug; attempt to clean up after + * ourselves nevertheless */ + igraph_destroy(&g); + fclose(ifile); + return 2; + } + + fclose(ifile); + + return 0; +} + +#define RUN_TEST(fname) { \ + index++; \ + if (test_file(fname)) { \ + return index; \ + } \ +} + +int main(void) { + int index = 0; + + /* Turn on attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* We do not care about errors; all we care about is that the library + * should not segfault and should not accept invalid input either */ + igraph_set_error_handler(igraph_error_handler_ignore); + + RUN_TEST("invalid_pajek1.net"); + RUN_TEST("invalid_pajek2.net"); + RUN_TEST("invalid_pajek3.net"); + + return 0; +} diff --git a/tests/regression/invalid1.gml b/tests/regression/invalid1.gml new file mode 100644 index 0000000..38d8e42 --- /dev/null +++ b/tests/regression/invalid1.gml @@ -0,0 +1,463 @@ +Creator "Mark Newman on Fri Jul 21 12:39:27 2006" +graph +[ + ntde + [ + id 1 + ] + node + [ + id 2 + ] + node + [ + id 3 + ] + node + [ + id 4 + ] + node + [ + id 5 + ] + node + [ + id 6 + ] + node + [ + id 7 + ] + node + [ + id 8 + ] + node + [ + id 9 + ] + node + [ + id 10 + ] + node + [ + id 11 + ] + node + [ + id 12 + ] + node + [ + id 13 + ] + node + [ + id 14 + ] + node + [ + id 15 + ] + node + [ + id 16 + ] + node + [ + id 17 + ] + node + [ + id 18 + ] + node + [ + id 19 + ] + node + [ + id 20 + ] + node + [ + id 21 + ] + node + [ + id 22 + ] + node + [ + id 23 + ] + node + [ + id 24 + ] + node + [ + id 25 + ] + node + [ + id 26 + ] + node + [ + id 27 + ] + node + [ + id 28 + ] + node + [ + id 29 + ] + node + [ + id 30 + ] + node + [ + id 31 + ] + node + [ + id 32 + ] + node + [ + id 33 + ] + node + [ + id 34 + ] + edge + [ + source 2 + target 1 + ] + edge + [ + source 3 + target 1 + ] + edge + [ + source 3 + target 2 + ] + edge + [ + source 4 + target 1 + ] + edge + [ + source 4 + target 2 + ] + edge + [ + source 4 + target 3 + ] + edge + [ + source 5 + target 1 + ] + edge + [ + source 2 + target 1 + ] + edge + [ + source 7 + target 1 + ] + edge + [ + source 7 + target 5 + ] + edge + [ + source 7 + target 6 + ] + edge + [ + source 8 + target 1 + ] + edge + [ + source 8 + target 2 + ] + edge + [ + source 8 + target 3 + ] + edge + [ + source 8 + target 4 + ] + edge + [ + source 9 + target 1 + ] + edge + [ + source 9 + target 3 + ] + edge + [ + source 10 + target 3 + ] + edge + [ + source 11 + target 1 + + source 20 + target 1 + ] + edge + [ + source 20 + target 2 + ] + edge + [ + source 22 + target 1 + ] + edge + [ + source 22 + target 2 + ] + edge + [ + source 26 + target 24 + ] + edge + [ + source 26 + target 25 + ] + edge + [ + source 28 + target 3 + ] + edge + [ + source 28 + target 24 + ] + edge + [ + source 28 + target 25 + ] + edge + [ + source 29 + target 3 + ] + edge + [ + source 30 + target 24 + ] + edge + [ + source 30 + target 27 + ] + edge + [ + source 31 + target 2 + ] + edge + [ + source 31 + target 9 + ] + edge + [ + source 32 + target 1 + ] + edge + [ + source 32 + target 25 + ] + edge + [ + source 32 + target 26 + ] + edge + [ + source 32 + target 29 + ] + edge + [ + source 33 + target 3 + ] + edge + [ + source 33 + target 9 + ] + edge + [ + source 33 + target 15 + ] + edge + [ + source 33 + target 16 + ] + edge + [ + source 33 + target 19 + ] + edge + [ + source 33 + target 21 + ] + edge + [ + source 33 + target 23 + ] + edge + [ + source 33 + target 24 + ] + edge + [ + source 33 + target 30 + ] + edge + [ + source 33 + target 31 + ] + edge + [ + source 33 + target 32 + ] + edge + [ + source 34 + target 9 + ] + edge + [ + source 34 + target 10 + ] + edge + [ + source 34 + target 14 + ] + edge + [ + source 34 + target 15 + ] + edge + [ + source 34 + target 16 + ] + edge + [ + source 34 + target 19 + ] + edge + [ + source 34 + target 20 + ] + edge + [ + source 34 + target 21 + ] + edge + [ + source 34 + target 23 + ] + edge + [ + source 34 + target 24 + ] + edge + [ + source 34 + target 27 + ] + edge + [ + source 34 + target 28 + ] + edge + [ + source 34 + target 29 + ] + edge + [ + source 17 + target 30 + ] + edge + [ + source 34 + target 31 + ] + edge + [ + source 34 + target 32 + ] + edge + [ + source 34 + target 33 + ] +] diff --git a/tests/regression/invalid1.graphml b/tests/regression/invalid1.graphml new file mode 100644 index 0000000..c370a24 --- /dev/null +++ b/tests/regression/invalid1.graphml @@ -0,0 +1,18 @@ + + + + + yellow + + + + + + , 1 + + + 2006-11-12* + blue + + + diff --git a/tests/regression/invalid2.gml b/tests/regression/invalid2.gml new file mode 100644 index 0000000..bc4d7ba --- /dev/null +++ b/tests/regression/invalid2.gml @@ -0,0 +1 @@ + d 1 diff --git a/tests/regression/invalid2.graphml b/tests/regression/invalid2.graphml new file mode 100644 index 0000000..3f4ea6f --- /dev/null +++ b/tests/regression/invalid2.graphml @@ -0,0 +1,26 @@ + + + + + yellow + + + + @ yellowwe + +"http://graphml.graphdrawingg/2001/XMLrawing.org/xmlns/1.0/graphml.xsd"> + + yellow + + + + + @ yellowwe + + + 1 + + + /edge> + + diff --git a/tests/regression/invalid3.graphml b/tests/regression/invalid3.graphml new file mode 100644 index 0000000..e69de29 diff --git a/tests/regression/invalid4.gml b/tests/regression/invalid4.gml new file mode 100644 index 0000000..709a772 --- /dev/null +++ b/tests/regression/invalid4.gml @@ -0,0 +1 @@ +Version-0 diff --git a/tests/regression/invalid4.graphml b/tests/regression/invalid4.graphml new file mode 100644 index 0000000000000000000000000000000000000000..739907ac460acf51592c0c9adf36c00ade6c8bb2 GIT binary patch literal 92 lcmcCvKn9M;98|Uf3b(W*%>pd&3q=^hQ7FqxXK1iJ4FHX!1`+@O literal 0 HcmV?d00001 diff --git a/tests/regression/invalid5.gml b/tests/regression/invalid5.gml new file mode 100644 index 0000000..672d31f --- /dev/null +++ b/tests/regression/invalid5.gml @@ -0,0 +1,4 @@ +graph[node[a +r +r P p +r d v]] diff --git a/tests/regression/invalid5.graphml b/tests/regression/invalid5.graphml new file mode 100644 index 0000000..26c40d7 --- /dev/null +++ b/tests/regression/invalid5.graphml @@ -0,0 +1,30 @@ + + + + yellYw + + + + 1 + ta> + green + + true + + + + blue + 0 + red "w" + + false + + + t + + i + + + + + 334 Harvard street, Cambridge, MA 02139 USA + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + 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 "core/indheap.h" + +#include "test_utilities.h" + +int main(void) { + + igraph_vector_t elems; + igraph_2wheap_t Q; + igraph_integer_t i; + igraph_real_t prev = IGRAPH_INFINITY; + + igraph_rng_seed(igraph_rng_default(), 42); /* make tests deterministic */ + + RNG_BEGIN(); + + igraph_vector_init(&elems, 100); + for (i = 0; i < igraph_vector_size(&elems); i++) { + VECTOR(elems)[i] = RNG_UNIF01(); + } + + igraph_2wheap_init(&Q, igraph_vector_size(&elems)); + for (i = 0; i < igraph_vector_size(&elems); i++) { + igraph_2wheap_push_with_index(&Q, i, VECTOR(elems)[i]); + } + + /*****/ + + for (i = 0; i < igraph_vector_size(&elems); i++) { + if (VECTOR(elems)[i] != igraph_2wheap_get(&Q, i)) { + return 1; + } + } + + /*****/ + + for (i = 0; i < igraph_vector_size(&elems); i++) { + igraph_integer_t j; + igraph_real_t tmp = igraph_2wheap_max(&Q); + if (tmp > prev) { + return 2; + } + if (tmp != igraph_2wheap_delete_max_index(&Q, &j)) { + return 3; + } + if (VECTOR(elems)[j] != tmp) { + return 4; + } + prev = tmp; + } + + /*****/ + + for (i = 0; i < igraph_vector_size(&elems); i++) { + igraph_2wheap_push_with_index(&Q, i, VECTOR(elems)[i]); + } + if (igraph_2wheap_size(&Q) != igraph_vector_size(&elems)) { + return 5; + } + for (i = 0; i < igraph_vector_size(&elems); i++) { + VECTOR(elems)[i] = RNG_UNIF01(); + igraph_2wheap_modify(&Q, i, VECTOR(elems)[i]); + } + for (i = 0; i < igraph_vector_size(&elems); i++) { + if (VECTOR(elems)[i] != igraph_2wheap_get(&Q, i)) { + return 6; + } + } + prev = IGRAPH_INFINITY; + for (i = 0; i < igraph_vector_size(&elems); i++) { + igraph_integer_t j; + igraph_real_t tmp = igraph_2wheap_max(&Q); + if (tmp > prev) { + return 7; + } + if (tmp != igraph_2wheap_delete_max_index(&Q, &j)) { + return 8; + } + if (VECTOR(elems)[j] != tmp) { + return 9; + } + prev = tmp; + } + if (!igraph_2wheap_empty(&Q)) { + return 10; + } + if (igraph_2wheap_size(&Q) != 0) { + return 11; + } + + igraph_2wheap_destroy(&Q); + igraph_vector_destroy(&elems); + + /* Hand-made example */ + +#define MAX do { igraph_2wheap_delete_max(&Q); igraph_2wheap_check(&Q); } while (0) +#define PUSH(i,e) do { igraph_2wheap_push_with_index(&Q, (i), -(e)); igraph_2wheap_check(&Q); } while (0); +#define MOD(i, e) do { igraph_2wheap_modify(&Q, (i), -(e)); igraph_2wheap_check(&Q); } while (0) + + igraph_2wheap_init(&Q, 21); + /* 0.00 [ 4] */ PUSH(4, 0); + /* MAX */ MAX; + /* 0.63 [11] */ PUSH(11, 0.63); + /* 0.05 [15] */ PUSH(15, 0.05); + /* MAX */ MAX; + /* 0.4 [12] */ PUSH(12, 0.4); + /* 0.4 [13] */ PUSH(13, 0.4); + /* 0.12 [16] */ PUSH(16, 0.12); + /* MAX */ MAX; + /* 1.1 [ 0] */ PUSH(0, 1.1); + /* 1.1 [14] */ PUSH(14, 1.1); + /* MAX */ MAX; + /* [11]/0.44 */ MOD(11, 0.44); + /* MAX */ MAX; + /* MAX */ MAX; + /* 1.1 [20] */ PUSH(20, 1.1); + /* MAX */ MAX; + /* 1.3 [ 7] */ PUSH(7, 1.3); + /* 1.7 [ 9] */ PUSH(9, 1.7); + /* MAX */ MAX; + /* 1.6 [19] */ PUSH(19, 1.6); + /* MAX */ MAX; + /* 2.1 [17] */ PUSH(17, 2.1); + /* 1.3 [18] */ PUSH(18, 1.3); + /* MAX */ MAX; + /* 2.3 [ 1] */ PUSH(1, 2.3); + /* 2.2 [ 5] */ PUSH(5, 2.2); + /* 2.3 [10] */ PUSH(10, 2.3); + /* MAX */ MAX; + /* [17]/1.5 */ MOD(17, 1.5); + /* MAX */ MAX; + /* 1.8 [ 6] */ PUSH(6, 1.8); + /* MAX */ MAX; + /* 1.3 [ 3] */ PUSH(3, 1.3); + /* [ 6]/1.3 */ MOD(6, 1.3); + /* MAX */ MAX; + /* 1.6 [ 8] */ PUSH(8, 1.6); + /* MAX */ MAX; + + igraph_2wheap_destroy(&Q); + + VERIFY_FINALLY_STACK(); + + RNG_END(); + + return 0; +} diff --git a/tests/unit/VF2-compat.c b/tests/unit/VF2-compat.c new file mode 100644 index 0000000..8460e70 --- /dev/null +++ b/tests/unit/VF2-compat.c @@ -0,0 +1,258 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +/* ----------------------------------------------------------- */ + +/* Vertices/edges with the same parity match */ +igraph_bool_t compat_parity(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_integer_t g1_num, + const igraph_integer_t g2_num, + void *arg) { + IGRAPH_UNUSED(graph1); + IGRAPH_UNUSED(graph2); + IGRAPH_UNUSED(arg); + return (g1_num % 2) == (g2_num % 2); +} + +/* Nothing vertex/edge 0 in graph1 */ +igraph_bool_t compat_not0(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_integer_t g1_num, + const igraph_integer_t g2_num, + void *arg) { + IGRAPH_UNUSED(graph1); + IGRAPH_UNUSED(graph2); + IGRAPH_UNUSED(arg); + IGRAPH_UNUSED(g2_num); + return g1_num != 0; +} + +int match_rings(void) { + + igraph_t r1, r2; + igraph_bool_t iso; + igraph_integer_t count; + igraph_ring(&r1, 10, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + igraph_ring(&r2, 10, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + + igraph_isomorphic_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + /*node_compat_fn=*/ 0, /*edge_compat_fn=*/ 0, + /*arg=*/ 0); + if (!iso) { + exit(1); + } + + igraph_isomorphic_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + compat_parity, /*edge_compat_fn=*/ 0, /*arg=*/ 0); + if (!iso) { + exit(2); + } + + igraph_isomorphic_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + compat_not0, /*edge_compat_fn=*/ 0, /*arg=*/ 0); + if (iso) { + exit(3); + } + + /* ------- */ + + igraph_isomorphic_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + /*node_compat_fn=*/ 0, compat_parity, /*arg=*/ 0); + if (!iso) { + exit(4); + } + + igraph_isomorphic_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + /*node_compat_fn=*/ 0, compat_not0, /*arg=*/ 0); + if (iso) { + exit(5); + } + + /* ------- */ + + igraph_count_isomorphisms_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &count, /*node_compat_fn=*/ 0, + /*edge_compat_fn=*/ 0, /*arg=*/ 0); + + if (count != 20) { + exit(6); + } + + igraph_count_isomorphisms_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &count, compat_parity, /*edge_compat_fn=*/ 0, + /*arg=*/ 0); + + if (count != 10) { + exit(7); + } + + igraph_count_isomorphisms_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &count, compat_not0, /*edge_compat_fn=*/ 0, + /*arg=*/ 0); + + if (count != 0) { + exit(8); + } + + /* ------- */ + + igraph_count_isomorphisms_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &count, /*node_compat_fn=*/ 0, compat_parity, + /*arg=*/ 0); + + if (count != 10) { + exit(9); + } + + igraph_count_isomorphisms_vf2(&r1, &r2, /*colors(4x)*/ 0, 0, 0, 0, + &count, /*node_compat_fn=*/ 0, compat_not0, + /*arg=*/ 0); + + if (count != 0) { + exit(10); + } + + igraph_destroy(&r1); + igraph_destroy(&r2); + return 0; +} + +int match_rings_open_closed(void) { + igraph_t ro, rc; + igraph_bool_t iso; + igraph_integer_t count; + igraph_ring(&ro, 10, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 0); + igraph_ring(&rc, 10, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + + igraph_subisomorphic_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + /*node_compat_fn=*/ 0, /*edge_compat_fn=*/ 0, + /*arg=*/ 0); + if (!iso) { + exit(31); + } + + igraph_subisomorphic_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + compat_parity, /*edge_compat_fn=*/ 0, + /*arg=*/ 0); + if (!iso) { + exit(32); + } + + igraph_subisomorphic_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + compat_not0, /*edge_compat_fn=*/ 0, + /*arg=*/ 0); + if (iso) { + exit(33); + } + + /* ------- */ + + igraph_subisomorphic_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + /*node_compat_fn=*/ 0, compat_parity, + /*arg=*/ 0); + if (!iso) { + exit(34); + } + + igraph_subisomorphic_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &iso, /*map12=*/ 0, /*map21=*/ 0, + /*node_compat_fn=*/ 0, compat_not0, + /*arg=*/ 0); + if (!iso) { + exit(35); + } + + /* ------- */ + + igraph_count_subisomorphisms_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &count, /*node_compat_fn=*/ 0, + /*edge_compat_fn=*/ 0, /*arg=*/ 0); + + if (count != 20) { + exit(36); + } + + igraph_count_subisomorphisms_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &count, compat_parity, + /*edge_compat_fn=*/ 0, /*arg=*/ 0); + + if (count != 10) { + exit(37); + } + + igraph_count_subisomorphisms_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &count, compat_not0, /*edge_compat_fn=*/ 0, + /*arg=*/ 0); + + if (count != 0) { + exit(38); + } + + /* ------- */ + + igraph_count_subisomorphisms_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &count, /*node_compat_fn=*/ 0, + compat_parity, /*arg=*/ 0); + + if (count != 10) { + exit(39); + } + + igraph_count_subisomorphisms_vf2(&rc, &ro, /*colors(4x)*/ 0, 0, 0, 0, + &count, /*node_compat_fn=*/ 0, compat_not0, + /*arg=*/ 0); + + if (count != 2) { + exit(40); + } + + igraph_destroy(&ro); + igraph_destroy(&rc); + return 0; +} + +/* ----------------------------------------------------------- */ + +int main(void) { + match_rings(); + match_rings_open_closed(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/adj.c b/tests/unit/adj.c new file mode 100644 index 0000000..30f658d --- /dev/null +++ b/tests/unit/adj.c @@ -0,0 +1,309 @@ +/* + 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, see . +*/ + +#include +#include "graph/internal.h" + +#include "test_utilities.h" + +void print_params(const igraph_t *graph, igraph_neimode_t mode, igraph_loops_t loops) { + printf(igraph_is_directed(graph) ? "directed; " : "undirected; "); + switch (mode) { + case IGRAPH_ALL: printf("ALL; "); break; + case IGRAPH_OUT: printf("OUT; "); break; + case IGRAPH_IN : printf("IN; "); break; + } + switch (loops) { + case IGRAPH_NO_LOOPS: printf("NO_LOOPS; "); break; + case IGRAPH_LOOPS_ONCE: printf("LOOPS_ONCE; "); break; + case IGRAPH_LOOPS_TWICE: printf("LOOPS_TWICE; "); break; + } +} + +void print_multiple(igraph_multiple_t multiple) { + switch (multiple) { + case IGRAPH_MULTIPLE: printf("MULTIPLE; "); break; + case IGRAPH_NO_MULTIPLE: printf("NO_MULTIPLE; "); break; + } +} + +/* Print adjacency list based on igraph_i_neighbors() */ +void print_adj1(const igraph_t *graph, igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple) { + igraph_integer_t vcount = igraph_vcount(graph); + igraph_vector_int_t neis; + + print_params(graph, mode, loops); + print_multiple(multiple); + printf("\n"); + + igraph_vector_int_init(&neis, 0); + for (igraph_integer_t v=0; v < vcount; v++) { + printf("%3" IGRAPH_PRId ": ", v); + igraph_i_neighbors(graph, &neis, v, mode, loops, multiple); + print_vector_int(&neis); + } + igraph_vector_int_destroy(&neis); +} + +/* Print adjacency list based on igraph_adjlist_init() */ +void print_adj2(const igraph_t *graph, igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple) { + igraph_integer_t vcount = igraph_vcount(graph); + igraph_adjlist_t al; + + print_params(graph, mode, loops); + print_multiple(multiple); + printf("\n"); + + igraph_adjlist_init(graph, &al, mode, loops, multiple); + for (igraph_integer_t v=0; v < vcount; v++) { + printf("%3" IGRAPH_PRId ": ", v); + print_vector_int(igraph_adjlist_get(&al, v)); + } + igraph_adjlist_destroy(&al); +} + +/* Print incidence list based on igraph_i_incident() */ +void print_inc1(const igraph_t *graph, igraph_neimode_t mode, igraph_loops_t loops) { + igraph_integer_t vcount = igraph_vcount(graph); + igraph_vector_int_t incs; + + print_params(graph, mode, loops); + printf("\n"); + + igraph_vector_int_init(&incs, 0); + for (igraph_integer_t v=0; v < vcount; v++) { + printf("%3" IGRAPH_PRId ": ", v); + igraph_i_incident(graph, &incs, v, mode, loops); + print_vector_int(&incs); + } + igraph_vector_int_destroy(&incs); +} + +/* Print incidence list based on igraph_inclist_init() */ +void print_inc2(const igraph_t *graph, igraph_neimode_t mode, igraph_loops_t loops) { + igraph_integer_t vcount = igraph_vcount(graph); + igraph_inclist_t il; + + print_params(graph, mode, loops); + printf("\n"); + + igraph_inclist_init(graph, &il, mode, loops); + for (igraph_integer_t v=0; v < vcount; v++) { + printf("%3" IGRAPH_PRId ": ", v); + print_vector_int(igraph_inclist_get(&il, v)); + } + igraph_inclist_destroy(&il); +} + +/* Verifies that igraph_i_neighbors() and igraph_i_incident() produce results in the same order. */ +void verify_ordering1(const igraph_t *graph, igraph_neimode_t mode, igraph_loops_t loops) { + igraph_vector_int_t neis, incs; + igraph_integer_t vcount = igraph_vcount(graph); + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + igraph_vector_int_init(&neis, 0); + igraph_vector_int_init(&incs, 0); + + for (igraph_integer_t v=0; v < vcount; v++) { + igraph_i_neighbors(graph, &neis, v, mode, loops, IGRAPH_MULTIPLE); + igraph_i_incident(graph, &incs, v, mode, loops); + + igraph_integer_t n = igraph_vector_int_size(&neis); + IGRAPH_ASSERT(igraph_vector_int_size(&incs) == n); + + for (igraph_integer_t i=0; i < n; i++) { + igraph_integer_t u1, u2; + + u1 = VECTOR(neis)[i]; + switch (mode) { + case IGRAPH_ALL: u2 = IGRAPH_OTHER(graph, VECTOR(incs)[i], v); break; + case IGRAPH_OUT: u2 = IGRAPH_TO(graph, VECTOR(incs)[i]); break; + case IGRAPH_IN: u2 = IGRAPH_FROM(graph, VECTOR(incs)[i]); break; + } + + if (u1 != u2) { + IGRAPH_FATALF( + "For vertex %d, the %dth neighbor vertex between adj (%d) and inc (%d) differs.", + (int) v, (int) i, (int) u1, (int) u2 + ); + } + } + } + + igraph_vector_int_destroy(&incs); + igraph_vector_int_destroy(&neis); +} + +/* Verifies that igraph_adjlist_init() and igraph_inclist_init() produce results in the same order. */ +void verify_ordering2(const igraph_t *graph, igraph_neimode_t mode, igraph_loops_t loops) { + igraph_adjlist_t al; + igraph_inclist_t il; + + igraph_integer_t vcount = igraph_vcount(graph); + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + igraph_adjlist_init(graph, &al, mode, loops, IGRAPH_MULTIPLE); + igraph_inclist_init(graph, &il, mode, loops); + + for (igraph_integer_t v=0; v < vcount; v++) { + igraph_vector_int_t *neis = igraph_adjlist_get(&al, v); + igraph_vector_int_t *incs = igraph_inclist_get(&il, v); + + igraph_integer_t n = igraph_vector_int_size(neis); + IGRAPH_ASSERT(igraph_vector_int_size(incs) == n); + + for (igraph_integer_t i=0; i < n; i++) { + igraph_integer_t u1, u2; + + u1 = VECTOR(*neis)[i]; + switch (mode) { + case IGRAPH_ALL: u2 = IGRAPH_OTHER(graph, VECTOR(*incs)[i], v); break; + case IGRAPH_OUT: u2 = IGRAPH_TO(graph, VECTOR(*incs)[i]); break; + case IGRAPH_IN: u2 = IGRAPH_FROM(graph, VECTOR(*incs)[i]); break; + } + + if (u1 != u2) { + IGRAPH_FATALF( + "For vertex %d, the %dth neighbor vertex between adj (%d) and inc (%d) differs.", + (int) v, (int) i, (int) u1, (int) u2 + ); + } + } + } + + igraph_adjlist_destroy(&al); + igraph_inclist_destroy(&il); +} + +int main(void) { + igraph_t dg, ug; + + igraph_small(&dg, 7, IGRAPH_DIRECTED, + // 0 1 2 3 4 5 6 7 8 9 10 11 12 + 4,4, 0,3, 0,3, 2,3, 1,4, 2,4, 3,4, 3,2, 4,4, 2,3, 1,1, 6,6, 6,6, + -1); + + igraph_copy(&ug, &dg); + igraph_to_undirected(&ug, IGRAPH_TO_UNDIRECTED_EACH, NULL); + + printf("UNDIRECTED\n\n"); + + printf("igraph_i_neighbors()\n"); + print_adj1(&ug, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + print_adj1(&ug, IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + print_adj1(&ug, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("\nigraph_adjlist_init()\n"); + print_adj2(&ug, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + print_adj2(&ug, IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + print_adj2(&ug, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("\nigraph_i_incident()\n"); + print_inc1(&ug, IGRAPH_ALL, IGRAPH_NO_LOOPS); + print_inc1(&ug, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + print_inc1(&ug, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("\nigraph_inclist_init()\n"); + print_inc2(&ug, IGRAPH_ALL, IGRAPH_NO_LOOPS); + print_inc2(&ug, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + print_inc2(&ug, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("\nDIRECTED ALL\n\n"); + + printf("igraph_i_neighbors()\n"); + print_adj1(&dg, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + print_adj1(&dg, IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + print_adj1(&dg, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("\nigraph_adjlist_init()\n"); + print_adj2(&dg, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + print_adj2(&dg, IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + print_adj2(&dg, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("\nigraph_i_incident()\n"); + print_inc1(&dg, IGRAPH_ALL, IGRAPH_NO_LOOPS); + print_inc1(&dg, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + print_inc1(&dg, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("\nigraph_inclist_init()\n"); + print_inc2(&dg, IGRAPH_ALL, IGRAPH_NO_LOOPS); + print_inc2(&dg, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + print_inc2(&dg, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("\nDIRECTED OUT\n\n"); + + printf("igraph_i_neighbors()\n"); + print_adj1(&dg, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + print_adj1(&dg, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("\nigraph_adjlist_init()\n"); + print_adj2(&dg, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + print_adj2(&dg, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("\nigraph_i_incident()\n"); + print_inc1(&dg, IGRAPH_OUT, IGRAPH_NO_LOOPS); + print_inc1(&dg, IGRAPH_OUT, IGRAPH_LOOPS_ONCE); + + printf("\nigraph_inclist_init()\n"); + print_inc2(&dg, IGRAPH_OUT, IGRAPH_NO_LOOPS); + print_inc2(&dg, IGRAPH_OUT, IGRAPH_LOOPS_ONCE); + + /* Verify that neighbour lists (adjlist) and incident edge lists (inclist) + * have the same ordering with all parameter combinations. */ + + verify_ordering1(&ug, IGRAPH_ALL, IGRAPH_NO_LOOPS); + verify_ordering1(&ug, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + verify_ordering1(&ug, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + verify_ordering1(&dg, IGRAPH_ALL, IGRAPH_NO_LOOPS); + verify_ordering1(&dg, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + verify_ordering1(&dg, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + verify_ordering1(&dg, IGRAPH_OUT, IGRAPH_NO_LOOPS); + verify_ordering1(&dg, IGRAPH_OUT, IGRAPH_LOOPS_ONCE); + + verify_ordering1(&dg, IGRAPH_IN, IGRAPH_NO_LOOPS); + verify_ordering1(&dg, IGRAPH_IN, IGRAPH_LOOPS_ONCE); + + verify_ordering2(&ug, IGRAPH_ALL, IGRAPH_NO_LOOPS); + verify_ordering2(&ug, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + verify_ordering2(&ug, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + verify_ordering2(&dg, IGRAPH_ALL, IGRAPH_NO_LOOPS); + verify_ordering2(&dg, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + verify_ordering2(&dg, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + verify_ordering2(&dg, IGRAPH_OUT, IGRAPH_NO_LOOPS); + verify_ordering2(&dg, IGRAPH_OUT, IGRAPH_LOOPS_ONCE); + + verify_ordering2(&dg, IGRAPH_IN, IGRAPH_NO_LOOPS); + verify_ordering2(&dg, IGRAPH_IN, IGRAPH_LOOPS_ONCE); + + igraph_destroy(&ug); + igraph_destroy(&dg); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/adj.out b/tests/unit/adj.out new file mode 100644 index 0000000..8ff4ebf --- /dev/null +++ b/tests/unit/adj.out @@ -0,0 +1,285 @@ +UNDIRECTED + +igraph_i_neighbors() +undirected; ALL; NO_LOOPS; MULTIPLE; + 0: ( 3 3 ) + 1: ( 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 ) + 5: ( ) + 6: ( ) +undirected; ALL; LOOPS_ONCE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 4 4 ) + 5: ( ) + 6: ( 6 6 ) +undirected; ALL; LOOPS_TWICE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 1 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 4 4 4 4 ) + 5: ( ) + 6: ( 6 6 6 6 ) + +igraph_adjlist_init() +undirected; ALL; NO_LOOPS; MULTIPLE; + 0: ( 3 3 ) + 1: ( 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 ) + 5: ( ) + 6: ( ) +undirected; ALL; LOOPS_ONCE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 4 4 ) + 5: ( ) + 6: ( 6 6 ) +undirected; ALL; LOOPS_TWICE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 1 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 4 4 4 4 ) + 5: ( ) + 6: ( 6 6 6 6 ) + +igraph_i_incident() +undirected; ALL; NO_LOOPS; + 0: ( 2 1 ) + 1: ( 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 9 7 3 6 ) + 4: ( 4 5 6 ) + 5: ( ) + 6: ( ) +undirected; ALL; LOOPS_ONCE; + 0: ( 2 1 ) + 1: ( 10 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 9 7 3 6 ) + 4: ( 4 5 6 8 0 ) + 5: ( ) + 6: ( 12 11 ) +undirected; ALL; LOOPS_TWICE; + 0: ( 2 1 ) + 1: ( 10 10 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 9 7 3 6 ) + 4: ( 4 5 6 8 0 8 0 ) + 5: ( ) + 6: ( 12 11 12 11 ) + +igraph_inclist_init() +undirected; ALL; NO_LOOPS; + 0: ( 2 1 ) + 1: ( 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 9 7 3 6 ) + 4: ( 4 5 6 ) + 5: ( ) + 6: ( ) +undirected; ALL; LOOPS_ONCE; + 0: ( 2 1 ) + 1: ( 10 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 9 7 3 6 ) + 4: ( 4 5 6 8 0 ) + 5: ( ) + 6: ( 12 11 ) +undirected; ALL; LOOPS_TWICE; + 0: ( 2 1 ) + 1: ( 10 10 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 9 7 3 6 ) + 4: ( 4 5 6 8 0 8 0 ) + 5: ( ) + 6: ( 12 11 12 11 ) + +DIRECTED ALL + +igraph_i_neighbors() +directed; ALL; NO_LOOPS; MULTIPLE; + 0: ( 3 3 ) + 1: ( 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 ) + 5: ( ) + 6: ( ) +directed; ALL; LOOPS_ONCE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 4 4 ) + 5: ( ) + 6: ( 6 6 ) +directed; ALL; LOOPS_TWICE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 1 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 4 4 4 4 ) + 5: ( ) + 6: ( 6 6 6 6 ) + +igraph_adjlist_init() +directed; ALL; NO_LOOPS; MULTIPLE; + 0: ( 3 3 ) + 1: ( 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 ) + 5: ( ) + 6: ( ) +directed; ALL; LOOPS_ONCE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 4 4 ) + 5: ( ) + 6: ( 6 6 ) +directed; ALL; LOOPS_TWICE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 1 4 ) + 2: ( 3 3 3 4 ) + 3: ( 0 0 2 2 2 4 ) + 4: ( 1 2 3 4 4 4 4 ) + 5: ( ) + 6: ( 6 6 6 6 ) + +igraph_i_incident() +directed; ALL; NO_LOOPS; + 0: ( 2 1 ) + 1: ( 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 7 9 3 6 ) + 4: ( 4 5 6 ) + 5: ( ) + 6: ( ) +directed; ALL; LOOPS_ONCE; + 0: ( 2 1 ) + 1: ( 10 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 7 9 3 6 ) + 4: ( 4 5 6 8 0 ) + 5: ( ) + 6: ( 12 11 ) +directed; ALL; LOOPS_TWICE; + 0: ( 2 1 ) + 1: ( 10 10 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 7 9 3 6 ) + 4: ( 4 5 6 8 8 0 0 ) + 5: ( ) + 6: ( 12 12 11 11 ) + +igraph_inclist_init() +directed; ALL; NO_LOOPS; + 0: ( 2 1 ) + 1: ( 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 7 9 3 6 ) + 4: ( 4 5 6 ) + 5: ( ) + 6: ( ) +directed; ALL; LOOPS_ONCE; + 0: ( 2 1 ) + 1: ( 10 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 7 9 3 6 ) + 4: ( 4 5 6 8 0 ) + 5: ( ) + 6: ( 12 11 ) +directed; ALL; LOOPS_TWICE; + 0: ( 2 1 ) + 1: ( 10 10 4 ) + 2: ( 9 7 3 5 ) + 3: ( 2 1 7 9 3 6 ) + 4: ( 4 5 6 8 8 0 0 ) + 5: ( ) + 6: ( 12 12 11 11 ) + +DIRECTED OUT + +igraph_i_neighbors() +directed; OUT; NO_LOOPS; MULTIPLE; + 0: ( 3 3 ) + 1: ( 4 ) + 2: ( 3 3 4 ) + 3: ( 2 4 ) + 4: ( ) + 5: ( ) + 6: ( ) +directed; OUT; LOOPS_ONCE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 4 ) + 2: ( 3 3 4 ) + 3: ( 2 4 ) + 4: ( 4 4 ) + 5: ( ) + 6: ( 6 6 ) + +igraph_adjlist_init() +directed; OUT; NO_LOOPS; MULTIPLE; + 0: ( 3 3 ) + 1: ( 4 ) + 2: ( 3 3 4 ) + 3: ( 2 4 ) + 4: ( ) + 5: ( ) + 6: ( ) +directed; OUT; LOOPS_ONCE; MULTIPLE; + 0: ( 3 3 ) + 1: ( 1 4 ) + 2: ( 3 3 4 ) + 3: ( 2 4 ) + 4: ( 4 4 ) + 5: ( ) + 6: ( 6 6 ) + +igraph_i_incident() +directed; OUT; NO_LOOPS; + 0: ( 2 1 ) + 1: ( 4 ) + 2: ( 9 3 5 ) + 3: ( 7 6 ) + 4: ( ) + 5: ( ) + 6: ( ) +directed; OUT; LOOPS_ONCE; + 0: ( 2 1 ) + 1: ( 10 4 ) + 2: ( 9 3 5 ) + 3: ( 7 6 ) + 4: ( 8 0 ) + 5: ( ) + 6: ( 12 11 ) + +igraph_inclist_init() +directed; OUT; NO_LOOPS; + 0: ( 2 1 ) + 1: ( 4 ) + 2: ( 9 3 5 ) + 3: ( 7 6 ) + 4: ( ) + 5: ( ) + 6: ( ) +directed; OUT; LOOPS_ONCE; + 0: ( 2 1 ) + 1: ( 10 4 ) + 2: ( 9 3 5 ) + 3: ( 7 6 ) + 4: ( 8 0 ) + 5: ( ) + 6: ( 12 11 ) diff --git a/tests/unit/adjlist.c b/tests/unit/adjlist.c new file mode 100644 index 0000000..ddfa775 --- /dev/null +++ b/tests/unit/adjlist.c @@ -0,0 +1,348 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int test_simple_trees(void) { + igraph_t g, g2; + igraph_adjlist_t adjlist; + igraph_bool_t iso; + + /* Directed, out */ + igraph_kary_tree(&g, 42, 3, IGRAPH_TREE_OUT); + igraph_adjlist_init(&g, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + igraph_adjlist(&g2, &adjlist, IGRAPH_OUT, /*duplicate=*/ 0); + igraph_isomorphic(&g, &g2, &iso); + IGRAPH_ASSERT(iso); + igraph_adjlist_destroy(&adjlist); + igraph_destroy(&g2); + igraph_destroy(&g); + + /* Directed, in */ + igraph_kary_tree(&g, 42, 3, IGRAPH_TREE_OUT); + igraph_adjlist_init(&g, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + igraph_adjlist(&g2, &adjlist, IGRAPH_IN, /*duplicate=*/ 0); + igraph_isomorphic(&g, &g2, &iso); + IGRAPH_ASSERT(iso); + igraph_adjlist_destroy(&adjlist); + igraph_destroy(&g2); + igraph_destroy(&g); + + /* Undirected */ + igraph_kary_tree(&g, 42, 3, IGRAPH_TREE_UNDIRECTED); + igraph_adjlist_init(&g, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + igraph_adjlist(&g2, &adjlist, IGRAPH_ALL, /*duplicate=*/ 1); + igraph_isomorphic(&g, &g2, &iso); + IGRAPH_ASSERT(iso); + igraph_adjlist_destroy(&adjlist); + igraph_destroy(&g2); + igraph_destroy(&g); + + return 0; +} + +#define TEST_ADJLIST(label, mode, loops, multiple) { \ + igraph_adjlist_init(&g, &adjlist, mode, loops, multiple); \ + printf(label ": "); \ + print_adjlist(&adjlist); \ + printf("\n"); \ + igraph_adjlist_destroy(&adjlist); \ +} + +#define TEST_LAZY_ADJLIST(label, mode, loops, multiple) { \ + igraph_lazy_adjlist_init(&g, &lazy_adjlist, mode, loops, multiple); \ + printf(label ": "); \ + print_lazy_adjlist(&lazy_adjlist); \ + printf("\n"); \ + igraph_lazy_adjlist_destroy(&lazy_adjlist); \ +} + +int test_loop_elimination_for_undirected_graph(void) { + igraph_t g; + igraph_adjlist_t adjlist; + igraph_lazy_adjlist_t lazy_adjlist; + + igraph_small( + &g, 5, /* directed = */ 0, + 0, 1, 0, 3, + 1, 2, + 2, 2, 2, 3, + 3, 0, 3, 4, + 4, 4, 4, 4, + -1 + ); + + printf("Testing loop edge elimination in undirected graph\n\n"); + + /* We are testing IGRAPH_ALL, IGRAPH_IN and IGRAPH_OUT below; it should + * make no difference */ + TEST_ADJLIST("Loops eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + TEST_ADJLIST("Loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + TEST_ADJLIST("Loops listed twice", IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("============================================================\n\n"); + + printf("Testing lazy loop edge elimination in undirected graph\n\n"); + + /* We are testing IGRAPH_ALL, IGRAPH_IN and IGRAPH_OUT below; it should + * make no difference */ + TEST_LAZY_ADJLIST("Loops eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + TEST_LAZY_ADJLIST("Loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + TEST_LAZY_ADJLIST("Loops listed twice", IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("============================================================\n\n"); + + igraph_destroy(&g); + + return 0; +} + +int test_loop_elimination_for_directed_graph(void) { + igraph_t g; + igraph_adjlist_t adjlist; + igraph_lazy_adjlist_t lazy_adjlist; + + igraph_small( + &g, 5, /* directed = */ 1, + 0, 1, 0, 3, + 1, 2, + 2, 2, 2, 3, + 3, 0, 3, 4, + 4, 4, 4, 4, + -1 + ); + + printf("Testing loop edge elimination in directed graph\n\n"); + + TEST_ADJLIST("In-edges, loops eliminated", IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + TEST_ADJLIST("In-edges, loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + TEST_ADJLIST("In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_IN, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + TEST_ADJLIST("Out-edges, loops eliminated", IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + TEST_ADJLIST("Out-edges, loops listed once", IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + TEST_ADJLIST("Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + TEST_ADJLIST("In- and out-edges, loops eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + TEST_ADJLIST("In- and out-edges, loops listed once", IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + TEST_ADJLIST("In- and out-edges, loops listed twice", IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("============================================================\n\n"); + + printf("Testing lazy loop edge elimination in directed graph\n\n"); + + TEST_LAZY_ADJLIST("In-edges, loops eliminated", IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + TEST_LAZY_ADJLIST("In-edges, loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + TEST_LAZY_ADJLIST("In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_IN, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + TEST_LAZY_ADJLIST("Out-edges, loops eliminated", IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + TEST_LAZY_ADJLIST("Out-edges, loops listed once", IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + TEST_LAZY_ADJLIST("Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + TEST_LAZY_ADJLIST("In- and out-edges, loops eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + TEST_LAZY_ADJLIST("In- and out-edges, loops listed once", IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + TEST_LAZY_ADJLIST("In- and out-edges, loops listed twice", IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("============================================================\n\n"); + + igraph_destroy(&g); + + return 0; +} + +int test_multiedge_elimination_for_undirected_graph(void) { + igraph_t g; + igraph_adjlist_t adjlist; + igraph_lazy_adjlist_t lazy_adjlist; + + igraph_small( + &g, 5, /* directed = */ 0, + 0, 1, 0, 3, 0, 8, + 1, 2, + 2, 2, 2, 3, + 3, 0, 3, 4, + 4, 4, 4, 4, 4, 5, 4, 5, 4, 5, + 5, 6, + 6, 7, 6, 8, + 8, 0, + -1 + ); + + printf("Testing multiple edge elimination in undirected graph\n\n"); + + /* We are testing IGRAPH_ALL, IGRAPH_IN and IGRAPH_OUT below; it should + * make no difference */ + TEST_ADJLIST("Loops also eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + TEST_ADJLIST("Loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + TEST_ADJLIST("Loops listed twice", IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + printf("============================================================\n\n"); + + printf("Testing lazy multiple edge elimination in undirected graph\n\n"); + + /* We are testing IGRAPH_ALL, IGRAPH_IN and IGRAPH_OUT below; it should + * make no difference */ + TEST_LAZY_ADJLIST("Loops also eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + TEST_LAZY_ADJLIST("Loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + TEST_LAZY_ADJLIST("Loops listed twice", IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + printf("============================================================\n\n"); + + igraph_destroy(&g); + + return 0; +} + +int test_multiedge_elimination_for_directed_graph(void) { + igraph_t g; + igraph_adjlist_t adjlist; + igraph_lazy_adjlist_t lazy_adjlist; + + igraph_small( + &g, 5, /* directed = */ 1, + 0, 1, 0, 3, 0, 8, + 1, 2, + 2, 2, 2, 3, + 3, 0, 3, 4, + 4, 4, 4, 4, 4, 5, 4, 5, 4, 5, + 5, 6, + 6, 7, 6, 8, + 8, 0, + -1 + ); + + printf("Testing multiple edge elimination in directed graph\n\n"); + + TEST_ADJLIST("In-edges, loops also eliminated", IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + TEST_ADJLIST("In-edges, loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + TEST_ADJLIST("In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_IN, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + TEST_ADJLIST("Out-edges, loops also eliminated", IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + TEST_ADJLIST("Out-edges, loops listed once", IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + TEST_ADJLIST("Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + TEST_ADJLIST("In- and out-edges, loops also eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + TEST_ADJLIST("In- and out-edges, loops listed once", IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + TEST_ADJLIST("In- and out-edges, loops listed twice", IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + printf("============================================================\n\n"); + + printf("Testing lazy multiple edge elimination in directed graph\n\n"); + + TEST_LAZY_ADJLIST("In-edges, loops also eliminated", IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + TEST_LAZY_ADJLIST("In-edges, loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + TEST_LAZY_ADJLIST("In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_IN, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + TEST_LAZY_ADJLIST("Out-edges, loops also eliminated", IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + TEST_LAZY_ADJLIST("Out-edges, loops listed once", IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + TEST_LAZY_ADJLIST("Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + TEST_LAZY_ADJLIST("In- and out-edges, loops also eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + TEST_LAZY_ADJLIST("In- and out-edges, loops listed once", IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + TEST_LAZY_ADJLIST("In- and out-edges, loops listed twice", IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + printf("============================================================\n\n"); + + igraph_destroy(&g); + + return 0; +} + +int test_caching(void) { + igraph_t g_simple, g_loop, g_multiloop, g_multi, g_multi_and_loop; + char *g_desc[] = {"simple", "loop", "multiloop", "multi", "multi and loop"}; + igraph_adjlist_t adjlist; + igraph_loops_t loops[] = {IGRAPH_NO_LOOPS, IGRAPH_LOOPS_ONCE, IGRAPH_LOOPS_TWICE}; + igraph_multiple_t multiple[] = {IGRAPH_NO_MULTIPLE, IGRAPH_MULTIPLE}; + igraph_neimode_t modes[] = {IGRAPH_OUT, IGRAPH_ALL}; + + igraph_vector_int_t edge; + igraph_integer_t vloop[] = {0,0}; + igraph_integer_t vmult[] = {0,1}; + + igraph_full(&g_simple, 5, IGRAPH_UNDIRECTED, /*loops*/ 0); + igraph_full(&g_loop, 5, IGRAPH_UNDIRECTED, /*loops*/ 0); + igraph_full(&g_multiloop, 5, IGRAPH_UNDIRECTED, /*loops*/ 0); + igraph_full(&g_multi, 5, IGRAPH_UNDIRECTED, /*loops*/ 0); + igraph_full(&g_multi_and_loop, 5, IGRAPH_UNDIRECTED, /*loops*/ 0); + + igraph_vector_int_view(&edge, vloop, 2); + igraph_add_edges(&g_loop, &edge, NULL); + igraph_add_edges(&g_multiloop, &edge, NULL); + igraph_add_edges(&g_multiloop, &edge, NULL); + igraph_add_edges(&g_multi_and_loop, &edge, NULL); + + igraph_vector_int_view(&edge, vmult, 2); + + igraph_add_edges(&g_multi, &edge, NULL); + igraph_add_edges(&g_multi_and_loop, &edge, NULL); + + igraph_t *graphs[] = {&g_simple, &g_loop, &g_multiloop, &g_multi, &g_multi_and_loop}; + + for (int g = 0; g < 5; g++) { + for (int loop = 0; loop < 3; loop++) { + for (int multi = 0; multi < 2; multi++) { + for (int mode = 0; mode < 2; mode++) { + printf("graph: %s, loop: %d multi: %d, mode: %d\n", g_desc[g], loop, multi, mode); + + igraph_invalidate_cache(graphs[g]); + igraph_adjlist_init(graphs[g], &adjlist, modes[mode], loops[loop], multiple[multi]); + if (!igraph_i_property_cache_has(graphs[g], IGRAPH_PROP_HAS_LOOP)) { + printf("loop not cached\n"); + } else { + printf("loop cached: %d\n", igraph_i_property_cache_get_bool(graphs[g], IGRAPH_PROP_HAS_LOOP)); + } + if (!igraph_i_property_cache_has(graphs[g], IGRAPH_PROP_HAS_MULTI)) { + printf("multi not cached\n"); + } else { + printf("multi cached: %d\n", igraph_i_property_cache_get_bool(graphs[g], IGRAPH_PROP_HAS_MULTI)); + } + igraph_adjlist_destroy(&adjlist); + } + } + } + igraph_destroy(graphs[g]); + } + + return 0; +} + +int main(void) { + + RUN_TEST(test_simple_trees); + + RUN_TEST(test_loop_elimination_for_undirected_graph); + RUN_TEST(test_loop_elimination_for_directed_graph); + RUN_TEST(test_multiedge_elimination_for_undirected_graph); + RUN_TEST(test_multiedge_elimination_for_directed_graph); + + RUN_TEST(test_caching); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/adjlist.out b/tests/unit/adjlist.out new file mode 100644 index 0000000..ffe6550 --- /dev/null +++ b/tests/unit/adjlist.out @@ -0,0 +1,692 @@ +Testing loop edge elimination in undirected graph + +Loops eliminated: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 ) +} + +Loops listed once: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 4 4 ) +} + +Loops listed twice: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 4 4 4 4 ) +} + +============================================================ + +Testing lazy loop edge elimination in undirected graph + +Loops eliminated: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 ) +} + +Loops listed once: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 4 4 ) +} + +Loops listed twice: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 4 4 4 4 ) +} + +============================================================ + +Testing loop edge elimination in directed graph + +In-edges, loops eliminated: { + 0: ( 3 ) + 1: ( 0 ) + 2: ( 1 ) + 3: ( 0 2 ) + 4: ( 3 ) +} + +In-edges, loops listed once: { + 0: ( 3 ) + 1: ( 0 ) + 2: ( 1 2 ) + 3: ( 0 2 ) + 4: ( 3 4 4 ) +} + +In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 3 ) + 1: ( 0 ) + 2: ( 1 2 ) + 3: ( 0 2 ) + 4: ( 3 4 4 ) +} + +Out-edges, loops eliminated: { + 0: ( 1 3 ) + 1: ( 2 ) + 2: ( 3 ) + 3: ( 0 4 ) + 4: ( ) +} + +Out-edges, loops listed once: { + 0: ( 1 3 ) + 1: ( 2 ) + 2: ( 2 3 ) + 3: ( 0 4 ) + 4: ( 4 4 ) +} + +Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 1 3 ) + 1: ( 2 ) + 2: ( 2 3 ) + 3: ( 0 4 ) + 4: ( 4 4 ) +} + +In- and out-edges, loops eliminated: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 ) +} + +In- and out-edges, loops listed once: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 4 4 ) +} + +In- and out-edges, loops listed twice: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 4 4 4 4 ) +} + +============================================================ + +Testing lazy loop edge elimination in directed graph + +In-edges, loops eliminated: { + 0: ( 3 ) + 1: ( 0 ) + 2: ( 1 ) + 3: ( 0 2 ) + 4: ( 3 ) +} + +In-edges, loops listed once: { + 0: ( 3 ) + 1: ( 0 ) + 2: ( 1 2 ) + 3: ( 0 2 ) + 4: ( 3 4 4 ) +} + +In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 3 ) + 1: ( 0 ) + 2: ( 1 2 ) + 3: ( 0 2 ) + 4: ( 3 4 4 ) +} + +Out-edges, loops eliminated: { + 0: ( 1 3 ) + 1: ( 2 ) + 2: ( 3 ) + 3: ( 0 4 ) + 4: ( ) +} + +Out-edges, loops listed once: { + 0: ( 1 3 ) + 1: ( 2 ) + 2: ( 2 3 ) + 3: ( 0 4 ) + 4: ( 4 4 ) +} + +Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 1 3 ) + 1: ( 2 ) + 2: ( 2 3 ) + 3: ( 0 4 ) + 4: ( 4 4 ) +} + +In- and out-edges, loops eliminated: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 ) +} + +In- and out-edges, loops listed once: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 4 4 ) +} + +In- and out-edges, loops listed twice: { + 0: ( 1 3 3 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 3 4 4 4 4 ) +} + +============================================================ + +Testing multiple edge elimination in undirected graph + +Loops also eliminated: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 3 ) + 3: ( 0 2 4 ) + 4: ( 3 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +Loops listed once: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 2 3 ) + 3: ( 0 2 4 ) + 4: ( 3 4 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +Loops listed twice: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 2 4 ) + 4: ( 3 4 4 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +============================================================ + +Testing lazy multiple edge elimination in undirected graph + +Loops also eliminated: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 3 ) + 3: ( 0 2 4 ) + 4: ( 3 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +Loops listed once: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 2 3 ) + 3: ( 0 2 4 ) + 4: ( 3 4 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +Loops listed twice: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 2 4 ) + 4: ( 3 4 4 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +============================================================ + +Testing multiple edge elimination in directed graph + +In-edges, loops also eliminated: { + 0: ( 3 8 ) + 1: ( 0 ) + 2: ( 1 ) + 3: ( 0 2 ) + 4: ( 3 ) + 5: ( 4 ) + 6: ( 5 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +In-edges, loops listed once: { + 0: ( 3 8 ) + 1: ( 0 ) + 2: ( 1 2 ) + 3: ( 0 2 ) + 4: ( 3 4 ) + 5: ( 4 ) + 6: ( 5 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 3 8 ) + 1: ( 0 ) + 2: ( 1 2 ) + 3: ( 0 2 ) + 4: ( 3 4 ) + 5: ( 4 ) + 6: ( 5 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +Out-edges, loops also eliminated: { + 0: ( 1 3 8 ) + 1: ( 2 ) + 2: ( 3 ) + 3: ( 0 4 ) + 4: ( 5 ) + 5: ( 6 ) + 6: ( 7 8 ) + 7: ( ) + 8: ( 0 ) +} + +Out-edges, loops listed once: { + 0: ( 1 3 8 ) + 1: ( 2 ) + 2: ( 2 3 ) + 3: ( 0 4 ) + 4: ( 4 5 ) + 5: ( 6 ) + 6: ( 7 8 ) + 7: ( ) + 8: ( 0 ) +} + +Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 1 3 8 ) + 1: ( 2 ) + 2: ( 2 3 ) + 3: ( 0 4 ) + 4: ( 4 5 ) + 5: ( 6 ) + 6: ( 7 8 ) + 7: ( ) + 8: ( 0 ) +} + +In- and out-edges, loops also eliminated: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 3 ) + 3: ( 0 2 4 ) + 4: ( 3 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +In- and out-edges, loops listed once: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 2 3 ) + 3: ( 0 2 4 ) + 4: ( 3 4 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +In- and out-edges, loops listed twice: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 2 4 ) + 4: ( 3 4 4 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +============================================================ + +Testing lazy multiple edge elimination in directed graph + +In-edges, loops also eliminated: { + 0: ( 3 8 ) + 1: ( 0 ) + 2: ( 1 ) + 3: ( 0 2 ) + 4: ( 3 ) + 5: ( 4 ) + 6: ( 5 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +In-edges, loops listed once: { + 0: ( 3 8 ) + 1: ( 0 ) + 2: ( 1 2 ) + 3: ( 0 2 ) + 4: ( 3 4 ) + 5: ( 4 ) + 6: ( 5 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 3 8 ) + 1: ( 0 ) + 2: ( 1 2 ) + 3: ( 0 2 ) + 4: ( 3 4 ) + 5: ( 4 ) + 6: ( 5 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +Out-edges, loops also eliminated: { + 0: ( 1 3 8 ) + 1: ( 2 ) + 2: ( 3 ) + 3: ( 0 4 ) + 4: ( 5 ) + 5: ( 6 ) + 6: ( 7 8 ) + 7: ( ) + 8: ( 0 ) +} + +Out-edges, loops listed once: { + 0: ( 1 3 8 ) + 1: ( 2 ) + 2: ( 2 3 ) + 3: ( 0 4 ) + 4: ( 4 5 ) + 5: ( 6 ) + 6: ( 7 8 ) + 7: ( ) + 8: ( 0 ) +} + +Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 1 3 8 ) + 1: ( 2 ) + 2: ( 2 3 ) + 3: ( 0 4 ) + 4: ( 4 5 ) + 5: ( 6 ) + 6: ( 7 8 ) + 7: ( ) + 8: ( 0 ) +} + +In- and out-edges, loops also eliminated: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 3 ) + 3: ( 0 2 4 ) + 4: ( 3 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +In- and out-edges, loops listed once: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 2 3 ) + 3: ( 0 2 4 ) + 4: ( 3 4 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +In- and out-edges, loops listed twice: { + 0: ( 1 3 8 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 2 4 ) + 4: ( 3 4 4 5 ) + 5: ( 4 6 ) + 6: ( 5 7 8 ) + 7: ( 6 ) + 8: ( 0 6 ) +} + +============================================================ + +graph: simple, loop: 0 multi: 0, mode: 0 +loop cached: 0 +multi cached: 0 +graph: simple, loop: 0 multi: 0, mode: 1 +loop cached: 0 +multi cached: 0 +graph: simple, loop: 0 multi: 1, mode: 0 +loop cached: 0 +multi not cached +graph: simple, loop: 0 multi: 1, mode: 1 +loop cached: 0 +multi not cached +graph: simple, loop: 1 multi: 0, mode: 0 +loop not cached +multi cached: 0 +graph: simple, loop: 1 multi: 0, mode: 1 +loop not cached +multi cached: 0 +graph: simple, loop: 1 multi: 1, mode: 0 +loop not cached +multi not cached +graph: simple, loop: 1 multi: 1, mode: 1 +loop not cached +multi not cached +graph: simple, loop: 2 multi: 0, mode: 0 +loop not cached +multi cached: 0 +graph: simple, loop: 2 multi: 0, mode: 1 +loop not cached +multi cached: 0 +graph: simple, loop: 2 multi: 1, mode: 0 +loop not cached +multi not cached +graph: simple, loop: 2 multi: 1, mode: 1 +loop not cached +multi not cached +graph: loop, loop: 0 multi: 0, mode: 0 +loop cached: 1 +multi cached: 0 +graph: loop, loop: 0 multi: 0, mode: 1 +loop cached: 1 +multi cached: 0 +graph: loop, loop: 0 multi: 1, mode: 0 +loop cached: 1 +multi not cached +graph: loop, loop: 0 multi: 1, mode: 1 +loop cached: 1 +multi not cached +graph: loop, loop: 1 multi: 0, mode: 0 +loop not cached +multi cached: 0 +graph: loop, loop: 1 multi: 0, mode: 1 +loop not cached +multi cached: 0 +graph: loop, loop: 1 multi: 1, mode: 0 +loop cached: 1 +multi not cached +graph: loop, loop: 1 multi: 1, mode: 1 +loop cached: 1 +multi not cached +graph: loop, loop: 2 multi: 0, mode: 0 +loop not cached +multi cached: 1 +graph: loop, loop: 2 multi: 0, mode: 1 +loop not cached +multi cached: 1 +graph: loop, loop: 2 multi: 1, mode: 0 +loop not cached +multi not cached +graph: loop, loop: 2 multi: 1, mode: 1 +loop not cached +multi not cached +graph: multiloop, loop: 0 multi: 0, mode: 0 +loop cached: 1 +multi cached: 1 +graph: multiloop, loop: 0 multi: 0, mode: 1 +loop cached: 1 +multi cached: 1 +graph: multiloop, loop: 0 multi: 1, mode: 0 +loop cached: 1 +multi not cached +graph: multiloop, loop: 0 multi: 1, mode: 1 +loop cached: 1 +multi not cached +graph: multiloop, loop: 1 multi: 0, mode: 0 +loop not cached +multi cached: 1 +graph: multiloop, loop: 1 multi: 0, mode: 1 +loop not cached +multi cached: 1 +graph: multiloop, loop: 1 multi: 1, mode: 0 +loop cached: 1 +multi not cached +graph: multiloop, loop: 1 multi: 1, mode: 1 +loop cached: 1 +multi not cached +graph: multiloop, loop: 2 multi: 0, mode: 0 +loop not cached +multi cached: 1 +graph: multiloop, loop: 2 multi: 0, mode: 1 +loop not cached +multi cached: 1 +graph: multiloop, loop: 2 multi: 1, mode: 0 +loop not cached +multi not cached +graph: multiloop, loop: 2 multi: 1, mode: 1 +loop not cached +multi not cached +graph: multi, loop: 0 multi: 0, mode: 0 +loop cached: 0 +multi cached: 1 +graph: multi, loop: 0 multi: 0, mode: 1 +loop cached: 0 +multi cached: 1 +graph: multi, loop: 0 multi: 1, mode: 0 +loop cached: 0 +multi not cached +graph: multi, loop: 0 multi: 1, mode: 1 +loop cached: 0 +multi not cached +graph: multi, loop: 1 multi: 0, mode: 0 +loop not cached +multi cached: 1 +graph: multi, loop: 1 multi: 0, mode: 1 +loop not cached +multi cached: 1 +graph: multi, loop: 1 multi: 1, mode: 0 +loop not cached +multi not cached +graph: multi, loop: 1 multi: 1, mode: 1 +loop not cached +multi not cached +graph: multi, loop: 2 multi: 0, mode: 0 +loop not cached +multi cached: 1 +graph: multi, loop: 2 multi: 0, mode: 1 +loop not cached +multi cached: 1 +graph: multi, loop: 2 multi: 1, mode: 0 +loop not cached +multi not cached +graph: multi, loop: 2 multi: 1, mode: 1 +loop not cached +multi not cached +graph: multi and loop, loop: 0 multi: 0, mode: 0 +loop cached: 1 +multi cached: 1 +graph: multi and loop, loop: 0 multi: 0, mode: 1 +loop cached: 1 +multi cached: 1 +graph: multi and loop, loop: 0 multi: 1, mode: 0 +loop cached: 1 +multi not cached +graph: multi and loop, loop: 0 multi: 1, mode: 1 +loop cached: 1 +multi not cached +graph: multi and loop, loop: 1 multi: 0, mode: 0 +loop not cached +multi cached: 1 +graph: multi and loop, loop: 1 multi: 0, mode: 1 +loop not cached +multi cached: 1 +graph: multi and loop, loop: 1 multi: 1, mode: 0 +loop cached: 1 +multi not cached +graph: multi and loop, loop: 1 multi: 1, mode: 1 +loop cached: 1 +multi not cached +graph: multi and loop, loop: 2 multi: 0, mode: 0 +loop not cached +multi cached: 1 +graph: multi and loop, loop: 2 multi: 0, mode: 1 +loop not cached +multi cached: 1 +graph: multi and loop, loop: 2 multi: 1, mode: 0 +loop not cached +multi not cached +graph: multi and loop, loop: 2 multi: 1, mode: 1 +loop not cached +multi not cached diff --git a/tests/unit/ak-4102.max b/tests/unit/ak-4102.max new file mode 100644 index 0000000..0ff4ab6 --- /dev/null +++ b/tests/unit/ak-4102.max @@ -0,0 +1,24623 @@ +c very bad maxflow problem +p max 16414 24619 +n 1 s +n 2 t +a 3 4 4103 +a 3 4106 1 +a 4 5 4102 +a 4 4106 1 +a 5 6 4101 +a 5 4106 1 +a 6 7 4100 +a 6 4106 1 +a 7 8 4099 +a 7 4106 1 +a 8 9 4098 +a 8 4106 1 +a 9 10 4097 +a 9 4106 1 +a 10 11 4096 +a 10 4106 1 +a 11 12 4095 +a 11 4106 1 +a 12 13 4094 +a 12 4106 1 +a 13 14 4093 +a 13 4106 1 +a 14 15 4092 +a 14 4106 1 +a 15 16 4091 +a 15 4106 1 +a 16 17 4090 +a 16 4106 1 +a 17 18 4089 +a 17 4106 1 +a 18 19 4088 +a 18 4106 1 +a 19 20 4087 +a 19 4106 1 +a 20 21 4086 +a 20 4106 1 +a 21 22 4085 +a 21 4106 1 +a 22 23 4084 +a 22 4106 1 +a 23 24 4083 +a 23 4106 1 +a 24 25 4082 +a 24 4106 1 +a 25 26 4081 +a 25 4106 1 +a 26 27 4080 +a 26 4106 1 +a 27 28 4079 +a 27 4106 1 +a 28 29 4078 +a 28 4106 1 +a 29 30 4077 +a 29 4106 1 +a 30 31 4076 +a 30 4106 1 +a 31 32 4075 +a 31 4106 1 +a 32 33 4074 +a 32 4106 1 +a 33 34 4073 +a 33 4106 1 +a 34 35 4072 +a 34 4106 1 +a 35 36 4071 +a 35 4106 1 +a 36 37 4070 +a 36 4106 1 +a 37 38 4069 +a 37 4106 1 +a 38 39 4068 +a 38 4106 1 +a 39 40 4067 +a 39 4106 1 +a 40 41 4066 +a 40 4106 1 +a 41 42 4065 +a 41 4106 1 +a 42 43 4064 +a 42 4106 1 +a 43 44 4063 +a 43 4106 1 +a 44 45 4062 +a 44 4106 1 +a 45 46 4061 +a 45 4106 1 +a 46 47 4060 +a 46 4106 1 +a 47 48 4059 +a 47 4106 1 +a 48 49 4058 +a 48 4106 1 +a 49 50 4057 +a 49 4106 1 +a 50 51 4056 +a 50 4106 1 +a 51 52 4055 +a 51 4106 1 +a 52 53 4054 +a 52 4106 1 +a 53 54 4053 +a 53 4106 1 +a 54 55 4052 +a 54 4106 1 +a 55 56 4051 +a 55 4106 1 +a 56 57 4050 +a 56 4106 1 +a 57 58 4049 +a 57 4106 1 +a 58 59 4048 +a 58 4106 1 +a 59 60 4047 +a 59 4106 1 +a 60 61 4046 +a 60 4106 1 +a 61 62 4045 +a 61 4106 1 +a 62 63 4044 +a 62 4106 1 +a 63 64 4043 +a 63 4106 1 +a 64 65 4042 +a 64 4106 1 +a 65 66 4041 +a 65 4106 1 +a 66 67 4040 +a 66 4106 1 +a 67 68 4039 +a 67 4106 1 +a 68 69 4038 +a 68 4106 1 +a 69 70 4037 +a 69 4106 1 +a 70 71 4036 +a 70 4106 1 +a 71 72 4035 +a 71 4106 1 +a 72 73 4034 +a 72 4106 1 +a 73 74 4033 +a 73 4106 1 +a 74 75 4032 +a 74 4106 1 +a 75 76 4031 +a 75 4106 1 +a 76 77 4030 +a 76 4106 1 +a 77 78 4029 +a 77 4106 1 +a 78 79 4028 +a 78 4106 1 +a 79 80 4027 +a 79 4106 1 +a 80 81 4026 +a 80 4106 1 +a 81 82 4025 +a 81 4106 1 +a 82 83 4024 +a 82 4106 1 +a 83 84 4023 +a 83 4106 1 +a 84 85 4022 +a 84 4106 1 +a 85 86 4021 +a 85 4106 1 +a 86 87 4020 +a 86 4106 1 +a 87 88 4019 +a 87 4106 1 +a 88 89 4018 +a 88 4106 1 +a 89 90 4017 +a 89 4106 1 +a 90 91 4016 +a 90 4106 1 +a 91 92 4015 +a 91 4106 1 +a 92 93 4014 +a 92 4106 1 +a 93 94 4013 +a 93 4106 1 +a 94 95 4012 +a 94 4106 1 +a 95 96 4011 +a 95 4106 1 +a 96 97 4010 +a 96 4106 1 +a 97 98 4009 +a 97 4106 1 +a 98 99 4008 +a 98 4106 1 +a 99 100 4007 +a 99 4106 1 +a 100 101 4006 +a 100 4106 1 +a 101 102 4005 +a 101 4106 1 +a 102 103 4004 +a 102 4106 1 +a 103 104 4003 +a 103 4106 1 +a 104 105 4002 +a 104 4106 1 +a 105 106 4001 +a 105 4106 1 +a 106 107 4000 +a 106 4106 1 +a 107 108 3999 +a 107 4106 1 +a 108 109 3998 +a 108 4106 1 +a 109 110 3997 +a 109 4106 1 +a 110 111 3996 +a 110 4106 1 +a 111 112 3995 +a 111 4106 1 +a 112 113 3994 +a 112 4106 1 +a 113 114 3993 +a 113 4106 1 +a 114 115 3992 +a 114 4106 1 +a 115 116 3991 +a 115 4106 1 +a 116 117 3990 +a 116 4106 1 +a 117 118 3989 +a 117 4106 1 +a 118 119 3988 +a 118 4106 1 +a 119 120 3987 +a 119 4106 1 +a 120 121 3986 +a 120 4106 1 +a 121 122 3985 +a 121 4106 1 +a 122 123 3984 +a 122 4106 1 +a 123 124 3983 +a 123 4106 1 +a 124 125 3982 +a 124 4106 1 +a 125 126 3981 +a 125 4106 1 +a 126 127 3980 +a 126 4106 1 +a 127 128 3979 +a 127 4106 1 +a 128 129 3978 +a 128 4106 1 +a 129 130 3977 +a 129 4106 1 +a 130 131 3976 +a 130 4106 1 +a 131 132 3975 +a 131 4106 1 +a 132 133 3974 +a 132 4106 1 +a 133 134 3973 +a 133 4106 1 +a 134 135 3972 +a 134 4106 1 +a 135 136 3971 +a 135 4106 1 +a 136 137 3970 +a 136 4106 1 +a 137 138 3969 +a 137 4106 1 +a 138 139 3968 +a 138 4106 1 +a 139 140 3967 +a 139 4106 1 +a 140 141 3966 +a 140 4106 1 +a 141 142 3965 +a 141 4106 1 +a 142 143 3964 +a 142 4106 1 +a 143 144 3963 +a 143 4106 1 +a 144 145 3962 +a 144 4106 1 +a 145 146 3961 +a 145 4106 1 +a 146 147 3960 +a 146 4106 1 +a 147 148 3959 +a 147 4106 1 +a 148 149 3958 +a 148 4106 1 +a 149 150 3957 +a 149 4106 1 +a 150 151 3956 +a 150 4106 1 +a 151 152 3955 +a 151 4106 1 +a 152 153 3954 +a 152 4106 1 +a 153 154 3953 +a 153 4106 1 +a 154 155 3952 +a 154 4106 1 +a 155 156 3951 +a 155 4106 1 +a 156 157 3950 +a 156 4106 1 +a 157 158 3949 +a 157 4106 1 +a 158 159 3948 +a 158 4106 1 +a 159 160 3947 +a 159 4106 1 +a 160 161 3946 +a 160 4106 1 +a 161 162 3945 +a 161 4106 1 +a 162 163 3944 +a 162 4106 1 +a 163 164 3943 +a 163 4106 1 +a 164 165 3942 +a 164 4106 1 +a 165 166 3941 +a 165 4106 1 +a 166 167 3940 +a 166 4106 1 +a 167 168 3939 +a 167 4106 1 +a 168 169 3938 +a 168 4106 1 +a 169 170 3937 +a 169 4106 1 +a 170 171 3936 +a 170 4106 1 +a 171 172 3935 +a 171 4106 1 +a 172 173 3934 +a 172 4106 1 +a 173 174 3933 +a 173 4106 1 +a 174 175 3932 +a 174 4106 1 +a 175 176 3931 +a 175 4106 1 +a 176 177 3930 +a 176 4106 1 +a 177 178 3929 +a 177 4106 1 +a 178 179 3928 +a 178 4106 1 +a 179 180 3927 +a 179 4106 1 +a 180 181 3926 +a 180 4106 1 +a 181 182 3925 +a 181 4106 1 +a 182 183 3924 +a 182 4106 1 +a 183 184 3923 +a 183 4106 1 +a 184 185 3922 +a 184 4106 1 +a 185 186 3921 +a 185 4106 1 +a 186 187 3920 +a 186 4106 1 +a 187 188 3919 +a 187 4106 1 +a 188 189 3918 +a 188 4106 1 +a 189 190 3917 +a 189 4106 1 +a 190 191 3916 +a 190 4106 1 +a 191 192 3915 +a 191 4106 1 +a 192 193 3914 +a 192 4106 1 +a 193 194 3913 +a 193 4106 1 +a 194 195 3912 +a 194 4106 1 +a 195 196 3911 +a 195 4106 1 +a 196 197 3910 +a 196 4106 1 +a 197 198 3909 +a 197 4106 1 +a 198 199 3908 +a 198 4106 1 +a 199 200 3907 +a 199 4106 1 +a 200 201 3906 +a 200 4106 1 +a 201 202 3905 +a 201 4106 1 +a 202 203 3904 +a 202 4106 1 +a 203 204 3903 +a 203 4106 1 +a 204 205 3902 +a 204 4106 1 +a 205 206 3901 +a 205 4106 1 +a 206 207 3900 +a 206 4106 1 +a 207 208 3899 +a 207 4106 1 +a 208 209 3898 +a 208 4106 1 +a 209 210 3897 +a 209 4106 1 +a 210 211 3896 +a 210 4106 1 +a 211 212 3895 +a 211 4106 1 +a 212 213 3894 +a 212 4106 1 +a 213 214 3893 +a 213 4106 1 +a 214 215 3892 +a 214 4106 1 +a 215 216 3891 +a 215 4106 1 +a 216 217 3890 +a 216 4106 1 +a 217 218 3889 +a 217 4106 1 +a 218 219 3888 +a 218 4106 1 +a 219 220 3887 +a 219 4106 1 +a 220 221 3886 +a 220 4106 1 +a 221 222 3885 +a 221 4106 1 +a 222 223 3884 +a 222 4106 1 +a 223 224 3883 +a 223 4106 1 +a 224 225 3882 +a 224 4106 1 +a 225 226 3881 +a 225 4106 1 +a 226 227 3880 +a 226 4106 1 +a 227 228 3879 +a 227 4106 1 +a 228 229 3878 +a 228 4106 1 +a 229 230 3877 +a 229 4106 1 +a 230 231 3876 +a 230 4106 1 +a 231 232 3875 +a 231 4106 1 +a 232 233 3874 +a 232 4106 1 +a 233 234 3873 +a 233 4106 1 +a 234 235 3872 +a 234 4106 1 +a 235 236 3871 +a 235 4106 1 +a 236 237 3870 +a 236 4106 1 +a 237 238 3869 +a 237 4106 1 +a 238 239 3868 +a 238 4106 1 +a 239 240 3867 +a 239 4106 1 +a 240 241 3866 +a 240 4106 1 +a 241 242 3865 +a 241 4106 1 +a 242 243 3864 +a 242 4106 1 +a 243 244 3863 +a 243 4106 1 +a 244 245 3862 +a 244 4106 1 +a 245 246 3861 +a 245 4106 1 +a 246 247 3860 +a 246 4106 1 +a 247 248 3859 +a 247 4106 1 +a 248 249 3858 +a 248 4106 1 +a 249 250 3857 +a 249 4106 1 +a 250 251 3856 +a 250 4106 1 +a 251 252 3855 +a 251 4106 1 +a 252 253 3854 +a 252 4106 1 +a 253 254 3853 +a 253 4106 1 +a 254 255 3852 +a 254 4106 1 +a 255 256 3851 +a 255 4106 1 +a 256 257 3850 +a 256 4106 1 +a 257 258 3849 +a 257 4106 1 +a 258 259 3848 +a 258 4106 1 +a 259 260 3847 +a 259 4106 1 +a 260 261 3846 +a 260 4106 1 +a 261 262 3845 +a 261 4106 1 +a 262 263 3844 +a 262 4106 1 +a 263 264 3843 +a 263 4106 1 +a 264 265 3842 +a 264 4106 1 +a 265 266 3841 +a 265 4106 1 +a 266 267 3840 +a 266 4106 1 +a 267 268 3839 +a 267 4106 1 +a 268 269 3838 +a 268 4106 1 +a 269 270 3837 +a 269 4106 1 +a 270 271 3836 +a 270 4106 1 +a 271 272 3835 +a 271 4106 1 +a 272 273 3834 +a 272 4106 1 +a 273 274 3833 +a 273 4106 1 +a 274 275 3832 +a 274 4106 1 +a 275 276 3831 +a 275 4106 1 +a 276 277 3830 +a 276 4106 1 +a 277 278 3829 +a 277 4106 1 +a 278 279 3828 +a 278 4106 1 +a 279 280 3827 +a 279 4106 1 +a 280 281 3826 +a 280 4106 1 +a 281 282 3825 +a 281 4106 1 +a 282 283 3824 +a 282 4106 1 +a 283 284 3823 +a 283 4106 1 +a 284 285 3822 +a 284 4106 1 +a 285 286 3821 +a 285 4106 1 +a 286 287 3820 +a 286 4106 1 +a 287 288 3819 +a 287 4106 1 +a 288 289 3818 +a 288 4106 1 +a 289 290 3817 +a 289 4106 1 +a 290 291 3816 +a 290 4106 1 +a 291 292 3815 +a 291 4106 1 +a 292 293 3814 +a 292 4106 1 +a 293 294 3813 +a 293 4106 1 +a 294 295 3812 +a 294 4106 1 +a 295 296 3811 +a 295 4106 1 +a 296 297 3810 +a 296 4106 1 +a 297 298 3809 +a 297 4106 1 +a 298 299 3808 +a 298 4106 1 +a 299 300 3807 +a 299 4106 1 +a 300 301 3806 +a 300 4106 1 +a 301 302 3805 +a 301 4106 1 +a 302 303 3804 +a 302 4106 1 +a 303 304 3803 +a 303 4106 1 +a 304 305 3802 +a 304 4106 1 +a 305 306 3801 +a 305 4106 1 +a 306 307 3800 +a 306 4106 1 +a 307 308 3799 +a 307 4106 1 +a 308 309 3798 +a 308 4106 1 +a 309 310 3797 +a 309 4106 1 +a 310 311 3796 +a 310 4106 1 +a 311 312 3795 +a 311 4106 1 +a 312 313 3794 +a 312 4106 1 +a 313 314 3793 +a 313 4106 1 +a 314 315 3792 +a 314 4106 1 +a 315 316 3791 +a 315 4106 1 +a 316 317 3790 +a 316 4106 1 +a 317 318 3789 +a 317 4106 1 +a 318 319 3788 +a 318 4106 1 +a 319 320 3787 +a 319 4106 1 +a 320 321 3786 +a 320 4106 1 +a 321 322 3785 +a 321 4106 1 +a 322 323 3784 +a 322 4106 1 +a 323 324 3783 +a 323 4106 1 +a 324 325 3782 +a 324 4106 1 +a 325 326 3781 +a 325 4106 1 +a 326 327 3780 +a 326 4106 1 +a 327 328 3779 +a 327 4106 1 +a 328 329 3778 +a 328 4106 1 +a 329 330 3777 +a 329 4106 1 +a 330 331 3776 +a 330 4106 1 +a 331 332 3775 +a 331 4106 1 +a 332 333 3774 +a 332 4106 1 +a 333 334 3773 +a 333 4106 1 +a 334 335 3772 +a 334 4106 1 +a 335 336 3771 +a 335 4106 1 +a 336 337 3770 +a 336 4106 1 +a 337 338 3769 +a 337 4106 1 +a 338 339 3768 +a 338 4106 1 +a 339 340 3767 +a 339 4106 1 +a 340 341 3766 +a 340 4106 1 +a 341 342 3765 +a 341 4106 1 +a 342 343 3764 +a 342 4106 1 +a 343 344 3763 +a 343 4106 1 +a 344 345 3762 +a 344 4106 1 +a 345 346 3761 +a 345 4106 1 +a 346 347 3760 +a 346 4106 1 +a 347 348 3759 +a 347 4106 1 +a 348 349 3758 +a 348 4106 1 +a 349 350 3757 +a 349 4106 1 +a 350 351 3756 +a 350 4106 1 +a 351 352 3755 +a 351 4106 1 +a 352 353 3754 +a 352 4106 1 +a 353 354 3753 +a 353 4106 1 +a 354 355 3752 +a 354 4106 1 +a 355 356 3751 +a 355 4106 1 +a 356 357 3750 +a 356 4106 1 +a 357 358 3749 +a 357 4106 1 +a 358 359 3748 +a 358 4106 1 +a 359 360 3747 +a 359 4106 1 +a 360 361 3746 +a 360 4106 1 +a 361 362 3745 +a 361 4106 1 +a 362 363 3744 +a 362 4106 1 +a 363 364 3743 +a 363 4106 1 +a 364 365 3742 +a 364 4106 1 +a 365 366 3741 +a 365 4106 1 +a 366 367 3740 +a 366 4106 1 +a 367 368 3739 +a 367 4106 1 +a 368 369 3738 +a 368 4106 1 +a 369 370 3737 +a 369 4106 1 +a 370 371 3736 +a 370 4106 1 +a 371 372 3735 +a 371 4106 1 +a 372 373 3734 +a 372 4106 1 +a 373 374 3733 +a 373 4106 1 +a 374 375 3732 +a 374 4106 1 +a 375 376 3731 +a 375 4106 1 +a 376 377 3730 +a 376 4106 1 +a 377 378 3729 +a 377 4106 1 +a 378 379 3728 +a 378 4106 1 +a 379 380 3727 +a 379 4106 1 +a 380 381 3726 +a 380 4106 1 +a 381 382 3725 +a 381 4106 1 +a 382 383 3724 +a 382 4106 1 +a 383 384 3723 +a 383 4106 1 +a 384 385 3722 +a 384 4106 1 +a 385 386 3721 +a 385 4106 1 +a 386 387 3720 +a 386 4106 1 +a 387 388 3719 +a 387 4106 1 +a 388 389 3718 +a 388 4106 1 +a 389 390 3717 +a 389 4106 1 +a 390 391 3716 +a 390 4106 1 +a 391 392 3715 +a 391 4106 1 +a 392 393 3714 +a 392 4106 1 +a 393 394 3713 +a 393 4106 1 +a 394 395 3712 +a 394 4106 1 +a 395 396 3711 +a 395 4106 1 +a 396 397 3710 +a 396 4106 1 +a 397 398 3709 +a 397 4106 1 +a 398 399 3708 +a 398 4106 1 +a 399 400 3707 +a 399 4106 1 +a 400 401 3706 +a 400 4106 1 +a 401 402 3705 +a 401 4106 1 +a 402 403 3704 +a 402 4106 1 +a 403 404 3703 +a 403 4106 1 +a 404 405 3702 +a 404 4106 1 +a 405 406 3701 +a 405 4106 1 +a 406 407 3700 +a 406 4106 1 +a 407 408 3699 +a 407 4106 1 +a 408 409 3698 +a 408 4106 1 +a 409 410 3697 +a 409 4106 1 +a 410 411 3696 +a 410 4106 1 +a 411 412 3695 +a 411 4106 1 +a 412 413 3694 +a 412 4106 1 +a 413 414 3693 +a 413 4106 1 +a 414 415 3692 +a 414 4106 1 +a 415 416 3691 +a 415 4106 1 +a 416 417 3690 +a 416 4106 1 +a 417 418 3689 +a 417 4106 1 +a 418 419 3688 +a 418 4106 1 +a 419 420 3687 +a 419 4106 1 +a 420 421 3686 +a 420 4106 1 +a 421 422 3685 +a 421 4106 1 +a 422 423 3684 +a 422 4106 1 +a 423 424 3683 +a 423 4106 1 +a 424 425 3682 +a 424 4106 1 +a 425 426 3681 +a 425 4106 1 +a 426 427 3680 +a 426 4106 1 +a 427 428 3679 +a 427 4106 1 +a 428 429 3678 +a 428 4106 1 +a 429 430 3677 +a 429 4106 1 +a 430 431 3676 +a 430 4106 1 +a 431 432 3675 +a 431 4106 1 +a 432 433 3674 +a 432 4106 1 +a 433 434 3673 +a 433 4106 1 +a 434 435 3672 +a 434 4106 1 +a 435 436 3671 +a 435 4106 1 +a 436 437 3670 +a 436 4106 1 +a 437 438 3669 +a 437 4106 1 +a 438 439 3668 +a 438 4106 1 +a 439 440 3667 +a 439 4106 1 +a 440 441 3666 +a 440 4106 1 +a 441 442 3665 +a 441 4106 1 +a 442 443 3664 +a 442 4106 1 +a 443 444 3663 +a 443 4106 1 +a 444 445 3662 +a 444 4106 1 +a 445 446 3661 +a 445 4106 1 +a 446 447 3660 +a 446 4106 1 +a 447 448 3659 +a 447 4106 1 +a 448 449 3658 +a 448 4106 1 +a 449 450 3657 +a 449 4106 1 +a 450 451 3656 +a 450 4106 1 +a 451 452 3655 +a 451 4106 1 +a 452 453 3654 +a 452 4106 1 +a 453 454 3653 +a 453 4106 1 +a 454 455 3652 +a 454 4106 1 +a 455 456 3651 +a 455 4106 1 +a 456 457 3650 +a 456 4106 1 +a 457 458 3649 +a 457 4106 1 +a 458 459 3648 +a 458 4106 1 +a 459 460 3647 +a 459 4106 1 +a 460 461 3646 +a 460 4106 1 +a 461 462 3645 +a 461 4106 1 +a 462 463 3644 +a 462 4106 1 +a 463 464 3643 +a 463 4106 1 +a 464 465 3642 +a 464 4106 1 +a 465 466 3641 +a 465 4106 1 +a 466 467 3640 +a 466 4106 1 +a 467 468 3639 +a 467 4106 1 +a 468 469 3638 +a 468 4106 1 +a 469 470 3637 +a 469 4106 1 +a 470 471 3636 +a 470 4106 1 +a 471 472 3635 +a 471 4106 1 +a 472 473 3634 +a 472 4106 1 +a 473 474 3633 +a 473 4106 1 +a 474 475 3632 +a 474 4106 1 +a 475 476 3631 +a 475 4106 1 +a 476 477 3630 +a 476 4106 1 +a 477 478 3629 +a 477 4106 1 +a 478 479 3628 +a 478 4106 1 +a 479 480 3627 +a 479 4106 1 +a 480 481 3626 +a 480 4106 1 +a 481 482 3625 +a 481 4106 1 +a 482 483 3624 +a 482 4106 1 +a 483 484 3623 +a 483 4106 1 +a 484 485 3622 +a 484 4106 1 +a 485 486 3621 +a 485 4106 1 +a 486 487 3620 +a 486 4106 1 +a 487 488 3619 +a 487 4106 1 +a 488 489 3618 +a 488 4106 1 +a 489 490 3617 +a 489 4106 1 +a 490 491 3616 +a 490 4106 1 +a 491 492 3615 +a 491 4106 1 +a 492 493 3614 +a 492 4106 1 +a 493 494 3613 +a 493 4106 1 +a 494 495 3612 +a 494 4106 1 +a 495 496 3611 +a 495 4106 1 +a 496 497 3610 +a 496 4106 1 +a 497 498 3609 +a 497 4106 1 +a 498 499 3608 +a 498 4106 1 +a 499 500 3607 +a 499 4106 1 +a 500 501 3606 +a 500 4106 1 +a 501 502 3605 +a 501 4106 1 +a 502 503 3604 +a 502 4106 1 +a 503 504 3603 +a 503 4106 1 +a 504 505 3602 +a 504 4106 1 +a 505 506 3601 +a 505 4106 1 +a 506 507 3600 +a 506 4106 1 +a 507 508 3599 +a 507 4106 1 +a 508 509 3598 +a 508 4106 1 +a 509 510 3597 +a 509 4106 1 +a 510 511 3596 +a 510 4106 1 +a 511 512 3595 +a 511 4106 1 +a 512 513 3594 +a 512 4106 1 +a 513 514 3593 +a 513 4106 1 +a 514 515 3592 +a 514 4106 1 +a 515 516 3591 +a 515 4106 1 +a 516 517 3590 +a 516 4106 1 +a 517 518 3589 +a 517 4106 1 +a 518 519 3588 +a 518 4106 1 +a 519 520 3587 +a 519 4106 1 +a 520 521 3586 +a 520 4106 1 +a 521 522 3585 +a 521 4106 1 +a 522 523 3584 +a 522 4106 1 +a 523 524 3583 +a 523 4106 1 +a 524 525 3582 +a 524 4106 1 +a 525 526 3581 +a 525 4106 1 +a 526 527 3580 +a 526 4106 1 +a 527 528 3579 +a 527 4106 1 +a 528 529 3578 +a 528 4106 1 +a 529 530 3577 +a 529 4106 1 +a 530 531 3576 +a 530 4106 1 +a 531 532 3575 +a 531 4106 1 +a 532 533 3574 +a 532 4106 1 +a 533 534 3573 +a 533 4106 1 +a 534 535 3572 +a 534 4106 1 +a 535 536 3571 +a 535 4106 1 +a 536 537 3570 +a 536 4106 1 +a 537 538 3569 +a 537 4106 1 +a 538 539 3568 +a 538 4106 1 +a 539 540 3567 +a 539 4106 1 +a 540 541 3566 +a 540 4106 1 +a 541 542 3565 +a 541 4106 1 +a 542 543 3564 +a 542 4106 1 +a 543 544 3563 +a 543 4106 1 +a 544 545 3562 +a 544 4106 1 +a 545 546 3561 +a 545 4106 1 +a 546 547 3560 +a 546 4106 1 +a 547 548 3559 +a 547 4106 1 +a 548 549 3558 +a 548 4106 1 +a 549 550 3557 +a 549 4106 1 +a 550 551 3556 +a 550 4106 1 +a 551 552 3555 +a 551 4106 1 +a 552 553 3554 +a 552 4106 1 +a 553 554 3553 +a 553 4106 1 +a 554 555 3552 +a 554 4106 1 +a 555 556 3551 +a 555 4106 1 +a 556 557 3550 +a 556 4106 1 +a 557 558 3549 +a 557 4106 1 +a 558 559 3548 +a 558 4106 1 +a 559 560 3547 +a 559 4106 1 +a 560 561 3546 +a 560 4106 1 +a 561 562 3545 +a 561 4106 1 +a 562 563 3544 +a 562 4106 1 +a 563 564 3543 +a 563 4106 1 +a 564 565 3542 +a 564 4106 1 +a 565 566 3541 +a 565 4106 1 +a 566 567 3540 +a 566 4106 1 +a 567 568 3539 +a 567 4106 1 +a 568 569 3538 +a 568 4106 1 +a 569 570 3537 +a 569 4106 1 +a 570 571 3536 +a 570 4106 1 +a 571 572 3535 +a 571 4106 1 +a 572 573 3534 +a 572 4106 1 +a 573 574 3533 +a 573 4106 1 +a 574 575 3532 +a 574 4106 1 +a 575 576 3531 +a 575 4106 1 +a 576 577 3530 +a 576 4106 1 +a 577 578 3529 +a 577 4106 1 +a 578 579 3528 +a 578 4106 1 +a 579 580 3527 +a 579 4106 1 +a 580 581 3526 +a 580 4106 1 +a 581 582 3525 +a 581 4106 1 +a 582 583 3524 +a 582 4106 1 +a 583 584 3523 +a 583 4106 1 +a 584 585 3522 +a 584 4106 1 +a 585 586 3521 +a 585 4106 1 +a 586 587 3520 +a 586 4106 1 +a 587 588 3519 +a 587 4106 1 +a 588 589 3518 +a 588 4106 1 +a 589 590 3517 +a 589 4106 1 +a 590 591 3516 +a 590 4106 1 +a 591 592 3515 +a 591 4106 1 +a 592 593 3514 +a 592 4106 1 +a 593 594 3513 +a 593 4106 1 +a 594 595 3512 +a 594 4106 1 +a 595 596 3511 +a 595 4106 1 +a 596 597 3510 +a 596 4106 1 +a 597 598 3509 +a 597 4106 1 +a 598 599 3508 +a 598 4106 1 +a 599 600 3507 +a 599 4106 1 +a 600 601 3506 +a 600 4106 1 +a 601 602 3505 +a 601 4106 1 +a 602 603 3504 +a 602 4106 1 +a 603 604 3503 +a 603 4106 1 +a 604 605 3502 +a 604 4106 1 +a 605 606 3501 +a 605 4106 1 +a 606 607 3500 +a 606 4106 1 +a 607 608 3499 +a 607 4106 1 +a 608 609 3498 +a 608 4106 1 +a 609 610 3497 +a 609 4106 1 +a 610 611 3496 +a 610 4106 1 +a 611 612 3495 +a 611 4106 1 +a 612 613 3494 +a 612 4106 1 +a 613 614 3493 +a 613 4106 1 +a 614 615 3492 +a 614 4106 1 +a 615 616 3491 +a 615 4106 1 +a 616 617 3490 +a 616 4106 1 +a 617 618 3489 +a 617 4106 1 +a 618 619 3488 +a 618 4106 1 +a 619 620 3487 +a 619 4106 1 +a 620 621 3486 +a 620 4106 1 +a 621 622 3485 +a 621 4106 1 +a 622 623 3484 +a 622 4106 1 +a 623 624 3483 +a 623 4106 1 +a 624 625 3482 +a 624 4106 1 +a 625 626 3481 +a 625 4106 1 +a 626 627 3480 +a 626 4106 1 +a 627 628 3479 +a 627 4106 1 +a 628 629 3478 +a 628 4106 1 +a 629 630 3477 +a 629 4106 1 +a 630 631 3476 +a 630 4106 1 +a 631 632 3475 +a 631 4106 1 +a 632 633 3474 +a 632 4106 1 +a 633 634 3473 +a 633 4106 1 +a 634 635 3472 +a 634 4106 1 +a 635 636 3471 +a 635 4106 1 +a 636 637 3470 +a 636 4106 1 +a 637 638 3469 +a 637 4106 1 +a 638 639 3468 +a 638 4106 1 +a 639 640 3467 +a 639 4106 1 +a 640 641 3466 +a 640 4106 1 +a 641 642 3465 +a 641 4106 1 +a 642 643 3464 +a 642 4106 1 +a 643 644 3463 +a 643 4106 1 +a 644 645 3462 +a 644 4106 1 +a 645 646 3461 +a 645 4106 1 +a 646 647 3460 +a 646 4106 1 +a 647 648 3459 +a 647 4106 1 +a 648 649 3458 +a 648 4106 1 +a 649 650 3457 +a 649 4106 1 +a 650 651 3456 +a 650 4106 1 +a 651 652 3455 +a 651 4106 1 +a 652 653 3454 +a 652 4106 1 +a 653 654 3453 +a 653 4106 1 +a 654 655 3452 +a 654 4106 1 +a 655 656 3451 +a 655 4106 1 +a 656 657 3450 +a 656 4106 1 +a 657 658 3449 +a 657 4106 1 +a 658 659 3448 +a 658 4106 1 +a 659 660 3447 +a 659 4106 1 +a 660 661 3446 +a 660 4106 1 +a 661 662 3445 +a 661 4106 1 +a 662 663 3444 +a 662 4106 1 +a 663 664 3443 +a 663 4106 1 +a 664 665 3442 +a 664 4106 1 +a 665 666 3441 +a 665 4106 1 +a 666 667 3440 +a 666 4106 1 +a 667 668 3439 +a 667 4106 1 +a 668 669 3438 +a 668 4106 1 +a 669 670 3437 +a 669 4106 1 +a 670 671 3436 +a 670 4106 1 +a 671 672 3435 +a 671 4106 1 +a 672 673 3434 +a 672 4106 1 +a 673 674 3433 +a 673 4106 1 +a 674 675 3432 +a 674 4106 1 +a 675 676 3431 +a 675 4106 1 +a 676 677 3430 +a 676 4106 1 +a 677 678 3429 +a 677 4106 1 +a 678 679 3428 +a 678 4106 1 +a 679 680 3427 +a 679 4106 1 +a 680 681 3426 +a 680 4106 1 +a 681 682 3425 +a 681 4106 1 +a 682 683 3424 +a 682 4106 1 +a 683 684 3423 +a 683 4106 1 +a 684 685 3422 +a 684 4106 1 +a 685 686 3421 +a 685 4106 1 +a 686 687 3420 +a 686 4106 1 +a 687 688 3419 +a 687 4106 1 +a 688 689 3418 +a 688 4106 1 +a 689 690 3417 +a 689 4106 1 +a 690 691 3416 +a 690 4106 1 +a 691 692 3415 +a 691 4106 1 +a 692 693 3414 +a 692 4106 1 +a 693 694 3413 +a 693 4106 1 +a 694 695 3412 +a 694 4106 1 +a 695 696 3411 +a 695 4106 1 +a 696 697 3410 +a 696 4106 1 +a 697 698 3409 +a 697 4106 1 +a 698 699 3408 +a 698 4106 1 +a 699 700 3407 +a 699 4106 1 +a 700 701 3406 +a 700 4106 1 +a 701 702 3405 +a 701 4106 1 +a 702 703 3404 +a 702 4106 1 +a 703 704 3403 +a 703 4106 1 +a 704 705 3402 +a 704 4106 1 +a 705 706 3401 +a 705 4106 1 +a 706 707 3400 +a 706 4106 1 +a 707 708 3399 +a 707 4106 1 +a 708 709 3398 +a 708 4106 1 +a 709 710 3397 +a 709 4106 1 +a 710 711 3396 +a 710 4106 1 +a 711 712 3395 +a 711 4106 1 +a 712 713 3394 +a 712 4106 1 +a 713 714 3393 +a 713 4106 1 +a 714 715 3392 +a 714 4106 1 +a 715 716 3391 +a 715 4106 1 +a 716 717 3390 +a 716 4106 1 +a 717 718 3389 +a 717 4106 1 +a 718 719 3388 +a 718 4106 1 +a 719 720 3387 +a 719 4106 1 +a 720 721 3386 +a 720 4106 1 +a 721 722 3385 +a 721 4106 1 +a 722 723 3384 +a 722 4106 1 +a 723 724 3383 +a 723 4106 1 +a 724 725 3382 +a 724 4106 1 +a 725 726 3381 +a 725 4106 1 +a 726 727 3380 +a 726 4106 1 +a 727 728 3379 +a 727 4106 1 +a 728 729 3378 +a 728 4106 1 +a 729 730 3377 +a 729 4106 1 +a 730 731 3376 +a 730 4106 1 +a 731 732 3375 +a 731 4106 1 +a 732 733 3374 +a 732 4106 1 +a 733 734 3373 +a 733 4106 1 +a 734 735 3372 +a 734 4106 1 +a 735 736 3371 +a 735 4106 1 +a 736 737 3370 +a 736 4106 1 +a 737 738 3369 +a 737 4106 1 +a 738 739 3368 +a 738 4106 1 +a 739 740 3367 +a 739 4106 1 +a 740 741 3366 +a 740 4106 1 +a 741 742 3365 +a 741 4106 1 +a 742 743 3364 +a 742 4106 1 +a 743 744 3363 +a 743 4106 1 +a 744 745 3362 +a 744 4106 1 +a 745 746 3361 +a 745 4106 1 +a 746 747 3360 +a 746 4106 1 +a 747 748 3359 +a 747 4106 1 +a 748 749 3358 +a 748 4106 1 +a 749 750 3357 +a 749 4106 1 +a 750 751 3356 +a 750 4106 1 +a 751 752 3355 +a 751 4106 1 +a 752 753 3354 +a 752 4106 1 +a 753 754 3353 +a 753 4106 1 +a 754 755 3352 +a 754 4106 1 +a 755 756 3351 +a 755 4106 1 +a 756 757 3350 +a 756 4106 1 +a 757 758 3349 +a 757 4106 1 +a 758 759 3348 +a 758 4106 1 +a 759 760 3347 +a 759 4106 1 +a 760 761 3346 +a 760 4106 1 +a 761 762 3345 +a 761 4106 1 +a 762 763 3344 +a 762 4106 1 +a 763 764 3343 +a 763 4106 1 +a 764 765 3342 +a 764 4106 1 +a 765 766 3341 +a 765 4106 1 +a 766 767 3340 +a 766 4106 1 +a 767 768 3339 +a 767 4106 1 +a 768 769 3338 +a 768 4106 1 +a 769 770 3337 +a 769 4106 1 +a 770 771 3336 +a 770 4106 1 +a 771 772 3335 +a 771 4106 1 +a 772 773 3334 +a 772 4106 1 +a 773 774 3333 +a 773 4106 1 +a 774 775 3332 +a 774 4106 1 +a 775 776 3331 +a 775 4106 1 +a 776 777 3330 +a 776 4106 1 +a 777 778 3329 +a 777 4106 1 +a 778 779 3328 +a 778 4106 1 +a 779 780 3327 +a 779 4106 1 +a 780 781 3326 +a 780 4106 1 +a 781 782 3325 +a 781 4106 1 +a 782 783 3324 +a 782 4106 1 +a 783 784 3323 +a 783 4106 1 +a 784 785 3322 +a 784 4106 1 +a 785 786 3321 +a 785 4106 1 +a 786 787 3320 +a 786 4106 1 +a 787 788 3319 +a 787 4106 1 +a 788 789 3318 +a 788 4106 1 +a 789 790 3317 +a 789 4106 1 +a 790 791 3316 +a 790 4106 1 +a 791 792 3315 +a 791 4106 1 +a 792 793 3314 +a 792 4106 1 +a 793 794 3313 +a 793 4106 1 +a 794 795 3312 +a 794 4106 1 +a 795 796 3311 +a 795 4106 1 +a 796 797 3310 +a 796 4106 1 +a 797 798 3309 +a 797 4106 1 +a 798 799 3308 +a 798 4106 1 +a 799 800 3307 +a 799 4106 1 +a 800 801 3306 +a 800 4106 1 +a 801 802 3305 +a 801 4106 1 +a 802 803 3304 +a 802 4106 1 +a 803 804 3303 +a 803 4106 1 +a 804 805 3302 +a 804 4106 1 +a 805 806 3301 +a 805 4106 1 +a 806 807 3300 +a 806 4106 1 +a 807 808 3299 +a 807 4106 1 +a 808 809 3298 +a 808 4106 1 +a 809 810 3297 +a 809 4106 1 +a 810 811 3296 +a 810 4106 1 +a 811 812 3295 +a 811 4106 1 +a 812 813 3294 +a 812 4106 1 +a 813 814 3293 +a 813 4106 1 +a 814 815 3292 +a 814 4106 1 +a 815 816 3291 +a 815 4106 1 +a 816 817 3290 +a 816 4106 1 +a 817 818 3289 +a 817 4106 1 +a 818 819 3288 +a 818 4106 1 +a 819 820 3287 +a 819 4106 1 +a 820 821 3286 +a 820 4106 1 +a 821 822 3285 +a 821 4106 1 +a 822 823 3284 +a 822 4106 1 +a 823 824 3283 +a 823 4106 1 +a 824 825 3282 +a 824 4106 1 +a 825 826 3281 +a 825 4106 1 +a 826 827 3280 +a 826 4106 1 +a 827 828 3279 +a 827 4106 1 +a 828 829 3278 +a 828 4106 1 +a 829 830 3277 +a 829 4106 1 +a 830 831 3276 +a 830 4106 1 +a 831 832 3275 +a 831 4106 1 +a 832 833 3274 +a 832 4106 1 +a 833 834 3273 +a 833 4106 1 +a 834 835 3272 +a 834 4106 1 +a 835 836 3271 +a 835 4106 1 +a 836 837 3270 +a 836 4106 1 +a 837 838 3269 +a 837 4106 1 +a 838 839 3268 +a 838 4106 1 +a 839 840 3267 +a 839 4106 1 +a 840 841 3266 +a 840 4106 1 +a 841 842 3265 +a 841 4106 1 +a 842 843 3264 +a 842 4106 1 +a 843 844 3263 +a 843 4106 1 +a 844 845 3262 +a 844 4106 1 +a 845 846 3261 +a 845 4106 1 +a 846 847 3260 +a 846 4106 1 +a 847 848 3259 +a 847 4106 1 +a 848 849 3258 +a 848 4106 1 +a 849 850 3257 +a 849 4106 1 +a 850 851 3256 +a 850 4106 1 +a 851 852 3255 +a 851 4106 1 +a 852 853 3254 +a 852 4106 1 +a 853 854 3253 +a 853 4106 1 +a 854 855 3252 +a 854 4106 1 +a 855 856 3251 +a 855 4106 1 +a 856 857 3250 +a 856 4106 1 +a 857 858 3249 +a 857 4106 1 +a 858 859 3248 +a 858 4106 1 +a 859 860 3247 +a 859 4106 1 +a 860 861 3246 +a 860 4106 1 +a 861 862 3245 +a 861 4106 1 +a 862 863 3244 +a 862 4106 1 +a 863 864 3243 +a 863 4106 1 +a 864 865 3242 +a 864 4106 1 +a 865 866 3241 +a 865 4106 1 +a 866 867 3240 +a 866 4106 1 +a 867 868 3239 +a 867 4106 1 +a 868 869 3238 +a 868 4106 1 +a 869 870 3237 +a 869 4106 1 +a 870 871 3236 +a 870 4106 1 +a 871 872 3235 +a 871 4106 1 +a 872 873 3234 +a 872 4106 1 +a 873 874 3233 +a 873 4106 1 +a 874 875 3232 +a 874 4106 1 +a 875 876 3231 +a 875 4106 1 +a 876 877 3230 +a 876 4106 1 +a 877 878 3229 +a 877 4106 1 +a 878 879 3228 +a 878 4106 1 +a 879 880 3227 +a 879 4106 1 +a 880 881 3226 +a 880 4106 1 +a 881 882 3225 +a 881 4106 1 +a 882 883 3224 +a 882 4106 1 +a 883 884 3223 +a 883 4106 1 +a 884 885 3222 +a 884 4106 1 +a 885 886 3221 +a 885 4106 1 +a 886 887 3220 +a 886 4106 1 +a 887 888 3219 +a 887 4106 1 +a 888 889 3218 +a 888 4106 1 +a 889 890 3217 +a 889 4106 1 +a 890 891 3216 +a 890 4106 1 +a 891 892 3215 +a 891 4106 1 +a 892 893 3214 +a 892 4106 1 +a 893 894 3213 +a 893 4106 1 +a 894 895 3212 +a 894 4106 1 +a 895 896 3211 +a 895 4106 1 +a 896 897 3210 +a 896 4106 1 +a 897 898 3209 +a 897 4106 1 +a 898 899 3208 +a 898 4106 1 +a 899 900 3207 +a 899 4106 1 +a 900 901 3206 +a 900 4106 1 +a 901 902 3205 +a 901 4106 1 +a 902 903 3204 +a 902 4106 1 +a 903 904 3203 +a 903 4106 1 +a 904 905 3202 +a 904 4106 1 +a 905 906 3201 +a 905 4106 1 +a 906 907 3200 +a 906 4106 1 +a 907 908 3199 +a 907 4106 1 +a 908 909 3198 +a 908 4106 1 +a 909 910 3197 +a 909 4106 1 +a 910 911 3196 +a 910 4106 1 +a 911 912 3195 +a 911 4106 1 +a 912 913 3194 +a 912 4106 1 +a 913 914 3193 +a 913 4106 1 +a 914 915 3192 +a 914 4106 1 +a 915 916 3191 +a 915 4106 1 +a 916 917 3190 +a 916 4106 1 +a 917 918 3189 +a 917 4106 1 +a 918 919 3188 +a 918 4106 1 +a 919 920 3187 +a 919 4106 1 +a 920 921 3186 +a 920 4106 1 +a 921 922 3185 +a 921 4106 1 +a 922 923 3184 +a 922 4106 1 +a 923 924 3183 +a 923 4106 1 +a 924 925 3182 +a 924 4106 1 +a 925 926 3181 +a 925 4106 1 +a 926 927 3180 +a 926 4106 1 +a 927 928 3179 +a 927 4106 1 +a 928 929 3178 +a 928 4106 1 +a 929 930 3177 +a 929 4106 1 +a 930 931 3176 +a 930 4106 1 +a 931 932 3175 +a 931 4106 1 +a 932 933 3174 +a 932 4106 1 +a 933 934 3173 +a 933 4106 1 +a 934 935 3172 +a 934 4106 1 +a 935 936 3171 +a 935 4106 1 +a 936 937 3170 +a 936 4106 1 +a 937 938 3169 +a 937 4106 1 +a 938 939 3168 +a 938 4106 1 +a 939 940 3167 +a 939 4106 1 +a 940 941 3166 +a 940 4106 1 +a 941 942 3165 +a 941 4106 1 +a 942 943 3164 +a 942 4106 1 +a 943 944 3163 +a 943 4106 1 +a 944 945 3162 +a 944 4106 1 +a 945 946 3161 +a 945 4106 1 +a 946 947 3160 +a 946 4106 1 +a 947 948 3159 +a 947 4106 1 +a 948 949 3158 +a 948 4106 1 +a 949 950 3157 +a 949 4106 1 +a 950 951 3156 +a 950 4106 1 +a 951 952 3155 +a 951 4106 1 +a 952 953 3154 +a 952 4106 1 +a 953 954 3153 +a 953 4106 1 +a 954 955 3152 +a 954 4106 1 +a 955 956 3151 +a 955 4106 1 +a 956 957 3150 +a 956 4106 1 +a 957 958 3149 +a 957 4106 1 +a 958 959 3148 +a 958 4106 1 +a 959 960 3147 +a 959 4106 1 +a 960 961 3146 +a 960 4106 1 +a 961 962 3145 +a 961 4106 1 +a 962 963 3144 +a 962 4106 1 +a 963 964 3143 +a 963 4106 1 +a 964 965 3142 +a 964 4106 1 +a 965 966 3141 +a 965 4106 1 +a 966 967 3140 +a 966 4106 1 +a 967 968 3139 +a 967 4106 1 +a 968 969 3138 +a 968 4106 1 +a 969 970 3137 +a 969 4106 1 +a 970 971 3136 +a 970 4106 1 +a 971 972 3135 +a 971 4106 1 +a 972 973 3134 +a 972 4106 1 +a 973 974 3133 +a 973 4106 1 +a 974 975 3132 +a 974 4106 1 +a 975 976 3131 +a 975 4106 1 +a 976 977 3130 +a 976 4106 1 +a 977 978 3129 +a 977 4106 1 +a 978 979 3128 +a 978 4106 1 +a 979 980 3127 +a 979 4106 1 +a 980 981 3126 +a 980 4106 1 +a 981 982 3125 +a 981 4106 1 +a 982 983 3124 +a 982 4106 1 +a 983 984 3123 +a 983 4106 1 +a 984 985 3122 +a 984 4106 1 +a 985 986 3121 +a 985 4106 1 +a 986 987 3120 +a 986 4106 1 +a 987 988 3119 +a 987 4106 1 +a 988 989 3118 +a 988 4106 1 +a 989 990 3117 +a 989 4106 1 +a 990 991 3116 +a 990 4106 1 +a 991 992 3115 +a 991 4106 1 +a 992 993 3114 +a 992 4106 1 +a 993 994 3113 +a 993 4106 1 +a 994 995 3112 +a 994 4106 1 +a 995 996 3111 +a 995 4106 1 +a 996 997 3110 +a 996 4106 1 +a 997 998 3109 +a 997 4106 1 +a 998 999 3108 +a 998 4106 1 +a 999 1000 3107 +a 999 4106 1 +a 1000 1001 3106 +a 1000 4106 1 +a 1001 1002 3105 +a 1001 4106 1 +a 1002 1003 3104 +a 1002 4106 1 +a 1003 1004 3103 +a 1003 4106 1 +a 1004 1005 3102 +a 1004 4106 1 +a 1005 1006 3101 +a 1005 4106 1 +a 1006 1007 3100 +a 1006 4106 1 +a 1007 1008 3099 +a 1007 4106 1 +a 1008 1009 3098 +a 1008 4106 1 +a 1009 1010 3097 +a 1009 4106 1 +a 1010 1011 3096 +a 1010 4106 1 +a 1011 1012 3095 +a 1011 4106 1 +a 1012 1013 3094 +a 1012 4106 1 +a 1013 1014 3093 +a 1013 4106 1 +a 1014 1015 3092 +a 1014 4106 1 +a 1015 1016 3091 +a 1015 4106 1 +a 1016 1017 3090 +a 1016 4106 1 +a 1017 1018 3089 +a 1017 4106 1 +a 1018 1019 3088 +a 1018 4106 1 +a 1019 1020 3087 +a 1019 4106 1 +a 1020 1021 3086 +a 1020 4106 1 +a 1021 1022 3085 +a 1021 4106 1 +a 1022 1023 3084 +a 1022 4106 1 +a 1023 1024 3083 +a 1023 4106 1 +a 1024 1025 3082 +a 1024 4106 1 +a 1025 1026 3081 +a 1025 4106 1 +a 1026 1027 3080 +a 1026 4106 1 +a 1027 1028 3079 +a 1027 4106 1 +a 1028 1029 3078 +a 1028 4106 1 +a 1029 1030 3077 +a 1029 4106 1 +a 1030 1031 3076 +a 1030 4106 1 +a 1031 1032 3075 +a 1031 4106 1 +a 1032 1033 3074 +a 1032 4106 1 +a 1033 1034 3073 +a 1033 4106 1 +a 1034 1035 3072 +a 1034 4106 1 +a 1035 1036 3071 +a 1035 4106 1 +a 1036 1037 3070 +a 1036 4106 1 +a 1037 1038 3069 +a 1037 4106 1 +a 1038 1039 3068 +a 1038 4106 1 +a 1039 1040 3067 +a 1039 4106 1 +a 1040 1041 3066 +a 1040 4106 1 +a 1041 1042 3065 +a 1041 4106 1 +a 1042 1043 3064 +a 1042 4106 1 +a 1043 1044 3063 +a 1043 4106 1 +a 1044 1045 3062 +a 1044 4106 1 +a 1045 1046 3061 +a 1045 4106 1 +a 1046 1047 3060 +a 1046 4106 1 +a 1047 1048 3059 +a 1047 4106 1 +a 1048 1049 3058 +a 1048 4106 1 +a 1049 1050 3057 +a 1049 4106 1 +a 1050 1051 3056 +a 1050 4106 1 +a 1051 1052 3055 +a 1051 4106 1 +a 1052 1053 3054 +a 1052 4106 1 +a 1053 1054 3053 +a 1053 4106 1 +a 1054 1055 3052 +a 1054 4106 1 +a 1055 1056 3051 +a 1055 4106 1 +a 1056 1057 3050 +a 1056 4106 1 +a 1057 1058 3049 +a 1057 4106 1 +a 1058 1059 3048 +a 1058 4106 1 +a 1059 1060 3047 +a 1059 4106 1 +a 1060 1061 3046 +a 1060 4106 1 +a 1061 1062 3045 +a 1061 4106 1 +a 1062 1063 3044 +a 1062 4106 1 +a 1063 1064 3043 +a 1063 4106 1 +a 1064 1065 3042 +a 1064 4106 1 +a 1065 1066 3041 +a 1065 4106 1 +a 1066 1067 3040 +a 1066 4106 1 +a 1067 1068 3039 +a 1067 4106 1 +a 1068 1069 3038 +a 1068 4106 1 +a 1069 1070 3037 +a 1069 4106 1 +a 1070 1071 3036 +a 1070 4106 1 +a 1071 1072 3035 +a 1071 4106 1 +a 1072 1073 3034 +a 1072 4106 1 +a 1073 1074 3033 +a 1073 4106 1 +a 1074 1075 3032 +a 1074 4106 1 +a 1075 1076 3031 +a 1075 4106 1 +a 1076 1077 3030 +a 1076 4106 1 +a 1077 1078 3029 +a 1077 4106 1 +a 1078 1079 3028 +a 1078 4106 1 +a 1079 1080 3027 +a 1079 4106 1 +a 1080 1081 3026 +a 1080 4106 1 +a 1081 1082 3025 +a 1081 4106 1 +a 1082 1083 3024 +a 1082 4106 1 +a 1083 1084 3023 +a 1083 4106 1 +a 1084 1085 3022 +a 1084 4106 1 +a 1085 1086 3021 +a 1085 4106 1 +a 1086 1087 3020 +a 1086 4106 1 +a 1087 1088 3019 +a 1087 4106 1 +a 1088 1089 3018 +a 1088 4106 1 +a 1089 1090 3017 +a 1089 4106 1 +a 1090 1091 3016 +a 1090 4106 1 +a 1091 1092 3015 +a 1091 4106 1 +a 1092 1093 3014 +a 1092 4106 1 +a 1093 1094 3013 +a 1093 4106 1 +a 1094 1095 3012 +a 1094 4106 1 +a 1095 1096 3011 +a 1095 4106 1 +a 1096 1097 3010 +a 1096 4106 1 +a 1097 1098 3009 +a 1097 4106 1 +a 1098 1099 3008 +a 1098 4106 1 +a 1099 1100 3007 +a 1099 4106 1 +a 1100 1101 3006 +a 1100 4106 1 +a 1101 1102 3005 +a 1101 4106 1 +a 1102 1103 3004 +a 1102 4106 1 +a 1103 1104 3003 +a 1103 4106 1 +a 1104 1105 3002 +a 1104 4106 1 +a 1105 1106 3001 +a 1105 4106 1 +a 1106 1107 3000 +a 1106 4106 1 +a 1107 1108 2999 +a 1107 4106 1 +a 1108 1109 2998 +a 1108 4106 1 +a 1109 1110 2997 +a 1109 4106 1 +a 1110 1111 2996 +a 1110 4106 1 +a 1111 1112 2995 +a 1111 4106 1 +a 1112 1113 2994 +a 1112 4106 1 +a 1113 1114 2993 +a 1113 4106 1 +a 1114 1115 2992 +a 1114 4106 1 +a 1115 1116 2991 +a 1115 4106 1 +a 1116 1117 2990 +a 1116 4106 1 +a 1117 1118 2989 +a 1117 4106 1 +a 1118 1119 2988 +a 1118 4106 1 +a 1119 1120 2987 +a 1119 4106 1 +a 1120 1121 2986 +a 1120 4106 1 +a 1121 1122 2985 +a 1121 4106 1 +a 1122 1123 2984 +a 1122 4106 1 +a 1123 1124 2983 +a 1123 4106 1 +a 1124 1125 2982 +a 1124 4106 1 +a 1125 1126 2981 +a 1125 4106 1 +a 1126 1127 2980 +a 1126 4106 1 +a 1127 1128 2979 +a 1127 4106 1 +a 1128 1129 2978 +a 1128 4106 1 +a 1129 1130 2977 +a 1129 4106 1 +a 1130 1131 2976 +a 1130 4106 1 +a 1131 1132 2975 +a 1131 4106 1 +a 1132 1133 2974 +a 1132 4106 1 +a 1133 1134 2973 +a 1133 4106 1 +a 1134 1135 2972 +a 1134 4106 1 +a 1135 1136 2971 +a 1135 4106 1 +a 1136 1137 2970 +a 1136 4106 1 +a 1137 1138 2969 +a 1137 4106 1 +a 1138 1139 2968 +a 1138 4106 1 +a 1139 1140 2967 +a 1139 4106 1 +a 1140 1141 2966 +a 1140 4106 1 +a 1141 1142 2965 +a 1141 4106 1 +a 1142 1143 2964 +a 1142 4106 1 +a 1143 1144 2963 +a 1143 4106 1 +a 1144 1145 2962 +a 1144 4106 1 +a 1145 1146 2961 +a 1145 4106 1 +a 1146 1147 2960 +a 1146 4106 1 +a 1147 1148 2959 +a 1147 4106 1 +a 1148 1149 2958 +a 1148 4106 1 +a 1149 1150 2957 +a 1149 4106 1 +a 1150 1151 2956 +a 1150 4106 1 +a 1151 1152 2955 +a 1151 4106 1 +a 1152 1153 2954 +a 1152 4106 1 +a 1153 1154 2953 +a 1153 4106 1 +a 1154 1155 2952 +a 1154 4106 1 +a 1155 1156 2951 +a 1155 4106 1 +a 1156 1157 2950 +a 1156 4106 1 +a 1157 1158 2949 +a 1157 4106 1 +a 1158 1159 2948 +a 1158 4106 1 +a 1159 1160 2947 +a 1159 4106 1 +a 1160 1161 2946 +a 1160 4106 1 +a 1161 1162 2945 +a 1161 4106 1 +a 1162 1163 2944 +a 1162 4106 1 +a 1163 1164 2943 +a 1163 4106 1 +a 1164 1165 2942 +a 1164 4106 1 +a 1165 1166 2941 +a 1165 4106 1 +a 1166 1167 2940 +a 1166 4106 1 +a 1167 1168 2939 +a 1167 4106 1 +a 1168 1169 2938 +a 1168 4106 1 +a 1169 1170 2937 +a 1169 4106 1 +a 1170 1171 2936 +a 1170 4106 1 +a 1171 1172 2935 +a 1171 4106 1 +a 1172 1173 2934 +a 1172 4106 1 +a 1173 1174 2933 +a 1173 4106 1 +a 1174 1175 2932 +a 1174 4106 1 +a 1175 1176 2931 +a 1175 4106 1 +a 1176 1177 2930 +a 1176 4106 1 +a 1177 1178 2929 +a 1177 4106 1 +a 1178 1179 2928 +a 1178 4106 1 +a 1179 1180 2927 +a 1179 4106 1 +a 1180 1181 2926 +a 1180 4106 1 +a 1181 1182 2925 +a 1181 4106 1 +a 1182 1183 2924 +a 1182 4106 1 +a 1183 1184 2923 +a 1183 4106 1 +a 1184 1185 2922 +a 1184 4106 1 +a 1185 1186 2921 +a 1185 4106 1 +a 1186 1187 2920 +a 1186 4106 1 +a 1187 1188 2919 +a 1187 4106 1 +a 1188 1189 2918 +a 1188 4106 1 +a 1189 1190 2917 +a 1189 4106 1 +a 1190 1191 2916 +a 1190 4106 1 +a 1191 1192 2915 +a 1191 4106 1 +a 1192 1193 2914 +a 1192 4106 1 +a 1193 1194 2913 +a 1193 4106 1 +a 1194 1195 2912 +a 1194 4106 1 +a 1195 1196 2911 +a 1195 4106 1 +a 1196 1197 2910 +a 1196 4106 1 +a 1197 1198 2909 +a 1197 4106 1 +a 1198 1199 2908 +a 1198 4106 1 +a 1199 1200 2907 +a 1199 4106 1 +a 1200 1201 2906 +a 1200 4106 1 +a 1201 1202 2905 +a 1201 4106 1 +a 1202 1203 2904 +a 1202 4106 1 +a 1203 1204 2903 +a 1203 4106 1 +a 1204 1205 2902 +a 1204 4106 1 +a 1205 1206 2901 +a 1205 4106 1 +a 1206 1207 2900 +a 1206 4106 1 +a 1207 1208 2899 +a 1207 4106 1 +a 1208 1209 2898 +a 1208 4106 1 +a 1209 1210 2897 +a 1209 4106 1 +a 1210 1211 2896 +a 1210 4106 1 +a 1211 1212 2895 +a 1211 4106 1 +a 1212 1213 2894 +a 1212 4106 1 +a 1213 1214 2893 +a 1213 4106 1 +a 1214 1215 2892 +a 1214 4106 1 +a 1215 1216 2891 +a 1215 4106 1 +a 1216 1217 2890 +a 1216 4106 1 +a 1217 1218 2889 +a 1217 4106 1 +a 1218 1219 2888 +a 1218 4106 1 +a 1219 1220 2887 +a 1219 4106 1 +a 1220 1221 2886 +a 1220 4106 1 +a 1221 1222 2885 +a 1221 4106 1 +a 1222 1223 2884 +a 1222 4106 1 +a 1223 1224 2883 +a 1223 4106 1 +a 1224 1225 2882 +a 1224 4106 1 +a 1225 1226 2881 +a 1225 4106 1 +a 1226 1227 2880 +a 1226 4106 1 +a 1227 1228 2879 +a 1227 4106 1 +a 1228 1229 2878 +a 1228 4106 1 +a 1229 1230 2877 +a 1229 4106 1 +a 1230 1231 2876 +a 1230 4106 1 +a 1231 1232 2875 +a 1231 4106 1 +a 1232 1233 2874 +a 1232 4106 1 +a 1233 1234 2873 +a 1233 4106 1 +a 1234 1235 2872 +a 1234 4106 1 +a 1235 1236 2871 +a 1235 4106 1 +a 1236 1237 2870 +a 1236 4106 1 +a 1237 1238 2869 +a 1237 4106 1 +a 1238 1239 2868 +a 1238 4106 1 +a 1239 1240 2867 +a 1239 4106 1 +a 1240 1241 2866 +a 1240 4106 1 +a 1241 1242 2865 +a 1241 4106 1 +a 1242 1243 2864 +a 1242 4106 1 +a 1243 1244 2863 +a 1243 4106 1 +a 1244 1245 2862 +a 1244 4106 1 +a 1245 1246 2861 +a 1245 4106 1 +a 1246 1247 2860 +a 1246 4106 1 +a 1247 1248 2859 +a 1247 4106 1 +a 1248 1249 2858 +a 1248 4106 1 +a 1249 1250 2857 +a 1249 4106 1 +a 1250 1251 2856 +a 1250 4106 1 +a 1251 1252 2855 +a 1251 4106 1 +a 1252 1253 2854 +a 1252 4106 1 +a 1253 1254 2853 +a 1253 4106 1 +a 1254 1255 2852 +a 1254 4106 1 +a 1255 1256 2851 +a 1255 4106 1 +a 1256 1257 2850 +a 1256 4106 1 +a 1257 1258 2849 +a 1257 4106 1 +a 1258 1259 2848 +a 1258 4106 1 +a 1259 1260 2847 +a 1259 4106 1 +a 1260 1261 2846 +a 1260 4106 1 +a 1261 1262 2845 +a 1261 4106 1 +a 1262 1263 2844 +a 1262 4106 1 +a 1263 1264 2843 +a 1263 4106 1 +a 1264 1265 2842 +a 1264 4106 1 +a 1265 1266 2841 +a 1265 4106 1 +a 1266 1267 2840 +a 1266 4106 1 +a 1267 1268 2839 +a 1267 4106 1 +a 1268 1269 2838 +a 1268 4106 1 +a 1269 1270 2837 +a 1269 4106 1 +a 1270 1271 2836 +a 1270 4106 1 +a 1271 1272 2835 +a 1271 4106 1 +a 1272 1273 2834 +a 1272 4106 1 +a 1273 1274 2833 +a 1273 4106 1 +a 1274 1275 2832 +a 1274 4106 1 +a 1275 1276 2831 +a 1275 4106 1 +a 1276 1277 2830 +a 1276 4106 1 +a 1277 1278 2829 +a 1277 4106 1 +a 1278 1279 2828 +a 1278 4106 1 +a 1279 1280 2827 +a 1279 4106 1 +a 1280 1281 2826 +a 1280 4106 1 +a 1281 1282 2825 +a 1281 4106 1 +a 1282 1283 2824 +a 1282 4106 1 +a 1283 1284 2823 +a 1283 4106 1 +a 1284 1285 2822 +a 1284 4106 1 +a 1285 1286 2821 +a 1285 4106 1 +a 1286 1287 2820 +a 1286 4106 1 +a 1287 1288 2819 +a 1287 4106 1 +a 1288 1289 2818 +a 1288 4106 1 +a 1289 1290 2817 +a 1289 4106 1 +a 1290 1291 2816 +a 1290 4106 1 +a 1291 1292 2815 +a 1291 4106 1 +a 1292 1293 2814 +a 1292 4106 1 +a 1293 1294 2813 +a 1293 4106 1 +a 1294 1295 2812 +a 1294 4106 1 +a 1295 1296 2811 +a 1295 4106 1 +a 1296 1297 2810 +a 1296 4106 1 +a 1297 1298 2809 +a 1297 4106 1 +a 1298 1299 2808 +a 1298 4106 1 +a 1299 1300 2807 +a 1299 4106 1 +a 1300 1301 2806 +a 1300 4106 1 +a 1301 1302 2805 +a 1301 4106 1 +a 1302 1303 2804 +a 1302 4106 1 +a 1303 1304 2803 +a 1303 4106 1 +a 1304 1305 2802 +a 1304 4106 1 +a 1305 1306 2801 +a 1305 4106 1 +a 1306 1307 2800 +a 1306 4106 1 +a 1307 1308 2799 +a 1307 4106 1 +a 1308 1309 2798 +a 1308 4106 1 +a 1309 1310 2797 +a 1309 4106 1 +a 1310 1311 2796 +a 1310 4106 1 +a 1311 1312 2795 +a 1311 4106 1 +a 1312 1313 2794 +a 1312 4106 1 +a 1313 1314 2793 +a 1313 4106 1 +a 1314 1315 2792 +a 1314 4106 1 +a 1315 1316 2791 +a 1315 4106 1 +a 1316 1317 2790 +a 1316 4106 1 +a 1317 1318 2789 +a 1317 4106 1 +a 1318 1319 2788 +a 1318 4106 1 +a 1319 1320 2787 +a 1319 4106 1 +a 1320 1321 2786 +a 1320 4106 1 +a 1321 1322 2785 +a 1321 4106 1 +a 1322 1323 2784 +a 1322 4106 1 +a 1323 1324 2783 +a 1323 4106 1 +a 1324 1325 2782 +a 1324 4106 1 +a 1325 1326 2781 +a 1325 4106 1 +a 1326 1327 2780 +a 1326 4106 1 +a 1327 1328 2779 +a 1327 4106 1 +a 1328 1329 2778 +a 1328 4106 1 +a 1329 1330 2777 +a 1329 4106 1 +a 1330 1331 2776 +a 1330 4106 1 +a 1331 1332 2775 +a 1331 4106 1 +a 1332 1333 2774 +a 1332 4106 1 +a 1333 1334 2773 +a 1333 4106 1 +a 1334 1335 2772 +a 1334 4106 1 +a 1335 1336 2771 +a 1335 4106 1 +a 1336 1337 2770 +a 1336 4106 1 +a 1337 1338 2769 +a 1337 4106 1 +a 1338 1339 2768 +a 1338 4106 1 +a 1339 1340 2767 +a 1339 4106 1 +a 1340 1341 2766 +a 1340 4106 1 +a 1341 1342 2765 +a 1341 4106 1 +a 1342 1343 2764 +a 1342 4106 1 +a 1343 1344 2763 +a 1343 4106 1 +a 1344 1345 2762 +a 1344 4106 1 +a 1345 1346 2761 +a 1345 4106 1 +a 1346 1347 2760 +a 1346 4106 1 +a 1347 1348 2759 +a 1347 4106 1 +a 1348 1349 2758 +a 1348 4106 1 +a 1349 1350 2757 +a 1349 4106 1 +a 1350 1351 2756 +a 1350 4106 1 +a 1351 1352 2755 +a 1351 4106 1 +a 1352 1353 2754 +a 1352 4106 1 +a 1353 1354 2753 +a 1353 4106 1 +a 1354 1355 2752 +a 1354 4106 1 +a 1355 1356 2751 +a 1355 4106 1 +a 1356 1357 2750 +a 1356 4106 1 +a 1357 1358 2749 +a 1357 4106 1 +a 1358 1359 2748 +a 1358 4106 1 +a 1359 1360 2747 +a 1359 4106 1 +a 1360 1361 2746 +a 1360 4106 1 +a 1361 1362 2745 +a 1361 4106 1 +a 1362 1363 2744 +a 1362 4106 1 +a 1363 1364 2743 +a 1363 4106 1 +a 1364 1365 2742 +a 1364 4106 1 +a 1365 1366 2741 +a 1365 4106 1 +a 1366 1367 2740 +a 1366 4106 1 +a 1367 1368 2739 +a 1367 4106 1 +a 1368 1369 2738 +a 1368 4106 1 +a 1369 1370 2737 +a 1369 4106 1 +a 1370 1371 2736 +a 1370 4106 1 +a 1371 1372 2735 +a 1371 4106 1 +a 1372 1373 2734 +a 1372 4106 1 +a 1373 1374 2733 +a 1373 4106 1 +a 1374 1375 2732 +a 1374 4106 1 +a 1375 1376 2731 +a 1375 4106 1 +a 1376 1377 2730 +a 1376 4106 1 +a 1377 1378 2729 +a 1377 4106 1 +a 1378 1379 2728 +a 1378 4106 1 +a 1379 1380 2727 +a 1379 4106 1 +a 1380 1381 2726 +a 1380 4106 1 +a 1381 1382 2725 +a 1381 4106 1 +a 1382 1383 2724 +a 1382 4106 1 +a 1383 1384 2723 +a 1383 4106 1 +a 1384 1385 2722 +a 1384 4106 1 +a 1385 1386 2721 +a 1385 4106 1 +a 1386 1387 2720 +a 1386 4106 1 +a 1387 1388 2719 +a 1387 4106 1 +a 1388 1389 2718 +a 1388 4106 1 +a 1389 1390 2717 +a 1389 4106 1 +a 1390 1391 2716 +a 1390 4106 1 +a 1391 1392 2715 +a 1391 4106 1 +a 1392 1393 2714 +a 1392 4106 1 +a 1393 1394 2713 +a 1393 4106 1 +a 1394 1395 2712 +a 1394 4106 1 +a 1395 1396 2711 +a 1395 4106 1 +a 1396 1397 2710 +a 1396 4106 1 +a 1397 1398 2709 +a 1397 4106 1 +a 1398 1399 2708 +a 1398 4106 1 +a 1399 1400 2707 +a 1399 4106 1 +a 1400 1401 2706 +a 1400 4106 1 +a 1401 1402 2705 +a 1401 4106 1 +a 1402 1403 2704 +a 1402 4106 1 +a 1403 1404 2703 +a 1403 4106 1 +a 1404 1405 2702 +a 1404 4106 1 +a 1405 1406 2701 +a 1405 4106 1 +a 1406 1407 2700 +a 1406 4106 1 +a 1407 1408 2699 +a 1407 4106 1 +a 1408 1409 2698 +a 1408 4106 1 +a 1409 1410 2697 +a 1409 4106 1 +a 1410 1411 2696 +a 1410 4106 1 +a 1411 1412 2695 +a 1411 4106 1 +a 1412 1413 2694 +a 1412 4106 1 +a 1413 1414 2693 +a 1413 4106 1 +a 1414 1415 2692 +a 1414 4106 1 +a 1415 1416 2691 +a 1415 4106 1 +a 1416 1417 2690 +a 1416 4106 1 +a 1417 1418 2689 +a 1417 4106 1 +a 1418 1419 2688 +a 1418 4106 1 +a 1419 1420 2687 +a 1419 4106 1 +a 1420 1421 2686 +a 1420 4106 1 +a 1421 1422 2685 +a 1421 4106 1 +a 1422 1423 2684 +a 1422 4106 1 +a 1423 1424 2683 +a 1423 4106 1 +a 1424 1425 2682 +a 1424 4106 1 +a 1425 1426 2681 +a 1425 4106 1 +a 1426 1427 2680 +a 1426 4106 1 +a 1427 1428 2679 +a 1427 4106 1 +a 1428 1429 2678 +a 1428 4106 1 +a 1429 1430 2677 +a 1429 4106 1 +a 1430 1431 2676 +a 1430 4106 1 +a 1431 1432 2675 +a 1431 4106 1 +a 1432 1433 2674 +a 1432 4106 1 +a 1433 1434 2673 +a 1433 4106 1 +a 1434 1435 2672 +a 1434 4106 1 +a 1435 1436 2671 +a 1435 4106 1 +a 1436 1437 2670 +a 1436 4106 1 +a 1437 1438 2669 +a 1437 4106 1 +a 1438 1439 2668 +a 1438 4106 1 +a 1439 1440 2667 +a 1439 4106 1 +a 1440 1441 2666 +a 1440 4106 1 +a 1441 1442 2665 +a 1441 4106 1 +a 1442 1443 2664 +a 1442 4106 1 +a 1443 1444 2663 +a 1443 4106 1 +a 1444 1445 2662 +a 1444 4106 1 +a 1445 1446 2661 +a 1445 4106 1 +a 1446 1447 2660 +a 1446 4106 1 +a 1447 1448 2659 +a 1447 4106 1 +a 1448 1449 2658 +a 1448 4106 1 +a 1449 1450 2657 +a 1449 4106 1 +a 1450 1451 2656 +a 1450 4106 1 +a 1451 1452 2655 +a 1451 4106 1 +a 1452 1453 2654 +a 1452 4106 1 +a 1453 1454 2653 +a 1453 4106 1 +a 1454 1455 2652 +a 1454 4106 1 +a 1455 1456 2651 +a 1455 4106 1 +a 1456 1457 2650 +a 1456 4106 1 +a 1457 1458 2649 +a 1457 4106 1 +a 1458 1459 2648 +a 1458 4106 1 +a 1459 1460 2647 +a 1459 4106 1 +a 1460 1461 2646 +a 1460 4106 1 +a 1461 1462 2645 +a 1461 4106 1 +a 1462 1463 2644 +a 1462 4106 1 +a 1463 1464 2643 +a 1463 4106 1 +a 1464 1465 2642 +a 1464 4106 1 +a 1465 1466 2641 +a 1465 4106 1 +a 1466 1467 2640 +a 1466 4106 1 +a 1467 1468 2639 +a 1467 4106 1 +a 1468 1469 2638 +a 1468 4106 1 +a 1469 1470 2637 +a 1469 4106 1 +a 1470 1471 2636 +a 1470 4106 1 +a 1471 1472 2635 +a 1471 4106 1 +a 1472 1473 2634 +a 1472 4106 1 +a 1473 1474 2633 +a 1473 4106 1 +a 1474 1475 2632 +a 1474 4106 1 +a 1475 1476 2631 +a 1475 4106 1 +a 1476 1477 2630 +a 1476 4106 1 +a 1477 1478 2629 +a 1477 4106 1 +a 1478 1479 2628 +a 1478 4106 1 +a 1479 1480 2627 +a 1479 4106 1 +a 1480 1481 2626 +a 1480 4106 1 +a 1481 1482 2625 +a 1481 4106 1 +a 1482 1483 2624 +a 1482 4106 1 +a 1483 1484 2623 +a 1483 4106 1 +a 1484 1485 2622 +a 1484 4106 1 +a 1485 1486 2621 +a 1485 4106 1 +a 1486 1487 2620 +a 1486 4106 1 +a 1487 1488 2619 +a 1487 4106 1 +a 1488 1489 2618 +a 1488 4106 1 +a 1489 1490 2617 +a 1489 4106 1 +a 1490 1491 2616 +a 1490 4106 1 +a 1491 1492 2615 +a 1491 4106 1 +a 1492 1493 2614 +a 1492 4106 1 +a 1493 1494 2613 +a 1493 4106 1 +a 1494 1495 2612 +a 1494 4106 1 +a 1495 1496 2611 +a 1495 4106 1 +a 1496 1497 2610 +a 1496 4106 1 +a 1497 1498 2609 +a 1497 4106 1 +a 1498 1499 2608 +a 1498 4106 1 +a 1499 1500 2607 +a 1499 4106 1 +a 1500 1501 2606 +a 1500 4106 1 +a 1501 1502 2605 +a 1501 4106 1 +a 1502 1503 2604 +a 1502 4106 1 +a 1503 1504 2603 +a 1503 4106 1 +a 1504 1505 2602 +a 1504 4106 1 +a 1505 1506 2601 +a 1505 4106 1 +a 1506 1507 2600 +a 1506 4106 1 +a 1507 1508 2599 +a 1507 4106 1 +a 1508 1509 2598 +a 1508 4106 1 +a 1509 1510 2597 +a 1509 4106 1 +a 1510 1511 2596 +a 1510 4106 1 +a 1511 1512 2595 +a 1511 4106 1 +a 1512 1513 2594 +a 1512 4106 1 +a 1513 1514 2593 +a 1513 4106 1 +a 1514 1515 2592 +a 1514 4106 1 +a 1515 1516 2591 +a 1515 4106 1 +a 1516 1517 2590 +a 1516 4106 1 +a 1517 1518 2589 +a 1517 4106 1 +a 1518 1519 2588 +a 1518 4106 1 +a 1519 1520 2587 +a 1519 4106 1 +a 1520 1521 2586 +a 1520 4106 1 +a 1521 1522 2585 +a 1521 4106 1 +a 1522 1523 2584 +a 1522 4106 1 +a 1523 1524 2583 +a 1523 4106 1 +a 1524 1525 2582 +a 1524 4106 1 +a 1525 1526 2581 +a 1525 4106 1 +a 1526 1527 2580 +a 1526 4106 1 +a 1527 1528 2579 +a 1527 4106 1 +a 1528 1529 2578 +a 1528 4106 1 +a 1529 1530 2577 +a 1529 4106 1 +a 1530 1531 2576 +a 1530 4106 1 +a 1531 1532 2575 +a 1531 4106 1 +a 1532 1533 2574 +a 1532 4106 1 +a 1533 1534 2573 +a 1533 4106 1 +a 1534 1535 2572 +a 1534 4106 1 +a 1535 1536 2571 +a 1535 4106 1 +a 1536 1537 2570 +a 1536 4106 1 +a 1537 1538 2569 +a 1537 4106 1 +a 1538 1539 2568 +a 1538 4106 1 +a 1539 1540 2567 +a 1539 4106 1 +a 1540 1541 2566 +a 1540 4106 1 +a 1541 1542 2565 +a 1541 4106 1 +a 1542 1543 2564 +a 1542 4106 1 +a 1543 1544 2563 +a 1543 4106 1 +a 1544 1545 2562 +a 1544 4106 1 +a 1545 1546 2561 +a 1545 4106 1 +a 1546 1547 2560 +a 1546 4106 1 +a 1547 1548 2559 +a 1547 4106 1 +a 1548 1549 2558 +a 1548 4106 1 +a 1549 1550 2557 +a 1549 4106 1 +a 1550 1551 2556 +a 1550 4106 1 +a 1551 1552 2555 +a 1551 4106 1 +a 1552 1553 2554 +a 1552 4106 1 +a 1553 1554 2553 +a 1553 4106 1 +a 1554 1555 2552 +a 1554 4106 1 +a 1555 1556 2551 +a 1555 4106 1 +a 1556 1557 2550 +a 1556 4106 1 +a 1557 1558 2549 +a 1557 4106 1 +a 1558 1559 2548 +a 1558 4106 1 +a 1559 1560 2547 +a 1559 4106 1 +a 1560 1561 2546 +a 1560 4106 1 +a 1561 1562 2545 +a 1561 4106 1 +a 1562 1563 2544 +a 1562 4106 1 +a 1563 1564 2543 +a 1563 4106 1 +a 1564 1565 2542 +a 1564 4106 1 +a 1565 1566 2541 +a 1565 4106 1 +a 1566 1567 2540 +a 1566 4106 1 +a 1567 1568 2539 +a 1567 4106 1 +a 1568 1569 2538 +a 1568 4106 1 +a 1569 1570 2537 +a 1569 4106 1 +a 1570 1571 2536 +a 1570 4106 1 +a 1571 1572 2535 +a 1571 4106 1 +a 1572 1573 2534 +a 1572 4106 1 +a 1573 1574 2533 +a 1573 4106 1 +a 1574 1575 2532 +a 1574 4106 1 +a 1575 1576 2531 +a 1575 4106 1 +a 1576 1577 2530 +a 1576 4106 1 +a 1577 1578 2529 +a 1577 4106 1 +a 1578 1579 2528 +a 1578 4106 1 +a 1579 1580 2527 +a 1579 4106 1 +a 1580 1581 2526 +a 1580 4106 1 +a 1581 1582 2525 +a 1581 4106 1 +a 1582 1583 2524 +a 1582 4106 1 +a 1583 1584 2523 +a 1583 4106 1 +a 1584 1585 2522 +a 1584 4106 1 +a 1585 1586 2521 +a 1585 4106 1 +a 1586 1587 2520 +a 1586 4106 1 +a 1587 1588 2519 +a 1587 4106 1 +a 1588 1589 2518 +a 1588 4106 1 +a 1589 1590 2517 +a 1589 4106 1 +a 1590 1591 2516 +a 1590 4106 1 +a 1591 1592 2515 +a 1591 4106 1 +a 1592 1593 2514 +a 1592 4106 1 +a 1593 1594 2513 +a 1593 4106 1 +a 1594 1595 2512 +a 1594 4106 1 +a 1595 1596 2511 +a 1595 4106 1 +a 1596 1597 2510 +a 1596 4106 1 +a 1597 1598 2509 +a 1597 4106 1 +a 1598 1599 2508 +a 1598 4106 1 +a 1599 1600 2507 +a 1599 4106 1 +a 1600 1601 2506 +a 1600 4106 1 +a 1601 1602 2505 +a 1601 4106 1 +a 1602 1603 2504 +a 1602 4106 1 +a 1603 1604 2503 +a 1603 4106 1 +a 1604 1605 2502 +a 1604 4106 1 +a 1605 1606 2501 +a 1605 4106 1 +a 1606 1607 2500 +a 1606 4106 1 +a 1607 1608 2499 +a 1607 4106 1 +a 1608 1609 2498 +a 1608 4106 1 +a 1609 1610 2497 +a 1609 4106 1 +a 1610 1611 2496 +a 1610 4106 1 +a 1611 1612 2495 +a 1611 4106 1 +a 1612 1613 2494 +a 1612 4106 1 +a 1613 1614 2493 +a 1613 4106 1 +a 1614 1615 2492 +a 1614 4106 1 +a 1615 1616 2491 +a 1615 4106 1 +a 1616 1617 2490 +a 1616 4106 1 +a 1617 1618 2489 +a 1617 4106 1 +a 1618 1619 2488 +a 1618 4106 1 +a 1619 1620 2487 +a 1619 4106 1 +a 1620 1621 2486 +a 1620 4106 1 +a 1621 1622 2485 +a 1621 4106 1 +a 1622 1623 2484 +a 1622 4106 1 +a 1623 1624 2483 +a 1623 4106 1 +a 1624 1625 2482 +a 1624 4106 1 +a 1625 1626 2481 +a 1625 4106 1 +a 1626 1627 2480 +a 1626 4106 1 +a 1627 1628 2479 +a 1627 4106 1 +a 1628 1629 2478 +a 1628 4106 1 +a 1629 1630 2477 +a 1629 4106 1 +a 1630 1631 2476 +a 1630 4106 1 +a 1631 1632 2475 +a 1631 4106 1 +a 1632 1633 2474 +a 1632 4106 1 +a 1633 1634 2473 +a 1633 4106 1 +a 1634 1635 2472 +a 1634 4106 1 +a 1635 1636 2471 +a 1635 4106 1 +a 1636 1637 2470 +a 1636 4106 1 +a 1637 1638 2469 +a 1637 4106 1 +a 1638 1639 2468 +a 1638 4106 1 +a 1639 1640 2467 +a 1639 4106 1 +a 1640 1641 2466 +a 1640 4106 1 +a 1641 1642 2465 +a 1641 4106 1 +a 1642 1643 2464 +a 1642 4106 1 +a 1643 1644 2463 +a 1643 4106 1 +a 1644 1645 2462 +a 1644 4106 1 +a 1645 1646 2461 +a 1645 4106 1 +a 1646 1647 2460 +a 1646 4106 1 +a 1647 1648 2459 +a 1647 4106 1 +a 1648 1649 2458 +a 1648 4106 1 +a 1649 1650 2457 +a 1649 4106 1 +a 1650 1651 2456 +a 1650 4106 1 +a 1651 1652 2455 +a 1651 4106 1 +a 1652 1653 2454 +a 1652 4106 1 +a 1653 1654 2453 +a 1653 4106 1 +a 1654 1655 2452 +a 1654 4106 1 +a 1655 1656 2451 +a 1655 4106 1 +a 1656 1657 2450 +a 1656 4106 1 +a 1657 1658 2449 +a 1657 4106 1 +a 1658 1659 2448 +a 1658 4106 1 +a 1659 1660 2447 +a 1659 4106 1 +a 1660 1661 2446 +a 1660 4106 1 +a 1661 1662 2445 +a 1661 4106 1 +a 1662 1663 2444 +a 1662 4106 1 +a 1663 1664 2443 +a 1663 4106 1 +a 1664 1665 2442 +a 1664 4106 1 +a 1665 1666 2441 +a 1665 4106 1 +a 1666 1667 2440 +a 1666 4106 1 +a 1667 1668 2439 +a 1667 4106 1 +a 1668 1669 2438 +a 1668 4106 1 +a 1669 1670 2437 +a 1669 4106 1 +a 1670 1671 2436 +a 1670 4106 1 +a 1671 1672 2435 +a 1671 4106 1 +a 1672 1673 2434 +a 1672 4106 1 +a 1673 1674 2433 +a 1673 4106 1 +a 1674 1675 2432 +a 1674 4106 1 +a 1675 1676 2431 +a 1675 4106 1 +a 1676 1677 2430 +a 1676 4106 1 +a 1677 1678 2429 +a 1677 4106 1 +a 1678 1679 2428 +a 1678 4106 1 +a 1679 1680 2427 +a 1679 4106 1 +a 1680 1681 2426 +a 1680 4106 1 +a 1681 1682 2425 +a 1681 4106 1 +a 1682 1683 2424 +a 1682 4106 1 +a 1683 1684 2423 +a 1683 4106 1 +a 1684 1685 2422 +a 1684 4106 1 +a 1685 1686 2421 +a 1685 4106 1 +a 1686 1687 2420 +a 1686 4106 1 +a 1687 1688 2419 +a 1687 4106 1 +a 1688 1689 2418 +a 1688 4106 1 +a 1689 1690 2417 +a 1689 4106 1 +a 1690 1691 2416 +a 1690 4106 1 +a 1691 1692 2415 +a 1691 4106 1 +a 1692 1693 2414 +a 1692 4106 1 +a 1693 1694 2413 +a 1693 4106 1 +a 1694 1695 2412 +a 1694 4106 1 +a 1695 1696 2411 +a 1695 4106 1 +a 1696 1697 2410 +a 1696 4106 1 +a 1697 1698 2409 +a 1697 4106 1 +a 1698 1699 2408 +a 1698 4106 1 +a 1699 1700 2407 +a 1699 4106 1 +a 1700 1701 2406 +a 1700 4106 1 +a 1701 1702 2405 +a 1701 4106 1 +a 1702 1703 2404 +a 1702 4106 1 +a 1703 1704 2403 +a 1703 4106 1 +a 1704 1705 2402 +a 1704 4106 1 +a 1705 1706 2401 +a 1705 4106 1 +a 1706 1707 2400 +a 1706 4106 1 +a 1707 1708 2399 +a 1707 4106 1 +a 1708 1709 2398 +a 1708 4106 1 +a 1709 1710 2397 +a 1709 4106 1 +a 1710 1711 2396 +a 1710 4106 1 +a 1711 1712 2395 +a 1711 4106 1 +a 1712 1713 2394 +a 1712 4106 1 +a 1713 1714 2393 +a 1713 4106 1 +a 1714 1715 2392 +a 1714 4106 1 +a 1715 1716 2391 +a 1715 4106 1 +a 1716 1717 2390 +a 1716 4106 1 +a 1717 1718 2389 +a 1717 4106 1 +a 1718 1719 2388 +a 1718 4106 1 +a 1719 1720 2387 +a 1719 4106 1 +a 1720 1721 2386 +a 1720 4106 1 +a 1721 1722 2385 +a 1721 4106 1 +a 1722 1723 2384 +a 1722 4106 1 +a 1723 1724 2383 +a 1723 4106 1 +a 1724 1725 2382 +a 1724 4106 1 +a 1725 1726 2381 +a 1725 4106 1 +a 1726 1727 2380 +a 1726 4106 1 +a 1727 1728 2379 +a 1727 4106 1 +a 1728 1729 2378 +a 1728 4106 1 +a 1729 1730 2377 +a 1729 4106 1 +a 1730 1731 2376 +a 1730 4106 1 +a 1731 1732 2375 +a 1731 4106 1 +a 1732 1733 2374 +a 1732 4106 1 +a 1733 1734 2373 +a 1733 4106 1 +a 1734 1735 2372 +a 1734 4106 1 +a 1735 1736 2371 +a 1735 4106 1 +a 1736 1737 2370 +a 1736 4106 1 +a 1737 1738 2369 +a 1737 4106 1 +a 1738 1739 2368 +a 1738 4106 1 +a 1739 1740 2367 +a 1739 4106 1 +a 1740 1741 2366 +a 1740 4106 1 +a 1741 1742 2365 +a 1741 4106 1 +a 1742 1743 2364 +a 1742 4106 1 +a 1743 1744 2363 +a 1743 4106 1 +a 1744 1745 2362 +a 1744 4106 1 +a 1745 1746 2361 +a 1745 4106 1 +a 1746 1747 2360 +a 1746 4106 1 +a 1747 1748 2359 +a 1747 4106 1 +a 1748 1749 2358 +a 1748 4106 1 +a 1749 1750 2357 +a 1749 4106 1 +a 1750 1751 2356 +a 1750 4106 1 +a 1751 1752 2355 +a 1751 4106 1 +a 1752 1753 2354 +a 1752 4106 1 +a 1753 1754 2353 +a 1753 4106 1 +a 1754 1755 2352 +a 1754 4106 1 +a 1755 1756 2351 +a 1755 4106 1 +a 1756 1757 2350 +a 1756 4106 1 +a 1757 1758 2349 +a 1757 4106 1 +a 1758 1759 2348 +a 1758 4106 1 +a 1759 1760 2347 +a 1759 4106 1 +a 1760 1761 2346 +a 1760 4106 1 +a 1761 1762 2345 +a 1761 4106 1 +a 1762 1763 2344 +a 1762 4106 1 +a 1763 1764 2343 +a 1763 4106 1 +a 1764 1765 2342 +a 1764 4106 1 +a 1765 1766 2341 +a 1765 4106 1 +a 1766 1767 2340 +a 1766 4106 1 +a 1767 1768 2339 +a 1767 4106 1 +a 1768 1769 2338 +a 1768 4106 1 +a 1769 1770 2337 +a 1769 4106 1 +a 1770 1771 2336 +a 1770 4106 1 +a 1771 1772 2335 +a 1771 4106 1 +a 1772 1773 2334 +a 1772 4106 1 +a 1773 1774 2333 +a 1773 4106 1 +a 1774 1775 2332 +a 1774 4106 1 +a 1775 1776 2331 +a 1775 4106 1 +a 1776 1777 2330 +a 1776 4106 1 +a 1777 1778 2329 +a 1777 4106 1 +a 1778 1779 2328 +a 1778 4106 1 +a 1779 1780 2327 +a 1779 4106 1 +a 1780 1781 2326 +a 1780 4106 1 +a 1781 1782 2325 +a 1781 4106 1 +a 1782 1783 2324 +a 1782 4106 1 +a 1783 1784 2323 +a 1783 4106 1 +a 1784 1785 2322 +a 1784 4106 1 +a 1785 1786 2321 +a 1785 4106 1 +a 1786 1787 2320 +a 1786 4106 1 +a 1787 1788 2319 +a 1787 4106 1 +a 1788 1789 2318 +a 1788 4106 1 +a 1789 1790 2317 +a 1789 4106 1 +a 1790 1791 2316 +a 1790 4106 1 +a 1791 1792 2315 +a 1791 4106 1 +a 1792 1793 2314 +a 1792 4106 1 +a 1793 1794 2313 +a 1793 4106 1 +a 1794 1795 2312 +a 1794 4106 1 +a 1795 1796 2311 +a 1795 4106 1 +a 1796 1797 2310 +a 1796 4106 1 +a 1797 1798 2309 +a 1797 4106 1 +a 1798 1799 2308 +a 1798 4106 1 +a 1799 1800 2307 +a 1799 4106 1 +a 1800 1801 2306 +a 1800 4106 1 +a 1801 1802 2305 +a 1801 4106 1 +a 1802 1803 2304 +a 1802 4106 1 +a 1803 1804 2303 +a 1803 4106 1 +a 1804 1805 2302 +a 1804 4106 1 +a 1805 1806 2301 +a 1805 4106 1 +a 1806 1807 2300 +a 1806 4106 1 +a 1807 1808 2299 +a 1807 4106 1 +a 1808 1809 2298 +a 1808 4106 1 +a 1809 1810 2297 +a 1809 4106 1 +a 1810 1811 2296 +a 1810 4106 1 +a 1811 1812 2295 +a 1811 4106 1 +a 1812 1813 2294 +a 1812 4106 1 +a 1813 1814 2293 +a 1813 4106 1 +a 1814 1815 2292 +a 1814 4106 1 +a 1815 1816 2291 +a 1815 4106 1 +a 1816 1817 2290 +a 1816 4106 1 +a 1817 1818 2289 +a 1817 4106 1 +a 1818 1819 2288 +a 1818 4106 1 +a 1819 1820 2287 +a 1819 4106 1 +a 1820 1821 2286 +a 1820 4106 1 +a 1821 1822 2285 +a 1821 4106 1 +a 1822 1823 2284 +a 1822 4106 1 +a 1823 1824 2283 +a 1823 4106 1 +a 1824 1825 2282 +a 1824 4106 1 +a 1825 1826 2281 +a 1825 4106 1 +a 1826 1827 2280 +a 1826 4106 1 +a 1827 1828 2279 +a 1827 4106 1 +a 1828 1829 2278 +a 1828 4106 1 +a 1829 1830 2277 +a 1829 4106 1 +a 1830 1831 2276 +a 1830 4106 1 +a 1831 1832 2275 +a 1831 4106 1 +a 1832 1833 2274 +a 1832 4106 1 +a 1833 1834 2273 +a 1833 4106 1 +a 1834 1835 2272 +a 1834 4106 1 +a 1835 1836 2271 +a 1835 4106 1 +a 1836 1837 2270 +a 1836 4106 1 +a 1837 1838 2269 +a 1837 4106 1 +a 1838 1839 2268 +a 1838 4106 1 +a 1839 1840 2267 +a 1839 4106 1 +a 1840 1841 2266 +a 1840 4106 1 +a 1841 1842 2265 +a 1841 4106 1 +a 1842 1843 2264 +a 1842 4106 1 +a 1843 1844 2263 +a 1843 4106 1 +a 1844 1845 2262 +a 1844 4106 1 +a 1845 1846 2261 +a 1845 4106 1 +a 1846 1847 2260 +a 1846 4106 1 +a 1847 1848 2259 +a 1847 4106 1 +a 1848 1849 2258 +a 1848 4106 1 +a 1849 1850 2257 +a 1849 4106 1 +a 1850 1851 2256 +a 1850 4106 1 +a 1851 1852 2255 +a 1851 4106 1 +a 1852 1853 2254 +a 1852 4106 1 +a 1853 1854 2253 +a 1853 4106 1 +a 1854 1855 2252 +a 1854 4106 1 +a 1855 1856 2251 +a 1855 4106 1 +a 1856 1857 2250 +a 1856 4106 1 +a 1857 1858 2249 +a 1857 4106 1 +a 1858 1859 2248 +a 1858 4106 1 +a 1859 1860 2247 +a 1859 4106 1 +a 1860 1861 2246 +a 1860 4106 1 +a 1861 1862 2245 +a 1861 4106 1 +a 1862 1863 2244 +a 1862 4106 1 +a 1863 1864 2243 +a 1863 4106 1 +a 1864 1865 2242 +a 1864 4106 1 +a 1865 1866 2241 +a 1865 4106 1 +a 1866 1867 2240 +a 1866 4106 1 +a 1867 1868 2239 +a 1867 4106 1 +a 1868 1869 2238 +a 1868 4106 1 +a 1869 1870 2237 +a 1869 4106 1 +a 1870 1871 2236 +a 1870 4106 1 +a 1871 1872 2235 +a 1871 4106 1 +a 1872 1873 2234 +a 1872 4106 1 +a 1873 1874 2233 +a 1873 4106 1 +a 1874 1875 2232 +a 1874 4106 1 +a 1875 1876 2231 +a 1875 4106 1 +a 1876 1877 2230 +a 1876 4106 1 +a 1877 1878 2229 +a 1877 4106 1 +a 1878 1879 2228 +a 1878 4106 1 +a 1879 1880 2227 +a 1879 4106 1 +a 1880 1881 2226 +a 1880 4106 1 +a 1881 1882 2225 +a 1881 4106 1 +a 1882 1883 2224 +a 1882 4106 1 +a 1883 1884 2223 +a 1883 4106 1 +a 1884 1885 2222 +a 1884 4106 1 +a 1885 1886 2221 +a 1885 4106 1 +a 1886 1887 2220 +a 1886 4106 1 +a 1887 1888 2219 +a 1887 4106 1 +a 1888 1889 2218 +a 1888 4106 1 +a 1889 1890 2217 +a 1889 4106 1 +a 1890 1891 2216 +a 1890 4106 1 +a 1891 1892 2215 +a 1891 4106 1 +a 1892 1893 2214 +a 1892 4106 1 +a 1893 1894 2213 +a 1893 4106 1 +a 1894 1895 2212 +a 1894 4106 1 +a 1895 1896 2211 +a 1895 4106 1 +a 1896 1897 2210 +a 1896 4106 1 +a 1897 1898 2209 +a 1897 4106 1 +a 1898 1899 2208 +a 1898 4106 1 +a 1899 1900 2207 +a 1899 4106 1 +a 1900 1901 2206 +a 1900 4106 1 +a 1901 1902 2205 +a 1901 4106 1 +a 1902 1903 2204 +a 1902 4106 1 +a 1903 1904 2203 +a 1903 4106 1 +a 1904 1905 2202 +a 1904 4106 1 +a 1905 1906 2201 +a 1905 4106 1 +a 1906 1907 2200 +a 1906 4106 1 +a 1907 1908 2199 +a 1907 4106 1 +a 1908 1909 2198 +a 1908 4106 1 +a 1909 1910 2197 +a 1909 4106 1 +a 1910 1911 2196 +a 1910 4106 1 +a 1911 1912 2195 +a 1911 4106 1 +a 1912 1913 2194 +a 1912 4106 1 +a 1913 1914 2193 +a 1913 4106 1 +a 1914 1915 2192 +a 1914 4106 1 +a 1915 1916 2191 +a 1915 4106 1 +a 1916 1917 2190 +a 1916 4106 1 +a 1917 1918 2189 +a 1917 4106 1 +a 1918 1919 2188 +a 1918 4106 1 +a 1919 1920 2187 +a 1919 4106 1 +a 1920 1921 2186 +a 1920 4106 1 +a 1921 1922 2185 +a 1921 4106 1 +a 1922 1923 2184 +a 1922 4106 1 +a 1923 1924 2183 +a 1923 4106 1 +a 1924 1925 2182 +a 1924 4106 1 +a 1925 1926 2181 +a 1925 4106 1 +a 1926 1927 2180 +a 1926 4106 1 +a 1927 1928 2179 +a 1927 4106 1 +a 1928 1929 2178 +a 1928 4106 1 +a 1929 1930 2177 +a 1929 4106 1 +a 1930 1931 2176 +a 1930 4106 1 +a 1931 1932 2175 +a 1931 4106 1 +a 1932 1933 2174 +a 1932 4106 1 +a 1933 1934 2173 +a 1933 4106 1 +a 1934 1935 2172 +a 1934 4106 1 +a 1935 1936 2171 +a 1935 4106 1 +a 1936 1937 2170 +a 1936 4106 1 +a 1937 1938 2169 +a 1937 4106 1 +a 1938 1939 2168 +a 1938 4106 1 +a 1939 1940 2167 +a 1939 4106 1 +a 1940 1941 2166 +a 1940 4106 1 +a 1941 1942 2165 +a 1941 4106 1 +a 1942 1943 2164 +a 1942 4106 1 +a 1943 1944 2163 +a 1943 4106 1 +a 1944 1945 2162 +a 1944 4106 1 +a 1945 1946 2161 +a 1945 4106 1 +a 1946 1947 2160 +a 1946 4106 1 +a 1947 1948 2159 +a 1947 4106 1 +a 1948 1949 2158 +a 1948 4106 1 +a 1949 1950 2157 +a 1949 4106 1 +a 1950 1951 2156 +a 1950 4106 1 +a 1951 1952 2155 +a 1951 4106 1 +a 1952 1953 2154 +a 1952 4106 1 +a 1953 1954 2153 +a 1953 4106 1 +a 1954 1955 2152 +a 1954 4106 1 +a 1955 1956 2151 +a 1955 4106 1 +a 1956 1957 2150 +a 1956 4106 1 +a 1957 1958 2149 +a 1957 4106 1 +a 1958 1959 2148 +a 1958 4106 1 +a 1959 1960 2147 +a 1959 4106 1 +a 1960 1961 2146 +a 1960 4106 1 +a 1961 1962 2145 +a 1961 4106 1 +a 1962 1963 2144 +a 1962 4106 1 +a 1963 1964 2143 +a 1963 4106 1 +a 1964 1965 2142 +a 1964 4106 1 +a 1965 1966 2141 +a 1965 4106 1 +a 1966 1967 2140 +a 1966 4106 1 +a 1967 1968 2139 +a 1967 4106 1 +a 1968 1969 2138 +a 1968 4106 1 +a 1969 1970 2137 +a 1969 4106 1 +a 1970 1971 2136 +a 1970 4106 1 +a 1971 1972 2135 +a 1971 4106 1 +a 1972 1973 2134 +a 1972 4106 1 +a 1973 1974 2133 +a 1973 4106 1 +a 1974 1975 2132 +a 1974 4106 1 +a 1975 1976 2131 +a 1975 4106 1 +a 1976 1977 2130 +a 1976 4106 1 +a 1977 1978 2129 +a 1977 4106 1 +a 1978 1979 2128 +a 1978 4106 1 +a 1979 1980 2127 +a 1979 4106 1 +a 1980 1981 2126 +a 1980 4106 1 +a 1981 1982 2125 +a 1981 4106 1 +a 1982 1983 2124 +a 1982 4106 1 +a 1983 1984 2123 +a 1983 4106 1 +a 1984 1985 2122 +a 1984 4106 1 +a 1985 1986 2121 +a 1985 4106 1 +a 1986 1987 2120 +a 1986 4106 1 +a 1987 1988 2119 +a 1987 4106 1 +a 1988 1989 2118 +a 1988 4106 1 +a 1989 1990 2117 +a 1989 4106 1 +a 1990 1991 2116 +a 1990 4106 1 +a 1991 1992 2115 +a 1991 4106 1 +a 1992 1993 2114 +a 1992 4106 1 +a 1993 1994 2113 +a 1993 4106 1 +a 1994 1995 2112 +a 1994 4106 1 +a 1995 1996 2111 +a 1995 4106 1 +a 1996 1997 2110 +a 1996 4106 1 +a 1997 1998 2109 +a 1997 4106 1 +a 1998 1999 2108 +a 1998 4106 1 +a 1999 2000 2107 +a 1999 4106 1 +a 2000 2001 2106 +a 2000 4106 1 +a 2001 2002 2105 +a 2001 4106 1 +a 2002 2003 2104 +a 2002 4106 1 +a 2003 2004 2103 +a 2003 4106 1 +a 2004 2005 2102 +a 2004 4106 1 +a 2005 2006 2101 +a 2005 4106 1 +a 2006 2007 2100 +a 2006 4106 1 +a 2007 2008 2099 +a 2007 4106 1 +a 2008 2009 2098 +a 2008 4106 1 +a 2009 2010 2097 +a 2009 4106 1 +a 2010 2011 2096 +a 2010 4106 1 +a 2011 2012 2095 +a 2011 4106 1 +a 2012 2013 2094 +a 2012 4106 1 +a 2013 2014 2093 +a 2013 4106 1 +a 2014 2015 2092 +a 2014 4106 1 +a 2015 2016 2091 +a 2015 4106 1 +a 2016 2017 2090 +a 2016 4106 1 +a 2017 2018 2089 +a 2017 4106 1 +a 2018 2019 2088 +a 2018 4106 1 +a 2019 2020 2087 +a 2019 4106 1 +a 2020 2021 2086 +a 2020 4106 1 +a 2021 2022 2085 +a 2021 4106 1 +a 2022 2023 2084 +a 2022 4106 1 +a 2023 2024 2083 +a 2023 4106 1 +a 2024 2025 2082 +a 2024 4106 1 +a 2025 2026 2081 +a 2025 4106 1 +a 2026 2027 2080 +a 2026 4106 1 +a 2027 2028 2079 +a 2027 4106 1 +a 2028 2029 2078 +a 2028 4106 1 +a 2029 2030 2077 +a 2029 4106 1 +a 2030 2031 2076 +a 2030 4106 1 +a 2031 2032 2075 +a 2031 4106 1 +a 2032 2033 2074 +a 2032 4106 1 +a 2033 2034 2073 +a 2033 4106 1 +a 2034 2035 2072 +a 2034 4106 1 +a 2035 2036 2071 +a 2035 4106 1 +a 2036 2037 2070 +a 2036 4106 1 +a 2037 2038 2069 +a 2037 4106 1 +a 2038 2039 2068 +a 2038 4106 1 +a 2039 2040 2067 +a 2039 4106 1 +a 2040 2041 2066 +a 2040 4106 1 +a 2041 2042 2065 +a 2041 4106 1 +a 2042 2043 2064 +a 2042 4106 1 +a 2043 2044 2063 +a 2043 4106 1 +a 2044 2045 2062 +a 2044 4106 1 +a 2045 2046 2061 +a 2045 4106 1 +a 2046 2047 2060 +a 2046 4106 1 +a 2047 2048 2059 +a 2047 4106 1 +a 2048 2049 2058 +a 2048 4106 1 +a 2049 2050 2057 +a 2049 4106 1 +a 2050 2051 2056 +a 2050 4106 1 +a 2051 2052 2055 +a 2051 4106 1 +a 2052 2053 2054 +a 2052 4106 1 +a 2053 2054 2053 +a 2053 4106 1 +a 2054 2055 2052 +a 2054 4106 1 +a 2055 2056 2051 +a 2055 4106 1 +a 2056 2057 2050 +a 2056 4106 1 +a 2057 2058 2049 +a 2057 4106 1 +a 2058 2059 2048 +a 2058 4106 1 +a 2059 2060 2047 +a 2059 4106 1 +a 2060 2061 2046 +a 2060 4106 1 +a 2061 2062 2045 +a 2061 4106 1 +a 2062 2063 2044 +a 2062 4106 1 +a 2063 2064 2043 +a 2063 4106 1 +a 2064 2065 2042 +a 2064 4106 1 +a 2065 2066 2041 +a 2065 4106 1 +a 2066 2067 2040 +a 2066 4106 1 +a 2067 2068 2039 +a 2067 4106 1 +a 2068 2069 2038 +a 2068 4106 1 +a 2069 2070 2037 +a 2069 4106 1 +a 2070 2071 2036 +a 2070 4106 1 +a 2071 2072 2035 +a 2071 4106 1 +a 2072 2073 2034 +a 2072 4106 1 +a 2073 2074 2033 +a 2073 4106 1 +a 2074 2075 2032 +a 2074 4106 1 +a 2075 2076 2031 +a 2075 4106 1 +a 2076 2077 2030 +a 2076 4106 1 +a 2077 2078 2029 +a 2077 4106 1 +a 2078 2079 2028 +a 2078 4106 1 +a 2079 2080 2027 +a 2079 4106 1 +a 2080 2081 2026 +a 2080 4106 1 +a 2081 2082 2025 +a 2081 4106 1 +a 2082 2083 2024 +a 2082 4106 1 +a 2083 2084 2023 +a 2083 4106 1 +a 2084 2085 2022 +a 2084 4106 1 +a 2085 2086 2021 +a 2085 4106 1 +a 2086 2087 2020 +a 2086 4106 1 +a 2087 2088 2019 +a 2087 4106 1 +a 2088 2089 2018 +a 2088 4106 1 +a 2089 2090 2017 +a 2089 4106 1 +a 2090 2091 2016 +a 2090 4106 1 +a 2091 2092 2015 +a 2091 4106 1 +a 2092 2093 2014 +a 2092 4106 1 +a 2093 2094 2013 +a 2093 4106 1 +a 2094 2095 2012 +a 2094 4106 1 +a 2095 2096 2011 +a 2095 4106 1 +a 2096 2097 2010 +a 2096 4106 1 +a 2097 2098 2009 +a 2097 4106 1 +a 2098 2099 2008 +a 2098 4106 1 +a 2099 2100 2007 +a 2099 4106 1 +a 2100 2101 2006 +a 2100 4106 1 +a 2101 2102 2005 +a 2101 4106 1 +a 2102 2103 2004 +a 2102 4106 1 +a 2103 2104 2003 +a 2103 4106 1 +a 2104 2105 2002 +a 2104 4106 1 +a 2105 2106 2001 +a 2105 4106 1 +a 2106 2107 2000 +a 2106 4106 1 +a 2107 2108 1999 +a 2107 4106 1 +a 2108 2109 1998 +a 2108 4106 1 +a 2109 2110 1997 +a 2109 4106 1 +a 2110 2111 1996 +a 2110 4106 1 +a 2111 2112 1995 +a 2111 4106 1 +a 2112 2113 1994 +a 2112 4106 1 +a 2113 2114 1993 +a 2113 4106 1 +a 2114 2115 1992 +a 2114 4106 1 +a 2115 2116 1991 +a 2115 4106 1 +a 2116 2117 1990 +a 2116 4106 1 +a 2117 2118 1989 +a 2117 4106 1 +a 2118 2119 1988 +a 2118 4106 1 +a 2119 2120 1987 +a 2119 4106 1 +a 2120 2121 1986 +a 2120 4106 1 +a 2121 2122 1985 +a 2121 4106 1 +a 2122 2123 1984 +a 2122 4106 1 +a 2123 2124 1983 +a 2123 4106 1 +a 2124 2125 1982 +a 2124 4106 1 +a 2125 2126 1981 +a 2125 4106 1 +a 2126 2127 1980 +a 2126 4106 1 +a 2127 2128 1979 +a 2127 4106 1 +a 2128 2129 1978 +a 2128 4106 1 +a 2129 2130 1977 +a 2129 4106 1 +a 2130 2131 1976 +a 2130 4106 1 +a 2131 2132 1975 +a 2131 4106 1 +a 2132 2133 1974 +a 2132 4106 1 +a 2133 2134 1973 +a 2133 4106 1 +a 2134 2135 1972 +a 2134 4106 1 +a 2135 2136 1971 +a 2135 4106 1 +a 2136 2137 1970 +a 2136 4106 1 +a 2137 2138 1969 +a 2137 4106 1 +a 2138 2139 1968 +a 2138 4106 1 +a 2139 2140 1967 +a 2139 4106 1 +a 2140 2141 1966 +a 2140 4106 1 +a 2141 2142 1965 +a 2141 4106 1 +a 2142 2143 1964 +a 2142 4106 1 +a 2143 2144 1963 +a 2143 4106 1 +a 2144 2145 1962 +a 2144 4106 1 +a 2145 2146 1961 +a 2145 4106 1 +a 2146 2147 1960 +a 2146 4106 1 +a 2147 2148 1959 +a 2147 4106 1 +a 2148 2149 1958 +a 2148 4106 1 +a 2149 2150 1957 +a 2149 4106 1 +a 2150 2151 1956 +a 2150 4106 1 +a 2151 2152 1955 +a 2151 4106 1 +a 2152 2153 1954 +a 2152 4106 1 +a 2153 2154 1953 +a 2153 4106 1 +a 2154 2155 1952 +a 2154 4106 1 +a 2155 2156 1951 +a 2155 4106 1 +a 2156 2157 1950 +a 2156 4106 1 +a 2157 2158 1949 +a 2157 4106 1 +a 2158 2159 1948 +a 2158 4106 1 +a 2159 2160 1947 +a 2159 4106 1 +a 2160 2161 1946 +a 2160 4106 1 +a 2161 2162 1945 +a 2161 4106 1 +a 2162 2163 1944 +a 2162 4106 1 +a 2163 2164 1943 +a 2163 4106 1 +a 2164 2165 1942 +a 2164 4106 1 +a 2165 2166 1941 +a 2165 4106 1 +a 2166 2167 1940 +a 2166 4106 1 +a 2167 2168 1939 +a 2167 4106 1 +a 2168 2169 1938 +a 2168 4106 1 +a 2169 2170 1937 +a 2169 4106 1 +a 2170 2171 1936 +a 2170 4106 1 +a 2171 2172 1935 +a 2171 4106 1 +a 2172 2173 1934 +a 2172 4106 1 +a 2173 2174 1933 +a 2173 4106 1 +a 2174 2175 1932 +a 2174 4106 1 +a 2175 2176 1931 +a 2175 4106 1 +a 2176 2177 1930 +a 2176 4106 1 +a 2177 2178 1929 +a 2177 4106 1 +a 2178 2179 1928 +a 2178 4106 1 +a 2179 2180 1927 +a 2179 4106 1 +a 2180 2181 1926 +a 2180 4106 1 +a 2181 2182 1925 +a 2181 4106 1 +a 2182 2183 1924 +a 2182 4106 1 +a 2183 2184 1923 +a 2183 4106 1 +a 2184 2185 1922 +a 2184 4106 1 +a 2185 2186 1921 +a 2185 4106 1 +a 2186 2187 1920 +a 2186 4106 1 +a 2187 2188 1919 +a 2187 4106 1 +a 2188 2189 1918 +a 2188 4106 1 +a 2189 2190 1917 +a 2189 4106 1 +a 2190 2191 1916 +a 2190 4106 1 +a 2191 2192 1915 +a 2191 4106 1 +a 2192 2193 1914 +a 2192 4106 1 +a 2193 2194 1913 +a 2193 4106 1 +a 2194 2195 1912 +a 2194 4106 1 +a 2195 2196 1911 +a 2195 4106 1 +a 2196 2197 1910 +a 2196 4106 1 +a 2197 2198 1909 +a 2197 4106 1 +a 2198 2199 1908 +a 2198 4106 1 +a 2199 2200 1907 +a 2199 4106 1 +a 2200 2201 1906 +a 2200 4106 1 +a 2201 2202 1905 +a 2201 4106 1 +a 2202 2203 1904 +a 2202 4106 1 +a 2203 2204 1903 +a 2203 4106 1 +a 2204 2205 1902 +a 2204 4106 1 +a 2205 2206 1901 +a 2205 4106 1 +a 2206 2207 1900 +a 2206 4106 1 +a 2207 2208 1899 +a 2207 4106 1 +a 2208 2209 1898 +a 2208 4106 1 +a 2209 2210 1897 +a 2209 4106 1 +a 2210 2211 1896 +a 2210 4106 1 +a 2211 2212 1895 +a 2211 4106 1 +a 2212 2213 1894 +a 2212 4106 1 +a 2213 2214 1893 +a 2213 4106 1 +a 2214 2215 1892 +a 2214 4106 1 +a 2215 2216 1891 +a 2215 4106 1 +a 2216 2217 1890 +a 2216 4106 1 +a 2217 2218 1889 +a 2217 4106 1 +a 2218 2219 1888 +a 2218 4106 1 +a 2219 2220 1887 +a 2219 4106 1 +a 2220 2221 1886 +a 2220 4106 1 +a 2221 2222 1885 +a 2221 4106 1 +a 2222 2223 1884 +a 2222 4106 1 +a 2223 2224 1883 +a 2223 4106 1 +a 2224 2225 1882 +a 2224 4106 1 +a 2225 2226 1881 +a 2225 4106 1 +a 2226 2227 1880 +a 2226 4106 1 +a 2227 2228 1879 +a 2227 4106 1 +a 2228 2229 1878 +a 2228 4106 1 +a 2229 2230 1877 +a 2229 4106 1 +a 2230 2231 1876 +a 2230 4106 1 +a 2231 2232 1875 +a 2231 4106 1 +a 2232 2233 1874 +a 2232 4106 1 +a 2233 2234 1873 +a 2233 4106 1 +a 2234 2235 1872 +a 2234 4106 1 +a 2235 2236 1871 +a 2235 4106 1 +a 2236 2237 1870 +a 2236 4106 1 +a 2237 2238 1869 +a 2237 4106 1 +a 2238 2239 1868 +a 2238 4106 1 +a 2239 2240 1867 +a 2239 4106 1 +a 2240 2241 1866 +a 2240 4106 1 +a 2241 2242 1865 +a 2241 4106 1 +a 2242 2243 1864 +a 2242 4106 1 +a 2243 2244 1863 +a 2243 4106 1 +a 2244 2245 1862 +a 2244 4106 1 +a 2245 2246 1861 +a 2245 4106 1 +a 2246 2247 1860 +a 2246 4106 1 +a 2247 2248 1859 +a 2247 4106 1 +a 2248 2249 1858 +a 2248 4106 1 +a 2249 2250 1857 +a 2249 4106 1 +a 2250 2251 1856 +a 2250 4106 1 +a 2251 2252 1855 +a 2251 4106 1 +a 2252 2253 1854 +a 2252 4106 1 +a 2253 2254 1853 +a 2253 4106 1 +a 2254 2255 1852 +a 2254 4106 1 +a 2255 2256 1851 +a 2255 4106 1 +a 2256 2257 1850 +a 2256 4106 1 +a 2257 2258 1849 +a 2257 4106 1 +a 2258 2259 1848 +a 2258 4106 1 +a 2259 2260 1847 +a 2259 4106 1 +a 2260 2261 1846 +a 2260 4106 1 +a 2261 2262 1845 +a 2261 4106 1 +a 2262 2263 1844 +a 2262 4106 1 +a 2263 2264 1843 +a 2263 4106 1 +a 2264 2265 1842 +a 2264 4106 1 +a 2265 2266 1841 +a 2265 4106 1 +a 2266 2267 1840 +a 2266 4106 1 +a 2267 2268 1839 +a 2267 4106 1 +a 2268 2269 1838 +a 2268 4106 1 +a 2269 2270 1837 +a 2269 4106 1 +a 2270 2271 1836 +a 2270 4106 1 +a 2271 2272 1835 +a 2271 4106 1 +a 2272 2273 1834 +a 2272 4106 1 +a 2273 2274 1833 +a 2273 4106 1 +a 2274 2275 1832 +a 2274 4106 1 +a 2275 2276 1831 +a 2275 4106 1 +a 2276 2277 1830 +a 2276 4106 1 +a 2277 2278 1829 +a 2277 4106 1 +a 2278 2279 1828 +a 2278 4106 1 +a 2279 2280 1827 +a 2279 4106 1 +a 2280 2281 1826 +a 2280 4106 1 +a 2281 2282 1825 +a 2281 4106 1 +a 2282 2283 1824 +a 2282 4106 1 +a 2283 2284 1823 +a 2283 4106 1 +a 2284 2285 1822 +a 2284 4106 1 +a 2285 2286 1821 +a 2285 4106 1 +a 2286 2287 1820 +a 2286 4106 1 +a 2287 2288 1819 +a 2287 4106 1 +a 2288 2289 1818 +a 2288 4106 1 +a 2289 2290 1817 +a 2289 4106 1 +a 2290 2291 1816 +a 2290 4106 1 +a 2291 2292 1815 +a 2291 4106 1 +a 2292 2293 1814 +a 2292 4106 1 +a 2293 2294 1813 +a 2293 4106 1 +a 2294 2295 1812 +a 2294 4106 1 +a 2295 2296 1811 +a 2295 4106 1 +a 2296 2297 1810 +a 2296 4106 1 +a 2297 2298 1809 +a 2297 4106 1 +a 2298 2299 1808 +a 2298 4106 1 +a 2299 2300 1807 +a 2299 4106 1 +a 2300 2301 1806 +a 2300 4106 1 +a 2301 2302 1805 +a 2301 4106 1 +a 2302 2303 1804 +a 2302 4106 1 +a 2303 2304 1803 +a 2303 4106 1 +a 2304 2305 1802 +a 2304 4106 1 +a 2305 2306 1801 +a 2305 4106 1 +a 2306 2307 1800 +a 2306 4106 1 +a 2307 2308 1799 +a 2307 4106 1 +a 2308 2309 1798 +a 2308 4106 1 +a 2309 2310 1797 +a 2309 4106 1 +a 2310 2311 1796 +a 2310 4106 1 +a 2311 2312 1795 +a 2311 4106 1 +a 2312 2313 1794 +a 2312 4106 1 +a 2313 2314 1793 +a 2313 4106 1 +a 2314 2315 1792 +a 2314 4106 1 +a 2315 2316 1791 +a 2315 4106 1 +a 2316 2317 1790 +a 2316 4106 1 +a 2317 2318 1789 +a 2317 4106 1 +a 2318 2319 1788 +a 2318 4106 1 +a 2319 2320 1787 +a 2319 4106 1 +a 2320 2321 1786 +a 2320 4106 1 +a 2321 2322 1785 +a 2321 4106 1 +a 2322 2323 1784 +a 2322 4106 1 +a 2323 2324 1783 +a 2323 4106 1 +a 2324 2325 1782 +a 2324 4106 1 +a 2325 2326 1781 +a 2325 4106 1 +a 2326 2327 1780 +a 2326 4106 1 +a 2327 2328 1779 +a 2327 4106 1 +a 2328 2329 1778 +a 2328 4106 1 +a 2329 2330 1777 +a 2329 4106 1 +a 2330 2331 1776 +a 2330 4106 1 +a 2331 2332 1775 +a 2331 4106 1 +a 2332 2333 1774 +a 2332 4106 1 +a 2333 2334 1773 +a 2333 4106 1 +a 2334 2335 1772 +a 2334 4106 1 +a 2335 2336 1771 +a 2335 4106 1 +a 2336 2337 1770 +a 2336 4106 1 +a 2337 2338 1769 +a 2337 4106 1 +a 2338 2339 1768 +a 2338 4106 1 +a 2339 2340 1767 +a 2339 4106 1 +a 2340 2341 1766 +a 2340 4106 1 +a 2341 2342 1765 +a 2341 4106 1 +a 2342 2343 1764 +a 2342 4106 1 +a 2343 2344 1763 +a 2343 4106 1 +a 2344 2345 1762 +a 2344 4106 1 +a 2345 2346 1761 +a 2345 4106 1 +a 2346 2347 1760 +a 2346 4106 1 +a 2347 2348 1759 +a 2347 4106 1 +a 2348 2349 1758 +a 2348 4106 1 +a 2349 2350 1757 +a 2349 4106 1 +a 2350 2351 1756 +a 2350 4106 1 +a 2351 2352 1755 +a 2351 4106 1 +a 2352 2353 1754 +a 2352 4106 1 +a 2353 2354 1753 +a 2353 4106 1 +a 2354 2355 1752 +a 2354 4106 1 +a 2355 2356 1751 +a 2355 4106 1 +a 2356 2357 1750 +a 2356 4106 1 +a 2357 2358 1749 +a 2357 4106 1 +a 2358 2359 1748 +a 2358 4106 1 +a 2359 2360 1747 +a 2359 4106 1 +a 2360 2361 1746 +a 2360 4106 1 +a 2361 2362 1745 +a 2361 4106 1 +a 2362 2363 1744 +a 2362 4106 1 +a 2363 2364 1743 +a 2363 4106 1 +a 2364 2365 1742 +a 2364 4106 1 +a 2365 2366 1741 +a 2365 4106 1 +a 2366 2367 1740 +a 2366 4106 1 +a 2367 2368 1739 +a 2367 4106 1 +a 2368 2369 1738 +a 2368 4106 1 +a 2369 2370 1737 +a 2369 4106 1 +a 2370 2371 1736 +a 2370 4106 1 +a 2371 2372 1735 +a 2371 4106 1 +a 2372 2373 1734 +a 2372 4106 1 +a 2373 2374 1733 +a 2373 4106 1 +a 2374 2375 1732 +a 2374 4106 1 +a 2375 2376 1731 +a 2375 4106 1 +a 2376 2377 1730 +a 2376 4106 1 +a 2377 2378 1729 +a 2377 4106 1 +a 2378 2379 1728 +a 2378 4106 1 +a 2379 2380 1727 +a 2379 4106 1 +a 2380 2381 1726 +a 2380 4106 1 +a 2381 2382 1725 +a 2381 4106 1 +a 2382 2383 1724 +a 2382 4106 1 +a 2383 2384 1723 +a 2383 4106 1 +a 2384 2385 1722 +a 2384 4106 1 +a 2385 2386 1721 +a 2385 4106 1 +a 2386 2387 1720 +a 2386 4106 1 +a 2387 2388 1719 +a 2387 4106 1 +a 2388 2389 1718 +a 2388 4106 1 +a 2389 2390 1717 +a 2389 4106 1 +a 2390 2391 1716 +a 2390 4106 1 +a 2391 2392 1715 +a 2391 4106 1 +a 2392 2393 1714 +a 2392 4106 1 +a 2393 2394 1713 +a 2393 4106 1 +a 2394 2395 1712 +a 2394 4106 1 +a 2395 2396 1711 +a 2395 4106 1 +a 2396 2397 1710 +a 2396 4106 1 +a 2397 2398 1709 +a 2397 4106 1 +a 2398 2399 1708 +a 2398 4106 1 +a 2399 2400 1707 +a 2399 4106 1 +a 2400 2401 1706 +a 2400 4106 1 +a 2401 2402 1705 +a 2401 4106 1 +a 2402 2403 1704 +a 2402 4106 1 +a 2403 2404 1703 +a 2403 4106 1 +a 2404 2405 1702 +a 2404 4106 1 +a 2405 2406 1701 +a 2405 4106 1 +a 2406 2407 1700 +a 2406 4106 1 +a 2407 2408 1699 +a 2407 4106 1 +a 2408 2409 1698 +a 2408 4106 1 +a 2409 2410 1697 +a 2409 4106 1 +a 2410 2411 1696 +a 2410 4106 1 +a 2411 2412 1695 +a 2411 4106 1 +a 2412 2413 1694 +a 2412 4106 1 +a 2413 2414 1693 +a 2413 4106 1 +a 2414 2415 1692 +a 2414 4106 1 +a 2415 2416 1691 +a 2415 4106 1 +a 2416 2417 1690 +a 2416 4106 1 +a 2417 2418 1689 +a 2417 4106 1 +a 2418 2419 1688 +a 2418 4106 1 +a 2419 2420 1687 +a 2419 4106 1 +a 2420 2421 1686 +a 2420 4106 1 +a 2421 2422 1685 +a 2421 4106 1 +a 2422 2423 1684 +a 2422 4106 1 +a 2423 2424 1683 +a 2423 4106 1 +a 2424 2425 1682 +a 2424 4106 1 +a 2425 2426 1681 +a 2425 4106 1 +a 2426 2427 1680 +a 2426 4106 1 +a 2427 2428 1679 +a 2427 4106 1 +a 2428 2429 1678 +a 2428 4106 1 +a 2429 2430 1677 +a 2429 4106 1 +a 2430 2431 1676 +a 2430 4106 1 +a 2431 2432 1675 +a 2431 4106 1 +a 2432 2433 1674 +a 2432 4106 1 +a 2433 2434 1673 +a 2433 4106 1 +a 2434 2435 1672 +a 2434 4106 1 +a 2435 2436 1671 +a 2435 4106 1 +a 2436 2437 1670 +a 2436 4106 1 +a 2437 2438 1669 +a 2437 4106 1 +a 2438 2439 1668 +a 2438 4106 1 +a 2439 2440 1667 +a 2439 4106 1 +a 2440 2441 1666 +a 2440 4106 1 +a 2441 2442 1665 +a 2441 4106 1 +a 2442 2443 1664 +a 2442 4106 1 +a 2443 2444 1663 +a 2443 4106 1 +a 2444 2445 1662 +a 2444 4106 1 +a 2445 2446 1661 +a 2445 4106 1 +a 2446 2447 1660 +a 2446 4106 1 +a 2447 2448 1659 +a 2447 4106 1 +a 2448 2449 1658 +a 2448 4106 1 +a 2449 2450 1657 +a 2449 4106 1 +a 2450 2451 1656 +a 2450 4106 1 +a 2451 2452 1655 +a 2451 4106 1 +a 2452 2453 1654 +a 2452 4106 1 +a 2453 2454 1653 +a 2453 4106 1 +a 2454 2455 1652 +a 2454 4106 1 +a 2455 2456 1651 +a 2455 4106 1 +a 2456 2457 1650 +a 2456 4106 1 +a 2457 2458 1649 +a 2457 4106 1 +a 2458 2459 1648 +a 2458 4106 1 +a 2459 2460 1647 +a 2459 4106 1 +a 2460 2461 1646 +a 2460 4106 1 +a 2461 2462 1645 +a 2461 4106 1 +a 2462 2463 1644 +a 2462 4106 1 +a 2463 2464 1643 +a 2463 4106 1 +a 2464 2465 1642 +a 2464 4106 1 +a 2465 2466 1641 +a 2465 4106 1 +a 2466 2467 1640 +a 2466 4106 1 +a 2467 2468 1639 +a 2467 4106 1 +a 2468 2469 1638 +a 2468 4106 1 +a 2469 2470 1637 +a 2469 4106 1 +a 2470 2471 1636 +a 2470 4106 1 +a 2471 2472 1635 +a 2471 4106 1 +a 2472 2473 1634 +a 2472 4106 1 +a 2473 2474 1633 +a 2473 4106 1 +a 2474 2475 1632 +a 2474 4106 1 +a 2475 2476 1631 +a 2475 4106 1 +a 2476 2477 1630 +a 2476 4106 1 +a 2477 2478 1629 +a 2477 4106 1 +a 2478 2479 1628 +a 2478 4106 1 +a 2479 2480 1627 +a 2479 4106 1 +a 2480 2481 1626 +a 2480 4106 1 +a 2481 2482 1625 +a 2481 4106 1 +a 2482 2483 1624 +a 2482 4106 1 +a 2483 2484 1623 +a 2483 4106 1 +a 2484 2485 1622 +a 2484 4106 1 +a 2485 2486 1621 +a 2485 4106 1 +a 2486 2487 1620 +a 2486 4106 1 +a 2487 2488 1619 +a 2487 4106 1 +a 2488 2489 1618 +a 2488 4106 1 +a 2489 2490 1617 +a 2489 4106 1 +a 2490 2491 1616 +a 2490 4106 1 +a 2491 2492 1615 +a 2491 4106 1 +a 2492 2493 1614 +a 2492 4106 1 +a 2493 2494 1613 +a 2493 4106 1 +a 2494 2495 1612 +a 2494 4106 1 +a 2495 2496 1611 +a 2495 4106 1 +a 2496 2497 1610 +a 2496 4106 1 +a 2497 2498 1609 +a 2497 4106 1 +a 2498 2499 1608 +a 2498 4106 1 +a 2499 2500 1607 +a 2499 4106 1 +a 2500 2501 1606 +a 2500 4106 1 +a 2501 2502 1605 +a 2501 4106 1 +a 2502 2503 1604 +a 2502 4106 1 +a 2503 2504 1603 +a 2503 4106 1 +a 2504 2505 1602 +a 2504 4106 1 +a 2505 2506 1601 +a 2505 4106 1 +a 2506 2507 1600 +a 2506 4106 1 +a 2507 2508 1599 +a 2507 4106 1 +a 2508 2509 1598 +a 2508 4106 1 +a 2509 2510 1597 +a 2509 4106 1 +a 2510 2511 1596 +a 2510 4106 1 +a 2511 2512 1595 +a 2511 4106 1 +a 2512 2513 1594 +a 2512 4106 1 +a 2513 2514 1593 +a 2513 4106 1 +a 2514 2515 1592 +a 2514 4106 1 +a 2515 2516 1591 +a 2515 4106 1 +a 2516 2517 1590 +a 2516 4106 1 +a 2517 2518 1589 +a 2517 4106 1 +a 2518 2519 1588 +a 2518 4106 1 +a 2519 2520 1587 +a 2519 4106 1 +a 2520 2521 1586 +a 2520 4106 1 +a 2521 2522 1585 +a 2521 4106 1 +a 2522 2523 1584 +a 2522 4106 1 +a 2523 2524 1583 +a 2523 4106 1 +a 2524 2525 1582 +a 2524 4106 1 +a 2525 2526 1581 +a 2525 4106 1 +a 2526 2527 1580 +a 2526 4106 1 +a 2527 2528 1579 +a 2527 4106 1 +a 2528 2529 1578 +a 2528 4106 1 +a 2529 2530 1577 +a 2529 4106 1 +a 2530 2531 1576 +a 2530 4106 1 +a 2531 2532 1575 +a 2531 4106 1 +a 2532 2533 1574 +a 2532 4106 1 +a 2533 2534 1573 +a 2533 4106 1 +a 2534 2535 1572 +a 2534 4106 1 +a 2535 2536 1571 +a 2535 4106 1 +a 2536 2537 1570 +a 2536 4106 1 +a 2537 2538 1569 +a 2537 4106 1 +a 2538 2539 1568 +a 2538 4106 1 +a 2539 2540 1567 +a 2539 4106 1 +a 2540 2541 1566 +a 2540 4106 1 +a 2541 2542 1565 +a 2541 4106 1 +a 2542 2543 1564 +a 2542 4106 1 +a 2543 2544 1563 +a 2543 4106 1 +a 2544 2545 1562 +a 2544 4106 1 +a 2545 2546 1561 +a 2545 4106 1 +a 2546 2547 1560 +a 2546 4106 1 +a 2547 2548 1559 +a 2547 4106 1 +a 2548 2549 1558 +a 2548 4106 1 +a 2549 2550 1557 +a 2549 4106 1 +a 2550 2551 1556 +a 2550 4106 1 +a 2551 2552 1555 +a 2551 4106 1 +a 2552 2553 1554 +a 2552 4106 1 +a 2553 2554 1553 +a 2553 4106 1 +a 2554 2555 1552 +a 2554 4106 1 +a 2555 2556 1551 +a 2555 4106 1 +a 2556 2557 1550 +a 2556 4106 1 +a 2557 2558 1549 +a 2557 4106 1 +a 2558 2559 1548 +a 2558 4106 1 +a 2559 2560 1547 +a 2559 4106 1 +a 2560 2561 1546 +a 2560 4106 1 +a 2561 2562 1545 +a 2561 4106 1 +a 2562 2563 1544 +a 2562 4106 1 +a 2563 2564 1543 +a 2563 4106 1 +a 2564 2565 1542 +a 2564 4106 1 +a 2565 2566 1541 +a 2565 4106 1 +a 2566 2567 1540 +a 2566 4106 1 +a 2567 2568 1539 +a 2567 4106 1 +a 2568 2569 1538 +a 2568 4106 1 +a 2569 2570 1537 +a 2569 4106 1 +a 2570 2571 1536 +a 2570 4106 1 +a 2571 2572 1535 +a 2571 4106 1 +a 2572 2573 1534 +a 2572 4106 1 +a 2573 2574 1533 +a 2573 4106 1 +a 2574 2575 1532 +a 2574 4106 1 +a 2575 2576 1531 +a 2575 4106 1 +a 2576 2577 1530 +a 2576 4106 1 +a 2577 2578 1529 +a 2577 4106 1 +a 2578 2579 1528 +a 2578 4106 1 +a 2579 2580 1527 +a 2579 4106 1 +a 2580 2581 1526 +a 2580 4106 1 +a 2581 2582 1525 +a 2581 4106 1 +a 2582 2583 1524 +a 2582 4106 1 +a 2583 2584 1523 +a 2583 4106 1 +a 2584 2585 1522 +a 2584 4106 1 +a 2585 2586 1521 +a 2585 4106 1 +a 2586 2587 1520 +a 2586 4106 1 +a 2587 2588 1519 +a 2587 4106 1 +a 2588 2589 1518 +a 2588 4106 1 +a 2589 2590 1517 +a 2589 4106 1 +a 2590 2591 1516 +a 2590 4106 1 +a 2591 2592 1515 +a 2591 4106 1 +a 2592 2593 1514 +a 2592 4106 1 +a 2593 2594 1513 +a 2593 4106 1 +a 2594 2595 1512 +a 2594 4106 1 +a 2595 2596 1511 +a 2595 4106 1 +a 2596 2597 1510 +a 2596 4106 1 +a 2597 2598 1509 +a 2597 4106 1 +a 2598 2599 1508 +a 2598 4106 1 +a 2599 2600 1507 +a 2599 4106 1 +a 2600 2601 1506 +a 2600 4106 1 +a 2601 2602 1505 +a 2601 4106 1 +a 2602 2603 1504 +a 2602 4106 1 +a 2603 2604 1503 +a 2603 4106 1 +a 2604 2605 1502 +a 2604 4106 1 +a 2605 2606 1501 +a 2605 4106 1 +a 2606 2607 1500 +a 2606 4106 1 +a 2607 2608 1499 +a 2607 4106 1 +a 2608 2609 1498 +a 2608 4106 1 +a 2609 2610 1497 +a 2609 4106 1 +a 2610 2611 1496 +a 2610 4106 1 +a 2611 2612 1495 +a 2611 4106 1 +a 2612 2613 1494 +a 2612 4106 1 +a 2613 2614 1493 +a 2613 4106 1 +a 2614 2615 1492 +a 2614 4106 1 +a 2615 2616 1491 +a 2615 4106 1 +a 2616 2617 1490 +a 2616 4106 1 +a 2617 2618 1489 +a 2617 4106 1 +a 2618 2619 1488 +a 2618 4106 1 +a 2619 2620 1487 +a 2619 4106 1 +a 2620 2621 1486 +a 2620 4106 1 +a 2621 2622 1485 +a 2621 4106 1 +a 2622 2623 1484 +a 2622 4106 1 +a 2623 2624 1483 +a 2623 4106 1 +a 2624 2625 1482 +a 2624 4106 1 +a 2625 2626 1481 +a 2625 4106 1 +a 2626 2627 1480 +a 2626 4106 1 +a 2627 2628 1479 +a 2627 4106 1 +a 2628 2629 1478 +a 2628 4106 1 +a 2629 2630 1477 +a 2629 4106 1 +a 2630 2631 1476 +a 2630 4106 1 +a 2631 2632 1475 +a 2631 4106 1 +a 2632 2633 1474 +a 2632 4106 1 +a 2633 2634 1473 +a 2633 4106 1 +a 2634 2635 1472 +a 2634 4106 1 +a 2635 2636 1471 +a 2635 4106 1 +a 2636 2637 1470 +a 2636 4106 1 +a 2637 2638 1469 +a 2637 4106 1 +a 2638 2639 1468 +a 2638 4106 1 +a 2639 2640 1467 +a 2639 4106 1 +a 2640 2641 1466 +a 2640 4106 1 +a 2641 2642 1465 +a 2641 4106 1 +a 2642 2643 1464 +a 2642 4106 1 +a 2643 2644 1463 +a 2643 4106 1 +a 2644 2645 1462 +a 2644 4106 1 +a 2645 2646 1461 +a 2645 4106 1 +a 2646 2647 1460 +a 2646 4106 1 +a 2647 2648 1459 +a 2647 4106 1 +a 2648 2649 1458 +a 2648 4106 1 +a 2649 2650 1457 +a 2649 4106 1 +a 2650 2651 1456 +a 2650 4106 1 +a 2651 2652 1455 +a 2651 4106 1 +a 2652 2653 1454 +a 2652 4106 1 +a 2653 2654 1453 +a 2653 4106 1 +a 2654 2655 1452 +a 2654 4106 1 +a 2655 2656 1451 +a 2655 4106 1 +a 2656 2657 1450 +a 2656 4106 1 +a 2657 2658 1449 +a 2657 4106 1 +a 2658 2659 1448 +a 2658 4106 1 +a 2659 2660 1447 +a 2659 4106 1 +a 2660 2661 1446 +a 2660 4106 1 +a 2661 2662 1445 +a 2661 4106 1 +a 2662 2663 1444 +a 2662 4106 1 +a 2663 2664 1443 +a 2663 4106 1 +a 2664 2665 1442 +a 2664 4106 1 +a 2665 2666 1441 +a 2665 4106 1 +a 2666 2667 1440 +a 2666 4106 1 +a 2667 2668 1439 +a 2667 4106 1 +a 2668 2669 1438 +a 2668 4106 1 +a 2669 2670 1437 +a 2669 4106 1 +a 2670 2671 1436 +a 2670 4106 1 +a 2671 2672 1435 +a 2671 4106 1 +a 2672 2673 1434 +a 2672 4106 1 +a 2673 2674 1433 +a 2673 4106 1 +a 2674 2675 1432 +a 2674 4106 1 +a 2675 2676 1431 +a 2675 4106 1 +a 2676 2677 1430 +a 2676 4106 1 +a 2677 2678 1429 +a 2677 4106 1 +a 2678 2679 1428 +a 2678 4106 1 +a 2679 2680 1427 +a 2679 4106 1 +a 2680 2681 1426 +a 2680 4106 1 +a 2681 2682 1425 +a 2681 4106 1 +a 2682 2683 1424 +a 2682 4106 1 +a 2683 2684 1423 +a 2683 4106 1 +a 2684 2685 1422 +a 2684 4106 1 +a 2685 2686 1421 +a 2685 4106 1 +a 2686 2687 1420 +a 2686 4106 1 +a 2687 2688 1419 +a 2687 4106 1 +a 2688 2689 1418 +a 2688 4106 1 +a 2689 2690 1417 +a 2689 4106 1 +a 2690 2691 1416 +a 2690 4106 1 +a 2691 2692 1415 +a 2691 4106 1 +a 2692 2693 1414 +a 2692 4106 1 +a 2693 2694 1413 +a 2693 4106 1 +a 2694 2695 1412 +a 2694 4106 1 +a 2695 2696 1411 +a 2695 4106 1 +a 2696 2697 1410 +a 2696 4106 1 +a 2697 2698 1409 +a 2697 4106 1 +a 2698 2699 1408 +a 2698 4106 1 +a 2699 2700 1407 +a 2699 4106 1 +a 2700 2701 1406 +a 2700 4106 1 +a 2701 2702 1405 +a 2701 4106 1 +a 2702 2703 1404 +a 2702 4106 1 +a 2703 2704 1403 +a 2703 4106 1 +a 2704 2705 1402 +a 2704 4106 1 +a 2705 2706 1401 +a 2705 4106 1 +a 2706 2707 1400 +a 2706 4106 1 +a 2707 2708 1399 +a 2707 4106 1 +a 2708 2709 1398 +a 2708 4106 1 +a 2709 2710 1397 +a 2709 4106 1 +a 2710 2711 1396 +a 2710 4106 1 +a 2711 2712 1395 +a 2711 4106 1 +a 2712 2713 1394 +a 2712 4106 1 +a 2713 2714 1393 +a 2713 4106 1 +a 2714 2715 1392 +a 2714 4106 1 +a 2715 2716 1391 +a 2715 4106 1 +a 2716 2717 1390 +a 2716 4106 1 +a 2717 2718 1389 +a 2717 4106 1 +a 2718 2719 1388 +a 2718 4106 1 +a 2719 2720 1387 +a 2719 4106 1 +a 2720 2721 1386 +a 2720 4106 1 +a 2721 2722 1385 +a 2721 4106 1 +a 2722 2723 1384 +a 2722 4106 1 +a 2723 2724 1383 +a 2723 4106 1 +a 2724 2725 1382 +a 2724 4106 1 +a 2725 2726 1381 +a 2725 4106 1 +a 2726 2727 1380 +a 2726 4106 1 +a 2727 2728 1379 +a 2727 4106 1 +a 2728 2729 1378 +a 2728 4106 1 +a 2729 2730 1377 +a 2729 4106 1 +a 2730 2731 1376 +a 2730 4106 1 +a 2731 2732 1375 +a 2731 4106 1 +a 2732 2733 1374 +a 2732 4106 1 +a 2733 2734 1373 +a 2733 4106 1 +a 2734 2735 1372 +a 2734 4106 1 +a 2735 2736 1371 +a 2735 4106 1 +a 2736 2737 1370 +a 2736 4106 1 +a 2737 2738 1369 +a 2737 4106 1 +a 2738 2739 1368 +a 2738 4106 1 +a 2739 2740 1367 +a 2739 4106 1 +a 2740 2741 1366 +a 2740 4106 1 +a 2741 2742 1365 +a 2741 4106 1 +a 2742 2743 1364 +a 2742 4106 1 +a 2743 2744 1363 +a 2743 4106 1 +a 2744 2745 1362 +a 2744 4106 1 +a 2745 2746 1361 +a 2745 4106 1 +a 2746 2747 1360 +a 2746 4106 1 +a 2747 2748 1359 +a 2747 4106 1 +a 2748 2749 1358 +a 2748 4106 1 +a 2749 2750 1357 +a 2749 4106 1 +a 2750 2751 1356 +a 2750 4106 1 +a 2751 2752 1355 +a 2751 4106 1 +a 2752 2753 1354 +a 2752 4106 1 +a 2753 2754 1353 +a 2753 4106 1 +a 2754 2755 1352 +a 2754 4106 1 +a 2755 2756 1351 +a 2755 4106 1 +a 2756 2757 1350 +a 2756 4106 1 +a 2757 2758 1349 +a 2757 4106 1 +a 2758 2759 1348 +a 2758 4106 1 +a 2759 2760 1347 +a 2759 4106 1 +a 2760 2761 1346 +a 2760 4106 1 +a 2761 2762 1345 +a 2761 4106 1 +a 2762 2763 1344 +a 2762 4106 1 +a 2763 2764 1343 +a 2763 4106 1 +a 2764 2765 1342 +a 2764 4106 1 +a 2765 2766 1341 +a 2765 4106 1 +a 2766 2767 1340 +a 2766 4106 1 +a 2767 2768 1339 +a 2767 4106 1 +a 2768 2769 1338 +a 2768 4106 1 +a 2769 2770 1337 +a 2769 4106 1 +a 2770 2771 1336 +a 2770 4106 1 +a 2771 2772 1335 +a 2771 4106 1 +a 2772 2773 1334 +a 2772 4106 1 +a 2773 2774 1333 +a 2773 4106 1 +a 2774 2775 1332 +a 2774 4106 1 +a 2775 2776 1331 +a 2775 4106 1 +a 2776 2777 1330 +a 2776 4106 1 +a 2777 2778 1329 +a 2777 4106 1 +a 2778 2779 1328 +a 2778 4106 1 +a 2779 2780 1327 +a 2779 4106 1 +a 2780 2781 1326 +a 2780 4106 1 +a 2781 2782 1325 +a 2781 4106 1 +a 2782 2783 1324 +a 2782 4106 1 +a 2783 2784 1323 +a 2783 4106 1 +a 2784 2785 1322 +a 2784 4106 1 +a 2785 2786 1321 +a 2785 4106 1 +a 2786 2787 1320 +a 2786 4106 1 +a 2787 2788 1319 +a 2787 4106 1 +a 2788 2789 1318 +a 2788 4106 1 +a 2789 2790 1317 +a 2789 4106 1 +a 2790 2791 1316 +a 2790 4106 1 +a 2791 2792 1315 +a 2791 4106 1 +a 2792 2793 1314 +a 2792 4106 1 +a 2793 2794 1313 +a 2793 4106 1 +a 2794 2795 1312 +a 2794 4106 1 +a 2795 2796 1311 +a 2795 4106 1 +a 2796 2797 1310 +a 2796 4106 1 +a 2797 2798 1309 +a 2797 4106 1 +a 2798 2799 1308 +a 2798 4106 1 +a 2799 2800 1307 +a 2799 4106 1 +a 2800 2801 1306 +a 2800 4106 1 +a 2801 2802 1305 +a 2801 4106 1 +a 2802 2803 1304 +a 2802 4106 1 +a 2803 2804 1303 +a 2803 4106 1 +a 2804 2805 1302 +a 2804 4106 1 +a 2805 2806 1301 +a 2805 4106 1 +a 2806 2807 1300 +a 2806 4106 1 +a 2807 2808 1299 +a 2807 4106 1 +a 2808 2809 1298 +a 2808 4106 1 +a 2809 2810 1297 +a 2809 4106 1 +a 2810 2811 1296 +a 2810 4106 1 +a 2811 2812 1295 +a 2811 4106 1 +a 2812 2813 1294 +a 2812 4106 1 +a 2813 2814 1293 +a 2813 4106 1 +a 2814 2815 1292 +a 2814 4106 1 +a 2815 2816 1291 +a 2815 4106 1 +a 2816 2817 1290 +a 2816 4106 1 +a 2817 2818 1289 +a 2817 4106 1 +a 2818 2819 1288 +a 2818 4106 1 +a 2819 2820 1287 +a 2819 4106 1 +a 2820 2821 1286 +a 2820 4106 1 +a 2821 2822 1285 +a 2821 4106 1 +a 2822 2823 1284 +a 2822 4106 1 +a 2823 2824 1283 +a 2823 4106 1 +a 2824 2825 1282 +a 2824 4106 1 +a 2825 2826 1281 +a 2825 4106 1 +a 2826 2827 1280 +a 2826 4106 1 +a 2827 2828 1279 +a 2827 4106 1 +a 2828 2829 1278 +a 2828 4106 1 +a 2829 2830 1277 +a 2829 4106 1 +a 2830 2831 1276 +a 2830 4106 1 +a 2831 2832 1275 +a 2831 4106 1 +a 2832 2833 1274 +a 2832 4106 1 +a 2833 2834 1273 +a 2833 4106 1 +a 2834 2835 1272 +a 2834 4106 1 +a 2835 2836 1271 +a 2835 4106 1 +a 2836 2837 1270 +a 2836 4106 1 +a 2837 2838 1269 +a 2837 4106 1 +a 2838 2839 1268 +a 2838 4106 1 +a 2839 2840 1267 +a 2839 4106 1 +a 2840 2841 1266 +a 2840 4106 1 +a 2841 2842 1265 +a 2841 4106 1 +a 2842 2843 1264 +a 2842 4106 1 +a 2843 2844 1263 +a 2843 4106 1 +a 2844 2845 1262 +a 2844 4106 1 +a 2845 2846 1261 +a 2845 4106 1 +a 2846 2847 1260 +a 2846 4106 1 +a 2847 2848 1259 +a 2847 4106 1 +a 2848 2849 1258 +a 2848 4106 1 +a 2849 2850 1257 +a 2849 4106 1 +a 2850 2851 1256 +a 2850 4106 1 +a 2851 2852 1255 +a 2851 4106 1 +a 2852 2853 1254 +a 2852 4106 1 +a 2853 2854 1253 +a 2853 4106 1 +a 2854 2855 1252 +a 2854 4106 1 +a 2855 2856 1251 +a 2855 4106 1 +a 2856 2857 1250 +a 2856 4106 1 +a 2857 2858 1249 +a 2857 4106 1 +a 2858 2859 1248 +a 2858 4106 1 +a 2859 2860 1247 +a 2859 4106 1 +a 2860 2861 1246 +a 2860 4106 1 +a 2861 2862 1245 +a 2861 4106 1 +a 2862 2863 1244 +a 2862 4106 1 +a 2863 2864 1243 +a 2863 4106 1 +a 2864 2865 1242 +a 2864 4106 1 +a 2865 2866 1241 +a 2865 4106 1 +a 2866 2867 1240 +a 2866 4106 1 +a 2867 2868 1239 +a 2867 4106 1 +a 2868 2869 1238 +a 2868 4106 1 +a 2869 2870 1237 +a 2869 4106 1 +a 2870 2871 1236 +a 2870 4106 1 +a 2871 2872 1235 +a 2871 4106 1 +a 2872 2873 1234 +a 2872 4106 1 +a 2873 2874 1233 +a 2873 4106 1 +a 2874 2875 1232 +a 2874 4106 1 +a 2875 2876 1231 +a 2875 4106 1 +a 2876 2877 1230 +a 2876 4106 1 +a 2877 2878 1229 +a 2877 4106 1 +a 2878 2879 1228 +a 2878 4106 1 +a 2879 2880 1227 +a 2879 4106 1 +a 2880 2881 1226 +a 2880 4106 1 +a 2881 2882 1225 +a 2881 4106 1 +a 2882 2883 1224 +a 2882 4106 1 +a 2883 2884 1223 +a 2883 4106 1 +a 2884 2885 1222 +a 2884 4106 1 +a 2885 2886 1221 +a 2885 4106 1 +a 2886 2887 1220 +a 2886 4106 1 +a 2887 2888 1219 +a 2887 4106 1 +a 2888 2889 1218 +a 2888 4106 1 +a 2889 2890 1217 +a 2889 4106 1 +a 2890 2891 1216 +a 2890 4106 1 +a 2891 2892 1215 +a 2891 4106 1 +a 2892 2893 1214 +a 2892 4106 1 +a 2893 2894 1213 +a 2893 4106 1 +a 2894 2895 1212 +a 2894 4106 1 +a 2895 2896 1211 +a 2895 4106 1 +a 2896 2897 1210 +a 2896 4106 1 +a 2897 2898 1209 +a 2897 4106 1 +a 2898 2899 1208 +a 2898 4106 1 +a 2899 2900 1207 +a 2899 4106 1 +a 2900 2901 1206 +a 2900 4106 1 +a 2901 2902 1205 +a 2901 4106 1 +a 2902 2903 1204 +a 2902 4106 1 +a 2903 2904 1203 +a 2903 4106 1 +a 2904 2905 1202 +a 2904 4106 1 +a 2905 2906 1201 +a 2905 4106 1 +a 2906 2907 1200 +a 2906 4106 1 +a 2907 2908 1199 +a 2907 4106 1 +a 2908 2909 1198 +a 2908 4106 1 +a 2909 2910 1197 +a 2909 4106 1 +a 2910 2911 1196 +a 2910 4106 1 +a 2911 2912 1195 +a 2911 4106 1 +a 2912 2913 1194 +a 2912 4106 1 +a 2913 2914 1193 +a 2913 4106 1 +a 2914 2915 1192 +a 2914 4106 1 +a 2915 2916 1191 +a 2915 4106 1 +a 2916 2917 1190 +a 2916 4106 1 +a 2917 2918 1189 +a 2917 4106 1 +a 2918 2919 1188 +a 2918 4106 1 +a 2919 2920 1187 +a 2919 4106 1 +a 2920 2921 1186 +a 2920 4106 1 +a 2921 2922 1185 +a 2921 4106 1 +a 2922 2923 1184 +a 2922 4106 1 +a 2923 2924 1183 +a 2923 4106 1 +a 2924 2925 1182 +a 2924 4106 1 +a 2925 2926 1181 +a 2925 4106 1 +a 2926 2927 1180 +a 2926 4106 1 +a 2927 2928 1179 +a 2927 4106 1 +a 2928 2929 1178 +a 2928 4106 1 +a 2929 2930 1177 +a 2929 4106 1 +a 2930 2931 1176 +a 2930 4106 1 +a 2931 2932 1175 +a 2931 4106 1 +a 2932 2933 1174 +a 2932 4106 1 +a 2933 2934 1173 +a 2933 4106 1 +a 2934 2935 1172 +a 2934 4106 1 +a 2935 2936 1171 +a 2935 4106 1 +a 2936 2937 1170 +a 2936 4106 1 +a 2937 2938 1169 +a 2937 4106 1 +a 2938 2939 1168 +a 2938 4106 1 +a 2939 2940 1167 +a 2939 4106 1 +a 2940 2941 1166 +a 2940 4106 1 +a 2941 2942 1165 +a 2941 4106 1 +a 2942 2943 1164 +a 2942 4106 1 +a 2943 2944 1163 +a 2943 4106 1 +a 2944 2945 1162 +a 2944 4106 1 +a 2945 2946 1161 +a 2945 4106 1 +a 2946 2947 1160 +a 2946 4106 1 +a 2947 2948 1159 +a 2947 4106 1 +a 2948 2949 1158 +a 2948 4106 1 +a 2949 2950 1157 +a 2949 4106 1 +a 2950 2951 1156 +a 2950 4106 1 +a 2951 2952 1155 +a 2951 4106 1 +a 2952 2953 1154 +a 2952 4106 1 +a 2953 2954 1153 +a 2953 4106 1 +a 2954 2955 1152 +a 2954 4106 1 +a 2955 2956 1151 +a 2955 4106 1 +a 2956 2957 1150 +a 2956 4106 1 +a 2957 2958 1149 +a 2957 4106 1 +a 2958 2959 1148 +a 2958 4106 1 +a 2959 2960 1147 +a 2959 4106 1 +a 2960 2961 1146 +a 2960 4106 1 +a 2961 2962 1145 +a 2961 4106 1 +a 2962 2963 1144 +a 2962 4106 1 +a 2963 2964 1143 +a 2963 4106 1 +a 2964 2965 1142 +a 2964 4106 1 +a 2965 2966 1141 +a 2965 4106 1 +a 2966 2967 1140 +a 2966 4106 1 +a 2967 2968 1139 +a 2967 4106 1 +a 2968 2969 1138 +a 2968 4106 1 +a 2969 2970 1137 +a 2969 4106 1 +a 2970 2971 1136 +a 2970 4106 1 +a 2971 2972 1135 +a 2971 4106 1 +a 2972 2973 1134 +a 2972 4106 1 +a 2973 2974 1133 +a 2973 4106 1 +a 2974 2975 1132 +a 2974 4106 1 +a 2975 2976 1131 +a 2975 4106 1 +a 2976 2977 1130 +a 2976 4106 1 +a 2977 2978 1129 +a 2977 4106 1 +a 2978 2979 1128 +a 2978 4106 1 +a 2979 2980 1127 +a 2979 4106 1 +a 2980 2981 1126 +a 2980 4106 1 +a 2981 2982 1125 +a 2981 4106 1 +a 2982 2983 1124 +a 2982 4106 1 +a 2983 2984 1123 +a 2983 4106 1 +a 2984 2985 1122 +a 2984 4106 1 +a 2985 2986 1121 +a 2985 4106 1 +a 2986 2987 1120 +a 2986 4106 1 +a 2987 2988 1119 +a 2987 4106 1 +a 2988 2989 1118 +a 2988 4106 1 +a 2989 2990 1117 +a 2989 4106 1 +a 2990 2991 1116 +a 2990 4106 1 +a 2991 2992 1115 +a 2991 4106 1 +a 2992 2993 1114 +a 2992 4106 1 +a 2993 2994 1113 +a 2993 4106 1 +a 2994 2995 1112 +a 2994 4106 1 +a 2995 2996 1111 +a 2995 4106 1 +a 2996 2997 1110 +a 2996 4106 1 +a 2997 2998 1109 +a 2997 4106 1 +a 2998 2999 1108 +a 2998 4106 1 +a 2999 3000 1107 +a 2999 4106 1 +a 3000 3001 1106 +a 3000 4106 1 +a 3001 3002 1105 +a 3001 4106 1 +a 3002 3003 1104 +a 3002 4106 1 +a 3003 3004 1103 +a 3003 4106 1 +a 3004 3005 1102 +a 3004 4106 1 +a 3005 3006 1101 +a 3005 4106 1 +a 3006 3007 1100 +a 3006 4106 1 +a 3007 3008 1099 +a 3007 4106 1 +a 3008 3009 1098 +a 3008 4106 1 +a 3009 3010 1097 +a 3009 4106 1 +a 3010 3011 1096 +a 3010 4106 1 +a 3011 3012 1095 +a 3011 4106 1 +a 3012 3013 1094 +a 3012 4106 1 +a 3013 3014 1093 +a 3013 4106 1 +a 3014 3015 1092 +a 3014 4106 1 +a 3015 3016 1091 +a 3015 4106 1 +a 3016 3017 1090 +a 3016 4106 1 +a 3017 3018 1089 +a 3017 4106 1 +a 3018 3019 1088 +a 3018 4106 1 +a 3019 3020 1087 +a 3019 4106 1 +a 3020 3021 1086 +a 3020 4106 1 +a 3021 3022 1085 +a 3021 4106 1 +a 3022 3023 1084 +a 3022 4106 1 +a 3023 3024 1083 +a 3023 4106 1 +a 3024 3025 1082 +a 3024 4106 1 +a 3025 3026 1081 +a 3025 4106 1 +a 3026 3027 1080 +a 3026 4106 1 +a 3027 3028 1079 +a 3027 4106 1 +a 3028 3029 1078 +a 3028 4106 1 +a 3029 3030 1077 +a 3029 4106 1 +a 3030 3031 1076 +a 3030 4106 1 +a 3031 3032 1075 +a 3031 4106 1 +a 3032 3033 1074 +a 3032 4106 1 +a 3033 3034 1073 +a 3033 4106 1 +a 3034 3035 1072 +a 3034 4106 1 +a 3035 3036 1071 +a 3035 4106 1 +a 3036 3037 1070 +a 3036 4106 1 +a 3037 3038 1069 +a 3037 4106 1 +a 3038 3039 1068 +a 3038 4106 1 +a 3039 3040 1067 +a 3039 4106 1 +a 3040 3041 1066 +a 3040 4106 1 +a 3041 3042 1065 +a 3041 4106 1 +a 3042 3043 1064 +a 3042 4106 1 +a 3043 3044 1063 +a 3043 4106 1 +a 3044 3045 1062 +a 3044 4106 1 +a 3045 3046 1061 +a 3045 4106 1 +a 3046 3047 1060 +a 3046 4106 1 +a 3047 3048 1059 +a 3047 4106 1 +a 3048 3049 1058 +a 3048 4106 1 +a 3049 3050 1057 +a 3049 4106 1 +a 3050 3051 1056 +a 3050 4106 1 +a 3051 3052 1055 +a 3051 4106 1 +a 3052 3053 1054 +a 3052 4106 1 +a 3053 3054 1053 +a 3053 4106 1 +a 3054 3055 1052 +a 3054 4106 1 +a 3055 3056 1051 +a 3055 4106 1 +a 3056 3057 1050 +a 3056 4106 1 +a 3057 3058 1049 +a 3057 4106 1 +a 3058 3059 1048 +a 3058 4106 1 +a 3059 3060 1047 +a 3059 4106 1 +a 3060 3061 1046 +a 3060 4106 1 +a 3061 3062 1045 +a 3061 4106 1 +a 3062 3063 1044 +a 3062 4106 1 +a 3063 3064 1043 +a 3063 4106 1 +a 3064 3065 1042 +a 3064 4106 1 +a 3065 3066 1041 +a 3065 4106 1 +a 3066 3067 1040 +a 3066 4106 1 +a 3067 3068 1039 +a 3067 4106 1 +a 3068 3069 1038 +a 3068 4106 1 +a 3069 3070 1037 +a 3069 4106 1 +a 3070 3071 1036 +a 3070 4106 1 +a 3071 3072 1035 +a 3071 4106 1 +a 3072 3073 1034 +a 3072 4106 1 +a 3073 3074 1033 +a 3073 4106 1 +a 3074 3075 1032 +a 3074 4106 1 +a 3075 3076 1031 +a 3075 4106 1 +a 3076 3077 1030 +a 3076 4106 1 +a 3077 3078 1029 +a 3077 4106 1 +a 3078 3079 1028 +a 3078 4106 1 +a 3079 3080 1027 +a 3079 4106 1 +a 3080 3081 1026 +a 3080 4106 1 +a 3081 3082 1025 +a 3081 4106 1 +a 3082 3083 1024 +a 3082 4106 1 +a 3083 3084 1023 +a 3083 4106 1 +a 3084 3085 1022 +a 3084 4106 1 +a 3085 3086 1021 +a 3085 4106 1 +a 3086 3087 1020 +a 3086 4106 1 +a 3087 3088 1019 +a 3087 4106 1 +a 3088 3089 1018 +a 3088 4106 1 +a 3089 3090 1017 +a 3089 4106 1 +a 3090 3091 1016 +a 3090 4106 1 +a 3091 3092 1015 +a 3091 4106 1 +a 3092 3093 1014 +a 3092 4106 1 +a 3093 3094 1013 +a 3093 4106 1 +a 3094 3095 1012 +a 3094 4106 1 +a 3095 3096 1011 +a 3095 4106 1 +a 3096 3097 1010 +a 3096 4106 1 +a 3097 3098 1009 +a 3097 4106 1 +a 3098 3099 1008 +a 3098 4106 1 +a 3099 3100 1007 +a 3099 4106 1 +a 3100 3101 1006 +a 3100 4106 1 +a 3101 3102 1005 +a 3101 4106 1 +a 3102 3103 1004 +a 3102 4106 1 +a 3103 3104 1003 +a 3103 4106 1 +a 3104 3105 1002 +a 3104 4106 1 +a 3105 3106 1001 +a 3105 4106 1 +a 3106 3107 1000 +a 3106 4106 1 +a 3107 3108 999 +a 3107 4106 1 +a 3108 3109 998 +a 3108 4106 1 +a 3109 3110 997 +a 3109 4106 1 +a 3110 3111 996 +a 3110 4106 1 +a 3111 3112 995 +a 3111 4106 1 +a 3112 3113 994 +a 3112 4106 1 +a 3113 3114 993 +a 3113 4106 1 +a 3114 3115 992 +a 3114 4106 1 +a 3115 3116 991 +a 3115 4106 1 +a 3116 3117 990 +a 3116 4106 1 +a 3117 3118 989 +a 3117 4106 1 +a 3118 3119 988 +a 3118 4106 1 +a 3119 3120 987 +a 3119 4106 1 +a 3120 3121 986 +a 3120 4106 1 +a 3121 3122 985 +a 3121 4106 1 +a 3122 3123 984 +a 3122 4106 1 +a 3123 3124 983 +a 3123 4106 1 +a 3124 3125 982 +a 3124 4106 1 +a 3125 3126 981 +a 3125 4106 1 +a 3126 3127 980 +a 3126 4106 1 +a 3127 3128 979 +a 3127 4106 1 +a 3128 3129 978 +a 3128 4106 1 +a 3129 3130 977 +a 3129 4106 1 +a 3130 3131 976 +a 3130 4106 1 +a 3131 3132 975 +a 3131 4106 1 +a 3132 3133 974 +a 3132 4106 1 +a 3133 3134 973 +a 3133 4106 1 +a 3134 3135 972 +a 3134 4106 1 +a 3135 3136 971 +a 3135 4106 1 +a 3136 3137 970 +a 3136 4106 1 +a 3137 3138 969 +a 3137 4106 1 +a 3138 3139 968 +a 3138 4106 1 +a 3139 3140 967 +a 3139 4106 1 +a 3140 3141 966 +a 3140 4106 1 +a 3141 3142 965 +a 3141 4106 1 +a 3142 3143 964 +a 3142 4106 1 +a 3143 3144 963 +a 3143 4106 1 +a 3144 3145 962 +a 3144 4106 1 +a 3145 3146 961 +a 3145 4106 1 +a 3146 3147 960 +a 3146 4106 1 +a 3147 3148 959 +a 3147 4106 1 +a 3148 3149 958 +a 3148 4106 1 +a 3149 3150 957 +a 3149 4106 1 +a 3150 3151 956 +a 3150 4106 1 +a 3151 3152 955 +a 3151 4106 1 +a 3152 3153 954 +a 3152 4106 1 +a 3153 3154 953 +a 3153 4106 1 +a 3154 3155 952 +a 3154 4106 1 +a 3155 3156 951 +a 3155 4106 1 +a 3156 3157 950 +a 3156 4106 1 +a 3157 3158 949 +a 3157 4106 1 +a 3158 3159 948 +a 3158 4106 1 +a 3159 3160 947 +a 3159 4106 1 +a 3160 3161 946 +a 3160 4106 1 +a 3161 3162 945 +a 3161 4106 1 +a 3162 3163 944 +a 3162 4106 1 +a 3163 3164 943 +a 3163 4106 1 +a 3164 3165 942 +a 3164 4106 1 +a 3165 3166 941 +a 3165 4106 1 +a 3166 3167 940 +a 3166 4106 1 +a 3167 3168 939 +a 3167 4106 1 +a 3168 3169 938 +a 3168 4106 1 +a 3169 3170 937 +a 3169 4106 1 +a 3170 3171 936 +a 3170 4106 1 +a 3171 3172 935 +a 3171 4106 1 +a 3172 3173 934 +a 3172 4106 1 +a 3173 3174 933 +a 3173 4106 1 +a 3174 3175 932 +a 3174 4106 1 +a 3175 3176 931 +a 3175 4106 1 +a 3176 3177 930 +a 3176 4106 1 +a 3177 3178 929 +a 3177 4106 1 +a 3178 3179 928 +a 3178 4106 1 +a 3179 3180 927 +a 3179 4106 1 +a 3180 3181 926 +a 3180 4106 1 +a 3181 3182 925 +a 3181 4106 1 +a 3182 3183 924 +a 3182 4106 1 +a 3183 3184 923 +a 3183 4106 1 +a 3184 3185 922 +a 3184 4106 1 +a 3185 3186 921 +a 3185 4106 1 +a 3186 3187 920 +a 3186 4106 1 +a 3187 3188 919 +a 3187 4106 1 +a 3188 3189 918 +a 3188 4106 1 +a 3189 3190 917 +a 3189 4106 1 +a 3190 3191 916 +a 3190 4106 1 +a 3191 3192 915 +a 3191 4106 1 +a 3192 3193 914 +a 3192 4106 1 +a 3193 3194 913 +a 3193 4106 1 +a 3194 3195 912 +a 3194 4106 1 +a 3195 3196 911 +a 3195 4106 1 +a 3196 3197 910 +a 3196 4106 1 +a 3197 3198 909 +a 3197 4106 1 +a 3198 3199 908 +a 3198 4106 1 +a 3199 3200 907 +a 3199 4106 1 +a 3200 3201 906 +a 3200 4106 1 +a 3201 3202 905 +a 3201 4106 1 +a 3202 3203 904 +a 3202 4106 1 +a 3203 3204 903 +a 3203 4106 1 +a 3204 3205 902 +a 3204 4106 1 +a 3205 3206 901 +a 3205 4106 1 +a 3206 3207 900 +a 3206 4106 1 +a 3207 3208 899 +a 3207 4106 1 +a 3208 3209 898 +a 3208 4106 1 +a 3209 3210 897 +a 3209 4106 1 +a 3210 3211 896 +a 3210 4106 1 +a 3211 3212 895 +a 3211 4106 1 +a 3212 3213 894 +a 3212 4106 1 +a 3213 3214 893 +a 3213 4106 1 +a 3214 3215 892 +a 3214 4106 1 +a 3215 3216 891 +a 3215 4106 1 +a 3216 3217 890 +a 3216 4106 1 +a 3217 3218 889 +a 3217 4106 1 +a 3218 3219 888 +a 3218 4106 1 +a 3219 3220 887 +a 3219 4106 1 +a 3220 3221 886 +a 3220 4106 1 +a 3221 3222 885 +a 3221 4106 1 +a 3222 3223 884 +a 3222 4106 1 +a 3223 3224 883 +a 3223 4106 1 +a 3224 3225 882 +a 3224 4106 1 +a 3225 3226 881 +a 3225 4106 1 +a 3226 3227 880 +a 3226 4106 1 +a 3227 3228 879 +a 3227 4106 1 +a 3228 3229 878 +a 3228 4106 1 +a 3229 3230 877 +a 3229 4106 1 +a 3230 3231 876 +a 3230 4106 1 +a 3231 3232 875 +a 3231 4106 1 +a 3232 3233 874 +a 3232 4106 1 +a 3233 3234 873 +a 3233 4106 1 +a 3234 3235 872 +a 3234 4106 1 +a 3235 3236 871 +a 3235 4106 1 +a 3236 3237 870 +a 3236 4106 1 +a 3237 3238 869 +a 3237 4106 1 +a 3238 3239 868 +a 3238 4106 1 +a 3239 3240 867 +a 3239 4106 1 +a 3240 3241 866 +a 3240 4106 1 +a 3241 3242 865 +a 3241 4106 1 +a 3242 3243 864 +a 3242 4106 1 +a 3243 3244 863 +a 3243 4106 1 +a 3244 3245 862 +a 3244 4106 1 +a 3245 3246 861 +a 3245 4106 1 +a 3246 3247 860 +a 3246 4106 1 +a 3247 3248 859 +a 3247 4106 1 +a 3248 3249 858 +a 3248 4106 1 +a 3249 3250 857 +a 3249 4106 1 +a 3250 3251 856 +a 3250 4106 1 +a 3251 3252 855 +a 3251 4106 1 +a 3252 3253 854 +a 3252 4106 1 +a 3253 3254 853 +a 3253 4106 1 +a 3254 3255 852 +a 3254 4106 1 +a 3255 3256 851 +a 3255 4106 1 +a 3256 3257 850 +a 3256 4106 1 +a 3257 3258 849 +a 3257 4106 1 +a 3258 3259 848 +a 3258 4106 1 +a 3259 3260 847 +a 3259 4106 1 +a 3260 3261 846 +a 3260 4106 1 +a 3261 3262 845 +a 3261 4106 1 +a 3262 3263 844 +a 3262 4106 1 +a 3263 3264 843 +a 3263 4106 1 +a 3264 3265 842 +a 3264 4106 1 +a 3265 3266 841 +a 3265 4106 1 +a 3266 3267 840 +a 3266 4106 1 +a 3267 3268 839 +a 3267 4106 1 +a 3268 3269 838 +a 3268 4106 1 +a 3269 3270 837 +a 3269 4106 1 +a 3270 3271 836 +a 3270 4106 1 +a 3271 3272 835 +a 3271 4106 1 +a 3272 3273 834 +a 3272 4106 1 +a 3273 3274 833 +a 3273 4106 1 +a 3274 3275 832 +a 3274 4106 1 +a 3275 3276 831 +a 3275 4106 1 +a 3276 3277 830 +a 3276 4106 1 +a 3277 3278 829 +a 3277 4106 1 +a 3278 3279 828 +a 3278 4106 1 +a 3279 3280 827 +a 3279 4106 1 +a 3280 3281 826 +a 3280 4106 1 +a 3281 3282 825 +a 3281 4106 1 +a 3282 3283 824 +a 3282 4106 1 +a 3283 3284 823 +a 3283 4106 1 +a 3284 3285 822 +a 3284 4106 1 +a 3285 3286 821 +a 3285 4106 1 +a 3286 3287 820 +a 3286 4106 1 +a 3287 3288 819 +a 3287 4106 1 +a 3288 3289 818 +a 3288 4106 1 +a 3289 3290 817 +a 3289 4106 1 +a 3290 3291 816 +a 3290 4106 1 +a 3291 3292 815 +a 3291 4106 1 +a 3292 3293 814 +a 3292 4106 1 +a 3293 3294 813 +a 3293 4106 1 +a 3294 3295 812 +a 3294 4106 1 +a 3295 3296 811 +a 3295 4106 1 +a 3296 3297 810 +a 3296 4106 1 +a 3297 3298 809 +a 3297 4106 1 +a 3298 3299 808 +a 3298 4106 1 +a 3299 3300 807 +a 3299 4106 1 +a 3300 3301 806 +a 3300 4106 1 +a 3301 3302 805 +a 3301 4106 1 +a 3302 3303 804 +a 3302 4106 1 +a 3303 3304 803 +a 3303 4106 1 +a 3304 3305 802 +a 3304 4106 1 +a 3305 3306 801 +a 3305 4106 1 +a 3306 3307 800 +a 3306 4106 1 +a 3307 3308 799 +a 3307 4106 1 +a 3308 3309 798 +a 3308 4106 1 +a 3309 3310 797 +a 3309 4106 1 +a 3310 3311 796 +a 3310 4106 1 +a 3311 3312 795 +a 3311 4106 1 +a 3312 3313 794 +a 3312 4106 1 +a 3313 3314 793 +a 3313 4106 1 +a 3314 3315 792 +a 3314 4106 1 +a 3315 3316 791 +a 3315 4106 1 +a 3316 3317 790 +a 3316 4106 1 +a 3317 3318 789 +a 3317 4106 1 +a 3318 3319 788 +a 3318 4106 1 +a 3319 3320 787 +a 3319 4106 1 +a 3320 3321 786 +a 3320 4106 1 +a 3321 3322 785 +a 3321 4106 1 +a 3322 3323 784 +a 3322 4106 1 +a 3323 3324 783 +a 3323 4106 1 +a 3324 3325 782 +a 3324 4106 1 +a 3325 3326 781 +a 3325 4106 1 +a 3326 3327 780 +a 3326 4106 1 +a 3327 3328 779 +a 3327 4106 1 +a 3328 3329 778 +a 3328 4106 1 +a 3329 3330 777 +a 3329 4106 1 +a 3330 3331 776 +a 3330 4106 1 +a 3331 3332 775 +a 3331 4106 1 +a 3332 3333 774 +a 3332 4106 1 +a 3333 3334 773 +a 3333 4106 1 +a 3334 3335 772 +a 3334 4106 1 +a 3335 3336 771 +a 3335 4106 1 +a 3336 3337 770 +a 3336 4106 1 +a 3337 3338 769 +a 3337 4106 1 +a 3338 3339 768 +a 3338 4106 1 +a 3339 3340 767 +a 3339 4106 1 +a 3340 3341 766 +a 3340 4106 1 +a 3341 3342 765 +a 3341 4106 1 +a 3342 3343 764 +a 3342 4106 1 +a 3343 3344 763 +a 3343 4106 1 +a 3344 3345 762 +a 3344 4106 1 +a 3345 3346 761 +a 3345 4106 1 +a 3346 3347 760 +a 3346 4106 1 +a 3347 3348 759 +a 3347 4106 1 +a 3348 3349 758 +a 3348 4106 1 +a 3349 3350 757 +a 3349 4106 1 +a 3350 3351 756 +a 3350 4106 1 +a 3351 3352 755 +a 3351 4106 1 +a 3352 3353 754 +a 3352 4106 1 +a 3353 3354 753 +a 3353 4106 1 +a 3354 3355 752 +a 3354 4106 1 +a 3355 3356 751 +a 3355 4106 1 +a 3356 3357 750 +a 3356 4106 1 +a 3357 3358 749 +a 3357 4106 1 +a 3358 3359 748 +a 3358 4106 1 +a 3359 3360 747 +a 3359 4106 1 +a 3360 3361 746 +a 3360 4106 1 +a 3361 3362 745 +a 3361 4106 1 +a 3362 3363 744 +a 3362 4106 1 +a 3363 3364 743 +a 3363 4106 1 +a 3364 3365 742 +a 3364 4106 1 +a 3365 3366 741 +a 3365 4106 1 +a 3366 3367 740 +a 3366 4106 1 +a 3367 3368 739 +a 3367 4106 1 +a 3368 3369 738 +a 3368 4106 1 +a 3369 3370 737 +a 3369 4106 1 +a 3370 3371 736 +a 3370 4106 1 +a 3371 3372 735 +a 3371 4106 1 +a 3372 3373 734 +a 3372 4106 1 +a 3373 3374 733 +a 3373 4106 1 +a 3374 3375 732 +a 3374 4106 1 +a 3375 3376 731 +a 3375 4106 1 +a 3376 3377 730 +a 3376 4106 1 +a 3377 3378 729 +a 3377 4106 1 +a 3378 3379 728 +a 3378 4106 1 +a 3379 3380 727 +a 3379 4106 1 +a 3380 3381 726 +a 3380 4106 1 +a 3381 3382 725 +a 3381 4106 1 +a 3382 3383 724 +a 3382 4106 1 +a 3383 3384 723 +a 3383 4106 1 +a 3384 3385 722 +a 3384 4106 1 +a 3385 3386 721 +a 3385 4106 1 +a 3386 3387 720 +a 3386 4106 1 +a 3387 3388 719 +a 3387 4106 1 +a 3388 3389 718 +a 3388 4106 1 +a 3389 3390 717 +a 3389 4106 1 +a 3390 3391 716 +a 3390 4106 1 +a 3391 3392 715 +a 3391 4106 1 +a 3392 3393 714 +a 3392 4106 1 +a 3393 3394 713 +a 3393 4106 1 +a 3394 3395 712 +a 3394 4106 1 +a 3395 3396 711 +a 3395 4106 1 +a 3396 3397 710 +a 3396 4106 1 +a 3397 3398 709 +a 3397 4106 1 +a 3398 3399 708 +a 3398 4106 1 +a 3399 3400 707 +a 3399 4106 1 +a 3400 3401 706 +a 3400 4106 1 +a 3401 3402 705 +a 3401 4106 1 +a 3402 3403 704 +a 3402 4106 1 +a 3403 3404 703 +a 3403 4106 1 +a 3404 3405 702 +a 3404 4106 1 +a 3405 3406 701 +a 3405 4106 1 +a 3406 3407 700 +a 3406 4106 1 +a 3407 3408 699 +a 3407 4106 1 +a 3408 3409 698 +a 3408 4106 1 +a 3409 3410 697 +a 3409 4106 1 +a 3410 3411 696 +a 3410 4106 1 +a 3411 3412 695 +a 3411 4106 1 +a 3412 3413 694 +a 3412 4106 1 +a 3413 3414 693 +a 3413 4106 1 +a 3414 3415 692 +a 3414 4106 1 +a 3415 3416 691 +a 3415 4106 1 +a 3416 3417 690 +a 3416 4106 1 +a 3417 3418 689 +a 3417 4106 1 +a 3418 3419 688 +a 3418 4106 1 +a 3419 3420 687 +a 3419 4106 1 +a 3420 3421 686 +a 3420 4106 1 +a 3421 3422 685 +a 3421 4106 1 +a 3422 3423 684 +a 3422 4106 1 +a 3423 3424 683 +a 3423 4106 1 +a 3424 3425 682 +a 3424 4106 1 +a 3425 3426 681 +a 3425 4106 1 +a 3426 3427 680 +a 3426 4106 1 +a 3427 3428 679 +a 3427 4106 1 +a 3428 3429 678 +a 3428 4106 1 +a 3429 3430 677 +a 3429 4106 1 +a 3430 3431 676 +a 3430 4106 1 +a 3431 3432 675 +a 3431 4106 1 +a 3432 3433 674 +a 3432 4106 1 +a 3433 3434 673 +a 3433 4106 1 +a 3434 3435 672 +a 3434 4106 1 +a 3435 3436 671 +a 3435 4106 1 +a 3436 3437 670 +a 3436 4106 1 +a 3437 3438 669 +a 3437 4106 1 +a 3438 3439 668 +a 3438 4106 1 +a 3439 3440 667 +a 3439 4106 1 +a 3440 3441 666 +a 3440 4106 1 +a 3441 3442 665 +a 3441 4106 1 +a 3442 3443 664 +a 3442 4106 1 +a 3443 3444 663 +a 3443 4106 1 +a 3444 3445 662 +a 3444 4106 1 +a 3445 3446 661 +a 3445 4106 1 +a 3446 3447 660 +a 3446 4106 1 +a 3447 3448 659 +a 3447 4106 1 +a 3448 3449 658 +a 3448 4106 1 +a 3449 3450 657 +a 3449 4106 1 +a 3450 3451 656 +a 3450 4106 1 +a 3451 3452 655 +a 3451 4106 1 +a 3452 3453 654 +a 3452 4106 1 +a 3453 3454 653 +a 3453 4106 1 +a 3454 3455 652 +a 3454 4106 1 +a 3455 3456 651 +a 3455 4106 1 +a 3456 3457 650 +a 3456 4106 1 +a 3457 3458 649 +a 3457 4106 1 +a 3458 3459 648 +a 3458 4106 1 +a 3459 3460 647 +a 3459 4106 1 +a 3460 3461 646 +a 3460 4106 1 +a 3461 3462 645 +a 3461 4106 1 +a 3462 3463 644 +a 3462 4106 1 +a 3463 3464 643 +a 3463 4106 1 +a 3464 3465 642 +a 3464 4106 1 +a 3465 3466 641 +a 3465 4106 1 +a 3466 3467 640 +a 3466 4106 1 +a 3467 3468 639 +a 3467 4106 1 +a 3468 3469 638 +a 3468 4106 1 +a 3469 3470 637 +a 3469 4106 1 +a 3470 3471 636 +a 3470 4106 1 +a 3471 3472 635 +a 3471 4106 1 +a 3472 3473 634 +a 3472 4106 1 +a 3473 3474 633 +a 3473 4106 1 +a 3474 3475 632 +a 3474 4106 1 +a 3475 3476 631 +a 3475 4106 1 +a 3476 3477 630 +a 3476 4106 1 +a 3477 3478 629 +a 3477 4106 1 +a 3478 3479 628 +a 3478 4106 1 +a 3479 3480 627 +a 3479 4106 1 +a 3480 3481 626 +a 3480 4106 1 +a 3481 3482 625 +a 3481 4106 1 +a 3482 3483 624 +a 3482 4106 1 +a 3483 3484 623 +a 3483 4106 1 +a 3484 3485 622 +a 3484 4106 1 +a 3485 3486 621 +a 3485 4106 1 +a 3486 3487 620 +a 3486 4106 1 +a 3487 3488 619 +a 3487 4106 1 +a 3488 3489 618 +a 3488 4106 1 +a 3489 3490 617 +a 3489 4106 1 +a 3490 3491 616 +a 3490 4106 1 +a 3491 3492 615 +a 3491 4106 1 +a 3492 3493 614 +a 3492 4106 1 +a 3493 3494 613 +a 3493 4106 1 +a 3494 3495 612 +a 3494 4106 1 +a 3495 3496 611 +a 3495 4106 1 +a 3496 3497 610 +a 3496 4106 1 +a 3497 3498 609 +a 3497 4106 1 +a 3498 3499 608 +a 3498 4106 1 +a 3499 3500 607 +a 3499 4106 1 +a 3500 3501 606 +a 3500 4106 1 +a 3501 3502 605 +a 3501 4106 1 +a 3502 3503 604 +a 3502 4106 1 +a 3503 3504 603 +a 3503 4106 1 +a 3504 3505 602 +a 3504 4106 1 +a 3505 3506 601 +a 3505 4106 1 +a 3506 3507 600 +a 3506 4106 1 +a 3507 3508 599 +a 3507 4106 1 +a 3508 3509 598 +a 3508 4106 1 +a 3509 3510 597 +a 3509 4106 1 +a 3510 3511 596 +a 3510 4106 1 +a 3511 3512 595 +a 3511 4106 1 +a 3512 3513 594 +a 3512 4106 1 +a 3513 3514 593 +a 3513 4106 1 +a 3514 3515 592 +a 3514 4106 1 +a 3515 3516 591 +a 3515 4106 1 +a 3516 3517 590 +a 3516 4106 1 +a 3517 3518 589 +a 3517 4106 1 +a 3518 3519 588 +a 3518 4106 1 +a 3519 3520 587 +a 3519 4106 1 +a 3520 3521 586 +a 3520 4106 1 +a 3521 3522 585 +a 3521 4106 1 +a 3522 3523 584 +a 3522 4106 1 +a 3523 3524 583 +a 3523 4106 1 +a 3524 3525 582 +a 3524 4106 1 +a 3525 3526 581 +a 3525 4106 1 +a 3526 3527 580 +a 3526 4106 1 +a 3527 3528 579 +a 3527 4106 1 +a 3528 3529 578 +a 3528 4106 1 +a 3529 3530 577 +a 3529 4106 1 +a 3530 3531 576 +a 3530 4106 1 +a 3531 3532 575 +a 3531 4106 1 +a 3532 3533 574 +a 3532 4106 1 +a 3533 3534 573 +a 3533 4106 1 +a 3534 3535 572 +a 3534 4106 1 +a 3535 3536 571 +a 3535 4106 1 +a 3536 3537 570 +a 3536 4106 1 +a 3537 3538 569 +a 3537 4106 1 +a 3538 3539 568 +a 3538 4106 1 +a 3539 3540 567 +a 3539 4106 1 +a 3540 3541 566 +a 3540 4106 1 +a 3541 3542 565 +a 3541 4106 1 +a 3542 3543 564 +a 3542 4106 1 +a 3543 3544 563 +a 3543 4106 1 +a 3544 3545 562 +a 3544 4106 1 +a 3545 3546 561 +a 3545 4106 1 +a 3546 3547 560 +a 3546 4106 1 +a 3547 3548 559 +a 3547 4106 1 +a 3548 3549 558 +a 3548 4106 1 +a 3549 3550 557 +a 3549 4106 1 +a 3550 3551 556 +a 3550 4106 1 +a 3551 3552 555 +a 3551 4106 1 +a 3552 3553 554 +a 3552 4106 1 +a 3553 3554 553 +a 3553 4106 1 +a 3554 3555 552 +a 3554 4106 1 +a 3555 3556 551 +a 3555 4106 1 +a 3556 3557 550 +a 3556 4106 1 +a 3557 3558 549 +a 3557 4106 1 +a 3558 3559 548 +a 3558 4106 1 +a 3559 3560 547 +a 3559 4106 1 +a 3560 3561 546 +a 3560 4106 1 +a 3561 3562 545 +a 3561 4106 1 +a 3562 3563 544 +a 3562 4106 1 +a 3563 3564 543 +a 3563 4106 1 +a 3564 3565 542 +a 3564 4106 1 +a 3565 3566 541 +a 3565 4106 1 +a 3566 3567 540 +a 3566 4106 1 +a 3567 3568 539 +a 3567 4106 1 +a 3568 3569 538 +a 3568 4106 1 +a 3569 3570 537 +a 3569 4106 1 +a 3570 3571 536 +a 3570 4106 1 +a 3571 3572 535 +a 3571 4106 1 +a 3572 3573 534 +a 3572 4106 1 +a 3573 3574 533 +a 3573 4106 1 +a 3574 3575 532 +a 3574 4106 1 +a 3575 3576 531 +a 3575 4106 1 +a 3576 3577 530 +a 3576 4106 1 +a 3577 3578 529 +a 3577 4106 1 +a 3578 3579 528 +a 3578 4106 1 +a 3579 3580 527 +a 3579 4106 1 +a 3580 3581 526 +a 3580 4106 1 +a 3581 3582 525 +a 3581 4106 1 +a 3582 3583 524 +a 3582 4106 1 +a 3583 3584 523 +a 3583 4106 1 +a 3584 3585 522 +a 3584 4106 1 +a 3585 3586 521 +a 3585 4106 1 +a 3586 3587 520 +a 3586 4106 1 +a 3587 3588 519 +a 3587 4106 1 +a 3588 3589 518 +a 3588 4106 1 +a 3589 3590 517 +a 3589 4106 1 +a 3590 3591 516 +a 3590 4106 1 +a 3591 3592 515 +a 3591 4106 1 +a 3592 3593 514 +a 3592 4106 1 +a 3593 3594 513 +a 3593 4106 1 +a 3594 3595 512 +a 3594 4106 1 +a 3595 3596 511 +a 3595 4106 1 +a 3596 3597 510 +a 3596 4106 1 +a 3597 3598 509 +a 3597 4106 1 +a 3598 3599 508 +a 3598 4106 1 +a 3599 3600 507 +a 3599 4106 1 +a 3600 3601 506 +a 3600 4106 1 +a 3601 3602 505 +a 3601 4106 1 +a 3602 3603 504 +a 3602 4106 1 +a 3603 3604 503 +a 3603 4106 1 +a 3604 3605 502 +a 3604 4106 1 +a 3605 3606 501 +a 3605 4106 1 +a 3606 3607 500 +a 3606 4106 1 +a 3607 3608 499 +a 3607 4106 1 +a 3608 3609 498 +a 3608 4106 1 +a 3609 3610 497 +a 3609 4106 1 +a 3610 3611 496 +a 3610 4106 1 +a 3611 3612 495 +a 3611 4106 1 +a 3612 3613 494 +a 3612 4106 1 +a 3613 3614 493 +a 3613 4106 1 +a 3614 3615 492 +a 3614 4106 1 +a 3615 3616 491 +a 3615 4106 1 +a 3616 3617 490 +a 3616 4106 1 +a 3617 3618 489 +a 3617 4106 1 +a 3618 3619 488 +a 3618 4106 1 +a 3619 3620 487 +a 3619 4106 1 +a 3620 3621 486 +a 3620 4106 1 +a 3621 3622 485 +a 3621 4106 1 +a 3622 3623 484 +a 3622 4106 1 +a 3623 3624 483 +a 3623 4106 1 +a 3624 3625 482 +a 3624 4106 1 +a 3625 3626 481 +a 3625 4106 1 +a 3626 3627 480 +a 3626 4106 1 +a 3627 3628 479 +a 3627 4106 1 +a 3628 3629 478 +a 3628 4106 1 +a 3629 3630 477 +a 3629 4106 1 +a 3630 3631 476 +a 3630 4106 1 +a 3631 3632 475 +a 3631 4106 1 +a 3632 3633 474 +a 3632 4106 1 +a 3633 3634 473 +a 3633 4106 1 +a 3634 3635 472 +a 3634 4106 1 +a 3635 3636 471 +a 3635 4106 1 +a 3636 3637 470 +a 3636 4106 1 +a 3637 3638 469 +a 3637 4106 1 +a 3638 3639 468 +a 3638 4106 1 +a 3639 3640 467 +a 3639 4106 1 +a 3640 3641 466 +a 3640 4106 1 +a 3641 3642 465 +a 3641 4106 1 +a 3642 3643 464 +a 3642 4106 1 +a 3643 3644 463 +a 3643 4106 1 +a 3644 3645 462 +a 3644 4106 1 +a 3645 3646 461 +a 3645 4106 1 +a 3646 3647 460 +a 3646 4106 1 +a 3647 3648 459 +a 3647 4106 1 +a 3648 3649 458 +a 3648 4106 1 +a 3649 3650 457 +a 3649 4106 1 +a 3650 3651 456 +a 3650 4106 1 +a 3651 3652 455 +a 3651 4106 1 +a 3652 3653 454 +a 3652 4106 1 +a 3653 3654 453 +a 3653 4106 1 +a 3654 3655 452 +a 3654 4106 1 +a 3655 3656 451 +a 3655 4106 1 +a 3656 3657 450 +a 3656 4106 1 +a 3657 3658 449 +a 3657 4106 1 +a 3658 3659 448 +a 3658 4106 1 +a 3659 3660 447 +a 3659 4106 1 +a 3660 3661 446 +a 3660 4106 1 +a 3661 3662 445 +a 3661 4106 1 +a 3662 3663 444 +a 3662 4106 1 +a 3663 3664 443 +a 3663 4106 1 +a 3664 3665 442 +a 3664 4106 1 +a 3665 3666 441 +a 3665 4106 1 +a 3666 3667 440 +a 3666 4106 1 +a 3667 3668 439 +a 3667 4106 1 +a 3668 3669 438 +a 3668 4106 1 +a 3669 3670 437 +a 3669 4106 1 +a 3670 3671 436 +a 3670 4106 1 +a 3671 3672 435 +a 3671 4106 1 +a 3672 3673 434 +a 3672 4106 1 +a 3673 3674 433 +a 3673 4106 1 +a 3674 3675 432 +a 3674 4106 1 +a 3675 3676 431 +a 3675 4106 1 +a 3676 3677 430 +a 3676 4106 1 +a 3677 3678 429 +a 3677 4106 1 +a 3678 3679 428 +a 3678 4106 1 +a 3679 3680 427 +a 3679 4106 1 +a 3680 3681 426 +a 3680 4106 1 +a 3681 3682 425 +a 3681 4106 1 +a 3682 3683 424 +a 3682 4106 1 +a 3683 3684 423 +a 3683 4106 1 +a 3684 3685 422 +a 3684 4106 1 +a 3685 3686 421 +a 3685 4106 1 +a 3686 3687 420 +a 3686 4106 1 +a 3687 3688 419 +a 3687 4106 1 +a 3688 3689 418 +a 3688 4106 1 +a 3689 3690 417 +a 3689 4106 1 +a 3690 3691 416 +a 3690 4106 1 +a 3691 3692 415 +a 3691 4106 1 +a 3692 3693 414 +a 3692 4106 1 +a 3693 3694 413 +a 3693 4106 1 +a 3694 3695 412 +a 3694 4106 1 +a 3695 3696 411 +a 3695 4106 1 +a 3696 3697 410 +a 3696 4106 1 +a 3697 3698 409 +a 3697 4106 1 +a 3698 3699 408 +a 3698 4106 1 +a 3699 3700 407 +a 3699 4106 1 +a 3700 3701 406 +a 3700 4106 1 +a 3701 3702 405 +a 3701 4106 1 +a 3702 3703 404 +a 3702 4106 1 +a 3703 3704 403 +a 3703 4106 1 +a 3704 3705 402 +a 3704 4106 1 +a 3705 3706 401 +a 3705 4106 1 +a 3706 3707 400 +a 3706 4106 1 +a 3707 3708 399 +a 3707 4106 1 +a 3708 3709 398 +a 3708 4106 1 +a 3709 3710 397 +a 3709 4106 1 +a 3710 3711 396 +a 3710 4106 1 +a 3711 3712 395 +a 3711 4106 1 +a 3712 3713 394 +a 3712 4106 1 +a 3713 3714 393 +a 3713 4106 1 +a 3714 3715 392 +a 3714 4106 1 +a 3715 3716 391 +a 3715 4106 1 +a 3716 3717 390 +a 3716 4106 1 +a 3717 3718 389 +a 3717 4106 1 +a 3718 3719 388 +a 3718 4106 1 +a 3719 3720 387 +a 3719 4106 1 +a 3720 3721 386 +a 3720 4106 1 +a 3721 3722 385 +a 3721 4106 1 +a 3722 3723 384 +a 3722 4106 1 +a 3723 3724 383 +a 3723 4106 1 +a 3724 3725 382 +a 3724 4106 1 +a 3725 3726 381 +a 3725 4106 1 +a 3726 3727 380 +a 3726 4106 1 +a 3727 3728 379 +a 3727 4106 1 +a 3728 3729 378 +a 3728 4106 1 +a 3729 3730 377 +a 3729 4106 1 +a 3730 3731 376 +a 3730 4106 1 +a 3731 3732 375 +a 3731 4106 1 +a 3732 3733 374 +a 3732 4106 1 +a 3733 3734 373 +a 3733 4106 1 +a 3734 3735 372 +a 3734 4106 1 +a 3735 3736 371 +a 3735 4106 1 +a 3736 3737 370 +a 3736 4106 1 +a 3737 3738 369 +a 3737 4106 1 +a 3738 3739 368 +a 3738 4106 1 +a 3739 3740 367 +a 3739 4106 1 +a 3740 3741 366 +a 3740 4106 1 +a 3741 3742 365 +a 3741 4106 1 +a 3742 3743 364 +a 3742 4106 1 +a 3743 3744 363 +a 3743 4106 1 +a 3744 3745 362 +a 3744 4106 1 +a 3745 3746 361 +a 3745 4106 1 +a 3746 3747 360 +a 3746 4106 1 +a 3747 3748 359 +a 3747 4106 1 +a 3748 3749 358 +a 3748 4106 1 +a 3749 3750 357 +a 3749 4106 1 +a 3750 3751 356 +a 3750 4106 1 +a 3751 3752 355 +a 3751 4106 1 +a 3752 3753 354 +a 3752 4106 1 +a 3753 3754 353 +a 3753 4106 1 +a 3754 3755 352 +a 3754 4106 1 +a 3755 3756 351 +a 3755 4106 1 +a 3756 3757 350 +a 3756 4106 1 +a 3757 3758 349 +a 3757 4106 1 +a 3758 3759 348 +a 3758 4106 1 +a 3759 3760 347 +a 3759 4106 1 +a 3760 3761 346 +a 3760 4106 1 +a 3761 3762 345 +a 3761 4106 1 +a 3762 3763 344 +a 3762 4106 1 +a 3763 3764 343 +a 3763 4106 1 +a 3764 3765 342 +a 3764 4106 1 +a 3765 3766 341 +a 3765 4106 1 +a 3766 3767 340 +a 3766 4106 1 +a 3767 3768 339 +a 3767 4106 1 +a 3768 3769 338 +a 3768 4106 1 +a 3769 3770 337 +a 3769 4106 1 +a 3770 3771 336 +a 3770 4106 1 +a 3771 3772 335 +a 3771 4106 1 +a 3772 3773 334 +a 3772 4106 1 +a 3773 3774 333 +a 3773 4106 1 +a 3774 3775 332 +a 3774 4106 1 +a 3775 3776 331 +a 3775 4106 1 +a 3776 3777 330 +a 3776 4106 1 +a 3777 3778 329 +a 3777 4106 1 +a 3778 3779 328 +a 3778 4106 1 +a 3779 3780 327 +a 3779 4106 1 +a 3780 3781 326 +a 3780 4106 1 +a 3781 3782 325 +a 3781 4106 1 +a 3782 3783 324 +a 3782 4106 1 +a 3783 3784 323 +a 3783 4106 1 +a 3784 3785 322 +a 3784 4106 1 +a 3785 3786 321 +a 3785 4106 1 +a 3786 3787 320 +a 3786 4106 1 +a 3787 3788 319 +a 3787 4106 1 +a 3788 3789 318 +a 3788 4106 1 +a 3789 3790 317 +a 3789 4106 1 +a 3790 3791 316 +a 3790 4106 1 +a 3791 3792 315 +a 3791 4106 1 +a 3792 3793 314 +a 3792 4106 1 +a 3793 3794 313 +a 3793 4106 1 +a 3794 3795 312 +a 3794 4106 1 +a 3795 3796 311 +a 3795 4106 1 +a 3796 3797 310 +a 3796 4106 1 +a 3797 3798 309 +a 3797 4106 1 +a 3798 3799 308 +a 3798 4106 1 +a 3799 3800 307 +a 3799 4106 1 +a 3800 3801 306 +a 3800 4106 1 +a 3801 3802 305 +a 3801 4106 1 +a 3802 3803 304 +a 3802 4106 1 +a 3803 3804 303 +a 3803 4106 1 +a 3804 3805 302 +a 3804 4106 1 +a 3805 3806 301 +a 3805 4106 1 +a 3806 3807 300 +a 3806 4106 1 +a 3807 3808 299 +a 3807 4106 1 +a 3808 3809 298 +a 3808 4106 1 +a 3809 3810 297 +a 3809 4106 1 +a 3810 3811 296 +a 3810 4106 1 +a 3811 3812 295 +a 3811 4106 1 +a 3812 3813 294 +a 3812 4106 1 +a 3813 3814 293 +a 3813 4106 1 +a 3814 3815 292 +a 3814 4106 1 +a 3815 3816 291 +a 3815 4106 1 +a 3816 3817 290 +a 3816 4106 1 +a 3817 3818 289 +a 3817 4106 1 +a 3818 3819 288 +a 3818 4106 1 +a 3819 3820 287 +a 3819 4106 1 +a 3820 3821 286 +a 3820 4106 1 +a 3821 3822 285 +a 3821 4106 1 +a 3822 3823 284 +a 3822 4106 1 +a 3823 3824 283 +a 3823 4106 1 +a 3824 3825 282 +a 3824 4106 1 +a 3825 3826 281 +a 3825 4106 1 +a 3826 3827 280 +a 3826 4106 1 +a 3827 3828 279 +a 3827 4106 1 +a 3828 3829 278 +a 3828 4106 1 +a 3829 3830 277 +a 3829 4106 1 +a 3830 3831 276 +a 3830 4106 1 +a 3831 3832 275 +a 3831 4106 1 +a 3832 3833 274 +a 3832 4106 1 +a 3833 3834 273 +a 3833 4106 1 +a 3834 3835 272 +a 3834 4106 1 +a 3835 3836 271 +a 3835 4106 1 +a 3836 3837 270 +a 3836 4106 1 +a 3837 3838 269 +a 3837 4106 1 +a 3838 3839 268 +a 3838 4106 1 +a 3839 3840 267 +a 3839 4106 1 +a 3840 3841 266 +a 3840 4106 1 +a 3841 3842 265 +a 3841 4106 1 +a 3842 3843 264 +a 3842 4106 1 +a 3843 3844 263 +a 3843 4106 1 +a 3844 3845 262 +a 3844 4106 1 +a 3845 3846 261 +a 3845 4106 1 +a 3846 3847 260 +a 3846 4106 1 +a 3847 3848 259 +a 3847 4106 1 +a 3848 3849 258 +a 3848 4106 1 +a 3849 3850 257 +a 3849 4106 1 +a 3850 3851 256 +a 3850 4106 1 +a 3851 3852 255 +a 3851 4106 1 +a 3852 3853 254 +a 3852 4106 1 +a 3853 3854 253 +a 3853 4106 1 +a 3854 3855 252 +a 3854 4106 1 +a 3855 3856 251 +a 3855 4106 1 +a 3856 3857 250 +a 3856 4106 1 +a 3857 3858 249 +a 3857 4106 1 +a 3858 3859 248 +a 3858 4106 1 +a 3859 3860 247 +a 3859 4106 1 +a 3860 3861 246 +a 3860 4106 1 +a 3861 3862 245 +a 3861 4106 1 +a 3862 3863 244 +a 3862 4106 1 +a 3863 3864 243 +a 3863 4106 1 +a 3864 3865 242 +a 3864 4106 1 +a 3865 3866 241 +a 3865 4106 1 +a 3866 3867 240 +a 3866 4106 1 +a 3867 3868 239 +a 3867 4106 1 +a 3868 3869 238 +a 3868 4106 1 +a 3869 3870 237 +a 3869 4106 1 +a 3870 3871 236 +a 3870 4106 1 +a 3871 3872 235 +a 3871 4106 1 +a 3872 3873 234 +a 3872 4106 1 +a 3873 3874 233 +a 3873 4106 1 +a 3874 3875 232 +a 3874 4106 1 +a 3875 3876 231 +a 3875 4106 1 +a 3876 3877 230 +a 3876 4106 1 +a 3877 3878 229 +a 3877 4106 1 +a 3878 3879 228 +a 3878 4106 1 +a 3879 3880 227 +a 3879 4106 1 +a 3880 3881 226 +a 3880 4106 1 +a 3881 3882 225 +a 3881 4106 1 +a 3882 3883 224 +a 3882 4106 1 +a 3883 3884 223 +a 3883 4106 1 +a 3884 3885 222 +a 3884 4106 1 +a 3885 3886 221 +a 3885 4106 1 +a 3886 3887 220 +a 3886 4106 1 +a 3887 3888 219 +a 3887 4106 1 +a 3888 3889 218 +a 3888 4106 1 +a 3889 3890 217 +a 3889 4106 1 +a 3890 3891 216 +a 3890 4106 1 +a 3891 3892 215 +a 3891 4106 1 +a 3892 3893 214 +a 3892 4106 1 +a 3893 3894 213 +a 3893 4106 1 +a 3894 3895 212 +a 3894 4106 1 +a 3895 3896 211 +a 3895 4106 1 +a 3896 3897 210 +a 3896 4106 1 +a 3897 3898 209 +a 3897 4106 1 +a 3898 3899 208 +a 3898 4106 1 +a 3899 3900 207 +a 3899 4106 1 +a 3900 3901 206 +a 3900 4106 1 +a 3901 3902 205 +a 3901 4106 1 +a 3902 3903 204 +a 3902 4106 1 +a 3903 3904 203 +a 3903 4106 1 +a 3904 3905 202 +a 3904 4106 1 +a 3905 3906 201 +a 3905 4106 1 +a 3906 3907 200 +a 3906 4106 1 +a 3907 3908 199 +a 3907 4106 1 +a 3908 3909 198 +a 3908 4106 1 +a 3909 3910 197 +a 3909 4106 1 +a 3910 3911 196 +a 3910 4106 1 +a 3911 3912 195 +a 3911 4106 1 +a 3912 3913 194 +a 3912 4106 1 +a 3913 3914 193 +a 3913 4106 1 +a 3914 3915 192 +a 3914 4106 1 +a 3915 3916 191 +a 3915 4106 1 +a 3916 3917 190 +a 3916 4106 1 +a 3917 3918 189 +a 3917 4106 1 +a 3918 3919 188 +a 3918 4106 1 +a 3919 3920 187 +a 3919 4106 1 +a 3920 3921 186 +a 3920 4106 1 +a 3921 3922 185 +a 3921 4106 1 +a 3922 3923 184 +a 3922 4106 1 +a 3923 3924 183 +a 3923 4106 1 +a 3924 3925 182 +a 3924 4106 1 +a 3925 3926 181 +a 3925 4106 1 +a 3926 3927 180 +a 3926 4106 1 +a 3927 3928 179 +a 3927 4106 1 +a 3928 3929 178 +a 3928 4106 1 +a 3929 3930 177 +a 3929 4106 1 +a 3930 3931 176 +a 3930 4106 1 +a 3931 3932 175 +a 3931 4106 1 +a 3932 3933 174 +a 3932 4106 1 +a 3933 3934 173 +a 3933 4106 1 +a 3934 3935 172 +a 3934 4106 1 +a 3935 3936 171 +a 3935 4106 1 +a 3936 3937 170 +a 3936 4106 1 +a 3937 3938 169 +a 3937 4106 1 +a 3938 3939 168 +a 3938 4106 1 +a 3939 3940 167 +a 3939 4106 1 +a 3940 3941 166 +a 3940 4106 1 +a 3941 3942 165 +a 3941 4106 1 +a 3942 3943 164 +a 3942 4106 1 +a 3943 3944 163 +a 3943 4106 1 +a 3944 3945 162 +a 3944 4106 1 +a 3945 3946 161 +a 3945 4106 1 +a 3946 3947 160 +a 3946 4106 1 +a 3947 3948 159 +a 3947 4106 1 +a 3948 3949 158 +a 3948 4106 1 +a 3949 3950 157 +a 3949 4106 1 +a 3950 3951 156 +a 3950 4106 1 +a 3951 3952 155 +a 3951 4106 1 +a 3952 3953 154 +a 3952 4106 1 +a 3953 3954 153 +a 3953 4106 1 +a 3954 3955 152 +a 3954 4106 1 +a 3955 3956 151 +a 3955 4106 1 +a 3956 3957 150 +a 3956 4106 1 +a 3957 3958 149 +a 3957 4106 1 +a 3958 3959 148 +a 3958 4106 1 +a 3959 3960 147 +a 3959 4106 1 +a 3960 3961 146 +a 3960 4106 1 +a 3961 3962 145 +a 3961 4106 1 +a 3962 3963 144 +a 3962 4106 1 +a 3963 3964 143 +a 3963 4106 1 +a 3964 3965 142 +a 3964 4106 1 +a 3965 3966 141 +a 3965 4106 1 +a 3966 3967 140 +a 3966 4106 1 +a 3967 3968 139 +a 3967 4106 1 +a 3968 3969 138 +a 3968 4106 1 +a 3969 3970 137 +a 3969 4106 1 +a 3970 3971 136 +a 3970 4106 1 +a 3971 3972 135 +a 3971 4106 1 +a 3972 3973 134 +a 3972 4106 1 +a 3973 3974 133 +a 3973 4106 1 +a 3974 3975 132 +a 3974 4106 1 +a 3975 3976 131 +a 3975 4106 1 +a 3976 3977 130 +a 3976 4106 1 +a 3977 3978 129 +a 3977 4106 1 +a 3978 3979 128 +a 3978 4106 1 +a 3979 3980 127 +a 3979 4106 1 +a 3980 3981 126 +a 3980 4106 1 +a 3981 3982 125 +a 3981 4106 1 +a 3982 3983 124 +a 3982 4106 1 +a 3983 3984 123 +a 3983 4106 1 +a 3984 3985 122 +a 3984 4106 1 +a 3985 3986 121 +a 3985 4106 1 +a 3986 3987 120 +a 3986 4106 1 +a 3987 3988 119 +a 3987 4106 1 +a 3988 3989 118 +a 3988 4106 1 +a 3989 3990 117 +a 3989 4106 1 +a 3990 3991 116 +a 3990 4106 1 +a 3991 3992 115 +a 3991 4106 1 +a 3992 3993 114 +a 3992 4106 1 +a 3993 3994 113 +a 3993 4106 1 +a 3994 3995 112 +a 3994 4106 1 +a 3995 3996 111 +a 3995 4106 1 +a 3996 3997 110 +a 3996 4106 1 +a 3997 3998 109 +a 3997 4106 1 +a 3998 3999 108 +a 3998 4106 1 +a 3999 4000 107 +a 3999 4106 1 +a 4000 4001 106 +a 4000 4106 1 +a 4001 4002 105 +a 4001 4106 1 +a 4002 4003 104 +a 4002 4106 1 +a 4003 4004 103 +a 4003 4106 1 +a 4004 4005 102 +a 4004 4106 1 +a 4005 4006 101 +a 4005 4106 1 +a 4006 4007 100 +a 4006 4106 1 +a 4007 4008 99 +a 4007 4106 1 +a 4008 4009 98 +a 4008 4106 1 +a 4009 4010 97 +a 4009 4106 1 +a 4010 4011 96 +a 4010 4106 1 +a 4011 4012 95 +a 4011 4106 1 +a 4012 4013 94 +a 4012 4106 1 +a 4013 4014 93 +a 4013 4106 1 +a 4014 4015 92 +a 4014 4106 1 +a 4015 4016 91 +a 4015 4106 1 +a 4016 4017 90 +a 4016 4106 1 +a 4017 4018 89 +a 4017 4106 1 +a 4018 4019 88 +a 4018 4106 1 +a 4019 4020 87 +a 4019 4106 1 +a 4020 4021 86 +a 4020 4106 1 +a 4021 4022 85 +a 4021 4106 1 +a 4022 4023 84 +a 4022 4106 1 +a 4023 4024 83 +a 4023 4106 1 +a 4024 4025 82 +a 4024 4106 1 +a 4025 4026 81 +a 4025 4106 1 +a 4026 4027 80 +a 4026 4106 1 +a 4027 4028 79 +a 4027 4106 1 +a 4028 4029 78 +a 4028 4106 1 +a 4029 4030 77 +a 4029 4106 1 +a 4030 4031 76 +a 4030 4106 1 +a 4031 4032 75 +a 4031 4106 1 +a 4032 4033 74 +a 4032 4106 1 +a 4033 4034 73 +a 4033 4106 1 +a 4034 4035 72 +a 4034 4106 1 +a 4035 4036 71 +a 4035 4106 1 +a 4036 4037 70 +a 4036 4106 1 +a 4037 4038 69 +a 4037 4106 1 +a 4038 4039 68 +a 4038 4106 1 +a 4039 4040 67 +a 4039 4106 1 +a 4040 4041 66 +a 4040 4106 1 +a 4041 4042 65 +a 4041 4106 1 +a 4042 4043 64 +a 4042 4106 1 +a 4043 4044 63 +a 4043 4106 1 +a 4044 4045 62 +a 4044 4106 1 +a 4045 4046 61 +a 4045 4106 1 +a 4046 4047 60 +a 4046 4106 1 +a 4047 4048 59 +a 4047 4106 1 +a 4048 4049 58 +a 4048 4106 1 +a 4049 4050 57 +a 4049 4106 1 +a 4050 4051 56 +a 4050 4106 1 +a 4051 4052 55 +a 4051 4106 1 +a 4052 4053 54 +a 4052 4106 1 +a 4053 4054 53 +a 4053 4106 1 +a 4054 4055 52 +a 4054 4106 1 +a 4055 4056 51 +a 4055 4106 1 +a 4056 4057 50 +a 4056 4106 1 +a 4057 4058 49 +a 4057 4106 1 +a 4058 4059 48 +a 4058 4106 1 +a 4059 4060 47 +a 4059 4106 1 +a 4060 4061 46 +a 4060 4106 1 +a 4061 4062 45 +a 4061 4106 1 +a 4062 4063 44 +a 4062 4106 1 +a 4063 4064 43 +a 4063 4106 1 +a 4064 4065 42 +a 4064 4106 1 +a 4065 4066 41 +a 4065 4106 1 +a 4066 4067 40 +a 4066 4106 1 +a 4067 4068 39 +a 4067 4106 1 +a 4068 4069 38 +a 4068 4106 1 +a 4069 4070 37 +a 4069 4106 1 +a 4070 4071 36 +a 4070 4106 1 +a 4071 4072 35 +a 4071 4106 1 +a 4072 4073 34 +a 4072 4106 1 +a 4073 4074 33 +a 4073 4106 1 +a 4074 4075 32 +a 4074 4106 1 +a 4075 4076 31 +a 4075 4106 1 +a 4076 4077 30 +a 4076 4106 1 +a 4077 4078 29 +a 4077 4106 1 +a 4078 4079 28 +a 4078 4106 1 +a 4079 4080 27 +a 4079 4106 1 +a 4080 4081 26 +a 4080 4106 1 +a 4081 4082 25 +a 4081 4106 1 +a 4082 4083 24 +a 4082 4106 1 +a 4083 4084 23 +a 4083 4106 1 +a 4084 4085 22 +a 4084 4106 1 +a 4085 4086 21 +a 4085 4106 1 +a 4086 4087 20 +a 4086 4106 1 +a 4087 4088 19 +a 4087 4106 1 +a 4088 4089 18 +a 4088 4106 1 +a 4089 4090 17 +a 4089 4106 1 +a 4090 4091 16 +a 4090 4106 1 +a 4091 4092 15 +a 4091 4106 1 +a 4092 4093 14 +a 4092 4106 1 +a 4093 4094 13 +a 4093 4106 1 +a 4094 4095 12 +a 4094 4106 1 +a 4095 4096 11 +a 4095 4106 1 +a 4096 4097 10 +a 4096 4106 1 +a 4097 4098 9 +a 4097 4106 1 +a 4098 4099 8 +a 4098 4106 1 +a 4099 4100 7 +a 4099 4106 1 +a 4100 4101 6 +a 4100 4106 1 +a 4101 4102 5 +a 4101 4106 1 +a 4102 4103 4 +a 4102 4106 1 +a 4103 4104 3 +a 4103 4106 1 +a 4104 4105 2 +a 4104 4106 1 +a 4105 8208 1 +a 4105 4106 1 +a 4106 4107 4103 +a 4107 4108 4103 +a 4108 4109 4103 +a 4109 4110 4103 +a 4110 4111 4103 +a 4111 4112 4103 +a 4112 4113 4103 +a 4113 4114 4103 +a 4114 4115 4103 +a 4115 4116 4103 +a 4116 4117 4103 +a 4117 4118 4103 +a 4118 4119 4103 +a 4119 4120 4103 +a 4120 4121 4103 +a 4121 4122 4103 +a 4122 4123 4103 +a 4123 4124 4103 +a 4124 4125 4103 +a 4125 4126 4103 +a 4126 4127 4103 +a 4127 4128 4103 +a 4128 4129 4103 +a 4129 4130 4103 +a 4130 4131 4103 +a 4131 4132 4103 +a 4132 4133 4103 +a 4133 4134 4103 +a 4134 4135 4103 +a 4135 4136 4103 +a 4136 4137 4103 +a 4137 4138 4103 +a 4138 4139 4103 +a 4139 4140 4103 +a 4140 4141 4103 +a 4141 4142 4103 +a 4142 4143 4103 +a 4143 4144 4103 +a 4144 4145 4103 +a 4145 4146 4103 +a 4146 4147 4103 +a 4147 4148 4103 +a 4148 4149 4103 +a 4149 4150 4103 +a 4150 4151 4103 +a 4151 4152 4103 +a 4152 4153 4103 +a 4153 4154 4103 +a 4154 4155 4103 +a 4155 4156 4103 +a 4156 4157 4103 +a 4157 4158 4103 +a 4158 4159 4103 +a 4159 4160 4103 +a 4160 4161 4103 +a 4161 4162 4103 +a 4162 4163 4103 +a 4163 4164 4103 +a 4164 4165 4103 +a 4165 4166 4103 +a 4166 4167 4103 +a 4167 4168 4103 +a 4168 4169 4103 +a 4169 4170 4103 +a 4170 4171 4103 +a 4171 4172 4103 +a 4172 4173 4103 +a 4173 4174 4103 +a 4174 4175 4103 +a 4175 4176 4103 +a 4176 4177 4103 +a 4177 4178 4103 +a 4178 4179 4103 +a 4179 4180 4103 +a 4180 4181 4103 +a 4181 4182 4103 +a 4182 4183 4103 +a 4183 4184 4103 +a 4184 4185 4103 +a 4185 4186 4103 +a 4186 4187 4103 +a 4187 4188 4103 +a 4188 4189 4103 +a 4189 4190 4103 +a 4190 4191 4103 +a 4191 4192 4103 +a 4192 4193 4103 +a 4193 4194 4103 +a 4194 4195 4103 +a 4195 4196 4103 +a 4196 4197 4103 +a 4197 4198 4103 +a 4198 4199 4103 +a 4199 4200 4103 +a 4200 4201 4103 +a 4201 4202 4103 +a 4202 4203 4103 +a 4203 4204 4103 +a 4204 4205 4103 +a 4205 4206 4103 +a 4206 4207 4103 +a 4207 4208 4103 +a 4208 4209 4103 +a 4209 4210 4103 +a 4210 4211 4103 +a 4211 4212 4103 +a 4212 4213 4103 +a 4213 4214 4103 +a 4214 4215 4103 +a 4215 4216 4103 +a 4216 4217 4103 +a 4217 4218 4103 +a 4218 4219 4103 +a 4219 4220 4103 +a 4220 4221 4103 +a 4221 4222 4103 +a 4222 4223 4103 +a 4223 4224 4103 +a 4224 4225 4103 +a 4225 4226 4103 +a 4226 4227 4103 +a 4227 4228 4103 +a 4228 4229 4103 +a 4229 4230 4103 +a 4230 4231 4103 +a 4231 4232 4103 +a 4232 4233 4103 +a 4233 4234 4103 +a 4234 4235 4103 +a 4235 4236 4103 +a 4236 4237 4103 +a 4237 4238 4103 +a 4238 4239 4103 +a 4239 4240 4103 +a 4240 4241 4103 +a 4241 4242 4103 +a 4242 4243 4103 +a 4243 4244 4103 +a 4244 4245 4103 +a 4245 4246 4103 +a 4246 4247 4103 +a 4247 4248 4103 +a 4248 4249 4103 +a 4249 4250 4103 +a 4250 4251 4103 +a 4251 4252 4103 +a 4252 4253 4103 +a 4253 4254 4103 +a 4254 4255 4103 +a 4255 4256 4103 +a 4256 4257 4103 +a 4257 4258 4103 +a 4258 4259 4103 +a 4259 4260 4103 +a 4260 4261 4103 +a 4261 4262 4103 +a 4262 4263 4103 +a 4263 4264 4103 +a 4264 4265 4103 +a 4265 4266 4103 +a 4266 4267 4103 +a 4267 4268 4103 +a 4268 4269 4103 +a 4269 4270 4103 +a 4270 4271 4103 +a 4271 4272 4103 +a 4272 4273 4103 +a 4273 4274 4103 +a 4274 4275 4103 +a 4275 4276 4103 +a 4276 4277 4103 +a 4277 4278 4103 +a 4278 4279 4103 +a 4279 4280 4103 +a 4280 4281 4103 +a 4281 4282 4103 +a 4282 4283 4103 +a 4283 4284 4103 +a 4284 4285 4103 +a 4285 4286 4103 +a 4286 4287 4103 +a 4287 4288 4103 +a 4288 4289 4103 +a 4289 4290 4103 +a 4290 4291 4103 +a 4291 4292 4103 +a 4292 4293 4103 +a 4293 4294 4103 +a 4294 4295 4103 +a 4295 4296 4103 +a 4296 4297 4103 +a 4297 4298 4103 +a 4298 4299 4103 +a 4299 4300 4103 +a 4300 4301 4103 +a 4301 4302 4103 +a 4302 4303 4103 +a 4303 4304 4103 +a 4304 4305 4103 +a 4305 4306 4103 +a 4306 4307 4103 +a 4307 4308 4103 +a 4308 4309 4103 +a 4309 4310 4103 +a 4310 4311 4103 +a 4311 4312 4103 +a 4312 4313 4103 +a 4313 4314 4103 +a 4314 4315 4103 +a 4315 4316 4103 +a 4316 4317 4103 +a 4317 4318 4103 +a 4318 4319 4103 +a 4319 4320 4103 +a 4320 4321 4103 +a 4321 4322 4103 +a 4322 4323 4103 +a 4323 4324 4103 +a 4324 4325 4103 +a 4325 4326 4103 +a 4326 4327 4103 +a 4327 4328 4103 +a 4328 4329 4103 +a 4329 4330 4103 +a 4330 4331 4103 +a 4331 4332 4103 +a 4332 4333 4103 +a 4333 4334 4103 +a 4334 4335 4103 +a 4335 4336 4103 +a 4336 4337 4103 +a 4337 4338 4103 +a 4338 4339 4103 +a 4339 4340 4103 +a 4340 4341 4103 +a 4341 4342 4103 +a 4342 4343 4103 +a 4343 4344 4103 +a 4344 4345 4103 +a 4345 4346 4103 +a 4346 4347 4103 +a 4347 4348 4103 +a 4348 4349 4103 +a 4349 4350 4103 +a 4350 4351 4103 +a 4351 4352 4103 +a 4352 4353 4103 +a 4353 4354 4103 +a 4354 4355 4103 +a 4355 4356 4103 +a 4356 4357 4103 +a 4357 4358 4103 +a 4358 4359 4103 +a 4359 4360 4103 +a 4360 4361 4103 +a 4361 4362 4103 +a 4362 4363 4103 +a 4363 4364 4103 +a 4364 4365 4103 +a 4365 4366 4103 +a 4366 4367 4103 +a 4367 4368 4103 +a 4368 4369 4103 +a 4369 4370 4103 +a 4370 4371 4103 +a 4371 4372 4103 +a 4372 4373 4103 +a 4373 4374 4103 +a 4374 4375 4103 +a 4375 4376 4103 +a 4376 4377 4103 +a 4377 4378 4103 +a 4378 4379 4103 +a 4379 4380 4103 +a 4380 4381 4103 +a 4381 4382 4103 +a 4382 4383 4103 +a 4383 4384 4103 +a 4384 4385 4103 +a 4385 4386 4103 +a 4386 4387 4103 +a 4387 4388 4103 +a 4388 4389 4103 +a 4389 4390 4103 +a 4390 4391 4103 +a 4391 4392 4103 +a 4392 4393 4103 +a 4393 4394 4103 +a 4394 4395 4103 +a 4395 4396 4103 +a 4396 4397 4103 +a 4397 4398 4103 +a 4398 4399 4103 +a 4399 4400 4103 +a 4400 4401 4103 +a 4401 4402 4103 +a 4402 4403 4103 +a 4403 4404 4103 +a 4404 4405 4103 +a 4405 4406 4103 +a 4406 4407 4103 +a 4407 4408 4103 +a 4408 4409 4103 +a 4409 4410 4103 +a 4410 4411 4103 +a 4411 4412 4103 +a 4412 4413 4103 +a 4413 4414 4103 +a 4414 4415 4103 +a 4415 4416 4103 +a 4416 4417 4103 +a 4417 4418 4103 +a 4418 4419 4103 +a 4419 4420 4103 +a 4420 4421 4103 +a 4421 4422 4103 +a 4422 4423 4103 +a 4423 4424 4103 +a 4424 4425 4103 +a 4425 4426 4103 +a 4426 4427 4103 +a 4427 4428 4103 +a 4428 4429 4103 +a 4429 4430 4103 +a 4430 4431 4103 +a 4431 4432 4103 +a 4432 4433 4103 +a 4433 4434 4103 +a 4434 4435 4103 +a 4435 4436 4103 +a 4436 4437 4103 +a 4437 4438 4103 +a 4438 4439 4103 +a 4439 4440 4103 +a 4440 4441 4103 +a 4441 4442 4103 +a 4442 4443 4103 +a 4443 4444 4103 +a 4444 4445 4103 +a 4445 4446 4103 +a 4446 4447 4103 +a 4447 4448 4103 +a 4448 4449 4103 +a 4449 4450 4103 +a 4450 4451 4103 +a 4451 4452 4103 +a 4452 4453 4103 +a 4453 4454 4103 +a 4454 4455 4103 +a 4455 4456 4103 +a 4456 4457 4103 +a 4457 4458 4103 +a 4458 4459 4103 +a 4459 4460 4103 +a 4460 4461 4103 +a 4461 4462 4103 +a 4462 4463 4103 +a 4463 4464 4103 +a 4464 4465 4103 +a 4465 4466 4103 +a 4466 4467 4103 +a 4467 4468 4103 +a 4468 4469 4103 +a 4469 4470 4103 +a 4470 4471 4103 +a 4471 4472 4103 +a 4472 4473 4103 +a 4473 4474 4103 +a 4474 4475 4103 +a 4475 4476 4103 +a 4476 4477 4103 +a 4477 4478 4103 +a 4478 4479 4103 +a 4479 4480 4103 +a 4480 4481 4103 +a 4481 4482 4103 +a 4482 4483 4103 +a 4483 4484 4103 +a 4484 4485 4103 +a 4485 4486 4103 +a 4486 4487 4103 +a 4487 4488 4103 +a 4488 4489 4103 +a 4489 4490 4103 +a 4490 4491 4103 +a 4491 4492 4103 +a 4492 4493 4103 +a 4493 4494 4103 +a 4494 4495 4103 +a 4495 4496 4103 +a 4496 4497 4103 +a 4497 4498 4103 +a 4498 4499 4103 +a 4499 4500 4103 +a 4500 4501 4103 +a 4501 4502 4103 +a 4502 4503 4103 +a 4503 4504 4103 +a 4504 4505 4103 +a 4505 4506 4103 +a 4506 4507 4103 +a 4507 4508 4103 +a 4508 4509 4103 +a 4509 4510 4103 +a 4510 4511 4103 +a 4511 4512 4103 +a 4512 4513 4103 +a 4513 4514 4103 +a 4514 4515 4103 +a 4515 4516 4103 +a 4516 4517 4103 +a 4517 4518 4103 +a 4518 4519 4103 +a 4519 4520 4103 +a 4520 4521 4103 +a 4521 4522 4103 +a 4522 4523 4103 +a 4523 4524 4103 +a 4524 4525 4103 +a 4525 4526 4103 +a 4526 4527 4103 +a 4527 4528 4103 +a 4528 4529 4103 +a 4529 4530 4103 +a 4530 4531 4103 +a 4531 4532 4103 +a 4532 4533 4103 +a 4533 4534 4103 +a 4534 4535 4103 +a 4535 4536 4103 +a 4536 4537 4103 +a 4537 4538 4103 +a 4538 4539 4103 +a 4539 4540 4103 +a 4540 4541 4103 +a 4541 4542 4103 +a 4542 4543 4103 +a 4543 4544 4103 +a 4544 4545 4103 +a 4545 4546 4103 +a 4546 4547 4103 +a 4547 4548 4103 +a 4548 4549 4103 +a 4549 4550 4103 +a 4550 4551 4103 +a 4551 4552 4103 +a 4552 4553 4103 +a 4553 4554 4103 +a 4554 4555 4103 +a 4555 4556 4103 +a 4556 4557 4103 +a 4557 4558 4103 +a 4558 4559 4103 +a 4559 4560 4103 +a 4560 4561 4103 +a 4561 4562 4103 +a 4562 4563 4103 +a 4563 4564 4103 +a 4564 4565 4103 +a 4565 4566 4103 +a 4566 4567 4103 +a 4567 4568 4103 +a 4568 4569 4103 +a 4569 4570 4103 +a 4570 4571 4103 +a 4571 4572 4103 +a 4572 4573 4103 +a 4573 4574 4103 +a 4574 4575 4103 +a 4575 4576 4103 +a 4576 4577 4103 +a 4577 4578 4103 +a 4578 4579 4103 +a 4579 4580 4103 +a 4580 4581 4103 +a 4581 4582 4103 +a 4582 4583 4103 +a 4583 4584 4103 +a 4584 4585 4103 +a 4585 4586 4103 +a 4586 4587 4103 +a 4587 4588 4103 +a 4588 4589 4103 +a 4589 4590 4103 +a 4590 4591 4103 +a 4591 4592 4103 +a 4592 4593 4103 +a 4593 4594 4103 +a 4594 4595 4103 +a 4595 4596 4103 +a 4596 4597 4103 +a 4597 4598 4103 +a 4598 4599 4103 +a 4599 4600 4103 +a 4600 4601 4103 +a 4601 4602 4103 +a 4602 4603 4103 +a 4603 4604 4103 +a 4604 4605 4103 +a 4605 4606 4103 +a 4606 4607 4103 +a 4607 4608 4103 +a 4608 4609 4103 +a 4609 4610 4103 +a 4610 4611 4103 +a 4611 4612 4103 +a 4612 4613 4103 +a 4613 4614 4103 +a 4614 4615 4103 +a 4615 4616 4103 +a 4616 4617 4103 +a 4617 4618 4103 +a 4618 4619 4103 +a 4619 4620 4103 +a 4620 4621 4103 +a 4621 4622 4103 +a 4622 4623 4103 +a 4623 4624 4103 +a 4624 4625 4103 +a 4625 4626 4103 +a 4626 4627 4103 +a 4627 4628 4103 +a 4628 4629 4103 +a 4629 4630 4103 +a 4630 4631 4103 +a 4631 4632 4103 +a 4632 4633 4103 +a 4633 4634 4103 +a 4634 4635 4103 +a 4635 4636 4103 +a 4636 4637 4103 +a 4637 4638 4103 +a 4638 4639 4103 +a 4639 4640 4103 +a 4640 4641 4103 +a 4641 4642 4103 +a 4642 4643 4103 +a 4643 4644 4103 +a 4644 4645 4103 +a 4645 4646 4103 +a 4646 4647 4103 +a 4647 4648 4103 +a 4648 4649 4103 +a 4649 4650 4103 +a 4650 4651 4103 +a 4651 4652 4103 +a 4652 4653 4103 +a 4653 4654 4103 +a 4654 4655 4103 +a 4655 4656 4103 +a 4656 4657 4103 +a 4657 4658 4103 +a 4658 4659 4103 +a 4659 4660 4103 +a 4660 4661 4103 +a 4661 4662 4103 +a 4662 4663 4103 +a 4663 4664 4103 +a 4664 4665 4103 +a 4665 4666 4103 +a 4666 4667 4103 +a 4667 4668 4103 +a 4668 4669 4103 +a 4669 4670 4103 +a 4670 4671 4103 +a 4671 4672 4103 +a 4672 4673 4103 +a 4673 4674 4103 +a 4674 4675 4103 +a 4675 4676 4103 +a 4676 4677 4103 +a 4677 4678 4103 +a 4678 4679 4103 +a 4679 4680 4103 +a 4680 4681 4103 +a 4681 4682 4103 +a 4682 4683 4103 +a 4683 4684 4103 +a 4684 4685 4103 +a 4685 4686 4103 +a 4686 4687 4103 +a 4687 4688 4103 +a 4688 4689 4103 +a 4689 4690 4103 +a 4690 4691 4103 +a 4691 4692 4103 +a 4692 4693 4103 +a 4693 4694 4103 +a 4694 4695 4103 +a 4695 4696 4103 +a 4696 4697 4103 +a 4697 4698 4103 +a 4698 4699 4103 +a 4699 4700 4103 +a 4700 4701 4103 +a 4701 4702 4103 +a 4702 4703 4103 +a 4703 4704 4103 +a 4704 4705 4103 +a 4705 4706 4103 +a 4706 4707 4103 +a 4707 4708 4103 +a 4708 4709 4103 +a 4709 4710 4103 +a 4710 4711 4103 +a 4711 4712 4103 +a 4712 4713 4103 +a 4713 4714 4103 +a 4714 4715 4103 +a 4715 4716 4103 +a 4716 4717 4103 +a 4717 4718 4103 +a 4718 4719 4103 +a 4719 4720 4103 +a 4720 4721 4103 +a 4721 4722 4103 +a 4722 4723 4103 +a 4723 4724 4103 +a 4724 4725 4103 +a 4725 4726 4103 +a 4726 4727 4103 +a 4727 4728 4103 +a 4728 4729 4103 +a 4729 4730 4103 +a 4730 4731 4103 +a 4731 4732 4103 +a 4732 4733 4103 +a 4733 4734 4103 +a 4734 4735 4103 +a 4735 4736 4103 +a 4736 4737 4103 +a 4737 4738 4103 +a 4738 4739 4103 +a 4739 4740 4103 +a 4740 4741 4103 +a 4741 4742 4103 +a 4742 4743 4103 +a 4743 4744 4103 +a 4744 4745 4103 +a 4745 4746 4103 +a 4746 4747 4103 +a 4747 4748 4103 +a 4748 4749 4103 +a 4749 4750 4103 +a 4750 4751 4103 +a 4751 4752 4103 +a 4752 4753 4103 +a 4753 4754 4103 +a 4754 4755 4103 +a 4755 4756 4103 +a 4756 4757 4103 +a 4757 4758 4103 +a 4758 4759 4103 +a 4759 4760 4103 +a 4760 4761 4103 +a 4761 4762 4103 +a 4762 4763 4103 +a 4763 4764 4103 +a 4764 4765 4103 +a 4765 4766 4103 +a 4766 4767 4103 +a 4767 4768 4103 +a 4768 4769 4103 +a 4769 4770 4103 +a 4770 4771 4103 +a 4771 4772 4103 +a 4772 4773 4103 +a 4773 4774 4103 +a 4774 4775 4103 +a 4775 4776 4103 +a 4776 4777 4103 +a 4777 4778 4103 +a 4778 4779 4103 +a 4779 4780 4103 +a 4780 4781 4103 +a 4781 4782 4103 +a 4782 4783 4103 +a 4783 4784 4103 +a 4784 4785 4103 +a 4785 4786 4103 +a 4786 4787 4103 +a 4787 4788 4103 +a 4788 4789 4103 +a 4789 4790 4103 +a 4790 4791 4103 +a 4791 4792 4103 +a 4792 4793 4103 +a 4793 4794 4103 +a 4794 4795 4103 +a 4795 4796 4103 +a 4796 4797 4103 +a 4797 4798 4103 +a 4798 4799 4103 +a 4799 4800 4103 +a 4800 4801 4103 +a 4801 4802 4103 +a 4802 4803 4103 +a 4803 4804 4103 +a 4804 4805 4103 +a 4805 4806 4103 +a 4806 4807 4103 +a 4807 4808 4103 +a 4808 4809 4103 +a 4809 4810 4103 +a 4810 4811 4103 +a 4811 4812 4103 +a 4812 4813 4103 +a 4813 4814 4103 +a 4814 4815 4103 +a 4815 4816 4103 +a 4816 4817 4103 +a 4817 4818 4103 +a 4818 4819 4103 +a 4819 4820 4103 +a 4820 4821 4103 +a 4821 4822 4103 +a 4822 4823 4103 +a 4823 4824 4103 +a 4824 4825 4103 +a 4825 4826 4103 +a 4826 4827 4103 +a 4827 4828 4103 +a 4828 4829 4103 +a 4829 4830 4103 +a 4830 4831 4103 +a 4831 4832 4103 +a 4832 4833 4103 +a 4833 4834 4103 +a 4834 4835 4103 +a 4835 4836 4103 +a 4836 4837 4103 +a 4837 4838 4103 +a 4838 4839 4103 +a 4839 4840 4103 +a 4840 4841 4103 +a 4841 4842 4103 +a 4842 4843 4103 +a 4843 4844 4103 +a 4844 4845 4103 +a 4845 4846 4103 +a 4846 4847 4103 +a 4847 4848 4103 +a 4848 4849 4103 +a 4849 4850 4103 +a 4850 4851 4103 +a 4851 4852 4103 +a 4852 4853 4103 +a 4853 4854 4103 +a 4854 4855 4103 +a 4855 4856 4103 +a 4856 4857 4103 +a 4857 4858 4103 +a 4858 4859 4103 +a 4859 4860 4103 +a 4860 4861 4103 +a 4861 4862 4103 +a 4862 4863 4103 +a 4863 4864 4103 +a 4864 4865 4103 +a 4865 4866 4103 +a 4866 4867 4103 +a 4867 4868 4103 +a 4868 4869 4103 +a 4869 4870 4103 +a 4870 4871 4103 +a 4871 4872 4103 +a 4872 4873 4103 +a 4873 4874 4103 +a 4874 4875 4103 +a 4875 4876 4103 +a 4876 4877 4103 +a 4877 4878 4103 +a 4878 4879 4103 +a 4879 4880 4103 +a 4880 4881 4103 +a 4881 4882 4103 +a 4882 4883 4103 +a 4883 4884 4103 +a 4884 4885 4103 +a 4885 4886 4103 +a 4886 4887 4103 +a 4887 4888 4103 +a 4888 4889 4103 +a 4889 4890 4103 +a 4890 4891 4103 +a 4891 4892 4103 +a 4892 4893 4103 +a 4893 4894 4103 +a 4894 4895 4103 +a 4895 4896 4103 +a 4896 4897 4103 +a 4897 4898 4103 +a 4898 4899 4103 +a 4899 4900 4103 +a 4900 4901 4103 +a 4901 4902 4103 +a 4902 4903 4103 +a 4903 4904 4103 +a 4904 4905 4103 +a 4905 4906 4103 +a 4906 4907 4103 +a 4907 4908 4103 +a 4908 4909 4103 +a 4909 4910 4103 +a 4910 4911 4103 +a 4911 4912 4103 +a 4912 4913 4103 +a 4913 4914 4103 +a 4914 4915 4103 +a 4915 4916 4103 +a 4916 4917 4103 +a 4917 4918 4103 +a 4918 4919 4103 +a 4919 4920 4103 +a 4920 4921 4103 +a 4921 4922 4103 +a 4922 4923 4103 +a 4923 4924 4103 +a 4924 4925 4103 +a 4925 4926 4103 +a 4926 4927 4103 +a 4927 4928 4103 +a 4928 4929 4103 +a 4929 4930 4103 +a 4930 4931 4103 +a 4931 4932 4103 +a 4932 4933 4103 +a 4933 4934 4103 +a 4934 4935 4103 +a 4935 4936 4103 +a 4936 4937 4103 +a 4937 4938 4103 +a 4938 4939 4103 +a 4939 4940 4103 +a 4940 4941 4103 +a 4941 4942 4103 +a 4942 4943 4103 +a 4943 4944 4103 +a 4944 4945 4103 +a 4945 4946 4103 +a 4946 4947 4103 +a 4947 4948 4103 +a 4948 4949 4103 +a 4949 4950 4103 +a 4950 4951 4103 +a 4951 4952 4103 +a 4952 4953 4103 +a 4953 4954 4103 +a 4954 4955 4103 +a 4955 4956 4103 +a 4956 4957 4103 +a 4957 4958 4103 +a 4958 4959 4103 +a 4959 4960 4103 +a 4960 4961 4103 +a 4961 4962 4103 +a 4962 4963 4103 +a 4963 4964 4103 +a 4964 4965 4103 +a 4965 4966 4103 +a 4966 4967 4103 +a 4967 4968 4103 +a 4968 4969 4103 +a 4969 4970 4103 +a 4970 4971 4103 +a 4971 4972 4103 +a 4972 4973 4103 +a 4973 4974 4103 +a 4974 4975 4103 +a 4975 4976 4103 +a 4976 4977 4103 +a 4977 4978 4103 +a 4978 4979 4103 +a 4979 4980 4103 +a 4980 4981 4103 +a 4981 4982 4103 +a 4982 4983 4103 +a 4983 4984 4103 +a 4984 4985 4103 +a 4985 4986 4103 +a 4986 4987 4103 +a 4987 4988 4103 +a 4988 4989 4103 +a 4989 4990 4103 +a 4990 4991 4103 +a 4991 4992 4103 +a 4992 4993 4103 +a 4993 4994 4103 +a 4994 4995 4103 +a 4995 4996 4103 +a 4996 4997 4103 +a 4997 4998 4103 +a 4998 4999 4103 +a 4999 5000 4103 +a 5000 5001 4103 +a 5001 5002 4103 +a 5002 5003 4103 +a 5003 5004 4103 +a 5004 5005 4103 +a 5005 5006 4103 +a 5006 5007 4103 +a 5007 5008 4103 +a 5008 5009 4103 +a 5009 5010 4103 +a 5010 5011 4103 +a 5011 5012 4103 +a 5012 5013 4103 +a 5013 5014 4103 +a 5014 5015 4103 +a 5015 5016 4103 +a 5016 5017 4103 +a 5017 5018 4103 +a 5018 5019 4103 +a 5019 5020 4103 +a 5020 5021 4103 +a 5021 5022 4103 +a 5022 5023 4103 +a 5023 5024 4103 +a 5024 5025 4103 +a 5025 5026 4103 +a 5026 5027 4103 +a 5027 5028 4103 +a 5028 5029 4103 +a 5029 5030 4103 +a 5030 5031 4103 +a 5031 5032 4103 +a 5032 5033 4103 +a 5033 5034 4103 +a 5034 5035 4103 +a 5035 5036 4103 +a 5036 5037 4103 +a 5037 5038 4103 +a 5038 5039 4103 +a 5039 5040 4103 +a 5040 5041 4103 +a 5041 5042 4103 +a 5042 5043 4103 +a 5043 5044 4103 +a 5044 5045 4103 +a 5045 5046 4103 +a 5046 5047 4103 +a 5047 5048 4103 +a 5048 5049 4103 +a 5049 5050 4103 +a 5050 5051 4103 +a 5051 5052 4103 +a 5052 5053 4103 +a 5053 5054 4103 +a 5054 5055 4103 +a 5055 5056 4103 +a 5056 5057 4103 +a 5057 5058 4103 +a 5058 5059 4103 +a 5059 5060 4103 +a 5060 5061 4103 +a 5061 5062 4103 +a 5062 5063 4103 +a 5063 5064 4103 +a 5064 5065 4103 +a 5065 5066 4103 +a 5066 5067 4103 +a 5067 5068 4103 +a 5068 5069 4103 +a 5069 5070 4103 +a 5070 5071 4103 +a 5071 5072 4103 +a 5072 5073 4103 +a 5073 5074 4103 +a 5074 5075 4103 +a 5075 5076 4103 +a 5076 5077 4103 +a 5077 5078 4103 +a 5078 5079 4103 +a 5079 5080 4103 +a 5080 5081 4103 +a 5081 5082 4103 +a 5082 5083 4103 +a 5083 5084 4103 +a 5084 5085 4103 +a 5085 5086 4103 +a 5086 5087 4103 +a 5087 5088 4103 +a 5088 5089 4103 +a 5089 5090 4103 +a 5090 5091 4103 +a 5091 5092 4103 +a 5092 5093 4103 +a 5093 5094 4103 +a 5094 5095 4103 +a 5095 5096 4103 +a 5096 5097 4103 +a 5097 5098 4103 +a 5098 5099 4103 +a 5099 5100 4103 +a 5100 5101 4103 +a 5101 5102 4103 +a 5102 5103 4103 +a 5103 5104 4103 +a 5104 5105 4103 +a 5105 5106 4103 +a 5106 5107 4103 +a 5107 5108 4103 +a 5108 5109 4103 +a 5109 5110 4103 +a 5110 5111 4103 +a 5111 5112 4103 +a 5112 5113 4103 +a 5113 5114 4103 +a 5114 5115 4103 +a 5115 5116 4103 +a 5116 5117 4103 +a 5117 5118 4103 +a 5118 5119 4103 +a 5119 5120 4103 +a 5120 5121 4103 +a 5121 5122 4103 +a 5122 5123 4103 +a 5123 5124 4103 +a 5124 5125 4103 +a 5125 5126 4103 +a 5126 5127 4103 +a 5127 5128 4103 +a 5128 5129 4103 +a 5129 5130 4103 +a 5130 5131 4103 +a 5131 5132 4103 +a 5132 5133 4103 +a 5133 5134 4103 +a 5134 5135 4103 +a 5135 5136 4103 +a 5136 5137 4103 +a 5137 5138 4103 +a 5138 5139 4103 +a 5139 5140 4103 +a 5140 5141 4103 +a 5141 5142 4103 +a 5142 5143 4103 +a 5143 5144 4103 +a 5144 5145 4103 +a 5145 5146 4103 +a 5146 5147 4103 +a 5147 5148 4103 +a 5148 5149 4103 +a 5149 5150 4103 +a 5150 5151 4103 +a 5151 5152 4103 +a 5152 5153 4103 +a 5153 5154 4103 +a 5154 5155 4103 +a 5155 5156 4103 +a 5156 5157 4103 +a 5157 5158 4103 +a 5158 5159 4103 +a 5159 5160 4103 +a 5160 5161 4103 +a 5161 5162 4103 +a 5162 5163 4103 +a 5163 5164 4103 +a 5164 5165 4103 +a 5165 5166 4103 +a 5166 5167 4103 +a 5167 5168 4103 +a 5168 5169 4103 +a 5169 5170 4103 +a 5170 5171 4103 +a 5171 5172 4103 +a 5172 5173 4103 +a 5173 5174 4103 +a 5174 5175 4103 +a 5175 5176 4103 +a 5176 5177 4103 +a 5177 5178 4103 +a 5178 5179 4103 +a 5179 5180 4103 +a 5180 5181 4103 +a 5181 5182 4103 +a 5182 5183 4103 +a 5183 5184 4103 +a 5184 5185 4103 +a 5185 5186 4103 +a 5186 5187 4103 +a 5187 5188 4103 +a 5188 5189 4103 +a 5189 5190 4103 +a 5190 5191 4103 +a 5191 5192 4103 +a 5192 5193 4103 +a 5193 5194 4103 +a 5194 5195 4103 +a 5195 5196 4103 +a 5196 5197 4103 +a 5197 5198 4103 +a 5198 5199 4103 +a 5199 5200 4103 +a 5200 5201 4103 +a 5201 5202 4103 +a 5202 5203 4103 +a 5203 5204 4103 +a 5204 5205 4103 +a 5205 5206 4103 +a 5206 5207 4103 +a 5207 5208 4103 +a 5208 5209 4103 +a 5209 5210 4103 +a 5210 5211 4103 +a 5211 5212 4103 +a 5212 5213 4103 +a 5213 5214 4103 +a 5214 5215 4103 +a 5215 5216 4103 +a 5216 5217 4103 +a 5217 5218 4103 +a 5218 5219 4103 +a 5219 5220 4103 +a 5220 5221 4103 +a 5221 5222 4103 +a 5222 5223 4103 +a 5223 5224 4103 +a 5224 5225 4103 +a 5225 5226 4103 +a 5226 5227 4103 +a 5227 5228 4103 +a 5228 5229 4103 +a 5229 5230 4103 +a 5230 5231 4103 +a 5231 5232 4103 +a 5232 5233 4103 +a 5233 5234 4103 +a 5234 5235 4103 +a 5235 5236 4103 +a 5236 5237 4103 +a 5237 5238 4103 +a 5238 5239 4103 +a 5239 5240 4103 +a 5240 5241 4103 +a 5241 5242 4103 +a 5242 5243 4103 +a 5243 5244 4103 +a 5244 5245 4103 +a 5245 5246 4103 +a 5246 5247 4103 +a 5247 5248 4103 +a 5248 5249 4103 +a 5249 5250 4103 +a 5250 5251 4103 +a 5251 5252 4103 +a 5252 5253 4103 +a 5253 5254 4103 +a 5254 5255 4103 +a 5255 5256 4103 +a 5256 5257 4103 +a 5257 5258 4103 +a 5258 5259 4103 +a 5259 5260 4103 +a 5260 5261 4103 +a 5261 5262 4103 +a 5262 5263 4103 +a 5263 5264 4103 +a 5264 5265 4103 +a 5265 5266 4103 +a 5266 5267 4103 +a 5267 5268 4103 +a 5268 5269 4103 +a 5269 5270 4103 +a 5270 5271 4103 +a 5271 5272 4103 +a 5272 5273 4103 +a 5273 5274 4103 +a 5274 5275 4103 +a 5275 5276 4103 +a 5276 5277 4103 +a 5277 5278 4103 +a 5278 5279 4103 +a 5279 5280 4103 +a 5280 5281 4103 +a 5281 5282 4103 +a 5282 5283 4103 +a 5283 5284 4103 +a 5284 5285 4103 +a 5285 5286 4103 +a 5286 5287 4103 +a 5287 5288 4103 +a 5288 5289 4103 +a 5289 5290 4103 +a 5290 5291 4103 +a 5291 5292 4103 +a 5292 5293 4103 +a 5293 5294 4103 +a 5294 5295 4103 +a 5295 5296 4103 +a 5296 5297 4103 +a 5297 5298 4103 +a 5298 5299 4103 +a 5299 5300 4103 +a 5300 5301 4103 +a 5301 5302 4103 +a 5302 5303 4103 +a 5303 5304 4103 +a 5304 5305 4103 +a 5305 5306 4103 +a 5306 5307 4103 +a 5307 5308 4103 +a 5308 5309 4103 +a 5309 5310 4103 +a 5310 5311 4103 +a 5311 5312 4103 +a 5312 5313 4103 +a 5313 5314 4103 +a 5314 5315 4103 +a 5315 5316 4103 +a 5316 5317 4103 +a 5317 5318 4103 +a 5318 5319 4103 +a 5319 5320 4103 +a 5320 5321 4103 +a 5321 5322 4103 +a 5322 5323 4103 +a 5323 5324 4103 +a 5324 5325 4103 +a 5325 5326 4103 +a 5326 5327 4103 +a 5327 5328 4103 +a 5328 5329 4103 +a 5329 5330 4103 +a 5330 5331 4103 +a 5331 5332 4103 +a 5332 5333 4103 +a 5333 5334 4103 +a 5334 5335 4103 +a 5335 5336 4103 +a 5336 5337 4103 +a 5337 5338 4103 +a 5338 5339 4103 +a 5339 5340 4103 +a 5340 5341 4103 +a 5341 5342 4103 +a 5342 5343 4103 +a 5343 5344 4103 +a 5344 5345 4103 +a 5345 5346 4103 +a 5346 5347 4103 +a 5347 5348 4103 +a 5348 5349 4103 +a 5349 5350 4103 +a 5350 5351 4103 +a 5351 5352 4103 +a 5352 5353 4103 +a 5353 5354 4103 +a 5354 5355 4103 +a 5355 5356 4103 +a 5356 5357 4103 +a 5357 5358 4103 +a 5358 5359 4103 +a 5359 5360 4103 +a 5360 5361 4103 +a 5361 5362 4103 +a 5362 5363 4103 +a 5363 5364 4103 +a 5364 5365 4103 +a 5365 5366 4103 +a 5366 5367 4103 +a 5367 5368 4103 +a 5368 5369 4103 +a 5369 5370 4103 +a 5370 5371 4103 +a 5371 5372 4103 +a 5372 5373 4103 +a 5373 5374 4103 +a 5374 5375 4103 +a 5375 5376 4103 +a 5376 5377 4103 +a 5377 5378 4103 +a 5378 5379 4103 +a 5379 5380 4103 +a 5380 5381 4103 +a 5381 5382 4103 +a 5382 5383 4103 +a 5383 5384 4103 +a 5384 5385 4103 +a 5385 5386 4103 +a 5386 5387 4103 +a 5387 5388 4103 +a 5388 5389 4103 +a 5389 5390 4103 +a 5390 5391 4103 +a 5391 5392 4103 +a 5392 5393 4103 +a 5393 5394 4103 +a 5394 5395 4103 +a 5395 5396 4103 +a 5396 5397 4103 +a 5397 5398 4103 +a 5398 5399 4103 +a 5399 5400 4103 +a 5400 5401 4103 +a 5401 5402 4103 +a 5402 5403 4103 +a 5403 5404 4103 +a 5404 5405 4103 +a 5405 5406 4103 +a 5406 5407 4103 +a 5407 5408 4103 +a 5408 5409 4103 +a 5409 5410 4103 +a 5410 5411 4103 +a 5411 5412 4103 +a 5412 5413 4103 +a 5413 5414 4103 +a 5414 5415 4103 +a 5415 5416 4103 +a 5416 5417 4103 +a 5417 5418 4103 +a 5418 5419 4103 +a 5419 5420 4103 +a 5420 5421 4103 +a 5421 5422 4103 +a 5422 5423 4103 +a 5423 5424 4103 +a 5424 5425 4103 +a 5425 5426 4103 +a 5426 5427 4103 +a 5427 5428 4103 +a 5428 5429 4103 +a 5429 5430 4103 +a 5430 5431 4103 +a 5431 5432 4103 +a 5432 5433 4103 +a 5433 5434 4103 +a 5434 5435 4103 +a 5435 5436 4103 +a 5436 5437 4103 +a 5437 5438 4103 +a 5438 5439 4103 +a 5439 5440 4103 +a 5440 5441 4103 +a 5441 5442 4103 +a 5442 5443 4103 +a 5443 5444 4103 +a 5444 5445 4103 +a 5445 5446 4103 +a 5446 5447 4103 +a 5447 5448 4103 +a 5448 5449 4103 +a 5449 5450 4103 +a 5450 5451 4103 +a 5451 5452 4103 +a 5452 5453 4103 +a 5453 5454 4103 +a 5454 5455 4103 +a 5455 5456 4103 +a 5456 5457 4103 +a 5457 5458 4103 +a 5458 5459 4103 +a 5459 5460 4103 +a 5460 5461 4103 +a 5461 5462 4103 +a 5462 5463 4103 +a 5463 5464 4103 +a 5464 5465 4103 +a 5465 5466 4103 +a 5466 5467 4103 +a 5467 5468 4103 +a 5468 5469 4103 +a 5469 5470 4103 +a 5470 5471 4103 +a 5471 5472 4103 +a 5472 5473 4103 +a 5473 5474 4103 +a 5474 5475 4103 +a 5475 5476 4103 +a 5476 5477 4103 +a 5477 5478 4103 +a 5478 5479 4103 +a 5479 5480 4103 +a 5480 5481 4103 +a 5481 5482 4103 +a 5482 5483 4103 +a 5483 5484 4103 +a 5484 5485 4103 +a 5485 5486 4103 +a 5486 5487 4103 +a 5487 5488 4103 +a 5488 5489 4103 +a 5489 5490 4103 +a 5490 5491 4103 +a 5491 5492 4103 +a 5492 5493 4103 +a 5493 5494 4103 +a 5494 5495 4103 +a 5495 5496 4103 +a 5496 5497 4103 +a 5497 5498 4103 +a 5498 5499 4103 +a 5499 5500 4103 +a 5500 5501 4103 +a 5501 5502 4103 +a 5502 5503 4103 +a 5503 5504 4103 +a 5504 5505 4103 +a 5505 5506 4103 +a 5506 5507 4103 +a 5507 5508 4103 +a 5508 5509 4103 +a 5509 5510 4103 +a 5510 5511 4103 +a 5511 5512 4103 +a 5512 5513 4103 +a 5513 5514 4103 +a 5514 5515 4103 +a 5515 5516 4103 +a 5516 5517 4103 +a 5517 5518 4103 +a 5518 5519 4103 +a 5519 5520 4103 +a 5520 5521 4103 +a 5521 5522 4103 +a 5522 5523 4103 +a 5523 5524 4103 +a 5524 5525 4103 +a 5525 5526 4103 +a 5526 5527 4103 +a 5527 5528 4103 +a 5528 5529 4103 +a 5529 5530 4103 +a 5530 5531 4103 +a 5531 5532 4103 +a 5532 5533 4103 +a 5533 5534 4103 +a 5534 5535 4103 +a 5535 5536 4103 +a 5536 5537 4103 +a 5537 5538 4103 +a 5538 5539 4103 +a 5539 5540 4103 +a 5540 5541 4103 +a 5541 5542 4103 +a 5542 5543 4103 +a 5543 5544 4103 +a 5544 5545 4103 +a 5545 5546 4103 +a 5546 5547 4103 +a 5547 5548 4103 +a 5548 5549 4103 +a 5549 5550 4103 +a 5550 5551 4103 +a 5551 5552 4103 +a 5552 5553 4103 +a 5553 5554 4103 +a 5554 5555 4103 +a 5555 5556 4103 +a 5556 5557 4103 +a 5557 5558 4103 +a 5558 5559 4103 +a 5559 5560 4103 +a 5560 5561 4103 +a 5561 5562 4103 +a 5562 5563 4103 +a 5563 5564 4103 +a 5564 5565 4103 +a 5565 5566 4103 +a 5566 5567 4103 +a 5567 5568 4103 +a 5568 5569 4103 +a 5569 5570 4103 +a 5570 5571 4103 +a 5571 5572 4103 +a 5572 5573 4103 +a 5573 5574 4103 +a 5574 5575 4103 +a 5575 5576 4103 +a 5576 5577 4103 +a 5577 5578 4103 +a 5578 5579 4103 +a 5579 5580 4103 +a 5580 5581 4103 +a 5581 5582 4103 +a 5582 5583 4103 +a 5583 5584 4103 +a 5584 5585 4103 +a 5585 5586 4103 +a 5586 5587 4103 +a 5587 5588 4103 +a 5588 5589 4103 +a 5589 5590 4103 +a 5590 5591 4103 +a 5591 5592 4103 +a 5592 5593 4103 +a 5593 5594 4103 +a 5594 5595 4103 +a 5595 5596 4103 +a 5596 5597 4103 +a 5597 5598 4103 +a 5598 5599 4103 +a 5599 5600 4103 +a 5600 5601 4103 +a 5601 5602 4103 +a 5602 5603 4103 +a 5603 5604 4103 +a 5604 5605 4103 +a 5605 5606 4103 +a 5606 5607 4103 +a 5607 5608 4103 +a 5608 5609 4103 +a 5609 5610 4103 +a 5610 5611 4103 +a 5611 5612 4103 +a 5612 5613 4103 +a 5613 5614 4103 +a 5614 5615 4103 +a 5615 5616 4103 +a 5616 5617 4103 +a 5617 5618 4103 +a 5618 5619 4103 +a 5619 5620 4103 +a 5620 5621 4103 +a 5621 5622 4103 +a 5622 5623 4103 +a 5623 5624 4103 +a 5624 5625 4103 +a 5625 5626 4103 +a 5626 5627 4103 +a 5627 5628 4103 +a 5628 5629 4103 +a 5629 5630 4103 +a 5630 5631 4103 +a 5631 5632 4103 +a 5632 5633 4103 +a 5633 5634 4103 +a 5634 5635 4103 +a 5635 5636 4103 +a 5636 5637 4103 +a 5637 5638 4103 +a 5638 5639 4103 +a 5639 5640 4103 +a 5640 5641 4103 +a 5641 5642 4103 +a 5642 5643 4103 +a 5643 5644 4103 +a 5644 5645 4103 +a 5645 5646 4103 +a 5646 5647 4103 +a 5647 5648 4103 +a 5648 5649 4103 +a 5649 5650 4103 +a 5650 5651 4103 +a 5651 5652 4103 +a 5652 5653 4103 +a 5653 5654 4103 +a 5654 5655 4103 +a 5655 5656 4103 +a 5656 5657 4103 +a 5657 5658 4103 +a 5658 5659 4103 +a 5659 5660 4103 +a 5660 5661 4103 +a 5661 5662 4103 +a 5662 5663 4103 +a 5663 5664 4103 +a 5664 5665 4103 +a 5665 5666 4103 +a 5666 5667 4103 +a 5667 5668 4103 +a 5668 5669 4103 +a 5669 5670 4103 +a 5670 5671 4103 +a 5671 5672 4103 +a 5672 5673 4103 +a 5673 5674 4103 +a 5674 5675 4103 +a 5675 5676 4103 +a 5676 5677 4103 +a 5677 5678 4103 +a 5678 5679 4103 +a 5679 5680 4103 +a 5680 5681 4103 +a 5681 5682 4103 +a 5682 5683 4103 +a 5683 5684 4103 +a 5684 5685 4103 +a 5685 5686 4103 +a 5686 5687 4103 +a 5687 5688 4103 +a 5688 5689 4103 +a 5689 5690 4103 +a 5690 5691 4103 +a 5691 5692 4103 +a 5692 5693 4103 +a 5693 5694 4103 +a 5694 5695 4103 +a 5695 5696 4103 +a 5696 5697 4103 +a 5697 5698 4103 +a 5698 5699 4103 +a 5699 5700 4103 +a 5700 5701 4103 +a 5701 5702 4103 +a 5702 5703 4103 +a 5703 5704 4103 +a 5704 5705 4103 +a 5705 5706 4103 +a 5706 5707 4103 +a 5707 5708 4103 +a 5708 5709 4103 +a 5709 5710 4103 +a 5710 5711 4103 +a 5711 5712 4103 +a 5712 5713 4103 +a 5713 5714 4103 +a 5714 5715 4103 +a 5715 5716 4103 +a 5716 5717 4103 +a 5717 5718 4103 +a 5718 5719 4103 +a 5719 5720 4103 +a 5720 5721 4103 +a 5721 5722 4103 +a 5722 5723 4103 +a 5723 5724 4103 +a 5724 5725 4103 +a 5725 5726 4103 +a 5726 5727 4103 +a 5727 5728 4103 +a 5728 5729 4103 +a 5729 5730 4103 +a 5730 5731 4103 +a 5731 5732 4103 +a 5732 5733 4103 +a 5733 5734 4103 +a 5734 5735 4103 +a 5735 5736 4103 +a 5736 5737 4103 +a 5737 5738 4103 +a 5738 5739 4103 +a 5739 5740 4103 +a 5740 5741 4103 +a 5741 5742 4103 +a 5742 5743 4103 +a 5743 5744 4103 +a 5744 5745 4103 +a 5745 5746 4103 +a 5746 5747 4103 +a 5747 5748 4103 +a 5748 5749 4103 +a 5749 5750 4103 +a 5750 5751 4103 +a 5751 5752 4103 +a 5752 5753 4103 +a 5753 5754 4103 +a 5754 5755 4103 +a 5755 5756 4103 +a 5756 5757 4103 +a 5757 5758 4103 +a 5758 5759 4103 +a 5759 5760 4103 +a 5760 5761 4103 +a 5761 5762 4103 +a 5762 5763 4103 +a 5763 5764 4103 +a 5764 5765 4103 +a 5765 5766 4103 +a 5766 5767 4103 +a 5767 5768 4103 +a 5768 5769 4103 +a 5769 5770 4103 +a 5770 5771 4103 +a 5771 5772 4103 +a 5772 5773 4103 +a 5773 5774 4103 +a 5774 5775 4103 +a 5775 5776 4103 +a 5776 5777 4103 +a 5777 5778 4103 +a 5778 5779 4103 +a 5779 5780 4103 +a 5780 5781 4103 +a 5781 5782 4103 +a 5782 5783 4103 +a 5783 5784 4103 +a 5784 5785 4103 +a 5785 5786 4103 +a 5786 5787 4103 +a 5787 5788 4103 +a 5788 5789 4103 +a 5789 5790 4103 +a 5790 5791 4103 +a 5791 5792 4103 +a 5792 5793 4103 +a 5793 5794 4103 +a 5794 5795 4103 +a 5795 5796 4103 +a 5796 5797 4103 +a 5797 5798 4103 +a 5798 5799 4103 +a 5799 5800 4103 +a 5800 5801 4103 +a 5801 5802 4103 +a 5802 5803 4103 +a 5803 5804 4103 +a 5804 5805 4103 +a 5805 5806 4103 +a 5806 5807 4103 +a 5807 5808 4103 +a 5808 5809 4103 +a 5809 5810 4103 +a 5810 5811 4103 +a 5811 5812 4103 +a 5812 5813 4103 +a 5813 5814 4103 +a 5814 5815 4103 +a 5815 5816 4103 +a 5816 5817 4103 +a 5817 5818 4103 +a 5818 5819 4103 +a 5819 5820 4103 +a 5820 5821 4103 +a 5821 5822 4103 +a 5822 5823 4103 +a 5823 5824 4103 +a 5824 5825 4103 +a 5825 5826 4103 +a 5826 5827 4103 +a 5827 5828 4103 +a 5828 5829 4103 +a 5829 5830 4103 +a 5830 5831 4103 +a 5831 5832 4103 +a 5832 5833 4103 +a 5833 5834 4103 +a 5834 5835 4103 +a 5835 5836 4103 +a 5836 5837 4103 +a 5837 5838 4103 +a 5838 5839 4103 +a 5839 5840 4103 +a 5840 5841 4103 +a 5841 5842 4103 +a 5842 5843 4103 +a 5843 5844 4103 +a 5844 5845 4103 +a 5845 5846 4103 +a 5846 5847 4103 +a 5847 5848 4103 +a 5848 5849 4103 +a 5849 5850 4103 +a 5850 5851 4103 +a 5851 5852 4103 +a 5852 5853 4103 +a 5853 5854 4103 +a 5854 5855 4103 +a 5855 5856 4103 +a 5856 5857 4103 +a 5857 5858 4103 +a 5858 5859 4103 +a 5859 5860 4103 +a 5860 5861 4103 +a 5861 5862 4103 +a 5862 5863 4103 +a 5863 5864 4103 +a 5864 5865 4103 +a 5865 5866 4103 +a 5866 5867 4103 +a 5867 5868 4103 +a 5868 5869 4103 +a 5869 5870 4103 +a 5870 5871 4103 +a 5871 5872 4103 +a 5872 5873 4103 +a 5873 5874 4103 +a 5874 5875 4103 +a 5875 5876 4103 +a 5876 5877 4103 +a 5877 5878 4103 +a 5878 5879 4103 +a 5879 5880 4103 +a 5880 5881 4103 +a 5881 5882 4103 +a 5882 5883 4103 +a 5883 5884 4103 +a 5884 5885 4103 +a 5885 5886 4103 +a 5886 5887 4103 +a 5887 5888 4103 +a 5888 5889 4103 +a 5889 5890 4103 +a 5890 5891 4103 +a 5891 5892 4103 +a 5892 5893 4103 +a 5893 5894 4103 +a 5894 5895 4103 +a 5895 5896 4103 +a 5896 5897 4103 +a 5897 5898 4103 +a 5898 5899 4103 +a 5899 5900 4103 +a 5900 5901 4103 +a 5901 5902 4103 +a 5902 5903 4103 +a 5903 5904 4103 +a 5904 5905 4103 +a 5905 5906 4103 +a 5906 5907 4103 +a 5907 5908 4103 +a 5908 5909 4103 +a 5909 5910 4103 +a 5910 5911 4103 +a 5911 5912 4103 +a 5912 5913 4103 +a 5913 5914 4103 +a 5914 5915 4103 +a 5915 5916 4103 +a 5916 5917 4103 +a 5917 5918 4103 +a 5918 5919 4103 +a 5919 5920 4103 +a 5920 5921 4103 +a 5921 5922 4103 +a 5922 5923 4103 +a 5923 5924 4103 +a 5924 5925 4103 +a 5925 5926 4103 +a 5926 5927 4103 +a 5927 5928 4103 +a 5928 5929 4103 +a 5929 5930 4103 +a 5930 5931 4103 +a 5931 5932 4103 +a 5932 5933 4103 +a 5933 5934 4103 +a 5934 5935 4103 +a 5935 5936 4103 +a 5936 5937 4103 +a 5937 5938 4103 +a 5938 5939 4103 +a 5939 5940 4103 +a 5940 5941 4103 +a 5941 5942 4103 +a 5942 5943 4103 +a 5943 5944 4103 +a 5944 5945 4103 +a 5945 5946 4103 +a 5946 5947 4103 +a 5947 5948 4103 +a 5948 5949 4103 +a 5949 5950 4103 +a 5950 5951 4103 +a 5951 5952 4103 +a 5952 5953 4103 +a 5953 5954 4103 +a 5954 5955 4103 +a 5955 5956 4103 +a 5956 5957 4103 +a 5957 5958 4103 +a 5958 5959 4103 +a 5959 5960 4103 +a 5960 5961 4103 +a 5961 5962 4103 +a 5962 5963 4103 +a 5963 5964 4103 +a 5964 5965 4103 +a 5965 5966 4103 +a 5966 5967 4103 +a 5967 5968 4103 +a 5968 5969 4103 +a 5969 5970 4103 +a 5970 5971 4103 +a 5971 5972 4103 +a 5972 5973 4103 +a 5973 5974 4103 +a 5974 5975 4103 +a 5975 5976 4103 +a 5976 5977 4103 +a 5977 5978 4103 +a 5978 5979 4103 +a 5979 5980 4103 +a 5980 5981 4103 +a 5981 5982 4103 +a 5982 5983 4103 +a 5983 5984 4103 +a 5984 5985 4103 +a 5985 5986 4103 +a 5986 5987 4103 +a 5987 5988 4103 +a 5988 5989 4103 +a 5989 5990 4103 +a 5990 5991 4103 +a 5991 5992 4103 +a 5992 5993 4103 +a 5993 5994 4103 +a 5994 5995 4103 +a 5995 5996 4103 +a 5996 5997 4103 +a 5997 5998 4103 +a 5998 5999 4103 +a 5999 6000 4103 +a 6000 6001 4103 +a 6001 6002 4103 +a 6002 6003 4103 +a 6003 6004 4103 +a 6004 6005 4103 +a 6005 6006 4103 +a 6006 6007 4103 +a 6007 6008 4103 +a 6008 6009 4103 +a 6009 6010 4103 +a 6010 6011 4103 +a 6011 6012 4103 +a 6012 6013 4103 +a 6013 6014 4103 +a 6014 6015 4103 +a 6015 6016 4103 +a 6016 6017 4103 +a 6017 6018 4103 +a 6018 6019 4103 +a 6019 6020 4103 +a 6020 6021 4103 +a 6021 6022 4103 +a 6022 6023 4103 +a 6023 6024 4103 +a 6024 6025 4103 +a 6025 6026 4103 +a 6026 6027 4103 +a 6027 6028 4103 +a 6028 6029 4103 +a 6029 6030 4103 +a 6030 6031 4103 +a 6031 6032 4103 +a 6032 6033 4103 +a 6033 6034 4103 +a 6034 6035 4103 +a 6035 6036 4103 +a 6036 6037 4103 +a 6037 6038 4103 +a 6038 6039 4103 +a 6039 6040 4103 +a 6040 6041 4103 +a 6041 6042 4103 +a 6042 6043 4103 +a 6043 6044 4103 +a 6044 6045 4103 +a 6045 6046 4103 +a 6046 6047 4103 +a 6047 6048 4103 +a 6048 6049 4103 +a 6049 6050 4103 +a 6050 6051 4103 +a 6051 6052 4103 +a 6052 6053 4103 +a 6053 6054 4103 +a 6054 6055 4103 +a 6055 6056 4103 +a 6056 6057 4103 +a 6057 6058 4103 +a 6058 6059 4103 +a 6059 6060 4103 +a 6060 6061 4103 +a 6061 6062 4103 +a 6062 6063 4103 +a 6063 6064 4103 +a 6064 6065 4103 +a 6065 6066 4103 +a 6066 6067 4103 +a 6067 6068 4103 +a 6068 6069 4103 +a 6069 6070 4103 +a 6070 6071 4103 +a 6071 6072 4103 +a 6072 6073 4103 +a 6073 6074 4103 +a 6074 6075 4103 +a 6075 6076 4103 +a 6076 6077 4103 +a 6077 6078 4103 +a 6078 6079 4103 +a 6079 6080 4103 +a 6080 6081 4103 +a 6081 6082 4103 +a 6082 6083 4103 +a 6083 6084 4103 +a 6084 6085 4103 +a 6085 6086 4103 +a 6086 6087 4103 +a 6087 6088 4103 +a 6088 6089 4103 +a 6089 6090 4103 +a 6090 6091 4103 +a 6091 6092 4103 +a 6092 6093 4103 +a 6093 6094 4103 +a 6094 6095 4103 +a 6095 6096 4103 +a 6096 6097 4103 +a 6097 6098 4103 +a 6098 6099 4103 +a 6099 6100 4103 +a 6100 6101 4103 +a 6101 6102 4103 +a 6102 6103 4103 +a 6103 6104 4103 +a 6104 6105 4103 +a 6105 6106 4103 +a 6106 6107 4103 +a 6107 6108 4103 +a 6108 6109 4103 +a 6109 6110 4103 +a 6110 6111 4103 +a 6111 6112 4103 +a 6112 6113 4103 +a 6113 6114 4103 +a 6114 6115 4103 +a 6115 6116 4103 +a 6116 6117 4103 +a 6117 6118 4103 +a 6118 6119 4103 +a 6119 6120 4103 +a 6120 6121 4103 +a 6121 6122 4103 +a 6122 6123 4103 +a 6123 6124 4103 +a 6124 6125 4103 +a 6125 6126 4103 +a 6126 6127 4103 +a 6127 6128 4103 +a 6128 6129 4103 +a 6129 6130 4103 +a 6130 6131 4103 +a 6131 6132 4103 +a 6132 6133 4103 +a 6133 6134 4103 +a 6134 6135 4103 +a 6135 6136 4103 +a 6136 6137 4103 +a 6137 6138 4103 +a 6138 6139 4103 +a 6139 6140 4103 +a 6140 6141 4103 +a 6141 6142 4103 +a 6142 6143 4103 +a 6143 6144 4103 +a 6144 6145 4103 +a 6145 6146 4103 +a 6146 6147 4103 +a 6147 6148 4103 +a 6148 6149 4103 +a 6149 6150 4103 +a 6150 6151 4103 +a 6151 6152 4103 +a 6152 6153 4103 +a 6153 6154 4103 +a 6154 6155 4103 +a 6155 6156 4103 +a 6156 6157 4103 +a 6157 6158 4103 +a 6158 6159 4103 +a 6159 6160 4103 +a 6160 6161 4103 +a 6161 6162 4103 +a 6162 6163 4103 +a 6163 6164 4103 +a 6164 6165 4103 +a 6165 6166 4103 +a 6166 6167 4103 +a 6167 6168 4103 +a 6168 6169 4103 +a 6169 6170 4103 +a 6170 6171 4103 +a 6171 6172 4103 +a 6172 6173 4103 +a 6173 6174 4103 +a 6174 6175 4103 +a 6175 6176 4103 +a 6176 6177 4103 +a 6177 6178 4103 +a 6178 6179 4103 +a 6179 6180 4103 +a 6180 6181 4103 +a 6181 6182 4103 +a 6182 6183 4103 +a 6183 6184 4103 +a 6184 6185 4103 +a 6185 6186 4103 +a 6186 6187 4103 +a 6187 6188 4103 +a 6188 6189 4103 +a 6189 6190 4103 +a 6190 6191 4103 +a 6191 6192 4103 +a 6192 6193 4103 +a 6193 6194 4103 +a 6194 6195 4103 +a 6195 6196 4103 +a 6196 6197 4103 +a 6197 6198 4103 +a 6198 6199 4103 +a 6199 6200 4103 +a 6200 6201 4103 +a 6201 6202 4103 +a 6202 6203 4103 +a 6203 6204 4103 +a 6204 6205 4103 +a 6205 6206 4103 +a 6206 6207 4103 +a 6207 6208 4103 +a 6208 6209 4103 +a 6209 6210 4103 +a 6210 6211 4103 +a 6211 6212 4103 +a 6212 6213 4103 +a 6213 6214 4103 +a 6214 6215 4103 +a 6215 6216 4103 +a 6216 6217 4103 +a 6217 6218 4103 +a 6218 6219 4103 +a 6219 6220 4103 +a 6220 6221 4103 +a 6221 6222 4103 +a 6222 6223 4103 +a 6223 6224 4103 +a 6224 6225 4103 +a 6225 6226 4103 +a 6226 6227 4103 +a 6227 6228 4103 +a 6228 6229 4103 +a 6229 6230 4103 +a 6230 6231 4103 +a 6231 6232 4103 +a 6232 6233 4103 +a 6233 6234 4103 +a 6234 6235 4103 +a 6235 6236 4103 +a 6236 6237 4103 +a 6237 6238 4103 +a 6238 6239 4103 +a 6239 6240 4103 +a 6240 6241 4103 +a 6241 6242 4103 +a 6242 6243 4103 +a 6243 6244 4103 +a 6244 6245 4103 +a 6245 6246 4103 +a 6246 6247 4103 +a 6247 6248 4103 +a 6248 6249 4103 +a 6249 6250 4103 +a 6250 6251 4103 +a 6251 6252 4103 +a 6252 6253 4103 +a 6253 6254 4103 +a 6254 6255 4103 +a 6255 6256 4103 +a 6256 6257 4103 +a 6257 6258 4103 +a 6258 6259 4103 +a 6259 6260 4103 +a 6260 6261 4103 +a 6261 6262 4103 +a 6262 6263 4103 +a 6263 6264 4103 +a 6264 6265 4103 +a 6265 6266 4103 +a 6266 6267 4103 +a 6267 6268 4103 +a 6268 6269 4103 +a 6269 6270 4103 +a 6270 6271 4103 +a 6271 6272 4103 +a 6272 6273 4103 +a 6273 6274 4103 +a 6274 6275 4103 +a 6275 6276 4103 +a 6276 6277 4103 +a 6277 6278 4103 +a 6278 6279 4103 +a 6279 6280 4103 +a 6280 6281 4103 +a 6281 6282 4103 +a 6282 6283 4103 +a 6283 6284 4103 +a 6284 6285 4103 +a 6285 6286 4103 +a 6286 6287 4103 +a 6287 6288 4103 +a 6288 6289 4103 +a 6289 6290 4103 +a 6290 6291 4103 +a 6291 6292 4103 +a 6292 6293 4103 +a 6293 6294 4103 +a 6294 6295 4103 +a 6295 6296 4103 +a 6296 6297 4103 +a 6297 6298 4103 +a 6298 6299 4103 +a 6299 6300 4103 +a 6300 6301 4103 +a 6301 6302 4103 +a 6302 6303 4103 +a 6303 6304 4103 +a 6304 6305 4103 +a 6305 6306 4103 +a 6306 6307 4103 +a 6307 6308 4103 +a 6308 6309 4103 +a 6309 6310 4103 +a 6310 6311 4103 +a 6311 6312 4103 +a 6312 6313 4103 +a 6313 6314 4103 +a 6314 6315 4103 +a 6315 6316 4103 +a 6316 6317 4103 +a 6317 6318 4103 +a 6318 6319 4103 +a 6319 6320 4103 +a 6320 6321 4103 +a 6321 6322 4103 +a 6322 6323 4103 +a 6323 6324 4103 +a 6324 6325 4103 +a 6325 6326 4103 +a 6326 6327 4103 +a 6327 6328 4103 +a 6328 6329 4103 +a 6329 6330 4103 +a 6330 6331 4103 +a 6331 6332 4103 +a 6332 6333 4103 +a 6333 6334 4103 +a 6334 6335 4103 +a 6335 6336 4103 +a 6336 6337 4103 +a 6337 6338 4103 +a 6338 6339 4103 +a 6339 6340 4103 +a 6340 6341 4103 +a 6341 6342 4103 +a 6342 6343 4103 +a 6343 6344 4103 +a 6344 6345 4103 +a 6345 6346 4103 +a 6346 6347 4103 +a 6347 6348 4103 +a 6348 6349 4103 +a 6349 6350 4103 +a 6350 6351 4103 +a 6351 6352 4103 +a 6352 6353 4103 +a 6353 6354 4103 +a 6354 6355 4103 +a 6355 6356 4103 +a 6356 6357 4103 +a 6357 6358 4103 +a 6358 6359 4103 +a 6359 6360 4103 +a 6360 6361 4103 +a 6361 6362 4103 +a 6362 6363 4103 +a 6363 6364 4103 +a 6364 6365 4103 +a 6365 6366 4103 +a 6366 6367 4103 +a 6367 6368 4103 +a 6368 6369 4103 +a 6369 6370 4103 +a 6370 6371 4103 +a 6371 6372 4103 +a 6372 6373 4103 +a 6373 6374 4103 +a 6374 6375 4103 +a 6375 6376 4103 +a 6376 6377 4103 +a 6377 6378 4103 +a 6378 6379 4103 +a 6379 6380 4103 +a 6380 6381 4103 +a 6381 6382 4103 +a 6382 6383 4103 +a 6383 6384 4103 +a 6384 6385 4103 +a 6385 6386 4103 +a 6386 6387 4103 +a 6387 6388 4103 +a 6388 6389 4103 +a 6389 6390 4103 +a 6390 6391 4103 +a 6391 6392 4103 +a 6392 6393 4103 +a 6393 6394 4103 +a 6394 6395 4103 +a 6395 6396 4103 +a 6396 6397 4103 +a 6397 6398 4103 +a 6398 6399 4103 +a 6399 6400 4103 +a 6400 6401 4103 +a 6401 6402 4103 +a 6402 6403 4103 +a 6403 6404 4103 +a 6404 6405 4103 +a 6405 6406 4103 +a 6406 6407 4103 +a 6407 6408 4103 +a 6408 6409 4103 +a 6409 6410 4103 +a 6410 6411 4103 +a 6411 6412 4103 +a 6412 6413 4103 +a 6413 6414 4103 +a 6414 6415 4103 +a 6415 6416 4103 +a 6416 6417 4103 +a 6417 6418 4103 +a 6418 6419 4103 +a 6419 6420 4103 +a 6420 6421 4103 +a 6421 6422 4103 +a 6422 6423 4103 +a 6423 6424 4103 +a 6424 6425 4103 +a 6425 6426 4103 +a 6426 6427 4103 +a 6427 6428 4103 +a 6428 6429 4103 +a 6429 6430 4103 +a 6430 6431 4103 +a 6431 6432 4103 +a 6432 6433 4103 +a 6433 6434 4103 +a 6434 6435 4103 +a 6435 6436 4103 +a 6436 6437 4103 +a 6437 6438 4103 +a 6438 6439 4103 +a 6439 6440 4103 +a 6440 6441 4103 +a 6441 6442 4103 +a 6442 6443 4103 +a 6443 6444 4103 +a 6444 6445 4103 +a 6445 6446 4103 +a 6446 6447 4103 +a 6447 6448 4103 +a 6448 6449 4103 +a 6449 6450 4103 +a 6450 6451 4103 +a 6451 6452 4103 +a 6452 6453 4103 +a 6453 6454 4103 +a 6454 6455 4103 +a 6455 6456 4103 +a 6456 6457 4103 +a 6457 6458 4103 +a 6458 6459 4103 +a 6459 6460 4103 +a 6460 6461 4103 +a 6461 6462 4103 +a 6462 6463 4103 +a 6463 6464 4103 +a 6464 6465 4103 +a 6465 6466 4103 +a 6466 6467 4103 +a 6467 6468 4103 +a 6468 6469 4103 +a 6469 6470 4103 +a 6470 6471 4103 +a 6471 6472 4103 +a 6472 6473 4103 +a 6473 6474 4103 +a 6474 6475 4103 +a 6475 6476 4103 +a 6476 6477 4103 +a 6477 6478 4103 +a 6478 6479 4103 +a 6479 6480 4103 +a 6480 6481 4103 +a 6481 6482 4103 +a 6482 6483 4103 +a 6483 6484 4103 +a 6484 6485 4103 +a 6485 6486 4103 +a 6486 6487 4103 +a 6487 6488 4103 +a 6488 6489 4103 +a 6489 6490 4103 +a 6490 6491 4103 +a 6491 6492 4103 +a 6492 6493 4103 +a 6493 6494 4103 +a 6494 6495 4103 +a 6495 6496 4103 +a 6496 6497 4103 +a 6497 6498 4103 +a 6498 6499 4103 +a 6499 6500 4103 +a 6500 6501 4103 +a 6501 6502 4103 +a 6502 6503 4103 +a 6503 6504 4103 +a 6504 6505 4103 +a 6505 6506 4103 +a 6506 6507 4103 +a 6507 6508 4103 +a 6508 6509 4103 +a 6509 6510 4103 +a 6510 6511 4103 +a 6511 6512 4103 +a 6512 6513 4103 +a 6513 6514 4103 +a 6514 6515 4103 +a 6515 6516 4103 +a 6516 6517 4103 +a 6517 6518 4103 +a 6518 6519 4103 +a 6519 6520 4103 +a 6520 6521 4103 +a 6521 6522 4103 +a 6522 6523 4103 +a 6523 6524 4103 +a 6524 6525 4103 +a 6525 6526 4103 +a 6526 6527 4103 +a 6527 6528 4103 +a 6528 6529 4103 +a 6529 6530 4103 +a 6530 6531 4103 +a 6531 6532 4103 +a 6532 6533 4103 +a 6533 6534 4103 +a 6534 6535 4103 +a 6535 6536 4103 +a 6536 6537 4103 +a 6537 6538 4103 +a 6538 6539 4103 +a 6539 6540 4103 +a 6540 6541 4103 +a 6541 6542 4103 +a 6542 6543 4103 +a 6543 6544 4103 +a 6544 6545 4103 +a 6545 6546 4103 +a 6546 6547 4103 +a 6547 6548 4103 +a 6548 6549 4103 +a 6549 6550 4103 +a 6550 6551 4103 +a 6551 6552 4103 +a 6552 6553 4103 +a 6553 6554 4103 +a 6554 6555 4103 +a 6555 6556 4103 +a 6556 6557 4103 +a 6557 6558 4103 +a 6558 6559 4103 +a 6559 6560 4103 +a 6560 6561 4103 +a 6561 6562 4103 +a 6562 6563 4103 +a 6563 6564 4103 +a 6564 6565 4103 +a 6565 6566 4103 +a 6566 6567 4103 +a 6567 6568 4103 +a 6568 6569 4103 +a 6569 6570 4103 +a 6570 6571 4103 +a 6571 6572 4103 +a 6572 6573 4103 +a 6573 6574 4103 +a 6574 6575 4103 +a 6575 6576 4103 +a 6576 6577 4103 +a 6577 6578 4103 +a 6578 6579 4103 +a 6579 6580 4103 +a 6580 6581 4103 +a 6581 6582 4103 +a 6582 6583 4103 +a 6583 6584 4103 +a 6584 6585 4103 +a 6585 6586 4103 +a 6586 6587 4103 +a 6587 6588 4103 +a 6588 6589 4103 +a 6589 6590 4103 +a 6590 6591 4103 +a 6591 6592 4103 +a 6592 6593 4103 +a 6593 6594 4103 +a 6594 6595 4103 +a 6595 6596 4103 +a 6596 6597 4103 +a 6597 6598 4103 +a 6598 6599 4103 +a 6599 6600 4103 +a 6600 6601 4103 +a 6601 6602 4103 +a 6602 6603 4103 +a 6603 6604 4103 +a 6604 6605 4103 +a 6605 6606 4103 +a 6606 6607 4103 +a 6607 6608 4103 +a 6608 6609 4103 +a 6609 6610 4103 +a 6610 6611 4103 +a 6611 6612 4103 +a 6612 6613 4103 +a 6613 6614 4103 +a 6614 6615 4103 +a 6615 6616 4103 +a 6616 6617 4103 +a 6617 6618 4103 +a 6618 6619 4103 +a 6619 6620 4103 +a 6620 6621 4103 +a 6621 6622 4103 +a 6622 6623 4103 +a 6623 6624 4103 +a 6624 6625 4103 +a 6625 6626 4103 +a 6626 6627 4103 +a 6627 6628 4103 +a 6628 6629 4103 +a 6629 6630 4103 +a 6630 6631 4103 +a 6631 6632 4103 +a 6632 6633 4103 +a 6633 6634 4103 +a 6634 6635 4103 +a 6635 6636 4103 +a 6636 6637 4103 +a 6637 6638 4103 +a 6638 6639 4103 +a 6639 6640 4103 +a 6640 6641 4103 +a 6641 6642 4103 +a 6642 6643 4103 +a 6643 6644 4103 +a 6644 6645 4103 +a 6645 6646 4103 +a 6646 6647 4103 +a 6647 6648 4103 +a 6648 6649 4103 +a 6649 6650 4103 +a 6650 6651 4103 +a 6651 6652 4103 +a 6652 6653 4103 +a 6653 6654 4103 +a 6654 6655 4103 +a 6655 6656 4103 +a 6656 6657 4103 +a 6657 6658 4103 +a 6658 6659 4103 +a 6659 6660 4103 +a 6660 6661 4103 +a 6661 6662 4103 +a 6662 6663 4103 +a 6663 6664 4103 +a 6664 6665 4103 +a 6665 6666 4103 +a 6666 6667 4103 +a 6667 6668 4103 +a 6668 6669 4103 +a 6669 6670 4103 +a 6670 6671 4103 +a 6671 6672 4103 +a 6672 6673 4103 +a 6673 6674 4103 +a 6674 6675 4103 +a 6675 6676 4103 +a 6676 6677 4103 +a 6677 6678 4103 +a 6678 6679 4103 +a 6679 6680 4103 +a 6680 6681 4103 +a 6681 6682 4103 +a 6682 6683 4103 +a 6683 6684 4103 +a 6684 6685 4103 +a 6685 6686 4103 +a 6686 6687 4103 +a 6687 6688 4103 +a 6688 6689 4103 +a 6689 6690 4103 +a 6690 6691 4103 +a 6691 6692 4103 +a 6692 6693 4103 +a 6693 6694 4103 +a 6694 6695 4103 +a 6695 6696 4103 +a 6696 6697 4103 +a 6697 6698 4103 +a 6698 6699 4103 +a 6699 6700 4103 +a 6700 6701 4103 +a 6701 6702 4103 +a 6702 6703 4103 +a 6703 6704 4103 +a 6704 6705 4103 +a 6705 6706 4103 +a 6706 6707 4103 +a 6707 6708 4103 +a 6708 6709 4103 +a 6709 6710 4103 +a 6710 6711 4103 +a 6711 6712 4103 +a 6712 6713 4103 +a 6713 6714 4103 +a 6714 6715 4103 +a 6715 6716 4103 +a 6716 6717 4103 +a 6717 6718 4103 +a 6718 6719 4103 +a 6719 6720 4103 +a 6720 6721 4103 +a 6721 6722 4103 +a 6722 6723 4103 +a 6723 6724 4103 +a 6724 6725 4103 +a 6725 6726 4103 +a 6726 6727 4103 +a 6727 6728 4103 +a 6728 6729 4103 +a 6729 6730 4103 +a 6730 6731 4103 +a 6731 6732 4103 +a 6732 6733 4103 +a 6733 6734 4103 +a 6734 6735 4103 +a 6735 6736 4103 +a 6736 6737 4103 +a 6737 6738 4103 +a 6738 6739 4103 +a 6739 6740 4103 +a 6740 6741 4103 +a 6741 6742 4103 +a 6742 6743 4103 +a 6743 6744 4103 +a 6744 6745 4103 +a 6745 6746 4103 +a 6746 6747 4103 +a 6747 6748 4103 +a 6748 6749 4103 +a 6749 6750 4103 +a 6750 6751 4103 +a 6751 6752 4103 +a 6752 6753 4103 +a 6753 6754 4103 +a 6754 6755 4103 +a 6755 6756 4103 +a 6756 6757 4103 +a 6757 6758 4103 +a 6758 6759 4103 +a 6759 6760 4103 +a 6760 6761 4103 +a 6761 6762 4103 +a 6762 6763 4103 +a 6763 6764 4103 +a 6764 6765 4103 +a 6765 6766 4103 +a 6766 6767 4103 +a 6767 6768 4103 +a 6768 6769 4103 +a 6769 6770 4103 +a 6770 6771 4103 +a 6771 6772 4103 +a 6772 6773 4103 +a 6773 6774 4103 +a 6774 6775 4103 +a 6775 6776 4103 +a 6776 6777 4103 +a 6777 6778 4103 +a 6778 6779 4103 +a 6779 6780 4103 +a 6780 6781 4103 +a 6781 6782 4103 +a 6782 6783 4103 +a 6783 6784 4103 +a 6784 6785 4103 +a 6785 6786 4103 +a 6786 6787 4103 +a 6787 6788 4103 +a 6788 6789 4103 +a 6789 6790 4103 +a 6790 6791 4103 +a 6791 6792 4103 +a 6792 6793 4103 +a 6793 6794 4103 +a 6794 6795 4103 +a 6795 6796 4103 +a 6796 6797 4103 +a 6797 6798 4103 +a 6798 6799 4103 +a 6799 6800 4103 +a 6800 6801 4103 +a 6801 6802 4103 +a 6802 6803 4103 +a 6803 6804 4103 +a 6804 6805 4103 +a 6805 6806 4103 +a 6806 6807 4103 +a 6807 6808 4103 +a 6808 6809 4103 +a 6809 6810 4103 +a 6810 6811 4103 +a 6811 6812 4103 +a 6812 6813 4103 +a 6813 6814 4103 +a 6814 6815 4103 +a 6815 6816 4103 +a 6816 6817 4103 +a 6817 6818 4103 +a 6818 6819 4103 +a 6819 6820 4103 +a 6820 6821 4103 +a 6821 6822 4103 +a 6822 6823 4103 +a 6823 6824 4103 +a 6824 6825 4103 +a 6825 6826 4103 +a 6826 6827 4103 +a 6827 6828 4103 +a 6828 6829 4103 +a 6829 6830 4103 +a 6830 6831 4103 +a 6831 6832 4103 +a 6832 6833 4103 +a 6833 6834 4103 +a 6834 6835 4103 +a 6835 6836 4103 +a 6836 6837 4103 +a 6837 6838 4103 +a 6838 6839 4103 +a 6839 6840 4103 +a 6840 6841 4103 +a 6841 6842 4103 +a 6842 6843 4103 +a 6843 6844 4103 +a 6844 6845 4103 +a 6845 6846 4103 +a 6846 6847 4103 +a 6847 6848 4103 +a 6848 6849 4103 +a 6849 6850 4103 +a 6850 6851 4103 +a 6851 6852 4103 +a 6852 6853 4103 +a 6853 6854 4103 +a 6854 6855 4103 +a 6855 6856 4103 +a 6856 6857 4103 +a 6857 6858 4103 +a 6858 6859 4103 +a 6859 6860 4103 +a 6860 6861 4103 +a 6861 6862 4103 +a 6862 6863 4103 +a 6863 6864 4103 +a 6864 6865 4103 +a 6865 6866 4103 +a 6866 6867 4103 +a 6867 6868 4103 +a 6868 6869 4103 +a 6869 6870 4103 +a 6870 6871 4103 +a 6871 6872 4103 +a 6872 6873 4103 +a 6873 6874 4103 +a 6874 6875 4103 +a 6875 6876 4103 +a 6876 6877 4103 +a 6877 6878 4103 +a 6878 6879 4103 +a 6879 6880 4103 +a 6880 6881 4103 +a 6881 6882 4103 +a 6882 6883 4103 +a 6883 6884 4103 +a 6884 6885 4103 +a 6885 6886 4103 +a 6886 6887 4103 +a 6887 6888 4103 +a 6888 6889 4103 +a 6889 6890 4103 +a 6890 6891 4103 +a 6891 6892 4103 +a 6892 6893 4103 +a 6893 6894 4103 +a 6894 6895 4103 +a 6895 6896 4103 +a 6896 6897 4103 +a 6897 6898 4103 +a 6898 6899 4103 +a 6899 6900 4103 +a 6900 6901 4103 +a 6901 6902 4103 +a 6902 6903 4103 +a 6903 6904 4103 +a 6904 6905 4103 +a 6905 6906 4103 +a 6906 6907 4103 +a 6907 6908 4103 +a 6908 6909 4103 +a 6909 6910 4103 +a 6910 6911 4103 +a 6911 6912 4103 +a 6912 6913 4103 +a 6913 6914 4103 +a 6914 6915 4103 +a 6915 6916 4103 +a 6916 6917 4103 +a 6917 6918 4103 +a 6918 6919 4103 +a 6919 6920 4103 +a 6920 6921 4103 +a 6921 6922 4103 +a 6922 6923 4103 +a 6923 6924 4103 +a 6924 6925 4103 +a 6925 6926 4103 +a 6926 6927 4103 +a 6927 6928 4103 +a 6928 6929 4103 +a 6929 6930 4103 +a 6930 6931 4103 +a 6931 6932 4103 +a 6932 6933 4103 +a 6933 6934 4103 +a 6934 6935 4103 +a 6935 6936 4103 +a 6936 6937 4103 +a 6937 6938 4103 +a 6938 6939 4103 +a 6939 6940 4103 +a 6940 6941 4103 +a 6941 6942 4103 +a 6942 6943 4103 +a 6943 6944 4103 +a 6944 6945 4103 +a 6945 6946 4103 +a 6946 6947 4103 +a 6947 6948 4103 +a 6948 6949 4103 +a 6949 6950 4103 +a 6950 6951 4103 +a 6951 6952 4103 +a 6952 6953 4103 +a 6953 6954 4103 +a 6954 6955 4103 +a 6955 6956 4103 +a 6956 6957 4103 +a 6957 6958 4103 +a 6958 6959 4103 +a 6959 6960 4103 +a 6960 6961 4103 +a 6961 6962 4103 +a 6962 6963 4103 +a 6963 6964 4103 +a 6964 6965 4103 +a 6965 6966 4103 +a 6966 6967 4103 +a 6967 6968 4103 +a 6968 6969 4103 +a 6969 6970 4103 +a 6970 6971 4103 +a 6971 6972 4103 +a 6972 6973 4103 +a 6973 6974 4103 +a 6974 6975 4103 +a 6975 6976 4103 +a 6976 6977 4103 +a 6977 6978 4103 +a 6978 6979 4103 +a 6979 6980 4103 +a 6980 6981 4103 +a 6981 6982 4103 +a 6982 6983 4103 +a 6983 6984 4103 +a 6984 6985 4103 +a 6985 6986 4103 +a 6986 6987 4103 +a 6987 6988 4103 +a 6988 6989 4103 +a 6989 6990 4103 +a 6990 6991 4103 +a 6991 6992 4103 +a 6992 6993 4103 +a 6993 6994 4103 +a 6994 6995 4103 +a 6995 6996 4103 +a 6996 6997 4103 +a 6997 6998 4103 +a 6998 6999 4103 +a 6999 7000 4103 +a 7000 7001 4103 +a 7001 7002 4103 +a 7002 7003 4103 +a 7003 7004 4103 +a 7004 7005 4103 +a 7005 7006 4103 +a 7006 7007 4103 +a 7007 7008 4103 +a 7008 7009 4103 +a 7009 7010 4103 +a 7010 7011 4103 +a 7011 7012 4103 +a 7012 7013 4103 +a 7013 7014 4103 +a 7014 7015 4103 +a 7015 7016 4103 +a 7016 7017 4103 +a 7017 7018 4103 +a 7018 7019 4103 +a 7019 7020 4103 +a 7020 7021 4103 +a 7021 7022 4103 +a 7022 7023 4103 +a 7023 7024 4103 +a 7024 7025 4103 +a 7025 7026 4103 +a 7026 7027 4103 +a 7027 7028 4103 +a 7028 7029 4103 +a 7029 7030 4103 +a 7030 7031 4103 +a 7031 7032 4103 +a 7032 7033 4103 +a 7033 7034 4103 +a 7034 7035 4103 +a 7035 7036 4103 +a 7036 7037 4103 +a 7037 7038 4103 +a 7038 7039 4103 +a 7039 7040 4103 +a 7040 7041 4103 +a 7041 7042 4103 +a 7042 7043 4103 +a 7043 7044 4103 +a 7044 7045 4103 +a 7045 7046 4103 +a 7046 7047 4103 +a 7047 7048 4103 +a 7048 7049 4103 +a 7049 7050 4103 +a 7050 7051 4103 +a 7051 7052 4103 +a 7052 7053 4103 +a 7053 7054 4103 +a 7054 7055 4103 +a 7055 7056 4103 +a 7056 7057 4103 +a 7057 7058 4103 +a 7058 7059 4103 +a 7059 7060 4103 +a 7060 7061 4103 +a 7061 7062 4103 +a 7062 7063 4103 +a 7063 7064 4103 +a 7064 7065 4103 +a 7065 7066 4103 +a 7066 7067 4103 +a 7067 7068 4103 +a 7068 7069 4103 +a 7069 7070 4103 +a 7070 7071 4103 +a 7071 7072 4103 +a 7072 7073 4103 +a 7073 7074 4103 +a 7074 7075 4103 +a 7075 7076 4103 +a 7076 7077 4103 +a 7077 7078 4103 +a 7078 7079 4103 +a 7079 7080 4103 +a 7080 7081 4103 +a 7081 7082 4103 +a 7082 7083 4103 +a 7083 7084 4103 +a 7084 7085 4103 +a 7085 7086 4103 +a 7086 7087 4103 +a 7087 7088 4103 +a 7088 7089 4103 +a 7089 7090 4103 +a 7090 7091 4103 +a 7091 7092 4103 +a 7092 7093 4103 +a 7093 7094 4103 +a 7094 7095 4103 +a 7095 7096 4103 +a 7096 7097 4103 +a 7097 7098 4103 +a 7098 7099 4103 +a 7099 7100 4103 +a 7100 7101 4103 +a 7101 7102 4103 +a 7102 7103 4103 +a 7103 7104 4103 +a 7104 7105 4103 +a 7105 7106 4103 +a 7106 7107 4103 +a 7107 7108 4103 +a 7108 7109 4103 +a 7109 7110 4103 +a 7110 7111 4103 +a 7111 7112 4103 +a 7112 7113 4103 +a 7113 7114 4103 +a 7114 7115 4103 +a 7115 7116 4103 +a 7116 7117 4103 +a 7117 7118 4103 +a 7118 7119 4103 +a 7119 7120 4103 +a 7120 7121 4103 +a 7121 7122 4103 +a 7122 7123 4103 +a 7123 7124 4103 +a 7124 7125 4103 +a 7125 7126 4103 +a 7126 7127 4103 +a 7127 7128 4103 +a 7128 7129 4103 +a 7129 7130 4103 +a 7130 7131 4103 +a 7131 7132 4103 +a 7132 7133 4103 +a 7133 7134 4103 +a 7134 7135 4103 +a 7135 7136 4103 +a 7136 7137 4103 +a 7137 7138 4103 +a 7138 7139 4103 +a 7139 7140 4103 +a 7140 7141 4103 +a 7141 7142 4103 +a 7142 7143 4103 +a 7143 7144 4103 +a 7144 7145 4103 +a 7145 7146 4103 +a 7146 7147 4103 +a 7147 7148 4103 +a 7148 7149 4103 +a 7149 7150 4103 +a 7150 7151 4103 +a 7151 7152 4103 +a 7152 7153 4103 +a 7153 7154 4103 +a 7154 7155 4103 +a 7155 7156 4103 +a 7156 7157 4103 +a 7157 7158 4103 +a 7158 7159 4103 +a 7159 7160 4103 +a 7160 7161 4103 +a 7161 7162 4103 +a 7162 7163 4103 +a 7163 7164 4103 +a 7164 7165 4103 +a 7165 7166 4103 +a 7166 7167 4103 +a 7167 7168 4103 +a 7168 7169 4103 +a 7169 7170 4103 +a 7170 7171 4103 +a 7171 7172 4103 +a 7172 7173 4103 +a 7173 7174 4103 +a 7174 7175 4103 +a 7175 7176 4103 +a 7176 7177 4103 +a 7177 7178 4103 +a 7178 7179 4103 +a 7179 7180 4103 +a 7180 7181 4103 +a 7181 7182 4103 +a 7182 7183 4103 +a 7183 7184 4103 +a 7184 7185 4103 +a 7185 7186 4103 +a 7186 7187 4103 +a 7187 7188 4103 +a 7188 7189 4103 +a 7189 7190 4103 +a 7190 7191 4103 +a 7191 7192 4103 +a 7192 7193 4103 +a 7193 7194 4103 +a 7194 7195 4103 +a 7195 7196 4103 +a 7196 7197 4103 +a 7197 7198 4103 +a 7198 7199 4103 +a 7199 7200 4103 +a 7200 7201 4103 +a 7201 7202 4103 +a 7202 7203 4103 +a 7203 7204 4103 +a 7204 7205 4103 +a 7205 7206 4103 +a 7206 7207 4103 +a 7207 7208 4103 +a 7208 7209 4103 +a 7209 7210 4103 +a 7210 7211 4103 +a 7211 7212 4103 +a 7212 7213 4103 +a 7213 7214 4103 +a 7214 7215 4103 +a 7215 7216 4103 +a 7216 7217 4103 +a 7217 7218 4103 +a 7218 7219 4103 +a 7219 7220 4103 +a 7220 7221 4103 +a 7221 7222 4103 +a 7222 7223 4103 +a 7223 7224 4103 +a 7224 7225 4103 +a 7225 7226 4103 +a 7226 7227 4103 +a 7227 7228 4103 +a 7228 7229 4103 +a 7229 7230 4103 +a 7230 7231 4103 +a 7231 7232 4103 +a 7232 7233 4103 +a 7233 7234 4103 +a 7234 7235 4103 +a 7235 7236 4103 +a 7236 7237 4103 +a 7237 7238 4103 +a 7238 7239 4103 +a 7239 7240 4103 +a 7240 7241 4103 +a 7241 7242 4103 +a 7242 7243 4103 +a 7243 7244 4103 +a 7244 7245 4103 +a 7245 7246 4103 +a 7246 7247 4103 +a 7247 7248 4103 +a 7248 7249 4103 +a 7249 7250 4103 +a 7250 7251 4103 +a 7251 7252 4103 +a 7252 7253 4103 +a 7253 7254 4103 +a 7254 7255 4103 +a 7255 7256 4103 +a 7256 7257 4103 +a 7257 7258 4103 +a 7258 7259 4103 +a 7259 7260 4103 +a 7260 7261 4103 +a 7261 7262 4103 +a 7262 7263 4103 +a 7263 7264 4103 +a 7264 7265 4103 +a 7265 7266 4103 +a 7266 7267 4103 +a 7267 7268 4103 +a 7268 7269 4103 +a 7269 7270 4103 +a 7270 7271 4103 +a 7271 7272 4103 +a 7272 7273 4103 +a 7273 7274 4103 +a 7274 7275 4103 +a 7275 7276 4103 +a 7276 7277 4103 +a 7277 7278 4103 +a 7278 7279 4103 +a 7279 7280 4103 +a 7280 7281 4103 +a 7281 7282 4103 +a 7282 7283 4103 +a 7283 7284 4103 +a 7284 7285 4103 +a 7285 7286 4103 +a 7286 7287 4103 +a 7287 7288 4103 +a 7288 7289 4103 +a 7289 7290 4103 +a 7290 7291 4103 +a 7291 7292 4103 +a 7292 7293 4103 +a 7293 7294 4103 +a 7294 7295 4103 +a 7295 7296 4103 +a 7296 7297 4103 +a 7297 7298 4103 +a 7298 7299 4103 +a 7299 7300 4103 +a 7300 7301 4103 +a 7301 7302 4103 +a 7302 7303 4103 +a 7303 7304 4103 +a 7304 7305 4103 +a 7305 7306 4103 +a 7306 7307 4103 +a 7307 7308 4103 +a 7308 7309 4103 +a 7309 7310 4103 +a 7310 7311 4103 +a 7311 7312 4103 +a 7312 7313 4103 +a 7313 7314 4103 +a 7314 7315 4103 +a 7315 7316 4103 +a 7316 7317 4103 +a 7317 7318 4103 +a 7318 7319 4103 +a 7319 7320 4103 +a 7320 7321 4103 +a 7321 7322 4103 +a 7322 7323 4103 +a 7323 7324 4103 +a 7324 7325 4103 +a 7325 7326 4103 +a 7326 7327 4103 +a 7327 7328 4103 +a 7328 7329 4103 +a 7329 7330 4103 +a 7330 7331 4103 +a 7331 7332 4103 +a 7332 7333 4103 +a 7333 7334 4103 +a 7334 7335 4103 +a 7335 7336 4103 +a 7336 7337 4103 +a 7337 7338 4103 +a 7338 7339 4103 +a 7339 7340 4103 +a 7340 7341 4103 +a 7341 7342 4103 +a 7342 7343 4103 +a 7343 7344 4103 +a 7344 7345 4103 +a 7345 7346 4103 +a 7346 7347 4103 +a 7347 7348 4103 +a 7348 7349 4103 +a 7349 7350 4103 +a 7350 7351 4103 +a 7351 7352 4103 +a 7352 7353 4103 +a 7353 7354 4103 +a 7354 7355 4103 +a 7355 7356 4103 +a 7356 7357 4103 +a 7357 7358 4103 +a 7358 7359 4103 +a 7359 7360 4103 +a 7360 7361 4103 +a 7361 7362 4103 +a 7362 7363 4103 +a 7363 7364 4103 +a 7364 7365 4103 +a 7365 7366 4103 +a 7366 7367 4103 +a 7367 7368 4103 +a 7368 7369 4103 +a 7369 7370 4103 +a 7370 7371 4103 +a 7371 7372 4103 +a 7372 7373 4103 +a 7373 7374 4103 +a 7374 7375 4103 +a 7375 7376 4103 +a 7376 7377 4103 +a 7377 7378 4103 +a 7378 7379 4103 +a 7379 7380 4103 +a 7380 7381 4103 +a 7381 7382 4103 +a 7382 7383 4103 +a 7383 7384 4103 +a 7384 7385 4103 +a 7385 7386 4103 +a 7386 7387 4103 +a 7387 7388 4103 +a 7388 7389 4103 +a 7389 7390 4103 +a 7390 7391 4103 +a 7391 7392 4103 +a 7392 7393 4103 +a 7393 7394 4103 +a 7394 7395 4103 +a 7395 7396 4103 +a 7396 7397 4103 +a 7397 7398 4103 +a 7398 7399 4103 +a 7399 7400 4103 +a 7400 7401 4103 +a 7401 7402 4103 +a 7402 7403 4103 +a 7403 7404 4103 +a 7404 7405 4103 +a 7405 7406 4103 +a 7406 7407 4103 +a 7407 7408 4103 +a 7408 7409 4103 +a 7409 7410 4103 +a 7410 7411 4103 +a 7411 7412 4103 +a 7412 7413 4103 +a 7413 7414 4103 +a 7414 7415 4103 +a 7415 7416 4103 +a 7416 7417 4103 +a 7417 7418 4103 +a 7418 7419 4103 +a 7419 7420 4103 +a 7420 7421 4103 +a 7421 7422 4103 +a 7422 7423 4103 +a 7423 7424 4103 +a 7424 7425 4103 +a 7425 7426 4103 +a 7426 7427 4103 +a 7427 7428 4103 +a 7428 7429 4103 +a 7429 7430 4103 +a 7430 7431 4103 +a 7431 7432 4103 +a 7432 7433 4103 +a 7433 7434 4103 +a 7434 7435 4103 +a 7435 7436 4103 +a 7436 7437 4103 +a 7437 7438 4103 +a 7438 7439 4103 +a 7439 7440 4103 +a 7440 7441 4103 +a 7441 7442 4103 +a 7442 7443 4103 +a 7443 7444 4103 +a 7444 7445 4103 +a 7445 7446 4103 +a 7446 7447 4103 +a 7447 7448 4103 +a 7448 7449 4103 +a 7449 7450 4103 +a 7450 7451 4103 +a 7451 7452 4103 +a 7452 7453 4103 +a 7453 7454 4103 +a 7454 7455 4103 +a 7455 7456 4103 +a 7456 7457 4103 +a 7457 7458 4103 +a 7458 7459 4103 +a 7459 7460 4103 +a 7460 7461 4103 +a 7461 7462 4103 +a 7462 7463 4103 +a 7463 7464 4103 +a 7464 7465 4103 +a 7465 7466 4103 +a 7466 7467 4103 +a 7467 7468 4103 +a 7468 7469 4103 +a 7469 7470 4103 +a 7470 7471 4103 +a 7471 7472 4103 +a 7472 7473 4103 +a 7473 7474 4103 +a 7474 7475 4103 +a 7475 7476 4103 +a 7476 7477 4103 +a 7477 7478 4103 +a 7478 7479 4103 +a 7479 7480 4103 +a 7480 7481 4103 +a 7481 7482 4103 +a 7482 7483 4103 +a 7483 7484 4103 +a 7484 7485 4103 +a 7485 7486 4103 +a 7486 7487 4103 +a 7487 7488 4103 +a 7488 7489 4103 +a 7489 7490 4103 +a 7490 7491 4103 +a 7491 7492 4103 +a 7492 7493 4103 +a 7493 7494 4103 +a 7494 7495 4103 +a 7495 7496 4103 +a 7496 7497 4103 +a 7497 7498 4103 +a 7498 7499 4103 +a 7499 7500 4103 +a 7500 7501 4103 +a 7501 7502 4103 +a 7502 7503 4103 +a 7503 7504 4103 +a 7504 7505 4103 +a 7505 7506 4103 +a 7506 7507 4103 +a 7507 7508 4103 +a 7508 7509 4103 +a 7509 7510 4103 +a 7510 7511 4103 +a 7511 7512 4103 +a 7512 7513 4103 +a 7513 7514 4103 +a 7514 7515 4103 +a 7515 7516 4103 +a 7516 7517 4103 +a 7517 7518 4103 +a 7518 7519 4103 +a 7519 7520 4103 +a 7520 7521 4103 +a 7521 7522 4103 +a 7522 7523 4103 +a 7523 7524 4103 +a 7524 7525 4103 +a 7525 7526 4103 +a 7526 7527 4103 +a 7527 7528 4103 +a 7528 7529 4103 +a 7529 7530 4103 +a 7530 7531 4103 +a 7531 7532 4103 +a 7532 7533 4103 +a 7533 7534 4103 +a 7534 7535 4103 +a 7535 7536 4103 +a 7536 7537 4103 +a 7537 7538 4103 +a 7538 7539 4103 +a 7539 7540 4103 +a 7540 7541 4103 +a 7541 7542 4103 +a 7542 7543 4103 +a 7543 7544 4103 +a 7544 7545 4103 +a 7545 7546 4103 +a 7546 7547 4103 +a 7547 7548 4103 +a 7548 7549 4103 +a 7549 7550 4103 +a 7550 7551 4103 +a 7551 7552 4103 +a 7552 7553 4103 +a 7553 7554 4103 +a 7554 7555 4103 +a 7555 7556 4103 +a 7556 7557 4103 +a 7557 7558 4103 +a 7558 7559 4103 +a 7559 7560 4103 +a 7560 7561 4103 +a 7561 7562 4103 +a 7562 7563 4103 +a 7563 7564 4103 +a 7564 7565 4103 +a 7565 7566 4103 +a 7566 7567 4103 +a 7567 7568 4103 +a 7568 7569 4103 +a 7569 7570 4103 +a 7570 7571 4103 +a 7571 7572 4103 +a 7572 7573 4103 +a 7573 7574 4103 +a 7574 7575 4103 +a 7575 7576 4103 +a 7576 7577 4103 +a 7577 7578 4103 +a 7578 7579 4103 +a 7579 7580 4103 +a 7580 7581 4103 +a 7581 7582 4103 +a 7582 7583 4103 +a 7583 7584 4103 +a 7584 7585 4103 +a 7585 7586 4103 +a 7586 7587 4103 +a 7587 7588 4103 +a 7588 7589 4103 +a 7589 7590 4103 +a 7590 7591 4103 +a 7591 7592 4103 +a 7592 7593 4103 +a 7593 7594 4103 +a 7594 7595 4103 +a 7595 7596 4103 +a 7596 7597 4103 +a 7597 7598 4103 +a 7598 7599 4103 +a 7599 7600 4103 +a 7600 7601 4103 +a 7601 7602 4103 +a 7602 7603 4103 +a 7603 7604 4103 +a 7604 7605 4103 +a 7605 7606 4103 +a 7606 7607 4103 +a 7607 7608 4103 +a 7608 7609 4103 +a 7609 7610 4103 +a 7610 7611 4103 +a 7611 7612 4103 +a 7612 7613 4103 +a 7613 7614 4103 +a 7614 7615 4103 +a 7615 7616 4103 +a 7616 7617 4103 +a 7617 7618 4103 +a 7618 7619 4103 +a 7619 7620 4103 +a 7620 7621 4103 +a 7621 7622 4103 +a 7622 7623 4103 +a 7623 7624 4103 +a 7624 7625 4103 +a 7625 7626 4103 +a 7626 7627 4103 +a 7627 7628 4103 +a 7628 7629 4103 +a 7629 7630 4103 +a 7630 7631 4103 +a 7631 7632 4103 +a 7632 7633 4103 +a 7633 7634 4103 +a 7634 7635 4103 +a 7635 7636 4103 +a 7636 7637 4103 +a 7637 7638 4103 +a 7638 7639 4103 +a 7639 7640 4103 +a 7640 7641 4103 +a 7641 7642 4103 +a 7642 7643 4103 +a 7643 7644 4103 +a 7644 7645 4103 +a 7645 7646 4103 +a 7646 7647 4103 +a 7647 7648 4103 +a 7648 7649 4103 +a 7649 7650 4103 +a 7650 7651 4103 +a 7651 7652 4103 +a 7652 7653 4103 +a 7653 7654 4103 +a 7654 7655 4103 +a 7655 7656 4103 +a 7656 7657 4103 +a 7657 7658 4103 +a 7658 7659 4103 +a 7659 7660 4103 +a 7660 7661 4103 +a 7661 7662 4103 +a 7662 7663 4103 +a 7663 7664 4103 +a 7664 7665 4103 +a 7665 7666 4103 +a 7666 7667 4103 +a 7667 7668 4103 +a 7668 7669 4103 +a 7669 7670 4103 +a 7670 7671 4103 +a 7671 7672 4103 +a 7672 7673 4103 +a 7673 7674 4103 +a 7674 7675 4103 +a 7675 7676 4103 +a 7676 7677 4103 +a 7677 7678 4103 +a 7678 7679 4103 +a 7679 7680 4103 +a 7680 7681 4103 +a 7681 7682 4103 +a 7682 7683 4103 +a 7683 7684 4103 +a 7684 7685 4103 +a 7685 7686 4103 +a 7686 7687 4103 +a 7687 7688 4103 +a 7688 7689 4103 +a 7689 7690 4103 +a 7690 7691 4103 +a 7691 7692 4103 +a 7692 7693 4103 +a 7693 7694 4103 +a 7694 7695 4103 +a 7695 7696 4103 +a 7696 7697 4103 +a 7697 7698 4103 +a 7698 7699 4103 +a 7699 7700 4103 +a 7700 7701 4103 +a 7701 7702 4103 +a 7702 7703 4103 +a 7703 7704 4103 +a 7704 7705 4103 +a 7705 7706 4103 +a 7706 7707 4103 +a 7707 7708 4103 +a 7708 7709 4103 +a 7709 7710 4103 +a 7710 7711 4103 +a 7711 7712 4103 +a 7712 7713 4103 +a 7713 7714 4103 +a 7714 7715 4103 +a 7715 7716 4103 +a 7716 7717 4103 +a 7717 7718 4103 +a 7718 7719 4103 +a 7719 7720 4103 +a 7720 7721 4103 +a 7721 7722 4103 +a 7722 7723 4103 +a 7723 7724 4103 +a 7724 7725 4103 +a 7725 7726 4103 +a 7726 7727 4103 +a 7727 7728 4103 +a 7728 7729 4103 +a 7729 7730 4103 +a 7730 7731 4103 +a 7731 7732 4103 +a 7732 7733 4103 +a 7733 7734 4103 +a 7734 7735 4103 +a 7735 7736 4103 +a 7736 7737 4103 +a 7737 7738 4103 +a 7738 7739 4103 +a 7739 7740 4103 +a 7740 7741 4103 +a 7741 7742 4103 +a 7742 7743 4103 +a 7743 7744 4103 +a 7744 7745 4103 +a 7745 7746 4103 +a 7746 7747 4103 +a 7747 7748 4103 +a 7748 7749 4103 +a 7749 7750 4103 +a 7750 7751 4103 +a 7751 7752 4103 +a 7752 7753 4103 +a 7753 7754 4103 +a 7754 7755 4103 +a 7755 7756 4103 +a 7756 7757 4103 +a 7757 7758 4103 +a 7758 7759 4103 +a 7759 7760 4103 +a 7760 7761 4103 +a 7761 7762 4103 +a 7762 7763 4103 +a 7763 7764 4103 +a 7764 7765 4103 +a 7765 7766 4103 +a 7766 7767 4103 +a 7767 7768 4103 +a 7768 7769 4103 +a 7769 7770 4103 +a 7770 7771 4103 +a 7771 7772 4103 +a 7772 7773 4103 +a 7773 7774 4103 +a 7774 7775 4103 +a 7775 7776 4103 +a 7776 7777 4103 +a 7777 7778 4103 +a 7778 7779 4103 +a 7779 7780 4103 +a 7780 7781 4103 +a 7781 7782 4103 +a 7782 7783 4103 +a 7783 7784 4103 +a 7784 7785 4103 +a 7785 7786 4103 +a 7786 7787 4103 +a 7787 7788 4103 +a 7788 7789 4103 +a 7789 7790 4103 +a 7790 7791 4103 +a 7791 7792 4103 +a 7792 7793 4103 +a 7793 7794 4103 +a 7794 7795 4103 +a 7795 7796 4103 +a 7796 7797 4103 +a 7797 7798 4103 +a 7798 7799 4103 +a 7799 7800 4103 +a 7800 7801 4103 +a 7801 7802 4103 +a 7802 7803 4103 +a 7803 7804 4103 +a 7804 7805 4103 +a 7805 7806 4103 +a 7806 7807 4103 +a 7807 7808 4103 +a 7808 7809 4103 +a 7809 7810 4103 +a 7810 7811 4103 +a 7811 7812 4103 +a 7812 7813 4103 +a 7813 7814 4103 +a 7814 7815 4103 +a 7815 7816 4103 +a 7816 7817 4103 +a 7817 7818 4103 +a 7818 7819 4103 +a 7819 7820 4103 +a 7820 7821 4103 +a 7821 7822 4103 +a 7822 7823 4103 +a 7823 7824 4103 +a 7824 7825 4103 +a 7825 7826 4103 +a 7826 7827 4103 +a 7827 7828 4103 +a 7828 7829 4103 +a 7829 7830 4103 +a 7830 7831 4103 +a 7831 7832 4103 +a 7832 7833 4103 +a 7833 7834 4103 +a 7834 7835 4103 +a 7835 7836 4103 +a 7836 7837 4103 +a 7837 7838 4103 +a 7838 7839 4103 +a 7839 7840 4103 +a 7840 7841 4103 +a 7841 7842 4103 +a 7842 7843 4103 +a 7843 7844 4103 +a 7844 7845 4103 +a 7845 7846 4103 +a 7846 7847 4103 +a 7847 7848 4103 +a 7848 7849 4103 +a 7849 7850 4103 +a 7850 7851 4103 +a 7851 7852 4103 +a 7852 7853 4103 +a 7853 7854 4103 +a 7854 7855 4103 +a 7855 7856 4103 +a 7856 7857 4103 +a 7857 7858 4103 +a 7858 7859 4103 +a 7859 7860 4103 +a 7860 7861 4103 +a 7861 7862 4103 +a 7862 7863 4103 +a 7863 7864 4103 +a 7864 7865 4103 +a 7865 7866 4103 +a 7866 7867 4103 +a 7867 7868 4103 +a 7868 7869 4103 +a 7869 7870 4103 +a 7870 7871 4103 +a 7871 7872 4103 +a 7872 7873 4103 +a 7873 7874 4103 +a 7874 7875 4103 +a 7875 7876 4103 +a 7876 7877 4103 +a 7877 7878 4103 +a 7878 7879 4103 +a 7879 7880 4103 +a 7880 7881 4103 +a 7881 7882 4103 +a 7882 7883 4103 +a 7883 7884 4103 +a 7884 7885 4103 +a 7885 7886 4103 +a 7886 7887 4103 +a 7887 7888 4103 +a 7888 7889 4103 +a 7889 7890 4103 +a 7890 7891 4103 +a 7891 7892 4103 +a 7892 7893 4103 +a 7893 7894 4103 +a 7894 7895 4103 +a 7895 7896 4103 +a 7896 7897 4103 +a 7897 7898 4103 +a 7898 7899 4103 +a 7899 7900 4103 +a 7900 7901 4103 +a 7901 7902 4103 +a 7902 7903 4103 +a 7903 7904 4103 +a 7904 7905 4103 +a 7905 7906 4103 +a 7906 7907 4103 +a 7907 7908 4103 +a 7908 7909 4103 +a 7909 7910 4103 +a 7910 7911 4103 +a 7911 7912 4103 +a 7912 7913 4103 +a 7913 7914 4103 +a 7914 7915 4103 +a 7915 7916 4103 +a 7916 7917 4103 +a 7917 7918 4103 +a 7918 7919 4103 +a 7919 7920 4103 +a 7920 7921 4103 +a 7921 7922 4103 +a 7922 7923 4103 +a 7923 7924 4103 +a 7924 7925 4103 +a 7925 7926 4103 +a 7926 7927 4103 +a 7927 7928 4103 +a 7928 7929 4103 +a 7929 7930 4103 +a 7930 7931 4103 +a 7931 7932 4103 +a 7932 7933 4103 +a 7933 7934 4103 +a 7934 7935 4103 +a 7935 7936 4103 +a 7936 7937 4103 +a 7937 7938 4103 +a 7938 7939 4103 +a 7939 7940 4103 +a 7940 7941 4103 +a 7941 7942 4103 +a 7942 7943 4103 +a 7943 7944 4103 +a 7944 7945 4103 +a 7945 7946 4103 +a 7946 7947 4103 +a 7947 7948 4103 +a 7948 7949 4103 +a 7949 7950 4103 +a 7950 7951 4103 +a 7951 7952 4103 +a 7952 7953 4103 +a 7953 7954 4103 +a 7954 7955 4103 +a 7955 7956 4103 +a 7956 7957 4103 +a 7957 7958 4103 +a 7958 7959 4103 +a 7959 7960 4103 +a 7960 7961 4103 +a 7961 7962 4103 +a 7962 7963 4103 +a 7963 7964 4103 +a 7964 7965 4103 +a 7965 7966 4103 +a 7966 7967 4103 +a 7967 7968 4103 +a 7968 7969 4103 +a 7969 7970 4103 +a 7970 7971 4103 +a 7971 7972 4103 +a 7972 7973 4103 +a 7973 7974 4103 +a 7974 7975 4103 +a 7975 7976 4103 +a 7976 7977 4103 +a 7977 7978 4103 +a 7978 7979 4103 +a 7979 7980 4103 +a 7980 7981 4103 +a 7981 7982 4103 +a 7982 7983 4103 +a 7983 7984 4103 +a 7984 7985 4103 +a 7985 7986 4103 +a 7986 7987 4103 +a 7987 7988 4103 +a 7988 7989 4103 +a 7989 7990 4103 +a 7990 7991 4103 +a 7991 7992 4103 +a 7992 7993 4103 +a 7993 7994 4103 +a 7994 7995 4103 +a 7995 7996 4103 +a 7996 7997 4103 +a 7997 7998 4103 +a 7998 7999 4103 +a 7999 8000 4103 +a 8000 8001 4103 +a 8001 8002 4103 +a 8002 8003 4103 +a 8003 8004 4103 +a 8004 8005 4103 +a 8005 8006 4103 +a 8006 8007 4103 +a 8007 8008 4103 +a 8008 8009 4103 +a 8009 8010 4103 +a 8010 8011 4103 +a 8011 8012 4103 +a 8012 8013 4103 +a 8013 8014 4103 +a 8014 8015 4103 +a 8015 8016 4103 +a 8016 8017 4103 +a 8017 8018 4103 +a 8018 8019 4103 +a 8019 8020 4103 +a 8020 8021 4103 +a 8021 8022 4103 +a 8022 8023 4103 +a 8023 8024 4103 +a 8024 8025 4103 +a 8025 8026 4103 +a 8026 8027 4103 +a 8027 8028 4103 +a 8028 8029 4103 +a 8029 8030 4103 +a 8030 8031 4103 +a 8031 8032 4103 +a 8032 8033 4103 +a 8033 8034 4103 +a 8034 8035 4103 +a 8035 8036 4103 +a 8036 8037 4103 +a 8037 8038 4103 +a 8038 8039 4103 +a 8039 8040 4103 +a 8040 8041 4103 +a 8041 8042 4103 +a 8042 8043 4103 +a 8043 8044 4103 +a 8044 8045 4103 +a 8045 8046 4103 +a 8046 8047 4103 +a 8047 8048 4103 +a 8048 8049 4103 +a 8049 8050 4103 +a 8050 8051 4103 +a 8051 8052 4103 +a 8052 8053 4103 +a 8053 8054 4103 +a 8054 8055 4103 +a 8055 8056 4103 +a 8056 8057 4103 +a 8057 8058 4103 +a 8058 8059 4103 +a 8059 8060 4103 +a 8060 8061 4103 +a 8061 8062 4103 +a 8062 8063 4103 +a 8063 8064 4103 +a 8064 8065 4103 +a 8065 8066 4103 +a 8066 8067 4103 +a 8067 8068 4103 +a 8068 8069 4103 +a 8069 8070 4103 +a 8070 8071 4103 +a 8071 8072 4103 +a 8072 8073 4103 +a 8073 8074 4103 +a 8074 8075 4103 +a 8075 8076 4103 +a 8076 8077 4103 +a 8077 8078 4103 +a 8078 8079 4103 +a 8079 8080 4103 +a 8080 8081 4103 +a 8081 8082 4103 +a 8082 8083 4103 +a 8083 8084 4103 +a 8084 8085 4103 +a 8085 8086 4103 +a 8086 8087 4103 +a 8087 8088 4103 +a 8088 8089 4103 +a 8089 8090 4103 +a 8090 8091 4103 +a 8091 8092 4103 +a 8092 8093 4103 +a 8093 8094 4103 +a 8094 8095 4103 +a 8095 8096 4103 +a 8096 8097 4103 +a 8097 8098 4103 +a 8098 8099 4103 +a 8099 8100 4103 +a 8100 8101 4103 +a 8101 8102 4103 +a 8102 8103 4103 +a 8103 8104 4103 +a 8104 8105 4103 +a 8105 8106 4103 +a 8106 8107 4103 +a 8107 8108 4103 +a 8108 8109 4103 +a 8109 8110 4103 +a 8110 8111 4103 +a 8111 8112 4103 +a 8112 8113 4103 +a 8113 8114 4103 +a 8114 8115 4103 +a 8115 8116 4103 +a 8116 8117 4103 +a 8117 8118 4103 +a 8118 8119 4103 +a 8119 8120 4103 +a 8120 8121 4103 +a 8121 8122 4103 +a 8122 8123 4103 +a 8123 8124 4103 +a 8124 8125 4103 +a 8125 8126 4103 +a 8126 8127 4103 +a 8127 8128 4103 +a 8128 8129 4103 +a 8129 8130 4103 +a 8130 8131 4103 +a 8131 8132 4103 +a 8132 8133 4103 +a 8133 8134 4103 +a 8134 8135 4103 +a 8135 8136 4103 +a 8136 8137 4103 +a 8137 8138 4103 +a 8138 8139 4103 +a 8139 8140 4103 +a 8140 8141 4103 +a 8141 8142 4103 +a 8142 8143 4103 +a 8143 8144 4103 +a 8144 8145 4103 +a 8145 8146 4103 +a 8146 8147 4103 +a 8147 8148 4103 +a 8148 8149 4103 +a 8149 8150 4103 +a 8150 8151 4103 +a 8151 8152 4103 +a 8152 8153 4103 +a 8153 8154 4103 +a 8154 8155 4103 +a 8155 8156 4103 +a 8156 8157 4103 +a 8157 8158 4103 +a 8158 8159 4103 +a 8159 8160 4103 +a 8160 8161 4103 +a 8161 8162 4103 +a 8162 8163 4103 +a 8163 8164 4103 +a 8164 8165 4103 +a 8165 8166 4103 +a 8166 8167 4103 +a 8167 8168 4103 +a 8168 8169 4103 +a 8169 8170 4103 +a 8170 8171 4103 +a 8171 8172 4103 +a 8172 8173 4103 +a 8173 8174 4103 +a 8174 8175 4103 +a 8175 8176 4103 +a 8176 8177 4103 +a 8177 8178 4103 +a 8178 8179 4103 +a 8179 8180 4103 +a 8180 8181 4103 +a 8181 8182 4103 +a 8182 8183 4103 +a 8183 8184 4103 +a 8184 8185 4103 +a 8185 8186 4103 +a 8186 8187 4103 +a 8187 8188 4103 +a 8188 8189 4103 +a 8189 8190 4103 +a 8190 8191 4103 +a 8191 8192 4103 +a 8192 8193 4103 +a 8193 8194 4103 +a 8194 8195 4103 +a 8195 8196 4103 +a 8196 8197 4103 +a 8197 8198 4103 +a 8198 8199 4103 +a 8199 8200 4103 +a 8200 8201 4103 +a 8201 8202 4103 +a 8202 8203 4103 +a 8203 8204 4103 +a 8204 8205 4103 +a 8205 8206 4103 +a 8206 8207 4103 +a 8207 8208 4103 +a 8209 8210 4102 +a 8210 8211 4102 +a 8211 8212 4102 +a 8212 8213 4102 +a 8213 8214 4102 +a 8214 8215 4102 +a 8215 8216 4102 +a 8216 8217 4102 +a 8217 8218 4102 +a 8218 8219 4102 +a 8219 8220 4102 +a 8220 8221 4102 +a 8221 8222 4102 +a 8222 8223 4102 +a 8223 8224 4102 +a 8224 8225 4102 +a 8225 8226 4102 +a 8226 8227 4102 +a 8227 8228 4102 +a 8228 8229 4102 +a 8229 8230 4102 +a 8230 8231 4102 +a 8231 8232 4102 +a 8232 8233 4102 +a 8233 8234 4102 +a 8234 8235 4102 +a 8235 8236 4102 +a 8236 8237 4102 +a 8237 8238 4102 +a 8238 8239 4102 +a 8239 8240 4102 +a 8240 8241 4102 +a 8241 8242 4102 +a 8242 8243 4102 +a 8243 8244 4102 +a 8244 8245 4102 +a 8245 8246 4102 +a 8246 8247 4102 +a 8247 8248 4102 +a 8248 8249 4102 +a 8249 8250 4102 +a 8250 8251 4102 +a 8251 8252 4102 +a 8252 8253 4102 +a 8253 8254 4102 +a 8254 8255 4102 +a 8255 8256 4102 +a 8256 8257 4102 +a 8257 8258 4102 +a 8258 8259 4102 +a 8259 8260 4102 +a 8260 8261 4102 +a 8261 8262 4102 +a 8262 8263 4102 +a 8263 8264 4102 +a 8264 8265 4102 +a 8265 8266 4102 +a 8266 8267 4102 +a 8267 8268 4102 +a 8268 8269 4102 +a 8269 8270 4102 +a 8270 8271 4102 +a 8271 8272 4102 +a 8272 8273 4102 +a 8273 8274 4102 +a 8274 8275 4102 +a 8275 8276 4102 +a 8276 8277 4102 +a 8277 8278 4102 +a 8278 8279 4102 +a 8279 8280 4102 +a 8280 8281 4102 +a 8281 8282 4102 +a 8282 8283 4102 +a 8283 8284 4102 +a 8284 8285 4102 +a 8285 8286 4102 +a 8286 8287 4102 +a 8287 8288 4102 +a 8288 8289 4102 +a 8289 8290 4102 +a 8290 8291 4102 +a 8291 8292 4102 +a 8292 8293 4102 +a 8293 8294 4102 +a 8294 8295 4102 +a 8295 8296 4102 +a 8296 8297 4102 +a 8297 8298 4102 +a 8298 8299 4102 +a 8299 8300 4102 +a 8300 8301 4102 +a 8301 8302 4102 +a 8302 8303 4102 +a 8303 8304 4102 +a 8304 8305 4102 +a 8305 8306 4102 +a 8306 8307 4102 +a 8307 8308 4102 +a 8308 8309 4102 +a 8309 8310 4102 +a 8310 8311 4102 +a 8311 8312 4102 +a 8312 8313 4102 +a 8313 8314 4102 +a 8314 8315 4102 +a 8315 8316 4102 +a 8316 8317 4102 +a 8317 8318 4102 +a 8318 8319 4102 +a 8319 8320 4102 +a 8320 8321 4102 +a 8321 8322 4102 +a 8322 8323 4102 +a 8323 8324 4102 +a 8324 8325 4102 +a 8325 8326 4102 +a 8326 8327 4102 +a 8327 8328 4102 +a 8328 8329 4102 +a 8329 8330 4102 +a 8330 8331 4102 +a 8331 8332 4102 +a 8332 8333 4102 +a 8333 8334 4102 +a 8334 8335 4102 +a 8335 8336 4102 +a 8336 8337 4102 +a 8337 8338 4102 +a 8338 8339 4102 +a 8339 8340 4102 +a 8340 8341 4102 +a 8341 8342 4102 +a 8342 8343 4102 +a 8343 8344 4102 +a 8344 8345 4102 +a 8345 8346 4102 +a 8346 8347 4102 +a 8347 8348 4102 +a 8348 8349 4102 +a 8349 8350 4102 +a 8350 8351 4102 +a 8351 8352 4102 +a 8352 8353 4102 +a 8353 8354 4102 +a 8354 8355 4102 +a 8355 8356 4102 +a 8356 8357 4102 +a 8357 8358 4102 +a 8358 8359 4102 +a 8359 8360 4102 +a 8360 8361 4102 +a 8361 8362 4102 +a 8362 8363 4102 +a 8363 8364 4102 +a 8364 8365 4102 +a 8365 8366 4102 +a 8366 8367 4102 +a 8367 8368 4102 +a 8368 8369 4102 +a 8369 8370 4102 +a 8370 8371 4102 +a 8371 8372 4102 +a 8372 8373 4102 +a 8373 8374 4102 +a 8374 8375 4102 +a 8375 8376 4102 +a 8376 8377 4102 +a 8377 8378 4102 +a 8378 8379 4102 +a 8379 8380 4102 +a 8380 8381 4102 +a 8381 8382 4102 +a 8382 8383 4102 +a 8383 8384 4102 +a 8384 8385 4102 +a 8385 8386 4102 +a 8386 8387 4102 +a 8387 8388 4102 +a 8388 8389 4102 +a 8389 8390 4102 +a 8390 8391 4102 +a 8391 8392 4102 +a 8392 8393 4102 +a 8393 8394 4102 +a 8394 8395 4102 +a 8395 8396 4102 +a 8396 8397 4102 +a 8397 8398 4102 +a 8398 8399 4102 +a 8399 8400 4102 +a 8400 8401 4102 +a 8401 8402 4102 +a 8402 8403 4102 +a 8403 8404 4102 +a 8404 8405 4102 +a 8405 8406 4102 +a 8406 8407 4102 +a 8407 8408 4102 +a 8408 8409 4102 +a 8409 8410 4102 +a 8410 8411 4102 +a 8411 8412 4102 +a 8412 8413 4102 +a 8413 8414 4102 +a 8414 8415 4102 +a 8415 8416 4102 +a 8416 8417 4102 +a 8417 8418 4102 +a 8418 8419 4102 +a 8419 8420 4102 +a 8420 8421 4102 +a 8421 8422 4102 +a 8422 8423 4102 +a 8423 8424 4102 +a 8424 8425 4102 +a 8425 8426 4102 +a 8426 8427 4102 +a 8427 8428 4102 +a 8428 8429 4102 +a 8429 8430 4102 +a 8430 8431 4102 +a 8431 8432 4102 +a 8432 8433 4102 +a 8433 8434 4102 +a 8434 8435 4102 +a 8435 8436 4102 +a 8436 8437 4102 +a 8437 8438 4102 +a 8438 8439 4102 +a 8439 8440 4102 +a 8440 8441 4102 +a 8441 8442 4102 +a 8442 8443 4102 +a 8443 8444 4102 +a 8444 8445 4102 +a 8445 8446 4102 +a 8446 8447 4102 +a 8447 8448 4102 +a 8448 8449 4102 +a 8449 8450 4102 +a 8450 8451 4102 +a 8451 8452 4102 +a 8452 8453 4102 +a 8453 8454 4102 +a 8454 8455 4102 +a 8455 8456 4102 +a 8456 8457 4102 +a 8457 8458 4102 +a 8458 8459 4102 +a 8459 8460 4102 +a 8460 8461 4102 +a 8461 8462 4102 +a 8462 8463 4102 +a 8463 8464 4102 +a 8464 8465 4102 +a 8465 8466 4102 +a 8466 8467 4102 +a 8467 8468 4102 +a 8468 8469 4102 +a 8469 8470 4102 +a 8470 8471 4102 +a 8471 8472 4102 +a 8472 8473 4102 +a 8473 8474 4102 +a 8474 8475 4102 +a 8475 8476 4102 +a 8476 8477 4102 +a 8477 8478 4102 +a 8478 8479 4102 +a 8479 8480 4102 +a 8480 8481 4102 +a 8481 8482 4102 +a 8482 8483 4102 +a 8483 8484 4102 +a 8484 8485 4102 +a 8485 8486 4102 +a 8486 8487 4102 +a 8487 8488 4102 +a 8488 8489 4102 +a 8489 8490 4102 +a 8490 8491 4102 +a 8491 8492 4102 +a 8492 8493 4102 +a 8493 8494 4102 +a 8494 8495 4102 +a 8495 8496 4102 +a 8496 8497 4102 +a 8497 8498 4102 +a 8498 8499 4102 +a 8499 8500 4102 +a 8500 8501 4102 +a 8501 8502 4102 +a 8502 8503 4102 +a 8503 8504 4102 +a 8504 8505 4102 +a 8505 8506 4102 +a 8506 8507 4102 +a 8507 8508 4102 +a 8508 8509 4102 +a 8509 8510 4102 +a 8510 8511 4102 +a 8511 8512 4102 +a 8512 8513 4102 +a 8513 8514 4102 +a 8514 8515 4102 +a 8515 8516 4102 +a 8516 8517 4102 +a 8517 8518 4102 +a 8518 8519 4102 +a 8519 8520 4102 +a 8520 8521 4102 +a 8521 8522 4102 +a 8522 8523 4102 +a 8523 8524 4102 +a 8524 8525 4102 +a 8525 8526 4102 +a 8526 8527 4102 +a 8527 8528 4102 +a 8528 8529 4102 +a 8529 8530 4102 +a 8530 8531 4102 +a 8531 8532 4102 +a 8532 8533 4102 +a 8533 8534 4102 +a 8534 8535 4102 +a 8535 8536 4102 +a 8536 8537 4102 +a 8537 8538 4102 +a 8538 8539 4102 +a 8539 8540 4102 +a 8540 8541 4102 +a 8541 8542 4102 +a 8542 8543 4102 +a 8543 8544 4102 +a 8544 8545 4102 +a 8545 8546 4102 +a 8546 8547 4102 +a 8547 8548 4102 +a 8548 8549 4102 +a 8549 8550 4102 +a 8550 8551 4102 +a 8551 8552 4102 +a 8552 8553 4102 +a 8553 8554 4102 +a 8554 8555 4102 +a 8555 8556 4102 +a 8556 8557 4102 +a 8557 8558 4102 +a 8558 8559 4102 +a 8559 8560 4102 +a 8560 8561 4102 +a 8561 8562 4102 +a 8562 8563 4102 +a 8563 8564 4102 +a 8564 8565 4102 +a 8565 8566 4102 +a 8566 8567 4102 +a 8567 8568 4102 +a 8568 8569 4102 +a 8569 8570 4102 +a 8570 8571 4102 +a 8571 8572 4102 +a 8572 8573 4102 +a 8573 8574 4102 +a 8574 8575 4102 +a 8575 8576 4102 +a 8576 8577 4102 +a 8577 8578 4102 +a 8578 8579 4102 +a 8579 8580 4102 +a 8580 8581 4102 +a 8581 8582 4102 +a 8582 8583 4102 +a 8583 8584 4102 +a 8584 8585 4102 +a 8585 8586 4102 +a 8586 8587 4102 +a 8587 8588 4102 +a 8588 8589 4102 +a 8589 8590 4102 +a 8590 8591 4102 +a 8591 8592 4102 +a 8592 8593 4102 +a 8593 8594 4102 +a 8594 8595 4102 +a 8595 8596 4102 +a 8596 8597 4102 +a 8597 8598 4102 +a 8598 8599 4102 +a 8599 8600 4102 +a 8600 8601 4102 +a 8601 8602 4102 +a 8602 8603 4102 +a 8603 8604 4102 +a 8604 8605 4102 +a 8605 8606 4102 +a 8606 8607 4102 +a 8607 8608 4102 +a 8608 8609 4102 +a 8609 8610 4102 +a 8610 8611 4102 +a 8611 8612 4102 +a 8612 8613 4102 +a 8613 8614 4102 +a 8614 8615 4102 +a 8615 8616 4102 +a 8616 8617 4102 +a 8617 8618 4102 +a 8618 8619 4102 +a 8619 8620 4102 +a 8620 8621 4102 +a 8621 8622 4102 +a 8622 8623 4102 +a 8623 8624 4102 +a 8624 8625 4102 +a 8625 8626 4102 +a 8626 8627 4102 +a 8627 8628 4102 +a 8628 8629 4102 +a 8629 8630 4102 +a 8630 8631 4102 +a 8631 8632 4102 +a 8632 8633 4102 +a 8633 8634 4102 +a 8634 8635 4102 +a 8635 8636 4102 +a 8636 8637 4102 +a 8637 8638 4102 +a 8638 8639 4102 +a 8639 8640 4102 +a 8640 8641 4102 +a 8641 8642 4102 +a 8642 8643 4102 +a 8643 8644 4102 +a 8644 8645 4102 +a 8645 8646 4102 +a 8646 8647 4102 +a 8647 8648 4102 +a 8648 8649 4102 +a 8649 8650 4102 +a 8650 8651 4102 +a 8651 8652 4102 +a 8652 8653 4102 +a 8653 8654 4102 +a 8654 8655 4102 +a 8655 8656 4102 +a 8656 8657 4102 +a 8657 8658 4102 +a 8658 8659 4102 +a 8659 8660 4102 +a 8660 8661 4102 +a 8661 8662 4102 +a 8662 8663 4102 +a 8663 8664 4102 +a 8664 8665 4102 +a 8665 8666 4102 +a 8666 8667 4102 +a 8667 8668 4102 +a 8668 8669 4102 +a 8669 8670 4102 +a 8670 8671 4102 +a 8671 8672 4102 +a 8672 8673 4102 +a 8673 8674 4102 +a 8674 8675 4102 +a 8675 8676 4102 +a 8676 8677 4102 +a 8677 8678 4102 +a 8678 8679 4102 +a 8679 8680 4102 +a 8680 8681 4102 +a 8681 8682 4102 +a 8682 8683 4102 +a 8683 8684 4102 +a 8684 8685 4102 +a 8685 8686 4102 +a 8686 8687 4102 +a 8687 8688 4102 +a 8688 8689 4102 +a 8689 8690 4102 +a 8690 8691 4102 +a 8691 8692 4102 +a 8692 8693 4102 +a 8693 8694 4102 +a 8694 8695 4102 +a 8695 8696 4102 +a 8696 8697 4102 +a 8697 8698 4102 +a 8698 8699 4102 +a 8699 8700 4102 +a 8700 8701 4102 +a 8701 8702 4102 +a 8702 8703 4102 +a 8703 8704 4102 +a 8704 8705 4102 +a 8705 8706 4102 +a 8706 8707 4102 +a 8707 8708 4102 +a 8708 8709 4102 +a 8709 8710 4102 +a 8710 8711 4102 +a 8711 8712 4102 +a 8712 8713 4102 +a 8713 8714 4102 +a 8714 8715 4102 +a 8715 8716 4102 +a 8716 8717 4102 +a 8717 8718 4102 +a 8718 8719 4102 +a 8719 8720 4102 +a 8720 8721 4102 +a 8721 8722 4102 +a 8722 8723 4102 +a 8723 8724 4102 +a 8724 8725 4102 +a 8725 8726 4102 +a 8726 8727 4102 +a 8727 8728 4102 +a 8728 8729 4102 +a 8729 8730 4102 +a 8730 8731 4102 +a 8731 8732 4102 +a 8732 8733 4102 +a 8733 8734 4102 +a 8734 8735 4102 +a 8735 8736 4102 +a 8736 8737 4102 +a 8737 8738 4102 +a 8738 8739 4102 +a 8739 8740 4102 +a 8740 8741 4102 +a 8741 8742 4102 +a 8742 8743 4102 +a 8743 8744 4102 +a 8744 8745 4102 +a 8745 8746 4102 +a 8746 8747 4102 +a 8747 8748 4102 +a 8748 8749 4102 +a 8749 8750 4102 +a 8750 8751 4102 +a 8751 8752 4102 +a 8752 8753 4102 +a 8753 8754 4102 +a 8754 8755 4102 +a 8755 8756 4102 +a 8756 8757 4102 +a 8757 8758 4102 +a 8758 8759 4102 +a 8759 8760 4102 +a 8760 8761 4102 +a 8761 8762 4102 +a 8762 8763 4102 +a 8763 8764 4102 +a 8764 8765 4102 +a 8765 8766 4102 +a 8766 8767 4102 +a 8767 8768 4102 +a 8768 8769 4102 +a 8769 8770 4102 +a 8770 8771 4102 +a 8771 8772 4102 +a 8772 8773 4102 +a 8773 8774 4102 +a 8774 8775 4102 +a 8775 8776 4102 +a 8776 8777 4102 +a 8777 8778 4102 +a 8778 8779 4102 +a 8779 8780 4102 +a 8780 8781 4102 +a 8781 8782 4102 +a 8782 8783 4102 +a 8783 8784 4102 +a 8784 8785 4102 +a 8785 8786 4102 +a 8786 8787 4102 +a 8787 8788 4102 +a 8788 8789 4102 +a 8789 8790 4102 +a 8790 8791 4102 +a 8791 8792 4102 +a 8792 8793 4102 +a 8793 8794 4102 +a 8794 8795 4102 +a 8795 8796 4102 +a 8796 8797 4102 +a 8797 8798 4102 +a 8798 8799 4102 +a 8799 8800 4102 +a 8800 8801 4102 +a 8801 8802 4102 +a 8802 8803 4102 +a 8803 8804 4102 +a 8804 8805 4102 +a 8805 8806 4102 +a 8806 8807 4102 +a 8807 8808 4102 +a 8808 8809 4102 +a 8809 8810 4102 +a 8810 8811 4102 +a 8811 8812 4102 +a 8812 8813 4102 +a 8813 8814 4102 +a 8814 8815 4102 +a 8815 8816 4102 +a 8816 8817 4102 +a 8817 8818 4102 +a 8818 8819 4102 +a 8819 8820 4102 +a 8820 8821 4102 +a 8821 8822 4102 +a 8822 8823 4102 +a 8823 8824 4102 +a 8824 8825 4102 +a 8825 8826 4102 +a 8826 8827 4102 +a 8827 8828 4102 +a 8828 8829 4102 +a 8829 8830 4102 +a 8830 8831 4102 +a 8831 8832 4102 +a 8832 8833 4102 +a 8833 8834 4102 +a 8834 8835 4102 +a 8835 8836 4102 +a 8836 8837 4102 +a 8837 8838 4102 +a 8838 8839 4102 +a 8839 8840 4102 +a 8840 8841 4102 +a 8841 8842 4102 +a 8842 8843 4102 +a 8843 8844 4102 +a 8844 8845 4102 +a 8845 8846 4102 +a 8846 8847 4102 +a 8847 8848 4102 +a 8848 8849 4102 +a 8849 8850 4102 +a 8850 8851 4102 +a 8851 8852 4102 +a 8852 8853 4102 +a 8853 8854 4102 +a 8854 8855 4102 +a 8855 8856 4102 +a 8856 8857 4102 +a 8857 8858 4102 +a 8858 8859 4102 +a 8859 8860 4102 +a 8860 8861 4102 +a 8861 8862 4102 +a 8862 8863 4102 +a 8863 8864 4102 +a 8864 8865 4102 +a 8865 8866 4102 +a 8866 8867 4102 +a 8867 8868 4102 +a 8868 8869 4102 +a 8869 8870 4102 +a 8870 8871 4102 +a 8871 8872 4102 +a 8872 8873 4102 +a 8873 8874 4102 +a 8874 8875 4102 +a 8875 8876 4102 +a 8876 8877 4102 +a 8877 8878 4102 +a 8878 8879 4102 +a 8879 8880 4102 +a 8880 8881 4102 +a 8881 8882 4102 +a 8882 8883 4102 +a 8883 8884 4102 +a 8884 8885 4102 +a 8885 8886 4102 +a 8886 8887 4102 +a 8887 8888 4102 +a 8888 8889 4102 +a 8889 8890 4102 +a 8890 8891 4102 +a 8891 8892 4102 +a 8892 8893 4102 +a 8893 8894 4102 +a 8894 8895 4102 +a 8895 8896 4102 +a 8896 8897 4102 +a 8897 8898 4102 +a 8898 8899 4102 +a 8899 8900 4102 +a 8900 8901 4102 +a 8901 8902 4102 +a 8902 8903 4102 +a 8903 8904 4102 +a 8904 8905 4102 +a 8905 8906 4102 +a 8906 8907 4102 +a 8907 8908 4102 +a 8908 8909 4102 +a 8909 8910 4102 +a 8910 8911 4102 +a 8911 8912 4102 +a 8912 8913 4102 +a 8913 8914 4102 +a 8914 8915 4102 +a 8915 8916 4102 +a 8916 8917 4102 +a 8917 8918 4102 +a 8918 8919 4102 +a 8919 8920 4102 +a 8920 8921 4102 +a 8921 8922 4102 +a 8922 8923 4102 +a 8923 8924 4102 +a 8924 8925 4102 +a 8925 8926 4102 +a 8926 8927 4102 +a 8927 8928 4102 +a 8928 8929 4102 +a 8929 8930 4102 +a 8930 8931 4102 +a 8931 8932 4102 +a 8932 8933 4102 +a 8933 8934 4102 +a 8934 8935 4102 +a 8935 8936 4102 +a 8936 8937 4102 +a 8937 8938 4102 +a 8938 8939 4102 +a 8939 8940 4102 +a 8940 8941 4102 +a 8941 8942 4102 +a 8942 8943 4102 +a 8943 8944 4102 +a 8944 8945 4102 +a 8945 8946 4102 +a 8946 8947 4102 +a 8947 8948 4102 +a 8948 8949 4102 +a 8949 8950 4102 +a 8950 8951 4102 +a 8951 8952 4102 +a 8952 8953 4102 +a 8953 8954 4102 +a 8954 8955 4102 +a 8955 8956 4102 +a 8956 8957 4102 +a 8957 8958 4102 +a 8958 8959 4102 +a 8959 8960 4102 +a 8960 8961 4102 +a 8961 8962 4102 +a 8962 8963 4102 +a 8963 8964 4102 +a 8964 8965 4102 +a 8965 8966 4102 +a 8966 8967 4102 +a 8967 8968 4102 +a 8968 8969 4102 +a 8969 8970 4102 +a 8970 8971 4102 +a 8971 8972 4102 +a 8972 8973 4102 +a 8973 8974 4102 +a 8974 8975 4102 +a 8975 8976 4102 +a 8976 8977 4102 +a 8977 8978 4102 +a 8978 8979 4102 +a 8979 8980 4102 +a 8980 8981 4102 +a 8981 8982 4102 +a 8982 8983 4102 +a 8983 8984 4102 +a 8984 8985 4102 +a 8985 8986 4102 +a 8986 8987 4102 +a 8987 8988 4102 +a 8988 8989 4102 +a 8989 8990 4102 +a 8990 8991 4102 +a 8991 8992 4102 +a 8992 8993 4102 +a 8993 8994 4102 +a 8994 8995 4102 +a 8995 8996 4102 +a 8996 8997 4102 +a 8997 8998 4102 +a 8998 8999 4102 +a 8999 9000 4102 +a 9000 9001 4102 +a 9001 9002 4102 +a 9002 9003 4102 +a 9003 9004 4102 +a 9004 9005 4102 +a 9005 9006 4102 +a 9006 9007 4102 +a 9007 9008 4102 +a 9008 9009 4102 +a 9009 9010 4102 +a 9010 9011 4102 +a 9011 9012 4102 +a 9012 9013 4102 +a 9013 9014 4102 +a 9014 9015 4102 +a 9015 9016 4102 +a 9016 9017 4102 +a 9017 9018 4102 +a 9018 9019 4102 +a 9019 9020 4102 +a 9020 9021 4102 +a 9021 9022 4102 +a 9022 9023 4102 +a 9023 9024 4102 +a 9024 9025 4102 +a 9025 9026 4102 +a 9026 9027 4102 +a 9027 9028 4102 +a 9028 9029 4102 +a 9029 9030 4102 +a 9030 9031 4102 +a 9031 9032 4102 +a 9032 9033 4102 +a 9033 9034 4102 +a 9034 9035 4102 +a 9035 9036 4102 +a 9036 9037 4102 +a 9037 9038 4102 +a 9038 9039 4102 +a 9039 9040 4102 +a 9040 9041 4102 +a 9041 9042 4102 +a 9042 9043 4102 +a 9043 9044 4102 +a 9044 9045 4102 +a 9045 9046 4102 +a 9046 9047 4102 +a 9047 9048 4102 +a 9048 9049 4102 +a 9049 9050 4102 +a 9050 9051 4102 +a 9051 9052 4102 +a 9052 9053 4102 +a 9053 9054 4102 +a 9054 9055 4102 +a 9055 9056 4102 +a 9056 9057 4102 +a 9057 9058 4102 +a 9058 9059 4102 +a 9059 9060 4102 +a 9060 9061 4102 +a 9061 9062 4102 +a 9062 9063 4102 +a 9063 9064 4102 +a 9064 9065 4102 +a 9065 9066 4102 +a 9066 9067 4102 +a 9067 9068 4102 +a 9068 9069 4102 +a 9069 9070 4102 +a 9070 9071 4102 +a 9071 9072 4102 +a 9072 9073 4102 +a 9073 9074 4102 +a 9074 9075 4102 +a 9075 9076 4102 +a 9076 9077 4102 +a 9077 9078 4102 +a 9078 9079 4102 +a 9079 9080 4102 +a 9080 9081 4102 +a 9081 9082 4102 +a 9082 9083 4102 +a 9083 9084 4102 +a 9084 9085 4102 +a 9085 9086 4102 +a 9086 9087 4102 +a 9087 9088 4102 +a 9088 9089 4102 +a 9089 9090 4102 +a 9090 9091 4102 +a 9091 9092 4102 +a 9092 9093 4102 +a 9093 9094 4102 +a 9094 9095 4102 +a 9095 9096 4102 +a 9096 9097 4102 +a 9097 9098 4102 +a 9098 9099 4102 +a 9099 9100 4102 +a 9100 9101 4102 +a 9101 9102 4102 +a 9102 9103 4102 +a 9103 9104 4102 +a 9104 9105 4102 +a 9105 9106 4102 +a 9106 9107 4102 +a 9107 9108 4102 +a 9108 9109 4102 +a 9109 9110 4102 +a 9110 9111 4102 +a 9111 9112 4102 +a 9112 9113 4102 +a 9113 9114 4102 +a 9114 9115 4102 +a 9115 9116 4102 +a 9116 9117 4102 +a 9117 9118 4102 +a 9118 9119 4102 +a 9119 9120 4102 +a 9120 9121 4102 +a 9121 9122 4102 +a 9122 9123 4102 +a 9123 9124 4102 +a 9124 9125 4102 +a 9125 9126 4102 +a 9126 9127 4102 +a 9127 9128 4102 +a 9128 9129 4102 +a 9129 9130 4102 +a 9130 9131 4102 +a 9131 9132 4102 +a 9132 9133 4102 +a 9133 9134 4102 +a 9134 9135 4102 +a 9135 9136 4102 +a 9136 9137 4102 +a 9137 9138 4102 +a 9138 9139 4102 +a 9139 9140 4102 +a 9140 9141 4102 +a 9141 9142 4102 +a 9142 9143 4102 +a 9143 9144 4102 +a 9144 9145 4102 +a 9145 9146 4102 +a 9146 9147 4102 +a 9147 9148 4102 +a 9148 9149 4102 +a 9149 9150 4102 +a 9150 9151 4102 +a 9151 9152 4102 +a 9152 9153 4102 +a 9153 9154 4102 +a 9154 9155 4102 +a 9155 9156 4102 +a 9156 9157 4102 +a 9157 9158 4102 +a 9158 9159 4102 +a 9159 9160 4102 +a 9160 9161 4102 +a 9161 9162 4102 +a 9162 9163 4102 +a 9163 9164 4102 +a 9164 9165 4102 +a 9165 9166 4102 +a 9166 9167 4102 +a 9167 9168 4102 +a 9168 9169 4102 +a 9169 9170 4102 +a 9170 9171 4102 +a 9171 9172 4102 +a 9172 9173 4102 +a 9173 9174 4102 +a 9174 9175 4102 +a 9175 9176 4102 +a 9176 9177 4102 +a 9177 9178 4102 +a 9178 9179 4102 +a 9179 9180 4102 +a 9180 9181 4102 +a 9181 9182 4102 +a 9182 9183 4102 +a 9183 9184 4102 +a 9184 9185 4102 +a 9185 9186 4102 +a 9186 9187 4102 +a 9187 9188 4102 +a 9188 9189 4102 +a 9189 9190 4102 +a 9190 9191 4102 +a 9191 9192 4102 +a 9192 9193 4102 +a 9193 9194 4102 +a 9194 9195 4102 +a 9195 9196 4102 +a 9196 9197 4102 +a 9197 9198 4102 +a 9198 9199 4102 +a 9199 9200 4102 +a 9200 9201 4102 +a 9201 9202 4102 +a 9202 9203 4102 +a 9203 9204 4102 +a 9204 9205 4102 +a 9205 9206 4102 +a 9206 9207 4102 +a 9207 9208 4102 +a 9208 9209 4102 +a 9209 9210 4102 +a 9210 9211 4102 +a 9211 9212 4102 +a 9212 9213 4102 +a 9213 9214 4102 +a 9214 9215 4102 +a 9215 9216 4102 +a 9216 9217 4102 +a 9217 9218 4102 +a 9218 9219 4102 +a 9219 9220 4102 +a 9220 9221 4102 +a 9221 9222 4102 +a 9222 9223 4102 +a 9223 9224 4102 +a 9224 9225 4102 +a 9225 9226 4102 +a 9226 9227 4102 +a 9227 9228 4102 +a 9228 9229 4102 +a 9229 9230 4102 +a 9230 9231 4102 +a 9231 9232 4102 +a 9232 9233 4102 +a 9233 9234 4102 +a 9234 9235 4102 +a 9235 9236 4102 +a 9236 9237 4102 +a 9237 9238 4102 +a 9238 9239 4102 +a 9239 9240 4102 +a 9240 9241 4102 +a 9241 9242 4102 +a 9242 9243 4102 +a 9243 9244 4102 +a 9244 9245 4102 +a 9245 9246 4102 +a 9246 9247 4102 +a 9247 9248 4102 +a 9248 9249 4102 +a 9249 9250 4102 +a 9250 9251 4102 +a 9251 9252 4102 +a 9252 9253 4102 +a 9253 9254 4102 +a 9254 9255 4102 +a 9255 9256 4102 +a 9256 9257 4102 +a 9257 9258 4102 +a 9258 9259 4102 +a 9259 9260 4102 +a 9260 9261 4102 +a 9261 9262 4102 +a 9262 9263 4102 +a 9263 9264 4102 +a 9264 9265 4102 +a 9265 9266 4102 +a 9266 9267 4102 +a 9267 9268 4102 +a 9268 9269 4102 +a 9269 9270 4102 +a 9270 9271 4102 +a 9271 9272 4102 +a 9272 9273 4102 +a 9273 9274 4102 +a 9274 9275 4102 +a 9275 9276 4102 +a 9276 9277 4102 +a 9277 9278 4102 +a 9278 9279 4102 +a 9279 9280 4102 +a 9280 9281 4102 +a 9281 9282 4102 +a 9282 9283 4102 +a 9283 9284 4102 +a 9284 9285 4102 +a 9285 9286 4102 +a 9286 9287 4102 +a 9287 9288 4102 +a 9288 9289 4102 +a 9289 9290 4102 +a 9290 9291 4102 +a 9291 9292 4102 +a 9292 9293 4102 +a 9293 9294 4102 +a 9294 9295 4102 +a 9295 9296 4102 +a 9296 9297 4102 +a 9297 9298 4102 +a 9298 9299 4102 +a 9299 9300 4102 +a 9300 9301 4102 +a 9301 9302 4102 +a 9302 9303 4102 +a 9303 9304 4102 +a 9304 9305 4102 +a 9305 9306 4102 +a 9306 9307 4102 +a 9307 9308 4102 +a 9308 9309 4102 +a 9309 9310 4102 +a 9310 9311 4102 +a 9311 9312 4102 +a 9312 9313 4102 +a 9313 9314 4102 +a 9314 9315 4102 +a 9315 9316 4102 +a 9316 9317 4102 +a 9317 9318 4102 +a 9318 9319 4102 +a 9319 9320 4102 +a 9320 9321 4102 +a 9321 9322 4102 +a 9322 9323 4102 +a 9323 9324 4102 +a 9324 9325 4102 +a 9325 9326 4102 +a 9326 9327 4102 +a 9327 9328 4102 +a 9328 9329 4102 +a 9329 9330 4102 +a 9330 9331 4102 +a 9331 9332 4102 +a 9332 9333 4102 +a 9333 9334 4102 +a 9334 9335 4102 +a 9335 9336 4102 +a 9336 9337 4102 +a 9337 9338 4102 +a 9338 9339 4102 +a 9339 9340 4102 +a 9340 9341 4102 +a 9341 9342 4102 +a 9342 9343 4102 +a 9343 9344 4102 +a 9344 9345 4102 +a 9345 9346 4102 +a 9346 9347 4102 +a 9347 9348 4102 +a 9348 9349 4102 +a 9349 9350 4102 +a 9350 9351 4102 +a 9351 9352 4102 +a 9352 9353 4102 +a 9353 9354 4102 +a 9354 9355 4102 +a 9355 9356 4102 +a 9356 9357 4102 +a 9357 9358 4102 +a 9358 9359 4102 +a 9359 9360 4102 +a 9360 9361 4102 +a 9361 9362 4102 +a 9362 9363 4102 +a 9363 9364 4102 +a 9364 9365 4102 +a 9365 9366 4102 +a 9366 9367 4102 +a 9367 9368 4102 +a 9368 9369 4102 +a 9369 9370 4102 +a 9370 9371 4102 +a 9371 9372 4102 +a 9372 9373 4102 +a 9373 9374 4102 +a 9374 9375 4102 +a 9375 9376 4102 +a 9376 9377 4102 +a 9377 9378 4102 +a 9378 9379 4102 +a 9379 9380 4102 +a 9380 9381 4102 +a 9381 9382 4102 +a 9382 9383 4102 +a 9383 9384 4102 +a 9384 9385 4102 +a 9385 9386 4102 +a 9386 9387 4102 +a 9387 9388 4102 +a 9388 9389 4102 +a 9389 9390 4102 +a 9390 9391 4102 +a 9391 9392 4102 +a 9392 9393 4102 +a 9393 9394 4102 +a 9394 9395 4102 +a 9395 9396 4102 +a 9396 9397 4102 +a 9397 9398 4102 +a 9398 9399 4102 +a 9399 9400 4102 +a 9400 9401 4102 +a 9401 9402 4102 +a 9402 9403 4102 +a 9403 9404 4102 +a 9404 9405 4102 +a 9405 9406 4102 +a 9406 9407 4102 +a 9407 9408 4102 +a 9408 9409 4102 +a 9409 9410 4102 +a 9410 9411 4102 +a 9411 9412 4102 +a 9412 9413 4102 +a 9413 9414 4102 +a 9414 9415 4102 +a 9415 9416 4102 +a 9416 9417 4102 +a 9417 9418 4102 +a 9418 9419 4102 +a 9419 9420 4102 +a 9420 9421 4102 +a 9421 9422 4102 +a 9422 9423 4102 +a 9423 9424 4102 +a 9424 9425 4102 +a 9425 9426 4102 +a 9426 9427 4102 +a 9427 9428 4102 +a 9428 9429 4102 +a 9429 9430 4102 +a 9430 9431 4102 +a 9431 9432 4102 +a 9432 9433 4102 +a 9433 9434 4102 +a 9434 9435 4102 +a 9435 9436 4102 +a 9436 9437 4102 +a 9437 9438 4102 +a 9438 9439 4102 +a 9439 9440 4102 +a 9440 9441 4102 +a 9441 9442 4102 +a 9442 9443 4102 +a 9443 9444 4102 +a 9444 9445 4102 +a 9445 9446 4102 +a 9446 9447 4102 +a 9447 9448 4102 +a 9448 9449 4102 +a 9449 9450 4102 +a 9450 9451 4102 +a 9451 9452 4102 +a 9452 9453 4102 +a 9453 9454 4102 +a 9454 9455 4102 +a 9455 9456 4102 +a 9456 9457 4102 +a 9457 9458 4102 +a 9458 9459 4102 +a 9459 9460 4102 +a 9460 9461 4102 +a 9461 9462 4102 +a 9462 9463 4102 +a 9463 9464 4102 +a 9464 9465 4102 +a 9465 9466 4102 +a 9466 9467 4102 +a 9467 9468 4102 +a 9468 9469 4102 +a 9469 9470 4102 +a 9470 9471 4102 +a 9471 9472 4102 +a 9472 9473 4102 +a 9473 9474 4102 +a 9474 9475 4102 +a 9475 9476 4102 +a 9476 9477 4102 +a 9477 9478 4102 +a 9478 9479 4102 +a 9479 9480 4102 +a 9480 9481 4102 +a 9481 9482 4102 +a 9482 9483 4102 +a 9483 9484 4102 +a 9484 9485 4102 +a 9485 9486 4102 +a 9486 9487 4102 +a 9487 9488 4102 +a 9488 9489 4102 +a 9489 9490 4102 +a 9490 9491 4102 +a 9491 9492 4102 +a 9492 9493 4102 +a 9493 9494 4102 +a 9494 9495 4102 +a 9495 9496 4102 +a 9496 9497 4102 +a 9497 9498 4102 +a 9498 9499 4102 +a 9499 9500 4102 +a 9500 9501 4102 +a 9501 9502 4102 +a 9502 9503 4102 +a 9503 9504 4102 +a 9504 9505 4102 +a 9505 9506 4102 +a 9506 9507 4102 +a 9507 9508 4102 +a 9508 9509 4102 +a 9509 9510 4102 +a 9510 9511 4102 +a 9511 9512 4102 +a 9512 9513 4102 +a 9513 9514 4102 +a 9514 9515 4102 +a 9515 9516 4102 +a 9516 9517 4102 +a 9517 9518 4102 +a 9518 9519 4102 +a 9519 9520 4102 +a 9520 9521 4102 +a 9521 9522 4102 +a 9522 9523 4102 +a 9523 9524 4102 +a 9524 9525 4102 +a 9525 9526 4102 +a 9526 9527 4102 +a 9527 9528 4102 +a 9528 9529 4102 +a 9529 9530 4102 +a 9530 9531 4102 +a 9531 9532 4102 +a 9532 9533 4102 +a 9533 9534 4102 +a 9534 9535 4102 +a 9535 9536 4102 +a 9536 9537 4102 +a 9537 9538 4102 +a 9538 9539 4102 +a 9539 9540 4102 +a 9540 9541 4102 +a 9541 9542 4102 +a 9542 9543 4102 +a 9543 9544 4102 +a 9544 9545 4102 +a 9545 9546 4102 +a 9546 9547 4102 +a 9547 9548 4102 +a 9548 9549 4102 +a 9549 9550 4102 +a 9550 9551 4102 +a 9551 9552 4102 +a 9552 9553 4102 +a 9553 9554 4102 +a 9554 9555 4102 +a 9555 9556 4102 +a 9556 9557 4102 +a 9557 9558 4102 +a 9558 9559 4102 +a 9559 9560 4102 +a 9560 9561 4102 +a 9561 9562 4102 +a 9562 9563 4102 +a 9563 9564 4102 +a 9564 9565 4102 +a 9565 9566 4102 +a 9566 9567 4102 +a 9567 9568 4102 +a 9568 9569 4102 +a 9569 9570 4102 +a 9570 9571 4102 +a 9571 9572 4102 +a 9572 9573 4102 +a 9573 9574 4102 +a 9574 9575 4102 +a 9575 9576 4102 +a 9576 9577 4102 +a 9577 9578 4102 +a 9578 9579 4102 +a 9579 9580 4102 +a 9580 9581 4102 +a 9581 9582 4102 +a 9582 9583 4102 +a 9583 9584 4102 +a 9584 9585 4102 +a 9585 9586 4102 +a 9586 9587 4102 +a 9587 9588 4102 +a 9588 9589 4102 +a 9589 9590 4102 +a 9590 9591 4102 +a 9591 9592 4102 +a 9592 9593 4102 +a 9593 9594 4102 +a 9594 9595 4102 +a 9595 9596 4102 +a 9596 9597 4102 +a 9597 9598 4102 +a 9598 9599 4102 +a 9599 9600 4102 +a 9600 9601 4102 +a 9601 9602 4102 +a 9602 9603 4102 +a 9603 9604 4102 +a 9604 9605 4102 +a 9605 9606 4102 +a 9606 9607 4102 +a 9607 9608 4102 +a 9608 9609 4102 +a 9609 9610 4102 +a 9610 9611 4102 +a 9611 9612 4102 +a 9612 9613 4102 +a 9613 9614 4102 +a 9614 9615 4102 +a 9615 9616 4102 +a 9616 9617 4102 +a 9617 9618 4102 +a 9618 9619 4102 +a 9619 9620 4102 +a 9620 9621 4102 +a 9621 9622 4102 +a 9622 9623 4102 +a 9623 9624 4102 +a 9624 9625 4102 +a 9625 9626 4102 +a 9626 9627 4102 +a 9627 9628 4102 +a 9628 9629 4102 +a 9629 9630 4102 +a 9630 9631 4102 +a 9631 9632 4102 +a 9632 9633 4102 +a 9633 9634 4102 +a 9634 9635 4102 +a 9635 9636 4102 +a 9636 9637 4102 +a 9637 9638 4102 +a 9638 9639 4102 +a 9639 9640 4102 +a 9640 9641 4102 +a 9641 9642 4102 +a 9642 9643 4102 +a 9643 9644 4102 +a 9644 9645 4102 +a 9645 9646 4102 +a 9646 9647 4102 +a 9647 9648 4102 +a 9648 9649 4102 +a 9649 9650 4102 +a 9650 9651 4102 +a 9651 9652 4102 +a 9652 9653 4102 +a 9653 9654 4102 +a 9654 9655 4102 +a 9655 9656 4102 +a 9656 9657 4102 +a 9657 9658 4102 +a 9658 9659 4102 +a 9659 9660 4102 +a 9660 9661 4102 +a 9661 9662 4102 +a 9662 9663 4102 +a 9663 9664 4102 +a 9664 9665 4102 +a 9665 9666 4102 +a 9666 9667 4102 +a 9667 9668 4102 +a 9668 9669 4102 +a 9669 9670 4102 +a 9670 9671 4102 +a 9671 9672 4102 +a 9672 9673 4102 +a 9673 9674 4102 +a 9674 9675 4102 +a 9675 9676 4102 +a 9676 9677 4102 +a 9677 9678 4102 +a 9678 9679 4102 +a 9679 9680 4102 +a 9680 9681 4102 +a 9681 9682 4102 +a 9682 9683 4102 +a 9683 9684 4102 +a 9684 9685 4102 +a 9685 9686 4102 +a 9686 9687 4102 +a 9687 9688 4102 +a 9688 9689 4102 +a 9689 9690 4102 +a 9690 9691 4102 +a 9691 9692 4102 +a 9692 9693 4102 +a 9693 9694 4102 +a 9694 9695 4102 +a 9695 9696 4102 +a 9696 9697 4102 +a 9697 9698 4102 +a 9698 9699 4102 +a 9699 9700 4102 +a 9700 9701 4102 +a 9701 9702 4102 +a 9702 9703 4102 +a 9703 9704 4102 +a 9704 9705 4102 +a 9705 9706 4102 +a 9706 9707 4102 +a 9707 9708 4102 +a 9708 9709 4102 +a 9709 9710 4102 +a 9710 9711 4102 +a 9711 9712 4102 +a 9712 9713 4102 +a 9713 9714 4102 +a 9714 9715 4102 +a 9715 9716 4102 +a 9716 9717 4102 +a 9717 9718 4102 +a 9718 9719 4102 +a 9719 9720 4102 +a 9720 9721 4102 +a 9721 9722 4102 +a 9722 9723 4102 +a 9723 9724 4102 +a 9724 9725 4102 +a 9725 9726 4102 +a 9726 9727 4102 +a 9727 9728 4102 +a 9728 9729 4102 +a 9729 9730 4102 +a 9730 9731 4102 +a 9731 9732 4102 +a 9732 9733 4102 +a 9733 9734 4102 +a 9734 9735 4102 +a 9735 9736 4102 +a 9736 9737 4102 +a 9737 9738 4102 +a 9738 9739 4102 +a 9739 9740 4102 +a 9740 9741 4102 +a 9741 9742 4102 +a 9742 9743 4102 +a 9743 9744 4102 +a 9744 9745 4102 +a 9745 9746 4102 +a 9746 9747 4102 +a 9747 9748 4102 +a 9748 9749 4102 +a 9749 9750 4102 +a 9750 9751 4102 +a 9751 9752 4102 +a 9752 9753 4102 +a 9753 9754 4102 +a 9754 9755 4102 +a 9755 9756 4102 +a 9756 9757 4102 +a 9757 9758 4102 +a 9758 9759 4102 +a 9759 9760 4102 +a 9760 9761 4102 +a 9761 9762 4102 +a 9762 9763 4102 +a 9763 9764 4102 +a 9764 9765 4102 +a 9765 9766 4102 +a 9766 9767 4102 +a 9767 9768 4102 +a 9768 9769 4102 +a 9769 9770 4102 +a 9770 9771 4102 +a 9771 9772 4102 +a 9772 9773 4102 +a 9773 9774 4102 +a 9774 9775 4102 +a 9775 9776 4102 +a 9776 9777 4102 +a 9777 9778 4102 +a 9778 9779 4102 +a 9779 9780 4102 +a 9780 9781 4102 +a 9781 9782 4102 +a 9782 9783 4102 +a 9783 9784 4102 +a 9784 9785 4102 +a 9785 9786 4102 +a 9786 9787 4102 +a 9787 9788 4102 +a 9788 9789 4102 +a 9789 9790 4102 +a 9790 9791 4102 +a 9791 9792 4102 +a 9792 9793 4102 +a 9793 9794 4102 +a 9794 9795 4102 +a 9795 9796 4102 +a 9796 9797 4102 +a 9797 9798 4102 +a 9798 9799 4102 +a 9799 9800 4102 +a 9800 9801 4102 +a 9801 9802 4102 +a 9802 9803 4102 +a 9803 9804 4102 +a 9804 9805 4102 +a 9805 9806 4102 +a 9806 9807 4102 +a 9807 9808 4102 +a 9808 9809 4102 +a 9809 9810 4102 +a 9810 9811 4102 +a 9811 9812 4102 +a 9812 9813 4102 +a 9813 9814 4102 +a 9814 9815 4102 +a 9815 9816 4102 +a 9816 9817 4102 +a 9817 9818 4102 +a 9818 9819 4102 +a 9819 9820 4102 +a 9820 9821 4102 +a 9821 9822 4102 +a 9822 9823 4102 +a 9823 9824 4102 +a 9824 9825 4102 +a 9825 9826 4102 +a 9826 9827 4102 +a 9827 9828 4102 +a 9828 9829 4102 +a 9829 9830 4102 +a 9830 9831 4102 +a 9831 9832 4102 +a 9832 9833 4102 +a 9833 9834 4102 +a 9834 9835 4102 +a 9835 9836 4102 +a 9836 9837 4102 +a 9837 9838 4102 +a 9838 9839 4102 +a 9839 9840 4102 +a 9840 9841 4102 +a 9841 9842 4102 +a 9842 9843 4102 +a 9843 9844 4102 +a 9844 9845 4102 +a 9845 9846 4102 +a 9846 9847 4102 +a 9847 9848 4102 +a 9848 9849 4102 +a 9849 9850 4102 +a 9850 9851 4102 +a 9851 9852 4102 +a 9852 9853 4102 +a 9853 9854 4102 +a 9854 9855 4102 +a 9855 9856 4102 +a 9856 9857 4102 +a 9857 9858 4102 +a 9858 9859 4102 +a 9859 9860 4102 +a 9860 9861 4102 +a 9861 9862 4102 +a 9862 9863 4102 +a 9863 9864 4102 +a 9864 9865 4102 +a 9865 9866 4102 +a 9866 9867 4102 +a 9867 9868 4102 +a 9868 9869 4102 +a 9869 9870 4102 +a 9870 9871 4102 +a 9871 9872 4102 +a 9872 9873 4102 +a 9873 9874 4102 +a 9874 9875 4102 +a 9875 9876 4102 +a 9876 9877 4102 +a 9877 9878 4102 +a 9878 9879 4102 +a 9879 9880 4102 +a 9880 9881 4102 +a 9881 9882 4102 +a 9882 9883 4102 +a 9883 9884 4102 +a 9884 9885 4102 +a 9885 9886 4102 +a 9886 9887 4102 +a 9887 9888 4102 +a 9888 9889 4102 +a 9889 9890 4102 +a 9890 9891 4102 +a 9891 9892 4102 +a 9892 9893 4102 +a 9893 9894 4102 +a 9894 9895 4102 +a 9895 9896 4102 +a 9896 9897 4102 +a 9897 9898 4102 +a 9898 9899 4102 +a 9899 9900 4102 +a 9900 9901 4102 +a 9901 9902 4102 +a 9902 9903 4102 +a 9903 9904 4102 +a 9904 9905 4102 +a 9905 9906 4102 +a 9906 9907 4102 +a 9907 9908 4102 +a 9908 9909 4102 +a 9909 9910 4102 +a 9910 9911 4102 +a 9911 9912 4102 +a 9912 9913 4102 +a 9913 9914 4102 +a 9914 9915 4102 +a 9915 9916 4102 +a 9916 9917 4102 +a 9917 9918 4102 +a 9918 9919 4102 +a 9919 9920 4102 +a 9920 9921 4102 +a 9921 9922 4102 +a 9922 9923 4102 +a 9923 9924 4102 +a 9924 9925 4102 +a 9925 9926 4102 +a 9926 9927 4102 +a 9927 9928 4102 +a 9928 9929 4102 +a 9929 9930 4102 +a 9930 9931 4102 +a 9931 9932 4102 +a 9932 9933 4102 +a 9933 9934 4102 +a 9934 9935 4102 +a 9935 9936 4102 +a 9936 9937 4102 +a 9937 9938 4102 +a 9938 9939 4102 +a 9939 9940 4102 +a 9940 9941 4102 +a 9941 9942 4102 +a 9942 9943 4102 +a 9943 9944 4102 +a 9944 9945 4102 +a 9945 9946 4102 +a 9946 9947 4102 +a 9947 9948 4102 +a 9948 9949 4102 +a 9949 9950 4102 +a 9950 9951 4102 +a 9951 9952 4102 +a 9952 9953 4102 +a 9953 9954 4102 +a 9954 9955 4102 +a 9955 9956 4102 +a 9956 9957 4102 +a 9957 9958 4102 +a 9958 9959 4102 +a 9959 9960 4102 +a 9960 9961 4102 +a 9961 9962 4102 +a 9962 9963 4102 +a 9963 9964 4102 +a 9964 9965 4102 +a 9965 9966 4102 +a 9966 9967 4102 +a 9967 9968 4102 +a 9968 9969 4102 +a 9969 9970 4102 +a 9970 9971 4102 +a 9971 9972 4102 +a 9972 9973 4102 +a 9973 9974 4102 +a 9974 9975 4102 +a 9975 9976 4102 +a 9976 9977 4102 +a 9977 9978 4102 +a 9978 9979 4102 +a 9979 9980 4102 +a 9980 9981 4102 +a 9981 9982 4102 +a 9982 9983 4102 +a 9983 9984 4102 +a 9984 9985 4102 +a 9985 9986 4102 +a 9986 9987 4102 +a 9987 9988 4102 +a 9988 9989 4102 +a 9989 9990 4102 +a 9990 9991 4102 +a 9991 9992 4102 +a 9992 9993 4102 +a 9993 9994 4102 +a 9994 9995 4102 +a 9995 9996 4102 +a 9996 9997 4102 +a 9997 9998 4102 +a 9998 9999 4102 +a 9999 10000 4102 +a 10000 10001 4102 +a 10001 10002 4102 +a 10002 10003 4102 +a 10003 10004 4102 +a 10004 10005 4102 +a 10005 10006 4102 +a 10006 10007 4102 +a 10007 10008 4102 +a 10008 10009 4102 +a 10009 10010 4102 +a 10010 10011 4102 +a 10011 10012 4102 +a 10012 10013 4102 +a 10013 10014 4102 +a 10014 10015 4102 +a 10015 10016 4102 +a 10016 10017 4102 +a 10017 10018 4102 +a 10018 10019 4102 +a 10019 10020 4102 +a 10020 10021 4102 +a 10021 10022 4102 +a 10022 10023 4102 +a 10023 10024 4102 +a 10024 10025 4102 +a 10025 10026 4102 +a 10026 10027 4102 +a 10027 10028 4102 +a 10028 10029 4102 +a 10029 10030 4102 +a 10030 10031 4102 +a 10031 10032 4102 +a 10032 10033 4102 +a 10033 10034 4102 +a 10034 10035 4102 +a 10035 10036 4102 +a 10036 10037 4102 +a 10037 10038 4102 +a 10038 10039 4102 +a 10039 10040 4102 +a 10040 10041 4102 +a 10041 10042 4102 +a 10042 10043 4102 +a 10043 10044 4102 +a 10044 10045 4102 +a 10045 10046 4102 +a 10046 10047 4102 +a 10047 10048 4102 +a 10048 10049 4102 +a 10049 10050 4102 +a 10050 10051 4102 +a 10051 10052 4102 +a 10052 10053 4102 +a 10053 10054 4102 +a 10054 10055 4102 +a 10055 10056 4102 +a 10056 10057 4102 +a 10057 10058 4102 +a 10058 10059 4102 +a 10059 10060 4102 +a 10060 10061 4102 +a 10061 10062 4102 +a 10062 10063 4102 +a 10063 10064 4102 +a 10064 10065 4102 +a 10065 10066 4102 +a 10066 10067 4102 +a 10067 10068 4102 +a 10068 10069 4102 +a 10069 10070 4102 +a 10070 10071 4102 +a 10071 10072 4102 +a 10072 10073 4102 +a 10073 10074 4102 +a 10074 10075 4102 +a 10075 10076 4102 +a 10076 10077 4102 +a 10077 10078 4102 +a 10078 10079 4102 +a 10079 10080 4102 +a 10080 10081 4102 +a 10081 10082 4102 +a 10082 10083 4102 +a 10083 10084 4102 +a 10084 10085 4102 +a 10085 10086 4102 +a 10086 10087 4102 +a 10087 10088 4102 +a 10088 10089 4102 +a 10089 10090 4102 +a 10090 10091 4102 +a 10091 10092 4102 +a 10092 10093 4102 +a 10093 10094 4102 +a 10094 10095 4102 +a 10095 10096 4102 +a 10096 10097 4102 +a 10097 10098 4102 +a 10098 10099 4102 +a 10099 10100 4102 +a 10100 10101 4102 +a 10101 10102 4102 +a 10102 10103 4102 +a 10103 10104 4102 +a 10104 10105 4102 +a 10105 10106 4102 +a 10106 10107 4102 +a 10107 10108 4102 +a 10108 10109 4102 +a 10109 10110 4102 +a 10110 10111 4102 +a 10111 10112 4102 +a 10112 10113 4102 +a 10113 10114 4102 +a 10114 10115 4102 +a 10115 10116 4102 +a 10116 10117 4102 +a 10117 10118 4102 +a 10118 10119 4102 +a 10119 10120 4102 +a 10120 10121 4102 +a 10121 10122 4102 +a 10122 10123 4102 +a 10123 10124 4102 +a 10124 10125 4102 +a 10125 10126 4102 +a 10126 10127 4102 +a 10127 10128 4102 +a 10128 10129 4102 +a 10129 10130 4102 +a 10130 10131 4102 +a 10131 10132 4102 +a 10132 10133 4102 +a 10133 10134 4102 +a 10134 10135 4102 +a 10135 10136 4102 +a 10136 10137 4102 +a 10137 10138 4102 +a 10138 10139 4102 +a 10139 10140 4102 +a 10140 10141 4102 +a 10141 10142 4102 +a 10142 10143 4102 +a 10143 10144 4102 +a 10144 10145 4102 +a 10145 10146 4102 +a 10146 10147 4102 +a 10147 10148 4102 +a 10148 10149 4102 +a 10149 10150 4102 +a 10150 10151 4102 +a 10151 10152 4102 +a 10152 10153 4102 +a 10153 10154 4102 +a 10154 10155 4102 +a 10155 10156 4102 +a 10156 10157 4102 +a 10157 10158 4102 +a 10158 10159 4102 +a 10159 10160 4102 +a 10160 10161 4102 +a 10161 10162 4102 +a 10162 10163 4102 +a 10163 10164 4102 +a 10164 10165 4102 +a 10165 10166 4102 +a 10166 10167 4102 +a 10167 10168 4102 +a 10168 10169 4102 +a 10169 10170 4102 +a 10170 10171 4102 +a 10171 10172 4102 +a 10172 10173 4102 +a 10173 10174 4102 +a 10174 10175 4102 +a 10175 10176 4102 +a 10176 10177 4102 +a 10177 10178 4102 +a 10178 10179 4102 +a 10179 10180 4102 +a 10180 10181 4102 +a 10181 10182 4102 +a 10182 10183 4102 +a 10183 10184 4102 +a 10184 10185 4102 +a 10185 10186 4102 +a 10186 10187 4102 +a 10187 10188 4102 +a 10188 10189 4102 +a 10189 10190 4102 +a 10190 10191 4102 +a 10191 10192 4102 +a 10192 10193 4102 +a 10193 10194 4102 +a 10194 10195 4102 +a 10195 10196 4102 +a 10196 10197 4102 +a 10197 10198 4102 +a 10198 10199 4102 +a 10199 10200 4102 +a 10200 10201 4102 +a 10201 10202 4102 +a 10202 10203 4102 +a 10203 10204 4102 +a 10204 10205 4102 +a 10205 10206 4102 +a 10206 10207 4102 +a 10207 10208 4102 +a 10208 10209 4102 +a 10209 10210 4102 +a 10210 10211 4102 +a 10211 10212 4102 +a 10212 10213 4102 +a 10213 10214 4102 +a 10214 10215 4102 +a 10215 10216 4102 +a 10216 10217 4102 +a 10217 10218 4102 +a 10218 10219 4102 +a 10219 10220 4102 +a 10220 10221 4102 +a 10221 10222 4102 +a 10222 10223 4102 +a 10223 10224 4102 +a 10224 10225 4102 +a 10225 10226 4102 +a 10226 10227 4102 +a 10227 10228 4102 +a 10228 10229 4102 +a 10229 10230 4102 +a 10230 10231 4102 +a 10231 10232 4102 +a 10232 10233 4102 +a 10233 10234 4102 +a 10234 10235 4102 +a 10235 10236 4102 +a 10236 10237 4102 +a 10237 10238 4102 +a 10238 10239 4102 +a 10239 10240 4102 +a 10240 10241 4102 +a 10241 10242 4102 +a 10242 10243 4102 +a 10243 10244 4102 +a 10244 10245 4102 +a 10245 10246 4102 +a 10246 10247 4102 +a 10247 10248 4102 +a 10248 10249 4102 +a 10249 10250 4102 +a 10250 10251 4102 +a 10251 10252 4102 +a 10252 10253 4102 +a 10253 10254 4102 +a 10254 10255 4102 +a 10255 10256 4102 +a 10256 10257 4102 +a 10257 10258 4102 +a 10258 10259 4102 +a 10259 10260 4102 +a 10260 10261 4102 +a 10261 10262 4102 +a 10262 10263 4102 +a 10263 10264 4102 +a 10264 10265 4102 +a 10265 10266 4102 +a 10266 10267 4102 +a 10267 10268 4102 +a 10268 10269 4102 +a 10269 10270 4102 +a 10270 10271 4102 +a 10271 10272 4102 +a 10272 10273 4102 +a 10273 10274 4102 +a 10274 10275 4102 +a 10275 10276 4102 +a 10276 10277 4102 +a 10277 10278 4102 +a 10278 10279 4102 +a 10279 10280 4102 +a 10280 10281 4102 +a 10281 10282 4102 +a 10282 10283 4102 +a 10283 10284 4102 +a 10284 10285 4102 +a 10285 10286 4102 +a 10286 10287 4102 +a 10287 10288 4102 +a 10288 10289 4102 +a 10289 10290 4102 +a 10290 10291 4102 +a 10291 10292 4102 +a 10292 10293 4102 +a 10293 10294 4102 +a 10294 10295 4102 +a 10295 10296 4102 +a 10296 10297 4102 +a 10297 10298 4102 +a 10298 10299 4102 +a 10299 10300 4102 +a 10300 10301 4102 +a 10301 10302 4102 +a 10302 10303 4102 +a 10303 10304 4102 +a 10304 10305 4102 +a 10305 10306 4102 +a 10306 10307 4102 +a 10307 10308 4102 +a 10308 10309 4102 +a 10309 10310 4102 +a 10310 10311 4102 +a 10311 10312 4102 +a 10312 10313 4102 +a 10313 10314 4102 +a 10314 10315 4102 +a 10315 10316 4102 +a 10316 10317 4102 +a 10317 10318 4102 +a 10318 10319 4102 +a 10319 10320 4102 +a 10320 10321 4102 +a 10321 10322 4102 +a 10322 10323 4102 +a 10323 10324 4102 +a 10324 10325 4102 +a 10325 10326 4102 +a 10326 10327 4102 +a 10327 10328 4102 +a 10328 10329 4102 +a 10329 10330 4102 +a 10330 10331 4102 +a 10331 10332 4102 +a 10332 10333 4102 +a 10333 10334 4102 +a 10334 10335 4102 +a 10335 10336 4102 +a 10336 10337 4102 +a 10337 10338 4102 +a 10338 10339 4102 +a 10339 10340 4102 +a 10340 10341 4102 +a 10341 10342 4102 +a 10342 10343 4102 +a 10343 10344 4102 +a 10344 10345 4102 +a 10345 10346 4102 +a 10346 10347 4102 +a 10347 10348 4102 +a 10348 10349 4102 +a 10349 10350 4102 +a 10350 10351 4102 +a 10351 10352 4102 +a 10352 10353 4102 +a 10353 10354 4102 +a 10354 10355 4102 +a 10355 10356 4102 +a 10356 10357 4102 +a 10357 10358 4102 +a 10358 10359 4102 +a 10359 10360 4102 +a 10360 10361 4102 +a 10361 10362 4102 +a 10362 10363 4102 +a 10363 10364 4102 +a 10364 10365 4102 +a 10365 10366 4102 +a 10366 10367 4102 +a 10367 10368 4102 +a 10368 10369 4102 +a 10369 10370 4102 +a 10370 10371 4102 +a 10371 10372 4102 +a 10372 10373 4102 +a 10373 10374 4102 +a 10374 10375 4102 +a 10375 10376 4102 +a 10376 10377 4102 +a 10377 10378 4102 +a 10378 10379 4102 +a 10379 10380 4102 +a 10380 10381 4102 +a 10381 10382 4102 +a 10382 10383 4102 +a 10383 10384 4102 +a 10384 10385 4102 +a 10385 10386 4102 +a 10386 10387 4102 +a 10387 10388 4102 +a 10388 10389 4102 +a 10389 10390 4102 +a 10390 10391 4102 +a 10391 10392 4102 +a 10392 10393 4102 +a 10393 10394 4102 +a 10394 10395 4102 +a 10395 10396 4102 +a 10396 10397 4102 +a 10397 10398 4102 +a 10398 10399 4102 +a 10399 10400 4102 +a 10400 10401 4102 +a 10401 10402 4102 +a 10402 10403 4102 +a 10403 10404 4102 +a 10404 10405 4102 +a 10405 10406 4102 +a 10406 10407 4102 +a 10407 10408 4102 +a 10408 10409 4102 +a 10409 10410 4102 +a 10410 10411 4102 +a 10411 10412 4102 +a 10412 10413 4102 +a 10413 10414 4102 +a 10414 10415 4102 +a 10415 10416 4102 +a 10416 10417 4102 +a 10417 10418 4102 +a 10418 10419 4102 +a 10419 10420 4102 +a 10420 10421 4102 +a 10421 10422 4102 +a 10422 10423 4102 +a 10423 10424 4102 +a 10424 10425 4102 +a 10425 10426 4102 +a 10426 10427 4102 +a 10427 10428 4102 +a 10428 10429 4102 +a 10429 10430 4102 +a 10430 10431 4102 +a 10431 10432 4102 +a 10432 10433 4102 +a 10433 10434 4102 +a 10434 10435 4102 +a 10435 10436 4102 +a 10436 10437 4102 +a 10437 10438 4102 +a 10438 10439 4102 +a 10439 10440 4102 +a 10440 10441 4102 +a 10441 10442 4102 +a 10442 10443 4102 +a 10443 10444 4102 +a 10444 10445 4102 +a 10445 10446 4102 +a 10446 10447 4102 +a 10447 10448 4102 +a 10448 10449 4102 +a 10449 10450 4102 +a 10450 10451 4102 +a 10451 10452 4102 +a 10452 10453 4102 +a 10453 10454 4102 +a 10454 10455 4102 +a 10455 10456 4102 +a 10456 10457 4102 +a 10457 10458 4102 +a 10458 10459 4102 +a 10459 10460 4102 +a 10460 10461 4102 +a 10461 10462 4102 +a 10462 10463 4102 +a 10463 10464 4102 +a 10464 10465 4102 +a 10465 10466 4102 +a 10466 10467 4102 +a 10467 10468 4102 +a 10468 10469 4102 +a 10469 10470 4102 +a 10470 10471 4102 +a 10471 10472 4102 +a 10472 10473 4102 +a 10473 10474 4102 +a 10474 10475 4102 +a 10475 10476 4102 +a 10476 10477 4102 +a 10477 10478 4102 +a 10478 10479 4102 +a 10479 10480 4102 +a 10480 10481 4102 +a 10481 10482 4102 +a 10482 10483 4102 +a 10483 10484 4102 +a 10484 10485 4102 +a 10485 10486 4102 +a 10486 10487 4102 +a 10487 10488 4102 +a 10488 10489 4102 +a 10489 10490 4102 +a 10490 10491 4102 +a 10491 10492 4102 +a 10492 10493 4102 +a 10493 10494 4102 +a 10494 10495 4102 +a 10495 10496 4102 +a 10496 10497 4102 +a 10497 10498 4102 +a 10498 10499 4102 +a 10499 10500 4102 +a 10500 10501 4102 +a 10501 10502 4102 +a 10502 10503 4102 +a 10503 10504 4102 +a 10504 10505 4102 +a 10505 10506 4102 +a 10506 10507 4102 +a 10507 10508 4102 +a 10508 10509 4102 +a 10509 10510 4102 +a 10510 10511 4102 +a 10511 10512 4102 +a 10512 10513 4102 +a 10513 10514 4102 +a 10514 10515 4102 +a 10515 10516 4102 +a 10516 10517 4102 +a 10517 10518 4102 +a 10518 10519 4102 +a 10519 10520 4102 +a 10520 10521 4102 +a 10521 10522 4102 +a 10522 10523 4102 +a 10523 10524 4102 +a 10524 10525 4102 +a 10525 10526 4102 +a 10526 10527 4102 +a 10527 10528 4102 +a 10528 10529 4102 +a 10529 10530 4102 +a 10530 10531 4102 +a 10531 10532 4102 +a 10532 10533 4102 +a 10533 10534 4102 +a 10534 10535 4102 +a 10535 10536 4102 +a 10536 10537 4102 +a 10537 10538 4102 +a 10538 10539 4102 +a 10539 10540 4102 +a 10540 10541 4102 +a 10541 10542 4102 +a 10542 10543 4102 +a 10543 10544 4102 +a 10544 10545 4102 +a 10545 10546 4102 +a 10546 10547 4102 +a 10547 10548 4102 +a 10548 10549 4102 +a 10549 10550 4102 +a 10550 10551 4102 +a 10551 10552 4102 +a 10552 10553 4102 +a 10553 10554 4102 +a 10554 10555 4102 +a 10555 10556 4102 +a 10556 10557 4102 +a 10557 10558 4102 +a 10558 10559 4102 +a 10559 10560 4102 +a 10560 10561 4102 +a 10561 10562 4102 +a 10562 10563 4102 +a 10563 10564 4102 +a 10564 10565 4102 +a 10565 10566 4102 +a 10566 10567 4102 +a 10567 10568 4102 +a 10568 10569 4102 +a 10569 10570 4102 +a 10570 10571 4102 +a 10571 10572 4102 +a 10572 10573 4102 +a 10573 10574 4102 +a 10574 10575 4102 +a 10575 10576 4102 +a 10576 10577 4102 +a 10577 10578 4102 +a 10578 10579 4102 +a 10579 10580 4102 +a 10580 10581 4102 +a 10581 10582 4102 +a 10582 10583 4102 +a 10583 10584 4102 +a 10584 10585 4102 +a 10585 10586 4102 +a 10586 10587 4102 +a 10587 10588 4102 +a 10588 10589 4102 +a 10589 10590 4102 +a 10590 10591 4102 +a 10591 10592 4102 +a 10592 10593 4102 +a 10593 10594 4102 +a 10594 10595 4102 +a 10595 10596 4102 +a 10596 10597 4102 +a 10597 10598 4102 +a 10598 10599 4102 +a 10599 10600 4102 +a 10600 10601 4102 +a 10601 10602 4102 +a 10602 10603 4102 +a 10603 10604 4102 +a 10604 10605 4102 +a 10605 10606 4102 +a 10606 10607 4102 +a 10607 10608 4102 +a 10608 10609 4102 +a 10609 10610 4102 +a 10610 10611 4102 +a 10611 10612 4102 +a 10612 10613 4102 +a 10613 10614 4102 +a 10614 10615 4102 +a 10615 10616 4102 +a 10616 10617 4102 +a 10617 10618 4102 +a 10618 10619 4102 +a 10619 10620 4102 +a 10620 10621 4102 +a 10621 10622 4102 +a 10622 10623 4102 +a 10623 10624 4102 +a 10624 10625 4102 +a 10625 10626 4102 +a 10626 10627 4102 +a 10627 10628 4102 +a 10628 10629 4102 +a 10629 10630 4102 +a 10630 10631 4102 +a 10631 10632 4102 +a 10632 10633 4102 +a 10633 10634 4102 +a 10634 10635 4102 +a 10635 10636 4102 +a 10636 10637 4102 +a 10637 10638 4102 +a 10638 10639 4102 +a 10639 10640 4102 +a 10640 10641 4102 +a 10641 10642 4102 +a 10642 10643 4102 +a 10643 10644 4102 +a 10644 10645 4102 +a 10645 10646 4102 +a 10646 10647 4102 +a 10647 10648 4102 +a 10648 10649 4102 +a 10649 10650 4102 +a 10650 10651 4102 +a 10651 10652 4102 +a 10652 10653 4102 +a 10653 10654 4102 +a 10654 10655 4102 +a 10655 10656 4102 +a 10656 10657 4102 +a 10657 10658 4102 +a 10658 10659 4102 +a 10659 10660 4102 +a 10660 10661 4102 +a 10661 10662 4102 +a 10662 10663 4102 +a 10663 10664 4102 +a 10664 10665 4102 +a 10665 10666 4102 +a 10666 10667 4102 +a 10667 10668 4102 +a 10668 10669 4102 +a 10669 10670 4102 +a 10670 10671 4102 +a 10671 10672 4102 +a 10672 10673 4102 +a 10673 10674 4102 +a 10674 10675 4102 +a 10675 10676 4102 +a 10676 10677 4102 +a 10677 10678 4102 +a 10678 10679 4102 +a 10679 10680 4102 +a 10680 10681 4102 +a 10681 10682 4102 +a 10682 10683 4102 +a 10683 10684 4102 +a 10684 10685 4102 +a 10685 10686 4102 +a 10686 10687 4102 +a 10687 10688 4102 +a 10688 10689 4102 +a 10689 10690 4102 +a 10690 10691 4102 +a 10691 10692 4102 +a 10692 10693 4102 +a 10693 10694 4102 +a 10694 10695 4102 +a 10695 10696 4102 +a 10696 10697 4102 +a 10697 10698 4102 +a 10698 10699 4102 +a 10699 10700 4102 +a 10700 10701 4102 +a 10701 10702 4102 +a 10702 10703 4102 +a 10703 10704 4102 +a 10704 10705 4102 +a 10705 10706 4102 +a 10706 10707 4102 +a 10707 10708 4102 +a 10708 10709 4102 +a 10709 10710 4102 +a 10710 10711 4102 +a 10711 10712 4102 +a 10712 10713 4102 +a 10713 10714 4102 +a 10714 10715 4102 +a 10715 10716 4102 +a 10716 10717 4102 +a 10717 10718 4102 +a 10718 10719 4102 +a 10719 10720 4102 +a 10720 10721 4102 +a 10721 10722 4102 +a 10722 10723 4102 +a 10723 10724 4102 +a 10724 10725 4102 +a 10725 10726 4102 +a 10726 10727 4102 +a 10727 10728 4102 +a 10728 10729 4102 +a 10729 10730 4102 +a 10730 10731 4102 +a 10731 10732 4102 +a 10732 10733 4102 +a 10733 10734 4102 +a 10734 10735 4102 +a 10735 10736 4102 +a 10736 10737 4102 +a 10737 10738 4102 +a 10738 10739 4102 +a 10739 10740 4102 +a 10740 10741 4102 +a 10741 10742 4102 +a 10742 10743 4102 +a 10743 10744 4102 +a 10744 10745 4102 +a 10745 10746 4102 +a 10746 10747 4102 +a 10747 10748 4102 +a 10748 10749 4102 +a 10749 10750 4102 +a 10750 10751 4102 +a 10751 10752 4102 +a 10752 10753 4102 +a 10753 10754 4102 +a 10754 10755 4102 +a 10755 10756 4102 +a 10756 10757 4102 +a 10757 10758 4102 +a 10758 10759 4102 +a 10759 10760 4102 +a 10760 10761 4102 +a 10761 10762 4102 +a 10762 10763 4102 +a 10763 10764 4102 +a 10764 10765 4102 +a 10765 10766 4102 +a 10766 10767 4102 +a 10767 10768 4102 +a 10768 10769 4102 +a 10769 10770 4102 +a 10770 10771 4102 +a 10771 10772 4102 +a 10772 10773 4102 +a 10773 10774 4102 +a 10774 10775 4102 +a 10775 10776 4102 +a 10776 10777 4102 +a 10777 10778 4102 +a 10778 10779 4102 +a 10779 10780 4102 +a 10780 10781 4102 +a 10781 10782 4102 +a 10782 10783 4102 +a 10783 10784 4102 +a 10784 10785 4102 +a 10785 10786 4102 +a 10786 10787 4102 +a 10787 10788 4102 +a 10788 10789 4102 +a 10789 10790 4102 +a 10790 10791 4102 +a 10791 10792 4102 +a 10792 10793 4102 +a 10793 10794 4102 +a 10794 10795 4102 +a 10795 10796 4102 +a 10796 10797 4102 +a 10797 10798 4102 +a 10798 10799 4102 +a 10799 10800 4102 +a 10800 10801 4102 +a 10801 10802 4102 +a 10802 10803 4102 +a 10803 10804 4102 +a 10804 10805 4102 +a 10805 10806 4102 +a 10806 10807 4102 +a 10807 10808 4102 +a 10808 10809 4102 +a 10809 10810 4102 +a 10810 10811 4102 +a 10811 10812 4102 +a 10812 10813 4102 +a 10813 10814 4102 +a 10814 10815 4102 +a 10815 10816 4102 +a 10816 10817 4102 +a 10817 10818 4102 +a 10818 10819 4102 +a 10819 10820 4102 +a 10820 10821 4102 +a 10821 10822 4102 +a 10822 10823 4102 +a 10823 10824 4102 +a 10824 10825 4102 +a 10825 10826 4102 +a 10826 10827 4102 +a 10827 10828 4102 +a 10828 10829 4102 +a 10829 10830 4102 +a 10830 10831 4102 +a 10831 10832 4102 +a 10832 10833 4102 +a 10833 10834 4102 +a 10834 10835 4102 +a 10835 10836 4102 +a 10836 10837 4102 +a 10837 10838 4102 +a 10838 10839 4102 +a 10839 10840 4102 +a 10840 10841 4102 +a 10841 10842 4102 +a 10842 10843 4102 +a 10843 10844 4102 +a 10844 10845 4102 +a 10845 10846 4102 +a 10846 10847 4102 +a 10847 10848 4102 +a 10848 10849 4102 +a 10849 10850 4102 +a 10850 10851 4102 +a 10851 10852 4102 +a 10852 10853 4102 +a 10853 10854 4102 +a 10854 10855 4102 +a 10855 10856 4102 +a 10856 10857 4102 +a 10857 10858 4102 +a 10858 10859 4102 +a 10859 10860 4102 +a 10860 10861 4102 +a 10861 10862 4102 +a 10862 10863 4102 +a 10863 10864 4102 +a 10864 10865 4102 +a 10865 10866 4102 +a 10866 10867 4102 +a 10867 10868 4102 +a 10868 10869 4102 +a 10869 10870 4102 +a 10870 10871 4102 +a 10871 10872 4102 +a 10872 10873 4102 +a 10873 10874 4102 +a 10874 10875 4102 +a 10875 10876 4102 +a 10876 10877 4102 +a 10877 10878 4102 +a 10878 10879 4102 +a 10879 10880 4102 +a 10880 10881 4102 +a 10881 10882 4102 +a 10882 10883 4102 +a 10883 10884 4102 +a 10884 10885 4102 +a 10885 10886 4102 +a 10886 10887 4102 +a 10887 10888 4102 +a 10888 10889 4102 +a 10889 10890 4102 +a 10890 10891 4102 +a 10891 10892 4102 +a 10892 10893 4102 +a 10893 10894 4102 +a 10894 10895 4102 +a 10895 10896 4102 +a 10896 10897 4102 +a 10897 10898 4102 +a 10898 10899 4102 +a 10899 10900 4102 +a 10900 10901 4102 +a 10901 10902 4102 +a 10902 10903 4102 +a 10903 10904 4102 +a 10904 10905 4102 +a 10905 10906 4102 +a 10906 10907 4102 +a 10907 10908 4102 +a 10908 10909 4102 +a 10909 10910 4102 +a 10910 10911 4102 +a 10911 10912 4102 +a 10912 10913 4102 +a 10913 10914 4102 +a 10914 10915 4102 +a 10915 10916 4102 +a 10916 10917 4102 +a 10917 10918 4102 +a 10918 10919 4102 +a 10919 10920 4102 +a 10920 10921 4102 +a 10921 10922 4102 +a 10922 10923 4102 +a 10923 10924 4102 +a 10924 10925 4102 +a 10925 10926 4102 +a 10926 10927 4102 +a 10927 10928 4102 +a 10928 10929 4102 +a 10929 10930 4102 +a 10930 10931 4102 +a 10931 10932 4102 +a 10932 10933 4102 +a 10933 10934 4102 +a 10934 10935 4102 +a 10935 10936 4102 +a 10936 10937 4102 +a 10937 10938 4102 +a 10938 10939 4102 +a 10939 10940 4102 +a 10940 10941 4102 +a 10941 10942 4102 +a 10942 10943 4102 +a 10943 10944 4102 +a 10944 10945 4102 +a 10945 10946 4102 +a 10946 10947 4102 +a 10947 10948 4102 +a 10948 10949 4102 +a 10949 10950 4102 +a 10950 10951 4102 +a 10951 10952 4102 +a 10952 10953 4102 +a 10953 10954 4102 +a 10954 10955 4102 +a 10955 10956 4102 +a 10956 10957 4102 +a 10957 10958 4102 +a 10958 10959 4102 +a 10959 10960 4102 +a 10960 10961 4102 +a 10961 10962 4102 +a 10962 10963 4102 +a 10963 10964 4102 +a 10964 10965 4102 +a 10965 10966 4102 +a 10966 10967 4102 +a 10967 10968 4102 +a 10968 10969 4102 +a 10969 10970 4102 +a 10970 10971 4102 +a 10971 10972 4102 +a 10972 10973 4102 +a 10973 10974 4102 +a 10974 10975 4102 +a 10975 10976 4102 +a 10976 10977 4102 +a 10977 10978 4102 +a 10978 10979 4102 +a 10979 10980 4102 +a 10980 10981 4102 +a 10981 10982 4102 +a 10982 10983 4102 +a 10983 10984 4102 +a 10984 10985 4102 +a 10985 10986 4102 +a 10986 10987 4102 +a 10987 10988 4102 +a 10988 10989 4102 +a 10989 10990 4102 +a 10990 10991 4102 +a 10991 10992 4102 +a 10992 10993 4102 +a 10993 10994 4102 +a 10994 10995 4102 +a 10995 10996 4102 +a 10996 10997 4102 +a 10997 10998 4102 +a 10998 10999 4102 +a 10999 11000 4102 +a 11000 11001 4102 +a 11001 11002 4102 +a 11002 11003 4102 +a 11003 11004 4102 +a 11004 11005 4102 +a 11005 11006 4102 +a 11006 11007 4102 +a 11007 11008 4102 +a 11008 11009 4102 +a 11009 11010 4102 +a 11010 11011 4102 +a 11011 11012 4102 +a 11012 11013 4102 +a 11013 11014 4102 +a 11014 11015 4102 +a 11015 11016 4102 +a 11016 11017 4102 +a 11017 11018 4102 +a 11018 11019 4102 +a 11019 11020 4102 +a 11020 11021 4102 +a 11021 11022 4102 +a 11022 11023 4102 +a 11023 11024 4102 +a 11024 11025 4102 +a 11025 11026 4102 +a 11026 11027 4102 +a 11027 11028 4102 +a 11028 11029 4102 +a 11029 11030 4102 +a 11030 11031 4102 +a 11031 11032 4102 +a 11032 11033 4102 +a 11033 11034 4102 +a 11034 11035 4102 +a 11035 11036 4102 +a 11036 11037 4102 +a 11037 11038 4102 +a 11038 11039 4102 +a 11039 11040 4102 +a 11040 11041 4102 +a 11041 11042 4102 +a 11042 11043 4102 +a 11043 11044 4102 +a 11044 11045 4102 +a 11045 11046 4102 +a 11046 11047 4102 +a 11047 11048 4102 +a 11048 11049 4102 +a 11049 11050 4102 +a 11050 11051 4102 +a 11051 11052 4102 +a 11052 11053 4102 +a 11053 11054 4102 +a 11054 11055 4102 +a 11055 11056 4102 +a 11056 11057 4102 +a 11057 11058 4102 +a 11058 11059 4102 +a 11059 11060 4102 +a 11060 11061 4102 +a 11061 11062 4102 +a 11062 11063 4102 +a 11063 11064 4102 +a 11064 11065 4102 +a 11065 11066 4102 +a 11066 11067 4102 +a 11067 11068 4102 +a 11068 11069 4102 +a 11069 11070 4102 +a 11070 11071 4102 +a 11071 11072 4102 +a 11072 11073 4102 +a 11073 11074 4102 +a 11074 11075 4102 +a 11075 11076 4102 +a 11076 11077 4102 +a 11077 11078 4102 +a 11078 11079 4102 +a 11079 11080 4102 +a 11080 11081 4102 +a 11081 11082 4102 +a 11082 11083 4102 +a 11083 11084 4102 +a 11084 11085 4102 +a 11085 11086 4102 +a 11086 11087 4102 +a 11087 11088 4102 +a 11088 11089 4102 +a 11089 11090 4102 +a 11090 11091 4102 +a 11091 11092 4102 +a 11092 11093 4102 +a 11093 11094 4102 +a 11094 11095 4102 +a 11095 11096 4102 +a 11096 11097 4102 +a 11097 11098 4102 +a 11098 11099 4102 +a 11099 11100 4102 +a 11100 11101 4102 +a 11101 11102 4102 +a 11102 11103 4102 +a 11103 11104 4102 +a 11104 11105 4102 +a 11105 11106 4102 +a 11106 11107 4102 +a 11107 11108 4102 +a 11108 11109 4102 +a 11109 11110 4102 +a 11110 11111 4102 +a 11111 11112 4102 +a 11112 11113 4102 +a 11113 11114 4102 +a 11114 11115 4102 +a 11115 11116 4102 +a 11116 11117 4102 +a 11117 11118 4102 +a 11118 11119 4102 +a 11119 11120 4102 +a 11120 11121 4102 +a 11121 11122 4102 +a 11122 11123 4102 +a 11123 11124 4102 +a 11124 11125 4102 +a 11125 11126 4102 +a 11126 11127 4102 +a 11127 11128 4102 +a 11128 11129 4102 +a 11129 11130 4102 +a 11130 11131 4102 +a 11131 11132 4102 +a 11132 11133 4102 +a 11133 11134 4102 +a 11134 11135 4102 +a 11135 11136 4102 +a 11136 11137 4102 +a 11137 11138 4102 +a 11138 11139 4102 +a 11139 11140 4102 +a 11140 11141 4102 +a 11141 11142 4102 +a 11142 11143 4102 +a 11143 11144 4102 +a 11144 11145 4102 +a 11145 11146 4102 +a 11146 11147 4102 +a 11147 11148 4102 +a 11148 11149 4102 +a 11149 11150 4102 +a 11150 11151 4102 +a 11151 11152 4102 +a 11152 11153 4102 +a 11153 11154 4102 +a 11154 11155 4102 +a 11155 11156 4102 +a 11156 11157 4102 +a 11157 11158 4102 +a 11158 11159 4102 +a 11159 11160 4102 +a 11160 11161 4102 +a 11161 11162 4102 +a 11162 11163 4102 +a 11163 11164 4102 +a 11164 11165 4102 +a 11165 11166 4102 +a 11166 11167 4102 +a 11167 11168 4102 +a 11168 11169 4102 +a 11169 11170 4102 +a 11170 11171 4102 +a 11171 11172 4102 +a 11172 11173 4102 +a 11173 11174 4102 +a 11174 11175 4102 +a 11175 11176 4102 +a 11176 11177 4102 +a 11177 11178 4102 +a 11178 11179 4102 +a 11179 11180 4102 +a 11180 11181 4102 +a 11181 11182 4102 +a 11182 11183 4102 +a 11183 11184 4102 +a 11184 11185 4102 +a 11185 11186 4102 +a 11186 11187 4102 +a 11187 11188 4102 +a 11188 11189 4102 +a 11189 11190 4102 +a 11190 11191 4102 +a 11191 11192 4102 +a 11192 11193 4102 +a 11193 11194 4102 +a 11194 11195 4102 +a 11195 11196 4102 +a 11196 11197 4102 +a 11197 11198 4102 +a 11198 11199 4102 +a 11199 11200 4102 +a 11200 11201 4102 +a 11201 11202 4102 +a 11202 11203 4102 +a 11203 11204 4102 +a 11204 11205 4102 +a 11205 11206 4102 +a 11206 11207 4102 +a 11207 11208 4102 +a 11208 11209 4102 +a 11209 11210 4102 +a 11210 11211 4102 +a 11211 11212 4102 +a 11212 11213 4102 +a 11213 11214 4102 +a 11214 11215 4102 +a 11215 11216 4102 +a 11216 11217 4102 +a 11217 11218 4102 +a 11218 11219 4102 +a 11219 11220 4102 +a 11220 11221 4102 +a 11221 11222 4102 +a 11222 11223 4102 +a 11223 11224 4102 +a 11224 11225 4102 +a 11225 11226 4102 +a 11226 11227 4102 +a 11227 11228 4102 +a 11228 11229 4102 +a 11229 11230 4102 +a 11230 11231 4102 +a 11231 11232 4102 +a 11232 11233 4102 +a 11233 11234 4102 +a 11234 11235 4102 +a 11235 11236 4102 +a 11236 11237 4102 +a 11237 11238 4102 +a 11238 11239 4102 +a 11239 11240 4102 +a 11240 11241 4102 +a 11241 11242 4102 +a 11242 11243 4102 +a 11243 11244 4102 +a 11244 11245 4102 +a 11245 11246 4102 +a 11246 11247 4102 +a 11247 11248 4102 +a 11248 11249 4102 +a 11249 11250 4102 +a 11250 11251 4102 +a 11251 11252 4102 +a 11252 11253 4102 +a 11253 11254 4102 +a 11254 11255 4102 +a 11255 11256 4102 +a 11256 11257 4102 +a 11257 11258 4102 +a 11258 11259 4102 +a 11259 11260 4102 +a 11260 11261 4102 +a 11261 11262 4102 +a 11262 11263 4102 +a 11263 11264 4102 +a 11264 11265 4102 +a 11265 11266 4102 +a 11266 11267 4102 +a 11267 11268 4102 +a 11268 11269 4102 +a 11269 11270 4102 +a 11270 11271 4102 +a 11271 11272 4102 +a 11272 11273 4102 +a 11273 11274 4102 +a 11274 11275 4102 +a 11275 11276 4102 +a 11276 11277 4102 +a 11277 11278 4102 +a 11278 11279 4102 +a 11279 11280 4102 +a 11280 11281 4102 +a 11281 11282 4102 +a 11282 11283 4102 +a 11283 11284 4102 +a 11284 11285 4102 +a 11285 11286 4102 +a 11286 11287 4102 +a 11287 11288 4102 +a 11288 11289 4102 +a 11289 11290 4102 +a 11290 11291 4102 +a 11291 11292 4102 +a 11292 11293 4102 +a 11293 11294 4102 +a 11294 11295 4102 +a 11295 11296 4102 +a 11296 11297 4102 +a 11297 11298 4102 +a 11298 11299 4102 +a 11299 11300 4102 +a 11300 11301 4102 +a 11301 11302 4102 +a 11302 11303 4102 +a 11303 11304 4102 +a 11304 11305 4102 +a 11305 11306 4102 +a 11306 11307 4102 +a 11307 11308 4102 +a 11308 11309 4102 +a 11309 11310 4102 +a 11310 11311 4102 +a 11311 11312 4102 +a 11312 11313 4102 +a 11313 11314 4102 +a 11314 11315 4102 +a 11315 11316 4102 +a 11316 11317 4102 +a 11317 11318 4102 +a 11318 11319 4102 +a 11319 11320 4102 +a 11320 11321 4102 +a 11321 11322 4102 +a 11322 11323 4102 +a 11323 11324 4102 +a 11324 11325 4102 +a 11325 11326 4102 +a 11326 11327 4102 +a 11327 11328 4102 +a 11328 11329 4102 +a 11329 11330 4102 +a 11330 11331 4102 +a 11331 11332 4102 +a 11332 11333 4102 +a 11333 11334 4102 +a 11334 11335 4102 +a 11335 11336 4102 +a 11336 11337 4102 +a 11337 11338 4102 +a 11338 11339 4102 +a 11339 11340 4102 +a 11340 11341 4102 +a 11341 11342 4102 +a 11342 11343 4102 +a 11343 11344 4102 +a 11344 11345 4102 +a 11345 11346 4102 +a 11346 11347 4102 +a 11347 11348 4102 +a 11348 11349 4102 +a 11349 11350 4102 +a 11350 11351 4102 +a 11351 11352 4102 +a 11352 11353 4102 +a 11353 11354 4102 +a 11354 11355 4102 +a 11355 11356 4102 +a 11356 11357 4102 +a 11357 11358 4102 +a 11358 11359 4102 +a 11359 11360 4102 +a 11360 11361 4102 +a 11361 11362 4102 +a 11362 11363 4102 +a 11363 11364 4102 +a 11364 11365 4102 +a 11365 11366 4102 +a 11366 11367 4102 +a 11367 11368 4102 +a 11368 11369 4102 +a 11369 11370 4102 +a 11370 11371 4102 +a 11371 11372 4102 +a 11372 11373 4102 +a 11373 11374 4102 +a 11374 11375 4102 +a 11375 11376 4102 +a 11376 11377 4102 +a 11377 11378 4102 +a 11378 11379 4102 +a 11379 11380 4102 +a 11380 11381 4102 +a 11381 11382 4102 +a 11382 11383 4102 +a 11383 11384 4102 +a 11384 11385 4102 +a 11385 11386 4102 +a 11386 11387 4102 +a 11387 11388 4102 +a 11388 11389 4102 +a 11389 11390 4102 +a 11390 11391 4102 +a 11391 11392 4102 +a 11392 11393 4102 +a 11393 11394 4102 +a 11394 11395 4102 +a 11395 11396 4102 +a 11396 11397 4102 +a 11397 11398 4102 +a 11398 11399 4102 +a 11399 11400 4102 +a 11400 11401 4102 +a 11401 11402 4102 +a 11402 11403 4102 +a 11403 11404 4102 +a 11404 11405 4102 +a 11405 11406 4102 +a 11406 11407 4102 +a 11407 11408 4102 +a 11408 11409 4102 +a 11409 11410 4102 +a 11410 11411 4102 +a 11411 11412 4102 +a 11412 11413 4102 +a 11413 11414 4102 +a 11414 11415 4102 +a 11415 11416 4102 +a 11416 11417 4102 +a 11417 11418 4102 +a 11418 11419 4102 +a 11419 11420 4102 +a 11420 11421 4102 +a 11421 11422 4102 +a 11422 11423 4102 +a 11423 11424 4102 +a 11424 11425 4102 +a 11425 11426 4102 +a 11426 11427 4102 +a 11427 11428 4102 +a 11428 11429 4102 +a 11429 11430 4102 +a 11430 11431 4102 +a 11431 11432 4102 +a 11432 11433 4102 +a 11433 11434 4102 +a 11434 11435 4102 +a 11435 11436 4102 +a 11436 11437 4102 +a 11437 11438 4102 +a 11438 11439 4102 +a 11439 11440 4102 +a 11440 11441 4102 +a 11441 11442 4102 +a 11442 11443 4102 +a 11443 11444 4102 +a 11444 11445 4102 +a 11445 11446 4102 +a 11446 11447 4102 +a 11447 11448 4102 +a 11448 11449 4102 +a 11449 11450 4102 +a 11450 11451 4102 +a 11451 11452 4102 +a 11452 11453 4102 +a 11453 11454 4102 +a 11454 11455 4102 +a 11455 11456 4102 +a 11456 11457 4102 +a 11457 11458 4102 +a 11458 11459 4102 +a 11459 11460 4102 +a 11460 11461 4102 +a 11461 11462 4102 +a 11462 11463 4102 +a 11463 11464 4102 +a 11464 11465 4102 +a 11465 11466 4102 +a 11466 11467 4102 +a 11467 11468 4102 +a 11468 11469 4102 +a 11469 11470 4102 +a 11470 11471 4102 +a 11471 11472 4102 +a 11472 11473 4102 +a 11473 11474 4102 +a 11474 11475 4102 +a 11475 11476 4102 +a 11476 11477 4102 +a 11477 11478 4102 +a 11478 11479 4102 +a 11479 11480 4102 +a 11480 11481 4102 +a 11481 11482 4102 +a 11482 11483 4102 +a 11483 11484 4102 +a 11484 11485 4102 +a 11485 11486 4102 +a 11486 11487 4102 +a 11487 11488 4102 +a 11488 11489 4102 +a 11489 11490 4102 +a 11490 11491 4102 +a 11491 11492 4102 +a 11492 11493 4102 +a 11493 11494 4102 +a 11494 11495 4102 +a 11495 11496 4102 +a 11496 11497 4102 +a 11497 11498 4102 +a 11498 11499 4102 +a 11499 11500 4102 +a 11500 11501 4102 +a 11501 11502 4102 +a 11502 11503 4102 +a 11503 11504 4102 +a 11504 11505 4102 +a 11505 11506 4102 +a 11506 11507 4102 +a 11507 11508 4102 +a 11508 11509 4102 +a 11509 11510 4102 +a 11510 11511 4102 +a 11511 11512 4102 +a 11512 11513 4102 +a 11513 11514 4102 +a 11514 11515 4102 +a 11515 11516 4102 +a 11516 11517 4102 +a 11517 11518 4102 +a 11518 11519 4102 +a 11519 11520 4102 +a 11520 11521 4102 +a 11521 11522 4102 +a 11522 11523 4102 +a 11523 11524 4102 +a 11524 11525 4102 +a 11525 11526 4102 +a 11526 11527 4102 +a 11527 11528 4102 +a 11528 11529 4102 +a 11529 11530 4102 +a 11530 11531 4102 +a 11531 11532 4102 +a 11532 11533 4102 +a 11533 11534 4102 +a 11534 11535 4102 +a 11535 11536 4102 +a 11536 11537 4102 +a 11537 11538 4102 +a 11538 11539 4102 +a 11539 11540 4102 +a 11540 11541 4102 +a 11541 11542 4102 +a 11542 11543 4102 +a 11543 11544 4102 +a 11544 11545 4102 +a 11545 11546 4102 +a 11546 11547 4102 +a 11547 11548 4102 +a 11548 11549 4102 +a 11549 11550 4102 +a 11550 11551 4102 +a 11551 11552 4102 +a 11552 11553 4102 +a 11553 11554 4102 +a 11554 11555 4102 +a 11555 11556 4102 +a 11556 11557 4102 +a 11557 11558 4102 +a 11558 11559 4102 +a 11559 11560 4102 +a 11560 11561 4102 +a 11561 11562 4102 +a 11562 11563 4102 +a 11563 11564 4102 +a 11564 11565 4102 +a 11565 11566 4102 +a 11566 11567 4102 +a 11567 11568 4102 +a 11568 11569 4102 +a 11569 11570 4102 +a 11570 11571 4102 +a 11571 11572 4102 +a 11572 11573 4102 +a 11573 11574 4102 +a 11574 11575 4102 +a 11575 11576 4102 +a 11576 11577 4102 +a 11577 11578 4102 +a 11578 11579 4102 +a 11579 11580 4102 +a 11580 11581 4102 +a 11581 11582 4102 +a 11582 11583 4102 +a 11583 11584 4102 +a 11584 11585 4102 +a 11585 11586 4102 +a 11586 11587 4102 +a 11587 11588 4102 +a 11588 11589 4102 +a 11589 11590 4102 +a 11590 11591 4102 +a 11591 11592 4102 +a 11592 11593 4102 +a 11593 11594 4102 +a 11594 11595 4102 +a 11595 11596 4102 +a 11596 11597 4102 +a 11597 11598 4102 +a 11598 11599 4102 +a 11599 11600 4102 +a 11600 11601 4102 +a 11601 11602 4102 +a 11602 11603 4102 +a 11603 11604 4102 +a 11604 11605 4102 +a 11605 11606 4102 +a 11606 11607 4102 +a 11607 11608 4102 +a 11608 11609 4102 +a 11609 11610 4102 +a 11610 11611 4102 +a 11611 11612 4102 +a 11612 11613 4102 +a 11613 11614 4102 +a 11614 11615 4102 +a 11615 11616 4102 +a 11616 11617 4102 +a 11617 11618 4102 +a 11618 11619 4102 +a 11619 11620 4102 +a 11620 11621 4102 +a 11621 11622 4102 +a 11622 11623 4102 +a 11623 11624 4102 +a 11624 11625 4102 +a 11625 11626 4102 +a 11626 11627 4102 +a 11627 11628 4102 +a 11628 11629 4102 +a 11629 11630 4102 +a 11630 11631 4102 +a 11631 11632 4102 +a 11632 11633 4102 +a 11633 11634 4102 +a 11634 11635 4102 +a 11635 11636 4102 +a 11636 11637 4102 +a 11637 11638 4102 +a 11638 11639 4102 +a 11639 11640 4102 +a 11640 11641 4102 +a 11641 11642 4102 +a 11642 11643 4102 +a 11643 11644 4102 +a 11644 11645 4102 +a 11645 11646 4102 +a 11646 11647 4102 +a 11647 11648 4102 +a 11648 11649 4102 +a 11649 11650 4102 +a 11650 11651 4102 +a 11651 11652 4102 +a 11652 11653 4102 +a 11653 11654 4102 +a 11654 11655 4102 +a 11655 11656 4102 +a 11656 11657 4102 +a 11657 11658 4102 +a 11658 11659 4102 +a 11659 11660 4102 +a 11660 11661 4102 +a 11661 11662 4102 +a 11662 11663 4102 +a 11663 11664 4102 +a 11664 11665 4102 +a 11665 11666 4102 +a 11666 11667 4102 +a 11667 11668 4102 +a 11668 11669 4102 +a 11669 11670 4102 +a 11670 11671 4102 +a 11671 11672 4102 +a 11672 11673 4102 +a 11673 11674 4102 +a 11674 11675 4102 +a 11675 11676 4102 +a 11676 11677 4102 +a 11677 11678 4102 +a 11678 11679 4102 +a 11679 11680 4102 +a 11680 11681 4102 +a 11681 11682 4102 +a 11682 11683 4102 +a 11683 11684 4102 +a 11684 11685 4102 +a 11685 11686 4102 +a 11686 11687 4102 +a 11687 11688 4102 +a 11688 11689 4102 +a 11689 11690 4102 +a 11690 11691 4102 +a 11691 11692 4102 +a 11692 11693 4102 +a 11693 11694 4102 +a 11694 11695 4102 +a 11695 11696 4102 +a 11696 11697 4102 +a 11697 11698 4102 +a 11698 11699 4102 +a 11699 11700 4102 +a 11700 11701 4102 +a 11701 11702 4102 +a 11702 11703 4102 +a 11703 11704 4102 +a 11704 11705 4102 +a 11705 11706 4102 +a 11706 11707 4102 +a 11707 11708 4102 +a 11708 11709 4102 +a 11709 11710 4102 +a 11710 11711 4102 +a 11711 11712 4102 +a 11712 11713 4102 +a 11713 11714 4102 +a 11714 11715 4102 +a 11715 11716 4102 +a 11716 11717 4102 +a 11717 11718 4102 +a 11718 11719 4102 +a 11719 11720 4102 +a 11720 11721 4102 +a 11721 11722 4102 +a 11722 11723 4102 +a 11723 11724 4102 +a 11724 11725 4102 +a 11725 11726 4102 +a 11726 11727 4102 +a 11727 11728 4102 +a 11728 11729 4102 +a 11729 11730 4102 +a 11730 11731 4102 +a 11731 11732 4102 +a 11732 11733 4102 +a 11733 11734 4102 +a 11734 11735 4102 +a 11735 11736 4102 +a 11736 11737 4102 +a 11737 11738 4102 +a 11738 11739 4102 +a 11739 11740 4102 +a 11740 11741 4102 +a 11741 11742 4102 +a 11742 11743 4102 +a 11743 11744 4102 +a 11744 11745 4102 +a 11745 11746 4102 +a 11746 11747 4102 +a 11747 11748 4102 +a 11748 11749 4102 +a 11749 11750 4102 +a 11750 11751 4102 +a 11751 11752 4102 +a 11752 11753 4102 +a 11753 11754 4102 +a 11754 11755 4102 +a 11755 11756 4102 +a 11756 11757 4102 +a 11757 11758 4102 +a 11758 11759 4102 +a 11759 11760 4102 +a 11760 11761 4102 +a 11761 11762 4102 +a 11762 11763 4102 +a 11763 11764 4102 +a 11764 11765 4102 +a 11765 11766 4102 +a 11766 11767 4102 +a 11767 11768 4102 +a 11768 11769 4102 +a 11769 11770 4102 +a 11770 11771 4102 +a 11771 11772 4102 +a 11772 11773 4102 +a 11773 11774 4102 +a 11774 11775 4102 +a 11775 11776 4102 +a 11776 11777 4102 +a 11777 11778 4102 +a 11778 11779 4102 +a 11779 11780 4102 +a 11780 11781 4102 +a 11781 11782 4102 +a 11782 11783 4102 +a 11783 11784 4102 +a 11784 11785 4102 +a 11785 11786 4102 +a 11786 11787 4102 +a 11787 11788 4102 +a 11788 11789 4102 +a 11789 11790 4102 +a 11790 11791 4102 +a 11791 11792 4102 +a 11792 11793 4102 +a 11793 11794 4102 +a 11794 11795 4102 +a 11795 11796 4102 +a 11796 11797 4102 +a 11797 11798 4102 +a 11798 11799 4102 +a 11799 11800 4102 +a 11800 11801 4102 +a 11801 11802 4102 +a 11802 11803 4102 +a 11803 11804 4102 +a 11804 11805 4102 +a 11805 11806 4102 +a 11806 11807 4102 +a 11807 11808 4102 +a 11808 11809 4102 +a 11809 11810 4102 +a 11810 11811 4102 +a 11811 11812 4102 +a 11812 11813 4102 +a 11813 11814 4102 +a 11814 11815 4102 +a 11815 11816 4102 +a 11816 11817 4102 +a 11817 11818 4102 +a 11818 11819 4102 +a 11819 11820 4102 +a 11820 11821 4102 +a 11821 11822 4102 +a 11822 11823 4102 +a 11823 11824 4102 +a 11824 11825 4102 +a 11825 11826 4102 +a 11826 11827 4102 +a 11827 11828 4102 +a 11828 11829 4102 +a 11829 11830 4102 +a 11830 11831 4102 +a 11831 11832 4102 +a 11832 11833 4102 +a 11833 11834 4102 +a 11834 11835 4102 +a 11835 11836 4102 +a 11836 11837 4102 +a 11837 11838 4102 +a 11838 11839 4102 +a 11839 11840 4102 +a 11840 11841 4102 +a 11841 11842 4102 +a 11842 11843 4102 +a 11843 11844 4102 +a 11844 11845 4102 +a 11845 11846 4102 +a 11846 11847 4102 +a 11847 11848 4102 +a 11848 11849 4102 +a 11849 11850 4102 +a 11850 11851 4102 +a 11851 11852 4102 +a 11852 11853 4102 +a 11853 11854 4102 +a 11854 11855 4102 +a 11855 11856 4102 +a 11856 11857 4102 +a 11857 11858 4102 +a 11858 11859 4102 +a 11859 11860 4102 +a 11860 11861 4102 +a 11861 11862 4102 +a 11862 11863 4102 +a 11863 11864 4102 +a 11864 11865 4102 +a 11865 11866 4102 +a 11866 11867 4102 +a 11867 11868 4102 +a 11868 11869 4102 +a 11869 11870 4102 +a 11870 11871 4102 +a 11871 11872 4102 +a 11872 11873 4102 +a 11873 11874 4102 +a 11874 11875 4102 +a 11875 11876 4102 +a 11876 11877 4102 +a 11877 11878 4102 +a 11878 11879 4102 +a 11879 11880 4102 +a 11880 11881 4102 +a 11881 11882 4102 +a 11882 11883 4102 +a 11883 11884 4102 +a 11884 11885 4102 +a 11885 11886 4102 +a 11886 11887 4102 +a 11887 11888 4102 +a 11888 11889 4102 +a 11889 11890 4102 +a 11890 11891 4102 +a 11891 11892 4102 +a 11892 11893 4102 +a 11893 11894 4102 +a 11894 11895 4102 +a 11895 11896 4102 +a 11896 11897 4102 +a 11897 11898 4102 +a 11898 11899 4102 +a 11899 11900 4102 +a 11900 11901 4102 +a 11901 11902 4102 +a 11902 11903 4102 +a 11903 11904 4102 +a 11904 11905 4102 +a 11905 11906 4102 +a 11906 11907 4102 +a 11907 11908 4102 +a 11908 11909 4102 +a 11909 11910 4102 +a 11910 11911 4102 +a 11911 11912 4102 +a 11912 11913 4102 +a 11913 11914 4102 +a 11914 11915 4102 +a 11915 11916 4102 +a 11916 11917 4102 +a 11917 11918 4102 +a 11918 11919 4102 +a 11919 11920 4102 +a 11920 11921 4102 +a 11921 11922 4102 +a 11922 11923 4102 +a 11923 11924 4102 +a 11924 11925 4102 +a 11925 11926 4102 +a 11926 11927 4102 +a 11927 11928 4102 +a 11928 11929 4102 +a 11929 11930 4102 +a 11930 11931 4102 +a 11931 11932 4102 +a 11932 11933 4102 +a 11933 11934 4102 +a 11934 11935 4102 +a 11935 11936 4102 +a 11936 11937 4102 +a 11937 11938 4102 +a 11938 11939 4102 +a 11939 11940 4102 +a 11940 11941 4102 +a 11941 11942 4102 +a 11942 11943 4102 +a 11943 11944 4102 +a 11944 11945 4102 +a 11945 11946 4102 +a 11946 11947 4102 +a 11947 11948 4102 +a 11948 11949 4102 +a 11949 11950 4102 +a 11950 11951 4102 +a 11951 11952 4102 +a 11952 11953 4102 +a 11953 11954 4102 +a 11954 11955 4102 +a 11955 11956 4102 +a 11956 11957 4102 +a 11957 11958 4102 +a 11958 11959 4102 +a 11959 11960 4102 +a 11960 11961 4102 +a 11961 11962 4102 +a 11962 11963 4102 +a 11963 11964 4102 +a 11964 11965 4102 +a 11965 11966 4102 +a 11966 11967 4102 +a 11967 11968 4102 +a 11968 11969 4102 +a 11969 11970 4102 +a 11970 11971 4102 +a 11971 11972 4102 +a 11972 11973 4102 +a 11973 11974 4102 +a 11974 11975 4102 +a 11975 11976 4102 +a 11976 11977 4102 +a 11977 11978 4102 +a 11978 11979 4102 +a 11979 11980 4102 +a 11980 11981 4102 +a 11981 11982 4102 +a 11982 11983 4102 +a 11983 11984 4102 +a 11984 11985 4102 +a 11985 11986 4102 +a 11986 11987 4102 +a 11987 11988 4102 +a 11988 11989 4102 +a 11989 11990 4102 +a 11990 11991 4102 +a 11991 11992 4102 +a 11992 11993 4102 +a 11993 11994 4102 +a 11994 11995 4102 +a 11995 11996 4102 +a 11996 11997 4102 +a 11997 11998 4102 +a 11998 11999 4102 +a 11999 12000 4102 +a 12000 12001 4102 +a 12001 12002 4102 +a 12002 12003 4102 +a 12003 12004 4102 +a 12004 12005 4102 +a 12005 12006 4102 +a 12006 12007 4102 +a 12007 12008 4102 +a 12008 12009 4102 +a 12009 12010 4102 +a 12010 12011 4102 +a 12011 12012 4102 +a 12012 12013 4102 +a 12013 12014 4102 +a 12014 12015 4102 +a 12015 12016 4102 +a 12016 12017 4102 +a 12017 12018 4102 +a 12018 12019 4102 +a 12019 12020 4102 +a 12020 12021 4102 +a 12021 12022 4102 +a 12022 12023 4102 +a 12023 12024 4102 +a 12024 12025 4102 +a 12025 12026 4102 +a 12026 12027 4102 +a 12027 12028 4102 +a 12028 12029 4102 +a 12029 12030 4102 +a 12030 12031 4102 +a 12031 12032 4102 +a 12032 12033 4102 +a 12033 12034 4102 +a 12034 12035 4102 +a 12035 12036 4102 +a 12036 12037 4102 +a 12037 12038 4102 +a 12038 12039 4102 +a 12039 12040 4102 +a 12040 12041 4102 +a 12041 12042 4102 +a 12042 12043 4102 +a 12043 12044 4102 +a 12044 12045 4102 +a 12045 12046 4102 +a 12046 12047 4102 +a 12047 12048 4102 +a 12048 12049 4102 +a 12049 12050 4102 +a 12050 12051 4102 +a 12051 12052 4102 +a 12052 12053 4102 +a 12053 12054 4102 +a 12054 12055 4102 +a 12055 12056 4102 +a 12056 12057 4102 +a 12057 12058 4102 +a 12058 12059 4102 +a 12059 12060 4102 +a 12060 12061 4102 +a 12061 12062 4102 +a 12062 12063 4102 +a 12063 12064 4102 +a 12064 12065 4102 +a 12065 12066 4102 +a 12066 12067 4102 +a 12067 12068 4102 +a 12068 12069 4102 +a 12069 12070 4102 +a 12070 12071 4102 +a 12071 12072 4102 +a 12072 12073 4102 +a 12073 12074 4102 +a 12074 12075 4102 +a 12075 12076 4102 +a 12076 12077 4102 +a 12077 12078 4102 +a 12078 12079 4102 +a 12079 12080 4102 +a 12080 12081 4102 +a 12081 12082 4102 +a 12082 12083 4102 +a 12083 12084 4102 +a 12084 12085 4102 +a 12085 12086 4102 +a 12086 12087 4102 +a 12087 12088 4102 +a 12088 12089 4102 +a 12089 12090 4102 +a 12090 12091 4102 +a 12091 12092 4102 +a 12092 12093 4102 +a 12093 12094 4102 +a 12094 12095 4102 +a 12095 12096 4102 +a 12096 12097 4102 +a 12097 12098 4102 +a 12098 12099 4102 +a 12099 12100 4102 +a 12100 12101 4102 +a 12101 12102 4102 +a 12102 12103 4102 +a 12103 12104 4102 +a 12104 12105 4102 +a 12105 12106 4102 +a 12106 12107 4102 +a 12107 12108 4102 +a 12108 12109 4102 +a 12109 12110 4102 +a 12110 12111 4102 +a 12111 12112 4102 +a 12112 12113 4102 +a 12113 12114 4102 +a 12114 12115 4102 +a 12115 12116 4102 +a 12116 12117 4102 +a 12117 12118 4102 +a 12118 12119 4102 +a 12119 12120 4102 +a 12120 12121 4102 +a 12121 12122 4102 +a 12122 12123 4102 +a 12123 12124 4102 +a 12124 12125 4102 +a 12125 12126 4102 +a 12126 12127 4102 +a 12127 12128 4102 +a 12128 12129 4102 +a 12129 12130 4102 +a 12130 12131 4102 +a 12131 12132 4102 +a 12132 12133 4102 +a 12133 12134 4102 +a 12134 12135 4102 +a 12135 12136 4102 +a 12136 12137 4102 +a 12137 12138 4102 +a 12138 12139 4102 +a 12139 12140 4102 +a 12140 12141 4102 +a 12141 12142 4102 +a 12142 12143 4102 +a 12143 12144 4102 +a 12144 12145 4102 +a 12145 12146 4102 +a 12146 12147 4102 +a 12147 12148 4102 +a 12148 12149 4102 +a 12149 12150 4102 +a 12150 12151 4102 +a 12151 12152 4102 +a 12152 12153 4102 +a 12153 12154 4102 +a 12154 12155 4102 +a 12155 12156 4102 +a 12156 12157 4102 +a 12157 12158 4102 +a 12158 12159 4102 +a 12159 12160 4102 +a 12160 12161 4102 +a 12161 12162 4102 +a 12162 12163 4102 +a 12163 12164 4102 +a 12164 12165 4102 +a 12165 12166 4102 +a 12166 12167 4102 +a 12167 12168 4102 +a 12168 12169 4102 +a 12169 12170 4102 +a 12170 12171 4102 +a 12171 12172 4102 +a 12172 12173 4102 +a 12173 12174 4102 +a 12174 12175 4102 +a 12175 12176 4102 +a 12176 12177 4102 +a 12177 12178 4102 +a 12178 12179 4102 +a 12179 12180 4102 +a 12180 12181 4102 +a 12181 12182 4102 +a 12182 12183 4102 +a 12183 12184 4102 +a 12184 12185 4102 +a 12185 12186 4102 +a 12186 12187 4102 +a 12187 12188 4102 +a 12188 12189 4102 +a 12189 12190 4102 +a 12190 12191 4102 +a 12191 12192 4102 +a 12192 12193 4102 +a 12193 12194 4102 +a 12194 12195 4102 +a 12195 12196 4102 +a 12196 12197 4102 +a 12197 12198 4102 +a 12198 12199 4102 +a 12199 12200 4102 +a 12200 12201 4102 +a 12201 12202 4102 +a 12202 12203 4102 +a 12203 12204 4102 +a 12204 12205 4102 +a 12205 12206 4102 +a 12206 12207 4102 +a 12207 12208 4102 +a 12208 12209 4102 +a 12209 12210 4102 +a 12210 12211 4102 +a 12211 12212 4102 +a 12212 12213 4102 +a 12213 12214 4102 +a 12214 12215 4102 +a 12215 12216 4102 +a 12216 12217 4102 +a 12217 12218 4102 +a 12218 12219 4102 +a 12219 12220 4102 +a 12220 12221 4102 +a 12221 12222 4102 +a 12222 12223 4102 +a 12223 12224 4102 +a 12224 12225 4102 +a 12225 12226 4102 +a 12226 12227 4102 +a 12227 12228 4102 +a 12228 12229 4102 +a 12229 12230 4102 +a 12230 12231 4102 +a 12231 12232 4102 +a 12232 12233 4102 +a 12233 12234 4102 +a 12234 12235 4102 +a 12235 12236 4102 +a 12236 12237 4102 +a 12237 12238 4102 +a 12238 12239 4102 +a 12239 12240 4102 +a 12240 12241 4102 +a 12241 12242 4102 +a 12242 12243 4102 +a 12243 12244 4102 +a 12244 12245 4102 +a 12245 12246 4102 +a 12246 12247 4102 +a 12247 12248 4102 +a 12248 12249 4102 +a 12249 12250 4102 +a 12250 12251 4102 +a 12251 12252 4102 +a 12252 12253 4102 +a 12253 12254 4102 +a 12254 12255 4102 +a 12255 12256 4102 +a 12256 12257 4102 +a 12257 12258 4102 +a 12258 12259 4102 +a 12259 12260 4102 +a 12260 12261 4102 +a 12261 12262 4102 +a 12262 12263 4102 +a 12263 12264 4102 +a 12264 12265 4102 +a 12265 12266 4102 +a 12266 12267 4102 +a 12267 12268 4102 +a 12268 12269 4102 +a 12269 12270 4102 +a 12270 12271 4102 +a 12271 12272 4102 +a 12272 12273 4102 +a 12273 12274 4102 +a 12274 12275 4102 +a 12275 12276 4102 +a 12276 12277 4102 +a 12277 12278 4102 +a 12278 12279 4102 +a 12279 12280 4102 +a 12280 12281 4102 +a 12281 12282 4102 +a 12282 12283 4102 +a 12283 12284 4102 +a 12284 12285 4102 +a 12285 12286 4102 +a 12286 12287 4102 +a 12287 12288 4102 +a 12288 12289 4102 +a 12289 12290 4102 +a 12290 12291 4102 +a 12291 12292 4102 +a 12292 12293 4102 +a 12293 12294 4102 +a 12294 12295 4102 +a 12295 12296 4102 +a 12296 12297 4102 +a 12297 12298 4102 +a 12298 12299 4102 +a 12299 12300 4102 +a 12300 12301 4102 +a 12301 12302 4102 +a 12302 12303 4102 +a 12303 12304 4102 +a 12304 12305 4102 +a 12305 12306 4102 +a 12306 12307 4102 +a 12307 12308 4102 +a 12308 12309 4102 +a 12309 12310 4102 +a 12310 12311 4102 +a 12311 12312 4102 +a 12312 12313 4102 +a 12313 12314 4102 +a 12314 12315 4102 +a 12315 12316 4102 +a 12316 12317 4102 +a 12317 12318 4102 +a 12318 12319 4102 +a 12319 12320 4102 +a 12320 12321 4102 +a 12321 12322 4102 +a 12322 12323 4102 +a 12323 12324 4102 +a 12324 12325 4102 +a 12325 12326 4102 +a 12326 12327 4102 +a 12327 12328 4102 +a 12328 12329 4102 +a 12329 12330 4102 +a 12330 12331 4102 +a 12331 12332 4102 +a 12332 12333 4102 +a 12333 12334 4102 +a 12334 12335 4102 +a 12335 12336 4102 +a 12336 12337 4102 +a 12337 12338 4102 +a 12338 12339 4102 +a 12339 12340 4102 +a 12340 12341 4102 +a 12341 12342 4102 +a 12342 12343 4102 +a 12343 12344 4102 +a 12344 12345 4102 +a 12345 12346 4102 +a 12346 12347 4102 +a 12347 12348 4102 +a 12348 12349 4102 +a 12349 12350 4102 +a 12350 12351 4102 +a 12351 12352 4102 +a 12352 12353 4102 +a 12353 12354 4102 +a 12354 12355 4102 +a 12355 12356 4102 +a 12356 12357 4102 +a 12357 12358 4102 +a 12358 12359 4102 +a 12359 12360 4102 +a 12360 12361 4102 +a 12361 12362 4102 +a 12362 12363 4102 +a 12363 12364 4102 +a 12364 12365 4102 +a 12365 12366 4102 +a 12366 12367 4102 +a 12367 12368 4102 +a 12368 12369 4102 +a 12369 12370 4102 +a 12370 12371 4102 +a 12371 12372 4102 +a 12372 12373 4102 +a 12373 12374 4102 +a 12374 12375 4102 +a 12375 12376 4102 +a 12376 12377 4102 +a 12377 12378 4102 +a 12378 12379 4102 +a 12379 12380 4102 +a 12380 12381 4102 +a 12381 12382 4102 +a 12382 12383 4102 +a 12383 12384 4102 +a 12384 12385 4102 +a 12385 12386 4102 +a 12386 12387 4102 +a 12387 12388 4102 +a 12388 12389 4102 +a 12389 12390 4102 +a 12390 12391 4102 +a 12391 12392 4102 +a 12392 12393 4102 +a 12393 12394 4102 +a 12394 12395 4102 +a 12395 12396 4102 +a 12396 12397 4102 +a 12397 12398 4102 +a 12398 12399 4102 +a 12399 12400 4102 +a 12400 12401 4102 +a 12401 12402 4102 +a 12402 12403 4102 +a 12403 12404 4102 +a 12404 12405 4102 +a 12405 12406 4102 +a 12406 12407 4102 +a 12407 12408 4102 +a 12408 12409 4102 +a 12409 12410 4102 +a 12410 12411 4102 +a 12411 12412 4102 +a 12412 12413 4102 +a 12413 12414 4102 +a 12414 12415 4102 +a 12415 12416 4102 +a 12416 12417 4102 +a 12417 12418 4102 +a 12418 12419 4102 +a 12419 12420 4102 +a 12420 12421 4102 +a 12421 12422 4102 +a 12422 12423 4102 +a 12423 12424 4102 +a 12424 12425 4102 +a 12425 12426 4102 +a 12426 12427 4102 +a 12427 12428 4102 +a 12428 12429 4102 +a 12429 12430 4102 +a 12430 12431 4102 +a 12431 12432 4102 +a 12432 12433 4102 +a 12433 12434 4102 +a 12434 12435 4102 +a 12435 12436 4102 +a 12436 12437 4102 +a 12437 12438 4102 +a 12438 12439 4102 +a 12439 12440 4102 +a 12440 12441 4102 +a 12441 12442 4102 +a 12442 12443 4102 +a 12443 12444 4102 +a 12444 12445 4102 +a 12445 12446 4102 +a 12446 12447 4102 +a 12447 12448 4102 +a 12448 12449 4102 +a 12449 12450 4102 +a 12450 12451 4102 +a 12451 12452 4102 +a 12452 12453 4102 +a 12453 12454 4102 +a 12454 12455 4102 +a 12455 12456 4102 +a 12456 12457 4102 +a 12457 12458 4102 +a 12458 12459 4102 +a 12459 12460 4102 +a 12460 12461 4102 +a 12461 12462 4102 +a 12462 12463 4102 +a 12463 12464 4102 +a 12464 12465 4102 +a 12465 12466 4102 +a 12466 12467 4102 +a 12467 12468 4102 +a 12468 12469 4102 +a 12469 12470 4102 +a 12470 12471 4102 +a 12471 12472 4102 +a 12472 12473 4102 +a 12473 12474 4102 +a 12474 12475 4102 +a 12475 12476 4102 +a 12476 12477 4102 +a 12477 12478 4102 +a 12478 12479 4102 +a 12479 12480 4102 +a 12480 12481 4102 +a 12481 12482 4102 +a 12482 12483 4102 +a 12483 12484 4102 +a 12484 12485 4102 +a 12485 12486 4102 +a 12486 12487 4102 +a 12487 12488 4102 +a 12488 12489 4102 +a 12489 12490 4102 +a 12490 12491 4102 +a 12491 12492 4102 +a 12492 12493 4102 +a 12493 12494 4102 +a 12494 12495 4102 +a 12495 12496 4102 +a 12496 12497 4102 +a 12497 12498 4102 +a 12498 12499 4102 +a 12499 12500 4102 +a 12500 12501 4102 +a 12501 12502 4102 +a 12502 12503 4102 +a 12503 12504 4102 +a 12504 12505 4102 +a 12505 12506 4102 +a 12506 12507 4102 +a 12507 12508 4102 +a 12508 12509 4102 +a 12509 12510 4102 +a 12510 12511 4102 +a 12511 12512 4102 +a 12512 12513 4102 +a 12513 12514 4102 +a 12514 12515 4102 +a 12515 12516 4102 +a 12516 12517 4102 +a 12517 12518 4102 +a 12518 12519 4102 +a 12519 12520 4102 +a 12520 12521 4102 +a 12521 12522 4102 +a 12522 12523 4102 +a 12523 12524 4102 +a 12524 12525 4102 +a 12525 12526 4102 +a 12526 12527 4102 +a 12527 12528 4102 +a 12528 12529 4102 +a 12529 12530 4102 +a 12530 12531 4102 +a 12531 12532 4102 +a 12532 12533 4102 +a 12533 12534 4102 +a 12534 12535 4102 +a 12535 12536 4102 +a 12536 12537 4102 +a 12537 12538 4102 +a 12538 12539 4102 +a 12539 12540 4102 +a 12540 12541 4102 +a 12541 12542 4102 +a 12542 12543 4102 +a 12543 12544 4102 +a 12544 12545 4102 +a 12545 12546 4102 +a 12546 12547 4102 +a 12547 12548 4102 +a 12548 12549 4102 +a 12549 12550 4102 +a 12550 12551 4102 +a 12551 12552 4102 +a 12552 12553 4102 +a 12553 12554 4102 +a 12554 12555 4102 +a 12555 12556 4102 +a 12556 12557 4102 +a 12557 12558 4102 +a 12558 12559 4102 +a 12559 12560 4102 +a 12560 12561 4102 +a 12561 12562 4102 +a 12562 12563 4102 +a 12563 12564 4102 +a 12564 12565 4102 +a 12565 12566 4102 +a 12566 12567 4102 +a 12567 12568 4102 +a 12568 12569 4102 +a 12569 12570 4102 +a 12570 12571 4102 +a 12571 12572 4102 +a 12572 12573 4102 +a 12573 12574 4102 +a 12574 12575 4102 +a 12575 12576 4102 +a 12576 12577 4102 +a 12577 12578 4102 +a 12578 12579 4102 +a 12579 12580 4102 +a 12580 12581 4102 +a 12581 12582 4102 +a 12582 12583 4102 +a 12583 12584 4102 +a 12584 12585 4102 +a 12585 12586 4102 +a 12586 12587 4102 +a 12587 12588 4102 +a 12588 12589 4102 +a 12589 12590 4102 +a 12590 12591 4102 +a 12591 12592 4102 +a 12592 12593 4102 +a 12593 12594 4102 +a 12594 12595 4102 +a 12595 12596 4102 +a 12596 12597 4102 +a 12597 12598 4102 +a 12598 12599 4102 +a 12599 12600 4102 +a 12600 12601 4102 +a 12601 12602 4102 +a 12602 12603 4102 +a 12603 12604 4102 +a 12604 12605 4102 +a 12605 12606 4102 +a 12606 12607 4102 +a 12607 12608 4102 +a 12608 12609 4102 +a 12609 12610 4102 +a 12610 12611 4102 +a 12611 12612 4102 +a 12612 12613 4102 +a 12613 12614 4102 +a 12614 12615 4102 +a 12615 12616 4102 +a 12616 12617 4102 +a 12617 12618 4102 +a 12618 12619 4102 +a 12619 12620 4102 +a 12620 12621 4102 +a 12621 12622 4102 +a 12622 12623 4102 +a 12623 12624 4102 +a 12624 12625 4102 +a 12625 12626 4102 +a 12626 12627 4102 +a 12627 12628 4102 +a 12628 12629 4102 +a 12629 12630 4102 +a 12630 12631 4102 +a 12631 12632 4102 +a 12632 12633 4102 +a 12633 12634 4102 +a 12634 12635 4102 +a 12635 12636 4102 +a 12636 12637 4102 +a 12637 12638 4102 +a 12638 12639 4102 +a 12639 12640 4102 +a 12640 12641 4102 +a 12641 12642 4102 +a 12642 12643 4102 +a 12643 12644 4102 +a 12644 12645 4102 +a 12645 12646 4102 +a 12646 12647 4102 +a 12647 12648 4102 +a 12648 12649 4102 +a 12649 12650 4102 +a 12650 12651 4102 +a 12651 12652 4102 +a 12652 12653 4102 +a 12653 12654 4102 +a 12654 12655 4102 +a 12655 12656 4102 +a 12656 12657 4102 +a 12657 12658 4102 +a 12658 12659 4102 +a 12659 12660 4102 +a 12660 12661 4102 +a 12661 12662 4102 +a 12662 12663 4102 +a 12663 12664 4102 +a 12664 12665 4102 +a 12665 12666 4102 +a 12666 12667 4102 +a 12667 12668 4102 +a 12668 12669 4102 +a 12669 12670 4102 +a 12670 12671 4102 +a 12671 12672 4102 +a 12672 12673 4102 +a 12673 12674 4102 +a 12674 12675 4102 +a 12675 12676 4102 +a 12676 12677 4102 +a 12677 12678 4102 +a 12678 12679 4102 +a 12679 12680 4102 +a 12680 12681 4102 +a 12681 12682 4102 +a 12682 12683 4102 +a 12683 12684 4102 +a 12684 12685 4102 +a 12685 12686 4102 +a 12686 12687 4102 +a 12687 12688 4102 +a 12688 12689 4102 +a 12689 12690 4102 +a 12690 12691 4102 +a 12691 12692 4102 +a 12692 12693 4102 +a 12693 12694 4102 +a 12694 12695 4102 +a 12695 12696 4102 +a 12696 12697 4102 +a 12697 12698 4102 +a 12698 12699 4102 +a 12699 12700 4102 +a 12700 12701 4102 +a 12701 12702 4102 +a 12702 12703 4102 +a 12703 12704 4102 +a 12704 12705 4102 +a 12705 12706 4102 +a 12706 12707 4102 +a 12707 12708 4102 +a 12708 12709 4102 +a 12709 12710 4102 +a 12710 12711 4102 +a 12711 12712 4102 +a 12712 12713 4102 +a 12713 12714 4102 +a 12714 12715 4102 +a 12715 12716 4102 +a 12716 12717 4102 +a 12717 12718 4102 +a 12718 12719 4102 +a 12719 12720 4102 +a 12720 12721 4102 +a 12721 12722 4102 +a 12722 12723 4102 +a 12723 12724 4102 +a 12724 12725 4102 +a 12725 12726 4102 +a 12726 12727 4102 +a 12727 12728 4102 +a 12728 12729 4102 +a 12729 12730 4102 +a 12730 12731 4102 +a 12731 12732 4102 +a 12732 12733 4102 +a 12733 12734 4102 +a 12734 12735 4102 +a 12735 12736 4102 +a 12736 12737 4102 +a 12737 12738 4102 +a 12738 12739 4102 +a 12739 12740 4102 +a 12740 12741 4102 +a 12741 12742 4102 +a 12742 12743 4102 +a 12743 12744 4102 +a 12744 12745 4102 +a 12745 12746 4102 +a 12746 12747 4102 +a 12747 12748 4102 +a 12748 12749 4102 +a 12749 12750 4102 +a 12750 12751 4102 +a 12751 12752 4102 +a 12752 12753 4102 +a 12753 12754 4102 +a 12754 12755 4102 +a 12755 12756 4102 +a 12756 12757 4102 +a 12757 12758 4102 +a 12758 12759 4102 +a 12759 12760 4102 +a 12760 12761 4102 +a 12761 12762 4102 +a 12762 12763 4102 +a 12763 12764 4102 +a 12764 12765 4102 +a 12765 12766 4102 +a 12766 12767 4102 +a 12767 12768 4102 +a 12768 12769 4102 +a 12769 12770 4102 +a 12770 12771 4102 +a 12771 12772 4102 +a 12772 12773 4102 +a 12773 12774 4102 +a 12774 12775 4102 +a 12775 12776 4102 +a 12776 12777 4102 +a 12777 12778 4102 +a 12778 12779 4102 +a 12779 12780 4102 +a 12780 12781 4102 +a 12781 12782 4102 +a 12782 12783 4102 +a 12783 12784 4102 +a 12784 12785 4102 +a 12785 12786 4102 +a 12786 12787 4102 +a 12787 12788 4102 +a 12788 12789 4102 +a 12789 12790 4102 +a 12790 12791 4102 +a 12791 12792 4102 +a 12792 12793 4102 +a 12793 12794 4102 +a 12794 12795 4102 +a 12795 12796 4102 +a 12796 12797 4102 +a 12797 12798 4102 +a 12798 12799 4102 +a 12799 12800 4102 +a 12800 12801 4102 +a 12801 12802 4102 +a 12802 12803 4102 +a 12803 12804 4102 +a 12804 12805 4102 +a 12805 12806 4102 +a 12806 12807 4102 +a 12807 12808 4102 +a 12808 12809 4102 +a 12809 12810 4102 +a 12810 12811 4102 +a 12811 12812 4102 +a 12812 12813 4102 +a 12813 12814 4102 +a 12814 12815 4102 +a 12815 12816 4102 +a 12816 12817 4102 +a 12817 12818 4102 +a 12818 12819 4102 +a 12819 12820 4102 +a 12820 12821 4102 +a 12821 12822 4102 +a 12822 12823 4102 +a 12823 12824 4102 +a 12824 12825 4102 +a 12825 12826 4102 +a 12826 12827 4102 +a 12827 12828 4102 +a 12828 12829 4102 +a 12829 12830 4102 +a 12830 12831 4102 +a 12831 12832 4102 +a 12832 12833 4102 +a 12833 12834 4102 +a 12834 12835 4102 +a 12835 12836 4102 +a 12836 12837 4102 +a 12837 12838 4102 +a 12838 12839 4102 +a 12839 12840 4102 +a 12840 12841 4102 +a 12841 12842 4102 +a 12842 12843 4102 +a 12843 12844 4102 +a 12844 12845 4102 +a 12845 12846 4102 +a 12846 12847 4102 +a 12847 12848 4102 +a 12848 12849 4102 +a 12849 12850 4102 +a 12850 12851 4102 +a 12851 12852 4102 +a 12852 12853 4102 +a 12853 12854 4102 +a 12854 12855 4102 +a 12855 12856 4102 +a 12856 12857 4102 +a 12857 12858 4102 +a 12858 12859 4102 +a 12859 12860 4102 +a 12860 12861 4102 +a 12861 12862 4102 +a 12862 12863 4102 +a 12863 12864 4102 +a 12864 12865 4102 +a 12865 12866 4102 +a 12866 12867 4102 +a 12867 12868 4102 +a 12868 12869 4102 +a 12869 12870 4102 +a 12870 12871 4102 +a 12871 12872 4102 +a 12872 12873 4102 +a 12873 12874 4102 +a 12874 12875 4102 +a 12875 12876 4102 +a 12876 12877 4102 +a 12877 12878 4102 +a 12878 12879 4102 +a 12879 12880 4102 +a 12880 12881 4102 +a 12881 12882 4102 +a 12882 12883 4102 +a 12883 12884 4102 +a 12884 12885 4102 +a 12885 12886 4102 +a 12886 12887 4102 +a 12887 12888 4102 +a 12888 12889 4102 +a 12889 12890 4102 +a 12890 12891 4102 +a 12891 12892 4102 +a 12892 12893 4102 +a 12893 12894 4102 +a 12894 12895 4102 +a 12895 12896 4102 +a 12896 12897 4102 +a 12897 12898 4102 +a 12898 12899 4102 +a 12899 12900 4102 +a 12900 12901 4102 +a 12901 12902 4102 +a 12902 12903 4102 +a 12903 12904 4102 +a 12904 12905 4102 +a 12905 12906 4102 +a 12906 12907 4102 +a 12907 12908 4102 +a 12908 12909 4102 +a 12909 12910 4102 +a 12910 12911 4102 +a 12911 12912 4102 +a 12912 12913 4102 +a 12913 12914 4102 +a 12914 12915 4102 +a 12915 12916 4102 +a 12916 12917 4102 +a 12917 12918 4102 +a 12918 12919 4102 +a 12919 12920 4102 +a 12920 12921 4102 +a 12921 12922 4102 +a 12922 12923 4102 +a 12923 12924 4102 +a 12924 12925 4102 +a 12925 12926 4102 +a 12926 12927 4102 +a 12927 12928 4102 +a 12928 12929 4102 +a 12929 12930 4102 +a 12930 12931 4102 +a 12931 12932 4102 +a 12932 12933 4102 +a 12933 12934 4102 +a 12934 12935 4102 +a 12935 12936 4102 +a 12936 12937 4102 +a 12937 12938 4102 +a 12938 12939 4102 +a 12939 12940 4102 +a 12940 12941 4102 +a 12941 12942 4102 +a 12942 12943 4102 +a 12943 12944 4102 +a 12944 12945 4102 +a 12945 12946 4102 +a 12946 12947 4102 +a 12947 12948 4102 +a 12948 12949 4102 +a 12949 12950 4102 +a 12950 12951 4102 +a 12951 12952 4102 +a 12952 12953 4102 +a 12953 12954 4102 +a 12954 12955 4102 +a 12955 12956 4102 +a 12956 12957 4102 +a 12957 12958 4102 +a 12958 12959 4102 +a 12959 12960 4102 +a 12960 12961 4102 +a 12961 12962 4102 +a 12962 12963 4102 +a 12963 12964 4102 +a 12964 12965 4102 +a 12965 12966 4102 +a 12966 12967 4102 +a 12967 12968 4102 +a 12968 12969 4102 +a 12969 12970 4102 +a 12970 12971 4102 +a 12971 12972 4102 +a 12972 12973 4102 +a 12973 12974 4102 +a 12974 12975 4102 +a 12975 12976 4102 +a 12976 12977 4102 +a 12977 12978 4102 +a 12978 12979 4102 +a 12979 12980 4102 +a 12980 12981 4102 +a 12981 12982 4102 +a 12982 12983 4102 +a 12983 12984 4102 +a 12984 12985 4102 +a 12985 12986 4102 +a 12986 12987 4102 +a 12987 12988 4102 +a 12988 12989 4102 +a 12989 12990 4102 +a 12990 12991 4102 +a 12991 12992 4102 +a 12992 12993 4102 +a 12993 12994 4102 +a 12994 12995 4102 +a 12995 12996 4102 +a 12996 12997 4102 +a 12997 12998 4102 +a 12998 12999 4102 +a 12999 13000 4102 +a 13000 13001 4102 +a 13001 13002 4102 +a 13002 13003 4102 +a 13003 13004 4102 +a 13004 13005 4102 +a 13005 13006 4102 +a 13006 13007 4102 +a 13007 13008 4102 +a 13008 13009 4102 +a 13009 13010 4102 +a 13010 13011 4102 +a 13011 13012 4102 +a 13012 13013 4102 +a 13013 13014 4102 +a 13014 13015 4102 +a 13015 13016 4102 +a 13016 13017 4102 +a 13017 13018 4102 +a 13018 13019 4102 +a 13019 13020 4102 +a 13020 13021 4102 +a 13021 13022 4102 +a 13022 13023 4102 +a 13023 13024 4102 +a 13024 13025 4102 +a 13025 13026 4102 +a 13026 13027 4102 +a 13027 13028 4102 +a 13028 13029 4102 +a 13029 13030 4102 +a 13030 13031 4102 +a 13031 13032 4102 +a 13032 13033 4102 +a 13033 13034 4102 +a 13034 13035 4102 +a 13035 13036 4102 +a 13036 13037 4102 +a 13037 13038 4102 +a 13038 13039 4102 +a 13039 13040 4102 +a 13040 13041 4102 +a 13041 13042 4102 +a 13042 13043 4102 +a 13043 13044 4102 +a 13044 13045 4102 +a 13045 13046 4102 +a 13046 13047 4102 +a 13047 13048 4102 +a 13048 13049 4102 +a 13049 13050 4102 +a 13050 13051 4102 +a 13051 13052 4102 +a 13052 13053 4102 +a 13053 13054 4102 +a 13054 13055 4102 +a 13055 13056 4102 +a 13056 13057 4102 +a 13057 13058 4102 +a 13058 13059 4102 +a 13059 13060 4102 +a 13060 13061 4102 +a 13061 13062 4102 +a 13062 13063 4102 +a 13063 13064 4102 +a 13064 13065 4102 +a 13065 13066 4102 +a 13066 13067 4102 +a 13067 13068 4102 +a 13068 13069 4102 +a 13069 13070 4102 +a 13070 13071 4102 +a 13071 13072 4102 +a 13072 13073 4102 +a 13073 13074 4102 +a 13074 13075 4102 +a 13075 13076 4102 +a 13076 13077 4102 +a 13077 13078 4102 +a 13078 13079 4102 +a 13079 13080 4102 +a 13080 13081 4102 +a 13081 13082 4102 +a 13082 13083 4102 +a 13083 13084 4102 +a 13084 13085 4102 +a 13085 13086 4102 +a 13086 13087 4102 +a 13087 13088 4102 +a 13088 13089 4102 +a 13089 13090 4102 +a 13090 13091 4102 +a 13091 13092 4102 +a 13092 13093 4102 +a 13093 13094 4102 +a 13094 13095 4102 +a 13095 13096 4102 +a 13096 13097 4102 +a 13097 13098 4102 +a 13098 13099 4102 +a 13099 13100 4102 +a 13100 13101 4102 +a 13101 13102 4102 +a 13102 13103 4102 +a 13103 13104 4102 +a 13104 13105 4102 +a 13105 13106 4102 +a 13106 13107 4102 +a 13107 13108 4102 +a 13108 13109 4102 +a 13109 13110 4102 +a 13110 13111 4102 +a 13111 13112 4102 +a 13112 13113 4102 +a 13113 13114 4102 +a 13114 13115 4102 +a 13115 13116 4102 +a 13116 13117 4102 +a 13117 13118 4102 +a 13118 13119 4102 +a 13119 13120 4102 +a 13120 13121 4102 +a 13121 13122 4102 +a 13122 13123 4102 +a 13123 13124 4102 +a 13124 13125 4102 +a 13125 13126 4102 +a 13126 13127 4102 +a 13127 13128 4102 +a 13128 13129 4102 +a 13129 13130 4102 +a 13130 13131 4102 +a 13131 13132 4102 +a 13132 13133 4102 +a 13133 13134 4102 +a 13134 13135 4102 +a 13135 13136 4102 +a 13136 13137 4102 +a 13137 13138 4102 +a 13138 13139 4102 +a 13139 13140 4102 +a 13140 13141 4102 +a 13141 13142 4102 +a 13142 13143 4102 +a 13143 13144 4102 +a 13144 13145 4102 +a 13145 13146 4102 +a 13146 13147 4102 +a 13147 13148 4102 +a 13148 13149 4102 +a 13149 13150 4102 +a 13150 13151 4102 +a 13151 13152 4102 +a 13152 13153 4102 +a 13153 13154 4102 +a 13154 13155 4102 +a 13155 13156 4102 +a 13156 13157 4102 +a 13157 13158 4102 +a 13158 13159 4102 +a 13159 13160 4102 +a 13160 13161 4102 +a 13161 13162 4102 +a 13162 13163 4102 +a 13163 13164 4102 +a 13164 13165 4102 +a 13165 13166 4102 +a 13166 13167 4102 +a 13167 13168 4102 +a 13168 13169 4102 +a 13169 13170 4102 +a 13170 13171 4102 +a 13171 13172 4102 +a 13172 13173 4102 +a 13173 13174 4102 +a 13174 13175 4102 +a 13175 13176 4102 +a 13176 13177 4102 +a 13177 13178 4102 +a 13178 13179 4102 +a 13179 13180 4102 +a 13180 13181 4102 +a 13181 13182 4102 +a 13182 13183 4102 +a 13183 13184 4102 +a 13184 13185 4102 +a 13185 13186 4102 +a 13186 13187 4102 +a 13187 13188 4102 +a 13188 13189 4102 +a 13189 13190 4102 +a 13190 13191 4102 +a 13191 13192 4102 +a 13192 13193 4102 +a 13193 13194 4102 +a 13194 13195 4102 +a 13195 13196 4102 +a 13196 13197 4102 +a 13197 13198 4102 +a 13198 13199 4102 +a 13199 13200 4102 +a 13200 13201 4102 +a 13201 13202 4102 +a 13202 13203 4102 +a 13203 13204 4102 +a 13204 13205 4102 +a 13205 13206 4102 +a 13206 13207 4102 +a 13207 13208 4102 +a 13208 13209 4102 +a 13209 13210 4102 +a 13210 13211 4102 +a 13211 13212 4102 +a 13212 13213 4102 +a 13213 13214 4102 +a 13214 13215 4102 +a 13215 13216 4102 +a 13216 13217 4102 +a 13217 13218 4102 +a 13218 13219 4102 +a 13219 13220 4102 +a 13220 13221 4102 +a 13221 13222 4102 +a 13222 13223 4102 +a 13223 13224 4102 +a 13224 13225 4102 +a 13225 13226 4102 +a 13226 13227 4102 +a 13227 13228 4102 +a 13228 13229 4102 +a 13229 13230 4102 +a 13230 13231 4102 +a 13231 13232 4102 +a 13232 13233 4102 +a 13233 13234 4102 +a 13234 13235 4102 +a 13235 13236 4102 +a 13236 13237 4102 +a 13237 13238 4102 +a 13238 13239 4102 +a 13239 13240 4102 +a 13240 13241 4102 +a 13241 13242 4102 +a 13242 13243 4102 +a 13243 13244 4102 +a 13244 13245 4102 +a 13245 13246 4102 +a 13246 13247 4102 +a 13247 13248 4102 +a 13248 13249 4102 +a 13249 13250 4102 +a 13250 13251 4102 +a 13251 13252 4102 +a 13252 13253 4102 +a 13253 13254 4102 +a 13254 13255 4102 +a 13255 13256 4102 +a 13256 13257 4102 +a 13257 13258 4102 +a 13258 13259 4102 +a 13259 13260 4102 +a 13260 13261 4102 +a 13261 13262 4102 +a 13262 13263 4102 +a 13263 13264 4102 +a 13264 13265 4102 +a 13265 13266 4102 +a 13266 13267 4102 +a 13267 13268 4102 +a 13268 13269 4102 +a 13269 13270 4102 +a 13270 13271 4102 +a 13271 13272 4102 +a 13272 13273 4102 +a 13273 13274 4102 +a 13274 13275 4102 +a 13275 13276 4102 +a 13276 13277 4102 +a 13277 13278 4102 +a 13278 13279 4102 +a 13279 13280 4102 +a 13280 13281 4102 +a 13281 13282 4102 +a 13282 13283 4102 +a 13283 13284 4102 +a 13284 13285 4102 +a 13285 13286 4102 +a 13286 13287 4102 +a 13287 13288 4102 +a 13288 13289 4102 +a 13289 13290 4102 +a 13290 13291 4102 +a 13291 13292 4102 +a 13292 13293 4102 +a 13293 13294 4102 +a 13294 13295 4102 +a 13295 13296 4102 +a 13296 13297 4102 +a 13297 13298 4102 +a 13298 13299 4102 +a 13299 13300 4102 +a 13300 13301 4102 +a 13301 13302 4102 +a 13302 13303 4102 +a 13303 13304 4102 +a 13304 13305 4102 +a 13305 13306 4102 +a 13306 13307 4102 +a 13307 13308 4102 +a 13308 13309 4102 +a 13309 13310 4102 +a 13310 13311 4102 +a 13311 13312 4102 +a 13312 13313 4102 +a 13313 13314 4102 +a 13314 13315 4102 +a 13315 13316 4102 +a 13316 13317 4102 +a 13317 13318 4102 +a 13318 13319 4102 +a 13319 13320 4102 +a 13320 13321 4102 +a 13321 13322 4102 +a 13322 13323 4102 +a 13323 13324 4102 +a 13324 13325 4102 +a 13325 13326 4102 +a 13326 13327 4102 +a 13327 13328 4102 +a 13328 13329 4102 +a 13329 13330 4102 +a 13330 13331 4102 +a 13331 13332 4102 +a 13332 13333 4102 +a 13333 13334 4102 +a 13334 13335 4102 +a 13335 13336 4102 +a 13336 13337 4102 +a 13337 13338 4102 +a 13338 13339 4102 +a 13339 13340 4102 +a 13340 13341 4102 +a 13341 13342 4102 +a 13342 13343 4102 +a 13343 13344 4102 +a 13344 13345 4102 +a 13345 13346 4102 +a 13346 13347 4102 +a 13347 13348 4102 +a 13348 13349 4102 +a 13349 13350 4102 +a 13350 13351 4102 +a 13351 13352 4102 +a 13352 13353 4102 +a 13353 13354 4102 +a 13354 13355 4102 +a 13355 13356 4102 +a 13356 13357 4102 +a 13357 13358 4102 +a 13358 13359 4102 +a 13359 13360 4102 +a 13360 13361 4102 +a 13361 13362 4102 +a 13362 13363 4102 +a 13363 13364 4102 +a 13364 13365 4102 +a 13365 13366 4102 +a 13366 13367 4102 +a 13367 13368 4102 +a 13368 13369 4102 +a 13369 13370 4102 +a 13370 13371 4102 +a 13371 13372 4102 +a 13372 13373 4102 +a 13373 13374 4102 +a 13374 13375 4102 +a 13375 13376 4102 +a 13376 13377 4102 +a 13377 13378 4102 +a 13378 13379 4102 +a 13379 13380 4102 +a 13380 13381 4102 +a 13381 13382 4102 +a 13382 13383 4102 +a 13383 13384 4102 +a 13384 13385 4102 +a 13385 13386 4102 +a 13386 13387 4102 +a 13387 13388 4102 +a 13388 13389 4102 +a 13389 13390 4102 +a 13390 13391 4102 +a 13391 13392 4102 +a 13392 13393 4102 +a 13393 13394 4102 +a 13394 13395 4102 +a 13395 13396 4102 +a 13396 13397 4102 +a 13397 13398 4102 +a 13398 13399 4102 +a 13399 13400 4102 +a 13400 13401 4102 +a 13401 13402 4102 +a 13402 13403 4102 +a 13403 13404 4102 +a 13404 13405 4102 +a 13405 13406 4102 +a 13406 13407 4102 +a 13407 13408 4102 +a 13408 13409 4102 +a 13409 13410 4102 +a 13410 13411 4102 +a 13411 13412 4102 +a 13412 13413 4102 +a 13413 13414 4102 +a 13414 13415 4102 +a 13415 13416 4102 +a 13416 13417 4102 +a 13417 13418 4102 +a 13418 13419 4102 +a 13419 13420 4102 +a 13420 13421 4102 +a 13421 13422 4102 +a 13422 13423 4102 +a 13423 13424 4102 +a 13424 13425 4102 +a 13425 13426 4102 +a 13426 13427 4102 +a 13427 13428 4102 +a 13428 13429 4102 +a 13429 13430 4102 +a 13430 13431 4102 +a 13431 13432 4102 +a 13432 13433 4102 +a 13433 13434 4102 +a 13434 13435 4102 +a 13435 13436 4102 +a 13436 13437 4102 +a 13437 13438 4102 +a 13438 13439 4102 +a 13439 13440 4102 +a 13440 13441 4102 +a 13441 13442 4102 +a 13442 13443 4102 +a 13443 13444 4102 +a 13444 13445 4102 +a 13445 13446 4102 +a 13446 13447 4102 +a 13447 13448 4102 +a 13448 13449 4102 +a 13449 13450 4102 +a 13450 13451 4102 +a 13451 13452 4102 +a 13452 13453 4102 +a 13453 13454 4102 +a 13454 13455 4102 +a 13455 13456 4102 +a 13456 13457 4102 +a 13457 13458 4102 +a 13458 13459 4102 +a 13459 13460 4102 +a 13460 13461 4102 +a 13461 13462 4102 +a 13462 13463 4102 +a 13463 13464 4102 +a 13464 13465 4102 +a 13465 13466 4102 +a 13466 13467 4102 +a 13467 13468 4102 +a 13468 13469 4102 +a 13469 13470 4102 +a 13470 13471 4102 +a 13471 13472 4102 +a 13472 13473 4102 +a 13473 13474 4102 +a 13474 13475 4102 +a 13475 13476 4102 +a 13476 13477 4102 +a 13477 13478 4102 +a 13478 13479 4102 +a 13479 13480 4102 +a 13480 13481 4102 +a 13481 13482 4102 +a 13482 13483 4102 +a 13483 13484 4102 +a 13484 13485 4102 +a 13485 13486 4102 +a 13486 13487 4102 +a 13487 13488 4102 +a 13488 13489 4102 +a 13489 13490 4102 +a 13490 13491 4102 +a 13491 13492 4102 +a 13492 13493 4102 +a 13493 13494 4102 +a 13494 13495 4102 +a 13495 13496 4102 +a 13496 13497 4102 +a 13497 13498 4102 +a 13498 13499 4102 +a 13499 13500 4102 +a 13500 13501 4102 +a 13501 13502 4102 +a 13502 13503 4102 +a 13503 13504 4102 +a 13504 13505 4102 +a 13505 13506 4102 +a 13506 13507 4102 +a 13507 13508 4102 +a 13508 13509 4102 +a 13509 13510 4102 +a 13510 13511 4102 +a 13511 13512 4102 +a 13512 13513 4102 +a 13513 13514 4102 +a 13514 13515 4102 +a 13515 13516 4102 +a 13516 13517 4102 +a 13517 13518 4102 +a 13518 13519 4102 +a 13519 13520 4102 +a 13520 13521 4102 +a 13521 13522 4102 +a 13522 13523 4102 +a 13523 13524 4102 +a 13524 13525 4102 +a 13525 13526 4102 +a 13526 13527 4102 +a 13527 13528 4102 +a 13528 13529 4102 +a 13529 13530 4102 +a 13530 13531 4102 +a 13531 13532 4102 +a 13532 13533 4102 +a 13533 13534 4102 +a 13534 13535 4102 +a 13535 13536 4102 +a 13536 13537 4102 +a 13537 13538 4102 +a 13538 13539 4102 +a 13539 13540 4102 +a 13540 13541 4102 +a 13541 13542 4102 +a 13542 13543 4102 +a 13543 13544 4102 +a 13544 13545 4102 +a 13545 13546 4102 +a 13546 13547 4102 +a 13547 13548 4102 +a 13548 13549 4102 +a 13549 13550 4102 +a 13550 13551 4102 +a 13551 13552 4102 +a 13552 13553 4102 +a 13553 13554 4102 +a 13554 13555 4102 +a 13555 13556 4102 +a 13556 13557 4102 +a 13557 13558 4102 +a 13558 13559 4102 +a 13559 13560 4102 +a 13560 13561 4102 +a 13561 13562 4102 +a 13562 13563 4102 +a 13563 13564 4102 +a 13564 13565 4102 +a 13565 13566 4102 +a 13566 13567 4102 +a 13567 13568 4102 +a 13568 13569 4102 +a 13569 13570 4102 +a 13570 13571 4102 +a 13571 13572 4102 +a 13572 13573 4102 +a 13573 13574 4102 +a 13574 13575 4102 +a 13575 13576 4102 +a 13576 13577 4102 +a 13577 13578 4102 +a 13578 13579 4102 +a 13579 13580 4102 +a 13580 13581 4102 +a 13581 13582 4102 +a 13582 13583 4102 +a 13583 13584 4102 +a 13584 13585 4102 +a 13585 13586 4102 +a 13586 13587 4102 +a 13587 13588 4102 +a 13588 13589 4102 +a 13589 13590 4102 +a 13590 13591 4102 +a 13591 13592 4102 +a 13592 13593 4102 +a 13593 13594 4102 +a 13594 13595 4102 +a 13595 13596 4102 +a 13596 13597 4102 +a 13597 13598 4102 +a 13598 13599 4102 +a 13599 13600 4102 +a 13600 13601 4102 +a 13601 13602 4102 +a 13602 13603 4102 +a 13603 13604 4102 +a 13604 13605 4102 +a 13605 13606 4102 +a 13606 13607 4102 +a 13607 13608 4102 +a 13608 13609 4102 +a 13609 13610 4102 +a 13610 13611 4102 +a 13611 13612 4102 +a 13612 13613 4102 +a 13613 13614 4102 +a 13614 13615 4102 +a 13615 13616 4102 +a 13616 13617 4102 +a 13617 13618 4102 +a 13618 13619 4102 +a 13619 13620 4102 +a 13620 13621 4102 +a 13621 13622 4102 +a 13622 13623 4102 +a 13623 13624 4102 +a 13624 13625 4102 +a 13625 13626 4102 +a 13626 13627 4102 +a 13627 13628 4102 +a 13628 13629 4102 +a 13629 13630 4102 +a 13630 13631 4102 +a 13631 13632 4102 +a 13632 13633 4102 +a 13633 13634 4102 +a 13634 13635 4102 +a 13635 13636 4102 +a 13636 13637 4102 +a 13637 13638 4102 +a 13638 13639 4102 +a 13639 13640 4102 +a 13640 13641 4102 +a 13641 13642 4102 +a 13642 13643 4102 +a 13643 13644 4102 +a 13644 13645 4102 +a 13645 13646 4102 +a 13646 13647 4102 +a 13647 13648 4102 +a 13648 13649 4102 +a 13649 13650 4102 +a 13650 13651 4102 +a 13651 13652 4102 +a 13652 13653 4102 +a 13653 13654 4102 +a 13654 13655 4102 +a 13655 13656 4102 +a 13656 13657 4102 +a 13657 13658 4102 +a 13658 13659 4102 +a 13659 13660 4102 +a 13660 13661 4102 +a 13661 13662 4102 +a 13662 13663 4102 +a 13663 13664 4102 +a 13664 13665 4102 +a 13665 13666 4102 +a 13666 13667 4102 +a 13667 13668 4102 +a 13668 13669 4102 +a 13669 13670 4102 +a 13670 13671 4102 +a 13671 13672 4102 +a 13672 13673 4102 +a 13673 13674 4102 +a 13674 13675 4102 +a 13675 13676 4102 +a 13676 13677 4102 +a 13677 13678 4102 +a 13678 13679 4102 +a 13679 13680 4102 +a 13680 13681 4102 +a 13681 13682 4102 +a 13682 13683 4102 +a 13683 13684 4102 +a 13684 13685 4102 +a 13685 13686 4102 +a 13686 13687 4102 +a 13687 13688 4102 +a 13688 13689 4102 +a 13689 13690 4102 +a 13690 13691 4102 +a 13691 13692 4102 +a 13692 13693 4102 +a 13693 13694 4102 +a 13694 13695 4102 +a 13695 13696 4102 +a 13696 13697 4102 +a 13697 13698 4102 +a 13698 13699 4102 +a 13699 13700 4102 +a 13700 13701 4102 +a 13701 13702 4102 +a 13702 13703 4102 +a 13703 13704 4102 +a 13704 13705 4102 +a 13705 13706 4102 +a 13706 13707 4102 +a 13707 13708 4102 +a 13708 13709 4102 +a 13709 13710 4102 +a 13710 13711 4102 +a 13711 13712 4102 +a 13712 13713 4102 +a 13713 13714 4102 +a 13714 13715 4102 +a 13715 13716 4102 +a 13716 13717 4102 +a 13717 13718 4102 +a 13718 13719 4102 +a 13719 13720 4102 +a 13720 13721 4102 +a 13721 13722 4102 +a 13722 13723 4102 +a 13723 13724 4102 +a 13724 13725 4102 +a 13725 13726 4102 +a 13726 13727 4102 +a 13727 13728 4102 +a 13728 13729 4102 +a 13729 13730 4102 +a 13730 13731 4102 +a 13731 13732 4102 +a 13732 13733 4102 +a 13733 13734 4102 +a 13734 13735 4102 +a 13735 13736 4102 +a 13736 13737 4102 +a 13737 13738 4102 +a 13738 13739 4102 +a 13739 13740 4102 +a 13740 13741 4102 +a 13741 13742 4102 +a 13742 13743 4102 +a 13743 13744 4102 +a 13744 13745 4102 +a 13745 13746 4102 +a 13746 13747 4102 +a 13747 13748 4102 +a 13748 13749 4102 +a 13749 13750 4102 +a 13750 13751 4102 +a 13751 13752 4102 +a 13752 13753 4102 +a 13753 13754 4102 +a 13754 13755 4102 +a 13755 13756 4102 +a 13756 13757 4102 +a 13757 13758 4102 +a 13758 13759 4102 +a 13759 13760 4102 +a 13760 13761 4102 +a 13761 13762 4102 +a 13762 13763 4102 +a 13763 13764 4102 +a 13764 13765 4102 +a 13765 13766 4102 +a 13766 13767 4102 +a 13767 13768 4102 +a 13768 13769 4102 +a 13769 13770 4102 +a 13770 13771 4102 +a 13771 13772 4102 +a 13772 13773 4102 +a 13773 13774 4102 +a 13774 13775 4102 +a 13775 13776 4102 +a 13776 13777 4102 +a 13777 13778 4102 +a 13778 13779 4102 +a 13779 13780 4102 +a 13780 13781 4102 +a 13781 13782 4102 +a 13782 13783 4102 +a 13783 13784 4102 +a 13784 13785 4102 +a 13785 13786 4102 +a 13786 13787 4102 +a 13787 13788 4102 +a 13788 13789 4102 +a 13789 13790 4102 +a 13790 13791 4102 +a 13791 13792 4102 +a 13792 13793 4102 +a 13793 13794 4102 +a 13794 13795 4102 +a 13795 13796 4102 +a 13796 13797 4102 +a 13797 13798 4102 +a 13798 13799 4102 +a 13799 13800 4102 +a 13800 13801 4102 +a 13801 13802 4102 +a 13802 13803 4102 +a 13803 13804 4102 +a 13804 13805 4102 +a 13805 13806 4102 +a 13806 13807 4102 +a 13807 13808 4102 +a 13808 13809 4102 +a 13809 13810 4102 +a 13810 13811 4102 +a 13811 13812 4102 +a 13812 13813 4102 +a 13813 13814 4102 +a 13814 13815 4102 +a 13815 13816 4102 +a 13816 13817 4102 +a 13817 13818 4102 +a 13818 13819 4102 +a 13819 13820 4102 +a 13820 13821 4102 +a 13821 13822 4102 +a 13822 13823 4102 +a 13823 13824 4102 +a 13824 13825 4102 +a 13825 13826 4102 +a 13826 13827 4102 +a 13827 13828 4102 +a 13828 13829 4102 +a 13829 13830 4102 +a 13830 13831 4102 +a 13831 13832 4102 +a 13832 13833 4102 +a 13833 13834 4102 +a 13834 13835 4102 +a 13835 13836 4102 +a 13836 13837 4102 +a 13837 13838 4102 +a 13838 13839 4102 +a 13839 13840 4102 +a 13840 13841 4102 +a 13841 13842 4102 +a 13842 13843 4102 +a 13843 13844 4102 +a 13844 13845 4102 +a 13845 13846 4102 +a 13846 13847 4102 +a 13847 13848 4102 +a 13848 13849 4102 +a 13849 13850 4102 +a 13850 13851 4102 +a 13851 13852 4102 +a 13852 13853 4102 +a 13853 13854 4102 +a 13854 13855 4102 +a 13855 13856 4102 +a 13856 13857 4102 +a 13857 13858 4102 +a 13858 13859 4102 +a 13859 13860 4102 +a 13860 13861 4102 +a 13861 13862 4102 +a 13862 13863 4102 +a 13863 13864 4102 +a 13864 13865 4102 +a 13865 13866 4102 +a 13866 13867 4102 +a 13867 13868 4102 +a 13868 13869 4102 +a 13869 13870 4102 +a 13870 13871 4102 +a 13871 13872 4102 +a 13872 13873 4102 +a 13873 13874 4102 +a 13874 13875 4102 +a 13875 13876 4102 +a 13876 13877 4102 +a 13877 13878 4102 +a 13878 13879 4102 +a 13879 13880 4102 +a 13880 13881 4102 +a 13881 13882 4102 +a 13882 13883 4102 +a 13883 13884 4102 +a 13884 13885 4102 +a 13885 13886 4102 +a 13886 13887 4102 +a 13887 13888 4102 +a 13888 13889 4102 +a 13889 13890 4102 +a 13890 13891 4102 +a 13891 13892 4102 +a 13892 13893 4102 +a 13893 13894 4102 +a 13894 13895 4102 +a 13895 13896 4102 +a 13896 13897 4102 +a 13897 13898 4102 +a 13898 13899 4102 +a 13899 13900 4102 +a 13900 13901 4102 +a 13901 13902 4102 +a 13902 13903 4102 +a 13903 13904 4102 +a 13904 13905 4102 +a 13905 13906 4102 +a 13906 13907 4102 +a 13907 13908 4102 +a 13908 13909 4102 +a 13909 13910 4102 +a 13910 13911 4102 +a 13911 13912 4102 +a 13912 13913 4102 +a 13913 13914 4102 +a 13914 13915 4102 +a 13915 13916 4102 +a 13916 13917 4102 +a 13917 13918 4102 +a 13918 13919 4102 +a 13919 13920 4102 +a 13920 13921 4102 +a 13921 13922 4102 +a 13922 13923 4102 +a 13923 13924 4102 +a 13924 13925 4102 +a 13925 13926 4102 +a 13926 13927 4102 +a 13927 13928 4102 +a 13928 13929 4102 +a 13929 13930 4102 +a 13930 13931 4102 +a 13931 13932 4102 +a 13932 13933 4102 +a 13933 13934 4102 +a 13934 13935 4102 +a 13935 13936 4102 +a 13936 13937 4102 +a 13937 13938 4102 +a 13938 13939 4102 +a 13939 13940 4102 +a 13940 13941 4102 +a 13941 13942 4102 +a 13942 13943 4102 +a 13943 13944 4102 +a 13944 13945 4102 +a 13945 13946 4102 +a 13946 13947 4102 +a 13947 13948 4102 +a 13948 13949 4102 +a 13949 13950 4102 +a 13950 13951 4102 +a 13951 13952 4102 +a 13952 13953 4102 +a 13953 13954 4102 +a 13954 13955 4102 +a 13955 13956 4102 +a 13956 13957 4102 +a 13957 13958 4102 +a 13958 13959 4102 +a 13959 13960 4102 +a 13960 13961 4102 +a 13961 13962 4102 +a 13962 13963 4102 +a 13963 13964 4102 +a 13964 13965 4102 +a 13965 13966 4102 +a 13966 13967 4102 +a 13967 13968 4102 +a 13968 13969 4102 +a 13969 13970 4102 +a 13970 13971 4102 +a 13971 13972 4102 +a 13972 13973 4102 +a 13973 13974 4102 +a 13974 13975 4102 +a 13975 13976 4102 +a 13976 13977 4102 +a 13977 13978 4102 +a 13978 13979 4102 +a 13979 13980 4102 +a 13980 13981 4102 +a 13981 13982 4102 +a 13982 13983 4102 +a 13983 13984 4102 +a 13984 13985 4102 +a 13985 13986 4102 +a 13986 13987 4102 +a 13987 13988 4102 +a 13988 13989 4102 +a 13989 13990 4102 +a 13990 13991 4102 +a 13991 13992 4102 +a 13992 13993 4102 +a 13993 13994 4102 +a 13994 13995 4102 +a 13995 13996 4102 +a 13996 13997 4102 +a 13997 13998 4102 +a 13998 13999 4102 +a 13999 14000 4102 +a 14000 14001 4102 +a 14001 14002 4102 +a 14002 14003 4102 +a 14003 14004 4102 +a 14004 14005 4102 +a 14005 14006 4102 +a 14006 14007 4102 +a 14007 14008 4102 +a 14008 14009 4102 +a 14009 14010 4102 +a 14010 14011 4102 +a 14011 14012 4102 +a 14012 14013 4102 +a 14013 14014 4102 +a 14014 14015 4102 +a 14015 14016 4102 +a 14016 14017 4102 +a 14017 14018 4102 +a 14018 14019 4102 +a 14019 14020 4102 +a 14020 14021 4102 +a 14021 14022 4102 +a 14022 14023 4102 +a 14023 14024 4102 +a 14024 14025 4102 +a 14025 14026 4102 +a 14026 14027 4102 +a 14027 14028 4102 +a 14028 14029 4102 +a 14029 14030 4102 +a 14030 14031 4102 +a 14031 14032 4102 +a 14032 14033 4102 +a 14033 14034 4102 +a 14034 14035 4102 +a 14035 14036 4102 +a 14036 14037 4102 +a 14037 14038 4102 +a 14038 14039 4102 +a 14039 14040 4102 +a 14040 14041 4102 +a 14041 14042 4102 +a 14042 14043 4102 +a 14043 14044 4102 +a 14044 14045 4102 +a 14045 14046 4102 +a 14046 14047 4102 +a 14047 14048 4102 +a 14048 14049 4102 +a 14049 14050 4102 +a 14050 14051 4102 +a 14051 14052 4102 +a 14052 14053 4102 +a 14053 14054 4102 +a 14054 14055 4102 +a 14055 14056 4102 +a 14056 14057 4102 +a 14057 14058 4102 +a 14058 14059 4102 +a 14059 14060 4102 +a 14060 14061 4102 +a 14061 14062 4102 +a 14062 14063 4102 +a 14063 14064 4102 +a 14064 14065 4102 +a 14065 14066 4102 +a 14066 14067 4102 +a 14067 14068 4102 +a 14068 14069 4102 +a 14069 14070 4102 +a 14070 14071 4102 +a 14071 14072 4102 +a 14072 14073 4102 +a 14073 14074 4102 +a 14074 14075 4102 +a 14075 14076 4102 +a 14076 14077 4102 +a 14077 14078 4102 +a 14078 14079 4102 +a 14079 14080 4102 +a 14080 14081 4102 +a 14081 14082 4102 +a 14082 14083 4102 +a 14083 14084 4102 +a 14084 14085 4102 +a 14085 14086 4102 +a 14086 14087 4102 +a 14087 14088 4102 +a 14088 14089 4102 +a 14089 14090 4102 +a 14090 14091 4102 +a 14091 14092 4102 +a 14092 14093 4102 +a 14093 14094 4102 +a 14094 14095 4102 +a 14095 14096 4102 +a 14096 14097 4102 +a 14097 14098 4102 +a 14098 14099 4102 +a 14099 14100 4102 +a 14100 14101 4102 +a 14101 14102 4102 +a 14102 14103 4102 +a 14103 14104 4102 +a 14104 14105 4102 +a 14105 14106 4102 +a 14106 14107 4102 +a 14107 14108 4102 +a 14108 14109 4102 +a 14109 14110 4102 +a 14110 14111 4102 +a 14111 14112 4102 +a 14112 14113 4102 +a 14113 14114 4102 +a 14114 14115 4102 +a 14115 14116 4102 +a 14116 14117 4102 +a 14117 14118 4102 +a 14118 14119 4102 +a 14119 14120 4102 +a 14120 14121 4102 +a 14121 14122 4102 +a 14122 14123 4102 +a 14123 14124 4102 +a 14124 14125 4102 +a 14125 14126 4102 +a 14126 14127 4102 +a 14127 14128 4102 +a 14128 14129 4102 +a 14129 14130 4102 +a 14130 14131 4102 +a 14131 14132 4102 +a 14132 14133 4102 +a 14133 14134 4102 +a 14134 14135 4102 +a 14135 14136 4102 +a 14136 14137 4102 +a 14137 14138 4102 +a 14138 14139 4102 +a 14139 14140 4102 +a 14140 14141 4102 +a 14141 14142 4102 +a 14142 14143 4102 +a 14143 14144 4102 +a 14144 14145 4102 +a 14145 14146 4102 +a 14146 14147 4102 +a 14147 14148 4102 +a 14148 14149 4102 +a 14149 14150 4102 +a 14150 14151 4102 +a 14151 14152 4102 +a 14152 14153 4102 +a 14153 14154 4102 +a 14154 14155 4102 +a 14155 14156 4102 +a 14156 14157 4102 +a 14157 14158 4102 +a 14158 14159 4102 +a 14159 14160 4102 +a 14160 14161 4102 +a 14161 14162 4102 +a 14162 14163 4102 +a 14163 14164 4102 +a 14164 14165 4102 +a 14165 14166 4102 +a 14166 14167 4102 +a 14167 14168 4102 +a 14168 14169 4102 +a 14169 14170 4102 +a 14170 14171 4102 +a 14171 14172 4102 +a 14172 14173 4102 +a 14173 14174 4102 +a 14174 14175 4102 +a 14175 14176 4102 +a 14176 14177 4102 +a 14177 14178 4102 +a 14178 14179 4102 +a 14179 14180 4102 +a 14180 14181 4102 +a 14181 14182 4102 +a 14182 14183 4102 +a 14183 14184 4102 +a 14184 14185 4102 +a 14185 14186 4102 +a 14186 14187 4102 +a 14187 14188 4102 +a 14188 14189 4102 +a 14189 14190 4102 +a 14190 14191 4102 +a 14191 14192 4102 +a 14192 14193 4102 +a 14193 14194 4102 +a 14194 14195 4102 +a 14195 14196 4102 +a 14196 14197 4102 +a 14197 14198 4102 +a 14198 14199 4102 +a 14199 14200 4102 +a 14200 14201 4102 +a 14201 14202 4102 +a 14202 14203 4102 +a 14203 14204 4102 +a 14204 14205 4102 +a 14205 14206 4102 +a 14206 14207 4102 +a 14207 14208 4102 +a 14208 14209 4102 +a 14209 14210 4102 +a 14210 14211 4102 +a 14211 14212 4102 +a 14212 14213 4102 +a 14213 14214 4102 +a 14214 14215 4102 +a 14215 14216 4102 +a 14216 14217 4102 +a 14217 14218 4102 +a 14218 14219 4102 +a 14219 14220 4102 +a 14220 14221 4102 +a 14221 14222 4102 +a 14222 14223 4102 +a 14223 14224 4102 +a 14224 14225 4102 +a 14225 14226 4102 +a 14226 14227 4102 +a 14227 14228 4102 +a 14228 14229 4102 +a 14229 14230 4102 +a 14230 14231 4102 +a 14231 14232 4102 +a 14232 14233 4102 +a 14233 14234 4102 +a 14234 14235 4102 +a 14235 14236 4102 +a 14236 14237 4102 +a 14237 14238 4102 +a 14238 14239 4102 +a 14239 14240 4102 +a 14240 14241 4102 +a 14241 14242 4102 +a 14242 14243 4102 +a 14243 14244 4102 +a 14244 14245 4102 +a 14245 14246 4102 +a 14246 14247 4102 +a 14247 14248 4102 +a 14248 14249 4102 +a 14249 14250 4102 +a 14250 14251 4102 +a 14251 14252 4102 +a 14252 14253 4102 +a 14253 14254 4102 +a 14254 14255 4102 +a 14255 14256 4102 +a 14256 14257 4102 +a 14257 14258 4102 +a 14258 14259 4102 +a 14259 14260 4102 +a 14260 14261 4102 +a 14261 14262 4102 +a 14262 14263 4102 +a 14263 14264 4102 +a 14264 14265 4102 +a 14265 14266 4102 +a 14266 14267 4102 +a 14267 14268 4102 +a 14268 14269 4102 +a 14269 14270 4102 +a 14270 14271 4102 +a 14271 14272 4102 +a 14272 14273 4102 +a 14273 14274 4102 +a 14274 14275 4102 +a 14275 14276 4102 +a 14276 14277 4102 +a 14277 14278 4102 +a 14278 14279 4102 +a 14279 14280 4102 +a 14280 14281 4102 +a 14281 14282 4102 +a 14282 14283 4102 +a 14283 14284 4102 +a 14284 14285 4102 +a 14285 14286 4102 +a 14286 14287 4102 +a 14287 14288 4102 +a 14288 14289 4102 +a 14289 14290 4102 +a 14290 14291 4102 +a 14291 14292 4102 +a 14292 14293 4102 +a 14293 14294 4102 +a 14294 14295 4102 +a 14295 14296 4102 +a 14296 14297 4102 +a 14297 14298 4102 +a 14298 14299 4102 +a 14299 14300 4102 +a 14300 14301 4102 +a 14301 14302 4102 +a 14302 14303 4102 +a 14303 14304 4102 +a 14304 14305 4102 +a 14305 14306 4102 +a 14306 14307 4102 +a 14307 14308 4102 +a 14308 14309 4102 +a 14309 14310 4102 +a 14310 14311 4102 +a 14311 14312 4102 +a 14312 14313 4102 +a 14313 14314 4102 +a 14314 14315 4102 +a 14315 14316 4102 +a 14316 14317 4102 +a 14317 14318 4102 +a 14318 14319 4102 +a 14319 14320 4102 +a 14320 14321 4102 +a 14321 14322 4102 +a 14322 14323 4102 +a 14323 14324 4102 +a 14324 14325 4102 +a 14325 14326 4102 +a 14326 14327 4102 +a 14327 14328 4102 +a 14328 14329 4102 +a 14329 14330 4102 +a 14330 14331 4102 +a 14331 14332 4102 +a 14332 14333 4102 +a 14333 14334 4102 +a 14334 14335 4102 +a 14335 14336 4102 +a 14336 14337 4102 +a 14337 14338 4102 +a 14338 14339 4102 +a 14339 14340 4102 +a 14340 14341 4102 +a 14341 14342 4102 +a 14342 14343 4102 +a 14343 14344 4102 +a 14344 14345 4102 +a 14345 14346 4102 +a 14346 14347 4102 +a 14347 14348 4102 +a 14348 14349 4102 +a 14349 14350 4102 +a 14350 14351 4102 +a 14351 14352 4102 +a 14352 14353 4102 +a 14353 14354 4102 +a 14354 14355 4102 +a 14355 14356 4102 +a 14356 14357 4102 +a 14357 14358 4102 +a 14358 14359 4102 +a 14359 14360 4102 +a 14360 14361 4102 +a 14361 14362 4102 +a 14362 14363 4102 +a 14363 14364 4102 +a 14364 14365 4102 +a 14365 14366 4102 +a 14366 14367 4102 +a 14367 14368 4102 +a 14368 14369 4102 +a 14369 14370 4102 +a 14370 14371 4102 +a 14371 14372 4102 +a 14372 14373 4102 +a 14373 14374 4102 +a 14374 14375 4102 +a 14375 14376 4102 +a 14376 14377 4102 +a 14377 14378 4102 +a 14378 14379 4102 +a 14379 14380 4102 +a 14380 14381 4102 +a 14381 14382 4102 +a 14382 14383 4102 +a 14383 14384 4102 +a 14384 14385 4102 +a 14385 14386 4102 +a 14386 14387 4102 +a 14387 14388 4102 +a 14388 14389 4102 +a 14389 14390 4102 +a 14390 14391 4102 +a 14391 14392 4102 +a 14392 14393 4102 +a 14393 14394 4102 +a 14394 14395 4102 +a 14395 14396 4102 +a 14396 14397 4102 +a 14397 14398 4102 +a 14398 14399 4102 +a 14399 14400 4102 +a 14400 14401 4102 +a 14401 14402 4102 +a 14402 14403 4102 +a 14403 14404 4102 +a 14404 14405 4102 +a 14405 14406 4102 +a 14406 14407 4102 +a 14407 14408 4102 +a 14408 14409 4102 +a 14409 14410 4102 +a 14410 14411 4102 +a 14411 14412 4102 +a 14412 14413 4102 +a 14413 14414 4102 +a 14414 14415 4102 +a 14415 14416 4102 +a 14416 14417 4102 +a 14417 14418 4102 +a 14418 14419 4102 +a 14419 14420 4102 +a 14420 14421 4102 +a 14421 14422 4102 +a 14422 14423 4102 +a 14423 14424 4102 +a 14424 14425 4102 +a 14425 14426 4102 +a 14426 14427 4102 +a 14427 14428 4102 +a 14428 14429 4102 +a 14429 14430 4102 +a 14430 14431 4102 +a 14431 14432 4102 +a 14432 14433 4102 +a 14433 14434 4102 +a 14434 14435 4102 +a 14435 14436 4102 +a 14436 14437 4102 +a 14437 14438 4102 +a 14438 14439 4102 +a 14439 14440 4102 +a 14440 14441 4102 +a 14441 14442 4102 +a 14442 14443 4102 +a 14443 14444 4102 +a 14444 14445 4102 +a 14445 14446 4102 +a 14446 14447 4102 +a 14447 14448 4102 +a 14448 14449 4102 +a 14449 14450 4102 +a 14450 14451 4102 +a 14451 14452 4102 +a 14452 14453 4102 +a 14453 14454 4102 +a 14454 14455 4102 +a 14455 14456 4102 +a 14456 14457 4102 +a 14457 14458 4102 +a 14458 14459 4102 +a 14459 14460 4102 +a 14460 14461 4102 +a 14461 14462 4102 +a 14462 14463 4102 +a 14463 14464 4102 +a 14464 14465 4102 +a 14465 14466 4102 +a 14466 14467 4102 +a 14467 14468 4102 +a 14468 14469 4102 +a 14469 14470 4102 +a 14470 14471 4102 +a 14471 14472 4102 +a 14472 14473 4102 +a 14473 14474 4102 +a 14474 14475 4102 +a 14475 14476 4102 +a 14476 14477 4102 +a 14477 14478 4102 +a 14478 14479 4102 +a 14479 14480 4102 +a 14480 14481 4102 +a 14481 14482 4102 +a 14482 14483 4102 +a 14483 14484 4102 +a 14484 14485 4102 +a 14485 14486 4102 +a 14486 14487 4102 +a 14487 14488 4102 +a 14488 14489 4102 +a 14489 14490 4102 +a 14490 14491 4102 +a 14491 14492 4102 +a 14492 14493 4102 +a 14493 14494 4102 +a 14494 14495 4102 +a 14495 14496 4102 +a 14496 14497 4102 +a 14497 14498 4102 +a 14498 14499 4102 +a 14499 14500 4102 +a 14500 14501 4102 +a 14501 14502 4102 +a 14502 14503 4102 +a 14503 14504 4102 +a 14504 14505 4102 +a 14505 14506 4102 +a 14506 14507 4102 +a 14507 14508 4102 +a 14508 14509 4102 +a 14509 14510 4102 +a 14510 14511 4102 +a 14511 14512 4102 +a 14512 14513 4102 +a 14513 14514 4102 +a 14514 14515 4102 +a 14515 14516 4102 +a 14516 14517 4102 +a 14517 14518 4102 +a 14518 14519 4102 +a 14519 14520 4102 +a 14520 14521 4102 +a 14521 14522 4102 +a 14522 14523 4102 +a 14523 14524 4102 +a 14524 14525 4102 +a 14525 14526 4102 +a 14526 14527 4102 +a 14527 14528 4102 +a 14528 14529 4102 +a 14529 14530 4102 +a 14530 14531 4102 +a 14531 14532 4102 +a 14532 14533 4102 +a 14533 14534 4102 +a 14534 14535 4102 +a 14535 14536 4102 +a 14536 14537 4102 +a 14537 14538 4102 +a 14538 14539 4102 +a 14539 14540 4102 +a 14540 14541 4102 +a 14541 14542 4102 +a 14542 14543 4102 +a 14543 14544 4102 +a 14544 14545 4102 +a 14545 14546 4102 +a 14546 14547 4102 +a 14547 14548 4102 +a 14548 14549 4102 +a 14549 14550 4102 +a 14550 14551 4102 +a 14551 14552 4102 +a 14552 14553 4102 +a 14553 14554 4102 +a 14554 14555 4102 +a 14555 14556 4102 +a 14556 14557 4102 +a 14557 14558 4102 +a 14558 14559 4102 +a 14559 14560 4102 +a 14560 14561 4102 +a 14561 14562 4102 +a 14562 14563 4102 +a 14563 14564 4102 +a 14564 14565 4102 +a 14565 14566 4102 +a 14566 14567 4102 +a 14567 14568 4102 +a 14568 14569 4102 +a 14569 14570 4102 +a 14570 14571 4102 +a 14571 14572 4102 +a 14572 14573 4102 +a 14573 14574 4102 +a 14574 14575 4102 +a 14575 14576 4102 +a 14576 14577 4102 +a 14577 14578 4102 +a 14578 14579 4102 +a 14579 14580 4102 +a 14580 14581 4102 +a 14581 14582 4102 +a 14582 14583 4102 +a 14583 14584 4102 +a 14584 14585 4102 +a 14585 14586 4102 +a 14586 14587 4102 +a 14587 14588 4102 +a 14588 14589 4102 +a 14589 14590 4102 +a 14590 14591 4102 +a 14591 14592 4102 +a 14592 14593 4102 +a 14593 14594 4102 +a 14594 14595 4102 +a 14595 14596 4102 +a 14596 14597 4102 +a 14597 14598 4102 +a 14598 14599 4102 +a 14599 14600 4102 +a 14600 14601 4102 +a 14601 14602 4102 +a 14602 14603 4102 +a 14603 14604 4102 +a 14604 14605 4102 +a 14605 14606 4102 +a 14606 14607 4102 +a 14607 14608 4102 +a 14608 14609 4102 +a 14609 14610 4102 +a 14610 14611 4102 +a 14611 14612 4102 +a 14612 14613 4102 +a 14613 14614 4102 +a 14614 14615 4102 +a 14615 14616 4102 +a 14616 14617 4102 +a 14617 14618 4102 +a 14618 14619 4102 +a 14619 14620 4102 +a 14620 14621 4102 +a 14621 14622 4102 +a 14622 14623 4102 +a 14623 14624 4102 +a 14624 14625 4102 +a 14625 14626 4102 +a 14626 14627 4102 +a 14627 14628 4102 +a 14628 14629 4102 +a 14629 14630 4102 +a 14630 14631 4102 +a 14631 14632 4102 +a 14632 14633 4102 +a 14633 14634 4102 +a 14634 14635 4102 +a 14635 14636 4102 +a 14636 14637 4102 +a 14637 14638 4102 +a 14638 14639 4102 +a 14639 14640 4102 +a 14640 14641 4102 +a 14641 14642 4102 +a 14642 14643 4102 +a 14643 14644 4102 +a 14644 14645 4102 +a 14645 14646 4102 +a 14646 14647 4102 +a 14647 14648 4102 +a 14648 14649 4102 +a 14649 14650 4102 +a 14650 14651 4102 +a 14651 14652 4102 +a 14652 14653 4102 +a 14653 14654 4102 +a 14654 14655 4102 +a 14655 14656 4102 +a 14656 14657 4102 +a 14657 14658 4102 +a 14658 14659 4102 +a 14659 14660 4102 +a 14660 14661 4102 +a 14661 14662 4102 +a 14662 14663 4102 +a 14663 14664 4102 +a 14664 14665 4102 +a 14665 14666 4102 +a 14666 14667 4102 +a 14667 14668 4102 +a 14668 14669 4102 +a 14669 14670 4102 +a 14670 14671 4102 +a 14671 14672 4102 +a 14672 14673 4102 +a 14673 14674 4102 +a 14674 14675 4102 +a 14675 14676 4102 +a 14676 14677 4102 +a 14677 14678 4102 +a 14678 14679 4102 +a 14679 14680 4102 +a 14680 14681 4102 +a 14681 14682 4102 +a 14682 14683 4102 +a 14683 14684 4102 +a 14684 14685 4102 +a 14685 14686 4102 +a 14686 14687 4102 +a 14687 14688 4102 +a 14688 14689 4102 +a 14689 14690 4102 +a 14690 14691 4102 +a 14691 14692 4102 +a 14692 14693 4102 +a 14693 14694 4102 +a 14694 14695 4102 +a 14695 14696 4102 +a 14696 14697 4102 +a 14697 14698 4102 +a 14698 14699 4102 +a 14699 14700 4102 +a 14700 14701 4102 +a 14701 14702 4102 +a 14702 14703 4102 +a 14703 14704 4102 +a 14704 14705 4102 +a 14705 14706 4102 +a 14706 14707 4102 +a 14707 14708 4102 +a 14708 14709 4102 +a 14709 14710 4102 +a 14710 14711 4102 +a 14711 14712 4102 +a 14712 14713 4102 +a 14713 14714 4102 +a 14714 14715 4102 +a 14715 14716 4102 +a 14716 14717 4102 +a 14717 14718 4102 +a 14718 14719 4102 +a 14719 14720 4102 +a 14720 14721 4102 +a 14721 14722 4102 +a 14722 14723 4102 +a 14723 14724 4102 +a 14724 14725 4102 +a 14725 14726 4102 +a 14726 14727 4102 +a 14727 14728 4102 +a 14728 14729 4102 +a 14729 14730 4102 +a 14730 14731 4102 +a 14731 14732 4102 +a 14732 14733 4102 +a 14733 14734 4102 +a 14734 14735 4102 +a 14735 14736 4102 +a 14736 14737 4102 +a 14737 14738 4102 +a 14738 14739 4102 +a 14739 14740 4102 +a 14740 14741 4102 +a 14741 14742 4102 +a 14742 14743 4102 +a 14743 14744 4102 +a 14744 14745 4102 +a 14745 14746 4102 +a 14746 14747 4102 +a 14747 14748 4102 +a 14748 14749 4102 +a 14749 14750 4102 +a 14750 14751 4102 +a 14751 14752 4102 +a 14752 14753 4102 +a 14753 14754 4102 +a 14754 14755 4102 +a 14755 14756 4102 +a 14756 14757 4102 +a 14757 14758 4102 +a 14758 14759 4102 +a 14759 14760 4102 +a 14760 14761 4102 +a 14761 14762 4102 +a 14762 14763 4102 +a 14763 14764 4102 +a 14764 14765 4102 +a 14765 14766 4102 +a 14766 14767 4102 +a 14767 14768 4102 +a 14768 14769 4102 +a 14769 14770 4102 +a 14770 14771 4102 +a 14771 14772 4102 +a 14772 14773 4102 +a 14773 14774 4102 +a 14774 14775 4102 +a 14775 14776 4102 +a 14776 14777 4102 +a 14777 14778 4102 +a 14778 14779 4102 +a 14779 14780 4102 +a 14780 14781 4102 +a 14781 14782 4102 +a 14782 14783 4102 +a 14783 14784 4102 +a 14784 14785 4102 +a 14785 14786 4102 +a 14786 14787 4102 +a 14787 14788 4102 +a 14788 14789 4102 +a 14789 14790 4102 +a 14790 14791 4102 +a 14791 14792 4102 +a 14792 14793 4102 +a 14793 14794 4102 +a 14794 14795 4102 +a 14795 14796 4102 +a 14796 14797 4102 +a 14797 14798 4102 +a 14798 14799 4102 +a 14799 14800 4102 +a 14800 14801 4102 +a 14801 14802 4102 +a 14802 14803 4102 +a 14803 14804 4102 +a 14804 14805 4102 +a 14805 14806 4102 +a 14806 14807 4102 +a 14807 14808 4102 +a 14808 14809 4102 +a 14809 14810 4102 +a 14810 14811 4102 +a 14811 14812 4102 +a 14812 14813 4102 +a 14813 14814 4102 +a 14814 14815 4102 +a 14815 14816 4102 +a 14816 14817 4102 +a 14817 14818 4102 +a 14818 14819 4102 +a 14819 14820 4102 +a 14820 14821 4102 +a 14821 14822 4102 +a 14822 14823 4102 +a 14823 14824 4102 +a 14824 14825 4102 +a 14825 14826 4102 +a 14826 14827 4102 +a 14827 14828 4102 +a 14828 14829 4102 +a 14829 14830 4102 +a 14830 14831 4102 +a 14831 14832 4102 +a 14832 14833 4102 +a 14833 14834 4102 +a 14834 14835 4102 +a 14835 14836 4102 +a 14836 14837 4102 +a 14837 14838 4102 +a 14838 14839 4102 +a 14839 14840 4102 +a 14840 14841 4102 +a 14841 14842 4102 +a 14842 14843 4102 +a 14843 14844 4102 +a 14844 14845 4102 +a 14845 14846 4102 +a 14846 14847 4102 +a 14847 14848 4102 +a 14848 14849 4102 +a 14849 14850 4102 +a 14850 14851 4102 +a 14851 14852 4102 +a 14852 14853 4102 +a 14853 14854 4102 +a 14854 14855 4102 +a 14855 14856 4102 +a 14856 14857 4102 +a 14857 14858 4102 +a 14858 14859 4102 +a 14859 14860 4102 +a 14860 14861 4102 +a 14861 14862 4102 +a 14862 14863 4102 +a 14863 14864 4102 +a 14864 14865 4102 +a 14865 14866 4102 +a 14866 14867 4102 +a 14867 14868 4102 +a 14868 14869 4102 +a 14869 14870 4102 +a 14870 14871 4102 +a 14871 14872 4102 +a 14872 14873 4102 +a 14873 14874 4102 +a 14874 14875 4102 +a 14875 14876 4102 +a 14876 14877 4102 +a 14877 14878 4102 +a 14878 14879 4102 +a 14879 14880 4102 +a 14880 14881 4102 +a 14881 14882 4102 +a 14882 14883 4102 +a 14883 14884 4102 +a 14884 14885 4102 +a 14885 14886 4102 +a 14886 14887 4102 +a 14887 14888 4102 +a 14888 14889 4102 +a 14889 14890 4102 +a 14890 14891 4102 +a 14891 14892 4102 +a 14892 14893 4102 +a 14893 14894 4102 +a 14894 14895 4102 +a 14895 14896 4102 +a 14896 14897 4102 +a 14897 14898 4102 +a 14898 14899 4102 +a 14899 14900 4102 +a 14900 14901 4102 +a 14901 14902 4102 +a 14902 14903 4102 +a 14903 14904 4102 +a 14904 14905 4102 +a 14905 14906 4102 +a 14906 14907 4102 +a 14907 14908 4102 +a 14908 14909 4102 +a 14909 14910 4102 +a 14910 14911 4102 +a 14911 14912 4102 +a 14912 14913 4102 +a 14913 14914 4102 +a 14914 14915 4102 +a 14915 14916 4102 +a 14916 14917 4102 +a 14917 14918 4102 +a 14918 14919 4102 +a 14919 14920 4102 +a 14920 14921 4102 +a 14921 14922 4102 +a 14922 14923 4102 +a 14923 14924 4102 +a 14924 14925 4102 +a 14925 14926 4102 +a 14926 14927 4102 +a 14927 14928 4102 +a 14928 14929 4102 +a 14929 14930 4102 +a 14930 14931 4102 +a 14931 14932 4102 +a 14932 14933 4102 +a 14933 14934 4102 +a 14934 14935 4102 +a 14935 14936 4102 +a 14936 14937 4102 +a 14937 14938 4102 +a 14938 14939 4102 +a 14939 14940 4102 +a 14940 14941 4102 +a 14941 14942 4102 +a 14942 14943 4102 +a 14943 14944 4102 +a 14944 14945 4102 +a 14945 14946 4102 +a 14946 14947 4102 +a 14947 14948 4102 +a 14948 14949 4102 +a 14949 14950 4102 +a 14950 14951 4102 +a 14951 14952 4102 +a 14952 14953 4102 +a 14953 14954 4102 +a 14954 14955 4102 +a 14955 14956 4102 +a 14956 14957 4102 +a 14957 14958 4102 +a 14958 14959 4102 +a 14959 14960 4102 +a 14960 14961 4102 +a 14961 14962 4102 +a 14962 14963 4102 +a 14963 14964 4102 +a 14964 14965 4102 +a 14965 14966 4102 +a 14966 14967 4102 +a 14967 14968 4102 +a 14968 14969 4102 +a 14969 14970 4102 +a 14970 14971 4102 +a 14971 14972 4102 +a 14972 14973 4102 +a 14973 14974 4102 +a 14974 14975 4102 +a 14975 14976 4102 +a 14976 14977 4102 +a 14977 14978 4102 +a 14978 14979 4102 +a 14979 14980 4102 +a 14980 14981 4102 +a 14981 14982 4102 +a 14982 14983 4102 +a 14983 14984 4102 +a 14984 14985 4102 +a 14985 14986 4102 +a 14986 14987 4102 +a 14987 14988 4102 +a 14988 14989 4102 +a 14989 14990 4102 +a 14990 14991 4102 +a 14991 14992 4102 +a 14992 14993 4102 +a 14993 14994 4102 +a 14994 14995 4102 +a 14995 14996 4102 +a 14996 14997 4102 +a 14997 14998 4102 +a 14998 14999 4102 +a 14999 15000 4102 +a 15000 15001 4102 +a 15001 15002 4102 +a 15002 15003 4102 +a 15003 15004 4102 +a 15004 15005 4102 +a 15005 15006 4102 +a 15006 15007 4102 +a 15007 15008 4102 +a 15008 15009 4102 +a 15009 15010 4102 +a 15010 15011 4102 +a 15011 15012 4102 +a 15012 15013 4102 +a 15013 15014 4102 +a 15014 15015 4102 +a 15015 15016 4102 +a 15016 15017 4102 +a 15017 15018 4102 +a 15018 15019 4102 +a 15019 15020 4102 +a 15020 15021 4102 +a 15021 15022 4102 +a 15022 15023 4102 +a 15023 15024 4102 +a 15024 15025 4102 +a 15025 15026 4102 +a 15026 15027 4102 +a 15027 15028 4102 +a 15028 15029 4102 +a 15029 15030 4102 +a 15030 15031 4102 +a 15031 15032 4102 +a 15032 15033 4102 +a 15033 15034 4102 +a 15034 15035 4102 +a 15035 15036 4102 +a 15036 15037 4102 +a 15037 15038 4102 +a 15038 15039 4102 +a 15039 15040 4102 +a 15040 15041 4102 +a 15041 15042 4102 +a 15042 15043 4102 +a 15043 15044 4102 +a 15044 15045 4102 +a 15045 15046 4102 +a 15046 15047 4102 +a 15047 15048 4102 +a 15048 15049 4102 +a 15049 15050 4102 +a 15050 15051 4102 +a 15051 15052 4102 +a 15052 15053 4102 +a 15053 15054 4102 +a 15054 15055 4102 +a 15055 15056 4102 +a 15056 15057 4102 +a 15057 15058 4102 +a 15058 15059 4102 +a 15059 15060 4102 +a 15060 15061 4102 +a 15061 15062 4102 +a 15062 15063 4102 +a 15063 15064 4102 +a 15064 15065 4102 +a 15065 15066 4102 +a 15066 15067 4102 +a 15067 15068 4102 +a 15068 15069 4102 +a 15069 15070 4102 +a 15070 15071 4102 +a 15071 15072 4102 +a 15072 15073 4102 +a 15073 15074 4102 +a 15074 15075 4102 +a 15075 15076 4102 +a 15076 15077 4102 +a 15077 15078 4102 +a 15078 15079 4102 +a 15079 15080 4102 +a 15080 15081 4102 +a 15081 15082 4102 +a 15082 15083 4102 +a 15083 15084 4102 +a 15084 15085 4102 +a 15085 15086 4102 +a 15086 15087 4102 +a 15087 15088 4102 +a 15088 15089 4102 +a 15089 15090 4102 +a 15090 15091 4102 +a 15091 15092 4102 +a 15092 15093 4102 +a 15093 15094 4102 +a 15094 15095 4102 +a 15095 15096 4102 +a 15096 15097 4102 +a 15097 15098 4102 +a 15098 15099 4102 +a 15099 15100 4102 +a 15100 15101 4102 +a 15101 15102 4102 +a 15102 15103 4102 +a 15103 15104 4102 +a 15104 15105 4102 +a 15105 15106 4102 +a 15106 15107 4102 +a 15107 15108 4102 +a 15108 15109 4102 +a 15109 15110 4102 +a 15110 15111 4102 +a 15111 15112 4102 +a 15112 15113 4102 +a 15113 15114 4102 +a 15114 15115 4102 +a 15115 15116 4102 +a 15116 15117 4102 +a 15117 15118 4102 +a 15118 15119 4102 +a 15119 15120 4102 +a 15120 15121 4102 +a 15121 15122 4102 +a 15122 15123 4102 +a 15123 15124 4102 +a 15124 15125 4102 +a 15125 15126 4102 +a 15126 15127 4102 +a 15127 15128 4102 +a 15128 15129 4102 +a 15129 15130 4102 +a 15130 15131 4102 +a 15131 15132 4102 +a 15132 15133 4102 +a 15133 15134 4102 +a 15134 15135 4102 +a 15135 15136 4102 +a 15136 15137 4102 +a 15137 15138 4102 +a 15138 15139 4102 +a 15139 15140 4102 +a 15140 15141 4102 +a 15141 15142 4102 +a 15142 15143 4102 +a 15143 15144 4102 +a 15144 15145 4102 +a 15145 15146 4102 +a 15146 15147 4102 +a 15147 15148 4102 +a 15148 15149 4102 +a 15149 15150 4102 +a 15150 15151 4102 +a 15151 15152 4102 +a 15152 15153 4102 +a 15153 15154 4102 +a 15154 15155 4102 +a 15155 15156 4102 +a 15156 15157 4102 +a 15157 15158 4102 +a 15158 15159 4102 +a 15159 15160 4102 +a 15160 15161 4102 +a 15161 15162 4102 +a 15162 15163 4102 +a 15163 15164 4102 +a 15164 15165 4102 +a 15165 15166 4102 +a 15166 15167 4102 +a 15167 15168 4102 +a 15168 15169 4102 +a 15169 15170 4102 +a 15170 15171 4102 +a 15171 15172 4102 +a 15172 15173 4102 +a 15173 15174 4102 +a 15174 15175 4102 +a 15175 15176 4102 +a 15176 15177 4102 +a 15177 15178 4102 +a 15178 15179 4102 +a 15179 15180 4102 +a 15180 15181 4102 +a 15181 15182 4102 +a 15182 15183 4102 +a 15183 15184 4102 +a 15184 15185 4102 +a 15185 15186 4102 +a 15186 15187 4102 +a 15187 15188 4102 +a 15188 15189 4102 +a 15189 15190 4102 +a 15190 15191 4102 +a 15191 15192 4102 +a 15192 15193 4102 +a 15193 15194 4102 +a 15194 15195 4102 +a 15195 15196 4102 +a 15196 15197 4102 +a 15197 15198 4102 +a 15198 15199 4102 +a 15199 15200 4102 +a 15200 15201 4102 +a 15201 15202 4102 +a 15202 15203 4102 +a 15203 15204 4102 +a 15204 15205 4102 +a 15205 15206 4102 +a 15206 15207 4102 +a 15207 15208 4102 +a 15208 15209 4102 +a 15209 15210 4102 +a 15210 15211 4102 +a 15211 15212 4102 +a 15212 15213 4102 +a 15213 15214 4102 +a 15214 15215 4102 +a 15215 15216 4102 +a 15216 15217 4102 +a 15217 15218 4102 +a 15218 15219 4102 +a 15219 15220 4102 +a 15220 15221 4102 +a 15221 15222 4102 +a 15222 15223 4102 +a 15223 15224 4102 +a 15224 15225 4102 +a 15225 15226 4102 +a 15226 15227 4102 +a 15227 15228 4102 +a 15228 15229 4102 +a 15229 15230 4102 +a 15230 15231 4102 +a 15231 15232 4102 +a 15232 15233 4102 +a 15233 15234 4102 +a 15234 15235 4102 +a 15235 15236 4102 +a 15236 15237 4102 +a 15237 15238 4102 +a 15238 15239 4102 +a 15239 15240 4102 +a 15240 15241 4102 +a 15241 15242 4102 +a 15242 15243 4102 +a 15243 15244 4102 +a 15244 15245 4102 +a 15245 15246 4102 +a 15246 15247 4102 +a 15247 15248 4102 +a 15248 15249 4102 +a 15249 15250 4102 +a 15250 15251 4102 +a 15251 15252 4102 +a 15252 15253 4102 +a 15253 15254 4102 +a 15254 15255 4102 +a 15255 15256 4102 +a 15256 15257 4102 +a 15257 15258 4102 +a 15258 15259 4102 +a 15259 15260 4102 +a 15260 15261 4102 +a 15261 15262 4102 +a 15262 15263 4102 +a 15263 15264 4102 +a 15264 15265 4102 +a 15265 15266 4102 +a 15266 15267 4102 +a 15267 15268 4102 +a 15268 15269 4102 +a 15269 15270 4102 +a 15270 15271 4102 +a 15271 15272 4102 +a 15272 15273 4102 +a 15273 15274 4102 +a 15274 15275 4102 +a 15275 15276 4102 +a 15276 15277 4102 +a 15277 15278 4102 +a 15278 15279 4102 +a 15279 15280 4102 +a 15280 15281 4102 +a 15281 15282 4102 +a 15282 15283 4102 +a 15283 15284 4102 +a 15284 15285 4102 +a 15285 15286 4102 +a 15286 15287 4102 +a 15287 15288 4102 +a 15288 15289 4102 +a 15289 15290 4102 +a 15290 15291 4102 +a 15291 15292 4102 +a 15292 15293 4102 +a 15293 15294 4102 +a 15294 15295 4102 +a 15295 15296 4102 +a 15296 15297 4102 +a 15297 15298 4102 +a 15298 15299 4102 +a 15299 15300 4102 +a 15300 15301 4102 +a 15301 15302 4102 +a 15302 15303 4102 +a 15303 15304 4102 +a 15304 15305 4102 +a 15305 15306 4102 +a 15306 15307 4102 +a 15307 15308 4102 +a 15308 15309 4102 +a 15309 15310 4102 +a 15310 15311 4102 +a 15311 15312 4102 +a 15312 15313 4102 +a 15313 15314 4102 +a 15314 15315 4102 +a 15315 15316 4102 +a 15316 15317 4102 +a 15317 15318 4102 +a 15318 15319 4102 +a 15319 15320 4102 +a 15320 15321 4102 +a 15321 15322 4102 +a 15322 15323 4102 +a 15323 15324 4102 +a 15324 15325 4102 +a 15325 15326 4102 +a 15326 15327 4102 +a 15327 15328 4102 +a 15328 15329 4102 +a 15329 15330 4102 +a 15330 15331 4102 +a 15331 15332 4102 +a 15332 15333 4102 +a 15333 15334 4102 +a 15334 15335 4102 +a 15335 15336 4102 +a 15336 15337 4102 +a 15337 15338 4102 +a 15338 15339 4102 +a 15339 15340 4102 +a 15340 15341 4102 +a 15341 15342 4102 +a 15342 15343 4102 +a 15343 15344 4102 +a 15344 15345 4102 +a 15345 15346 4102 +a 15346 15347 4102 +a 15347 15348 4102 +a 15348 15349 4102 +a 15349 15350 4102 +a 15350 15351 4102 +a 15351 15352 4102 +a 15352 15353 4102 +a 15353 15354 4102 +a 15354 15355 4102 +a 15355 15356 4102 +a 15356 15357 4102 +a 15357 15358 4102 +a 15358 15359 4102 +a 15359 15360 4102 +a 15360 15361 4102 +a 15361 15362 4102 +a 15362 15363 4102 +a 15363 15364 4102 +a 15364 15365 4102 +a 15365 15366 4102 +a 15366 15367 4102 +a 15367 15368 4102 +a 15368 15369 4102 +a 15369 15370 4102 +a 15370 15371 4102 +a 15371 15372 4102 +a 15372 15373 4102 +a 15373 15374 4102 +a 15374 15375 4102 +a 15375 15376 4102 +a 15376 15377 4102 +a 15377 15378 4102 +a 15378 15379 4102 +a 15379 15380 4102 +a 15380 15381 4102 +a 15381 15382 4102 +a 15382 15383 4102 +a 15383 15384 4102 +a 15384 15385 4102 +a 15385 15386 4102 +a 15386 15387 4102 +a 15387 15388 4102 +a 15388 15389 4102 +a 15389 15390 4102 +a 15390 15391 4102 +a 15391 15392 4102 +a 15392 15393 4102 +a 15393 15394 4102 +a 15394 15395 4102 +a 15395 15396 4102 +a 15396 15397 4102 +a 15397 15398 4102 +a 15398 15399 4102 +a 15399 15400 4102 +a 15400 15401 4102 +a 15401 15402 4102 +a 15402 15403 4102 +a 15403 15404 4102 +a 15404 15405 4102 +a 15405 15406 4102 +a 15406 15407 4102 +a 15407 15408 4102 +a 15408 15409 4102 +a 15409 15410 4102 +a 15410 15411 4102 +a 15411 15412 4102 +a 15412 15413 4102 +a 15413 15414 4102 +a 15414 15415 4102 +a 15415 15416 4102 +a 15416 15417 4102 +a 15417 15418 4102 +a 15418 15419 4102 +a 15419 15420 4102 +a 15420 15421 4102 +a 15421 15422 4102 +a 15422 15423 4102 +a 15423 15424 4102 +a 15424 15425 4102 +a 15425 15426 4102 +a 15426 15427 4102 +a 15427 15428 4102 +a 15428 15429 4102 +a 15429 15430 4102 +a 15430 15431 4102 +a 15431 15432 4102 +a 15432 15433 4102 +a 15433 15434 4102 +a 15434 15435 4102 +a 15435 15436 4102 +a 15436 15437 4102 +a 15437 15438 4102 +a 15438 15439 4102 +a 15439 15440 4102 +a 15440 15441 4102 +a 15441 15442 4102 +a 15442 15443 4102 +a 15443 15444 4102 +a 15444 15445 4102 +a 15445 15446 4102 +a 15446 15447 4102 +a 15447 15448 4102 +a 15448 15449 4102 +a 15449 15450 4102 +a 15450 15451 4102 +a 15451 15452 4102 +a 15452 15453 4102 +a 15453 15454 4102 +a 15454 15455 4102 +a 15455 15456 4102 +a 15456 15457 4102 +a 15457 15458 4102 +a 15458 15459 4102 +a 15459 15460 4102 +a 15460 15461 4102 +a 15461 15462 4102 +a 15462 15463 4102 +a 15463 15464 4102 +a 15464 15465 4102 +a 15465 15466 4102 +a 15466 15467 4102 +a 15467 15468 4102 +a 15468 15469 4102 +a 15469 15470 4102 +a 15470 15471 4102 +a 15471 15472 4102 +a 15472 15473 4102 +a 15473 15474 4102 +a 15474 15475 4102 +a 15475 15476 4102 +a 15476 15477 4102 +a 15477 15478 4102 +a 15478 15479 4102 +a 15479 15480 4102 +a 15480 15481 4102 +a 15481 15482 4102 +a 15482 15483 4102 +a 15483 15484 4102 +a 15484 15485 4102 +a 15485 15486 4102 +a 15486 15487 4102 +a 15487 15488 4102 +a 15488 15489 4102 +a 15489 15490 4102 +a 15490 15491 4102 +a 15491 15492 4102 +a 15492 15493 4102 +a 15493 15494 4102 +a 15494 15495 4102 +a 15495 15496 4102 +a 15496 15497 4102 +a 15497 15498 4102 +a 15498 15499 4102 +a 15499 15500 4102 +a 15500 15501 4102 +a 15501 15502 4102 +a 15502 15503 4102 +a 15503 15504 4102 +a 15504 15505 4102 +a 15505 15506 4102 +a 15506 15507 4102 +a 15507 15508 4102 +a 15508 15509 4102 +a 15509 15510 4102 +a 15510 15511 4102 +a 15511 15512 4102 +a 15512 15513 4102 +a 15513 15514 4102 +a 15514 15515 4102 +a 15515 15516 4102 +a 15516 15517 4102 +a 15517 15518 4102 +a 15518 15519 4102 +a 15519 15520 4102 +a 15520 15521 4102 +a 15521 15522 4102 +a 15522 15523 4102 +a 15523 15524 4102 +a 15524 15525 4102 +a 15525 15526 4102 +a 15526 15527 4102 +a 15527 15528 4102 +a 15528 15529 4102 +a 15529 15530 4102 +a 15530 15531 4102 +a 15531 15532 4102 +a 15532 15533 4102 +a 15533 15534 4102 +a 15534 15535 4102 +a 15535 15536 4102 +a 15536 15537 4102 +a 15537 15538 4102 +a 15538 15539 4102 +a 15539 15540 4102 +a 15540 15541 4102 +a 15541 15542 4102 +a 15542 15543 4102 +a 15543 15544 4102 +a 15544 15545 4102 +a 15545 15546 4102 +a 15546 15547 4102 +a 15547 15548 4102 +a 15548 15549 4102 +a 15549 15550 4102 +a 15550 15551 4102 +a 15551 15552 4102 +a 15552 15553 4102 +a 15553 15554 4102 +a 15554 15555 4102 +a 15555 15556 4102 +a 15556 15557 4102 +a 15557 15558 4102 +a 15558 15559 4102 +a 15559 15560 4102 +a 15560 15561 4102 +a 15561 15562 4102 +a 15562 15563 4102 +a 15563 15564 4102 +a 15564 15565 4102 +a 15565 15566 4102 +a 15566 15567 4102 +a 15567 15568 4102 +a 15568 15569 4102 +a 15569 15570 4102 +a 15570 15571 4102 +a 15571 15572 4102 +a 15572 15573 4102 +a 15573 15574 4102 +a 15574 15575 4102 +a 15575 15576 4102 +a 15576 15577 4102 +a 15577 15578 4102 +a 15578 15579 4102 +a 15579 15580 4102 +a 15580 15581 4102 +a 15581 15582 4102 +a 15582 15583 4102 +a 15583 15584 4102 +a 15584 15585 4102 +a 15585 15586 4102 +a 15586 15587 4102 +a 15587 15588 4102 +a 15588 15589 4102 +a 15589 15590 4102 +a 15590 15591 4102 +a 15591 15592 4102 +a 15592 15593 4102 +a 15593 15594 4102 +a 15594 15595 4102 +a 15595 15596 4102 +a 15596 15597 4102 +a 15597 15598 4102 +a 15598 15599 4102 +a 15599 15600 4102 +a 15600 15601 4102 +a 15601 15602 4102 +a 15602 15603 4102 +a 15603 15604 4102 +a 15604 15605 4102 +a 15605 15606 4102 +a 15606 15607 4102 +a 15607 15608 4102 +a 15608 15609 4102 +a 15609 15610 4102 +a 15610 15611 4102 +a 15611 15612 4102 +a 15612 15613 4102 +a 15613 15614 4102 +a 15614 15615 4102 +a 15615 15616 4102 +a 15616 15617 4102 +a 15617 15618 4102 +a 15618 15619 4102 +a 15619 15620 4102 +a 15620 15621 4102 +a 15621 15622 4102 +a 15622 15623 4102 +a 15623 15624 4102 +a 15624 15625 4102 +a 15625 15626 4102 +a 15626 15627 4102 +a 15627 15628 4102 +a 15628 15629 4102 +a 15629 15630 4102 +a 15630 15631 4102 +a 15631 15632 4102 +a 15632 15633 4102 +a 15633 15634 4102 +a 15634 15635 4102 +a 15635 15636 4102 +a 15636 15637 4102 +a 15637 15638 4102 +a 15638 15639 4102 +a 15639 15640 4102 +a 15640 15641 4102 +a 15641 15642 4102 +a 15642 15643 4102 +a 15643 15644 4102 +a 15644 15645 4102 +a 15645 15646 4102 +a 15646 15647 4102 +a 15647 15648 4102 +a 15648 15649 4102 +a 15649 15650 4102 +a 15650 15651 4102 +a 15651 15652 4102 +a 15652 15653 4102 +a 15653 15654 4102 +a 15654 15655 4102 +a 15655 15656 4102 +a 15656 15657 4102 +a 15657 15658 4102 +a 15658 15659 4102 +a 15659 15660 4102 +a 15660 15661 4102 +a 15661 15662 4102 +a 15662 15663 4102 +a 15663 15664 4102 +a 15664 15665 4102 +a 15665 15666 4102 +a 15666 15667 4102 +a 15667 15668 4102 +a 15668 15669 4102 +a 15669 15670 4102 +a 15670 15671 4102 +a 15671 15672 4102 +a 15672 15673 4102 +a 15673 15674 4102 +a 15674 15675 4102 +a 15675 15676 4102 +a 15676 15677 4102 +a 15677 15678 4102 +a 15678 15679 4102 +a 15679 15680 4102 +a 15680 15681 4102 +a 15681 15682 4102 +a 15682 15683 4102 +a 15683 15684 4102 +a 15684 15685 4102 +a 15685 15686 4102 +a 15686 15687 4102 +a 15687 15688 4102 +a 15688 15689 4102 +a 15689 15690 4102 +a 15690 15691 4102 +a 15691 15692 4102 +a 15692 15693 4102 +a 15693 15694 4102 +a 15694 15695 4102 +a 15695 15696 4102 +a 15696 15697 4102 +a 15697 15698 4102 +a 15698 15699 4102 +a 15699 15700 4102 +a 15700 15701 4102 +a 15701 15702 4102 +a 15702 15703 4102 +a 15703 15704 4102 +a 15704 15705 4102 +a 15705 15706 4102 +a 15706 15707 4102 +a 15707 15708 4102 +a 15708 15709 4102 +a 15709 15710 4102 +a 15710 15711 4102 +a 15711 15712 4102 +a 15712 15713 4102 +a 15713 15714 4102 +a 15714 15715 4102 +a 15715 15716 4102 +a 15716 15717 4102 +a 15717 15718 4102 +a 15718 15719 4102 +a 15719 15720 4102 +a 15720 15721 4102 +a 15721 15722 4102 +a 15722 15723 4102 +a 15723 15724 4102 +a 15724 15725 4102 +a 15725 15726 4102 +a 15726 15727 4102 +a 15727 15728 4102 +a 15728 15729 4102 +a 15729 15730 4102 +a 15730 15731 4102 +a 15731 15732 4102 +a 15732 15733 4102 +a 15733 15734 4102 +a 15734 15735 4102 +a 15735 15736 4102 +a 15736 15737 4102 +a 15737 15738 4102 +a 15738 15739 4102 +a 15739 15740 4102 +a 15740 15741 4102 +a 15741 15742 4102 +a 15742 15743 4102 +a 15743 15744 4102 +a 15744 15745 4102 +a 15745 15746 4102 +a 15746 15747 4102 +a 15747 15748 4102 +a 15748 15749 4102 +a 15749 15750 4102 +a 15750 15751 4102 +a 15751 15752 4102 +a 15752 15753 4102 +a 15753 15754 4102 +a 15754 15755 4102 +a 15755 15756 4102 +a 15756 15757 4102 +a 15757 15758 4102 +a 15758 15759 4102 +a 15759 15760 4102 +a 15760 15761 4102 +a 15761 15762 4102 +a 15762 15763 4102 +a 15763 15764 4102 +a 15764 15765 4102 +a 15765 15766 4102 +a 15766 15767 4102 +a 15767 15768 4102 +a 15768 15769 4102 +a 15769 15770 4102 +a 15770 15771 4102 +a 15771 15772 4102 +a 15772 15773 4102 +a 15773 15774 4102 +a 15774 15775 4102 +a 15775 15776 4102 +a 15776 15777 4102 +a 15777 15778 4102 +a 15778 15779 4102 +a 15779 15780 4102 +a 15780 15781 4102 +a 15781 15782 4102 +a 15782 15783 4102 +a 15783 15784 4102 +a 15784 15785 4102 +a 15785 15786 4102 +a 15786 15787 4102 +a 15787 15788 4102 +a 15788 15789 4102 +a 15789 15790 4102 +a 15790 15791 4102 +a 15791 15792 4102 +a 15792 15793 4102 +a 15793 15794 4102 +a 15794 15795 4102 +a 15795 15796 4102 +a 15796 15797 4102 +a 15797 15798 4102 +a 15798 15799 4102 +a 15799 15800 4102 +a 15800 15801 4102 +a 15801 15802 4102 +a 15802 15803 4102 +a 15803 15804 4102 +a 15804 15805 4102 +a 15805 15806 4102 +a 15806 15807 4102 +a 15807 15808 4102 +a 15808 15809 4102 +a 15809 15810 4102 +a 15810 15811 4102 +a 15811 15812 4102 +a 15812 15813 4102 +a 15813 15814 4102 +a 15814 15815 4102 +a 15815 15816 4102 +a 15816 15817 4102 +a 15817 15818 4102 +a 15818 15819 4102 +a 15819 15820 4102 +a 15820 15821 4102 +a 15821 15822 4102 +a 15822 15823 4102 +a 15823 15824 4102 +a 15824 15825 4102 +a 15825 15826 4102 +a 15826 15827 4102 +a 15827 15828 4102 +a 15828 15829 4102 +a 15829 15830 4102 +a 15830 15831 4102 +a 15831 15832 4102 +a 15832 15833 4102 +a 15833 15834 4102 +a 15834 15835 4102 +a 15835 15836 4102 +a 15836 15837 4102 +a 15837 15838 4102 +a 15838 15839 4102 +a 15839 15840 4102 +a 15840 15841 4102 +a 15841 15842 4102 +a 15842 15843 4102 +a 15843 15844 4102 +a 15844 15845 4102 +a 15845 15846 4102 +a 15846 15847 4102 +a 15847 15848 4102 +a 15848 15849 4102 +a 15849 15850 4102 +a 15850 15851 4102 +a 15851 15852 4102 +a 15852 15853 4102 +a 15853 15854 4102 +a 15854 15855 4102 +a 15855 15856 4102 +a 15856 15857 4102 +a 15857 15858 4102 +a 15858 15859 4102 +a 15859 15860 4102 +a 15860 15861 4102 +a 15861 15862 4102 +a 15862 15863 4102 +a 15863 15864 4102 +a 15864 15865 4102 +a 15865 15866 4102 +a 15866 15867 4102 +a 15867 15868 4102 +a 15868 15869 4102 +a 15869 15870 4102 +a 15870 15871 4102 +a 15871 15872 4102 +a 15872 15873 4102 +a 15873 15874 4102 +a 15874 15875 4102 +a 15875 15876 4102 +a 15876 15877 4102 +a 15877 15878 4102 +a 15878 15879 4102 +a 15879 15880 4102 +a 15880 15881 4102 +a 15881 15882 4102 +a 15882 15883 4102 +a 15883 15884 4102 +a 15884 15885 4102 +a 15885 15886 4102 +a 15886 15887 4102 +a 15887 15888 4102 +a 15888 15889 4102 +a 15889 15890 4102 +a 15890 15891 4102 +a 15891 15892 4102 +a 15892 15893 4102 +a 15893 15894 4102 +a 15894 15895 4102 +a 15895 15896 4102 +a 15896 15897 4102 +a 15897 15898 4102 +a 15898 15899 4102 +a 15899 15900 4102 +a 15900 15901 4102 +a 15901 15902 4102 +a 15902 15903 4102 +a 15903 15904 4102 +a 15904 15905 4102 +a 15905 15906 4102 +a 15906 15907 4102 +a 15907 15908 4102 +a 15908 15909 4102 +a 15909 15910 4102 +a 15910 15911 4102 +a 15911 15912 4102 +a 15912 15913 4102 +a 15913 15914 4102 +a 15914 15915 4102 +a 15915 15916 4102 +a 15916 15917 4102 +a 15917 15918 4102 +a 15918 15919 4102 +a 15919 15920 4102 +a 15920 15921 4102 +a 15921 15922 4102 +a 15922 15923 4102 +a 15923 15924 4102 +a 15924 15925 4102 +a 15925 15926 4102 +a 15926 15927 4102 +a 15927 15928 4102 +a 15928 15929 4102 +a 15929 15930 4102 +a 15930 15931 4102 +a 15931 15932 4102 +a 15932 15933 4102 +a 15933 15934 4102 +a 15934 15935 4102 +a 15935 15936 4102 +a 15936 15937 4102 +a 15937 15938 4102 +a 15938 15939 4102 +a 15939 15940 4102 +a 15940 15941 4102 +a 15941 15942 4102 +a 15942 15943 4102 +a 15943 15944 4102 +a 15944 15945 4102 +a 15945 15946 4102 +a 15946 15947 4102 +a 15947 15948 4102 +a 15948 15949 4102 +a 15949 15950 4102 +a 15950 15951 4102 +a 15951 15952 4102 +a 15952 15953 4102 +a 15953 15954 4102 +a 15954 15955 4102 +a 15955 15956 4102 +a 15956 15957 4102 +a 15957 15958 4102 +a 15958 15959 4102 +a 15959 15960 4102 +a 15960 15961 4102 +a 15961 15962 4102 +a 15962 15963 4102 +a 15963 15964 4102 +a 15964 15965 4102 +a 15965 15966 4102 +a 15966 15967 4102 +a 15967 15968 4102 +a 15968 15969 4102 +a 15969 15970 4102 +a 15970 15971 4102 +a 15971 15972 4102 +a 15972 15973 4102 +a 15973 15974 4102 +a 15974 15975 4102 +a 15975 15976 4102 +a 15976 15977 4102 +a 15977 15978 4102 +a 15978 15979 4102 +a 15979 15980 4102 +a 15980 15981 4102 +a 15981 15982 4102 +a 15982 15983 4102 +a 15983 15984 4102 +a 15984 15985 4102 +a 15985 15986 4102 +a 15986 15987 4102 +a 15987 15988 4102 +a 15988 15989 4102 +a 15989 15990 4102 +a 15990 15991 4102 +a 15991 15992 4102 +a 15992 15993 4102 +a 15993 15994 4102 +a 15994 15995 4102 +a 15995 15996 4102 +a 15996 15997 4102 +a 15997 15998 4102 +a 15998 15999 4102 +a 15999 16000 4102 +a 16000 16001 4102 +a 16001 16002 4102 +a 16002 16003 4102 +a 16003 16004 4102 +a 16004 16005 4102 +a 16005 16006 4102 +a 16006 16007 4102 +a 16007 16008 4102 +a 16008 16009 4102 +a 16009 16010 4102 +a 16010 16011 4102 +a 16011 16012 4102 +a 16012 16013 4102 +a 16013 16014 4102 +a 16014 16015 4102 +a 16015 16016 4102 +a 16016 16017 4102 +a 16017 16018 4102 +a 16018 16019 4102 +a 16019 16020 4102 +a 16020 16021 4102 +a 16021 16022 4102 +a 16022 16023 4102 +a 16023 16024 4102 +a 16024 16025 4102 +a 16025 16026 4102 +a 16026 16027 4102 +a 16027 16028 4102 +a 16028 16029 4102 +a 16029 16030 4102 +a 16030 16031 4102 +a 16031 16032 4102 +a 16032 16033 4102 +a 16033 16034 4102 +a 16034 16035 4102 +a 16035 16036 4102 +a 16036 16037 4102 +a 16037 16038 4102 +a 16038 16039 4102 +a 16039 16040 4102 +a 16040 16041 4102 +a 16041 16042 4102 +a 16042 16043 4102 +a 16043 16044 4102 +a 16044 16045 4102 +a 16045 16046 4102 +a 16046 16047 4102 +a 16047 16048 4102 +a 16048 16049 4102 +a 16049 16050 4102 +a 16050 16051 4102 +a 16051 16052 4102 +a 16052 16053 4102 +a 16053 16054 4102 +a 16054 16055 4102 +a 16055 16056 4102 +a 16056 16057 4102 +a 16057 16058 4102 +a 16058 16059 4102 +a 16059 16060 4102 +a 16060 16061 4102 +a 16061 16062 4102 +a 16062 16063 4102 +a 16063 16064 4102 +a 16064 16065 4102 +a 16065 16066 4102 +a 16066 16067 4102 +a 16067 16068 4102 +a 16068 16069 4102 +a 16069 16070 4102 +a 16070 16071 4102 +a 16071 16072 4102 +a 16072 16073 4102 +a 16073 16074 4102 +a 16074 16075 4102 +a 16075 16076 4102 +a 16076 16077 4102 +a 16077 16078 4102 +a 16078 16079 4102 +a 16079 16080 4102 +a 16080 16081 4102 +a 16081 16082 4102 +a 16082 16083 4102 +a 16083 16084 4102 +a 16084 16085 4102 +a 16085 16086 4102 +a 16086 16087 4102 +a 16087 16088 4102 +a 16088 16089 4102 +a 16089 16090 4102 +a 16090 16091 4102 +a 16091 16092 4102 +a 16092 16093 4102 +a 16093 16094 4102 +a 16094 16095 4102 +a 16095 16096 4102 +a 16096 16097 4102 +a 16097 16098 4102 +a 16098 16099 4102 +a 16099 16100 4102 +a 16100 16101 4102 +a 16101 16102 4102 +a 16102 16103 4102 +a 16103 16104 4102 +a 16104 16105 4102 +a 16105 16106 4102 +a 16106 16107 4102 +a 16107 16108 4102 +a 16108 16109 4102 +a 16109 16110 4102 +a 16110 16111 4102 +a 16111 16112 4102 +a 16112 16113 4102 +a 16113 16114 4102 +a 16114 16115 4102 +a 16115 16116 4102 +a 16116 16117 4102 +a 16117 16118 4102 +a 16118 16119 4102 +a 16119 16120 4102 +a 16120 16121 4102 +a 16121 16122 4102 +a 16122 16123 4102 +a 16123 16124 4102 +a 16124 16125 4102 +a 16125 16126 4102 +a 16126 16127 4102 +a 16127 16128 4102 +a 16128 16129 4102 +a 16129 16130 4102 +a 16130 16131 4102 +a 16131 16132 4102 +a 16132 16133 4102 +a 16133 16134 4102 +a 16134 16135 4102 +a 16135 16136 4102 +a 16136 16137 4102 +a 16137 16138 4102 +a 16138 16139 4102 +a 16139 16140 4102 +a 16140 16141 4102 +a 16141 16142 4102 +a 16142 16143 4102 +a 16143 16144 4102 +a 16144 16145 4102 +a 16145 16146 4102 +a 16146 16147 4102 +a 16147 16148 4102 +a 16148 16149 4102 +a 16149 16150 4102 +a 16150 16151 4102 +a 16151 16152 4102 +a 16152 16153 4102 +a 16153 16154 4102 +a 16154 16155 4102 +a 16155 16156 4102 +a 16156 16157 4102 +a 16157 16158 4102 +a 16158 16159 4102 +a 16159 16160 4102 +a 16160 16161 4102 +a 16161 16162 4102 +a 16162 16163 4102 +a 16163 16164 4102 +a 16164 16165 4102 +a 16165 16166 4102 +a 16166 16167 4102 +a 16167 16168 4102 +a 16168 16169 4102 +a 16169 16170 4102 +a 16170 16171 4102 +a 16171 16172 4102 +a 16172 16173 4102 +a 16173 16174 4102 +a 16174 16175 4102 +a 16175 16176 4102 +a 16176 16177 4102 +a 16177 16178 4102 +a 16178 16179 4102 +a 16179 16180 4102 +a 16180 16181 4102 +a 16181 16182 4102 +a 16182 16183 4102 +a 16183 16184 4102 +a 16184 16185 4102 +a 16185 16186 4102 +a 16186 16187 4102 +a 16187 16188 4102 +a 16188 16189 4102 +a 16189 16190 4102 +a 16190 16191 4102 +a 16191 16192 4102 +a 16192 16193 4102 +a 16193 16194 4102 +a 16194 16195 4102 +a 16195 16196 4102 +a 16196 16197 4102 +a 16197 16198 4102 +a 16198 16199 4102 +a 16199 16200 4102 +a 16200 16201 4102 +a 16201 16202 4102 +a 16202 16203 4102 +a 16203 16204 4102 +a 16204 16205 4102 +a 16205 16206 4102 +a 16206 16207 4102 +a 16207 16208 4102 +a 16208 16209 4102 +a 16209 16210 4102 +a 16210 16211 4102 +a 16211 16212 4102 +a 16212 16213 4102 +a 16213 16214 4102 +a 16214 16215 4102 +a 16215 16216 4102 +a 16216 16217 4102 +a 16217 16218 4102 +a 16218 16219 4102 +a 16219 16220 4102 +a 16220 16221 4102 +a 16221 16222 4102 +a 16222 16223 4102 +a 16223 16224 4102 +a 16224 16225 4102 +a 16225 16226 4102 +a 16226 16227 4102 +a 16227 16228 4102 +a 16228 16229 4102 +a 16229 16230 4102 +a 16230 16231 4102 +a 16231 16232 4102 +a 16232 16233 4102 +a 16233 16234 4102 +a 16234 16235 4102 +a 16235 16236 4102 +a 16236 16237 4102 +a 16237 16238 4102 +a 16238 16239 4102 +a 16239 16240 4102 +a 16240 16241 4102 +a 16241 16242 4102 +a 16242 16243 4102 +a 16243 16244 4102 +a 16244 16245 4102 +a 16245 16246 4102 +a 16246 16247 4102 +a 16247 16248 4102 +a 16248 16249 4102 +a 16249 16250 4102 +a 16250 16251 4102 +a 16251 16252 4102 +a 16252 16253 4102 +a 16253 16254 4102 +a 16254 16255 4102 +a 16255 16256 4102 +a 16256 16257 4102 +a 16257 16258 4102 +a 16258 16259 4102 +a 16259 16260 4102 +a 16260 16261 4102 +a 16261 16262 4102 +a 16262 16263 4102 +a 16263 16264 4102 +a 16264 16265 4102 +a 16265 16266 4102 +a 16266 16267 4102 +a 16267 16268 4102 +a 16268 16269 4102 +a 16269 16270 4102 +a 16270 16271 4102 +a 16271 16272 4102 +a 16272 16273 4102 +a 16273 16274 4102 +a 16274 16275 4102 +a 16275 16276 4102 +a 16276 16277 4102 +a 16277 16278 4102 +a 16278 16279 4102 +a 16279 16280 4102 +a 16280 16281 4102 +a 16281 16282 4102 +a 16282 16283 4102 +a 16283 16284 4102 +a 16284 16285 4102 +a 16285 16286 4102 +a 16286 16287 4102 +a 16287 16288 4102 +a 16288 16289 4102 +a 16289 16290 4102 +a 16290 16291 4102 +a 16291 16292 4102 +a 16292 16293 4102 +a 16293 16294 4102 +a 16294 16295 4102 +a 16295 16296 4102 +a 16296 16297 4102 +a 16297 16298 4102 +a 16298 16299 4102 +a 16299 16300 4102 +a 16300 16301 4102 +a 16301 16302 4102 +a 16302 16303 4102 +a 16303 16304 4102 +a 16304 16305 4102 +a 16305 16306 4102 +a 16306 16307 4102 +a 16307 16308 4102 +a 16308 16309 4102 +a 16309 16310 4102 +a 16310 16311 4102 +a 16311 16312 4102 +a 16312 16313 4102 +a 16313 16314 4102 +a 16314 16315 4102 +a 16315 16316 4102 +a 16316 16317 4102 +a 16317 16318 4102 +a 16318 16319 4102 +a 16319 16320 4102 +a 16320 16321 4102 +a 16321 16322 4102 +a 16322 16323 4102 +a 16323 16324 4102 +a 16324 16325 4102 +a 16325 16326 4102 +a 16326 16327 4102 +a 16327 16328 4102 +a 16328 16329 4102 +a 16329 16330 4102 +a 16330 16331 4102 +a 16331 16332 4102 +a 16332 16333 4102 +a 16333 16334 4102 +a 16334 16335 4102 +a 16335 16336 4102 +a 16336 16337 4102 +a 16337 16338 4102 +a 16338 16339 4102 +a 16339 16340 4102 +a 16340 16341 4102 +a 16341 16342 4102 +a 16342 16343 4102 +a 16343 16344 4102 +a 16344 16345 4102 +a 16345 16346 4102 +a 16346 16347 4102 +a 16347 16348 4102 +a 16348 16349 4102 +a 16349 16350 4102 +a 16350 16351 4102 +a 16351 16352 4102 +a 16352 16353 4102 +a 16353 16354 4102 +a 16354 16355 4102 +a 16355 16356 4102 +a 16356 16357 4102 +a 16357 16358 4102 +a 16358 16359 4102 +a 16359 16360 4102 +a 16360 16361 4102 +a 16361 16362 4102 +a 16362 16363 4102 +a 16363 16364 4102 +a 16364 16365 4102 +a 16365 16366 4102 +a 16366 16367 4102 +a 16367 16368 4102 +a 16368 16369 4102 +a 16369 16370 4102 +a 16370 16371 4102 +a 16371 16372 4102 +a 16372 16373 4102 +a 16373 16374 4102 +a 16374 16375 4102 +a 16375 16376 4102 +a 16376 16377 4102 +a 16377 16378 4102 +a 16378 16379 4102 +a 16379 16380 4102 +a 16380 16381 4102 +a 16381 16382 4102 +a 16382 16383 4102 +a 16383 16384 4102 +a 16384 16385 4102 +a 16385 16386 4102 +a 16386 16387 4102 +a 16387 16388 4102 +a 16388 16389 4102 +a 16389 16390 4102 +a 16390 16391 4102 +a 16391 16392 4102 +a 16392 16393 4102 +a 16393 16394 4102 +a 16394 16395 4102 +a 16395 16396 4102 +a 16396 16397 4102 +a 16397 16398 4102 +a 16398 16399 4102 +a 16399 16400 4102 +a 16400 16401 4102 +a 16401 16402 4102 +a 16402 16403 4102 +a 16403 16404 4102 +a 16404 16405 4102 +a 16405 16406 4102 +a 16406 16407 4102 +a 16407 16408 4102 +a 16408 16409 4102 +a 16409 16410 4102 +a 16410 16411 4102 +a 16411 16412 4102 +a 16412 16413 4102 +a 16413 16414 4102 +a 8209 16414 1 +a 8210 16413 1 +a 8211 16412 1 +a 8212 16411 1 +a 8213 16410 1 +a 8214 16409 1 +a 8215 16408 1 +a 8216 16407 1 +a 8217 16406 1 +a 8218 16405 1 +a 8219 16404 1 +a 8220 16403 1 +a 8221 16402 1 +a 8222 16401 1 +a 8223 16400 1 +a 8224 16399 1 +a 8225 16398 1 +a 8226 16397 1 +a 8227 16396 1 +a 8228 16395 1 +a 8229 16394 1 +a 8230 16393 1 +a 8231 16392 1 +a 8232 16391 1 +a 8233 16390 1 +a 8234 16389 1 +a 8235 16388 1 +a 8236 16387 1 +a 8237 16386 1 +a 8238 16385 1 +a 8239 16384 1 +a 8240 16383 1 +a 8241 16382 1 +a 8242 16381 1 +a 8243 16380 1 +a 8244 16379 1 +a 8245 16378 1 +a 8246 16377 1 +a 8247 16376 1 +a 8248 16375 1 +a 8249 16374 1 +a 8250 16373 1 +a 8251 16372 1 +a 8252 16371 1 +a 8253 16370 1 +a 8254 16369 1 +a 8255 16368 1 +a 8256 16367 1 +a 8257 16366 1 +a 8258 16365 1 +a 8259 16364 1 +a 8260 16363 1 +a 8261 16362 1 +a 8262 16361 1 +a 8263 16360 1 +a 8264 16359 1 +a 8265 16358 1 +a 8266 16357 1 +a 8267 16356 1 +a 8268 16355 1 +a 8269 16354 1 +a 8270 16353 1 +a 8271 16352 1 +a 8272 16351 1 +a 8273 16350 1 +a 8274 16349 1 +a 8275 16348 1 +a 8276 16347 1 +a 8277 16346 1 +a 8278 16345 1 +a 8279 16344 1 +a 8280 16343 1 +a 8281 16342 1 +a 8282 16341 1 +a 8283 16340 1 +a 8284 16339 1 +a 8285 16338 1 +a 8286 16337 1 +a 8287 16336 1 +a 8288 16335 1 +a 8289 16334 1 +a 8290 16333 1 +a 8291 16332 1 +a 8292 16331 1 +a 8293 16330 1 +a 8294 16329 1 +a 8295 16328 1 +a 8296 16327 1 +a 8297 16326 1 +a 8298 16325 1 +a 8299 16324 1 +a 8300 16323 1 +a 8301 16322 1 +a 8302 16321 1 +a 8303 16320 1 +a 8304 16319 1 +a 8305 16318 1 +a 8306 16317 1 +a 8307 16316 1 +a 8308 16315 1 +a 8309 16314 1 +a 8310 16313 1 +a 8311 16312 1 +a 8312 16311 1 +a 8313 16310 1 +a 8314 16309 1 +a 8315 16308 1 +a 8316 16307 1 +a 8317 16306 1 +a 8318 16305 1 +a 8319 16304 1 +a 8320 16303 1 +a 8321 16302 1 +a 8322 16301 1 +a 8323 16300 1 +a 8324 16299 1 +a 8325 16298 1 +a 8326 16297 1 +a 8327 16296 1 +a 8328 16295 1 +a 8329 16294 1 +a 8330 16293 1 +a 8331 16292 1 +a 8332 16291 1 +a 8333 16290 1 +a 8334 16289 1 +a 8335 16288 1 +a 8336 16287 1 +a 8337 16286 1 +a 8338 16285 1 +a 8339 16284 1 +a 8340 16283 1 +a 8341 16282 1 +a 8342 16281 1 +a 8343 16280 1 +a 8344 16279 1 +a 8345 16278 1 +a 8346 16277 1 +a 8347 16276 1 +a 8348 16275 1 +a 8349 16274 1 +a 8350 16273 1 +a 8351 16272 1 +a 8352 16271 1 +a 8353 16270 1 +a 8354 16269 1 +a 8355 16268 1 +a 8356 16267 1 +a 8357 16266 1 +a 8358 16265 1 +a 8359 16264 1 +a 8360 16263 1 +a 8361 16262 1 +a 8362 16261 1 +a 8363 16260 1 +a 8364 16259 1 +a 8365 16258 1 +a 8366 16257 1 +a 8367 16256 1 +a 8368 16255 1 +a 8369 16254 1 +a 8370 16253 1 +a 8371 16252 1 +a 8372 16251 1 +a 8373 16250 1 +a 8374 16249 1 +a 8375 16248 1 +a 8376 16247 1 +a 8377 16246 1 +a 8378 16245 1 +a 8379 16244 1 +a 8380 16243 1 +a 8381 16242 1 +a 8382 16241 1 +a 8383 16240 1 +a 8384 16239 1 +a 8385 16238 1 +a 8386 16237 1 +a 8387 16236 1 +a 8388 16235 1 +a 8389 16234 1 +a 8390 16233 1 +a 8391 16232 1 +a 8392 16231 1 +a 8393 16230 1 +a 8394 16229 1 +a 8395 16228 1 +a 8396 16227 1 +a 8397 16226 1 +a 8398 16225 1 +a 8399 16224 1 +a 8400 16223 1 +a 8401 16222 1 +a 8402 16221 1 +a 8403 16220 1 +a 8404 16219 1 +a 8405 16218 1 +a 8406 16217 1 +a 8407 16216 1 +a 8408 16215 1 +a 8409 16214 1 +a 8410 16213 1 +a 8411 16212 1 +a 8412 16211 1 +a 8413 16210 1 +a 8414 16209 1 +a 8415 16208 1 +a 8416 16207 1 +a 8417 16206 1 +a 8418 16205 1 +a 8419 16204 1 +a 8420 16203 1 +a 8421 16202 1 +a 8422 16201 1 +a 8423 16200 1 +a 8424 16199 1 +a 8425 16198 1 +a 8426 16197 1 +a 8427 16196 1 +a 8428 16195 1 +a 8429 16194 1 +a 8430 16193 1 +a 8431 16192 1 +a 8432 16191 1 +a 8433 16190 1 +a 8434 16189 1 +a 8435 16188 1 +a 8436 16187 1 +a 8437 16186 1 +a 8438 16185 1 +a 8439 16184 1 +a 8440 16183 1 +a 8441 16182 1 +a 8442 16181 1 +a 8443 16180 1 +a 8444 16179 1 +a 8445 16178 1 +a 8446 16177 1 +a 8447 16176 1 +a 8448 16175 1 +a 8449 16174 1 +a 8450 16173 1 +a 8451 16172 1 +a 8452 16171 1 +a 8453 16170 1 +a 8454 16169 1 +a 8455 16168 1 +a 8456 16167 1 +a 8457 16166 1 +a 8458 16165 1 +a 8459 16164 1 +a 8460 16163 1 +a 8461 16162 1 +a 8462 16161 1 +a 8463 16160 1 +a 8464 16159 1 +a 8465 16158 1 +a 8466 16157 1 +a 8467 16156 1 +a 8468 16155 1 +a 8469 16154 1 +a 8470 16153 1 +a 8471 16152 1 +a 8472 16151 1 +a 8473 16150 1 +a 8474 16149 1 +a 8475 16148 1 +a 8476 16147 1 +a 8477 16146 1 +a 8478 16145 1 +a 8479 16144 1 +a 8480 16143 1 +a 8481 16142 1 +a 8482 16141 1 +a 8483 16140 1 +a 8484 16139 1 +a 8485 16138 1 +a 8486 16137 1 +a 8487 16136 1 +a 8488 16135 1 +a 8489 16134 1 +a 8490 16133 1 +a 8491 16132 1 +a 8492 16131 1 +a 8493 16130 1 +a 8494 16129 1 +a 8495 16128 1 +a 8496 16127 1 +a 8497 16126 1 +a 8498 16125 1 +a 8499 16124 1 +a 8500 16123 1 +a 8501 16122 1 +a 8502 16121 1 +a 8503 16120 1 +a 8504 16119 1 +a 8505 16118 1 +a 8506 16117 1 +a 8507 16116 1 +a 8508 16115 1 +a 8509 16114 1 +a 8510 16113 1 +a 8511 16112 1 +a 8512 16111 1 +a 8513 16110 1 +a 8514 16109 1 +a 8515 16108 1 +a 8516 16107 1 +a 8517 16106 1 +a 8518 16105 1 +a 8519 16104 1 +a 8520 16103 1 +a 8521 16102 1 +a 8522 16101 1 +a 8523 16100 1 +a 8524 16099 1 +a 8525 16098 1 +a 8526 16097 1 +a 8527 16096 1 +a 8528 16095 1 +a 8529 16094 1 +a 8530 16093 1 +a 8531 16092 1 +a 8532 16091 1 +a 8533 16090 1 +a 8534 16089 1 +a 8535 16088 1 +a 8536 16087 1 +a 8537 16086 1 +a 8538 16085 1 +a 8539 16084 1 +a 8540 16083 1 +a 8541 16082 1 +a 8542 16081 1 +a 8543 16080 1 +a 8544 16079 1 +a 8545 16078 1 +a 8546 16077 1 +a 8547 16076 1 +a 8548 16075 1 +a 8549 16074 1 +a 8550 16073 1 +a 8551 16072 1 +a 8552 16071 1 +a 8553 16070 1 +a 8554 16069 1 +a 8555 16068 1 +a 8556 16067 1 +a 8557 16066 1 +a 8558 16065 1 +a 8559 16064 1 +a 8560 16063 1 +a 8561 16062 1 +a 8562 16061 1 +a 8563 16060 1 +a 8564 16059 1 +a 8565 16058 1 +a 8566 16057 1 +a 8567 16056 1 +a 8568 16055 1 +a 8569 16054 1 +a 8570 16053 1 +a 8571 16052 1 +a 8572 16051 1 +a 8573 16050 1 +a 8574 16049 1 +a 8575 16048 1 +a 8576 16047 1 +a 8577 16046 1 +a 8578 16045 1 +a 8579 16044 1 +a 8580 16043 1 +a 8581 16042 1 +a 8582 16041 1 +a 8583 16040 1 +a 8584 16039 1 +a 8585 16038 1 +a 8586 16037 1 +a 8587 16036 1 +a 8588 16035 1 +a 8589 16034 1 +a 8590 16033 1 +a 8591 16032 1 +a 8592 16031 1 +a 8593 16030 1 +a 8594 16029 1 +a 8595 16028 1 +a 8596 16027 1 +a 8597 16026 1 +a 8598 16025 1 +a 8599 16024 1 +a 8600 16023 1 +a 8601 16022 1 +a 8602 16021 1 +a 8603 16020 1 +a 8604 16019 1 +a 8605 16018 1 +a 8606 16017 1 +a 8607 16016 1 +a 8608 16015 1 +a 8609 16014 1 +a 8610 16013 1 +a 8611 16012 1 +a 8612 16011 1 +a 8613 16010 1 +a 8614 16009 1 +a 8615 16008 1 +a 8616 16007 1 +a 8617 16006 1 +a 8618 16005 1 +a 8619 16004 1 +a 8620 16003 1 +a 8621 16002 1 +a 8622 16001 1 +a 8623 16000 1 +a 8624 15999 1 +a 8625 15998 1 +a 8626 15997 1 +a 8627 15996 1 +a 8628 15995 1 +a 8629 15994 1 +a 8630 15993 1 +a 8631 15992 1 +a 8632 15991 1 +a 8633 15990 1 +a 8634 15989 1 +a 8635 15988 1 +a 8636 15987 1 +a 8637 15986 1 +a 8638 15985 1 +a 8639 15984 1 +a 8640 15983 1 +a 8641 15982 1 +a 8642 15981 1 +a 8643 15980 1 +a 8644 15979 1 +a 8645 15978 1 +a 8646 15977 1 +a 8647 15976 1 +a 8648 15975 1 +a 8649 15974 1 +a 8650 15973 1 +a 8651 15972 1 +a 8652 15971 1 +a 8653 15970 1 +a 8654 15969 1 +a 8655 15968 1 +a 8656 15967 1 +a 8657 15966 1 +a 8658 15965 1 +a 8659 15964 1 +a 8660 15963 1 +a 8661 15962 1 +a 8662 15961 1 +a 8663 15960 1 +a 8664 15959 1 +a 8665 15958 1 +a 8666 15957 1 +a 8667 15956 1 +a 8668 15955 1 +a 8669 15954 1 +a 8670 15953 1 +a 8671 15952 1 +a 8672 15951 1 +a 8673 15950 1 +a 8674 15949 1 +a 8675 15948 1 +a 8676 15947 1 +a 8677 15946 1 +a 8678 15945 1 +a 8679 15944 1 +a 8680 15943 1 +a 8681 15942 1 +a 8682 15941 1 +a 8683 15940 1 +a 8684 15939 1 +a 8685 15938 1 +a 8686 15937 1 +a 8687 15936 1 +a 8688 15935 1 +a 8689 15934 1 +a 8690 15933 1 +a 8691 15932 1 +a 8692 15931 1 +a 8693 15930 1 +a 8694 15929 1 +a 8695 15928 1 +a 8696 15927 1 +a 8697 15926 1 +a 8698 15925 1 +a 8699 15924 1 +a 8700 15923 1 +a 8701 15922 1 +a 8702 15921 1 +a 8703 15920 1 +a 8704 15919 1 +a 8705 15918 1 +a 8706 15917 1 +a 8707 15916 1 +a 8708 15915 1 +a 8709 15914 1 +a 8710 15913 1 +a 8711 15912 1 +a 8712 15911 1 +a 8713 15910 1 +a 8714 15909 1 +a 8715 15908 1 +a 8716 15907 1 +a 8717 15906 1 +a 8718 15905 1 +a 8719 15904 1 +a 8720 15903 1 +a 8721 15902 1 +a 8722 15901 1 +a 8723 15900 1 +a 8724 15899 1 +a 8725 15898 1 +a 8726 15897 1 +a 8727 15896 1 +a 8728 15895 1 +a 8729 15894 1 +a 8730 15893 1 +a 8731 15892 1 +a 8732 15891 1 +a 8733 15890 1 +a 8734 15889 1 +a 8735 15888 1 +a 8736 15887 1 +a 8737 15886 1 +a 8738 15885 1 +a 8739 15884 1 +a 8740 15883 1 +a 8741 15882 1 +a 8742 15881 1 +a 8743 15880 1 +a 8744 15879 1 +a 8745 15878 1 +a 8746 15877 1 +a 8747 15876 1 +a 8748 15875 1 +a 8749 15874 1 +a 8750 15873 1 +a 8751 15872 1 +a 8752 15871 1 +a 8753 15870 1 +a 8754 15869 1 +a 8755 15868 1 +a 8756 15867 1 +a 8757 15866 1 +a 8758 15865 1 +a 8759 15864 1 +a 8760 15863 1 +a 8761 15862 1 +a 8762 15861 1 +a 8763 15860 1 +a 8764 15859 1 +a 8765 15858 1 +a 8766 15857 1 +a 8767 15856 1 +a 8768 15855 1 +a 8769 15854 1 +a 8770 15853 1 +a 8771 15852 1 +a 8772 15851 1 +a 8773 15850 1 +a 8774 15849 1 +a 8775 15848 1 +a 8776 15847 1 +a 8777 15846 1 +a 8778 15845 1 +a 8779 15844 1 +a 8780 15843 1 +a 8781 15842 1 +a 8782 15841 1 +a 8783 15840 1 +a 8784 15839 1 +a 8785 15838 1 +a 8786 15837 1 +a 8787 15836 1 +a 8788 15835 1 +a 8789 15834 1 +a 8790 15833 1 +a 8791 15832 1 +a 8792 15831 1 +a 8793 15830 1 +a 8794 15829 1 +a 8795 15828 1 +a 8796 15827 1 +a 8797 15826 1 +a 8798 15825 1 +a 8799 15824 1 +a 8800 15823 1 +a 8801 15822 1 +a 8802 15821 1 +a 8803 15820 1 +a 8804 15819 1 +a 8805 15818 1 +a 8806 15817 1 +a 8807 15816 1 +a 8808 15815 1 +a 8809 15814 1 +a 8810 15813 1 +a 8811 15812 1 +a 8812 15811 1 +a 8813 15810 1 +a 8814 15809 1 +a 8815 15808 1 +a 8816 15807 1 +a 8817 15806 1 +a 8818 15805 1 +a 8819 15804 1 +a 8820 15803 1 +a 8821 15802 1 +a 8822 15801 1 +a 8823 15800 1 +a 8824 15799 1 +a 8825 15798 1 +a 8826 15797 1 +a 8827 15796 1 +a 8828 15795 1 +a 8829 15794 1 +a 8830 15793 1 +a 8831 15792 1 +a 8832 15791 1 +a 8833 15790 1 +a 8834 15789 1 +a 8835 15788 1 +a 8836 15787 1 +a 8837 15786 1 +a 8838 15785 1 +a 8839 15784 1 +a 8840 15783 1 +a 8841 15782 1 +a 8842 15781 1 +a 8843 15780 1 +a 8844 15779 1 +a 8845 15778 1 +a 8846 15777 1 +a 8847 15776 1 +a 8848 15775 1 +a 8849 15774 1 +a 8850 15773 1 +a 8851 15772 1 +a 8852 15771 1 +a 8853 15770 1 +a 8854 15769 1 +a 8855 15768 1 +a 8856 15767 1 +a 8857 15766 1 +a 8858 15765 1 +a 8859 15764 1 +a 8860 15763 1 +a 8861 15762 1 +a 8862 15761 1 +a 8863 15760 1 +a 8864 15759 1 +a 8865 15758 1 +a 8866 15757 1 +a 8867 15756 1 +a 8868 15755 1 +a 8869 15754 1 +a 8870 15753 1 +a 8871 15752 1 +a 8872 15751 1 +a 8873 15750 1 +a 8874 15749 1 +a 8875 15748 1 +a 8876 15747 1 +a 8877 15746 1 +a 8878 15745 1 +a 8879 15744 1 +a 8880 15743 1 +a 8881 15742 1 +a 8882 15741 1 +a 8883 15740 1 +a 8884 15739 1 +a 8885 15738 1 +a 8886 15737 1 +a 8887 15736 1 +a 8888 15735 1 +a 8889 15734 1 +a 8890 15733 1 +a 8891 15732 1 +a 8892 15731 1 +a 8893 15730 1 +a 8894 15729 1 +a 8895 15728 1 +a 8896 15727 1 +a 8897 15726 1 +a 8898 15725 1 +a 8899 15724 1 +a 8900 15723 1 +a 8901 15722 1 +a 8902 15721 1 +a 8903 15720 1 +a 8904 15719 1 +a 8905 15718 1 +a 8906 15717 1 +a 8907 15716 1 +a 8908 15715 1 +a 8909 15714 1 +a 8910 15713 1 +a 8911 15712 1 +a 8912 15711 1 +a 8913 15710 1 +a 8914 15709 1 +a 8915 15708 1 +a 8916 15707 1 +a 8917 15706 1 +a 8918 15705 1 +a 8919 15704 1 +a 8920 15703 1 +a 8921 15702 1 +a 8922 15701 1 +a 8923 15700 1 +a 8924 15699 1 +a 8925 15698 1 +a 8926 15697 1 +a 8927 15696 1 +a 8928 15695 1 +a 8929 15694 1 +a 8930 15693 1 +a 8931 15692 1 +a 8932 15691 1 +a 8933 15690 1 +a 8934 15689 1 +a 8935 15688 1 +a 8936 15687 1 +a 8937 15686 1 +a 8938 15685 1 +a 8939 15684 1 +a 8940 15683 1 +a 8941 15682 1 +a 8942 15681 1 +a 8943 15680 1 +a 8944 15679 1 +a 8945 15678 1 +a 8946 15677 1 +a 8947 15676 1 +a 8948 15675 1 +a 8949 15674 1 +a 8950 15673 1 +a 8951 15672 1 +a 8952 15671 1 +a 8953 15670 1 +a 8954 15669 1 +a 8955 15668 1 +a 8956 15667 1 +a 8957 15666 1 +a 8958 15665 1 +a 8959 15664 1 +a 8960 15663 1 +a 8961 15662 1 +a 8962 15661 1 +a 8963 15660 1 +a 8964 15659 1 +a 8965 15658 1 +a 8966 15657 1 +a 8967 15656 1 +a 8968 15655 1 +a 8969 15654 1 +a 8970 15653 1 +a 8971 15652 1 +a 8972 15651 1 +a 8973 15650 1 +a 8974 15649 1 +a 8975 15648 1 +a 8976 15647 1 +a 8977 15646 1 +a 8978 15645 1 +a 8979 15644 1 +a 8980 15643 1 +a 8981 15642 1 +a 8982 15641 1 +a 8983 15640 1 +a 8984 15639 1 +a 8985 15638 1 +a 8986 15637 1 +a 8987 15636 1 +a 8988 15635 1 +a 8989 15634 1 +a 8990 15633 1 +a 8991 15632 1 +a 8992 15631 1 +a 8993 15630 1 +a 8994 15629 1 +a 8995 15628 1 +a 8996 15627 1 +a 8997 15626 1 +a 8998 15625 1 +a 8999 15624 1 +a 9000 15623 1 +a 9001 15622 1 +a 9002 15621 1 +a 9003 15620 1 +a 9004 15619 1 +a 9005 15618 1 +a 9006 15617 1 +a 9007 15616 1 +a 9008 15615 1 +a 9009 15614 1 +a 9010 15613 1 +a 9011 15612 1 +a 9012 15611 1 +a 9013 15610 1 +a 9014 15609 1 +a 9015 15608 1 +a 9016 15607 1 +a 9017 15606 1 +a 9018 15605 1 +a 9019 15604 1 +a 9020 15603 1 +a 9021 15602 1 +a 9022 15601 1 +a 9023 15600 1 +a 9024 15599 1 +a 9025 15598 1 +a 9026 15597 1 +a 9027 15596 1 +a 9028 15595 1 +a 9029 15594 1 +a 9030 15593 1 +a 9031 15592 1 +a 9032 15591 1 +a 9033 15590 1 +a 9034 15589 1 +a 9035 15588 1 +a 9036 15587 1 +a 9037 15586 1 +a 9038 15585 1 +a 9039 15584 1 +a 9040 15583 1 +a 9041 15582 1 +a 9042 15581 1 +a 9043 15580 1 +a 9044 15579 1 +a 9045 15578 1 +a 9046 15577 1 +a 9047 15576 1 +a 9048 15575 1 +a 9049 15574 1 +a 9050 15573 1 +a 9051 15572 1 +a 9052 15571 1 +a 9053 15570 1 +a 9054 15569 1 +a 9055 15568 1 +a 9056 15567 1 +a 9057 15566 1 +a 9058 15565 1 +a 9059 15564 1 +a 9060 15563 1 +a 9061 15562 1 +a 9062 15561 1 +a 9063 15560 1 +a 9064 15559 1 +a 9065 15558 1 +a 9066 15557 1 +a 9067 15556 1 +a 9068 15555 1 +a 9069 15554 1 +a 9070 15553 1 +a 9071 15552 1 +a 9072 15551 1 +a 9073 15550 1 +a 9074 15549 1 +a 9075 15548 1 +a 9076 15547 1 +a 9077 15546 1 +a 9078 15545 1 +a 9079 15544 1 +a 9080 15543 1 +a 9081 15542 1 +a 9082 15541 1 +a 9083 15540 1 +a 9084 15539 1 +a 9085 15538 1 +a 9086 15537 1 +a 9087 15536 1 +a 9088 15535 1 +a 9089 15534 1 +a 9090 15533 1 +a 9091 15532 1 +a 9092 15531 1 +a 9093 15530 1 +a 9094 15529 1 +a 9095 15528 1 +a 9096 15527 1 +a 9097 15526 1 +a 9098 15525 1 +a 9099 15524 1 +a 9100 15523 1 +a 9101 15522 1 +a 9102 15521 1 +a 9103 15520 1 +a 9104 15519 1 +a 9105 15518 1 +a 9106 15517 1 +a 9107 15516 1 +a 9108 15515 1 +a 9109 15514 1 +a 9110 15513 1 +a 9111 15512 1 +a 9112 15511 1 +a 9113 15510 1 +a 9114 15509 1 +a 9115 15508 1 +a 9116 15507 1 +a 9117 15506 1 +a 9118 15505 1 +a 9119 15504 1 +a 9120 15503 1 +a 9121 15502 1 +a 9122 15501 1 +a 9123 15500 1 +a 9124 15499 1 +a 9125 15498 1 +a 9126 15497 1 +a 9127 15496 1 +a 9128 15495 1 +a 9129 15494 1 +a 9130 15493 1 +a 9131 15492 1 +a 9132 15491 1 +a 9133 15490 1 +a 9134 15489 1 +a 9135 15488 1 +a 9136 15487 1 +a 9137 15486 1 +a 9138 15485 1 +a 9139 15484 1 +a 9140 15483 1 +a 9141 15482 1 +a 9142 15481 1 +a 9143 15480 1 +a 9144 15479 1 +a 9145 15478 1 +a 9146 15477 1 +a 9147 15476 1 +a 9148 15475 1 +a 9149 15474 1 +a 9150 15473 1 +a 9151 15472 1 +a 9152 15471 1 +a 9153 15470 1 +a 9154 15469 1 +a 9155 15468 1 +a 9156 15467 1 +a 9157 15466 1 +a 9158 15465 1 +a 9159 15464 1 +a 9160 15463 1 +a 9161 15462 1 +a 9162 15461 1 +a 9163 15460 1 +a 9164 15459 1 +a 9165 15458 1 +a 9166 15457 1 +a 9167 15456 1 +a 9168 15455 1 +a 9169 15454 1 +a 9170 15453 1 +a 9171 15452 1 +a 9172 15451 1 +a 9173 15450 1 +a 9174 15449 1 +a 9175 15448 1 +a 9176 15447 1 +a 9177 15446 1 +a 9178 15445 1 +a 9179 15444 1 +a 9180 15443 1 +a 9181 15442 1 +a 9182 15441 1 +a 9183 15440 1 +a 9184 15439 1 +a 9185 15438 1 +a 9186 15437 1 +a 9187 15436 1 +a 9188 15435 1 +a 9189 15434 1 +a 9190 15433 1 +a 9191 15432 1 +a 9192 15431 1 +a 9193 15430 1 +a 9194 15429 1 +a 9195 15428 1 +a 9196 15427 1 +a 9197 15426 1 +a 9198 15425 1 +a 9199 15424 1 +a 9200 15423 1 +a 9201 15422 1 +a 9202 15421 1 +a 9203 15420 1 +a 9204 15419 1 +a 9205 15418 1 +a 9206 15417 1 +a 9207 15416 1 +a 9208 15415 1 +a 9209 15414 1 +a 9210 15413 1 +a 9211 15412 1 +a 9212 15411 1 +a 9213 15410 1 +a 9214 15409 1 +a 9215 15408 1 +a 9216 15407 1 +a 9217 15406 1 +a 9218 15405 1 +a 9219 15404 1 +a 9220 15403 1 +a 9221 15402 1 +a 9222 15401 1 +a 9223 15400 1 +a 9224 15399 1 +a 9225 15398 1 +a 9226 15397 1 +a 9227 15396 1 +a 9228 15395 1 +a 9229 15394 1 +a 9230 15393 1 +a 9231 15392 1 +a 9232 15391 1 +a 9233 15390 1 +a 9234 15389 1 +a 9235 15388 1 +a 9236 15387 1 +a 9237 15386 1 +a 9238 15385 1 +a 9239 15384 1 +a 9240 15383 1 +a 9241 15382 1 +a 9242 15381 1 +a 9243 15380 1 +a 9244 15379 1 +a 9245 15378 1 +a 9246 15377 1 +a 9247 15376 1 +a 9248 15375 1 +a 9249 15374 1 +a 9250 15373 1 +a 9251 15372 1 +a 9252 15371 1 +a 9253 15370 1 +a 9254 15369 1 +a 9255 15368 1 +a 9256 15367 1 +a 9257 15366 1 +a 9258 15365 1 +a 9259 15364 1 +a 9260 15363 1 +a 9261 15362 1 +a 9262 15361 1 +a 9263 15360 1 +a 9264 15359 1 +a 9265 15358 1 +a 9266 15357 1 +a 9267 15356 1 +a 9268 15355 1 +a 9269 15354 1 +a 9270 15353 1 +a 9271 15352 1 +a 9272 15351 1 +a 9273 15350 1 +a 9274 15349 1 +a 9275 15348 1 +a 9276 15347 1 +a 9277 15346 1 +a 9278 15345 1 +a 9279 15344 1 +a 9280 15343 1 +a 9281 15342 1 +a 9282 15341 1 +a 9283 15340 1 +a 9284 15339 1 +a 9285 15338 1 +a 9286 15337 1 +a 9287 15336 1 +a 9288 15335 1 +a 9289 15334 1 +a 9290 15333 1 +a 9291 15332 1 +a 9292 15331 1 +a 9293 15330 1 +a 9294 15329 1 +a 9295 15328 1 +a 9296 15327 1 +a 9297 15326 1 +a 9298 15325 1 +a 9299 15324 1 +a 9300 15323 1 +a 9301 15322 1 +a 9302 15321 1 +a 9303 15320 1 +a 9304 15319 1 +a 9305 15318 1 +a 9306 15317 1 +a 9307 15316 1 +a 9308 15315 1 +a 9309 15314 1 +a 9310 15313 1 +a 9311 15312 1 +a 9312 15311 1 +a 9313 15310 1 +a 9314 15309 1 +a 9315 15308 1 +a 9316 15307 1 +a 9317 15306 1 +a 9318 15305 1 +a 9319 15304 1 +a 9320 15303 1 +a 9321 15302 1 +a 9322 15301 1 +a 9323 15300 1 +a 9324 15299 1 +a 9325 15298 1 +a 9326 15297 1 +a 9327 15296 1 +a 9328 15295 1 +a 9329 15294 1 +a 9330 15293 1 +a 9331 15292 1 +a 9332 15291 1 +a 9333 15290 1 +a 9334 15289 1 +a 9335 15288 1 +a 9336 15287 1 +a 9337 15286 1 +a 9338 15285 1 +a 9339 15284 1 +a 9340 15283 1 +a 9341 15282 1 +a 9342 15281 1 +a 9343 15280 1 +a 9344 15279 1 +a 9345 15278 1 +a 9346 15277 1 +a 9347 15276 1 +a 9348 15275 1 +a 9349 15274 1 +a 9350 15273 1 +a 9351 15272 1 +a 9352 15271 1 +a 9353 15270 1 +a 9354 15269 1 +a 9355 15268 1 +a 9356 15267 1 +a 9357 15266 1 +a 9358 15265 1 +a 9359 15264 1 +a 9360 15263 1 +a 9361 15262 1 +a 9362 15261 1 +a 9363 15260 1 +a 9364 15259 1 +a 9365 15258 1 +a 9366 15257 1 +a 9367 15256 1 +a 9368 15255 1 +a 9369 15254 1 +a 9370 15253 1 +a 9371 15252 1 +a 9372 15251 1 +a 9373 15250 1 +a 9374 15249 1 +a 9375 15248 1 +a 9376 15247 1 +a 9377 15246 1 +a 9378 15245 1 +a 9379 15244 1 +a 9380 15243 1 +a 9381 15242 1 +a 9382 15241 1 +a 9383 15240 1 +a 9384 15239 1 +a 9385 15238 1 +a 9386 15237 1 +a 9387 15236 1 +a 9388 15235 1 +a 9389 15234 1 +a 9390 15233 1 +a 9391 15232 1 +a 9392 15231 1 +a 9393 15230 1 +a 9394 15229 1 +a 9395 15228 1 +a 9396 15227 1 +a 9397 15226 1 +a 9398 15225 1 +a 9399 15224 1 +a 9400 15223 1 +a 9401 15222 1 +a 9402 15221 1 +a 9403 15220 1 +a 9404 15219 1 +a 9405 15218 1 +a 9406 15217 1 +a 9407 15216 1 +a 9408 15215 1 +a 9409 15214 1 +a 9410 15213 1 +a 9411 15212 1 +a 9412 15211 1 +a 9413 15210 1 +a 9414 15209 1 +a 9415 15208 1 +a 9416 15207 1 +a 9417 15206 1 +a 9418 15205 1 +a 9419 15204 1 +a 9420 15203 1 +a 9421 15202 1 +a 9422 15201 1 +a 9423 15200 1 +a 9424 15199 1 +a 9425 15198 1 +a 9426 15197 1 +a 9427 15196 1 +a 9428 15195 1 +a 9429 15194 1 +a 9430 15193 1 +a 9431 15192 1 +a 9432 15191 1 +a 9433 15190 1 +a 9434 15189 1 +a 9435 15188 1 +a 9436 15187 1 +a 9437 15186 1 +a 9438 15185 1 +a 9439 15184 1 +a 9440 15183 1 +a 9441 15182 1 +a 9442 15181 1 +a 9443 15180 1 +a 9444 15179 1 +a 9445 15178 1 +a 9446 15177 1 +a 9447 15176 1 +a 9448 15175 1 +a 9449 15174 1 +a 9450 15173 1 +a 9451 15172 1 +a 9452 15171 1 +a 9453 15170 1 +a 9454 15169 1 +a 9455 15168 1 +a 9456 15167 1 +a 9457 15166 1 +a 9458 15165 1 +a 9459 15164 1 +a 9460 15163 1 +a 9461 15162 1 +a 9462 15161 1 +a 9463 15160 1 +a 9464 15159 1 +a 9465 15158 1 +a 9466 15157 1 +a 9467 15156 1 +a 9468 15155 1 +a 9469 15154 1 +a 9470 15153 1 +a 9471 15152 1 +a 9472 15151 1 +a 9473 15150 1 +a 9474 15149 1 +a 9475 15148 1 +a 9476 15147 1 +a 9477 15146 1 +a 9478 15145 1 +a 9479 15144 1 +a 9480 15143 1 +a 9481 15142 1 +a 9482 15141 1 +a 9483 15140 1 +a 9484 15139 1 +a 9485 15138 1 +a 9486 15137 1 +a 9487 15136 1 +a 9488 15135 1 +a 9489 15134 1 +a 9490 15133 1 +a 9491 15132 1 +a 9492 15131 1 +a 9493 15130 1 +a 9494 15129 1 +a 9495 15128 1 +a 9496 15127 1 +a 9497 15126 1 +a 9498 15125 1 +a 9499 15124 1 +a 9500 15123 1 +a 9501 15122 1 +a 9502 15121 1 +a 9503 15120 1 +a 9504 15119 1 +a 9505 15118 1 +a 9506 15117 1 +a 9507 15116 1 +a 9508 15115 1 +a 9509 15114 1 +a 9510 15113 1 +a 9511 15112 1 +a 9512 15111 1 +a 9513 15110 1 +a 9514 15109 1 +a 9515 15108 1 +a 9516 15107 1 +a 9517 15106 1 +a 9518 15105 1 +a 9519 15104 1 +a 9520 15103 1 +a 9521 15102 1 +a 9522 15101 1 +a 9523 15100 1 +a 9524 15099 1 +a 9525 15098 1 +a 9526 15097 1 +a 9527 15096 1 +a 9528 15095 1 +a 9529 15094 1 +a 9530 15093 1 +a 9531 15092 1 +a 9532 15091 1 +a 9533 15090 1 +a 9534 15089 1 +a 9535 15088 1 +a 9536 15087 1 +a 9537 15086 1 +a 9538 15085 1 +a 9539 15084 1 +a 9540 15083 1 +a 9541 15082 1 +a 9542 15081 1 +a 9543 15080 1 +a 9544 15079 1 +a 9545 15078 1 +a 9546 15077 1 +a 9547 15076 1 +a 9548 15075 1 +a 9549 15074 1 +a 9550 15073 1 +a 9551 15072 1 +a 9552 15071 1 +a 9553 15070 1 +a 9554 15069 1 +a 9555 15068 1 +a 9556 15067 1 +a 9557 15066 1 +a 9558 15065 1 +a 9559 15064 1 +a 9560 15063 1 +a 9561 15062 1 +a 9562 15061 1 +a 9563 15060 1 +a 9564 15059 1 +a 9565 15058 1 +a 9566 15057 1 +a 9567 15056 1 +a 9568 15055 1 +a 9569 15054 1 +a 9570 15053 1 +a 9571 15052 1 +a 9572 15051 1 +a 9573 15050 1 +a 9574 15049 1 +a 9575 15048 1 +a 9576 15047 1 +a 9577 15046 1 +a 9578 15045 1 +a 9579 15044 1 +a 9580 15043 1 +a 9581 15042 1 +a 9582 15041 1 +a 9583 15040 1 +a 9584 15039 1 +a 9585 15038 1 +a 9586 15037 1 +a 9587 15036 1 +a 9588 15035 1 +a 9589 15034 1 +a 9590 15033 1 +a 9591 15032 1 +a 9592 15031 1 +a 9593 15030 1 +a 9594 15029 1 +a 9595 15028 1 +a 9596 15027 1 +a 9597 15026 1 +a 9598 15025 1 +a 9599 15024 1 +a 9600 15023 1 +a 9601 15022 1 +a 9602 15021 1 +a 9603 15020 1 +a 9604 15019 1 +a 9605 15018 1 +a 9606 15017 1 +a 9607 15016 1 +a 9608 15015 1 +a 9609 15014 1 +a 9610 15013 1 +a 9611 15012 1 +a 9612 15011 1 +a 9613 15010 1 +a 9614 15009 1 +a 9615 15008 1 +a 9616 15007 1 +a 9617 15006 1 +a 9618 15005 1 +a 9619 15004 1 +a 9620 15003 1 +a 9621 15002 1 +a 9622 15001 1 +a 9623 15000 1 +a 9624 14999 1 +a 9625 14998 1 +a 9626 14997 1 +a 9627 14996 1 +a 9628 14995 1 +a 9629 14994 1 +a 9630 14993 1 +a 9631 14992 1 +a 9632 14991 1 +a 9633 14990 1 +a 9634 14989 1 +a 9635 14988 1 +a 9636 14987 1 +a 9637 14986 1 +a 9638 14985 1 +a 9639 14984 1 +a 9640 14983 1 +a 9641 14982 1 +a 9642 14981 1 +a 9643 14980 1 +a 9644 14979 1 +a 9645 14978 1 +a 9646 14977 1 +a 9647 14976 1 +a 9648 14975 1 +a 9649 14974 1 +a 9650 14973 1 +a 9651 14972 1 +a 9652 14971 1 +a 9653 14970 1 +a 9654 14969 1 +a 9655 14968 1 +a 9656 14967 1 +a 9657 14966 1 +a 9658 14965 1 +a 9659 14964 1 +a 9660 14963 1 +a 9661 14962 1 +a 9662 14961 1 +a 9663 14960 1 +a 9664 14959 1 +a 9665 14958 1 +a 9666 14957 1 +a 9667 14956 1 +a 9668 14955 1 +a 9669 14954 1 +a 9670 14953 1 +a 9671 14952 1 +a 9672 14951 1 +a 9673 14950 1 +a 9674 14949 1 +a 9675 14948 1 +a 9676 14947 1 +a 9677 14946 1 +a 9678 14945 1 +a 9679 14944 1 +a 9680 14943 1 +a 9681 14942 1 +a 9682 14941 1 +a 9683 14940 1 +a 9684 14939 1 +a 9685 14938 1 +a 9686 14937 1 +a 9687 14936 1 +a 9688 14935 1 +a 9689 14934 1 +a 9690 14933 1 +a 9691 14932 1 +a 9692 14931 1 +a 9693 14930 1 +a 9694 14929 1 +a 9695 14928 1 +a 9696 14927 1 +a 9697 14926 1 +a 9698 14925 1 +a 9699 14924 1 +a 9700 14923 1 +a 9701 14922 1 +a 9702 14921 1 +a 9703 14920 1 +a 9704 14919 1 +a 9705 14918 1 +a 9706 14917 1 +a 9707 14916 1 +a 9708 14915 1 +a 9709 14914 1 +a 9710 14913 1 +a 9711 14912 1 +a 9712 14911 1 +a 9713 14910 1 +a 9714 14909 1 +a 9715 14908 1 +a 9716 14907 1 +a 9717 14906 1 +a 9718 14905 1 +a 9719 14904 1 +a 9720 14903 1 +a 9721 14902 1 +a 9722 14901 1 +a 9723 14900 1 +a 9724 14899 1 +a 9725 14898 1 +a 9726 14897 1 +a 9727 14896 1 +a 9728 14895 1 +a 9729 14894 1 +a 9730 14893 1 +a 9731 14892 1 +a 9732 14891 1 +a 9733 14890 1 +a 9734 14889 1 +a 9735 14888 1 +a 9736 14887 1 +a 9737 14886 1 +a 9738 14885 1 +a 9739 14884 1 +a 9740 14883 1 +a 9741 14882 1 +a 9742 14881 1 +a 9743 14880 1 +a 9744 14879 1 +a 9745 14878 1 +a 9746 14877 1 +a 9747 14876 1 +a 9748 14875 1 +a 9749 14874 1 +a 9750 14873 1 +a 9751 14872 1 +a 9752 14871 1 +a 9753 14870 1 +a 9754 14869 1 +a 9755 14868 1 +a 9756 14867 1 +a 9757 14866 1 +a 9758 14865 1 +a 9759 14864 1 +a 9760 14863 1 +a 9761 14862 1 +a 9762 14861 1 +a 9763 14860 1 +a 9764 14859 1 +a 9765 14858 1 +a 9766 14857 1 +a 9767 14856 1 +a 9768 14855 1 +a 9769 14854 1 +a 9770 14853 1 +a 9771 14852 1 +a 9772 14851 1 +a 9773 14850 1 +a 9774 14849 1 +a 9775 14848 1 +a 9776 14847 1 +a 9777 14846 1 +a 9778 14845 1 +a 9779 14844 1 +a 9780 14843 1 +a 9781 14842 1 +a 9782 14841 1 +a 9783 14840 1 +a 9784 14839 1 +a 9785 14838 1 +a 9786 14837 1 +a 9787 14836 1 +a 9788 14835 1 +a 9789 14834 1 +a 9790 14833 1 +a 9791 14832 1 +a 9792 14831 1 +a 9793 14830 1 +a 9794 14829 1 +a 9795 14828 1 +a 9796 14827 1 +a 9797 14826 1 +a 9798 14825 1 +a 9799 14824 1 +a 9800 14823 1 +a 9801 14822 1 +a 9802 14821 1 +a 9803 14820 1 +a 9804 14819 1 +a 9805 14818 1 +a 9806 14817 1 +a 9807 14816 1 +a 9808 14815 1 +a 9809 14814 1 +a 9810 14813 1 +a 9811 14812 1 +a 9812 14811 1 +a 9813 14810 1 +a 9814 14809 1 +a 9815 14808 1 +a 9816 14807 1 +a 9817 14806 1 +a 9818 14805 1 +a 9819 14804 1 +a 9820 14803 1 +a 9821 14802 1 +a 9822 14801 1 +a 9823 14800 1 +a 9824 14799 1 +a 9825 14798 1 +a 9826 14797 1 +a 9827 14796 1 +a 9828 14795 1 +a 9829 14794 1 +a 9830 14793 1 +a 9831 14792 1 +a 9832 14791 1 +a 9833 14790 1 +a 9834 14789 1 +a 9835 14788 1 +a 9836 14787 1 +a 9837 14786 1 +a 9838 14785 1 +a 9839 14784 1 +a 9840 14783 1 +a 9841 14782 1 +a 9842 14781 1 +a 9843 14780 1 +a 9844 14779 1 +a 9845 14778 1 +a 9846 14777 1 +a 9847 14776 1 +a 9848 14775 1 +a 9849 14774 1 +a 9850 14773 1 +a 9851 14772 1 +a 9852 14771 1 +a 9853 14770 1 +a 9854 14769 1 +a 9855 14768 1 +a 9856 14767 1 +a 9857 14766 1 +a 9858 14765 1 +a 9859 14764 1 +a 9860 14763 1 +a 9861 14762 1 +a 9862 14761 1 +a 9863 14760 1 +a 9864 14759 1 +a 9865 14758 1 +a 9866 14757 1 +a 9867 14756 1 +a 9868 14755 1 +a 9869 14754 1 +a 9870 14753 1 +a 9871 14752 1 +a 9872 14751 1 +a 9873 14750 1 +a 9874 14749 1 +a 9875 14748 1 +a 9876 14747 1 +a 9877 14746 1 +a 9878 14745 1 +a 9879 14744 1 +a 9880 14743 1 +a 9881 14742 1 +a 9882 14741 1 +a 9883 14740 1 +a 9884 14739 1 +a 9885 14738 1 +a 9886 14737 1 +a 9887 14736 1 +a 9888 14735 1 +a 9889 14734 1 +a 9890 14733 1 +a 9891 14732 1 +a 9892 14731 1 +a 9893 14730 1 +a 9894 14729 1 +a 9895 14728 1 +a 9896 14727 1 +a 9897 14726 1 +a 9898 14725 1 +a 9899 14724 1 +a 9900 14723 1 +a 9901 14722 1 +a 9902 14721 1 +a 9903 14720 1 +a 9904 14719 1 +a 9905 14718 1 +a 9906 14717 1 +a 9907 14716 1 +a 9908 14715 1 +a 9909 14714 1 +a 9910 14713 1 +a 9911 14712 1 +a 9912 14711 1 +a 9913 14710 1 +a 9914 14709 1 +a 9915 14708 1 +a 9916 14707 1 +a 9917 14706 1 +a 9918 14705 1 +a 9919 14704 1 +a 9920 14703 1 +a 9921 14702 1 +a 9922 14701 1 +a 9923 14700 1 +a 9924 14699 1 +a 9925 14698 1 +a 9926 14697 1 +a 9927 14696 1 +a 9928 14695 1 +a 9929 14694 1 +a 9930 14693 1 +a 9931 14692 1 +a 9932 14691 1 +a 9933 14690 1 +a 9934 14689 1 +a 9935 14688 1 +a 9936 14687 1 +a 9937 14686 1 +a 9938 14685 1 +a 9939 14684 1 +a 9940 14683 1 +a 9941 14682 1 +a 9942 14681 1 +a 9943 14680 1 +a 9944 14679 1 +a 9945 14678 1 +a 9946 14677 1 +a 9947 14676 1 +a 9948 14675 1 +a 9949 14674 1 +a 9950 14673 1 +a 9951 14672 1 +a 9952 14671 1 +a 9953 14670 1 +a 9954 14669 1 +a 9955 14668 1 +a 9956 14667 1 +a 9957 14666 1 +a 9958 14665 1 +a 9959 14664 1 +a 9960 14663 1 +a 9961 14662 1 +a 9962 14661 1 +a 9963 14660 1 +a 9964 14659 1 +a 9965 14658 1 +a 9966 14657 1 +a 9967 14656 1 +a 9968 14655 1 +a 9969 14654 1 +a 9970 14653 1 +a 9971 14652 1 +a 9972 14651 1 +a 9973 14650 1 +a 9974 14649 1 +a 9975 14648 1 +a 9976 14647 1 +a 9977 14646 1 +a 9978 14645 1 +a 9979 14644 1 +a 9980 14643 1 +a 9981 14642 1 +a 9982 14641 1 +a 9983 14640 1 +a 9984 14639 1 +a 9985 14638 1 +a 9986 14637 1 +a 9987 14636 1 +a 9988 14635 1 +a 9989 14634 1 +a 9990 14633 1 +a 9991 14632 1 +a 9992 14631 1 +a 9993 14630 1 +a 9994 14629 1 +a 9995 14628 1 +a 9996 14627 1 +a 9997 14626 1 +a 9998 14625 1 +a 9999 14624 1 +a 10000 14623 1 +a 10001 14622 1 +a 10002 14621 1 +a 10003 14620 1 +a 10004 14619 1 +a 10005 14618 1 +a 10006 14617 1 +a 10007 14616 1 +a 10008 14615 1 +a 10009 14614 1 +a 10010 14613 1 +a 10011 14612 1 +a 10012 14611 1 +a 10013 14610 1 +a 10014 14609 1 +a 10015 14608 1 +a 10016 14607 1 +a 10017 14606 1 +a 10018 14605 1 +a 10019 14604 1 +a 10020 14603 1 +a 10021 14602 1 +a 10022 14601 1 +a 10023 14600 1 +a 10024 14599 1 +a 10025 14598 1 +a 10026 14597 1 +a 10027 14596 1 +a 10028 14595 1 +a 10029 14594 1 +a 10030 14593 1 +a 10031 14592 1 +a 10032 14591 1 +a 10033 14590 1 +a 10034 14589 1 +a 10035 14588 1 +a 10036 14587 1 +a 10037 14586 1 +a 10038 14585 1 +a 10039 14584 1 +a 10040 14583 1 +a 10041 14582 1 +a 10042 14581 1 +a 10043 14580 1 +a 10044 14579 1 +a 10045 14578 1 +a 10046 14577 1 +a 10047 14576 1 +a 10048 14575 1 +a 10049 14574 1 +a 10050 14573 1 +a 10051 14572 1 +a 10052 14571 1 +a 10053 14570 1 +a 10054 14569 1 +a 10055 14568 1 +a 10056 14567 1 +a 10057 14566 1 +a 10058 14565 1 +a 10059 14564 1 +a 10060 14563 1 +a 10061 14562 1 +a 10062 14561 1 +a 10063 14560 1 +a 10064 14559 1 +a 10065 14558 1 +a 10066 14557 1 +a 10067 14556 1 +a 10068 14555 1 +a 10069 14554 1 +a 10070 14553 1 +a 10071 14552 1 +a 10072 14551 1 +a 10073 14550 1 +a 10074 14549 1 +a 10075 14548 1 +a 10076 14547 1 +a 10077 14546 1 +a 10078 14545 1 +a 10079 14544 1 +a 10080 14543 1 +a 10081 14542 1 +a 10082 14541 1 +a 10083 14540 1 +a 10084 14539 1 +a 10085 14538 1 +a 10086 14537 1 +a 10087 14536 1 +a 10088 14535 1 +a 10089 14534 1 +a 10090 14533 1 +a 10091 14532 1 +a 10092 14531 1 +a 10093 14530 1 +a 10094 14529 1 +a 10095 14528 1 +a 10096 14527 1 +a 10097 14526 1 +a 10098 14525 1 +a 10099 14524 1 +a 10100 14523 1 +a 10101 14522 1 +a 10102 14521 1 +a 10103 14520 1 +a 10104 14519 1 +a 10105 14518 1 +a 10106 14517 1 +a 10107 14516 1 +a 10108 14515 1 +a 10109 14514 1 +a 10110 14513 1 +a 10111 14512 1 +a 10112 14511 1 +a 10113 14510 1 +a 10114 14509 1 +a 10115 14508 1 +a 10116 14507 1 +a 10117 14506 1 +a 10118 14505 1 +a 10119 14504 1 +a 10120 14503 1 +a 10121 14502 1 +a 10122 14501 1 +a 10123 14500 1 +a 10124 14499 1 +a 10125 14498 1 +a 10126 14497 1 +a 10127 14496 1 +a 10128 14495 1 +a 10129 14494 1 +a 10130 14493 1 +a 10131 14492 1 +a 10132 14491 1 +a 10133 14490 1 +a 10134 14489 1 +a 10135 14488 1 +a 10136 14487 1 +a 10137 14486 1 +a 10138 14485 1 +a 10139 14484 1 +a 10140 14483 1 +a 10141 14482 1 +a 10142 14481 1 +a 10143 14480 1 +a 10144 14479 1 +a 10145 14478 1 +a 10146 14477 1 +a 10147 14476 1 +a 10148 14475 1 +a 10149 14474 1 +a 10150 14473 1 +a 10151 14472 1 +a 10152 14471 1 +a 10153 14470 1 +a 10154 14469 1 +a 10155 14468 1 +a 10156 14467 1 +a 10157 14466 1 +a 10158 14465 1 +a 10159 14464 1 +a 10160 14463 1 +a 10161 14462 1 +a 10162 14461 1 +a 10163 14460 1 +a 10164 14459 1 +a 10165 14458 1 +a 10166 14457 1 +a 10167 14456 1 +a 10168 14455 1 +a 10169 14454 1 +a 10170 14453 1 +a 10171 14452 1 +a 10172 14451 1 +a 10173 14450 1 +a 10174 14449 1 +a 10175 14448 1 +a 10176 14447 1 +a 10177 14446 1 +a 10178 14445 1 +a 10179 14444 1 +a 10180 14443 1 +a 10181 14442 1 +a 10182 14441 1 +a 10183 14440 1 +a 10184 14439 1 +a 10185 14438 1 +a 10186 14437 1 +a 10187 14436 1 +a 10188 14435 1 +a 10189 14434 1 +a 10190 14433 1 +a 10191 14432 1 +a 10192 14431 1 +a 10193 14430 1 +a 10194 14429 1 +a 10195 14428 1 +a 10196 14427 1 +a 10197 14426 1 +a 10198 14425 1 +a 10199 14424 1 +a 10200 14423 1 +a 10201 14422 1 +a 10202 14421 1 +a 10203 14420 1 +a 10204 14419 1 +a 10205 14418 1 +a 10206 14417 1 +a 10207 14416 1 +a 10208 14415 1 +a 10209 14414 1 +a 10210 14413 1 +a 10211 14412 1 +a 10212 14411 1 +a 10213 14410 1 +a 10214 14409 1 +a 10215 14408 1 +a 10216 14407 1 +a 10217 14406 1 +a 10218 14405 1 +a 10219 14404 1 +a 10220 14403 1 +a 10221 14402 1 +a 10222 14401 1 +a 10223 14400 1 +a 10224 14399 1 +a 10225 14398 1 +a 10226 14397 1 +a 10227 14396 1 +a 10228 14395 1 +a 10229 14394 1 +a 10230 14393 1 +a 10231 14392 1 +a 10232 14391 1 +a 10233 14390 1 +a 10234 14389 1 +a 10235 14388 1 +a 10236 14387 1 +a 10237 14386 1 +a 10238 14385 1 +a 10239 14384 1 +a 10240 14383 1 +a 10241 14382 1 +a 10242 14381 1 +a 10243 14380 1 +a 10244 14379 1 +a 10245 14378 1 +a 10246 14377 1 +a 10247 14376 1 +a 10248 14375 1 +a 10249 14374 1 +a 10250 14373 1 +a 10251 14372 1 +a 10252 14371 1 +a 10253 14370 1 +a 10254 14369 1 +a 10255 14368 1 +a 10256 14367 1 +a 10257 14366 1 +a 10258 14365 1 +a 10259 14364 1 +a 10260 14363 1 +a 10261 14362 1 +a 10262 14361 1 +a 10263 14360 1 +a 10264 14359 1 +a 10265 14358 1 +a 10266 14357 1 +a 10267 14356 1 +a 10268 14355 1 +a 10269 14354 1 +a 10270 14353 1 +a 10271 14352 1 +a 10272 14351 1 +a 10273 14350 1 +a 10274 14349 1 +a 10275 14348 1 +a 10276 14347 1 +a 10277 14346 1 +a 10278 14345 1 +a 10279 14344 1 +a 10280 14343 1 +a 10281 14342 1 +a 10282 14341 1 +a 10283 14340 1 +a 10284 14339 1 +a 10285 14338 1 +a 10286 14337 1 +a 10287 14336 1 +a 10288 14335 1 +a 10289 14334 1 +a 10290 14333 1 +a 10291 14332 1 +a 10292 14331 1 +a 10293 14330 1 +a 10294 14329 1 +a 10295 14328 1 +a 10296 14327 1 +a 10297 14326 1 +a 10298 14325 1 +a 10299 14324 1 +a 10300 14323 1 +a 10301 14322 1 +a 10302 14321 1 +a 10303 14320 1 +a 10304 14319 1 +a 10305 14318 1 +a 10306 14317 1 +a 10307 14316 1 +a 10308 14315 1 +a 10309 14314 1 +a 10310 14313 1 +a 10311 14312 1 +a 10312 14311 1 +a 10313 14310 1 +a 10314 14309 1 +a 10315 14308 1 +a 10316 14307 1 +a 10317 14306 1 +a 10318 14305 1 +a 10319 14304 1 +a 10320 14303 1 +a 10321 14302 1 +a 10322 14301 1 +a 10323 14300 1 +a 10324 14299 1 +a 10325 14298 1 +a 10326 14297 1 +a 10327 14296 1 +a 10328 14295 1 +a 10329 14294 1 +a 10330 14293 1 +a 10331 14292 1 +a 10332 14291 1 +a 10333 14290 1 +a 10334 14289 1 +a 10335 14288 1 +a 10336 14287 1 +a 10337 14286 1 +a 10338 14285 1 +a 10339 14284 1 +a 10340 14283 1 +a 10341 14282 1 +a 10342 14281 1 +a 10343 14280 1 +a 10344 14279 1 +a 10345 14278 1 +a 10346 14277 1 +a 10347 14276 1 +a 10348 14275 1 +a 10349 14274 1 +a 10350 14273 1 +a 10351 14272 1 +a 10352 14271 1 +a 10353 14270 1 +a 10354 14269 1 +a 10355 14268 1 +a 10356 14267 1 +a 10357 14266 1 +a 10358 14265 1 +a 10359 14264 1 +a 10360 14263 1 +a 10361 14262 1 +a 10362 14261 1 +a 10363 14260 1 +a 10364 14259 1 +a 10365 14258 1 +a 10366 14257 1 +a 10367 14256 1 +a 10368 14255 1 +a 10369 14254 1 +a 10370 14253 1 +a 10371 14252 1 +a 10372 14251 1 +a 10373 14250 1 +a 10374 14249 1 +a 10375 14248 1 +a 10376 14247 1 +a 10377 14246 1 +a 10378 14245 1 +a 10379 14244 1 +a 10380 14243 1 +a 10381 14242 1 +a 10382 14241 1 +a 10383 14240 1 +a 10384 14239 1 +a 10385 14238 1 +a 10386 14237 1 +a 10387 14236 1 +a 10388 14235 1 +a 10389 14234 1 +a 10390 14233 1 +a 10391 14232 1 +a 10392 14231 1 +a 10393 14230 1 +a 10394 14229 1 +a 10395 14228 1 +a 10396 14227 1 +a 10397 14226 1 +a 10398 14225 1 +a 10399 14224 1 +a 10400 14223 1 +a 10401 14222 1 +a 10402 14221 1 +a 10403 14220 1 +a 10404 14219 1 +a 10405 14218 1 +a 10406 14217 1 +a 10407 14216 1 +a 10408 14215 1 +a 10409 14214 1 +a 10410 14213 1 +a 10411 14212 1 +a 10412 14211 1 +a 10413 14210 1 +a 10414 14209 1 +a 10415 14208 1 +a 10416 14207 1 +a 10417 14206 1 +a 10418 14205 1 +a 10419 14204 1 +a 10420 14203 1 +a 10421 14202 1 +a 10422 14201 1 +a 10423 14200 1 +a 10424 14199 1 +a 10425 14198 1 +a 10426 14197 1 +a 10427 14196 1 +a 10428 14195 1 +a 10429 14194 1 +a 10430 14193 1 +a 10431 14192 1 +a 10432 14191 1 +a 10433 14190 1 +a 10434 14189 1 +a 10435 14188 1 +a 10436 14187 1 +a 10437 14186 1 +a 10438 14185 1 +a 10439 14184 1 +a 10440 14183 1 +a 10441 14182 1 +a 10442 14181 1 +a 10443 14180 1 +a 10444 14179 1 +a 10445 14178 1 +a 10446 14177 1 +a 10447 14176 1 +a 10448 14175 1 +a 10449 14174 1 +a 10450 14173 1 +a 10451 14172 1 +a 10452 14171 1 +a 10453 14170 1 +a 10454 14169 1 +a 10455 14168 1 +a 10456 14167 1 +a 10457 14166 1 +a 10458 14165 1 +a 10459 14164 1 +a 10460 14163 1 +a 10461 14162 1 +a 10462 14161 1 +a 10463 14160 1 +a 10464 14159 1 +a 10465 14158 1 +a 10466 14157 1 +a 10467 14156 1 +a 10468 14155 1 +a 10469 14154 1 +a 10470 14153 1 +a 10471 14152 1 +a 10472 14151 1 +a 10473 14150 1 +a 10474 14149 1 +a 10475 14148 1 +a 10476 14147 1 +a 10477 14146 1 +a 10478 14145 1 +a 10479 14144 1 +a 10480 14143 1 +a 10481 14142 1 +a 10482 14141 1 +a 10483 14140 1 +a 10484 14139 1 +a 10485 14138 1 +a 10486 14137 1 +a 10487 14136 1 +a 10488 14135 1 +a 10489 14134 1 +a 10490 14133 1 +a 10491 14132 1 +a 10492 14131 1 +a 10493 14130 1 +a 10494 14129 1 +a 10495 14128 1 +a 10496 14127 1 +a 10497 14126 1 +a 10498 14125 1 +a 10499 14124 1 +a 10500 14123 1 +a 10501 14122 1 +a 10502 14121 1 +a 10503 14120 1 +a 10504 14119 1 +a 10505 14118 1 +a 10506 14117 1 +a 10507 14116 1 +a 10508 14115 1 +a 10509 14114 1 +a 10510 14113 1 +a 10511 14112 1 +a 10512 14111 1 +a 10513 14110 1 +a 10514 14109 1 +a 10515 14108 1 +a 10516 14107 1 +a 10517 14106 1 +a 10518 14105 1 +a 10519 14104 1 +a 10520 14103 1 +a 10521 14102 1 +a 10522 14101 1 +a 10523 14100 1 +a 10524 14099 1 +a 10525 14098 1 +a 10526 14097 1 +a 10527 14096 1 +a 10528 14095 1 +a 10529 14094 1 +a 10530 14093 1 +a 10531 14092 1 +a 10532 14091 1 +a 10533 14090 1 +a 10534 14089 1 +a 10535 14088 1 +a 10536 14087 1 +a 10537 14086 1 +a 10538 14085 1 +a 10539 14084 1 +a 10540 14083 1 +a 10541 14082 1 +a 10542 14081 1 +a 10543 14080 1 +a 10544 14079 1 +a 10545 14078 1 +a 10546 14077 1 +a 10547 14076 1 +a 10548 14075 1 +a 10549 14074 1 +a 10550 14073 1 +a 10551 14072 1 +a 10552 14071 1 +a 10553 14070 1 +a 10554 14069 1 +a 10555 14068 1 +a 10556 14067 1 +a 10557 14066 1 +a 10558 14065 1 +a 10559 14064 1 +a 10560 14063 1 +a 10561 14062 1 +a 10562 14061 1 +a 10563 14060 1 +a 10564 14059 1 +a 10565 14058 1 +a 10566 14057 1 +a 10567 14056 1 +a 10568 14055 1 +a 10569 14054 1 +a 10570 14053 1 +a 10571 14052 1 +a 10572 14051 1 +a 10573 14050 1 +a 10574 14049 1 +a 10575 14048 1 +a 10576 14047 1 +a 10577 14046 1 +a 10578 14045 1 +a 10579 14044 1 +a 10580 14043 1 +a 10581 14042 1 +a 10582 14041 1 +a 10583 14040 1 +a 10584 14039 1 +a 10585 14038 1 +a 10586 14037 1 +a 10587 14036 1 +a 10588 14035 1 +a 10589 14034 1 +a 10590 14033 1 +a 10591 14032 1 +a 10592 14031 1 +a 10593 14030 1 +a 10594 14029 1 +a 10595 14028 1 +a 10596 14027 1 +a 10597 14026 1 +a 10598 14025 1 +a 10599 14024 1 +a 10600 14023 1 +a 10601 14022 1 +a 10602 14021 1 +a 10603 14020 1 +a 10604 14019 1 +a 10605 14018 1 +a 10606 14017 1 +a 10607 14016 1 +a 10608 14015 1 +a 10609 14014 1 +a 10610 14013 1 +a 10611 14012 1 +a 10612 14011 1 +a 10613 14010 1 +a 10614 14009 1 +a 10615 14008 1 +a 10616 14007 1 +a 10617 14006 1 +a 10618 14005 1 +a 10619 14004 1 +a 10620 14003 1 +a 10621 14002 1 +a 10622 14001 1 +a 10623 14000 1 +a 10624 13999 1 +a 10625 13998 1 +a 10626 13997 1 +a 10627 13996 1 +a 10628 13995 1 +a 10629 13994 1 +a 10630 13993 1 +a 10631 13992 1 +a 10632 13991 1 +a 10633 13990 1 +a 10634 13989 1 +a 10635 13988 1 +a 10636 13987 1 +a 10637 13986 1 +a 10638 13985 1 +a 10639 13984 1 +a 10640 13983 1 +a 10641 13982 1 +a 10642 13981 1 +a 10643 13980 1 +a 10644 13979 1 +a 10645 13978 1 +a 10646 13977 1 +a 10647 13976 1 +a 10648 13975 1 +a 10649 13974 1 +a 10650 13973 1 +a 10651 13972 1 +a 10652 13971 1 +a 10653 13970 1 +a 10654 13969 1 +a 10655 13968 1 +a 10656 13967 1 +a 10657 13966 1 +a 10658 13965 1 +a 10659 13964 1 +a 10660 13963 1 +a 10661 13962 1 +a 10662 13961 1 +a 10663 13960 1 +a 10664 13959 1 +a 10665 13958 1 +a 10666 13957 1 +a 10667 13956 1 +a 10668 13955 1 +a 10669 13954 1 +a 10670 13953 1 +a 10671 13952 1 +a 10672 13951 1 +a 10673 13950 1 +a 10674 13949 1 +a 10675 13948 1 +a 10676 13947 1 +a 10677 13946 1 +a 10678 13945 1 +a 10679 13944 1 +a 10680 13943 1 +a 10681 13942 1 +a 10682 13941 1 +a 10683 13940 1 +a 10684 13939 1 +a 10685 13938 1 +a 10686 13937 1 +a 10687 13936 1 +a 10688 13935 1 +a 10689 13934 1 +a 10690 13933 1 +a 10691 13932 1 +a 10692 13931 1 +a 10693 13930 1 +a 10694 13929 1 +a 10695 13928 1 +a 10696 13927 1 +a 10697 13926 1 +a 10698 13925 1 +a 10699 13924 1 +a 10700 13923 1 +a 10701 13922 1 +a 10702 13921 1 +a 10703 13920 1 +a 10704 13919 1 +a 10705 13918 1 +a 10706 13917 1 +a 10707 13916 1 +a 10708 13915 1 +a 10709 13914 1 +a 10710 13913 1 +a 10711 13912 1 +a 10712 13911 1 +a 10713 13910 1 +a 10714 13909 1 +a 10715 13908 1 +a 10716 13907 1 +a 10717 13906 1 +a 10718 13905 1 +a 10719 13904 1 +a 10720 13903 1 +a 10721 13902 1 +a 10722 13901 1 +a 10723 13900 1 +a 10724 13899 1 +a 10725 13898 1 +a 10726 13897 1 +a 10727 13896 1 +a 10728 13895 1 +a 10729 13894 1 +a 10730 13893 1 +a 10731 13892 1 +a 10732 13891 1 +a 10733 13890 1 +a 10734 13889 1 +a 10735 13888 1 +a 10736 13887 1 +a 10737 13886 1 +a 10738 13885 1 +a 10739 13884 1 +a 10740 13883 1 +a 10741 13882 1 +a 10742 13881 1 +a 10743 13880 1 +a 10744 13879 1 +a 10745 13878 1 +a 10746 13877 1 +a 10747 13876 1 +a 10748 13875 1 +a 10749 13874 1 +a 10750 13873 1 +a 10751 13872 1 +a 10752 13871 1 +a 10753 13870 1 +a 10754 13869 1 +a 10755 13868 1 +a 10756 13867 1 +a 10757 13866 1 +a 10758 13865 1 +a 10759 13864 1 +a 10760 13863 1 +a 10761 13862 1 +a 10762 13861 1 +a 10763 13860 1 +a 10764 13859 1 +a 10765 13858 1 +a 10766 13857 1 +a 10767 13856 1 +a 10768 13855 1 +a 10769 13854 1 +a 10770 13853 1 +a 10771 13852 1 +a 10772 13851 1 +a 10773 13850 1 +a 10774 13849 1 +a 10775 13848 1 +a 10776 13847 1 +a 10777 13846 1 +a 10778 13845 1 +a 10779 13844 1 +a 10780 13843 1 +a 10781 13842 1 +a 10782 13841 1 +a 10783 13840 1 +a 10784 13839 1 +a 10785 13838 1 +a 10786 13837 1 +a 10787 13836 1 +a 10788 13835 1 +a 10789 13834 1 +a 10790 13833 1 +a 10791 13832 1 +a 10792 13831 1 +a 10793 13830 1 +a 10794 13829 1 +a 10795 13828 1 +a 10796 13827 1 +a 10797 13826 1 +a 10798 13825 1 +a 10799 13824 1 +a 10800 13823 1 +a 10801 13822 1 +a 10802 13821 1 +a 10803 13820 1 +a 10804 13819 1 +a 10805 13818 1 +a 10806 13817 1 +a 10807 13816 1 +a 10808 13815 1 +a 10809 13814 1 +a 10810 13813 1 +a 10811 13812 1 +a 10812 13811 1 +a 10813 13810 1 +a 10814 13809 1 +a 10815 13808 1 +a 10816 13807 1 +a 10817 13806 1 +a 10818 13805 1 +a 10819 13804 1 +a 10820 13803 1 +a 10821 13802 1 +a 10822 13801 1 +a 10823 13800 1 +a 10824 13799 1 +a 10825 13798 1 +a 10826 13797 1 +a 10827 13796 1 +a 10828 13795 1 +a 10829 13794 1 +a 10830 13793 1 +a 10831 13792 1 +a 10832 13791 1 +a 10833 13790 1 +a 10834 13789 1 +a 10835 13788 1 +a 10836 13787 1 +a 10837 13786 1 +a 10838 13785 1 +a 10839 13784 1 +a 10840 13783 1 +a 10841 13782 1 +a 10842 13781 1 +a 10843 13780 1 +a 10844 13779 1 +a 10845 13778 1 +a 10846 13777 1 +a 10847 13776 1 +a 10848 13775 1 +a 10849 13774 1 +a 10850 13773 1 +a 10851 13772 1 +a 10852 13771 1 +a 10853 13770 1 +a 10854 13769 1 +a 10855 13768 1 +a 10856 13767 1 +a 10857 13766 1 +a 10858 13765 1 +a 10859 13764 1 +a 10860 13763 1 +a 10861 13762 1 +a 10862 13761 1 +a 10863 13760 1 +a 10864 13759 1 +a 10865 13758 1 +a 10866 13757 1 +a 10867 13756 1 +a 10868 13755 1 +a 10869 13754 1 +a 10870 13753 1 +a 10871 13752 1 +a 10872 13751 1 +a 10873 13750 1 +a 10874 13749 1 +a 10875 13748 1 +a 10876 13747 1 +a 10877 13746 1 +a 10878 13745 1 +a 10879 13744 1 +a 10880 13743 1 +a 10881 13742 1 +a 10882 13741 1 +a 10883 13740 1 +a 10884 13739 1 +a 10885 13738 1 +a 10886 13737 1 +a 10887 13736 1 +a 10888 13735 1 +a 10889 13734 1 +a 10890 13733 1 +a 10891 13732 1 +a 10892 13731 1 +a 10893 13730 1 +a 10894 13729 1 +a 10895 13728 1 +a 10896 13727 1 +a 10897 13726 1 +a 10898 13725 1 +a 10899 13724 1 +a 10900 13723 1 +a 10901 13722 1 +a 10902 13721 1 +a 10903 13720 1 +a 10904 13719 1 +a 10905 13718 1 +a 10906 13717 1 +a 10907 13716 1 +a 10908 13715 1 +a 10909 13714 1 +a 10910 13713 1 +a 10911 13712 1 +a 10912 13711 1 +a 10913 13710 1 +a 10914 13709 1 +a 10915 13708 1 +a 10916 13707 1 +a 10917 13706 1 +a 10918 13705 1 +a 10919 13704 1 +a 10920 13703 1 +a 10921 13702 1 +a 10922 13701 1 +a 10923 13700 1 +a 10924 13699 1 +a 10925 13698 1 +a 10926 13697 1 +a 10927 13696 1 +a 10928 13695 1 +a 10929 13694 1 +a 10930 13693 1 +a 10931 13692 1 +a 10932 13691 1 +a 10933 13690 1 +a 10934 13689 1 +a 10935 13688 1 +a 10936 13687 1 +a 10937 13686 1 +a 10938 13685 1 +a 10939 13684 1 +a 10940 13683 1 +a 10941 13682 1 +a 10942 13681 1 +a 10943 13680 1 +a 10944 13679 1 +a 10945 13678 1 +a 10946 13677 1 +a 10947 13676 1 +a 10948 13675 1 +a 10949 13674 1 +a 10950 13673 1 +a 10951 13672 1 +a 10952 13671 1 +a 10953 13670 1 +a 10954 13669 1 +a 10955 13668 1 +a 10956 13667 1 +a 10957 13666 1 +a 10958 13665 1 +a 10959 13664 1 +a 10960 13663 1 +a 10961 13662 1 +a 10962 13661 1 +a 10963 13660 1 +a 10964 13659 1 +a 10965 13658 1 +a 10966 13657 1 +a 10967 13656 1 +a 10968 13655 1 +a 10969 13654 1 +a 10970 13653 1 +a 10971 13652 1 +a 10972 13651 1 +a 10973 13650 1 +a 10974 13649 1 +a 10975 13648 1 +a 10976 13647 1 +a 10977 13646 1 +a 10978 13645 1 +a 10979 13644 1 +a 10980 13643 1 +a 10981 13642 1 +a 10982 13641 1 +a 10983 13640 1 +a 10984 13639 1 +a 10985 13638 1 +a 10986 13637 1 +a 10987 13636 1 +a 10988 13635 1 +a 10989 13634 1 +a 10990 13633 1 +a 10991 13632 1 +a 10992 13631 1 +a 10993 13630 1 +a 10994 13629 1 +a 10995 13628 1 +a 10996 13627 1 +a 10997 13626 1 +a 10998 13625 1 +a 10999 13624 1 +a 11000 13623 1 +a 11001 13622 1 +a 11002 13621 1 +a 11003 13620 1 +a 11004 13619 1 +a 11005 13618 1 +a 11006 13617 1 +a 11007 13616 1 +a 11008 13615 1 +a 11009 13614 1 +a 11010 13613 1 +a 11011 13612 1 +a 11012 13611 1 +a 11013 13610 1 +a 11014 13609 1 +a 11015 13608 1 +a 11016 13607 1 +a 11017 13606 1 +a 11018 13605 1 +a 11019 13604 1 +a 11020 13603 1 +a 11021 13602 1 +a 11022 13601 1 +a 11023 13600 1 +a 11024 13599 1 +a 11025 13598 1 +a 11026 13597 1 +a 11027 13596 1 +a 11028 13595 1 +a 11029 13594 1 +a 11030 13593 1 +a 11031 13592 1 +a 11032 13591 1 +a 11033 13590 1 +a 11034 13589 1 +a 11035 13588 1 +a 11036 13587 1 +a 11037 13586 1 +a 11038 13585 1 +a 11039 13584 1 +a 11040 13583 1 +a 11041 13582 1 +a 11042 13581 1 +a 11043 13580 1 +a 11044 13579 1 +a 11045 13578 1 +a 11046 13577 1 +a 11047 13576 1 +a 11048 13575 1 +a 11049 13574 1 +a 11050 13573 1 +a 11051 13572 1 +a 11052 13571 1 +a 11053 13570 1 +a 11054 13569 1 +a 11055 13568 1 +a 11056 13567 1 +a 11057 13566 1 +a 11058 13565 1 +a 11059 13564 1 +a 11060 13563 1 +a 11061 13562 1 +a 11062 13561 1 +a 11063 13560 1 +a 11064 13559 1 +a 11065 13558 1 +a 11066 13557 1 +a 11067 13556 1 +a 11068 13555 1 +a 11069 13554 1 +a 11070 13553 1 +a 11071 13552 1 +a 11072 13551 1 +a 11073 13550 1 +a 11074 13549 1 +a 11075 13548 1 +a 11076 13547 1 +a 11077 13546 1 +a 11078 13545 1 +a 11079 13544 1 +a 11080 13543 1 +a 11081 13542 1 +a 11082 13541 1 +a 11083 13540 1 +a 11084 13539 1 +a 11085 13538 1 +a 11086 13537 1 +a 11087 13536 1 +a 11088 13535 1 +a 11089 13534 1 +a 11090 13533 1 +a 11091 13532 1 +a 11092 13531 1 +a 11093 13530 1 +a 11094 13529 1 +a 11095 13528 1 +a 11096 13527 1 +a 11097 13526 1 +a 11098 13525 1 +a 11099 13524 1 +a 11100 13523 1 +a 11101 13522 1 +a 11102 13521 1 +a 11103 13520 1 +a 11104 13519 1 +a 11105 13518 1 +a 11106 13517 1 +a 11107 13516 1 +a 11108 13515 1 +a 11109 13514 1 +a 11110 13513 1 +a 11111 13512 1 +a 11112 13511 1 +a 11113 13510 1 +a 11114 13509 1 +a 11115 13508 1 +a 11116 13507 1 +a 11117 13506 1 +a 11118 13505 1 +a 11119 13504 1 +a 11120 13503 1 +a 11121 13502 1 +a 11122 13501 1 +a 11123 13500 1 +a 11124 13499 1 +a 11125 13498 1 +a 11126 13497 1 +a 11127 13496 1 +a 11128 13495 1 +a 11129 13494 1 +a 11130 13493 1 +a 11131 13492 1 +a 11132 13491 1 +a 11133 13490 1 +a 11134 13489 1 +a 11135 13488 1 +a 11136 13487 1 +a 11137 13486 1 +a 11138 13485 1 +a 11139 13484 1 +a 11140 13483 1 +a 11141 13482 1 +a 11142 13481 1 +a 11143 13480 1 +a 11144 13479 1 +a 11145 13478 1 +a 11146 13477 1 +a 11147 13476 1 +a 11148 13475 1 +a 11149 13474 1 +a 11150 13473 1 +a 11151 13472 1 +a 11152 13471 1 +a 11153 13470 1 +a 11154 13469 1 +a 11155 13468 1 +a 11156 13467 1 +a 11157 13466 1 +a 11158 13465 1 +a 11159 13464 1 +a 11160 13463 1 +a 11161 13462 1 +a 11162 13461 1 +a 11163 13460 1 +a 11164 13459 1 +a 11165 13458 1 +a 11166 13457 1 +a 11167 13456 1 +a 11168 13455 1 +a 11169 13454 1 +a 11170 13453 1 +a 11171 13452 1 +a 11172 13451 1 +a 11173 13450 1 +a 11174 13449 1 +a 11175 13448 1 +a 11176 13447 1 +a 11177 13446 1 +a 11178 13445 1 +a 11179 13444 1 +a 11180 13443 1 +a 11181 13442 1 +a 11182 13441 1 +a 11183 13440 1 +a 11184 13439 1 +a 11185 13438 1 +a 11186 13437 1 +a 11187 13436 1 +a 11188 13435 1 +a 11189 13434 1 +a 11190 13433 1 +a 11191 13432 1 +a 11192 13431 1 +a 11193 13430 1 +a 11194 13429 1 +a 11195 13428 1 +a 11196 13427 1 +a 11197 13426 1 +a 11198 13425 1 +a 11199 13424 1 +a 11200 13423 1 +a 11201 13422 1 +a 11202 13421 1 +a 11203 13420 1 +a 11204 13419 1 +a 11205 13418 1 +a 11206 13417 1 +a 11207 13416 1 +a 11208 13415 1 +a 11209 13414 1 +a 11210 13413 1 +a 11211 13412 1 +a 11212 13411 1 +a 11213 13410 1 +a 11214 13409 1 +a 11215 13408 1 +a 11216 13407 1 +a 11217 13406 1 +a 11218 13405 1 +a 11219 13404 1 +a 11220 13403 1 +a 11221 13402 1 +a 11222 13401 1 +a 11223 13400 1 +a 11224 13399 1 +a 11225 13398 1 +a 11226 13397 1 +a 11227 13396 1 +a 11228 13395 1 +a 11229 13394 1 +a 11230 13393 1 +a 11231 13392 1 +a 11232 13391 1 +a 11233 13390 1 +a 11234 13389 1 +a 11235 13388 1 +a 11236 13387 1 +a 11237 13386 1 +a 11238 13385 1 +a 11239 13384 1 +a 11240 13383 1 +a 11241 13382 1 +a 11242 13381 1 +a 11243 13380 1 +a 11244 13379 1 +a 11245 13378 1 +a 11246 13377 1 +a 11247 13376 1 +a 11248 13375 1 +a 11249 13374 1 +a 11250 13373 1 +a 11251 13372 1 +a 11252 13371 1 +a 11253 13370 1 +a 11254 13369 1 +a 11255 13368 1 +a 11256 13367 1 +a 11257 13366 1 +a 11258 13365 1 +a 11259 13364 1 +a 11260 13363 1 +a 11261 13362 1 +a 11262 13361 1 +a 11263 13360 1 +a 11264 13359 1 +a 11265 13358 1 +a 11266 13357 1 +a 11267 13356 1 +a 11268 13355 1 +a 11269 13354 1 +a 11270 13353 1 +a 11271 13352 1 +a 11272 13351 1 +a 11273 13350 1 +a 11274 13349 1 +a 11275 13348 1 +a 11276 13347 1 +a 11277 13346 1 +a 11278 13345 1 +a 11279 13344 1 +a 11280 13343 1 +a 11281 13342 1 +a 11282 13341 1 +a 11283 13340 1 +a 11284 13339 1 +a 11285 13338 1 +a 11286 13337 1 +a 11287 13336 1 +a 11288 13335 1 +a 11289 13334 1 +a 11290 13333 1 +a 11291 13332 1 +a 11292 13331 1 +a 11293 13330 1 +a 11294 13329 1 +a 11295 13328 1 +a 11296 13327 1 +a 11297 13326 1 +a 11298 13325 1 +a 11299 13324 1 +a 11300 13323 1 +a 11301 13322 1 +a 11302 13321 1 +a 11303 13320 1 +a 11304 13319 1 +a 11305 13318 1 +a 11306 13317 1 +a 11307 13316 1 +a 11308 13315 1 +a 11309 13314 1 +a 11310 13313 1 +a 11311 13312 1 +a 11312 13311 1 +a 11313 13310 1 +a 11314 13309 1 +a 11315 13308 1 +a 11316 13307 1 +a 11317 13306 1 +a 11318 13305 1 +a 11319 13304 1 +a 11320 13303 1 +a 11321 13302 1 +a 11322 13301 1 +a 11323 13300 1 +a 11324 13299 1 +a 11325 13298 1 +a 11326 13297 1 +a 11327 13296 1 +a 11328 13295 1 +a 11329 13294 1 +a 11330 13293 1 +a 11331 13292 1 +a 11332 13291 1 +a 11333 13290 1 +a 11334 13289 1 +a 11335 13288 1 +a 11336 13287 1 +a 11337 13286 1 +a 11338 13285 1 +a 11339 13284 1 +a 11340 13283 1 +a 11341 13282 1 +a 11342 13281 1 +a 11343 13280 1 +a 11344 13279 1 +a 11345 13278 1 +a 11346 13277 1 +a 11347 13276 1 +a 11348 13275 1 +a 11349 13274 1 +a 11350 13273 1 +a 11351 13272 1 +a 11352 13271 1 +a 11353 13270 1 +a 11354 13269 1 +a 11355 13268 1 +a 11356 13267 1 +a 11357 13266 1 +a 11358 13265 1 +a 11359 13264 1 +a 11360 13263 1 +a 11361 13262 1 +a 11362 13261 1 +a 11363 13260 1 +a 11364 13259 1 +a 11365 13258 1 +a 11366 13257 1 +a 11367 13256 1 +a 11368 13255 1 +a 11369 13254 1 +a 11370 13253 1 +a 11371 13252 1 +a 11372 13251 1 +a 11373 13250 1 +a 11374 13249 1 +a 11375 13248 1 +a 11376 13247 1 +a 11377 13246 1 +a 11378 13245 1 +a 11379 13244 1 +a 11380 13243 1 +a 11381 13242 1 +a 11382 13241 1 +a 11383 13240 1 +a 11384 13239 1 +a 11385 13238 1 +a 11386 13237 1 +a 11387 13236 1 +a 11388 13235 1 +a 11389 13234 1 +a 11390 13233 1 +a 11391 13232 1 +a 11392 13231 1 +a 11393 13230 1 +a 11394 13229 1 +a 11395 13228 1 +a 11396 13227 1 +a 11397 13226 1 +a 11398 13225 1 +a 11399 13224 1 +a 11400 13223 1 +a 11401 13222 1 +a 11402 13221 1 +a 11403 13220 1 +a 11404 13219 1 +a 11405 13218 1 +a 11406 13217 1 +a 11407 13216 1 +a 11408 13215 1 +a 11409 13214 1 +a 11410 13213 1 +a 11411 13212 1 +a 11412 13211 1 +a 11413 13210 1 +a 11414 13209 1 +a 11415 13208 1 +a 11416 13207 1 +a 11417 13206 1 +a 11418 13205 1 +a 11419 13204 1 +a 11420 13203 1 +a 11421 13202 1 +a 11422 13201 1 +a 11423 13200 1 +a 11424 13199 1 +a 11425 13198 1 +a 11426 13197 1 +a 11427 13196 1 +a 11428 13195 1 +a 11429 13194 1 +a 11430 13193 1 +a 11431 13192 1 +a 11432 13191 1 +a 11433 13190 1 +a 11434 13189 1 +a 11435 13188 1 +a 11436 13187 1 +a 11437 13186 1 +a 11438 13185 1 +a 11439 13184 1 +a 11440 13183 1 +a 11441 13182 1 +a 11442 13181 1 +a 11443 13180 1 +a 11444 13179 1 +a 11445 13178 1 +a 11446 13177 1 +a 11447 13176 1 +a 11448 13175 1 +a 11449 13174 1 +a 11450 13173 1 +a 11451 13172 1 +a 11452 13171 1 +a 11453 13170 1 +a 11454 13169 1 +a 11455 13168 1 +a 11456 13167 1 +a 11457 13166 1 +a 11458 13165 1 +a 11459 13164 1 +a 11460 13163 1 +a 11461 13162 1 +a 11462 13161 1 +a 11463 13160 1 +a 11464 13159 1 +a 11465 13158 1 +a 11466 13157 1 +a 11467 13156 1 +a 11468 13155 1 +a 11469 13154 1 +a 11470 13153 1 +a 11471 13152 1 +a 11472 13151 1 +a 11473 13150 1 +a 11474 13149 1 +a 11475 13148 1 +a 11476 13147 1 +a 11477 13146 1 +a 11478 13145 1 +a 11479 13144 1 +a 11480 13143 1 +a 11481 13142 1 +a 11482 13141 1 +a 11483 13140 1 +a 11484 13139 1 +a 11485 13138 1 +a 11486 13137 1 +a 11487 13136 1 +a 11488 13135 1 +a 11489 13134 1 +a 11490 13133 1 +a 11491 13132 1 +a 11492 13131 1 +a 11493 13130 1 +a 11494 13129 1 +a 11495 13128 1 +a 11496 13127 1 +a 11497 13126 1 +a 11498 13125 1 +a 11499 13124 1 +a 11500 13123 1 +a 11501 13122 1 +a 11502 13121 1 +a 11503 13120 1 +a 11504 13119 1 +a 11505 13118 1 +a 11506 13117 1 +a 11507 13116 1 +a 11508 13115 1 +a 11509 13114 1 +a 11510 13113 1 +a 11511 13112 1 +a 11512 13111 1 +a 11513 13110 1 +a 11514 13109 1 +a 11515 13108 1 +a 11516 13107 1 +a 11517 13106 1 +a 11518 13105 1 +a 11519 13104 1 +a 11520 13103 1 +a 11521 13102 1 +a 11522 13101 1 +a 11523 13100 1 +a 11524 13099 1 +a 11525 13098 1 +a 11526 13097 1 +a 11527 13096 1 +a 11528 13095 1 +a 11529 13094 1 +a 11530 13093 1 +a 11531 13092 1 +a 11532 13091 1 +a 11533 13090 1 +a 11534 13089 1 +a 11535 13088 1 +a 11536 13087 1 +a 11537 13086 1 +a 11538 13085 1 +a 11539 13084 1 +a 11540 13083 1 +a 11541 13082 1 +a 11542 13081 1 +a 11543 13080 1 +a 11544 13079 1 +a 11545 13078 1 +a 11546 13077 1 +a 11547 13076 1 +a 11548 13075 1 +a 11549 13074 1 +a 11550 13073 1 +a 11551 13072 1 +a 11552 13071 1 +a 11553 13070 1 +a 11554 13069 1 +a 11555 13068 1 +a 11556 13067 1 +a 11557 13066 1 +a 11558 13065 1 +a 11559 13064 1 +a 11560 13063 1 +a 11561 13062 1 +a 11562 13061 1 +a 11563 13060 1 +a 11564 13059 1 +a 11565 13058 1 +a 11566 13057 1 +a 11567 13056 1 +a 11568 13055 1 +a 11569 13054 1 +a 11570 13053 1 +a 11571 13052 1 +a 11572 13051 1 +a 11573 13050 1 +a 11574 13049 1 +a 11575 13048 1 +a 11576 13047 1 +a 11577 13046 1 +a 11578 13045 1 +a 11579 13044 1 +a 11580 13043 1 +a 11581 13042 1 +a 11582 13041 1 +a 11583 13040 1 +a 11584 13039 1 +a 11585 13038 1 +a 11586 13037 1 +a 11587 13036 1 +a 11588 13035 1 +a 11589 13034 1 +a 11590 13033 1 +a 11591 13032 1 +a 11592 13031 1 +a 11593 13030 1 +a 11594 13029 1 +a 11595 13028 1 +a 11596 13027 1 +a 11597 13026 1 +a 11598 13025 1 +a 11599 13024 1 +a 11600 13023 1 +a 11601 13022 1 +a 11602 13021 1 +a 11603 13020 1 +a 11604 13019 1 +a 11605 13018 1 +a 11606 13017 1 +a 11607 13016 1 +a 11608 13015 1 +a 11609 13014 1 +a 11610 13013 1 +a 11611 13012 1 +a 11612 13011 1 +a 11613 13010 1 +a 11614 13009 1 +a 11615 13008 1 +a 11616 13007 1 +a 11617 13006 1 +a 11618 13005 1 +a 11619 13004 1 +a 11620 13003 1 +a 11621 13002 1 +a 11622 13001 1 +a 11623 13000 1 +a 11624 12999 1 +a 11625 12998 1 +a 11626 12997 1 +a 11627 12996 1 +a 11628 12995 1 +a 11629 12994 1 +a 11630 12993 1 +a 11631 12992 1 +a 11632 12991 1 +a 11633 12990 1 +a 11634 12989 1 +a 11635 12988 1 +a 11636 12987 1 +a 11637 12986 1 +a 11638 12985 1 +a 11639 12984 1 +a 11640 12983 1 +a 11641 12982 1 +a 11642 12981 1 +a 11643 12980 1 +a 11644 12979 1 +a 11645 12978 1 +a 11646 12977 1 +a 11647 12976 1 +a 11648 12975 1 +a 11649 12974 1 +a 11650 12973 1 +a 11651 12972 1 +a 11652 12971 1 +a 11653 12970 1 +a 11654 12969 1 +a 11655 12968 1 +a 11656 12967 1 +a 11657 12966 1 +a 11658 12965 1 +a 11659 12964 1 +a 11660 12963 1 +a 11661 12962 1 +a 11662 12961 1 +a 11663 12960 1 +a 11664 12959 1 +a 11665 12958 1 +a 11666 12957 1 +a 11667 12956 1 +a 11668 12955 1 +a 11669 12954 1 +a 11670 12953 1 +a 11671 12952 1 +a 11672 12951 1 +a 11673 12950 1 +a 11674 12949 1 +a 11675 12948 1 +a 11676 12947 1 +a 11677 12946 1 +a 11678 12945 1 +a 11679 12944 1 +a 11680 12943 1 +a 11681 12942 1 +a 11682 12941 1 +a 11683 12940 1 +a 11684 12939 1 +a 11685 12938 1 +a 11686 12937 1 +a 11687 12936 1 +a 11688 12935 1 +a 11689 12934 1 +a 11690 12933 1 +a 11691 12932 1 +a 11692 12931 1 +a 11693 12930 1 +a 11694 12929 1 +a 11695 12928 1 +a 11696 12927 1 +a 11697 12926 1 +a 11698 12925 1 +a 11699 12924 1 +a 11700 12923 1 +a 11701 12922 1 +a 11702 12921 1 +a 11703 12920 1 +a 11704 12919 1 +a 11705 12918 1 +a 11706 12917 1 +a 11707 12916 1 +a 11708 12915 1 +a 11709 12914 1 +a 11710 12913 1 +a 11711 12912 1 +a 11712 12911 1 +a 11713 12910 1 +a 11714 12909 1 +a 11715 12908 1 +a 11716 12907 1 +a 11717 12906 1 +a 11718 12905 1 +a 11719 12904 1 +a 11720 12903 1 +a 11721 12902 1 +a 11722 12901 1 +a 11723 12900 1 +a 11724 12899 1 +a 11725 12898 1 +a 11726 12897 1 +a 11727 12896 1 +a 11728 12895 1 +a 11729 12894 1 +a 11730 12893 1 +a 11731 12892 1 +a 11732 12891 1 +a 11733 12890 1 +a 11734 12889 1 +a 11735 12888 1 +a 11736 12887 1 +a 11737 12886 1 +a 11738 12885 1 +a 11739 12884 1 +a 11740 12883 1 +a 11741 12882 1 +a 11742 12881 1 +a 11743 12880 1 +a 11744 12879 1 +a 11745 12878 1 +a 11746 12877 1 +a 11747 12876 1 +a 11748 12875 1 +a 11749 12874 1 +a 11750 12873 1 +a 11751 12872 1 +a 11752 12871 1 +a 11753 12870 1 +a 11754 12869 1 +a 11755 12868 1 +a 11756 12867 1 +a 11757 12866 1 +a 11758 12865 1 +a 11759 12864 1 +a 11760 12863 1 +a 11761 12862 1 +a 11762 12861 1 +a 11763 12860 1 +a 11764 12859 1 +a 11765 12858 1 +a 11766 12857 1 +a 11767 12856 1 +a 11768 12855 1 +a 11769 12854 1 +a 11770 12853 1 +a 11771 12852 1 +a 11772 12851 1 +a 11773 12850 1 +a 11774 12849 1 +a 11775 12848 1 +a 11776 12847 1 +a 11777 12846 1 +a 11778 12845 1 +a 11779 12844 1 +a 11780 12843 1 +a 11781 12842 1 +a 11782 12841 1 +a 11783 12840 1 +a 11784 12839 1 +a 11785 12838 1 +a 11786 12837 1 +a 11787 12836 1 +a 11788 12835 1 +a 11789 12834 1 +a 11790 12833 1 +a 11791 12832 1 +a 11792 12831 1 +a 11793 12830 1 +a 11794 12829 1 +a 11795 12828 1 +a 11796 12827 1 +a 11797 12826 1 +a 11798 12825 1 +a 11799 12824 1 +a 11800 12823 1 +a 11801 12822 1 +a 11802 12821 1 +a 11803 12820 1 +a 11804 12819 1 +a 11805 12818 1 +a 11806 12817 1 +a 11807 12816 1 +a 11808 12815 1 +a 11809 12814 1 +a 11810 12813 1 +a 11811 12812 1 +a 11812 12811 1 +a 11813 12810 1 +a 11814 12809 1 +a 11815 12808 1 +a 11816 12807 1 +a 11817 12806 1 +a 11818 12805 1 +a 11819 12804 1 +a 11820 12803 1 +a 11821 12802 1 +a 11822 12801 1 +a 11823 12800 1 +a 11824 12799 1 +a 11825 12798 1 +a 11826 12797 1 +a 11827 12796 1 +a 11828 12795 1 +a 11829 12794 1 +a 11830 12793 1 +a 11831 12792 1 +a 11832 12791 1 +a 11833 12790 1 +a 11834 12789 1 +a 11835 12788 1 +a 11836 12787 1 +a 11837 12786 1 +a 11838 12785 1 +a 11839 12784 1 +a 11840 12783 1 +a 11841 12782 1 +a 11842 12781 1 +a 11843 12780 1 +a 11844 12779 1 +a 11845 12778 1 +a 11846 12777 1 +a 11847 12776 1 +a 11848 12775 1 +a 11849 12774 1 +a 11850 12773 1 +a 11851 12772 1 +a 11852 12771 1 +a 11853 12770 1 +a 11854 12769 1 +a 11855 12768 1 +a 11856 12767 1 +a 11857 12766 1 +a 11858 12765 1 +a 11859 12764 1 +a 11860 12763 1 +a 11861 12762 1 +a 11862 12761 1 +a 11863 12760 1 +a 11864 12759 1 +a 11865 12758 1 +a 11866 12757 1 +a 11867 12756 1 +a 11868 12755 1 +a 11869 12754 1 +a 11870 12753 1 +a 11871 12752 1 +a 11872 12751 1 +a 11873 12750 1 +a 11874 12749 1 +a 11875 12748 1 +a 11876 12747 1 +a 11877 12746 1 +a 11878 12745 1 +a 11879 12744 1 +a 11880 12743 1 +a 11881 12742 1 +a 11882 12741 1 +a 11883 12740 1 +a 11884 12739 1 +a 11885 12738 1 +a 11886 12737 1 +a 11887 12736 1 +a 11888 12735 1 +a 11889 12734 1 +a 11890 12733 1 +a 11891 12732 1 +a 11892 12731 1 +a 11893 12730 1 +a 11894 12729 1 +a 11895 12728 1 +a 11896 12727 1 +a 11897 12726 1 +a 11898 12725 1 +a 11899 12724 1 +a 11900 12723 1 +a 11901 12722 1 +a 11902 12721 1 +a 11903 12720 1 +a 11904 12719 1 +a 11905 12718 1 +a 11906 12717 1 +a 11907 12716 1 +a 11908 12715 1 +a 11909 12714 1 +a 11910 12713 1 +a 11911 12712 1 +a 11912 12711 1 +a 11913 12710 1 +a 11914 12709 1 +a 11915 12708 1 +a 11916 12707 1 +a 11917 12706 1 +a 11918 12705 1 +a 11919 12704 1 +a 11920 12703 1 +a 11921 12702 1 +a 11922 12701 1 +a 11923 12700 1 +a 11924 12699 1 +a 11925 12698 1 +a 11926 12697 1 +a 11927 12696 1 +a 11928 12695 1 +a 11929 12694 1 +a 11930 12693 1 +a 11931 12692 1 +a 11932 12691 1 +a 11933 12690 1 +a 11934 12689 1 +a 11935 12688 1 +a 11936 12687 1 +a 11937 12686 1 +a 11938 12685 1 +a 11939 12684 1 +a 11940 12683 1 +a 11941 12682 1 +a 11942 12681 1 +a 11943 12680 1 +a 11944 12679 1 +a 11945 12678 1 +a 11946 12677 1 +a 11947 12676 1 +a 11948 12675 1 +a 11949 12674 1 +a 11950 12673 1 +a 11951 12672 1 +a 11952 12671 1 +a 11953 12670 1 +a 11954 12669 1 +a 11955 12668 1 +a 11956 12667 1 +a 11957 12666 1 +a 11958 12665 1 +a 11959 12664 1 +a 11960 12663 1 +a 11961 12662 1 +a 11962 12661 1 +a 11963 12660 1 +a 11964 12659 1 +a 11965 12658 1 +a 11966 12657 1 +a 11967 12656 1 +a 11968 12655 1 +a 11969 12654 1 +a 11970 12653 1 +a 11971 12652 1 +a 11972 12651 1 +a 11973 12650 1 +a 11974 12649 1 +a 11975 12648 1 +a 11976 12647 1 +a 11977 12646 1 +a 11978 12645 1 +a 11979 12644 1 +a 11980 12643 1 +a 11981 12642 1 +a 11982 12641 1 +a 11983 12640 1 +a 11984 12639 1 +a 11985 12638 1 +a 11986 12637 1 +a 11987 12636 1 +a 11988 12635 1 +a 11989 12634 1 +a 11990 12633 1 +a 11991 12632 1 +a 11992 12631 1 +a 11993 12630 1 +a 11994 12629 1 +a 11995 12628 1 +a 11996 12627 1 +a 11997 12626 1 +a 11998 12625 1 +a 11999 12624 1 +a 12000 12623 1 +a 12001 12622 1 +a 12002 12621 1 +a 12003 12620 1 +a 12004 12619 1 +a 12005 12618 1 +a 12006 12617 1 +a 12007 12616 1 +a 12008 12615 1 +a 12009 12614 1 +a 12010 12613 1 +a 12011 12612 1 +a 12012 12611 1 +a 12013 12610 1 +a 12014 12609 1 +a 12015 12608 1 +a 12016 12607 1 +a 12017 12606 1 +a 12018 12605 1 +a 12019 12604 1 +a 12020 12603 1 +a 12021 12602 1 +a 12022 12601 1 +a 12023 12600 1 +a 12024 12599 1 +a 12025 12598 1 +a 12026 12597 1 +a 12027 12596 1 +a 12028 12595 1 +a 12029 12594 1 +a 12030 12593 1 +a 12031 12592 1 +a 12032 12591 1 +a 12033 12590 1 +a 12034 12589 1 +a 12035 12588 1 +a 12036 12587 1 +a 12037 12586 1 +a 12038 12585 1 +a 12039 12584 1 +a 12040 12583 1 +a 12041 12582 1 +a 12042 12581 1 +a 12043 12580 1 +a 12044 12579 1 +a 12045 12578 1 +a 12046 12577 1 +a 12047 12576 1 +a 12048 12575 1 +a 12049 12574 1 +a 12050 12573 1 +a 12051 12572 1 +a 12052 12571 1 +a 12053 12570 1 +a 12054 12569 1 +a 12055 12568 1 +a 12056 12567 1 +a 12057 12566 1 +a 12058 12565 1 +a 12059 12564 1 +a 12060 12563 1 +a 12061 12562 1 +a 12062 12561 1 +a 12063 12560 1 +a 12064 12559 1 +a 12065 12558 1 +a 12066 12557 1 +a 12067 12556 1 +a 12068 12555 1 +a 12069 12554 1 +a 12070 12553 1 +a 12071 12552 1 +a 12072 12551 1 +a 12073 12550 1 +a 12074 12549 1 +a 12075 12548 1 +a 12076 12547 1 +a 12077 12546 1 +a 12078 12545 1 +a 12079 12544 1 +a 12080 12543 1 +a 12081 12542 1 +a 12082 12541 1 +a 12083 12540 1 +a 12084 12539 1 +a 12085 12538 1 +a 12086 12537 1 +a 12087 12536 1 +a 12088 12535 1 +a 12089 12534 1 +a 12090 12533 1 +a 12091 12532 1 +a 12092 12531 1 +a 12093 12530 1 +a 12094 12529 1 +a 12095 12528 1 +a 12096 12527 1 +a 12097 12526 1 +a 12098 12525 1 +a 12099 12524 1 +a 12100 12523 1 +a 12101 12522 1 +a 12102 12521 1 +a 12103 12520 1 +a 12104 12519 1 +a 12105 12518 1 +a 12106 12517 1 +a 12107 12516 1 +a 12108 12515 1 +a 12109 12514 1 +a 12110 12513 1 +a 12111 12512 1 +a 12112 12511 1 +a 12113 12510 1 +a 12114 12509 1 +a 12115 12508 1 +a 12116 12507 1 +a 12117 12506 1 +a 12118 12505 1 +a 12119 12504 1 +a 12120 12503 1 +a 12121 12502 1 +a 12122 12501 1 +a 12123 12500 1 +a 12124 12499 1 +a 12125 12498 1 +a 12126 12497 1 +a 12127 12496 1 +a 12128 12495 1 +a 12129 12494 1 +a 12130 12493 1 +a 12131 12492 1 +a 12132 12491 1 +a 12133 12490 1 +a 12134 12489 1 +a 12135 12488 1 +a 12136 12487 1 +a 12137 12486 1 +a 12138 12485 1 +a 12139 12484 1 +a 12140 12483 1 +a 12141 12482 1 +a 12142 12481 1 +a 12143 12480 1 +a 12144 12479 1 +a 12145 12478 1 +a 12146 12477 1 +a 12147 12476 1 +a 12148 12475 1 +a 12149 12474 1 +a 12150 12473 1 +a 12151 12472 1 +a 12152 12471 1 +a 12153 12470 1 +a 12154 12469 1 +a 12155 12468 1 +a 12156 12467 1 +a 12157 12466 1 +a 12158 12465 1 +a 12159 12464 1 +a 12160 12463 1 +a 12161 12462 1 +a 12162 12461 1 +a 12163 12460 1 +a 12164 12459 1 +a 12165 12458 1 +a 12166 12457 1 +a 12167 12456 1 +a 12168 12455 1 +a 12169 12454 1 +a 12170 12453 1 +a 12171 12452 1 +a 12172 12451 1 +a 12173 12450 1 +a 12174 12449 1 +a 12175 12448 1 +a 12176 12447 1 +a 12177 12446 1 +a 12178 12445 1 +a 12179 12444 1 +a 12180 12443 1 +a 12181 12442 1 +a 12182 12441 1 +a 12183 12440 1 +a 12184 12439 1 +a 12185 12438 1 +a 12186 12437 1 +a 12187 12436 1 +a 12188 12435 1 +a 12189 12434 1 +a 12190 12433 1 +a 12191 12432 1 +a 12192 12431 1 +a 12193 12430 1 +a 12194 12429 1 +a 12195 12428 1 +a 12196 12427 1 +a 12197 12426 1 +a 12198 12425 1 +a 12199 12424 1 +a 12200 12423 1 +a 12201 12422 1 +a 12202 12421 1 +a 12203 12420 1 +a 12204 12419 1 +a 12205 12418 1 +a 12206 12417 1 +a 12207 12416 1 +a 12208 12415 1 +a 12209 12414 1 +a 12210 12413 1 +a 12211 12412 1 +a 12212 12411 1 +a 12213 12410 1 +a 12214 12409 1 +a 12215 12408 1 +a 12216 12407 1 +a 12217 12406 1 +a 12218 12405 1 +a 12219 12404 1 +a 12220 12403 1 +a 12221 12402 1 +a 12222 12401 1 +a 12223 12400 1 +a 12224 12399 1 +a 12225 12398 1 +a 12226 12397 1 +a 12227 12396 1 +a 12228 12395 1 +a 12229 12394 1 +a 12230 12393 1 +a 12231 12392 1 +a 12232 12391 1 +a 12233 12390 1 +a 12234 12389 1 +a 12235 12388 1 +a 12236 12387 1 +a 12237 12386 1 +a 12238 12385 1 +a 12239 12384 1 +a 12240 12383 1 +a 12241 12382 1 +a 12242 12381 1 +a 12243 12380 1 +a 12244 12379 1 +a 12245 12378 1 +a 12246 12377 1 +a 12247 12376 1 +a 12248 12375 1 +a 12249 12374 1 +a 12250 12373 1 +a 12251 12372 1 +a 12252 12371 1 +a 12253 12370 1 +a 12254 12369 1 +a 12255 12368 1 +a 12256 12367 1 +a 12257 12366 1 +a 12258 12365 1 +a 12259 12364 1 +a 12260 12363 1 +a 12261 12362 1 +a 12262 12361 1 +a 12263 12360 1 +a 12264 12359 1 +a 12265 12358 1 +a 12266 12357 1 +a 12267 12356 1 +a 12268 12355 1 +a 12269 12354 1 +a 12270 12353 1 +a 12271 12352 1 +a 12272 12351 1 +a 12273 12350 1 +a 12274 12349 1 +a 12275 12348 1 +a 12276 12347 1 +a 12277 12346 1 +a 12278 12345 1 +a 12279 12344 1 +a 12280 12343 1 +a 12281 12342 1 +a 12282 12341 1 +a 12283 12340 1 +a 12284 12339 1 +a 12285 12338 1 +a 12286 12337 1 +a 12287 12336 1 +a 12288 12335 1 +a 12289 12334 1 +a 12290 12333 1 +a 12291 12332 1 +a 12292 12331 1 +a 12293 12330 1 +a 12294 12329 1 +a 12295 12328 1 +a 12296 12327 1 +a 12297 12326 1 +a 12298 12325 1 +a 12299 12324 1 +a 12300 12323 1 +a 12301 12322 1 +a 12302 12321 1 +a 12303 12320 1 +a 12304 12319 1 +a 12305 12318 1 +a 12306 12317 1 +a 12307 12316 1 +a 12308 12315 1 +a 12309 12314 1 +a 12310 12313 1 +a 1 3 1000000 +a 1 8209 1000000 +a 8208 2 1000000 +a 16414 2 1000000 diff --git a/tests/unit/all_almost_e.c b/tests/unit/all_almost_e.c new file mode 100644 index 0000000..1c577c1 --- /dev/null +++ b/tests/unit/all_almost_e.c @@ -0,0 +1,87 @@ +/* + 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, see . +*/ + +#include + +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_matrix_t rm1, rm2; + igraph_matrix_complex_t cm1, cm2; + const igraph_integer_t nrow = 50, ncol = 60; + + igraph_rng_seed(igraph_rng_default(), 97); + + /* Real matrices */ + + igraph_matrix_init(&rm1, nrow, ncol); + for (igraph_integer_t i=0; i < nrow; i++) { + for (igraph_integer_t j=0; j < ncol; j++) { + MATRIX(rm1, i, j) = RNG_UNIF(0.0, 3.0); + } + } + + igraph_matrix_init_copy(&rm2, &rm1); + for (igraph_integer_t i=0; i < nrow; i++) { + for (igraph_integer_t j=0; j < ncol; j++) { + MATRIX(rm2, i, j) = pow(MATRIX(rm2, i, j), 1 / 7.0); + MATRIX(rm2, i, j) = pow(MATRIX(rm2, i, j), 7.0); + } + } + + IGRAPH_ASSERT(igraph_matrix_all_almost_e(&rm1, &rm2, 4*DBL_EPSILON)); + MATRIX(rm2, 0, 0) *= 2; + IGRAPH_ASSERT(! igraph_matrix_all_almost_e(&rm1, &rm2, 4*DBL_EPSILON)); + + igraph_matrix_destroy(&rm2); + igraph_matrix_destroy(&rm1); + + /* Complex matrices */ + + igraph_matrix_complex_init(&cm1, nrow, ncol); + + for (igraph_integer_t i=0; i < nrow; i++) { + for (igraph_integer_t j=0; j < ncol; j++) { + IGRAPH_REAL(MATRIX(cm1, i, j)) = RNG_NORMAL(0,1); + IGRAPH_IMAG(MATRIX(cm1, i, j)) = RNG_NORMAL(0,1); + } + } + + + igraph_matrix_complex_init_copy(&cm2, &cm1); + for (igraph_integer_t i=0; i < nrow; i++) { + for (igraph_integer_t j=0; j < ncol; j++) { + MATRIX(cm2, i, j) = igraph_complex_pow_real(MATRIX(cm2, i, j), 1 / 7.0); + MATRIX(cm2, i, j) = igraph_complex_pow_real(MATRIX(cm2, i, j), 7.0); + } + } + + IGRAPH_ASSERT(igraph_matrix_complex_all_almost_e(&cm1, &cm2, 8*DBL_EPSILON)); + MATRIX(cm2, 0, 0) = igraph_complex_mul(MATRIX(cm2, 0, 0), MATRIX(cm2, 1, 1)); + IGRAPH_ASSERT(! igraph_matrix_complex_all_almost_e(&cm1, &cm2, 8*DBL_EPSILON)); + + igraph_matrix_complex_destroy(&cm2); + igraph_matrix_complex_destroy(&cm1); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/all_shortest_paths.c b/tests/unit/all_shortest_paths.c new file mode 100644 index 0000000..d86c32d --- /dev/null +++ b/tests/unit/all_shortest_paths.c @@ -0,0 +1,162 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t paths, paths_edge; + igraph_vector_int_t nrgeo; + igraph_vector_t weights; + igraph_integer_t from, to; + + igraph_vector_int_list_init(&paths, 0); + igraph_vector_int_list_init(&paths_edge, 0); + igraph_vector_int_init(&nrgeo, 0); + + igraph_vector_init(&weights, 0); + + /* Note on the output: + * get_all_shortest_paths functions sort their output based on the + * last vertex only. Thus the ordering is not fully defined. + * + * This test does not currently canonicalize (i.e. sort) + * the result before printing it. + */ + + printf("Singleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + + from = 0; to = 0; + + igraph_get_all_shortest_paths(&graph, &paths, NULL, &nrgeo, from, igraph_vss_1(to), IGRAPH_ALL); + + printf("Vertex paths:\n"); + print_vector_int_list(&paths); + IGRAPH_ASSERT(igraph_vector_int_list_size(&paths) == VECTOR(nrgeo)[to]); + + printf("\nSingleton graph, weighted\n"); + igraph_vector_resize(&weights, igraph_ecount(&graph)); + igraph_vector_fill(&weights, 1); + igraph_get_all_shortest_paths_dijkstra(&graph, &paths, NULL, &nrgeo, from, igraph_vss_1(to), &weights, IGRAPH_ALL); + + printf("Vertex paths:\n"); + print_vector_int_list(&paths); + IGRAPH_ASSERT(igraph_vector_int_list_size(&paths) == VECTOR(nrgeo)[to]); + + igraph_destroy(&graph); + + printf("\nNo paths\n"); + igraph_empty(&graph, 2, IGRAPH_UNDIRECTED); + + from = 0; to = 1; + + igraph_get_all_shortest_paths(&graph, &paths, &paths_edge, &nrgeo, from, igraph_vss_1(to), IGRAPH_ALL); + + printf("Vertex paths:\n"); + print_vector_int_list(&paths); + printf("Edge paths:\n"); + print_vector_int_list(&paths_edge); + IGRAPH_ASSERT(igraph_vector_int_list_size(&paths) == VECTOR(nrgeo)[to]); + + igraph_destroy(&graph); + + /* This graph has multi-edges (which induce multiple paths of the + * same length) as well as more paths of the same length between + * vertices 0 and 4. */ + igraph_small(&graph, 0, IGRAPH_ADJ_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 0, 1, 2, 5, 4, 5, 1, 6, 6, 7, 3, 7, + -1); + + from = 0; to = 4; + + printf("\nUnweighted\n"); + igraph_get_all_shortest_paths(&graph, &paths, &paths_edge, &nrgeo, from, igraph_vss_1(to), IGRAPH_ALL); + + printf("Vertex paths:\n"); + print_vector_int_list(&paths); + printf("Edge paths:\n"); + print_vector_int_list(&paths_edge); + IGRAPH_ASSERT(igraph_vector_int_list_size(&paths) == VECTOR(nrgeo)[to]); + + printf("\nWeighted, uniform weights\n"); + igraph_vector_resize(&weights, igraph_ecount(&graph)); + igraph_vector_fill(&weights, 1.5); /* constant weights */ + + igraph_get_all_shortest_paths_dijkstra(&graph, &paths, &paths_edge, &nrgeo, from, igraph_vss_1(to), &weights, IGRAPH_ALL); + + printf("Vertex paths:\n"); + print_vector_int_list(&paths); + printf("Edge paths:\n"); + print_vector_int_list(&paths_edge); + IGRAPH_ASSERT(igraph_vector_int_list_size(&paths) == VECTOR(nrgeo)[to]); + + printf("\nWeighted, multiple weighted shortest paths\n"); + VECTOR(weights)[1] = 3.0; /* create path with one more hop, but equal weighted length */ + VECTOR(weights)[4] = 2.0; /* break symmetry on pair of parallel edges */ + + igraph_get_all_shortest_paths_dijkstra(&graph, &paths, &paths_edge, &nrgeo, from, igraph_vss_1(to), &weights, IGRAPH_ALL); + + printf("Vertex paths:\n"); + print_vector_int_list(&paths); + printf("Edge paths:\n"); + print_vector_int_list(&paths_edge); + IGRAPH_ASSERT(igraph_vector_int_list_size(&paths) == VECTOR(nrgeo)[to]); + + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + /* Graph is from https://github.com/igraph/rigraph/issues/314 */ + printf("\nWeighted, multiple weighted shortest paths, testing tolerances\n"); + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,3, 1,2, 2,5, 2,3, 2,4, 3,5, 5,6, 6,7, 1,8, 8,9, 4,10, 6,11, 8,12, 8,13, 4,14, 7,15, + -1); + + igraph_real_t weights_raw[] = { 1.9617537, 0.9060834, 2.2165446, 1.6251956, + 2.4473929, 0.5913490, 8.7093236, 2.8387330, + 6.1225042, 20.7217776, 6.8027218, 16.3147479, + 5.2605598, 6.6816853, 4.9482123, 1.8989790 }; + + /* Choose carefully: If not using tolerances, the result would be incorrect + * for starting vertices 5 and 6, but not for all other starting vertices. */ + from = 6; + printf("From: %" IGRAPH_PRId ", to: all.\n", from); + + igraph_vector_view(&weights, weights_raw, sizeof(weights_raw) / sizeof(igraph_real_t)); + igraph_get_all_shortest_paths_dijkstra(&graph, &paths, &paths_edge, &nrgeo, from, igraph_vss_all(), &weights, IGRAPH_ALL); + + printf("Vertex paths:\n"); + print_vector_int_list(&paths); + printf("Edge paths:\n"); + print_vector_int_list(&paths_edge); + + printf("nrgeo: "); + print_vector_int(&nrgeo); + + igraph_vector_int_list_destroy(&paths); + igraph_vector_int_list_destroy(&paths_edge); + + igraph_vector_int_destroy(&nrgeo); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/all_shortest_paths.out b/tests/unit/all_shortest_paths.out new file mode 100644 index 0000000..1ff23bc --- /dev/null +++ b/tests/unit/all_shortest_paths.out @@ -0,0 +1,125 @@ +Singleton graph +Vertex paths: +{ + 0: ( 0 ) +} + +Singleton graph, weighted +Vertex paths: +{ + 0: ( 0 ) +} + +No paths +Vertex paths: +{ +} +Edge paths: +{ +} + +Unweighted +Vertex paths: +{ + 0: ( 0 1 2 5 4 ) + 1: ( 0 1 2 5 4 ) + 2: ( 0 1 2 3 4 ) + 3: ( 0 1 2 3 4 ) +} +Edge paths: +{ + 0: ( 4 1 5 6 ) + 1: ( 0 1 5 6 ) + 2: ( 4 1 2 3 ) + 3: ( 0 1 2 3 ) +} + +Weighted, uniform weights +Vertex paths: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 0 1 2 3 4 ) + 2: ( 0 1 2 5 4 ) + 3: ( 0 1 2 5 4 ) +} +Edge paths: +{ + 0: ( 0 1 2 3 ) + 1: ( 4 1 2 3 ) + 2: ( 0 1 5 6 ) + 3: ( 4 1 5 6 ) +} + +Weighted, multiple weighted shortest paths +Vertex paths: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 0 1 6 7 3 4 ) + 2: ( 0 1 2 5 4 ) +} +Edge paths: +{ + 0: ( 0 1 2 3 ) + 1: ( 0 7 8 9 3 ) + 2: ( 0 1 5 6 ) +} + +Weighted, multiple weighted shortest paths, testing tolerances +From: 6, to: all. +Vertex paths: +{ + 0: ( 6 5 3 0 ) + 1: ( 6 5 3 2 1 ) + 2: ( 6 5 2 1 ) + 3: ( 6 5 3 2 ) + 4: ( 6 5 2 ) + 5: ( 6 5 3 ) + 6: ( 6 5 3 2 4 ) + 7: ( 6 5 2 4 ) + 8: ( 6 5 ) + 9: ( 6 ) + 10: ( 6 7 ) + 11: ( 6 5 3 2 1 8 ) + 12: ( 6 5 2 1 8 ) + 13: ( 6 5 3 2 1 8 9 ) + 14: ( 6 5 2 1 8 9 ) + 15: ( 6 5 3 2 4 10 ) + 16: ( 6 5 2 4 10 ) + 17: ( 6 11 ) + 18: ( 6 5 3 2 1 8 12 ) + 19: ( 6 5 2 1 8 12 ) + 20: ( 6 5 3 2 1 8 13 ) + 21: ( 6 5 2 1 8 13 ) + 22: ( 6 5 3 2 4 14 ) + 23: ( 6 5 2 4 14 ) + 24: ( 6 7 15 ) +} +Edge paths: +{ + 0: ( 6 5 0 ) + 1: ( 6 5 3 1 ) + 2: ( 6 2 1 ) + 3: ( 6 5 3 ) + 4: ( 6 2 ) + 5: ( 6 5 ) + 6: ( 6 5 3 4 ) + 7: ( 6 2 4 ) + 8: ( 6 ) + 9: ( ) + 10: ( 7 ) + 11: ( 6 5 3 1 8 ) + 12: ( 6 2 1 8 ) + 13: ( 6 5 3 1 8 9 ) + 14: ( 6 2 1 8 9 ) + 15: ( 6 5 3 4 10 ) + 16: ( 6 2 4 10 ) + 17: ( 11 ) + 18: ( 6 5 3 1 8 12 ) + 19: ( 6 2 1 8 12 ) + 20: ( 6 5 3 1 8 13 ) + 21: ( 6 2 1 8 13 ) + 22: ( 6 5 3 4 14 ) + 23: ( 6 2 4 14 ) + 24: ( 7 15 ) +} +nrgeo: ( 1 2 2 1 2 1 1 1 2 2 2 1 2 2 2 1 ) diff --git a/tests/unit/assortativity.c b/tests/unit/assortativity.c new file mode 100644 index 0000000..383b059 --- /dev/null +++ b/tests/unit/assortativity.c @@ -0,0 +1,362 @@ +/* -*- 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 + +#include "test_utilities.h" + +void assortativity_unnormalized(const igraph_t *graph, igraph_real_t *res, igraph_bool_t directed) { + if (! igraph_is_directed(graph)) { + directed = 0; + } + + if (directed) { + igraph_vector_t outdeg, indeg; + igraph_vector_init(&outdeg, 0); + igraph_vector_init(&indeg, 0); + igraph_strength(graph, &outdeg, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, NULL); + igraph_strength(graph, &indeg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS, NULL); + igraph_assortativity(graph, &outdeg, &indeg, res, directed, 0); + igraph_vector_destroy(&outdeg); + igraph_vector_destroy(&indeg); + } else { + igraph_vector_t deg; + igraph_vector_init(°, 0); + igraph_strength(graph, °, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, NULL); + igraph_assortativity(graph, °, NULL, res, directed, 0); + igraph_vector_destroy(°); + } +} + +int main(void) { + + igraph_t g; + igraph_real_t assort, assort2, assort_unnorm, modularity; + igraph_vector_t values; + igraph_vector_int_t types; + + /* Assortativity based on vertex categories */ + + igraph_vector_int_init(&types, 0); + + igraph_vector_init(&values, 0); + + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_assortativity_nominal(&g, &types, &assort, IGRAPH_UNDIRECTED, /* normalized */ 1); + printf("Null graph nominal assortativity: "); + igraph_real_printf(assort); + printf("\n"); + igraph_assortativity(&g, &values, NULL, &assort, IGRAPH_UNDIRECTED, /* normalized */ 1); + printf("Null graph value assortativity: "); + igraph_real_printf(assort); + printf("\n"); + igraph_destroy(&g); + + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + + igraph_vector_int_resize(&types, 1); + VECTOR(types)[0] = 0; + + igraph_vector_resize(&values, 1); + VECTOR(values)[0] = 0; + + igraph_assortativity_nominal(&g, &types, &assort, IGRAPH_UNDIRECTED, /* normalized */ 1); + printf("Singleton graph nominal assortativity: "); + igraph_real_printf(assort); + printf("\n"); + igraph_assortativity_nominal(&g, &types, &assort, IGRAPH_UNDIRECTED, /* normalized */ 0); + printf("Singleton graph nominal assortativity, unnormalized: "); + igraph_real_printf(assort); + printf("\n"); + igraph_assortativity(&g, &values, NULL, &assort, IGRAPH_UNDIRECTED, /* normalized */ 1); + printf("Singleton graph value assortativity: "); + igraph_real_printf(assort); + printf("\n"); + igraph_assortativity(&g, &values, NULL, &assort, IGRAPH_UNDIRECTED, /* normalized */ 0); + printf("Singleton graph value assortativity, unnormalized: "); + igraph_real_printf(assort); + printf("\n"); + + igraph_add_edge(&g, 0, 0); + igraph_assortativity_nominal(&g, &types, &assort, IGRAPH_UNDIRECTED, /* normalized */ 1); + printf("Singleton graph with loop, nominal assortativity: "); + igraph_real_printf(assort); + printf("\n"); + igraph_assortativity_nominal(&g, &types, &assort, IGRAPH_UNDIRECTED, /* normalized */ 0); + printf("Singleton graph with loop, nominal assortativity, unnormalized: "); + igraph_real_printf(assort); + printf("\n"); + igraph_assortativity(&g, &values, NULL, &assort, IGRAPH_UNDIRECTED, /* normalized */ 1); + printf("Singleton graph with loop, value assortativity: "); + igraph_real_printf(assort); + printf("\n"); + igraph_assortativity(&g, &values, NULL, &assort, IGRAPH_UNDIRECTED, /* normalized */ 0); + printf("Singleton graph with loop, value assortativity, unnormalized: "); + igraph_real_printf(assort); + printf("\n"); + + igraph_destroy(&g); + igraph_vector_destroy(&values); + + printf("\n"); + + igraph_famous(&g, "zachary"); + + igraph_degree(&g, &types, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + + igraph_assortativity_nominal(&g, &types, &assort, IGRAPH_UNDIRECTED, /* normalized */ 1); + printf("Karate club, normalized assortativity based on degree categories: %g\n", assort); + + igraph_assortativity_nominal(&g, &types, &assort_unnorm, IGRAPH_UNDIRECTED, /* normalized */ 0); + printf("Karate club, non-normalized assortativity based on degree categories: %g\n", assort_unnorm); + + /* unnormalized assortativity based on categories is the same as modularity */ + igraph_modularity(&g, &types, NULL, 1, 0, &modularity); + IGRAPH_ASSERT(igraph_almost_equals(assort_unnorm, modularity, 1e-15)); + + igraph_destroy(&g); + igraph_vector_int_destroy(&types); + + /*--------------------------------------*/ + /* Assortativity based on vertex values */ + + igraph_famous(&g, "zachary"); + + igraph_vector_init_range(&values, 0, igraph_vcount(&g)); + + igraph_assortativity(&g, &values, 0, &assort, IGRAPH_UNDIRECTED, /*normalized=*/ 1); + printf("Assortativity based on values: %g\n", assort); + + /* Assortativity is a Pearson correlation, thus it must be invariant to + * a constant shift in the values. */ + igraph_vector_add_constant(&values, -5); + igraph_assortativity(&g, &values, 0, &assort2, IGRAPH_UNDIRECTED, /*normalized=*/ 1); + IGRAPH_ASSERT(igraph_almost_equals(assort, assort2, 1e-15)); + + igraph_assortativity(&g, &values, 0, &assort, IGRAPH_UNDIRECTED, /*normalized=*/ 0); + printf("Assortativity based on values, unnormalized: %g\n", assort); + + /* Assortativity is a Pearson correlation, thus it must be invariant to + * a constant shift in the values. */ + igraph_vector_add_constant(&values, -5); + igraph_assortativity(&g, &values, 0, &assort2, IGRAPH_UNDIRECTED, /*normalized=*/ 0); + IGRAPH_ASSERT(igraph_almost_equals(assort, assort2, 1e-15)); + + igraph_vector_destroy(&values); + igraph_destroy(&g); + + /*--------------------------------*/ + /* Assortativity based on degrees */ + + /* Normalized case */ + printf("\nDegree assortativity, NORMALIZED\n"); + + igraph_famous(&g, "zachary"); + + igraph_assortativity_degree(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity: %g\n", assort); + + igraph_destroy(&g); + + /* Directed graph */ + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, 0,3, 3,2, -1); + + igraph_assortativity_degree(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, directed: %g\n", assort); + + igraph_destroy(&g); + + /* Verify handling of self-loops */ + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0, 0,3, 3,3, -1); + + igraph_assortativity_degree(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, undirected, with self-loop: %g\n", assort); + + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, 0,3, 3,2, 2,2, -1); + + igraph_assortativity_degree(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, directed, with self-loop: %g\n", assort); + + igraph_destroy(&g); + + /* Verify handling of multi-edges */ + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0, 0,3, 1,2, -1); + + igraph_assortativity_degree(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, undirected, with multi-edges: %g\n", assort); + + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, 0,3, 3,2, 0,2, -1); + + igraph_assortativity_degree(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, directed, with multi-edges: %g\n", assort); + + igraph_destroy(&g); + + /* Unnormalized case */ + printf("\nDegree assortativity, UNNORMALIZED\n"); + + igraph_famous(&g, "zachary"); + + assortativity_unnormalized(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity: %g\n", assort); + + igraph_destroy(&g); + + /* Directed graph */ + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, 0,3, 3,2, -1); + + assortativity_unnormalized(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, directed: %g\n", assort); + + igraph_destroy(&g); + + /* Verify handling of self-loops */ + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0, 0,3, 3,3, -1); + + assortativity_unnormalized(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, undirected, with self-loop: %g\n", assort); + + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, 0,3, 3,2, 2,2, -1); + + assortativity_unnormalized(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, directed, with self-loop: %g\n", assort); + + igraph_destroy(&g); + + /* Verify handling of multi-edges */ + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0, 0,3, 1,2, -1); + + assortativity_unnormalized(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, undirected, with multi-edges: %g\n", assort); + + igraph_destroy(&g); + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, 0,3, 3,2, 0,2, -1); + + assortativity_unnormalized(&g, &assort, /*directed=*/ 1); + printf("Degree assortativity, directed, with multi-edges: %g\n", assort); + + igraph_destroy(&g); + + /*------------------*/ + /* Football network */ + + igraph_integer_t football_types[] = { + 7, 0, 2, 3, 7, 3, 2, 8, 8, 7, 3, 10, 6, 2, 6, 2, 7, 9, 6, 1, 9, 8, 8, 7, 10, 0, 6, 9, + 11, 1, 1, 6, 2, 0, 6, 1, 5, 0, 6, 2, 3, 7, 5, 6, 4, 0, 11, 2, 4, 11, 10, 8, 3, 11, 6, + 1, 9, 4, 11, 10, 2, 6, 9, 10, 2, 9, 4, 11, 8, 10, 9, 6, 3, 11, 3, 4, 9, 8, 8, 1, 5, 3, + 5, 11, 3, 6, 4, 9, 11, 0, 5, 4, 4, 7, 1, 9, 9, 10, 3, 6, 2, 1, 3, 0, 7, 0, 2, 3, 8, 0, + 4, 8, 4, 9, 11 + }; + + printf("\nFootball network: "); + igraph_small(&g, sizeof(football_types) / sizeof(football_types[0]), + IGRAPH_UNDIRECTED, + 0, 1, 2, 3, 0, 4, 4, 5, 3, 5, 2, 6, 6, 7, 7, 8, 8, 9, 0, 9, 4, 9, 5, 10, 10, 11, 5, 11, + 3, 11, 12, 13, 2, 13, 2, 14, 12, 14, 14, 15, 13, 15, 2, 15, 4, 16, 9, 16, 0, 16, + 16, 17, 12, 17, 12, 18, 18, 19, 17, 20, 20, 21, 8, 21, 7, 21, 9, 22, 7, 22, 21, + 22, 8, 22, 22, 23, 9, 23, 4, 23, 16, 23, 0, 23, 11, 24, 24, 25, 1, 25, 3, 26, 12, + 26, 14, 26, 26, 27, 17, 27, 1, 27, 17, 27, 4, 28, 11, 28, 24, 28, 19, 29, 29, + 30, 19, 30, 18, 31, 31, 32, 21, 32, 15, 32, 13, 32, 6, 32, 0, 33, 1, 33, 25, 33, + 19, 33, 31, 34, 26, 34, 12, 34, 18, 34, 34, 35, 0, 35, 29, 35, 19, 35, 30, 35, + 18, 36, 12, 36, 20, 36, 19, 36, 36, 37, 1, 37, 25, 37, 33, 37, 18, 38, 16, 38, + 28, 38, 26, 38, 14, 38, 12, 38, 38, 39, 6, 39, 32, 39, 13, 39, 15, 39, 7, 40, 3, + 40, 40, 41, 8, 41, 4, 41, 23, 41, 9, 41, 0, 41, 16, 41, 34, 42, 29, 42, 18, 42, + 26, 42, 42, 43, 36, 43, 26, 43, 31, 43, 38, 43, 12, 43, 14, 43, 19, 44, 35, 44, + 30, 44, 44, 45, 13, 45, 33, 45, 1, 45, 37, 45, 25, 45, 21, 46, 46, 47, 22, 47, + 6, 47, 15, 47, 2, 47, 39, 47, 32, 47, 44, 48, 48, 49, 32, 49, 46, 49, 30, 50, + 24, 50, 11, 50, 28, 50, 50, 51, 40, 51, 8, 51, 22, 51, 21, 51, 3, 52, 40, 52, 5, + 52, 52, 53, 25, 53, 48, 53, 49, 53, 46, 53, 39, 54, 31, 54, 38, 54, 14, 54, 34, + 54, 18, 54, 54, 55, 31, 55, 6, 55, 35, 55, 29, 55, 19, 55, 30, 55, 27, 56, 56, + 57, 1, 57, 42, 57, 44, 57, 48, 57, 3, 58, 6, 58, 17, 58, 36, 58, 36, 59, 58, 59, + 59, 60, 10, 60, 39, 60, 6, 60, 47, 60, 13, 60, 15, 60, 2, 60, 43, 61, 47, 61, + 54, 61, 18, 61, 26, 61, 31, 61, 34, 61, 61, 62, 20, 62, 45, 62, 17, 62, 27, 62, + 56, 62, 27, 63, 58, 63, 59, 63, 42, 63, 63, 64, 9, 64, 32, 64, 60, 64, 2, 64, 6, + 64, 47, 64, 13, 64, 0, 65, 27, 65, 17, 65, 63, 65, 56, 65, 20, 65, 65, 66, 59, + 66, 24, 66, 44, 66, 48, 66, 16, 67, 41, 67, 46, 67, 53, 67, 49, 67, 67, 68, 15, + 68, 50, 68, 21, 68, 51, 68, 7, 68, 22, 68, 8, 68, 4, 69, 24, 69, 28, 69, 50, 69, + 11, 69, 69, 70, 43, 70, 65, 70, 20, 70, 56, 70, 62, 70, 27, 70, 60, 71, 18, 71, + 14, 71, 34, 71, 54, 71, 38, 71, 61, 71, 31, 71, 71, 72, 2, 72, 10, 72, 3, 72, + 40, 72, 52, 72, 7, 73, 49, 73, 53, 73, 67, 73, 46, 73, 73, 74, 2, 74, 72, 74, 5, + 74, 10, 74, 52, 74, 3, 74, 40, 74, 20, 75, 66, 75, 48, 75, 57, 75, 44, 75, 75, + 76, 27, 76, 59, 76, 20, 76, 70, 76, 66, 76, 56, 76, 62, 76, 73, 77, 22, 77, 7, + 77, 51, 77, 21, 77, 8, 77, 77, 78, 23, 78, 50, 78, 28, 78, 22, 78, 8, 78, 68, + 78, 7, 78, 51, 78, 31, 79, 43, 79, 30, 79, 19, 79, 29, 79, 35, 79, 55, 79, 79, + 80, 37, 80, 29, 80, 16, 81, 5, 81, 40, 81, 10, 81, 72, 81, 3, 81, 81, 82, 74, + 82, 39, 82, 77, 82, 80, 82, 30, 82, 29, 82, 7, 82, 53, 83, 81, 83, 69, 83, 73, + 83, 46, 83, 67, 83, 49, 83, 83, 84, 24, 84, 49, 84, 52, 84, 3, 84, 74, 84, 10, + 84, 81, 84, 5, 84, 3, 84, 6, 85, 14, 85, 38, 85, 43, 85, 80, 85, 12, 85, 26, 85, + 31, 85, 44, 86, 53, 86, 75, 86, 57, 86, 48, 86, 80, 86, 66, 86, 86, 87, 17, 87, + 62, 87, 56, 87, 24, 87, 20, 87, 65, 87, 49, 88, 58, 88, 83, 88, 69, 88, 46, 88, + 53, 88, 73, 88, 67, 88, 88, 89, 1, 89, 37, 89, 25, 89, 33, 89, 55, 89, 45, 89, + 5, 90, 8, 90, 23, 90, 0, 90, 11, 90, 50, 90, 24, 90, 69, 90, 28, 90, 29, 91, 48, + 91, 66, 91, 69, 91, 44, 91, 86, 91, 57, 91, 80, 91, 91, 92, 35, 92, 15, 92, 86, + 92, 48, 92, 57, 92, 61, 92, 66, 92, 75, 92, 0, 93, 23, 93, 80, 93, 16, 93, 4, + 93, 82, 93, 91, 93, 41, 93, 9, 93, 34, 94, 19, 94, 55, 94, 79, 94, 80, 94, 29, + 94, 30, 94, 82, 94, 35, 94, 70, 95, 69, 95, 76, 95, 62, 95, 56, 95, 27, 95, 17, + 95, 87, 95, 37, 95, 48, 96, 17, 96, 76, 96, 27, 96, 56, 96, 65, 96, 20, 96, 87, + 96, 5, 97, 86, 97, 58, 97, 11, 97, 59, 97, 63, 97, 97, 98, 77, 98, 48, 98, 84, + 98, 40, 98, 10, 98, 5, 98, 52, 98, 81, 98, 89, 99, 34, 99, 14, 99, 85, 99, 54, + 99, 18, 99, 31, 99, 61, 99, 71, 99, 14, 99, 99, 100, 82, 100, 13, 100, 2, 100, + 15, 100, 32, 100, 64, 100, 47, 100, 39, 100, 6, 100, 51, 101, 30, 101, 94, + 101, 1, 101, 79, 101, 58, 101, 19, 101, 55, 101, 35, 101, 29, 101, 100, 102, + 74, 102, 52, 102, 98, 102, 72, 102, 40, 102, 10, 102, 3, 102, 102, 103, 33, + 103, 45, 103, 25, 103, 89, 103, 37, 103, 1, 103, 70, 103, 72, 104, 11, 104, + 0, 104, 93, 104, 67, 104, 41, 104, 16, 104, 87, 104, 23, 104, 4, 104, 9, 104, + 89, 105, 103, 105, 33, 105, 62, 105, 37, 105, 45, 105, 1, 105, 80, 105, 25, + 105, 25, 106, 56, 106, 92, 106, 2, 106, 13, 106, 32, 106, 60, 106, 6, 106, + 64, 106, 15, 106, 39, 106, 88, 107, 75, 107, 98, 107, 102, 107, 72, 107, 40, + 107, 81, 107, 5, 107, 10, 107, 84, 107, 4, 108, 9, 108, 7, 108, 51, 108, 77, + 108, 21, 108, 78, 108, 22, 108, 68, 108, 79, 109, 30, 109, 63, 109, 1, 109, + 33, 109, 103, 109, 105, 109, 45, 109, 25, 109, 89, 109, 37, 109, 67, 110, + 13, 110, 24, 110, 80, 110, 88, 110, 49, 110, 73, 110, 46, 110, 83, 110, 53, + 110, 23, 111, 64, 111, 46, 111, 78, 111, 8, 111, 21, 111, 51, 111, 7, 111, + 108, 111, 68, 111, 77, 111, 52, 112, 96, 112, 97, 112, 57, 112, 66, 112, 63, + 112, 44, 112, 92, 112, 75, 112, 91, 112, 28, 113, 20, 113, 95, 113, 59, 113, + 70, 113, 17, 113, 87, 113, 76, 113, 65, 113, 96, 113, 83, 114, 88, 114, 110, + 114, 53, 114, 49, 114, 73, 114, 46, 114, 67, 114, 58, 114, 15, 114, 104, 114, + -1); + igraph_simplify(&g, /*remove_multiple=*/ true, /*remove_loops=*/ true, /*edge_comb=*/ NULL); + igraph_vector_int_view(&types, football_types, sizeof(football_types) / sizeof(football_types[0])); + igraph_assortativity_nominal(&g, &types, &assort, IGRAPH_UNDIRECTED, /*normalized=*/ true); + printf("%g\n", assort); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/assortativity.out b/tests/unit/assortativity.out new file mode 100644 index 0000000..a48d382 --- /dev/null +++ b/tests/unit/assortativity.out @@ -0,0 +1,33 @@ +Null graph nominal assortativity: NaN +Null graph value assortativity: NaN +Singleton graph nominal assortativity: NaN +Singleton graph nominal assortativity, unnormalized: NaN +Singleton graph value assortativity: NaN +Singleton graph value assortativity, unnormalized: NaN +Singleton graph with loop, nominal assortativity: NaN +Singleton graph with loop, nominal assortativity, unnormalized: 0 +Singleton graph with loop, value assortativity: NaN +Singleton graph with loop, value assortativity, unnormalized: 0 + +Karate club, normalized assortativity based on degree categories: -0.077745 +Karate club, non-normalized assortativity based on degree categories: -0.0693623 +Assortativity based on values: 0.448049 +Assortativity based on values, unnormalized: 69.3478 + +Degree assortativity, NORMALIZED +Degree assortativity: -0.475613 +Degree assortativity, directed: -0.666667 +Degree assortativity, undirected, with self-loop: 0.166667 +Degree assortativity, directed, with self-loop: -0.707107 +Degree assortativity, undirected, with multi-edges: -0.111111 +Degree assortativity, directed, with multi-edges: -0.333333 + +Degree assortativity, UNNORMALIZED +Degree assortativity: -13.6943 +Degree assortativity, directed: -0.16 +Degree assortativity, undirected, with self-loop: 0.04 +Degree assortativity, directed, with self-loop: -0.333333 +Degree assortativity, undirected, with multi-edges: -0.04 +Degree assortativity, directed, with multi-edges: -0.333333 + +Football network: 0.607938 diff --git a/tests/unit/bfs.c b/tests/unit/bfs.c new file mode 100644 index 0000000..0490e03 --- /dev/null +++ b/tests/unit/bfs.c @@ -0,0 +1,150 @@ +/* -*- 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 + +*/ + +#include + +#include "test_utilities.h" + +igraph_error_t bfs_callback(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) { + IGRAPH_UNUSED(graph); + IGRAPH_UNUSED(pred); + IGRAPH_UNUSED(succ); + IGRAPH_UNUSED(rank); + IGRAPH_UNUSED(dist); + IGRAPH_UNUSED(extra); + printf(" %" IGRAPH_PRId, vid); + return IGRAPH_SUCCESS; +} + +int main(void) { + + igraph_t graph, ring; + igraph_vector_int_t restricted, order, rank, father, pred, succ, dist, roots; + igraph_integer_t i; + + igraph_ring(&ring, 10, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + igraph_disjoint_union(&graph, &ring, &ring); + igraph_destroy(&ring); + + igraph_vector_int_init(&order, 0); + igraph_vector_int_init(&rank, 0); + igraph_vector_int_init(&father, 0); + igraph_vector_int_init(&pred, 0); + igraph_vector_int_init(&succ, 0); + igraph_vector_int_init(&dist, 0); + + igraph_bfs(&graph, /*root=*/0, /*roots=*/ 0, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 1, /*restricted=*/ 0, + &order, &rank, &father, &pred, &succ, &dist, + /*callback=*/ 0, /*extra=*/ 0); + + print_vector_int(&order); + print_vector_int(&rank); + print_vector_int(&father); + print_vector_int(&pred); + print_vector_int(&succ); + print_vector_int(&dist); + + igraph_vector_int_destroy(&order); + igraph_vector_int_destroy(&rank); + igraph_vector_int_destroy(&father); + igraph_vector_int_destroy(&pred); + igraph_vector_int_destroy(&succ); + igraph_vector_int_destroy(&dist); + + /* Test the callback */ + + printf("("); + igraph_bfs(&graph, /*root=*/ 0, /*roots=*/ 0, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 1, /*restricted=*/ 0, + 0, 0, 0, 0, 0, 0, &bfs_callback, 0); + printf(" )\n"); + + /* Test different roots */ + + printf("("); + igraph_bfs(&graph, /*root=*/ 2, /*roots=*/ 0, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 1, /*restricted=*/ 0, + 0, 0, 0, 0, 0, 0, &bfs_callback, 0); + printf(" )\n"); + + /* Test restricted */ + + igraph_vector_int_init(&restricted, 0); + for (i = 5; i < igraph_vcount(&graph); i++) { + igraph_vector_int_push_back(&restricted, i); + } + printf("("); + igraph_bfs(&graph, /*root=*/ 5, /*roots=*/ 0, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 1, &restricted, + 0, 0, 0, 0, 0, 0, &bfs_callback, 0); + printf(" )\n"); + + /* Root not in restricted set */ + + printf("("); + igraph_bfs(&graph, /*root=*/ 4, /*roots=*/ 0, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 1, &restricted, + 0, 0, 0, 0, 0, 0, &bfs_callback, 0); + printf(" )\n"); + + printf("("); + igraph_bfs(&graph, /*root=*/ 3, /*roots=*/ 0, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 0, &restricted, + 0, 0, 0, 0, 0, 0, &bfs_callback, 0); + printf(" )\n"); + + /* Multiple root vertices */ + + igraph_vector_int_init(&roots, 3); + VECTOR(roots)[0] = 3; + VECTOR(roots)[1] = 4; + VECTOR(roots)[2] = 6; + printf("("); + igraph_bfs(&graph, /*root=*/ -1, &roots, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 0, &restricted, + 0, 0, 0, 0, 0, 0, &bfs_callback, 0); + printf(" )\n"); + + /* Empty root vertex vector */ + + igraph_vector_int_clear(&roots); + printf("("); + igraph_bfs(&graph, /*root=*/ -1, &roots, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ 0, &restricted, + 0, 0, 0, 0, 0, 0, &bfs_callback, 0); + printf(" )\n"); + + igraph_vector_int_destroy(&roots); + igraph_vector_int_destroy(&restricted); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/bfs.out b/tests/unit/bfs.out new file mode 100644 index 0000000..2681672 --- /dev/null +++ b/tests/unit/bfs.out @@ -0,0 +1,13 @@ +( 0 1 9 2 8 3 7 4 6 5 10 11 19 12 18 13 17 14 16 15 ) +( 0 1 3 5 7 9 8 6 4 2 10 11 13 15 17 19 18 16 14 12 ) +( -1 0 1 2 3 4 7 8 9 0 -1 10 11 12 13 14 17 18 19 10 ) +( -1 0 9 8 7 6 4 3 2 1 -1 10 19 18 17 16 14 13 12 11 ) +( 1 9 8 7 6 -1 5 4 3 2 11 19 18 17 16 -1 15 14 13 12 ) +( 0 1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1 ) +( 0 1 9 2 8 3 7 4 6 5 10 11 19 12 18 13 17 14 16 15 ) +( 2 1 3 0 4 9 5 8 6 7 10 11 19 12 18 13 17 14 16 15 ) +( 5 6 7 8 9 10 11 19 12 18 13 17 14 16 15 ) +( 5 6 7 8 9 10 11 19 12 18 13 17 14 16 15 ) +( ) +( 6 5 7 8 9 ) +( ) diff --git a/tests/unit/bfs_simple.c b/tests/unit/bfs_simple.c new file mode 100644 index 0000000..fc92fd4 --- /dev/null +++ b/tests/unit/bfs_simple.c @@ -0,0 +1,78 @@ +/* -*- 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 + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t vids, layers, parents; + + igraph_vector_int_init(&vids, 0); + igraph_vector_int_init(&layers, 0); + igraph_vector_int_init(&parents, 0); + + /* Test a ring graph */ + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, 0, 0); + igraph_bfs_simple(&g, 0, IGRAPH_ALL, &vids, &layers, &parents); + print_vector_int(&vids); + print_vector_int(&layers); + print_vector_int(&parents); + igraph_destroy(&g); + + /* Test a tree graph */ + igraph_kary_tree(&g, 20, 2, IGRAPH_TREE_UNDIRECTED); + igraph_bfs_simple(&g, 0, IGRAPH_ALL, &vids, &layers, &parents); + print_vector_int(&vids); + print_vector_int(&layers); + print_vector_int(&parents); + igraph_destroy(&g); + + /* Test th same graph with all arguments as nulls to see if we tolerate that */ + igraph_kary_tree(&g, 20, 2, IGRAPH_TREE_UNDIRECTED); + igraph_bfs_simple(&g, 0, IGRAPH_ALL, 0, 0, 0); + igraph_destroy(&g); + + /* Also test the case when 'layers' is not null and 'vids' is null to ensure + * that we don't need 'vids' in the internal implementation to populate + * 'layers' */ + igraph_kary_tree(&g, 20, 2, IGRAPH_TREE_UNDIRECTED); + igraph_bfs_simple(&g, 0, IGRAPH_ALL, 0, &layers, 0); + print_vector_int(&layers); + igraph_destroy(&g); + + /* Test directed graph where not all nodes are reachable */ + igraph_kary_tree(&g, 20, 2, IGRAPH_TREE_OUT); + igraph_bfs_simple(&g, 7, IGRAPH_OUT, 0, &layers, &parents); + print_vector_int(&layers); + print_vector_int(&parents); + igraph_destroy(&g); + + igraph_vector_int_destroy(&vids); + igraph_vector_int_destroy(&layers); + igraph_vector_int_destroy(&parents); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/bfs_simple.out b/tests/unit/bfs_simple.out new file mode 100644 index 0000000..25ed9dd --- /dev/null +++ b/tests/unit/bfs_simple.out @@ -0,0 +1,9 @@ +( 0 1 2 3 4 5 6 7 8 9 ) +( 0 1 2 3 4 5 6 7 8 9 10 ) +( -1 0 1 2 3 4 5 6 7 8 ) +( 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ) +( 0 1 3 7 15 20 ) +( -1 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 ) +( 0 1 3 7 15 20 ) +( 0 1 3 ) +( -2 -2 -2 -2 -2 -2 -2 -1 -2 -2 -2 -2 -2 -2 -2 7 7 -2 -2 -2 ) diff --git a/tests/unit/bipartite.net b/tests/unit/bipartite.net new file mode 100644 index 0000000..2d4f2c9 --- /dev/null +++ b/tests/unit/bipartite.net @@ -0,0 +1,26 @@ +*Vertices 13 8 +1 "A" +2 "B" +3 "C" +4 "D" +5 "E" +6 "F" +7 "G" +8 "H" +9 "x-1" +10 "x-2" +11 "x-3" +12 "x-4" +13 "x-5" +*Edges +1 10 +1 13 +2 12 +3 10 +3 11 +4 11 +5 12 +5 13 +6 12 +8 11 +8 13 diff --git a/tests/unit/bitset.c b/tests/unit/bitset.c new file mode 100644 index 0000000..e4fd46d --- /dev/null +++ b/tests/unit/bitset.c @@ -0,0 +1,405 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_bitset_t v1, v2, v3; + igraph_integer_t n; + + printf("Initialise empty bitset\n"); + n = 0; + igraph_bitset_init(&v1, n); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == n); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Initialise bitset of length 32\n"); + n = 32; + igraph_bitset_init(&v1, n); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Initialise bitset of length 64\n"); + n = 64; + igraph_bitset_init(&v1, n); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Initialise bitset of length 75\n"); + n = 75; + igraph_bitset_init(&v1, n); + print_bitset(&v1); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == n); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test IGRAPH_BIT_SET\n"); + n = 35; + igraph_bitset_init(&v1, n); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 24); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 13); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 17); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 34); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 13); + print_bitset(&v1); + printf("\n"); + + printf("Test IGRAPH_BIT_CLEAR\n"); + IGRAPH_BIT_CLEAR(v1, 33); + print_bitset(&v1); + IGRAPH_BIT_CLEAR(v1, 34); + print_bitset(&v1); + IGRAPH_BIT_CLEAR(v1, 17); + print_bitset(&v1); + printf("\n"); + + printf("Test printing\n"); + igraph_bitset_print(&v1); + printf("\n\n"); + + printf("Test bitset copy constructor\n"); + igraph_bitset_init_copy(&v2, &v1); + print_bitset(&v2); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + printf("\n"); + + printf("Test bitset resize\n"); + igraph_bitset_init(&v1, 0); + print_bitset(&v1); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == 0); + igraph_bitset_resize(&v1, 10); + print_bitset(&v1); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == 10); + IGRAPH_BIT_SET(v1, 3); + print_bitset(&v1); + igraph_bitset_resize(&v1, 64); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 63); + print_bitset(&v1); + igraph_bitset_resize(&v1, 70); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 64); + print_bitset(&v1); + igraph_bitset_resize(&v1, 64); + print_bitset(&v1); + igraph_bitset_resize(&v1, 63); + print_bitset(&v1); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == 63); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test OR\n"); + n = 7; + igraph_bitset_init(&v1, n); + igraph_bitset_init(&v2, n); + igraph_bitset_init(&v3, n); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + IGRAPH_BIT_SET(v2, 1); + IGRAPH_BIT_SET(v2, 2); + IGRAPH_BIT_SET(v2, 5); + print_bitset(&v2); + IGRAPH_BIT_SET(v3, 2); + IGRAPH_BIT_SET(v3, 3); + IGRAPH_BIT_SET(v3, 6); + print_bitset(&v3); + igraph_bitset_or(&v1, &v2, &v3); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + igraph_bitset_destroy(&v3); + printf("\n"); + + printf("Test AND\n"); + n = 7; + igraph_bitset_init(&v1, n); + igraph_bitset_init(&v2, n); + igraph_bitset_init(&v3, n); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + IGRAPH_BIT_SET(v2, 1); + IGRAPH_BIT_SET(v2, 2); + IGRAPH_BIT_SET(v2, 5); + print_bitset(&v2); + IGRAPH_BIT_SET(v3, 2); + IGRAPH_BIT_SET(v3, 3); + IGRAPH_BIT_SET(v3, 6); + print_bitset(&v3); + igraph_bitset_and(&v1, &v2, &v3); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + igraph_bitset_destroy(&v3); + printf("\n"); + + printf("Test XOR\n"); + n = 7; + igraph_bitset_init(&v2, n); + igraph_bitset_init(&v1, n); + igraph_bitset_init(&v3, n); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + IGRAPH_BIT_SET(v2, 1); + IGRAPH_BIT_SET(v2, 2); + IGRAPH_BIT_SET(v2, 5); + print_bitset(&v2); + IGRAPH_BIT_SET(v3, 2); + IGRAPH_BIT_SET(v3, 3); + IGRAPH_BIT_SET(v3, 6); + print_bitset(&v3); + igraph_bitset_xor(&v1, &v2, &v3); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + igraph_bitset_destroy(&v3); + printf("\n"); + + printf("Test NOT\n"); + n = 7; + igraph_bitset_init(&v1, n); + igraph_bitset_init(&v2, n); + IGRAPH_BIT_SET(v1, 1); + print_bitset(&v1); + IGRAPH_BIT_SET(v2, 1); + IGRAPH_BIT_SET(v2, 2); + IGRAPH_BIT_SET(v2, 5); + print_bitset(&v2); + igraph_bitset_not(&v1, &v2); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + printf("\n"); + + printf("Test popcount\n"); + n = 75; + igraph_bitset_init(&v1, n); + print_bitset(&v1); + + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + for (igraph_integer_t i = 2; i < n; i++) { + if (i == 2 || i == 3 || i == 5 || i == 7 || i == 11 || !(i%2 == 0 || i%3 == 0 || i%5 == 0 || i%7 == 0 || i%11 == 0)) { + IGRAPH_BIT_SET(v1, i); + } + } + print_bitset(&v1); + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + IGRAPH_BIT_CLEAR(v1, 67); + print_bitset(&v1); + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + IGRAPH_BIT_SET(v1, 9); + print_bitset(&v1); + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test count leading zeros\n"); + n = 67; + igraph_bitset_init(&v1, n); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_SET(v1, 23); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_CLEAR(v1, 0); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_SET(v1, n - 3); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_SET(v1, n - 1); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + + printf("Test count leading ones\n"); + n = 67; + igraph_bitset_init(&v1, n); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 0); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 23); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_CLEAR(v1, n - 3); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_CLEAR(v1, n - 1); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + + printf("Test count trailing zeros\n"); + n = 67; + igraph_bitset_init(&v1, n); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_SET(v1, 23); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_CLEAR(v1, 0); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_CLEAR(v1, 23); + IGRAPH_BIT_SET(v1, n - 1); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_SET(v1, 2); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test count trailing ones\n"); + n = 67; + igraph_bitset_init(&v1, n); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 23); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 0); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_SET(v1, 23); + IGRAPH_BIT_CLEAR(v1, n - 1); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 2); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test checking all elements\n"); + /* 0000 */ + igraph_bitset_init(&v1, 4); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* 1000 */ + IGRAPH_BIT_SET(v1, 3); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* 000 */ + /* This resize leaves a set bit in the unused part of the last word of the. + * bitset. This helps test that masking out the unused part is working. */ + igraph_bitset_resize(&v1, 3); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* 001 */ + IGRAPH_BIT_SET(v1, 0); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* 111 */ + IGRAPH_BIT_SET(v1, 1); IGRAPH_BIT_SET(v1, 2); + IGRAPH_ASSERT(igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_zero(&v1)); + igraph_bitset_destroy(&v1); + + igraph_bitset_init(&v1, 67); + /* all zeros */ + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* some ones */ + IGRAPH_BIT_SET(v1, 10); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* all ones */ + igraph_bitset_fill(&v1, true); + IGRAPH_ASSERT(igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_zero(&v1)); + /* some zeros */ + IGRAPH_BIT_CLEAR(v1, 20); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + igraph_bitset_destroy(&v1); + + /* Empty bitset */ + igraph_bitset_init(&v1, 0); + IGRAPH_ASSERT(igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_zero(&v1)); + igraph_bitset_destroy(&v1); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/bitset.out b/tests/unit/bitset.out new file mode 100644 index 0000000..c7c5561 --- /dev/null +++ b/tests/unit/bitset.out @@ -0,0 +1,137 @@ +Initialise empty bitset + +Initialise bitset of length 32 +( 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 ) + +Initialise bitset of length 64 +( 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 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 ) + +Initialise bitset of length 75 +( 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 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 0 0 0 0 0 0 0 0 0 0 0 ) + +Test IGRAPH_BIT_SET +( 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 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) + +Test IGRAPH_BIT_CLEAR +( 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) + +Test printing +00000000001000000000010000000000001 + +Test bitset copy constructor +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) + +Test bitset resize +( ) +( 0 0 0 0 0 0 0 0 0 0 ) +( 0 0 0 0 0 0 1 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 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 1 0 0 0 ) +( 1 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 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 1 0 0 0 ) +( 0 0 0 0 0 0 1 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 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 1 0 0 0 ) +( 0 0 0 0 0 1 1 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 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 1 0 0 0 ) +( 1 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 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 1 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 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 1 0 0 0 ) + +Test OR +( 0 0 0 0 0 0 1 ) +( 0 1 0 0 1 1 0 ) +( 1 0 0 1 1 0 0 ) +( 1 1 0 1 1 1 0 ) + +Test AND +( 0 0 0 0 0 0 1 ) +( 0 1 0 0 1 1 0 ) +( 1 0 0 1 1 0 0 ) +( 0 0 0 0 1 0 0 ) + +Test XOR +( 0 0 0 0 0 0 1 ) +( 0 1 0 0 1 1 0 ) +( 1 0 0 1 1 0 0 ) +( 1 1 0 1 0 1 0 ) + +Test NOT +( 0 0 0 0 0 1 0 ) +( 0 1 0 0 1 1 0 ) +( 1 0 1 1 0 0 1 ) + +Test popcount +( 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 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 0 0 0 0 0 0 0 0 0 0 0 ) +Popcount: 0 +( 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 1 0 0 ) +Popcount: 21 +( 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 1 0 0 ) +Popcount: 20 +( 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 1 1 0 0 ) +Popcount: 21 +( 1 0 1 0 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 0 1 1 1 1 1 0 1 0 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 0 1 1 ) +Popcount: 54 + +Test count leading zeros +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading zeros: 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 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 0 0 0 0 ) +Leading zeros: 67 +( 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 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 0 0 1 ) +Leading zeros: 66 +( 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 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +Leading zeros: 43 +( 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 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Leading zeros: 43 +( 0 0 1 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 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Leading zeros: 2 +( 1 0 1 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 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Leading zeros: 0 + +Test count leading ones +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading ones: 67 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 ) +Leading ones: 66 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 ) +Leading ones: 43 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading ones: 43 +( 1 1 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 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading ones: 2 +( 0 1 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 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading ones: 0 + +Test count trailing zeros +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing zeros: 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 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 0 0 0 0 ) +Trailing zeros: 67 +( 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 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 0 0 1 ) +Trailing zeros: 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 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +Trailing zeros: 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 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Trailing zeros: 23 +( 1 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 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 0 0 ) +Trailing zeros: 66 +( 1 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 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 1 0 0 ) +Trailing zeros: 2 + +Test count trailing ones +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing ones: 67 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing ones: 23 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 ) +Trailing ones: 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 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing ones: 23 +( 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing ones: 66 +( 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 ) +Trailing ones: 2 + +Test checking all elements diff --git a/tests/unit/bliss_automorphisms.c b/tests/unit/bliss_automorphisms.c new file mode 100644 index 0000000..0f46a83 --- /dev/null +++ b/tests/unit/bliss_automorphisms.c @@ -0,0 +1,57 @@ +/* + IGraph library. + Copyright (C) 2020-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, see . +*/ + +#include + +#include "test_utilities.h" + +#define TEST_GRAPH(name) \ + igraph_count_automorphisms(&graph, NULL, IGRAPH_BLISS_F, &info); \ + printf("%s: %s\n", name, info.group_size); \ + igraph_free(info.group_size); \ + igraph_destroy(&graph); + +#define TEST_FAMOUS(name) \ + igraph_famous(&graph, name); \ + TEST_GRAPH(name); + +int main(void) { + igraph_t graph; + igraph_bliss_info_t info; + + TEST_FAMOUS("Frucht"); + TEST_FAMOUS("Coxeter"); + TEST_FAMOUS("Petersen"); + TEST_FAMOUS("Meredith"); + + igraph_full(&graph, 23, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + TEST_GRAPH("Complete 23"); + + igraph_star(&graph, 17, IGRAPH_STAR_OUT, 0); + TEST_GRAPH("Directed star 17"); + + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + TEST_GRAPH("Null graph"); + + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + TEST_GRAPH("Singleton graph"); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/bliss_automorphisms.out b/tests/unit/bliss_automorphisms.out new file mode 100644 index 0000000..eb69fdf --- /dev/null +++ b/tests/unit/bliss_automorphisms.out @@ -0,0 +1,8 @@ +Frucht: 1 +Coxeter: 336 +Petersen: 120 +Meredith: 38698352640 +Complete 23: 25852016738884976640000 +Directed star 17: 20922789888000 +Null graph: 1 +Singleton graph: 1 diff --git a/tests/unit/cattributes5.c b/tests/unit/cattributes5.c new file mode 100644 index 0000000..1cdc496 --- /dev/null +++ b/tests/unit/cattributes5.c @@ -0,0 +1,98 @@ +/* -*- 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 + +#include "test_utilities.h" + +static void simplify_write_destroy(igraph_t *g, igraph_attribute_combination_t *comb) { + igraph_simplify(g, /*remove_multiple=*/ true, /*remove_loops=*/ true, comb); + igraph_write_graph_graphml(g, stdout, /*prefixattr=*/ true); + igraph_attribute_combination_destroy(comb); + igraph_destroy(g); +} + +static void type_test(igraph_t *g, igraph_attribute_combination_type_t type_attr) { + igraph_t g2; + igraph_attribute_combination_t comb; + + igraph_copy(&g2, g); + igraph_attribute_combination(&comb, + "type", type_attr, + "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); +} + +int main(void) { + + igraph_t g, g2; + igraph_attribute_combination_t comb; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_small(&g, 4, IGRAPH_DIRECTED, + 0, 1, 0, 1, 0, 1, + 1, 2, 2, 3, + -1); + + SETEAB(&g, "type", 0, 1); + SETEAB(&g, "type", 1, 1); + SETEAB(&g, "type", 2, 0); + SETEAB(&g, "type", 3, 0); + SETEAB(&g, "type", 4, 1); + + /* ****************************************************** */ + + igraph_copy(&g2, &g); + igraph_attribute_combination(&comb, + "weight", IGRAPH_ATTRIBUTE_COMBINE_SUM, + "type", IGRAPH_ATTRIBUTE_COMBINE_FIRST, + "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); + + /* ****************************************************** */ + + igraph_copy(&g2, &g); + igraph_attribute_combination(&comb, + "", IGRAPH_ATTRIBUTE_COMBINE_LAST, + IGRAPH_NO_MORE_ATTRIBUTES); + simplify_write_destroy(&g2, &comb); + + /* ****************************************************** */ + + type_test(&g, IGRAPH_ATTRIBUTE_COMBINE_LAST); + type_test(&g, IGRAPH_ATTRIBUTE_COMBINE_SUM); + type_test(&g, IGRAPH_ATTRIBUTE_COMBINE_PROD); + type_test(&g, IGRAPH_ATTRIBUTE_COMBINE_MIN); + type_test(&g, IGRAPH_ATTRIBUTE_COMBINE_MAX); + type_test(&g, IGRAPH_ATTRIBUTE_COMBINE_MEAN); + type_test(&g, IGRAPH_ATTRIBUTE_COMBINE_MEDIAN); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/cattributes5.out b/tests/unit/cattributes5.out new file mode 100644 index 0000000..79ccfab --- /dev/null +++ b/tests/unit/cattributes5.out @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + true + + + false + + + true + + + + + + + + + + + + + + + + + + false + + + false + + + true + + + + + + + + + + + + + + + + + + false + + + false + + + true + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + + + + + + + + + + + + + + + + false + + + false + + + true + + + + + + + + + + + + + + + + + + false + + + false + + + true + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + diff --git a/tests/unit/cattributes6.c b/tests/unit/cattributes6.c new file mode 100644 index 0000000..7be5f4c --- /dev/null +++ b/tests/unit/cattributes6.c @@ -0,0 +1,323 @@ +/* -*- 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 + +#include "test_utilities.h" + +#include +#include + + +static void check_vector_queries(const igraph_t *g) { + igraph_vector_t vec; + igraph_strvector_t svec; + igraph_vector_bool_t bvec; + igraph_strvector_t vnames, enames; + igraph_vector_int_t vtypes, etypes; + igraph_integer_t i, j; + + igraph_vector_int_init(&vtypes, 0); + igraph_vector_int_init(&etypes, 0); + igraph_strvector_init(&vnames, 0); + igraph_strvector_init(&enames, 0); + + + igraph_vector_init(&vec, 0); + igraph_strvector_init(&svec, 0); + igraph_vector_bool_init(&bvec, 0); + + igraph_cattribute_list(g, 0, 0, &vnames, &vtypes, + &enames, &etypes); + for (j = 0; j < igraph_strvector_size(&vnames); j++) { + if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_cattribute_VANV(g, igraph_strvector_get(&vnames, j), igraph_vss_all(), &vec); + for (i = 0; i < igraph_vcount(g); i++) { + igraph_real_t num = VAN(g, igraph_strvector_get(&vnames, j), i); + if (num != VECTOR(vec)[i] && + (!isnan(num) || !isnan(VECTOR(vec)[i]))) { + exit(51); + } + } + } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_STRING) { + igraph_cattribute_VASV(g, igraph_strvector_get(&vnames, j), igraph_vss_all(), &svec); + for (i = 0; i < igraph_vcount(g); i++) { + const char *str = VAS(g, igraph_strvector_get(&vnames, j), i); + if (strcmp(str, igraph_strvector_get(&svec, i))) { + exit(52); + } + } + } else { + igraph_cattribute_VABV(g, igraph_strvector_get(&vnames, j), igraph_vss_all(), &bvec); + for (i = 0; i < igraph_vcount(g); i++) { + igraph_bool_t b = VAB(g, igraph_strvector_get(&vnames, j), i); + if (b != VECTOR(bvec)[i]) { + exit(53); + } + } + } + } + + for (j = 0; j < igraph_strvector_size(&enames); j++) { + if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_cattribute_EANV(g, igraph_strvector_get(&enames, j), + igraph_ess_all(IGRAPH_EDGEORDER_ID), &vec); + for (i = 0; i < igraph_ecount(g); i++) { + igraph_real_t num = EAN(g, igraph_strvector_get(&enames, j), i); + if (num != VECTOR(vec)[i] && + (!isnan(num) || !isnan(VECTOR(vec)[i]))) { + exit(54); + } + } + } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_STRING) { + igraph_cattribute_EASV(g, igraph_strvector_get(&enames, j), + igraph_ess_all(IGRAPH_EDGEORDER_ID), &svec); + for (i = 0; i < igraph_ecount(g); i++) { + const char *str = EAS(g, igraph_strvector_get(&enames, j), i); + if (strcmp(str, igraph_strvector_get(&svec, i))) { + exit(55); + } + } + } else { + igraph_cattribute_EABV(g, igraph_strvector_get(&enames, j), + igraph_ess_all(IGRAPH_EDGEORDER_ID), &bvec); + for (i = 0; i < igraph_ecount(g); i++) { + igraph_bool_t b = EAB(g, igraph_strvector_get(&enames, j), i); + if (b != VECTOR(bvec)[i]) { + exit(56); + } + } + } + } + + igraph_strvector_destroy(&svec); + igraph_vector_destroy(&vec); + igraph_vector_bool_destroy(&bvec); + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_vector_int_destroy(&etypes); + igraph_vector_int_destroy(&vtypes); +} + +int main(void) { + + igraph_t g, g2; + FILE *ifile; + igraph_integer_t i; + igraph_vector_t y; + igraph_strvector_t id; + igraph_vector_bool_t type; + char str[21]; + + /* turn on attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + ifile = fopen("links.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + SETGAB(&g, "bool_graph_attr", 1); + SETEAB(&g, "bool_edge_attr", 0, 1); + SETVAB(&g, "bool_vertex_attr", 0, 1); + + print_attributes(&g); + + /* Copying a graph */ + igraph_copy(&g2, &g); + print_attributes(&g2); + igraph_destroy(&g2); + + /* Adding vertices */ + igraph_add_vertices(&g, 3, 0); + print_attributes(&g); + + /* Adding edges */ + igraph_add_edge(&g, 1, 1); + igraph_add_edge(&g, 2, 5); + igraph_add_edge(&g, 3, 6); + print_attributes(&g); + + /* Deleting vertices */ + igraph_delete_vertices(&g, igraph_vss_1(1)); + igraph_delete_vertices(&g, igraph_vss_1(4)); + print_attributes(&g); + + /* Deleting edges */ + igraph_delete_edges(&g, igraph_ess_1(igraph_ecount(&g) - 1)); + igraph_delete_edges(&g, igraph_ess_1(0)); + print_attributes(&g); + + /* Set graph attributes */ + SETGAN(&g, "id", 10); + SETGAS(&g, "name", "toy"); + SETGAB(&g, "is_regular", 0); + + printf("Before deleting some attributes:\n"); + print_attributes(&g); + /* Delete graph attributes */ + DELGA(&g, "id"); + DELGA(&g, "name"); + DELGA(&g, "is_regular"); + + /* Delete vertex attributes */ + DELVA(&g, "x"); + DELVA(&g, "shape"); + DELVA(&g, "xfact"); + DELVA(&g, "yfact"); + + /* Delete edge attributes */ + DELEA(&g, "hook1"); + DELEA(&g, "hook2"); + DELEA(&g, "label"); + + printf("After deleting some attributes:\n"); + print_attributes(&g); + + /* Set vertex attributes */ + SETVAN(&g, "y", 0, -1); + SETVAN(&g, "y", 1, 2.1); + + SETVAS(&g, "id", 0, "foo"); + SETVAS(&g, "id", 1, "bar"); + + SETVAB(&g, "type", 0, 1); + SETVAB(&g, "type", 1, 0); + + /* Set edge attributes */ + SETEAN(&g, "weight", 2, 100.0); + SETEAN(&g, "weight", 0, -100.1); + + SETEAS(&g, "color", 2, "RED"); + SETEAS(&g, "color", 0, "Blue"); + + SETEAB(&g, "type", 0, 1); + SETEAB(&g, "type", 2, 0); + + printf("After setting vertex and edge attributes:\n"); + print_attributes(&g); + check_vector_queries(&g); + + /* Set vertex attributes as vector */ + igraph_vector_init(&y, igraph_vcount(&g)); + igraph_vector_fill(&y, 1.23); + SETVANV(&g, "y", &y); + igraph_vector_destroy(&y); + + igraph_vector_init_range(&y, 0, igraph_vcount(&g)); + SETVANV(&g, "foobar", &y); + igraph_vector_destroy(&y); + + igraph_vector_bool_init(&type, igraph_vcount(&g)); + for (i = 0; i < igraph_vcount(&g); i++) { + VECTOR(type)[i] = (i % 2 == 1); + } + SETVABV(&g, "type", &type); + igraph_vector_bool_destroy(&type); + + igraph_strvector_init(&id, igraph_vcount(&g)); + for (i = 0; i < igraph_vcount(&g); i++) { + snprintf(str, sizeof(str) - 1, "%" IGRAPH_PRId, i); + igraph_strvector_set(&id, i, str); + } + SETVASV(&g, "foo", &id); + igraph_strvector_destroy(&id); + + igraph_strvector_init(&id, igraph_vcount(&g)); + for (i = 0; i < igraph_vcount(&g); i++) { + snprintf(str, sizeof(str) - 1, "%" IGRAPH_PRId, i); + igraph_strvector_set(&id, i, str); + } + SETVASV(&g, "id", &id); + igraph_strvector_destroy(&id); + + /* Set edge attributes as vector */ + igraph_vector_init(&y, igraph_ecount(&g)); + igraph_vector_fill(&y, 12.3); + SETEANV(&g, "weight", &y); + igraph_vector_destroy(&y); + + igraph_vector_init_range(&y, 0, igraph_ecount(&g)); + SETEANV(&g, "foobar", &y); + igraph_vector_destroy(&y); + + igraph_vector_bool_init(&type, igraph_ecount(&g)); + for (i = 0; i < igraph_ecount(&g); i++) { + VECTOR(type)[i] = (i % 2 == 1); + } + SETEABV(&g, "type", &type); + igraph_vector_bool_destroy(&type); + + igraph_strvector_init(&id, igraph_ecount(&g)); + for (i = 0; i < igraph_ecount(&g); i++) { + snprintf(str, sizeof(str) - 1, "%" IGRAPH_PRId, i); + igraph_strvector_set(&id, i, str); + } + SETEASV(&g, "foo", &id); + igraph_strvector_destroy(&id); + + igraph_strvector_init(&id, igraph_ecount(&g)); + for (i = 0; i < igraph_ecount(&g); i++) { + snprintf(str, sizeof(str) - 1, "%" IGRAPH_PRId, i); + igraph_strvector_set(&id, i, str); + } + SETEASV(&g, "color", &id); + igraph_strvector_destroy(&id); + + printf("After setting vertex and edge attributes by vector:\n"); + print_attributes(&g); + /* Delete all remaining attributes */ + DELALL(&g); + + printf("After deleting all attributes:\n"); + print_attributes(&g); + + igraph_destroy(&g); + + printf("Setting attributes on vertex 0, permuting with 2\n"); + { + igraph_t g2; + igraph_vector_int_t per; + + igraph_vector_int_init_int(&per, 3, 2, 1, 0); + + igraph_small(&g, 3, IGRAPH_DIRECTED, 0,1, 1,2, -1); + SETVAN(&g, "foo", 0, 5); + SETVAB(&g, "bar", 0, 1); + SETVAS(&g, "baz", 0, "foobar"); + printf("Permuting to different graph:\n"); + igraph_permute_vertices(&g, &g2, &per); + print_attributes(&g2); + printf("Permuting to same graph:\n"); + igraph_cattribute_table.permute_vertices(&g, &g, &per); + print_attributes(&g); + igraph_destroy(&g); + igraph_destroy(&g2); + igraph_vector_int_destroy(&per); + } + + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/cattributes6.out b/tests/unit/cattributes6.out new file mode 100644 index 0000000..fc0f996 --- /dev/null +++ b/tests/unit/cattributes6.out @@ -0,0 +1,152 @@ +bool_graph_attr=1 +Vertex 0: id="1" name="1" x=0.0938 y=0.0896 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=1 +Vertex 1: id="2" name="2" x=0.8188 y=0.2458 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=0 +Vertex 2: id="3" name="3" x=0.3688 y=0.7792 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 3: id="4" name="4" x=0.9583 y=0.8563 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Edge 0 (0-0): weight=1 hook2=0 edgewidth=3 color="Blue" arrowsize=3 angle1=-130 velocity1=0.6 angle2=-130 velocity2=0.6 arrowpos=0.5 label="Bezier loop" labelcolor="BlueViolet" fontsize=20 labelangle=58 labelpos=0.3 labeldegree=360 labelangle2=0 linepattern="" hook1=0 bool_edge_attr=1 +Edge 1 (1-0): weight=1 hook2=0 edgewidth=NaN color="" arrowsize=NaN angle1=120 velocity1=1.3 angle2=-120 velocity2=0.3 arrowpos=25 label="Bezier arc" labelcolor="" fontsize=NaN labelangle=19 labelpos=0.5 labeldegree=180 labelangle2=270 linepattern="" hook1=0 bool_edge_attr=0 +Edge 2 (0-1): weight=1 hook2=0 edgewidth=NaN color="" arrowsize=NaN angle1=40 velocity1=2.8 angle2=30 velocity2=0.8 arrowpos=25 label="Bezier arc" labelcolor="" fontsize=NaN labelangle=NaN labelpos=0.65 labeldegree=0 labelangle2=90 linepattern="" hook1=0 bool_edge_attr=0 +Edge 3 (3-1): weight=-1 hook2=0 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=250 arrowpos=25 label="Circular arc" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="" hook1=0 bool_edge_attr=0 +Edge 4 (2-3): weight=1 hook2=0 edgewidth=2 color="OliveGreen" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 label="Straight arc" labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 5 (0-2): weight=1 hook2=0 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 label="Oval arc" labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 6 (2-2): weight=-1 hook2=12 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 label="Circular loop" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" hook1=6 bool_edge_attr=0 + +bool_graph_attr=1 +Vertex 0: id="1" name="1" x=0.0938 y=0.0896 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=1 +Vertex 1: id="2" name="2" x=0.8188 y=0.2458 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=0 +Vertex 2: id="3" name="3" x=0.3688 y=0.7792 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 3: id="4" name="4" x=0.9583 y=0.8563 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Edge 0 (0-0): weight=1 hook2=0 edgewidth=3 color="Blue" arrowsize=3 angle1=-130 velocity1=0.6 angle2=-130 velocity2=0.6 arrowpos=0.5 label="Bezier loop" labelcolor="BlueViolet" fontsize=20 labelangle=58 labelpos=0.3 labeldegree=360 labelangle2=0 linepattern="" hook1=0 bool_edge_attr=1 +Edge 1 (1-0): weight=1 hook2=0 edgewidth=NaN color="" arrowsize=NaN angle1=120 velocity1=1.3 angle2=-120 velocity2=0.3 arrowpos=25 label="Bezier arc" labelcolor="" fontsize=NaN labelangle=19 labelpos=0.5 labeldegree=180 labelangle2=270 linepattern="" hook1=0 bool_edge_attr=0 +Edge 2 (0-1): weight=1 hook2=0 edgewidth=NaN color="" arrowsize=NaN angle1=40 velocity1=2.8 angle2=30 velocity2=0.8 arrowpos=25 label="Bezier arc" labelcolor="" fontsize=NaN labelangle=NaN labelpos=0.65 labeldegree=0 labelangle2=90 linepattern="" hook1=0 bool_edge_attr=0 +Edge 3 (3-1): weight=-1 hook2=0 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=250 arrowpos=25 label="Circular arc" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="" hook1=0 bool_edge_attr=0 +Edge 4 (2-3): weight=1 hook2=0 edgewidth=2 color="OliveGreen" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 label="Straight arc" labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 5 (0-2): weight=1 hook2=0 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 label="Oval arc" labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 6 (2-2): weight=-1 hook2=12 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 label="Circular loop" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" hook1=6 bool_edge_attr=0 + +bool_graph_attr=1 +Vertex 0: id="1" name="1" x=0.0938 y=0.0896 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=1 +Vertex 1: id="2" name="2" x=0.8188 y=0.2458 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=0 +Vertex 2: id="3" name="3" x=0.3688 y=0.7792 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 3: id="4" name="4" x=0.9583 y=0.8563 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 4: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Vertex 5: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Vertex 6: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Edge 0 (0-0): weight=1 hook2=0 edgewidth=3 color="Blue" arrowsize=3 angle1=-130 velocity1=0.6 angle2=-130 velocity2=0.6 arrowpos=0.5 label="Bezier loop" labelcolor="BlueViolet" fontsize=20 labelangle=58 labelpos=0.3 labeldegree=360 labelangle2=0 linepattern="" hook1=0 bool_edge_attr=1 +Edge 1 (1-0): weight=1 hook2=0 edgewidth=NaN color="" arrowsize=NaN angle1=120 velocity1=1.3 angle2=-120 velocity2=0.3 arrowpos=25 label="Bezier arc" labelcolor="" fontsize=NaN labelangle=19 labelpos=0.5 labeldegree=180 labelangle2=270 linepattern="" hook1=0 bool_edge_attr=0 +Edge 2 (0-1): weight=1 hook2=0 edgewidth=NaN color="" arrowsize=NaN angle1=40 velocity1=2.8 angle2=30 velocity2=0.8 arrowpos=25 label="Bezier arc" labelcolor="" fontsize=NaN labelangle=NaN labelpos=0.65 labeldegree=0 labelangle2=90 linepattern="" hook1=0 bool_edge_attr=0 +Edge 3 (3-1): weight=-1 hook2=0 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=250 arrowpos=25 label="Circular arc" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="" hook1=0 bool_edge_attr=0 +Edge 4 (2-3): weight=1 hook2=0 edgewidth=2 color="OliveGreen" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 label="Straight arc" labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 5 (0-2): weight=1 hook2=0 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 label="Oval arc" labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 6 (2-2): weight=-1 hook2=12 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 label="Circular loop" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" hook1=6 bool_edge_attr=0 + +bool_graph_attr=1 +Vertex 0: id="1" name="1" x=0.0938 y=0.0896 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=1 +Vertex 1: id="2" name="2" x=0.8188 y=0.2458 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=0 +Vertex 2: id="3" name="3" x=0.3688 y=0.7792 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 3: id="4" name="4" x=0.9583 y=0.8563 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 4: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Vertex 5: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Vertex 6: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Edge 0 (0-0): weight=1 hook2=0 edgewidth=3 color="Blue" arrowsize=3 angle1=-130 velocity1=0.6 angle2=-130 velocity2=0.6 arrowpos=0.5 label="Bezier loop" labelcolor="BlueViolet" fontsize=20 labelangle=58 labelpos=0.3 labeldegree=360 labelangle2=0 linepattern="" hook1=0 bool_edge_attr=1 +Edge 1 (1-0): weight=1 hook2=0 edgewidth=NaN color="" arrowsize=NaN angle1=120 velocity1=1.3 angle2=-120 velocity2=0.3 arrowpos=25 label="Bezier arc" labelcolor="" fontsize=NaN labelangle=19 labelpos=0.5 labeldegree=180 labelangle2=270 linepattern="" hook1=0 bool_edge_attr=0 +Edge 2 (0-1): weight=1 hook2=0 edgewidth=NaN color="" arrowsize=NaN angle1=40 velocity1=2.8 angle2=30 velocity2=0.8 arrowpos=25 label="Bezier arc" labelcolor="" fontsize=NaN labelangle=NaN labelpos=0.65 labeldegree=0 labelangle2=90 linepattern="" hook1=0 bool_edge_attr=0 +Edge 3 (3-1): weight=-1 hook2=0 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=250 arrowpos=25 label="Circular arc" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="" hook1=0 bool_edge_attr=0 +Edge 4 (2-3): weight=1 hook2=0 edgewidth=2 color="OliveGreen" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 label="Straight arc" labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 5 (0-2): weight=1 hook2=0 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 label="Oval arc" labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 6 (2-2): weight=-1 hook2=12 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 label="Circular loop" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" hook1=6 bool_edge_attr=0 +Edge 7 (1-1): weight=NaN hook2=NaN edgewidth=NaN color="" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=NaN label="" labelcolor="" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="" hook1=NaN bool_edge_attr=0 +Edge 8 (2-5): weight=NaN hook2=NaN edgewidth=NaN color="" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=NaN label="" labelcolor="" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="" hook1=NaN bool_edge_attr=0 +Edge 9 (3-6): weight=NaN hook2=NaN edgewidth=NaN color="" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=NaN label="" labelcolor="" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="" hook1=NaN bool_edge_attr=0 + +bool_graph_attr=1 +Vertex 0: id="1" name="1" x=0.0938 y=0.0896 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=1 +Vertex 1: id="3" name="3" x=0.3688 y=0.7792 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 2: id="4" name="4" x=0.9583 y=0.8563 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 3: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Vertex 4: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Edge 0 (0-0): weight=1 hook2=0 edgewidth=3 color="Blue" arrowsize=3 angle1=-130 velocity1=0.6 angle2=-130 velocity2=0.6 arrowpos=0.5 label="Bezier loop" labelcolor="BlueViolet" fontsize=20 labelangle=58 labelpos=0.3 labeldegree=360 labelangle2=0 linepattern="" hook1=0 bool_edge_attr=1 +Edge 1 (1-2): weight=1 hook2=0 edgewidth=2 color="OliveGreen" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 label="Straight arc" labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 2 (0-1): weight=1 hook2=0 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 label="Oval arc" labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 3 (1-1): weight=-1 hook2=12 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 label="Circular loop" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" hook1=6 bool_edge_attr=0 +Edge 4 (2-4): weight=NaN hook2=NaN edgewidth=NaN color="" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=NaN label="" labelcolor="" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="" hook1=NaN bool_edge_attr=0 + +bool_graph_attr=1 +Vertex 0: id="1" name="1" x=0.0938 y=0.0896 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=1 +Vertex 1: id="3" name="3" x=0.3688 y=0.7792 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 2: id="4" name="4" x=0.9583 y=0.8563 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 3: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Vertex 4: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Edge 0 (1-2): weight=1 hook2=0 edgewidth=2 color="OliveGreen" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 label="Straight arc" labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 1 (0-1): weight=1 hook2=0 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 label="Oval arc" labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 2 (1-1): weight=-1 hook2=12 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 label="Circular loop" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" hook1=6 bool_edge_attr=0 + +Before deleting some attributes: +bool_graph_attr=1 id=10 name="toy" is_regular=0 +Vertex 0: id="1" name="1" x=0.0938 y=0.0896 shape="ellipse" xfact=1 yfact=1 bool_vertex_attr=1 +Vertex 1: id="3" name="3" x=0.3688 y=0.7792 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 2: id="4" name="4" x=0.9583 y=0.8563 shape="ellipse" xfact=1 yfact=0 bool_vertex_attr=0 +Vertex 3: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Vertex 4: id="" name="" x=NaN y=NaN shape="" xfact=NaN yfact=NaN bool_vertex_attr=0 +Edge 0 (1-2): weight=1 hook2=0 edgewidth=2 color="OliveGreen" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 label="Straight arc" labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 1 (0-1): weight=1 hook2=0 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 label="Oval arc" labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" hook1=0 bool_edge_attr=0 +Edge 2 (1-1): weight=-1 hook2=12 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 label="Circular loop" labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" hook1=6 bool_edge_attr=0 + +After deleting some attributes: +bool_graph_attr=1 +Vertex 0: id="1" name="1" y=0.0896 bool_vertex_attr=1 +Vertex 1: id="3" name="3" y=0.7792 bool_vertex_attr=0 +Vertex 2: id="4" name="4" y=0.8563 bool_vertex_attr=0 +Vertex 3: id="" name="" y=NaN bool_vertex_attr=0 +Vertex 4: id="" name="" y=NaN bool_vertex_attr=0 +Edge 0 (1-2): weight=1 edgewidth=2 color="OliveGreen" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" bool_edge_attr=0 +Edge 1 (0-1): weight=1 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" bool_edge_attr=0 +Edge 2 (1-1): weight=-1 edgewidth=1 color="Red" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" bool_edge_attr=0 + +After setting vertex and edge attributes: +bool_graph_attr=1 +Vertex 0: id="foo" name="1" y=-1 bool_vertex_attr=1 type=1 +Vertex 1: id="bar" name="3" y=2.1 bool_vertex_attr=0 type=0 +Vertex 2: id="4" name="4" y=0.8563 bool_vertex_attr=0 type=0 +Vertex 3: id="" name="" y=NaN bool_vertex_attr=0 type=0 +Vertex 4: id="" name="" y=NaN bool_vertex_attr=0 type=0 +Edge 0 (1-2): weight=-100.1 edgewidth=2 color="Blue" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" bool_edge_attr=0 type=1 +Edge 1 (0-1): weight=1 edgewidth=5 color="Brown" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" bool_edge_attr=0 type=0 +Edge 2 (1-1): weight=100 edgewidth=1 color="RED" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" bool_edge_attr=0 type=0 + +After setting vertex and edge attributes by vector: +bool_graph_attr=1 +Vertex 0: id="0" name="1" y=1.23 bool_vertex_attr=1 type=0 foobar=0 foo="0" +Vertex 1: id="1" name="3" y=1.23 bool_vertex_attr=0 type=1 foobar=1 foo="1" +Vertex 2: id="2" name="4" y=1.23 bool_vertex_attr=0 type=0 foobar=2 foo="2" +Vertex 3: id="3" name="" y=1.23 bool_vertex_attr=0 type=1 foobar=3 foo="3" +Vertex 4: id="4" name="" y=1.23 bool_vertex_attr=0 type=0 foobar=4 foo="4" +Edge 0 (1-2): weight=12.3 edgewidth=2 color="0" arrowsize=NaN angle1=NaN velocity1=NaN angle2=NaN velocity2=NaN arrowpos=25 labelcolor="PineGreen" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" bool_edge_attr=0 type=0 foobar=0 foo="0" +Edge 1 (0-1): weight=12.3 edgewidth=5 color="1" arrowsize=NaN angle1=NaN velocity1=-1 angle2=NaN velocity2=-20 arrowpos=25 labelcolor="Black" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=NaN labelangle2=NaN linepattern="Dashed" bool_edge_attr=0 type=1 foobar=1 foo="1" +Edge 2 (1-1): weight=12.3 edgewidth=1 color="2" arrowsize=NaN angle1=NaN velocity1=-2 angle2=NaN velocity2=-15 arrowpos=0.5 labelcolor="OrangeRed" fontsize=NaN labelangle=NaN labelpos=NaN labeldegree=180 labelangle2=270 linepattern="" bool_edge_attr=0 type=0 foobar=2 foo="2" + +After deleting all attributes: +Vertex 0: +Vertex 1: +Vertex 2: +Vertex 3: +Vertex 4: +Edge 0 (1-2): +Edge 1 (0-1): +Edge 2 (1-1): + +Setting attributes on vertex 0, permuting with 2 +Permuting to different graph: +Vertex 0: foo=NaN bar=0 baz="" +Vertex 1: foo=NaN bar=0 baz="" +Vertex 2: foo=5 bar=1 baz="foobar" +Edge 0 (2-1): +Edge 1 (1-0): + +Permuting to same graph: +Vertex 0: foo=NaN bar=0 baz="" +Vertex 1: foo=NaN bar=0 baz="" +Vertex 2: foo=5 bar=1 baz="foobar" +Edge 0 (0-1): +Edge 1 (1-2): + diff --git a/tests/unit/centralization.c b/tests/unit/centralization.c new file mode 100644 index 0000000..ec84a93 --- /dev/null +++ b/tests/unit/centralization.c @@ -0,0 +1,74 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_real_t cent; + igraph_arpack_options_t arpack_options; + + igraph_star(&g, 10, IGRAPH_STAR_IN, /*center=*/ 0); + igraph_centralization_degree(&g, + /*res=*/ NULL, + /*mode=*/ IGRAPH_IN, IGRAPH_NO_LOOPS, + ¢, /*theoretical_max=*/ NULL, + /*normalized=*/ true); + printf("in-star, degree: %g\n", cent); + + igraph_centralization_betweenness(&g, /*res=*/ NULL, + IGRAPH_UNDIRECTED, ¢, + /*theoretical_max=*/ NULL, + /*normalized=*/ true); + printf("in-star, betweenness: %g\n", cent); + igraph_destroy(&g); + + + igraph_star(&g, 10, IGRAPH_STAR_OUT, /*center=*/ 0); + igraph_centralization_degree(&g, /*res=*/ NULL, + /*mode=*/ IGRAPH_OUT, IGRAPH_NO_LOOPS, + ¢, /*theoretical_max=*/ NULL, + /*normalized=*/ true); + printf("out-star, degree: %g\n", cent); + + igraph_centralization_betweenness(&g, /*res=*/ NULL, + IGRAPH_UNDIRECTED, ¢, + /*theoretical_max=*/ NULL, + /*normalized=*/ true); + printf("out-star, betweenness: %g\n", cent); + igraph_destroy(&g); + + igraph_small(&g, /*n=*/ 10, /*directed=*/ 0, 0, 1, -1); + igraph_arpack_options_init(&arpack_options); + igraph_centralization_eigenvector_centrality( + &g, + /*vector=*/ NULL, + /*value=*/ NULL, + IGRAPH_DIRECTED, + /*scale=*/ false, + &arpack_options, ¢, + /*theoretical_max=*/ NULL, + /*normalized=*/ true); + + printf("dyad, eigenvector centrality, not scaled: %g\n", cent); + + igraph_destroy(&g); + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/centralization.out b/tests/unit/centralization.out new file mode 100644 index 0000000..5579acd --- /dev/null +++ b/tests/unit/centralization.out @@ -0,0 +1,5 @@ +in-star, degree: 1 +in-star, betweenness: 1 +out-star, degree: 1 +out-star, betweenness: 1 +dyad, eigenvector centrality, not scaled: 1 diff --git a/tests/unit/cmp_epsilon.c b/tests/unit/cmp_epsilon.c new file mode 100644 index 0000000..d194dcc --- /dev/null +++ b/tests/unit/cmp_epsilon.c @@ -0,0 +1,77 @@ +/* + IGraph library. + Copyright (C) 2023 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 +#include + +#include "test_utilities.h" + +int main(void) { + /* Test "normal" cases */ + IGRAPH_ASSERT(igraph_cmp_epsilon(1, 2, 0.25) < 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(1, 2, 0.5) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(2, 1, 0.25) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(2, 1, 0.5) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(10, 11, 0.25) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(10, 11, 0.5) == 0); + + /* Test infinities. Infinities are not equal to finite numbers with any + * tolerance */ + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, 2, 0.5) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, 2, 20) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, 2, 200) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, 2, 0.5) < 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, 2, 20) < 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, 2, 200) < 0); + + /* Infinities may be equal, though */ + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, IGRAPH_INFINITY, 0.5) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, IGRAPH_INFINITY, 0.1) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, IGRAPH_INFINITY, 1e-7) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, -IGRAPH_INFINITY, 0.5) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, -IGRAPH_INFINITY, 0.1) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, -IGRAPH_INFINITY, 1e-7) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, IGRAPH_INFINITY, 0.5) < 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, IGRAPH_INFINITY, 0.1) < 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, IGRAPH_INFINITY, 1e-7) < 0); + + /* NaNs are not equal to anything, not even each other. Sign of the result + * is not guaranteed */ + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, IGRAPH_NAN, 0.1) != 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, IGRAPH_NAN, 0.1) != 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(0, IGRAPH_NAN, 1e-7) != 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_NAN, 0, 1e-7) != 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_NAN, IGRAPH_NAN, 1e-7) != 0); + + /* Comparison with zero tolerance should be a "strict" comparison */ + IGRAPH_ASSERT(igraph_cmp_epsilon(1, 2, 0) < 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(2, 1, 0) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(2, 2, 0) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(0, DBL_MIN, 0) < 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(DBL_MIN, 0, 0) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, 0, 0) < 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(-IGRAPH_INFINITY, -IGRAPH_INFINITY, 0) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, 0, 0) > 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_INFINITY, IGRAPH_INFINITY, 0) == 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_NAN, 0, 0) != 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_NAN, IGRAPH_NAN, 0) != 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_NAN, IGRAPH_INFINITY, 0) != 0); + IGRAPH_ASSERT(igraph_cmp_epsilon(IGRAPH_NAN, -IGRAPH_INFINITY, 0) != 0); + + return 0; +} diff --git a/tests/unit/community_indexing.c b/tests/unit/community_indexing.c new file mode 100644 index 0000000..c02f62d --- /dev/null +++ b/tests/unit/community_indexing.c @@ -0,0 +1,88 @@ +/* + 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, see . +*/ + +#include + +#include "test_utilities.h" + +/* This test verifies that the membership vectors returns by community detection + * functions are properly indexed, i.e. there are no negative indices and there + * are no empty communities. */ + +void check(const igraph_vector_int_t *m) { + igraph_vector_int_t m2; + igraph_vector_int_init_copy(&m2, m); + igraph_reindex_membership(&m2, NULL, NULL); + IGRAPH_ASSERT(igraph_vector_int_min(m) == 0); + IGRAPH_ASSERT(igraph_vector_int_max(m) == igraph_vector_int_max(&m2)); + igraph_vector_int_destroy(&m2); +} + +int main(void) { + igraph_t graph; + igraph_vector_int_t membership; + igraph_error_handler_t *handler; + igraph_error_t ret; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_int_init(&membership, 0); + + igraph_grg_game(&graph, 100, 0.2, false, NULL, NULL); + + igraph_community_fastgreedy(&graph, NULL, NULL, NULL, &membership); + check(&membership); + + igraph_community_label_propagation(&graph, &membership, IGRAPH_ALL, NULL, NULL, NULL); + check(&membership); + + igraph_community_walktrap(&graph, NULL, 4, NULL, NULL, &membership); + check(&membership); + + igraph_community_edge_betweenness(&graph, NULL, NULL, NULL, NULL, NULL, &membership, IGRAPH_UNDIRECTED, NULL); + check(&membership); + + igraph_community_leading_eigenvector(&graph, NULL, NULL, &membership, igraph_vcount(&graph), NULL, NULL, false, NULL, NULL, NULL, NULL, NULL); + check(&membership); + + igraph_community_leiden(&graph, NULL, NULL, 1, 0.01, 1, false, &membership, NULL, NULL); + check(&membership); + + igraph_community_multilevel(&graph, NULL, 1, &membership, NULL, NULL); + check(&membership); + + igraph_destroy(&graph); + + igraph_grg_game(&graph, 20, 0.5, false, NULL, NULL); + + handler = igraph_set_error_handler(&igraph_error_handler_ignore); + ret = igraph_community_optimal_modularity(&graph, NULL, &membership, NULL); + igraph_set_error_handler(handler); + if (ret != IGRAPH_UNIMPLEMENTED) { /* Test only when GLPK is available */ + IGRAPH_ASSERT(ret == IGRAPH_SUCCESS); + check(&membership); + } + + igraph_destroy(&graph); + + igraph_vector_int_destroy(&membership); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/community_label_propagation.c b/tests/unit/community_label_propagation.c new file mode 100644 index 0000000..138fcb2 --- /dev/null +++ b/tests/unit/community_label_propagation.c @@ -0,0 +1,116 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t membership; + igraph_vector_t weights; + igraph_vector_int_t initial; + igraph_vector_bool_t fixed; + igraph_integer_t i, j; + + /* Simple triangle graph, the output should be always one community */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0, 1, 0, 2, 1, 2, -1); + igraph_vector_int_init(&membership, 0); + + for (j = 0; j < 100; j++) { + /* label propagation is a stochastic method */ + igraph_rng_seed(igraph_rng_default(), j); + + igraph_community_label_propagation(&g, &membership, IGRAPH_ALL, 0, 0, 0); + + for (i = 0; i < 3; i++) + IGRAPH_ASSERT(VECTOR(membership)[i] == VECTOR(membership)[0]); + } + + igraph_destroy(&g); + + /* label propagation is a stochastic method */ + igraph_rng_seed(igraph_rng_default(), 765); + + /* Zachary Karate club -- this is just a quick smoke test */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + + igraph_community_label_propagation(&g, &membership, IGRAPH_ALL, 0, 0, 0); + + igraph_destroy(&g); + + /* Simple star graph to test weights */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, + 2, 3, 2, 4, 3, 4, 3, 5, 4, 5, -1); + igraph_vector_init_int_end(&weights, -1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1); + igraph_vector_int_init_int_end(&initial, -1, 0, 0, 1, 1, 1, 1, -1); + igraph_vector_bool_init(&fixed, 6); + VECTOR(fixed)[3] = 1; + VECTOR(fixed)[4] = 1; + VECTOR(fixed)[5] = 1; + igraph_community_label_propagation(&g, &membership, IGRAPH_ALL, &weights, + &initial, &fixed); + for (i = 0; i < igraph_vcount(&g); i++) + IGRAPH_ASSERT(VECTOR(membership)[i] == (i < 2 ? 0 : 1)); + + igraph_community_label_propagation(&g, &membership, IGRAPH_ALL, 0, + &initial, &fixed); + for (i = 0; i < igraph_vcount(&g); i++) + IGRAPH_ASSERT(VECTOR(membership)[i] == 0); + + /* Check whether it works with no fixed vertices at all + * while an initial configuration is given -- see bug + * #570902 in Launchpad. This is a simple smoke test only. */ + igraph_community_label_propagation(&g, &membership, IGRAPH_ALL, &weights, + &initial, 0); + + igraph_vector_bool_destroy(&fixed); + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&initial); + igraph_destroy(&g); + + igraph_vector_int_destroy(&membership); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/community_label_propagation2.c b/tests/unit/community_label_propagation2.c new file mode 100644 index 0000000..f4ad178 --- /dev/null +++ b/tests/unit/community_label_propagation2.c @@ -0,0 +1,86 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include + +#include "test_utilities.h" + +/* Test case for bug #1852 */ + +int main(void) { + igraph_t graph; + igraph_vector_int_t membership, initial_labels; + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Undirected graph with unlabelled components */ + + igraph_small(&graph, 4, IGRAPH_UNDIRECTED, + 1,2, -1); + + igraph_vector_int_init(&membership, 0); + igraph_vector_int_init(&initial_labels, igraph_vcount(&graph)); + VECTOR(initial_labels)[0] = 1; + VECTOR(initial_labels)[1] = -1; + VECTOR(initial_labels)[2] = -1; + VECTOR(initial_labels)[3] = -1; + + igraph_community_label_propagation(&graph, &membership, IGRAPH_ALL, NULL, &initial_labels, NULL); + print_vector_int(&membership); + + igraph_destroy(&graph); + + /* Directed graph with unlabelled nodes not reachable from any labelled ones. */ + + igraph_small(&graph, 8, IGRAPH_DIRECTED, + 0, 1, 1, 2, 3, 1, 2, 4, 4, 5, 5, 2, 4, 6, + -1); + + igraph_vector_int_resize(&initial_labels, igraph_vcount(&graph)); + igraph_vector_int_null(&initial_labels); + VECTOR(initial_labels)[0] = -1; + VECTOR(initial_labels)[1] = -1; + VECTOR(initial_labels)[2] = 1; + VECTOR(initial_labels)[3] = -1; + VECTOR(initial_labels)[4] = 2; + VECTOR(initial_labels)[6] = -1; + VECTOR(initial_labels)[7] = -1; + + igraph_community_label_propagation(&graph, &membership, IGRAPH_OUT, NULL, &initial_labels, NULL); + print_vector_int(&membership); + + igraph_destroy(&graph); + + /* None of the nodes are labelled initially */ + + igraph_full(&graph, 5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_int_resize(&initial_labels, igraph_vcount(&graph)); + igraph_vector_int_fill(&initial_labels, -1); + + igraph_community_label_propagation(&graph, &membership, IGRAPH_OUT, NULL, &initial_labels, NULL); + print_vector_int(&membership); + + igraph_destroy(&graph); + + igraph_vector_int_destroy(&initial_labels); + igraph_vector_int_destroy(&membership); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/community_label_propagation2.out b/tests/unit/community_label_propagation2.out new file mode 100644 index 0000000..260d8cb --- /dev/null +++ b/tests/unit/community_label_propagation2.out @@ -0,0 +1,3 @@ +( 0 2 2 1 ) +( 3 2 0 2 0 0 0 1 ) +( 0 0 0 0 0 ) diff --git a/tests/unit/community_label_propagation3.c b/tests/unit/community_label_propagation3.c new file mode 100644 index 0000000..a524718 --- /dev/null +++ b/tests/unit/community_label_propagation3.c @@ -0,0 +1,75 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t membership, initial; + igraph_vector_bool_t fixed; + igraph_integer_t i; + + /* label propagation is a stochastic method */ + igraph_rng_seed(igraph_rng_default(), 765); + + /* Zachary Karate club -- does not matter, we are simply interested in + * whether the system handles it correctly if a fixed node is unlabeled */ + igraph_famous(&g, "zachary"); + + + igraph_vector_int_init(&initial, igraph_vcount(&g)); + igraph_vector_int_fill(&initial, -1); + igraph_vector_bool_init(&fixed, igraph_vcount(&g)); + igraph_vector_bool_fill(&fixed, 0); + VECTOR(fixed)[7] = 1; + VECTOR(fixed)[13] = 1; + + igraph_vector_int_init(&membership, 0); + + igraph_community_label_propagation(&g, &membership, IGRAPH_OUT, 0, &initial, &fixed); + + for (i = 0; i < igraph_vcount(&g); i++) { + /* Check that the "fixed" vector has not been changed */ + if (i == 7 || i == 13) { + IGRAPH_ASSERT(VECTOR(fixed)[i]); + } else { + IGRAPH_ASSERT(!VECTOR(fixed)[i]); + } + + /* Check that no vertex remained unlabeled */ + IGRAPH_ASSERT(VECTOR(membership)[i] >= 0); + } + + igraph_vector_bool_destroy(&fixed); + igraph_vector_int_destroy(&initial); + igraph_vector_int_destroy(&membership); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/community_leiden.c b/tests/unit/community_leiden.c new file mode 100644 index 0000000..d2c4162 --- /dev/null +++ b/tests/unit/community_leiden.c @@ -0,0 +1,200 @@ +/* -*- 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 + +#include "test_utilities.h" + +void run_leiden_CPM(const igraph_t *graph, const igraph_vector_t *edge_weights, const igraph_real_t resolution_parameter) { + + igraph_vector_int_t membership; + igraph_integer_t nb_clusters = igraph_vcount(graph); + igraph_real_t quality; + + /* Initialize with singleton partition. */ + igraph_vector_int_init(&membership, igraph_vcount(graph)); + + igraph_community_leiden(graph, edge_weights, NULL, resolution_parameter, 0.01, 0, 1, &membership, &nb_clusters, &quality); + + printf("Leiden found %" IGRAPH_PRId " clusters using CPM (resolution parameter=%.2f), quality is %.4f.\n", nb_clusters, resolution_parameter, quality); + + printf("Membership: "); + igraph_vector_int_print(&membership); + printf("\n"); + + igraph_vector_int_destroy(&membership); +} + +void run_leiden_modularity(igraph_t *graph, igraph_vector_t *edge_weights) { + + igraph_vector_int_t membership; + igraph_vector_t strength; + igraph_integer_t nb_clusters = igraph_vcount(graph); + igraph_real_t quality; + igraph_real_t m; + + igraph_vector_init(&strength, igraph_vcount(graph)); + igraph_strength(graph, &strength, igraph_vss_all(), IGRAPH_ALL, 1, edge_weights); + m = edge_weights ? igraph_vector_sum(edge_weights) : igraph_ecount(graph); + + /* Initialize with singleton partition. */ + igraph_vector_int_init(&membership, igraph_vcount(graph)); + + igraph_community_leiden(graph, edge_weights, &strength, 1.0 / (2 * m), 0.01, 0, 1, &membership, &nb_clusters, &quality); + + if (isnan(quality)) { + printf("Leiden found %" IGRAPH_PRId " clusters using modularity, quality is nan.\n", nb_clusters); + } else { + printf("Leiden found %" IGRAPH_PRId " clusters using modularity, quality is %.4f.\n", nb_clusters, quality); + } + + printf("Membership: "); + igraph_vector_int_print(&membership); + printf("\n"); + + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&strength); +} + +int main(void) { + igraph_t graph; + igraph_vector_t weights; + + igraph_vector_init(&weights, 0); + + /* Set default seed to get reproducible results */ + igraph_rng_seed(igraph_rng_default(), 0); + + /* Simple unweighted graph */ + igraph_small(&graph, 10, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 5, 6, 5, 7, 5, 8, 5, 9, 6, 7, 6, 8, 6, 9, 7, 8, 7, 9, 8, 9, + 0, 5, -1); + run_leiden_modularity(&graph, NULL); + + /* Same simple graph, with uniform edge weights */ + igraph_vector_resize(&weights, igraph_ecount(&graph)); + igraph_vector_fill(&weights, 2); + run_leiden_modularity(&graph, &weights); + igraph_destroy(&graph); + + /* Simple nonuniform weighted graph, with and without weights */ + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, -1); + igraph_vector_resize(&weights, 8); + igraph_vector_fill(&weights, 1); + VECTOR(weights)[0] = 10; + VECTOR(weights)[1] = 10; + run_leiden_modularity(&graph, NULL); + run_leiden_modularity(&graph, &weights); + igraph_destroy(&graph); + + /* Zachary Karate club */ + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + run_leiden_modularity(&graph, NULL); + run_leiden_CPM(&graph, NULL, 0.06); + igraph_destroy(&graph); + + /* Simple disconnected graph with isolates */ + igraph_small(&graph, 9, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, + 4, 5, 4, 6, 4, 7, 5, 6, 5, 7, 6, 7, + -1); + run_leiden_modularity(&graph, NULL); + igraph_destroy(&graph); + + /* Disjoint union of two rings */ + igraph_small(&graph, 20, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 0, 9, + 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 10, 19, -1); + run_leiden_modularity(&graph, NULL); + run_leiden_CPM(&graph, NULL, 0.05); + igraph_destroy(&graph); + + /* Completely empty graph */ + igraph_small(&graph, 10, IGRAPH_UNDIRECTED, -1); + run_leiden_modularity(&graph, NULL); + igraph_destroy(&graph); + + + /* Set default seed to get reproducible results */ + igraph_rng_seed(igraph_rng_default(), 0); + + /* Ring graph without loop edges */ + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,3, 3,4, 4,5, 5,0, -1); + run_leiden_CPM(&graph, NULL, 0.4); + igraph_destroy(&graph); + + /* Set default seed to get reproducible results */ + igraph_rng_seed(igraph_rng_default(), 0); + + /* Ring graph with loop edges */ + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,3, 3,4, 4,5, 5,0, + 0,0, 1,1, 2,2, 3,3, 4,4, 5,5, + -1); + run_leiden_CPM(&graph, NULL, 0.4); + igraph_destroy(&graph); + + /* Regression test -- graph with two vertices and two edges */ + igraph_small(&graph, 2, IGRAPH_UNDIRECTED, 0, 0, 1, 1, -1); + run_leiden_modularity(&graph, NULL); + igraph_destroy(&graph); + + /* The next two tests need an empty weight vector. */ + igraph_vector_clear(&weights); + + /* Null graph */ + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + run_leiden_modularity(&graph, &weights); + igraph_destroy(&graph); + + /* Edgeless graph */ + igraph_empty(&graph, 5, IGRAPH_UNDIRECTED); + run_leiden_modularity(&graph, &weights); + igraph_destroy(&graph); + + igraph_vector_destroy(&weights); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/community_leiden.out b/tests/unit/community_leiden.out new file mode 100644 index 0000000..f1ed73a --- /dev/null +++ b/tests/unit/community_leiden.out @@ -0,0 +1,45 @@ +Leiden found 2 clusters using modularity, quality is 0.4524. +Membership: 0 0 0 0 0 1 1 1 1 1 + +Leiden found 2 clusters using modularity, quality is 0.4524. +Membership: 0 0 0 0 0 1 1 1 1 1 + +Leiden found 2 clusters using modularity, quality is 0.1797. +Membership: 0 0 1 1 1 1 + +Leiden found 2 clusters using modularity, quality is 0.1709. +Membership: 0 0 0 1 1 1 + +Leiden found 4 clusters using modularity, quality is 0.4198. +Membership: 0 0 0 0 1 1 1 0 2 2 1 0 0 0 2 2 1 0 2 0 2 0 2 3 3 3 2 3 3 2 2 3 2 2 + +Leiden found 2 clusters using CPM (resolution parameter=0.06), quality is 0.6495. +Membership: 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 + +Leiden found 3 clusters using modularity, quality is 0.5000. +Membership: 0 0 0 0 1 1 1 1 2 + +Leiden found 4 clusters using modularity, quality is 0.5450. +Membership: 0 0 1 1 1 1 1 1 0 0 2 2 2 2 3 3 3 3 3 2 + +Leiden found 2 clusters using CPM (resolution parameter=0.05), quality is 0.7500. +Membership: 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 + +Leiden found 10 clusters using modularity, quality is nan. +Membership: 0 1 2 3 4 5 6 7 8 9 + +Leiden found 2 clusters using CPM (resolution parameter=0.40), quality is 0.0667. +Membership: 0 0 1 1 1 0 + +Leiden found 2 clusters using CPM (resolution parameter=0.40), quality is 0.5333. +Membership: 0 0 1 1 1 0 + +Leiden found 2 clusters using modularity, quality is 0.5000. +Membership: 0 1 + +Leiden found 0 clusters using modularity, quality is nan. +Membership: + +Leiden found 5 clusters using modularity, quality is nan. +Membership: 0 1 2 3 4 + diff --git a/tests/unit/community_walktrap.c b/tests/unit/community_walktrap.c new file mode 100644 index 0000000..0977b06 --- /dev/null +++ b/tests/unit/community_walktrap.c @@ -0,0 +1,204 @@ +/* -*- 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 + +#include + +#include "test_utilities.h" + +/* Replace modularity values which are very close to zero + * by exact zeros, so that we can have consistent test outputs + * across platforms. */ +void fixup_modularity(igraph_vector_t *modularity) { + igraph_integer_t i; + igraph_integer_t len = igraph_vector_size(modularity); + + for (i=0; i < len; ++i) { + if (fabs(VECTOR(*modularity)[i]) < 1e-15) { + VECTOR(*modularity)[i] = 0.0; + } + } +} + +int main(void) { + igraph_t graph; + + igraph_matrix_int_t merges; + igraph_vector_t modularity; + igraph_vector_int_t membership; + igraph_vector_t weights; + + /* Set default seed to get reproducible results */ + igraph_rng_seed(igraph_rng_default(), 42); + + printf("Basic test\n\n"); + + /* Simple unweighted graph */ + igraph_small(&graph, 3, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, -1); + + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_init(&modularity, 0); + igraph_vector_int_init(&membership, 0); + + igraph_community_walktrap(&graph, NULL, 4, &merges, &modularity, &membership); + printf("Merges:\n"); + print_matrix_int(&merges); + + printf("Modularity: "); + fixup_modularity(&modularity); + print_vector(&modularity); + + printf("Membership: "); + print_vector_int(&membership); + + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&modularity); + igraph_matrix_int_destroy(&merges); + + /* Test the case when modularity=NULL and membership=NULL as this caused a crash in + * the R interface, see https://github.com/igraph/rigraph/issues/289 */ + + printf("\nWithout modularity and membership calculation\n\n"); + + igraph_matrix_int_init(&merges, 0, 0); + + igraph_community_walktrap(&graph, NULL, 4, &merges, NULL, NULL); + printf("Merges:\n"); + print_matrix_int(&merges); + igraph_matrix_int_destroy(&merges); + + /* Test the case when merges=NULL but modularity is requested, + * as the result was previously invalid due to not incrementing + * the "merge index" during computation. */ + + printf("\nWithout merges matrix calculation\n\n"); + + igraph_vector_init(&modularity, 0); + igraph_community_walktrap(&graph, NULL, 4, NULL, &modularity, NULL); + printf("Modularity:\n"); + fixup_modularity(&modularity); + print_vector(&modularity); + igraph_vector_destroy(&modularity); + + igraph_destroy(&graph); + + /* Test for bug https://github.com/igraph/igraph/issues/2042 */ + + printf("\nBug 2042\n\n"); + + igraph_small(&graph, 3, IGRAPH_UNDIRECTED, + 0,1, -1); + + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_init(&modularity, 0); + igraph_vector_int_init(&membership, 0); + igraph_vector_init_real(&weights, 1, 0.2); + + igraph_community_walktrap(&graph, &weights, 4, &merges, &modularity, &membership); + printf("Merges:\n"); + print_matrix_int(&merges); + + printf("Modularity: "); + fixup_modularity(&modularity); + print_vector(&modularity); + + printf("Membership: "); + print_vector_int(&membership); + + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&modularity); + igraph_matrix_int_destroy(&merges); + + igraph_destroy(&graph); + + printf("\nSmall weighted graph\n\n"); + + igraph_ring(&graph, 6, IGRAPH_UNDIRECTED, 0, 1); + + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_init(&modularity, 0); + igraph_vector_int_init(&membership, 0); + igraph_vector_init_real(&weights, 6, + 1.0, 0.5, 0.25, 0.75, 1.25, 1.5); + + igraph_community_walktrap(&graph, &weights, 4, &merges, &modularity, &membership); + printf("Merges:\n"); + print_matrix_int(&merges); + + printf("Modularity: "); + fixup_modularity(&modularity); + print_vector(&modularity); + + printf("Membership: "); + print_vector_int(&membership); + + /* Negative weights are not allowed */ + VECTOR(weights)[0] = -0.1; + CHECK_ERROR(igraph_community_walktrap(&graph, &weights, 4, &merges, &modularity, &membership), IGRAPH_EINVAL); + + /* Invalid weight vector size */ + VECTOR(weights)[0] = 0.1; + igraph_vector_pop_back(&weights); + CHECK_ERROR(igraph_community_walktrap(&graph, &weights, 4, &merges, &modularity, &membership), IGRAPH_EINVAL); + + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&modularity); + igraph_matrix_int_destroy(&merges); + + igraph_destroy(&graph); + + printf("\nIsolated vertices\n\n"); + + igraph_empty(&graph, 5, IGRAPH_UNDIRECTED); + + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_init(&modularity, 0); + igraph_vector_int_init(&membership, 0); + + igraph_community_walktrap(&graph, NULL, 4, &merges, &modularity, &membership); + printf("Merges:\n"); + print_matrix_int(&merges); + + printf("Modularity: "); + fixup_modularity(&modularity); + print_vector(&modularity); + + printf("Membership: "); + print_vector_int(&membership); + + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&modularity); + igraph_matrix_int_destroy(&merges); + + igraph_destroy(&graph); + + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/community_walktrap.out b/tests/unit/community_walktrap.out new file mode 100644 index 0000000..8a7d283 --- /dev/null +++ b/tests/unit/community_walktrap.out @@ -0,0 +1,43 @@ +Basic test + +Merges: +[ 1 2 + 0 3 ] +Modularity: ( -0.333333 -0.222222 0 ) +Membership: ( 0 0 0 ) + +Without modularity and membership calculation + +Merges: +[ 1 2 + 0 3 ] + +Without merges matrix calculation + +Modularity: +( -0.333333 -0.222222 0 ) + +Bug 2042 + +Merges: +[ 0 1 ] +Modularity: ( -0.5 0 ) +Membership: ( 0 0 1 ) + +Small weighted graph + +Merges: +[ 3 4 + 1 2 + 0 5 + 7 8 + 6 9 ] +Modularity: ( -0.196145 -0.0895692 -0.0147392 0.146259 0.122449 0 ) +Membership: ( 0 1 1 2 2 0 ) + +Isolated vertices + +Merges: +[ 0-by-2 ] +Modularity: ( NaN ) +Membership: ( 0 1 2 3 4 ) diff --git a/tests/unit/components.c b/tests/unit/components.c new file mode 100644 index 0000000..da7af6e --- /dev/null +++ b/tests/unit/components.c @@ -0,0 +1,97 @@ +/* + IGraph library. + Copyright (C) 2023 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 + +#include "test_utilities.h" + +void compute_and_print(const igraph_t *g, igraph_connectedness_t mode) { + igraph_vector_int_t membership, csize; + igraph_integer_t no; + + igraph_vector_int_init(&membership, 1); + igraph_vector_int_init(&csize, 1); + + igraph_connected_components(g, &membership, &csize, &no, mode); + + /* Canonicalize membership representation */ + igraph_reindex_membership(&membership, NULL, NULL); + + printf("Mode: %s\n", mode == IGRAPH_STRONG ? "strong" : "weak"); + print_vector_int(&membership); + print_vector_int(&csize); + printf("No. of components: %" IGRAPH_PRId "\n", no); + + igraph_vector_int_destroy(&csize); + igraph_vector_int_destroy(&membership); +} + +int main(void) { + igraph_t g; + + /* Component calculations are run twice to exercise the cache */ + + printf("\nNull graph (not connected)\n"); + igraph_empty(&g, 0, IGRAPH_DIRECTED); + compute_and_print(&g, IGRAPH_STRONG); + compute_and_print(&g, IGRAPH_STRONG); + igraph_invalidate_cache(&g); + compute_and_print(&g, IGRAPH_WEAK); + compute_and_print(&g, IGRAPH_WEAK); + igraph_destroy(&g); + + printf("\nSingleton graph (connected)\n"); + igraph_empty(&g, 1, IGRAPH_DIRECTED); + compute_and_print(&g, IGRAPH_STRONG); + compute_and_print(&g, IGRAPH_STRONG); + igraph_invalidate_cache(&g); + compute_and_print(&g, IGRAPH_WEAK); + compute_and_print(&g, IGRAPH_WEAK); + igraph_destroy(&g); + + printf("\nKautz graph (connected)\n"); + igraph_kautz(&g, 2, 2); + compute_and_print(&g, IGRAPH_STRONG); + compute_and_print(&g, IGRAPH_STRONG); + igraph_invalidate_cache(&g); + compute_and_print(&g, IGRAPH_WEAK); + compute_and_print(&g, IGRAPH_WEAK); + igraph_destroy(&g); + + printf("\nDirected 2-path\n"); + igraph_small(&g, 2, IGRAPH_DIRECTED, 0,1, -1); + compute_and_print(&g, IGRAPH_STRONG); + compute_and_print(&g, IGRAPH_STRONG); + igraph_invalidate_cache(&g); + compute_and_print(&g, IGRAPH_WEAK); + compute_and_print(&g, IGRAPH_WEAK); + igraph_destroy(&g); + + printf("\nTwo disjoint 3-cycles\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, 3,4, 4,5, 5,3, -1); + compute_and_print(&g, IGRAPH_STRONG); + compute_and_print(&g, IGRAPH_STRONG); + igraph_invalidate_cache(&g); + compute_and_print(&g, IGRAPH_WEAK); + compute_and_print(&g, IGRAPH_WEAK); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/components.out b/tests/unit/components.out new file mode 100644 index 0000000..7f8a9d7 --- /dev/null +++ b/tests/unit/components.out @@ -0,0 +1,90 @@ + +Null graph (not connected) +Mode: strong +( ) +( ) +No. of components: 0 +Mode: strong +( ) +( ) +No. of components: 0 +Mode: weak +( ) +( ) +No. of components: 0 +Mode: weak +( ) +( ) +No. of components: 0 + +Singleton graph (connected) +Mode: strong +( 0 ) +( 1 ) +No. of components: 1 +Mode: strong +( 0 ) +( 1 ) +No. of components: 1 +Mode: weak +( 0 ) +( 1 ) +No. of components: 1 +Mode: weak +( 0 ) +( 1 ) +No. of components: 1 + +Kautz graph (connected) +Mode: strong +( 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 12 ) +No. of components: 1 +Mode: strong +( 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 12 ) +No. of components: 1 +Mode: weak +( 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 12 ) +No. of components: 1 +Mode: weak +( 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 12 ) +No. of components: 1 + +Directed 2-path +Mode: strong +( 0 1 ) +( 1 1 ) +No. of components: 2 +Mode: strong +( 0 1 ) +( 1 1 ) +No. of components: 2 +Mode: weak +( 0 0 ) +( 2 ) +No. of components: 1 +Mode: weak +( 0 0 ) +( 2 ) +No. of components: 1 + +Two disjoint 3-cycles +Mode: strong +( 0 0 0 1 1 1 ) +( 3 3 ) +No. of components: 2 +Mode: strong +( 0 0 0 1 1 1 ) +( 3 3 ) +No. of components: 2 +Mode: weak +( 0 0 0 1 1 1 ) +( 3 3 ) +No. of components: 2 +Mode: weak +( 0 0 0 1 1 1 ) +( 3 3 ) +No. of components: 2 diff --git a/tests/unit/constructor-failure.c b/tests/unit/constructor-failure.c new file mode 100644 index 0000000..1ada038 --- /dev/null +++ b/tests/unit/constructor-failure.c @@ -0,0 +1,210 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +/* This test attempts to verify that when adding vertices or edges to a graph fails, + * the graph, as well as its attribute table, are left in a consistent state. + * This test operates with the C attribute handler. Since this attribute table is + * not directly accessible, only limited checks are done to verify its consistency. + * + * We use the printignore() error handler insted of ignore() as this eases debugging. + * Thus, this test has not corresponding expected output file. All checks are done + * directly in the code. + */ + +/* Check that the graph stays in a consistent state upon failure. + * This macro does not destroy the graph. */ +#define CHECK1(funcall) \ + do { \ + igraph_integer_t vcount = igraph_vcount(&graph); \ + igraph_integer_t ecount = igraph_ecount(&graph); \ + igraph_error_handler_t *handler; \ + handler = igraph_set_error_handler(igraph_error_handler_printignore); \ + IGRAPH_ASSERT(funcall != IGRAPH_SUCCESS); \ + igraph_set_error_handler(handler); \ + verify_graph(&graph, vcount, ecount); \ + } while (0) + +/* Check that there is no invalid memory access if the graph is on the finally + * stack when failure occurs. This macro unrolls the finally stack and destroys + * the graph. */ +#define CHECK2(funcall) \ + do { \ + igraph_error_handler_t *handler; \ + IGRAPH_FINALLY(igraph_destroy, &graph); \ + handler = igraph_set_error_handler(igraph_error_handler_printignore); \ + IGRAPH_ASSERT(funcall != IGRAPH_SUCCESS); \ + igraph_set_error_handler(handler); \ + } while (0) + +/* Do both checks, destroying the graph and resetting the finally stack in the process. */ +#define CHECK_DESTROY(funcall) \ + do { CHECK1(funcall); CHECK2(funcall); } while (0) + +void verify_graph(const igraph_t *graph, igraph_integer_t vcount, igraph_integer_t ecount) { + IGRAPH_ASSERT(igraph_vector_int_size(&graph->from) == ecount); + IGRAPH_ASSERT(igraph_vector_int_size(&graph->to) == ecount); + IGRAPH_ASSERT(igraph_vector_int_size(&graph->oi) == ecount); + IGRAPH_ASSERT(igraph_vector_int_size(&graph->ii) == ecount); + IGRAPH_ASSERT(igraph_vector_int_size(&graph->os) == vcount + 1); + IGRAPH_ASSERT(igraph_vector_int_size(&graph->is) == vcount + 1); +} + +int main(void) { + igraph_t graph; + + /* Enable attribute handler */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* Helper vectors */ + + igraph_vector_t nvalues; + igraph_vector_init(&nvalues, 0); + + igraph_strvector_t svalues; + igraph_strvector_init(&svalues, 0); + + igraph_vector_bool_t bvalues; + igraph_vector_bool_init(&bvalues, 0); + + const igraph_vector_int_t edges; /* 2 edges */ + igraph_vector_int_init_int((igraph_vector_int_t *) &edges, 4, 0,1, 1,2); + + /* attr1, numeric, 3 values */ + + igraph_vector_ptr_t attr1; + igraph_vector_ptr_init(&attr1, 1); + + igraph_attribute_record_t a1; + a1.name = "foo"; + a1.type = IGRAPH_ATTRIBUTE_NUMERIC; + + igraph_vector_t vec_a1; + igraph_vector_init_int(&vec_a1, 3, 1, 2, 3); + a1.value = &vec_a1; + + igraph_vs_t vs1; + igraph_vs_vector_small(&vs1, 0, 1, 2, -1); + + VECTOR(attr1)[0] = &a1; + + /* attr2, string, 2 values */ + + igraph_vector_ptr_t attr2; + igraph_vector_ptr_init(&attr2, 1); + + igraph_attribute_record_t a2; + a2.name = "bar"; + a2.type = IGRAPH_ATTRIBUTE_STRING; + + igraph_strvector_t vec_a2; + igraph_strvector_init(&vec_a2, 2); + igraph_strvector_set(&vec_a2, 0, "one"); + igraph_strvector_set(&vec_a2, 1, "two"); + a2.value = &vec_a2; + + VECTOR(attr2)[0] = &a2; + + /* attr3, boolean, 2 values */ + + igraph_vector_ptr_t attr3; + igraph_vector_ptr_init(&attr3, 1); + + igraph_attribute_record_t a3; + a3.name = "baz"; + a3.type = IGRAPH_ATTRIBUTE_BOOLEAN; + + igraph_vector_bool_t vec_a3; + igraph_vector_bool_init_int(&vec_a3, 2, 1, 0); + a3.value = &vec_a3; + + igraph_vs_t vs3; + igraph_vs_vector_small(&vs3, 3, 4, -1); + + VECTOR(attr3)[0] = &a3; + + /* Perform tests */ + + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + CHECK_DESTROY(igraph_add_vertices(&graph, 2, &attr1)); /* wrong number of vertices */ + + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_add_vertices(&graph, 3, &attr1); + CHECK_DESTROY(igraph_add_edges(&graph, &edges, &attr1)); /* wrong number of edges */ + + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_add_vertices(&graph, 3, &attr1); + igraph_add_edges(&graph, &edges, &attr2); + igraph_add_vertices(&graph, 2, &attr3); + + CHECK1(igraph_add_vertices(&graph, 2, &attr1)); /* wrong number of vertices */ + /* Attribute handling still works and graph has correct attributes after failure to add vertices? */ + IGRAPH_ASSERT(igraph_cattribute_VANV(&graph, a1.name, vs1, &nvalues) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vector_all_e(&vec_a1, &nvalues)); + IGRAPH_ASSERT(igraph_cattribute_EASV(&graph, a2.name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &svalues) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_strvector_size(&svalues) == igraph_strvector_size(&vec_a2)); + for (igraph_integer_t i=0; i < igraph_strvector_size(&svalues); ++i) { + IGRAPH_ASSERT(strcmp(igraph_strvector_get(&vec_a2, i), igraph_strvector_get(&svalues, i)) == 0); + } + IGRAPH_ASSERT(igraph_cattribute_VABV(&graph, a3.name, vs3, &bvalues) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vector_bool_all_e(&vec_a3, &bvalues)); + + CHECK1(igraph_add_edges(&graph, &edges, &attr1)); /* wrong number of edges */ + /* Attribute handling still works and graph has correct attributes after failure to add vertices? */ + IGRAPH_ASSERT(igraph_cattribute_VANV(&graph, a1.name, vs1, &nvalues) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vector_all_e(&vec_a1, &nvalues)); + IGRAPH_ASSERT(igraph_cattribute_EASV(&graph, a2.name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &svalues) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_strvector_size(&svalues) == igraph_strvector_size(&vec_a2)); + for (igraph_integer_t i=0; i < igraph_strvector_size(&svalues); ++i) { + IGRAPH_ASSERT(strcmp(igraph_strvector_get(&vec_a2, i), igraph_strvector_get(&svalues, i)) == 0); + } + IGRAPH_ASSERT(igraph_cattribute_VABV(&graph, a3.name, vs3, &bvalues) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vector_bool_all_e(&vec_a3, &bvalues)); + + igraph_destroy(&graph); + + /* Release attr1 */ + + igraph_vector_destroy(&vec_a1); + igraph_vector_ptr_destroy(&attr1); + igraph_vs_destroy(&vs1); + + /* Release attr2 */ + + igraph_strvector_destroy(&vec_a2); + igraph_vector_ptr_destroy(&attr2); + + /* Release attr3 */ + + igraph_vector_bool_destroy(&vec_a3); + igraph_vector_ptr_destroy(&attr3); + igraph_vs_destroy(&vs3); + + /* Release helpers */ + + igraph_vector_int_destroy((igraph_vector_int_t *) &edges); + igraph_strvector_destroy(&svalues); + igraph_vector_destroy(&nvalues); + igraph_vector_bool_destroy(&bvalues); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/coreness.c b/tests/unit/coreness.c new file mode 100644 index 0000000..335768f --- /dev/null +++ b/tests/unit/coreness.c @@ -0,0 +1,232 @@ +/* + 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, see . +*/ + +#include + +#include "test_utilities.h" + +void validate_coreness( + const igraph_t* graph, const igraph_vector_int_t* coreness, + igraph_neimode_t mode +) { + igraph_integer_t i, j, min_coreness, max_coreness; + igraph_integer_t nv = igraph_vcount(graph); + igraph_t subgraph; + igraph_vs_t vs; + igraph_vector_int_t vids, degree; + + IGRAPH_ASSERT(igraph_vector_int_size(coreness) == nv); + if (igraph_vcount(graph) < 1) { + return; + } + + min_coreness = igraph_vector_int_min(coreness); + max_coreness = igraph_vector_int_max(coreness); + + IGRAPH_ASSERT(min_coreness >= 0); + + igraph_vector_int_init(&vids, 0); + igraph_vector_int_init(°ree, 0); + + for (i = max_coreness; i >= 0; i--) { + igraph_vector_int_clear(&vids); + for (j = 0; j < nv; j++) { + if (VECTOR(*coreness)[j] >= i) { + igraph_vector_int_push_back(&vids, j); + } + } + + igraph_vs_vector(&vs, &vids); + igraph_induced_subgraph(graph, &subgraph, vs, IGRAPH_SUBGRAPH_AUTO); + igraph_vs_destroy(&vs); + + igraph_degree(&subgraph, °ree, igraph_vss_all(), mode, IGRAPH_LOOPS_TWICE); + for (j = 0; j < igraph_vcount(&subgraph); j++) { + IGRAPH_ASSERT(VECTOR(degree)[j] >= i); + } + + igraph_destroy(&subgraph); + } + + igraph_vector_int_destroy(°ree); + igraph_vector_int_destroy(&vids); +} + +void test_graph(const igraph_t* graph, igraph_bool_t print) { + igraph_vector_int_t coreness; + + igraph_vector_int_init(&coreness, 0); + + if (igraph_is_directed(graph)) { + igraph_coreness(graph, &coreness, IGRAPH_ALL); + validate_coreness(graph, &coreness, IGRAPH_ALL); + if (print) { + printf("mode = ALL: "); + print_vector_int(&coreness); + } + + igraph_coreness(graph, &coreness, IGRAPH_OUT); + validate_coreness(graph, &coreness, IGRAPH_OUT); + if (print) { + printf("mode = OUT: "); + print_vector_int(&coreness); + } + + igraph_coreness(graph, &coreness, IGRAPH_IN); + validate_coreness(graph, &coreness, IGRAPH_IN); + if (print) { + printf("mode = IN: "); + print_vector_int(&coreness); + } + } else { + igraph_coreness(graph, &coreness, IGRAPH_ALL); + validate_coreness(graph, &coreness, IGRAPH_ALL); + if (print) { + print_vector_int(&coreness); + } + } + + igraph_vector_int_destroy(&coreness); +} + +void add_loop_and_multiple_edges(igraph_t* graph, igraph_real_t loop_prob, igraph_real_t multi_prob) { + igraph_integer_t i, n, from, to; + igraph_vector_int_t extra_edges; + + igraph_vector_int_init(&extra_edges, 0); + + RNG_BEGIN(); + + n = igraph_vcount(graph); + for (i = 0; i < n; i++) { + if (RNG_UNIF01() < loop_prob) { + igraph_vector_int_push_back(&extra_edges, i); + igraph_vector_int_push_back(&extra_edges, i); + } + } + + n = igraph_ecount(graph); + for (i = 0; i < n; i++) { + if (RNG_UNIF01() < multi_prob) { + igraph_edge(graph, i, &from, &to); + igraph_vector_int_push_back(&extra_edges, from); + igraph_vector_int_push_back(&extra_edges, to); + } + } + + RNG_END(); + + igraph_add_edges(graph, &extra_edges, 0); + + igraph_vector_int_destroy(&extra_edges); +} + +void remove_some_edges(igraph_t* graph, igraph_real_t prob) { + igraph_integer_t i, n; + igraph_vector_int_t to_remove; + + igraph_vector_int_init(&to_remove, 0); + + RNG_BEGIN(); + + n = igraph_ecount(graph); + for (i = 0; i < n; i++) { + if (igraph_rng_get_unif01(igraph_rng_default()) < prob) { + igraph_vector_int_push_back(&to_remove, i); + } + } + + RNG_END(); + + igraph_delete_edges(graph, igraph_ess_vector(&to_remove)); + + igraph_vector_int_destroy(&to_remove); +} + +int main(void) { + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 137); + + /* Empty and singleton graph */ + printf("Empty graph.\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + test_graph(&g, /* print = */ 1); + igraph_destroy(&g); + printf("Singleton graph.\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + test_graph(&g, /* print = */ 1); + igraph_destroy(&g); + + /* Simple full graph */ + printf("Full graph.\n"); + igraph_full(&g, 5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + test_graph(&g, /* print = */ 1); + igraph_destroy(&g); + + /* Full graph with loops */ + printf("Full graph with loops.\n"); + igraph_full(&g, 5, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + test_graph(&g, /* print = */ 1); + igraph_destroy(&g); + + /* Full directed graph */ + printf("Full directed graph.\n"); + igraph_full(&g, 5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + test_graph(&g, /* print = */ 1); + igraph_destroy(&g); + + /* Full directed graph */ + printf("Full directed graph with loops.\n"); + igraph_full(&g, 5, IGRAPH_DIRECTED, IGRAPH_LOOPS); + test_graph(&g, /* print = */ 1); + igraph_destroy(&g); + + /* Zachary karate club */ + printf("Zachary karate club.\n"); + igraph_famous(&g, "zachary"); + test_graph(&g, /* print = */ 1); + igraph_destroy(&g); + + /* Zachary karate club, randomly directed edges */ + printf("Zachary karate club, directed edges.\n"); + igraph_famous(&g, "zachary"); + igraph_to_directed(&g, IGRAPH_TO_DIRECTED_MUTUAL); + remove_some_edges(&g, 0.2); + test_graph(&g, /* print = */ 1); + igraph_destroy(&g); + + /* Zachary karate club with random loops and multi-edges */ + printf("Zachary karate club with loops and multi-edges.\n"); + for (int i = 0; i < 20; i++) { + igraph_famous(&g, "zachary"); + add_loop_and_multiple_edges(&g, 0.5, 0.2); + test_graph(&g, /* print = */ 0); + igraph_destroy(&g); + } + + /* Geometric random graph */ + printf("Geometric random graph.\n"); + igraph_grg_game(&g, 100, 0.2, /* torus = */ 0, /* x = */ 0, /* y = */ 0); + test_graph(&g, /* print = */ 0); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/coreness.out b/tests/unit/coreness.out new file mode 100644 index 0000000..ccf47c6 --- /dev/null +++ b/tests/unit/coreness.out @@ -0,0 +1,24 @@ +Empty graph. +( ) +Singleton graph. +( 0 ) +Full graph. +( 4 4 4 4 4 ) +Full graph with loops. +( 6 6 6 6 6 ) +Full directed graph. +mode = ALL: ( 8 8 8 8 8 ) +mode = OUT: ( 4 4 4 4 4 ) +mode = IN: ( 4 4 4 4 4 ) +Full directed graph with loops. +mode = ALL: ( 10 10 10 10 10 ) +mode = OUT: ( 5 5 5 5 5 ) +mode = IN: ( 5 5 5 5 5 ) +Zachary karate club. +( 4 4 4 4 3 3 3 4 4 2 3 1 2 4 2 2 2 2 2 3 2 2 2 3 3 3 2 3 3 3 4 3 4 4 ) +Zachary karate club, directed edges. +mode = ALL: ( 6 6 6 6 4 4 4 6 6 2 4 2 3 6 4 2 3 3 4 4 1 0 2 5 4 4 3 4 3 5 6 5 6 5 ) +mode = OUT: ( 3 3 3 3 2 2 2 3 3 1 2 1 1 3 2 2 1 2 2 2 1 0 1 2 2 2 1 2 2 2 3 2 3 2 ) +mode = IN: ( 3 3 3 3 2 2 2 3 3 1 2 1 2 3 2 0 2 1 2 2 0 0 1 3 2 1 2 2 1 3 3 2 3 3 ) +Zachary karate club with loops and multi-edges. +Geometric random graph. diff --git a/tests/unit/cutheap.c b/tests/unit/cutheap.c new file mode 100644 index 0000000..fedbc81 --- /dev/null +++ b/tests/unit/cutheap.c @@ -0,0 +1,50 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "core/cutheap.h" + +#include "test_utilities.h" + +int main(void) { + igraph_i_cutheap_t ch; + igraph_integer_t i; + + igraph_i_cutheap_init(&ch, 10); + + for (i = 0; i < 10; i++) { + igraph_i_cutheap_update(&ch, i, i); + } + while (!igraph_i_cutheap_empty(&ch)) { + igraph_integer_t idx = igraph_i_cutheap_popmax(&ch); + printf("%" IGRAPH_PRId " ", idx); + } + printf("\n"); + + igraph_i_cutheap_destroy(&ch); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/cutheap.out b/tests/unit/cutheap.out new file mode 100644 index 0000000..b53cb66 --- /dev/null +++ b/tests/unit/cutheap.out @@ -0,0 +1 @@ +9 8 7 6 5 4 3 2 1 0 diff --git a/tests/unit/cycle_bases.c b/tests/unit/cycle_bases.c new file mode 100644 index 0000000..f9bb823 --- /dev/null +++ b/tests/unit/cycle_bases.c @@ -0,0 +1,184 @@ +/* + 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, see . +*/ + +#include + +#include "test_utilities.h" + +void print_check_destroy(igraph_t *graph, igraph_vector_int_list_t *result) { + igraph_integer_t i, rank = igraph_vector_int_list_size(result); + igraph_integer_t ecount = igraph_ecount(graph), vcount = igraph_vcount(graph); + igraph_integer_t ccount; + + for (i=0; i < rank; ++i) { + igraph_vector_int_t *cycle = igraph_vector_int_list_get_ptr(result, i); + IGRAPH_ASSERT(igraph_vector_int_size(cycle) > 0); + igraph_integer_t first_edge = VECTOR(*cycle)[0]; + igraph_integer_t last_edge = VECTOR(*cycle)[igraph_vector_int_size(cycle) - 1]; + /* Verify that first and last edges of the cycle share a vertex */ + IGRAPH_ASSERT( + (IGRAPH_FROM(graph, first_edge) == IGRAPH_FROM(graph, last_edge) || + IGRAPH_FROM(graph, first_edge) == IGRAPH_TO(graph, last_edge)) || + (IGRAPH_TO(graph, first_edge) == IGRAPH_FROM(graph, last_edge) || + IGRAPH_TO(graph, first_edge) == IGRAPH_TO(graph, last_edge)) + ); + print_vector_int(cycle); + } + igraph_connected_components(graph, NULL, NULL, &ccount, IGRAPH_WEAK); + IGRAPH_ASSERT(rank == ecount - vcount + ccount); + igraph_destroy(graph); +} + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t result; + igraph_integer_t rank; + + igraph_vector_int_list_init(&result, 0); + + printf("FUNDAMENTAL CYCLE BASIS\n"); + + printf("\nNull graph\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_fundamental_cycles(&graph, &result, -1, -1, NULL); + print_check_destroy(&graph, &result); + + printf("\nSingleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_fundamental_cycles(&graph, &result, -1, -1, NULL); + print_check_destroy(&graph, &result); + + printf("\nSingle vertex with loop\n"); + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, + 0,0, + -1); + igraph_fundamental_cycles(&graph, &result, -1, -1, NULL); + print_check_destroy(&graph, &result); + + printf("\nTree\n"); + igraph_kary_tree(&graph, 3, 2, IGRAPH_TREE_UNDIRECTED); + igraph_fundamental_cycles(&graph, &result, -1, -1, NULL); + print_check_destroy(&graph, &result); + + printf("\n2-cycle\n"); + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,1, 0,1, + -1); + igraph_fundamental_cycles(&graph, &result, -1, -1, NULL); + print_check_destroy(&graph, &result); + + printf("\nDisconnected\n"); + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 1,2, 2,3, 3,1, + 4,5, 5,4, 4,5, + 6,7, 7,8, 8,9, 9,6, 6,8, + 10,10, 10,11, + 12,12, + -1); + igraph_fundamental_cycles(&graph, &result, -1, -1, NULL); + print_check_destroy(&graph, &result); + + printf("\nMINIMUM WEIGHT CYCLE BASIS\n"); + + printf("\nNull graph\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_minimum_cycle_basis(&graph, &result, /* cutoff */ -1, /* complete */ true, /* ordered */ true, NULL); + print_check_destroy(&graph, &result); + + printf("\nSingleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_minimum_cycle_basis(&graph, &result, /* cutoff */ -1, /* complete */ true, /* ordered */ true, NULL); + print_check_destroy(&graph, &result); + + printf("\nSingle vertex with loop\n"); + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, + 0,0, + -1); + igraph_minimum_cycle_basis(&graph, &result, /* cutoff */ -1, /* complete */ true, /* ordered */ true, NULL); + print_check_destroy(&graph, &result); + + printf("\nTree\n"); + igraph_kary_tree(&graph, 3, 2, IGRAPH_TREE_UNDIRECTED); + igraph_minimum_cycle_basis(&graph, &result, /* cutoff */ -1, /* complete */ true, /* ordered */ true, NULL); + print_check_destroy(&graph, &result); + + printf("\n2-cycle\n"); + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,1, 0,1, + -1); + igraph_minimum_cycle_basis(&graph, &result, /* cutoff */ -1, /* complete */ true, /* ordered */ true, NULL); + print_check_destroy(&graph, &result); + + printf("\nDisconnected\n"); + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 1,2, 2,3, 3,1, + 4,5, 5,4, 4,5, + 6,7, 7,8, 8,9, 9,6, 6,8, + 10,10, 10,11, + 12,12, + -1); + igraph_minimum_cycle_basis(&graph, &result, /* cutoff */ -1, /* complete */ true, /* ordered */ true, NULL); + print_check_destroy(&graph, &result); + + printf("\nPeriodic (5,6)-grid\n"); + + + { + igraph_vector_int_t dimvec; + igraph_vector_bool_t periodic; + + igraph_vector_int_init_int_end(&dimvec, -1, + 1, 5, 6, -1); + + igraph_vector_bool_init(&periodic, igraph_vector_int_size(&dimvec)); + igraph_vector_bool_fill(&periodic, 1); + + igraph_square_lattice(&graph, &dimvec, 1, IGRAPH_ADJ_UNDIRECTED, /* mutual */ false, &periodic); + + igraph_vector_bool_destroy(&periodic); + igraph_vector_int_destroy(&dimvec); + } + + igraph_minimum_cycle_basis(&graph, &result, /* cutoff */ -1, /* complete */ true, /* ordered */ true, NULL); + + rank = igraph_vector_int_list_size(&result); + + /* In a periodic grid graph, all elements in the minimum basis have size 4, + * except two, which have size equal to the grid dimensions. */ + + IGRAPH_ASSERT(igraph_vector_int_size(&VECTOR(result)[0]) == 4); + IGRAPH_ASSERT(igraph_vector_int_size(&VECTOR(result)[rank-1]) == 6); + IGRAPH_ASSERT(igraph_vector_int_size(&VECTOR(result)[rank-2]) == 5); + IGRAPH_ASSERT(igraph_vector_int_size(&VECTOR(result)[rank-3]) == 4); + + print_check_destroy(&graph, &result); + +#if 0 + /* This is for benchmarking */ + igraph_rng_seed(igraph_rng_default(), 42); + igraph_watts_strogatz_game(&graph, 3, 10, 2, 0.1, 0, 0); + igraph_minimum_cycle_basis(&graph, /* cutoff */ -1, /* complete */ true, * ordered */ true, &result); + print_check_destroy(&graph, &result); +#endif + + igraph_vector_int_list_destroy(&result); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/cycle_bases.out b/tests/unit/cycle_bases.out new file mode 100644 index 0000000..ec00893 --- /dev/null +++ b/tests/unit/cycle_bases.out @@ -0,0 +1,78 @@ +FUNDAMENTAL CYCLE BASIS + +Null graph + +Singleton graph + +Single vertex with loop +( 0 ) + +Tree + +2-cycle +( 0 1 ) + +Disconnected +( 1 0 2 ) +( 4 5 ) +( 3 5 ) +( 7 6 10 ) +( 8 10 9 ) +( 11 ) +( 13 ) + +MINIMUM WEIGHT CYCLE BASIS + +Null graph + +Singleton graph + +Single vertex with loop +( 0 ) + +Tree + +2-cycle +( 0 1 ) + +Disconnected +( 11 ) +( 13 ) +( 3 5 ) +( 4 5 ) +( 0 1 2 ) +( 6 7 10 ) +( 8 9 10 ) + +Periodic (5,6)-grid +( 0 1 10 3 ) +( 0 51 50 53 ) +( 1 8 9 18 ) +( 2 3 12 5 ) +( 2 53 52 55 ) +( 4 5 14 7 ) +( 4 55 54 57 ) +( 6 7 16 9 ) +( 6 57 56 59 ) +( 8 59 58 51 ) +( 10 11 20 13 ) +( 11 18 19 28 ) +( 12 13 22 15 ) +( 14 15 24 17 ) +( 16 17 26 19 ) +( 20 21 30 23 ) +( 21 28 29 38 ) +( 22 23 32 25 ) +( 24 25 34 27 ) +( 26 27 36 29 ) +( 30 31 40 33 ) +( 31 38 39 48 ) +( 32 33 42 35 ) +( 34 35 44 37 ) +( 36 37 46 39 ) +( 40 41 50 43 ) +( 41 48 49 58 ) +( 42 43 52 45 ) +( 44 45 54 47 ) +( 0 8 6 4 2 ) +( 1 51 41 31 21 11 ) diff --git a/tests/unit/d_indheap.c b/tests/unit/d_indheap.c new file mode 100644 index 0000000..88ae0aa --- /dev/null +++ b/tests/unit/d_indheap.c @@ -0,0 +1,103 @@ +/* -*- 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 + +#include "core/indheap.h" + +#include "test_utilities.h" + +int main(void) { + + igraph_d_indheap_t h; + igraph_integer_t idx1, idx2; + + /* igraph_d_indheap_init, igraph_d_indheap_destroy */ + igraph_d_indheap_init(&h, 0); + igraph_d_indheap_destroy(&h); + igraph_d_indheap_init(&h, 100); + igraph_d_indheap_destroy(&h); + + /* igraph_d_indheap_empty, igraph_d_indheap_size */ + igraph_d_indheap_init(&h, 10); + if (!igraph_d_indheap_empty(&h)) { + return 1; + } + if (igraph_d_indheap_size(&h) != 0) { + return 2; + } + igraph_d_indheap_push(&h, 10, 0, 0); + if (igraph_d_indheap_empty(&h)) { + return 3; + } + if (igraph_d_indheap_size(&h) != 1) { + return 4; + } + + /* igraph_d_indheap_push */ + igraph_d_indheap_push(&h, 9, 9, 8); + igraph_d_indheap_push(&h, 3, 3, 2); + igraph_d_indheap_push(&h, 2, 2, 1); + igraph_d_indheap_push(&h, 1, 1, 0); + igraph_d_indheap_push(&h, 7, 7, 6); + igraph_d_indheap_push(&h, 4, 4, 3); + igraph_d_indheap_push(&h, 0, 0, 1); + igraph_d_indheap_push(&h, 6, 6, 5); + igraph_d_indheap_push(&h, 5, 5, 4); + igraph_d_indheap_push(&h, 8, 8, 7); + + /* igraph_d_indheap_max, igraph_d_indheap_delete_max */ + while (!igraph_d_indheap_empty(&h)) { + printf("% " IGRAPH_PRId , (igraph_integer_t)igraph_d_indheap_max(&h)); + printf("% " IGRAPH_PRId "\n", (igraph_integer_t)igraph_d_indheap_delete_max(&h)); + } + + /* igraph_d_indheap_reserve */ + igraph_d_indheap_reserve(&h, 5); + igraph_d_indheap_reserve(&h, 20); + igraph_d_indheap_reserve(&h, 0); + igraph_d_indheap_reserve(&h, 3); + + /* igraph_d_indheap_max_index */ + igraph_d_indheap_push(&h, 0, 0, 1); + igraph_d_indheap_push(&h, 8, 8, 7); + igraph_d_indheap_push(&h, 2, 2, 1); + igraph_d_indheap_push(&h, 7, 7, 6); + igraph_d_indheap_push(&h, 9, 9, 8); + igraph_d_indheap_push(&h, 4, 4, 3); + igraph_d_indheap_push(&h, 3, 3, 2); + igraph_d_indheap_push(&h, 5, 5, 4); + igraph_d_indheap_push(&h, 1, 1, 0); + igraph_d_indheap_push(&h, 6, 6, 5); + while (!igraph_d_indheap_empty(&h)) { + igraph_d_indheap_max_index(&h, &idx1, &idx2); + printf(" %" IGRAPH_PRId " %" IGRAPH_PRId, idx1, idx2); + igraph_d_indheap_delete_max(&h); + } + printf("\n"); + igraph_d_indheap_destroy(&h); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/d_indheap.out b/tests/unit/d_indheap.out new file mode 100644 index 0000000..7b58a46 --- /dev/null +++ b/tests/unit/d_indheap.out @@ -0,0 +1,12 @@ + 10 10 + 9 9 + 8 8 + 7 7 + 6 6 + 5 5 + 4 4 + 3 3 + 2 2 + 1 1 + 0 0 + 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 1 diff --git a/tests/unit/dgemv.c b/tests/unit/dgemv.c new file mode 100644 index 0000000..2a1c2a7 --- /dev/null +++ b/tests/unit/dgemv.c @@ -0,0 +1,103 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include + +#include "test_utilities.h" + +/* Matrix-vector multiplication: y = A.x */ +void matmul(const igraph_matrix_t *A, const igraph_vector_t *x, igraph_vector_t *y, igraph_real_t beta) { + igraph_integer_t i, j, nr = igraph_matrix_nrow(A), nc = igraph_matrix_ncol(A); + + IGRAPH_ASSERT(nc == igraph_vector_size(x)); + IGRAPH_ASSERT(nr == igraph_vector_size(y)); + + igraph_vector_scale(y, beta); + + for (i=0; i < nr; ++i) { + for (j=0; j < nc; ++j) { + VECTOR(*y)[i] += MATRIX(*A, i, j) * VECTOR(*x)[j]; + } + } +} + +int main(void) { + igraph_matrix_t A; + igraph_vector_t x, y1, y2; + igraph_integer_t i, j; + const igraph_integer_t nr = 5, nc = 8; + + igraph_rng_seed(igraph_rng_default(), 54632); + + igraph_matrix_init(&A, nr, nc); + igraph_vector_init(&x, nc); + + /* Fill with arbitrary values. Should be zeroes by beta. */ + igraph_vector_init_range(&y1, 1, nr + 1); + igraph_vector_init_copy(&y2, &y1); + + for (i=0; i < nr; ++i) { + for (j=0; j < nc; ++j) { + MATRIX(A, i, j) = (igraph_real_t) RNG_INTEGER(-10, 10); + } + } + + for (j=0; j < nc; ++j) { + VECTOR(x)[j] = (igraph_real_t) RNG_INTEGER(-10, 10); + } + + printf("Input matrix A:\n"); + print_matrix(&A); + + printf("\nInput vector x:\n"); + print_vector(&x); + + igraph_blas_dgemv(0, 1, &A, &x, 0, &y1); + matmul(&A, &x, &y2, 0); + + printf("\nResult vector DGEMV:\n"); + print_vector(&y1); + + printf("\nResult vector naive:\n"); + print_vector(&y2); + + /* Results should be exact since all values are integers */ + IGRAPH_ASSERT(igraph_vector_all_e(&y1, &y2)); + + printf("\nAdding to previous result with beta=2:\n"); + + igraph_blas_dgemv(0, 1, &A, &x, 2, &y1); + matmul(&A, &x, &y2, 2); + + printf("\nResult vector DGEMV:\n"); + print_vector(&y1); + + printf("\nResult vector naive:\n"); + print_vector(&y2); + + IGRAPH_ASSERT(igraph_vector_all_e(&y1, &y2)); + + igraph_vector_destroy(&y2); + igraph_vector_destroy(&y1); + igraph_vector_destroy(&x); + igraph_matrix_destroy(&A); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/dgemv.out b/tests/unit/dgemv.out new file mode 100644 index 0000000..51b4599 --- /dev/null +++ b/tests/unit/dgemv.out @@ -0,0 +1,23 @@ +Input matrix A: +[ -8 -3 6 -8 1 6 -10 -6 + 5 9 0 0 -10 10 7 -6 + -6 -9 5 7 -10 -7 -10 -10 + -7 -7 -5 0 6 10 5 2 + 10 2 -6 4 -2 -4 -8 -2 ] + +Input vector x: +( -1 -9 -5 -5 -6 -8 3 2 ) + +Result vector DGEMV: +( -51 -97 93 -2 -2 ) + +Result vector naive: +( -51 -97 93 -2 -2 ) + +Adding to previous result with beta=2: + +Result vector DGEMV: +( -153 -291 279 -6 -6 ) + +Result vector naive: +( -153 -291 279 -6 -6 ) diff --git a/tests/unit/edge_selectors.c b/tests/unit/edge_selectors.c new file mode 100644 index 0000000..a53215d --- /dev/null +++ b/tests/unit/edge_selectors.c @@ -0,0 +1,143 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void check(igraph_t *graph, igraph_es_t *es) { + igraph_eit_t eit; + igraph_integer_t edge; + IGRAPH_ASSERT(igraph_eit_create(graph, *es, &eit) == IGRAPH_SUCCESS); + for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + edge = IGRAPH_EIT_GET(eit); + printf("%" IGRAPH_PRId " %" IGRAPH_PRId "\n", IGRAPH_FROM(graph, edge), IGRAPH_TO(graph, edge)); + } + igraph_eit_destroy(&eit); +} + +int main(void) { + igraph_t g, g_no_vertices, g_no_edges; + igraph_es_t es, es_copy; + igraph_vector_int_t v, check_as_vector; + igraph_eit_t eit; + + igraph_small(&g, 5, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, -1); + igraph_small(&g_no_vertices, 0, IGRAPH_UNDIRECTED, -1); + igraph_small(&g_no_edges, 5, IGRAPH_UNDIRECTED, -1); + + printf("Checking es_vector:\n"); + igraph_vector_int_init_int(&v, 3, 2, 3, 4); + igraph_es_vector(&es, &v); + check(&g, &es); + CHECK_ERROR(igraph_eit_create(&g_no_edges, es, &eit), IGRAPH_EINVAL); + CHECK_ERROR(igraph_eit_create(&g_no_vertices, es, &eit), IGRAPH_EINVAL); + + printf("es_vector_copy\n"); + igraph_es_vector_copy(&es, &v); + check(&g, &es); + + printf("es_copy\n"); + igraph_es_copy(&es_copy, &es); + check(&g, &es); + igraph_es_destroy(&es_copy); + + printf("es_as_vector\n"); + igraph_vector_int_clear(&v); + igraph_es_as_vector(&g, es, &v); + igraph_vector_int_print(&v); + igraph_es_destroy(&es); + igraph_vector_int_destroy(&v); + + printf("es_vector with negative entry should fail.\n"); + igraph_vector_int_init_int(&v, 3, -2, 3, 4); + igraph_es_vector(&es, &v); + CHECK_ERROR(igraph_eit_create(&g, es, &eit), IGRAPH_EINVAL); + igraph_vector_int_destroy(&v); + + printf("Checking es_range:\n"); + igraph_es_range(&es, 2, 5); + check(&g, &es); + CHECK_ERROR(igraph_eit_create(&g_no_edges, es, &eit), IGRAPH_EINVAL); + CHECK_ERROR(igraph_eit_create(&g_no_vertices, es, &eit), IGRAPH_EINVAL); + + printf("Checking eit_as_vector using seq:\n"); + igraph_eit_create(&g, es, &eit); + igraph_vector_int_init_int(&check_as_vector, 0); + igraph_eit_as_vector(&eit, &check_as_vector); + igraph_vector_int_print(&check_as_vector); + igraph_vector_int_destroy(&check_as_vector); + + printf("Checking ess_range using es_range parameters:\n"); + es = igraph_ess_range(2, 5); + check(&g, &es); + CHECK_ERROR(igraph_eit_create(&g_no_edges, es, &eit), IGRAPH_EINVAL); + CHECK_ERROR(igraph_eit_create(&g_no_vertices, es, &eit), IGRAPH_EINVAL); + + printf("Checking whether ess_range accepts an empty range.\n"); + es = igraph_ess_range(2, 2); + check(&g, &es); + CHECK_ERROR(igraph_eit_create(&g_no_edges, es, &eit), IGRAPH_EINVAL); + CHECK_ERROR(igraph_eit_create(&g_no_vertices, es, &eit), IGRAPH_EINVAL); + + printf("Checking es_path:\n"); + igraph_vector_int_init_int(&v, 3, 4, 3, 2); + igraph_es_path(&es, &v, /*directed*/0); + check(&g, &es); + CHECK_ERROR(igraph_eit_create(&g_no_vertices, es, &eit), IGRAPH_EINVVID); + CHECK_ERROR(igraph_eit_create(&g_no_edges, es, &eit), IGRAPH_EINVAL); + igraph_es_destroy(&es); + + igraph_es_path(&es, &v, /*directed*/1); + CHECK_ERROR(igraph_eit_create(&g, es, &eit), IGRAPH_EINVAL); + igraph_vector_int_destroy(&v); + igraph_es_destroy(&es); + + printf("es_path with negative entry should fail.\n"); + igraph_vector_int_init_int(&v, 3, -4, 3, 2); + igraph_es_path(&es, &v, /*directed*/0); + CHECK_ERROR(igraph_eit_create(&g, es, &eit), IGRAPH_EINVVID); + + printf("Checking es_type.\n"); + IGRAPH_ASSERT(igraph_es_type(&es) == IGRAPH_ES_PATH); + igraph_es_destroy(&es); + + printf("Checking es_none.\n"); + igraph_es_none(&es); + check(&g, &es); + + printf("es_pairs:\n"); + igraph_vector_int_destroy(&v); + igraph_vector_int_init_int(&v, 4, 1,1, 2,0); + igraph_es_pairs(&es, &v, IGRAPH_DIRECTED); + check(&g, &es); + igraph_es_destroy(&es); + + printf("es_pairs for nonexistent pair should fail.\n"); + igraph_vector_int_destroy(&v); + igraph_vector_int_init_int(&v, 4, 6,1, 2,0); + igraph_es_pairs(&es, &v, IGRAPH_DIRECTED); + CHECK_ERROR(igraph_eit_create(&g, es, &eit), IGRAPH_EINVVID); + + igraph_vector_int_destroy(&v); + igraph_destroy(&g); + igraph_destroy(&g_no_vertices); + igraph_destroy(&g_no_edges); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/edge_selectors.out b/tests/unit/edge_selectors.out new file mode 100644 index 0000000..463eeb7 --- /dev/null +++ b/tests/unit/edge_selectors.out @@ -0,0 +1,36 @@ +Checking es_vector: +1 1 +1 3 +2 0 +es_vector_copy +1 1 +1 3 +2 0 +es_copy +1 1 +1 3 +2 0 +es_as_vector +2 3 4 +es_vector with negative entry should fail. +Checking es_range: +1 1 +1 3 +2 0 +Checking eit_as_vector using seq: +2 3 4 +Checking ess_range using es_range parameters: +1 1 +1 3 +2 0 +Checking whether ess_range accepts an empty range. +Checking es_path: +3 4 +2 3 +es_path with negative entry should fail. +Checking es_type. +Checking es_none. +es_pairs: +1 1 +2 0 +es_pairs for nonexistent pair should fail. diff --git a/tests/unit/efficiency.c b/tests/unit/efficiency.c new file mode 100644 index 0000000..56b4dff --- /dev/null +++ b/tests/unit/efficiency.c @@ -0,0 +1,153 @@ +/* + IGraph library. + Copyright (C) 2020-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, see . +*/ + +#include + +#include "test_utilities.h" + +int test_graph(const char* name, const igraph_t* graph, const igraph_real_t* weights_array) { + igraph_real_t eff; + igraph_vector_t eff_vec; + igraph_vector_t weights; + + printf("###### Testing graph: %s ######\n\n", name); + + igraph_vector_init(&eff_vec, 0); + + if (weights_array) { + printf("UNWEIGHTED CASE:\n\n"); + } + + igraph_global_efficiency(graph, &eff, NULL, 0); + printf("Global efficiency, undirected: %f\n", eff); + + igraph_global_efficiency(graph, &eff, NULL, 1); + printf("Global efficiency, directed: %f\n", eff); + + igraph_average_local_efficiency(graph, &eff, NULL, 0, IGRAPH_ALL); + printf("Average local efficiency, undirected: %f\n", eff); + + igraph_average_local_efficiency(graph, &eff, NULL, 1, IGRAPH_ALL); + printf("Average local efficiency, directed, all neighbors: %f\n", eff); + + igraph_average_local_efficiency(graph, &eff, NULL, 1, IGRAPH_IN); + printf("Average local efficiency, directed, in-neighbors: %f\n", eff); + + igraph_average_local_efficiency(graph, &eff, NULL, 1, IGRAPH_OUT); + printf("Average local efficiency, directed, out-neighbors: %f\n", eff); + + printf("\nLocal efficiency, undirected:\n"); + igraph_local_efficiency(graph, &eff_vec, igraph_vss_all(), NULL, 0, IGRAPH_ALL); + print_vector(&eff_vec); + + printf("\nLocal efficiency, directed, all neighbors:\n"); + igraph_local_efficiency(graph, &eff_vec, igraph_vss_all(), NULL, 1, IGRAPH_ALL); + print_vector(&eff_vec); + + printf("\nLocal efficiency, directed, in-neighbors:\n"); + igraph_local_efficiency(graph, &eff_vec, igraph_vss_all(), NULL, 1, IGRAPH_IN); + print_vector(&eff_vec); + + printf("\nLocal efficiency, directed, out-neighbors:\n"); + igraph_local_efficiency(graph, &eff_vec, igraph_vss_all(), NULL, 1, IGRAPH_OUT); + print_vector(&eff_vec); + + if (weights_array) { + igraph_vector_view(&weights, weights_array, igraph_ecount(graph)); + printf("\nWEIGHTED CASE:\n\n"); + + igraph_global_efficiency(graph, &eff, &weights, 0); + printf("Global efficiency, undirected: %f\n", eff); + + igraph_global_efficiency(graph, &eff, &weights, 1); + printf("Global efficiency, directed: %f\n", eff); + + igraph_average_local_efficiency(graph, &eff, &weights, 0, IGRAPH_ALL); + printf("Average local efficiency, undirected: %f\n", eff); + + igraph_average_local_efficiency(graph, &eff, &weights, 1, IGRAPH_ALL); + printf("Average local efficiency, directed, all neighbors: %f\n", eff); + + igraph_average_local_efficiency(graph, &eff, &weights, 1, IGRAPH_IN); + printf("Average local efficiency, directed, in-neighbors: %f\n", eff); + + igraph_average_local_efficiency(graph, &eff, &weights, 1, IGRAPH_OUT); + printf("Average local efficiency, directed, out-neighbors: %f\n", eff); + + printf("\nLocal efficiency, undirected:\n"); + igraph_local_efficiency(graph, &eff_vec, igraph_vss_all(), &weights, 0, IGRAPH_ALL); + print_vector(&eff_vec); + + printf("\nLocal efficiency, directed, all neighbors:\n"); + igraph_local_efficiency(graph, &eff_vec, igraph_vss_all(), &weights, 1, IGRAPH_ALL); + print_vector(&eff_vec); + + printf("\nLocal efficiency, directed, in-neighbors:\n"); + igraph_local_efficiency(graph, &eff_vec, igraph_vss_all(), &weights, 1, IGRAPH_IN); + print_vector(&eff_vec); + + printf("\nLocal efficiency, directed, out-neighbors:\n"); + igraph_local_efficiency(graph, &eff_vec, igraph_vss_all(), &weights, 1, IGRAPH_OUT); + print_vector(&eff_vec); + } + + printf("\n\n"); + + igraph_vector_destroy(&eff_vec); + + return 0; +} + +int test_ring(void) { + int result; + igraph_t graph; + const igraph_real_t weights_array[] = {1, 1, 1, 1}; + + igraph_ring(&graph, 4, IGRAPH_DIRECTED, /* mutual = */ 0, /* circular = */ 1); + result = test_graph("Ring graph", &graph, weights_array); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return result; +} + +int test_small_graph(void) { + int result; + igraph_t graph; + const igraph_real_t weights_array[] = {4, 4, 4, 3, 1, 5, 1, 2, 4, 5, 3, 5, 5, 4, 1, 1, 5, 4, 1, 1, 2, 1, 3, 5}; + + igraph_small(&graph, 13, /* directed= */ 1, + 0,8, 1,3, 1,4, 1,5, 1,8, 1,10, 2,0, 2,1, 2,4, 3,5, 4,2, 4,7, + 4,9, 5,3, 5,10, 6,7, 8,2, 8,3, 8,4, 8,9, 9,3, 9,4, 11,9, 11,3, + -1); + result = test_graph("Small test graph", &graph, weights_array); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return result; +} + +int main(void) { + + RUN_TEST(test_ring); + RUN_TEST(test_small_graph); + + return 0; +} diff --git a/tests/unit/efficiency.out b/tests/unit/efficiency.out new file mode 100644 index 0000000..c9a4b18 --- /dev/null +++ b/tests/unit/efficiency.out @@ -0,0 +1,90 @@ +###### Testing graph: Ring graph ###### + +UNWEIGHTED CASE: + +Global efficiency, undirected: 0.833333 +Global efficiency, directed: 0.611111 +Average local efficiency, undirected: 0.500000 +Average local efficiency, directed, all neighbors: 0.250000 +Average local efficiency, directed, in-neighbors: 0.000000 +Average local efficiency, directed, out-neighbors: 0.000000 + +Local efficiency, undirected: +( 0.5 0.5 0.5 0.5 ) + +Local efficiency, directed, all neighbors: +( 0.25 0.25 0.25 0.25 ) + +Local efficiency, directed, in-neighbors: +( 0 0 0 0 ) + +Local efficiency, directed, out-neighbors: +( 0 0 0 0 ) + +WEIGHTED CASE: + +Global efficiency, undirected: 0.833333 +Global efficiency, directed: 0.611111 +Average local efficiency, undirected: 0.500000 +Average local efficiency, directed, all neighbors: 0.250000 +Average local efficiency, directed, in-neighbors: 0.000000 +Average local efficiency, directed, out-neighbors: 0.000000 + +Local efficiency, undirected: +( 0.5 0.5 0.5 0.5 ) + +Local efficiency, directed, all neighbors: +( 0.25 0.25 0.25 0.25 ) + +Local efficiency, directed, in-neighbors: +( 0 0 0 0 ) + +Local efficiency, directed, out-neighbors: +( 0 0 0 0 ) + + +###### Testing graph: Small test graph ###### + +UNWEIGHTED CASE: + +Global efficiency, undirected: 0.509615 +Global efficiency, directed: 0.274786 +Average local efficiency, undirected: 0.605769 +Average local efficiency, directed, all neighbors: 0.329936 +Average local efficiency, directed, in-neighbors: 0.212115 +Average local efficiency, directed, out-neighbors: 0.149466 + +Local efficiency, undirected: +( 1 0.633333 0.833333 0.641667 0.5 0.833333 0 0 0.711111 0.722222 1 1 0 ) + +Local efficiency, directed, all neighbors: +( 0.75 0.401111 0.375 0.340833 0.316667 0.333333 0 0 0.466667 0.305556 0.5 0.5 0 ) + +Local efficiency, directed, in-neighbors: +( 0 0 0.5 0.340833 0.527778 0.5 0 0 0.166667 0.222222 0.5 0 0 ) + +Local efficiency, directed, out-neighbors: +( 0 0.3875 0.25 0 0.0555556 0 0 0 0.583333 0.166667 0 0.5 0 ) + +WEIGHTED CASE: + +Global efficiency, undirected: 0.247686 +Global efficiency, directed: 0.125356 +Average local efficiency, undirected: 0.281019 +Average local efficiency, directed, all neighbors: 0.150368 +Average local efficiency, directed, in-neighbors: 0.122897 +Average local efficiency, directed, out-neighbors: 0.068520 + +Local efficiency, undirected: +( 0.333333 0.306219 0.525 0.419167 0.358333 0.187037 0 0 0.380635 0.310185 0.333333 0.5 0 ) + +Local efficiency, directed, all neighbors: +( 0.291667 0.16403 0.245833 0.207976 0.204643 0.075 0 0 0.204987 0.143981 0.166667 0.25 0 ) + +Local efficiency, directed, in-neighbors: +( 0 0 0.5 0.207976 0.341071 0.125 0 0 0.0625 0.194444 0.166667 0 0 ) + +Local efficiency, directed, out-neighbors: +( 0 0.180711 0.116667 0 0.0416667 0 0 0 0.246164 0.0555556 0 0.25 0 ) + + diff --git a/tests/unit/empty b/tests/unit/empty new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/erdos_renyi_game_gnm.c b/tests/unit/erdos_renyi_game_gnm.c new file mode 100644 index 0000000..c0c73e1 --- /dev/null +++ b/tests/unit/erdos_renyi_game_gnm.c @@ -0,0 +1,116 @@ +/* -*- 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, see . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_bool_t simple; + + /* Ensure that the test is deterministic */ + igraph_rng_seed(igraph_rng_default(), 137); + + /* singleton with loop */ + + igraph_erdos_renyi_game_gnm(&g, 1, 1, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 1); + IGRAPH_ASSERT(igraph_ecount(&g) == 1); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnm(&g, 1, 1, IGRAPH_DIRECTED, IGRAPH_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 1); + IGRAPH_ASSERT(igraph_ecount(&g) == 1); + IGRAPH_ASSERT(igraph_is_directed(&g)); + + igraph_destroy(&g); + + + /* directed with loops */ + igraph_erdos_renyi_game_gnm(&g, 10, 10 * 10 - 1, IGRAPH_DIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 10 - 1); + IGRAPH_ASSERT(igraph_is_directed(&g)); + + igraph_simplify(&g, /*remove_multiple=*/ false, /*remove_loops=*/ true, /*edge_comb=*/ NULL); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 9 || igraph_ecount(&g) == 10 * 9 - 1); + + igraph_destroy(&g); + + /* directed without loops */ + igraph_erdos_renyi_game_gnm(&g, 10, 10 * 9 - 1, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 9 - 1); + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + /* undirected with loops */ + igraph_erdos_renyi_game_gnm(&g, 10, 10 * 11 / 2 - 1, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 11 / 2 - 1); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + + igraph_simplify(&g, /*remove_multiple=*/ false, /*remove_loops=*/ true, /*edge_comb=*/ NULL); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 9 / 2 || igraph_ecount(&g) == 10 * 9 / 2 - 1); + + igraph_destroy(&g); + + /* undirected without loops */ + igraph_erdos_renyi_game_gnm(&g, 10, 10 * 9 / 2 - 1, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 9 / 2 - 1); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + + /* Create a couple of large graphs too */ + igraph_erdos_renyi_game_gnm(&g, 100000, 2.0 * 100000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100000); + IGRAPH_ASSERT(igraph_ecount(&g) == 200000); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnm(&g, 100000, 2.0 * 100000, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(simple); + IGRAPH_ASSERT(igraph_vcount(&g) == 100000); + IGRAPH_ASSERT(igraph_ecount(&g) == 200000); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnm(&g, 100000, 2.0 * 100000, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100000); + IGRAPH_ASSERT(igraph_ecount(&g) == 200000); + igraph_simplify(&g, 0, 1, /*edge_comb=*/ 0); /* only remove loops */ + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnm(&g, 100000, 2.0 * 100000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100000); + IGRAPH_ASSERT(igraph_ecount(&g) == 200000); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/erdos_renyi_game_gnp.c b/tests/unit/erdos_renyi_game_gnp.c new file mode 100644 index 0000000..0c86e42 --- /dev/null +++ b/tests/unit/erdos_renyi_game_gnp.c @@ -0,0 +1,173 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include + +#include "test_utilities.h" + +void stress_test(void) { + igraph_rng_seed(igraph_rng_default(), 137); + + for (igraph_integer_t size=2; size < 5; size++) { + for (igraph_integer_t i=0; i < 100; i++) { + igraph_t g; + igraph_bool_t simple; + + igraph_erdos_renyi_game_gnp(&g, size, 0.5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + + igraph_is_simple(&g, &simple); + if (! simple) { + printf("Erdos-Renyi GNP graph is not simple! size=%" IGRAPH_PRId ", i=%" IGRAPH_PRId ".\n", + size, i); + print_graph(&g); + } + IGRAPH_ASSERT(simple); + + igraph_destroy(&g); + } + } + + for (igraph_integer_t size=2; size < 5; size++) { + for (igraph_integer_t i=0; i < 100; i++) { + igraph_t g; + igraph_bool_t simple; + + igraph_erdos_renyi_game_gnp(&g, size, 0.5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_is_simple(&g, &simple); + if (! simple) { + printf("Erdos-Renyi GNP graph is not simple! size=%" IGRAPH_PRId ", i=%" IGRAPH_PRId ".\n", + size, i); + print_graph(&g); + } + IGRAPH_ASSERT(simple); + + igraph_destroy(&g); + } + } + + VERIFY_FINALLY_STACK(); +} + +void test_examples(void) { + igraph_t g; + igraph_bool_t simple; + + /* Ensure that the test is deterministic */ + igraph_rng_seed(igraph_rng_default(), 137); + + /* Empty graph */ + + igraph_erdos_renyi_game_gnp(&g, 10, 0.0, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 10, 0.0, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + /* Singleton with loop */ + + igraph_erdos_renyi_game_gnp(&g, 1, 1.0, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_ecount(&g) == 1); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(! simple); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 1, 1.0, IGRAPH_DIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_ecount(&g) == 1); + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(! simple); + igraph_destroy(&g); + + /* Complete graph */ + + igraph_erdos_renyi_game_gnp(&g, 10, 1.0, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 9 / 2); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 10, 1.0, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 9); + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 10, 1.0, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 11 / 2); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(! simple); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 10, 1.0, IGRAPH_DIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 10); + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_is_simple(&g, &simple); IGRAPH_ASSERT(! simple); + igraph_destroy(&g); + + /* Random graph */ + + igraph_erdos_renyi_game_gnp(&g, 10, 0.5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 10, 0.5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 10, 0.5, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 10, 0.5, IGRAPH_DIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_destroy(&g); + + /* Create a couple of large graphs too */ + + igraph_erdos_renyi_game_gnp(&g, 100000, 2.0 / 100000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100000); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 100000, 2.0 / 100000, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100000); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 100000, 2.0 / 100000, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100000); + igraph_destroy(&g); + + igraph_erdos_renyi_game_gnp(&g, 100000, 2.0 / 100000, IGRAPH_DIRECTED, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100000); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); +} + +int main(void) { + + test_examples(); + stress_test(); + + return 0; +} diff --git a/tests/unit/error_macros.c b/tests/unit/error_macros.c new file mode 100644 index 0000000..262e8a6 --- /dev/null +++ b/tests/unit/error_macros.c @@ -0,0 +1,71 @@ +/* + 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 . +*/ + +#include + +#include +#include + +int cause_error(void) { + IGRAPH_ERRORF("%d %f %ld %c", IGRAPH_EINVAL, 1, 1.0, 1L, 'a'); + return IGRAPH_SUCCESS; +} + +int cause_warning(void) { + IGRAPH_WARNINGF("%d %f %ld %c", 1, 1.0, 1L, 'a'); + return IGRAPH_SUCCESS; +} + +int cause_fatal(void) { + IGRAPH_FATALF("%d %f %ld %c", 1, 1.0, 1L, 'a'); +} + +void error_handler(const char *reason, const char *file, int line, igraph_error_t igraph_errno) { + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + printf("Error. Reason: %s\nErrno: %d\n", reason, igraph_errno); +} + +void warning_handler(const char *reason, const char *file, int line) { + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + printf("Warning. Reason: %s\n", reason); +} + +void fatal_handler(const char *reason, const char *file, int line) { + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + printf("Fatal. Reason: %s\n", reason); + exit(0); +} + +int main(void) { + igraph_set_error_handler(&error_handler); + igraph_set_warning_handler(&warning_handler); + igraph_set_fatal_handler(&fatal_handler); + + IGRAPH_ASSERT(cause_error() == IGRAPH_EINVAL); + IGRAPH_ASSERT(cause_warning() == IGRAPH_SUCCESS); + cause_fatal(); + + /* The igraph_fatal() call must not return, so the following lines should not run. */ + + printf("This should not be printed."); + + return 1; +} diff --git a/tests/unit/error_macros.out b/tests/unit/error_macros.out new file mode 100644 index 0000000..0142ead --- /dev/null +++ b/tests/unit/error_macros.out @@ -0,0 +1,4 @@ +Error. Reason: 1 1.000000 1 a +Errno: 4 +Warning. Reason: 1 1.000000 1 a +Fatal. Reason: 1 1.000000 1 a diff --git a/tests/unit/expand_path_to_pairs.c b/tests/unit/expand_path_to_pairs.c new file mode 100644 index 0000000..b1cfb25 --- /dev/null +++ b/tests/unit/expand_path_to_pairs.c @@ -0,0 +1,61 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +igraph_error_t test_path_expansion(void) { + igraph_vector_int_t path; + + igraph_vector_int_init(&path, 0); + IGRAPH_CHECK(igraph_expand_path_to_pairs(&path)); + print_vector_int(&path); + igraph_vector_int_destroy(&path); + + igraph_vector_int_init(&path, 0); + igraph_vector_int_push_back(&path, 0); + IGRAPH_CHECK(igraph_expand_path_to_pairs(&path)); + print_vector_int(&path); + igraph_vector_int_destroy(&path); + + igraph_vector_int_init(&path, 0); + igraph_vector_int_push_back(&path, 0); + igraph_vector_int_push_back(&path, 1); + IGRAPH_CHECK(igraph_expand_path_to_pairs(&path)); + print_vector_int(&path); + igraph_vector_int_destroy(&path); + + igraph_vector_int_init(&path, 0); + igraph_vector_int_push_back(&path, 2); + igraph_vector_int_push_back(&path, 3); + igraph_vector_int_push_back(&path, 5); + igraph_vector_int_push_back(&path, 7); + IGRAPH_CHECK(igraph_expand_path_to_pairs(&path)); + print_vector_int(&path); + igraph_vector_int_destroy(&path); + + return IGRAPH_SUCCESS; +} + +int main(void) { + IGRAPH_ASSERT(test_path_expansion() == IGRAPH_SUCCESS); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/expand_path_to_pairs.out b/tests/unit/expand_path_to_pairs.out new file mode 100644 index 0000000..4b9fc06 --- /dev/null +++ b/tests/unit/expand_path_to_pairs.out @@ -0,0 +1,4 @@ +( ) +( ) +( 0 1 ) +( 2 3 3 5 5 7 ) diff --git a/tests/unit/fatal_handler.c b/tests/unit/fatal_handler.c new file mode 100644 index 0000000..ce28e9a --- /dev/null +++ b/tests/unit/fatal_handler.c @@ -0,0 +1,39 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include +#include + +igraph_fatal_handler_t hanlder; + +void handler(const char *reason, const char *file, int line) { + printf("Reason: %s\nFile: %s\nLine: %d\n", reason, file, line); + exit(0); /* We use exit(0) instead of abort() to allow the test to succeed. */ +} + +int main(void) { + igraph_set_fatal_handler(&handler); + + igraph_fatal("REASON", "FILENAME", 123); + + /* The igraph_fatal() call must not return, so the following lines should not run. */ + + printf("This should not be printed."); + + return 0; +} diff --git a/tests/unit/fatal_handler.out b/tests/unit/fatal_handler.out new file mode 100644 index 0000000..1f7a3e6 --- /dev/null +++ b/tests/unit/fatal_handler.out @@ -0,0 +1,3 @@ +Reason: REASON +File: FILENAME +Line: 123 diff --git a/tests/unit/foreign_empty.c b/tests/unit/foreign_empty.c new file mode 100644 index 0000000..3585cd7 --- /dev/null +++ b/tests/unit/foreign_empty.c @@ -0,0 +1,91 @@ +/* + 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 . +*/ + +#include +#include +#include "test_utilities.h" + +/* This test verifies that trying to read an empty file does not cause + * a crash or fatal error in any of the file format readers. */ + +int main(void) { + igraph_t graph; + FILE *file; + + file = fopen("empty", "r"); + IGRAPH_ASSERT(file != NULL); + + /* Formats for which an emtpy file is valid */ + + /* Edge list */ + IGRAPH_ASSERT(igraph_read_graph_edgelist(&graph, file, 0, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 0); + igraph_destroy(&graph); + rewind(file); + VERIFY_FINALLY_STACK(); + + /* NCOL */ + IGRAPH_ASSERT(igraph_read_graph_ncol(&graph, file, NULL, 1, IGRAPH_ADD_WEIGHTS_IF_PRESENT, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 0); + igraph_destroy(&graph); + rewind(file); + VERIFY_FINALLY_STACK(); + + /* LGL */ + IGRAPH_ASSERT(igraph_read_graph_lgl(&graph, file, 1, IGRAPH_ADD_WEIGHTS_IF_PRESENT, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 0); + igraph_destroy(&graph); + rewind(file); + VERIFY_FINALLY_STACK(); + + /* Formats for which an empty file is invalid */ + + igraph_set_error_handler(&igraph_error_handler_printignore); + + /* GraphML */ + IGRAPH_ASSERT(igraph_read_graph_graphml(&graph, file, 0) != IGRAPH_SUCCESS); + rewind(file); + VERIFY_FINALLY_STACK(); + + /* Pajek */ + IGRAPH_ASSERT(igraph_read_graph_pajek(&graph, file) != IGRAPH_SUCCESS); + rewind(file); + VERIFY_FINALLY_STACK(); + + /* DL */ + IGRAPH_ASSERT(igraph_read_graph_dl(&graph, file, IGRAPH_UNDIRECTED) != IGRAPH_SUCCESS); + rewind(file); + VERIFY_FINALLY_STACK(); + + /* GML */ + IGRAPH_ASSERT(igraph_read_graph_gml(&graph, file) != IGRAPH_SUCCESS); + rewind(file); + VERIFY_FINALLY_STACK(); + + /* graphdb */ + IGRAPH_ASSERT(igraph_read_graph_graphdb(&graph, file, IGRAPH_UNDIRECTED) != IGRAPH_SUCCESS); + rewind(file); + VERIFY_FINALLY_STACK(); + + /* DIMACS flow problem */ + IGRAPH_ASSERT(igraph_read_graph_dimacs_flow(&graph, file, NULL, NULL, NULL, NULL, NULL, IGRAPH_DIRECTED) != IGRAPH_SUCCESS); + rewind(file); + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/full.c b/tests/unit/full.c new file mode 100644 index 0000000..7ee5237 --- /dev/null +++ b/tests/unit/full.c @@ -0,0 +1,68 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_integer_t n_vertices = 10; + + printf("Null graph\n"); + igraph_full(&g, 0, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Singleton graph, no loops\n"); + igraph_full(&g, 1, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Singleton graph, loops\n"); + igraph_full(&g, 1, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Undirected, no loops\n"); + igraph_full(&g, n_vertices, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Directed, no loops\n"); + igraph_full(&g, n_vertices, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Undirected, with loops\n"); + igraph_full(&g, n_vertices, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Directed, with loops\n"); + igraph_full(&g, n_vertices, IGRAPH_DIRECTED, IGRAPH_LOOPS); + print_graph_canon(&g); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/full.out b/tests/unit/full.out new file mode 100644 index 0000000..d033c22 --- /dev/null +++ b/tests/unit/full.out @@ -0,0 +1,326 @@ +Null graph +directed: false +vcount: 0 +edges: { +} +Singleton graph, no loops +directed: false +vcount: 1 +edges: { +} +Singleton graph, loops +directed: false +vcount: 1 +edges: { +0 0 +} +Undirected, no loops +directed: false +vcount: 10 +edges: { +0 1 +0 2 +0 3 +0 4 +0 5 +0 6 +0 7 +0 8 +0 9 +1 2 +1 3 +1 4 +1 5 +1 6 +1 7 +1 8 +1 9 +2 3 +2 4 +2 5 +2 6 +2 7 +2 8 +2 9 +3 4 +3 5 +3 6 +3 7 +3 8 +3 9 +4 5 +4 6 +4 7 +4 8 +4 9 +5 6 +5 7 +5 8 +5 9 +6 7 +6 8 +6 9 +7 8 +7 9 +8 9 +} +Directed, no loops +directed: true +vcount: 10 +edges: { +0 1 +0 2 +0 3 +0 4 +0 5 +0 6 +0 7 +0 8 +0 9 +1 0 +1 2 +1 3 +1 4 +1 5 +1 6 +1 7 +1 8 +1 9 +2 0 +2 1 +2 3 +2 4 +2 5 +2 6 +2 7 +2 8 +2 9 +3 0 +3 1 +3 2 +3 4 +3 5 +3 6 +3 7 +3 8 +3 9 +4 0 +4 1 +4 2 +4 3 +4 5 +4 6 +4 7 +4 8 +4 9 +5 0 +5 1 +5 2 +5 3 +5 4 +5 6 +5 7 +5 8 +5 9 +6 0 +6 1 +6 2 +6 3 +6 4 +6 5 +6 7 +6 8 +6 9 +7 0 +7 1 +7 2 +7 3 +7 4 +7 5 +7 6 +7 8 +7 9 +8 0 +8 1 +8 2 +8 3 +8 4 +8 5 +8 6 +8 7 +8 9 +9 0 +9 1 +9 2 +9 3 +9 4 +9 5 +9 6 +9 7 +9 8 +} +Undirected, with loops +directed: false +vcount: 10 +edges: { +0 0 +0 1 +0 2 +0 3 +0 4 +0 5 +0 6 +0 7 +0 8 +0 9 +1 1 +1 2 +1 3 +1 4 +1 5 +1 6 +1 7 +1 8 +1 9 +2 2 +2 3 +2 4 +2 5 +2 6 +2 7 +2 8 +2 9 +3 3 +3 4 +3 5 +3 6 +3 7 +3 8 +3 9 +4 4 +4 5 +4 6 +4 7 +4 8 +4 9 +5 5 +5 6 +5 7 +5 8 +5 9 +6 6 +6 7 +6 8 +6 9 +7 7 +7 8 +7 9 +8 8 +8 9 +9 9 +} +Directed, with loops +directed: true +vcount: 10 +edges: { +0 0 +0 1 +0 2 +0 3 +0 4 +0 5 +0 6 +0 7 +0 8 +0 9 +1 0 +1 1 +1 2 +1 3 +1 4 +1 5 +1 6 +1 7 +1 8 +1 9 +2 0 +2 1 +2 2 +2 3 +2 4 +2 5 +2 6 +2 7 +2 8 +2 9 +3 0 +3 1 +3 2 +3 3 +3 4 +3 5 +3 6 +3 7 +3 8 +3 9 +4 0 +4 1 +4 2 +4 3 +4 4 +4 5 +4 6 +4 7 +4 8 +4 9 +5 0 +5 1 +5 2 +5 3 +5 4 +5 5 +5 6 +5 7 +5 8 +5 9 +6 0 +6 1 +6 2 +6 3 +6 4 +6 5 +6 6 +6 7 +6 8 +6 9 +7 0 +7 1 +7 2 +7 3 +7 4 +7 5 +7 6 +7 7 +7 8 +7 9 +8 0 +8 1 +8 2 +8 3 +8 4 +8 5 +8 6 +8 7 +8 8 +8 9 +9 0 +9 1 +9 2 +9 3 +9 4 +9 5 +9 6 +9 7 +9 8 +9 9 +} diff --git a/tests/unit/gen2wheap.c b/tests/unit/gen2wheap.c new file mode 100644 index 0000000..dc30338 --- /dev/null +++ b/tests/unit/gen2wheap.c @@ -0,0 +1,123 @@ + +#include + +#include "core/genheap.h" + +#include "test_utilities.h" + +typedef struct intpair_t { + int x, y; +} intpair_t; + +int cmp(const void *a, const void *b) { + const intpair_t *sa = a, *sb = b; + if (sa->x < sb->x) return -1; + if (sa->x > sb->x) return 1; + if (sa->y < sb->y) return -1; + if (sa->y > sb->y) return 1; + return 0; +} + +void intpair_print(const intpair_t *p) { + printf("(%d, %d)", p->x, p->y); +} + +int main(void) { + igraph_integer_t n; + igraph_vector_int_t idx; + igraph_gen2wheap_t h; + igraph_integer_t i; + intpair_t p; + + igraph_rng_seed(igraph_rng_default(), 42); + + n = 10; + + igraph_gen2wheap_init(&h, cmp, sizeof(intpair_t), n); + IGRAPH_ASSERT(igraph_gen2wheap_max_size(&h) == n); + + igraph_vector_int_init_range(&idx, 0, n); + igraph_vector_int_shuffle(&idx); + + for (i=0; i < n; i++) { + igraph_integer_t j = VECTOR(idx)[i]; + p.x = RNG_INTEGER(1, 10); + p.y = RNG_INTEGER(1, 10); + printf("Adding %2" IGRAPH_PRId ": ", j); + intpair_print(&p); + printf("\n"); + IGRAPH_ASSERT(! igraph_gen2wheap_has_elem(&h, j)); + igraph_gen2wheap_push_with_index(&h, j, &p); + igraph_gen2wheap_check(&h); + IGRAPH_ASSERT(igraph_gen2wheap_has_elem(&h, j)); + IGRAPH_ASSERT(igraph_gen2wheap_size(&h) == i+1); + } + + printf("\n"); + + i = igraph_gen2wheap_size(&h); + while (!igraph_gen2wheap_empty(&h)) { + printf("Removing %2" IGRAPH_PRId ": ", igraph_gen2wheap_max_index(&h)); + intpair_print(igraph_gen2wheap_max(&h)); + printf("\n"); + igraph_gen2wheap_delete_max(&h); + igraph_gen2wheap_check(&h); + i--; + IGRAPH_ASSERT(igraph_gen2wheap_size(&h) == i); + } + + printf("\n"); + + n=5; + for (igraph_integer_t i=0; i < n; i++) { + p.x = RNG_INTEGER(1, 10); + p.y = RNG_INTEGER(1, 10); + printf("Adding %2" IGRAPH_PRId ": ", i); + intpair_print(&p); + printf("\n"); + IGRAPH_ASSERT(! igraph_gen2wheap_has_elem(&h, i)); + igraph_gen2wheap_push_with_index(&h, i, &p); + igraph_gen2wheap_check(&h); + IGRAPH_ASSERT(igraph_gen2wheap_has_elem(&h, i)); + IGRAPH_ASSERT(igraph_gen2wheap_size(&h) == i+1); + } + + i = 1; + p.x = -1; p.y = -1; + printf("\nModifying %" IGRAPH_PRId " to ", i); + intpair_print(&p); + printf("\n"); + igraph_gen2wheap_modify(&h, i, &p); + igraph_gen2wheap_check(&h); + + i = igraph_gen2wheap_max_index(&h); + p = * (intpair_t *) igraph_gen2wheap_max(&h); + p.x -= 3; + printf("Modifying max to "); + intpair_print(&p); + printf("\n"); + igraph_gen2wheap_modify(&h, i, &p); + igraph_gen2wheap_check(&h); + + printf("\n"); + + i = igraph_gen2wheap_size(&h); + while (!igraph_gen2wheap_empty(&h)) { + printf("Removing %2" IGRAPH_PRId ": ", igraph_gen2wheap_max_index(&h)); + intpair_print(igraph_gen2wheap_max(&h)); + printf("\n"); + igraph_gen2wheap_delete_max(&h); + igraph_gen2wheap_check(&h); + i--; + IGRAPH_ASSERT(igraph_gen2wheap_size(&h) == i); + } + + printf("\n"); + + igraph_vector_int_destroy(&idx); + igraph_gen2wheap_destroy(&h); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/gen2wheap.out b/tests/unit/gen2wheap.out new file mode 100644 index 0000000..3dd5ab0 --- /dev/null +++ b/tests/unit/gen2wheap.out @@ -0,0 +1,37 @@ +Adding 9: (6, 9) +Adding 2: (10, 10) +Adding 0: (7, 4) +Adding 8: (9, 2) +Adding 3: (7, 2) +Adding 5: (7, 1) +Adding 7: (5, 1) +Adding 6: (5, 4) +Adding 4: (10, 10) +Adding 1: (6, 4) + +Removing 4: (10, 10) +Removing 2: (10, 10) +Removing 8: (9, 2) +Removing 0: (7, 4) +Removing 3: (7, 2) +Removing 5: (7, 1) +Removing 9: (6, 9) +Removing 1: (6, 4) +Removing 6: (5, 4) +Removing 7: (5, 1) + +Adding 0: (10, 7) +Adding 1: (6, 7) +Adding 2: (2, 5) +Adding 3: (5, 2) +Adding 4: (10, 2) + +Modifying 1 to (-1, -1) +Modifying max to (7, 7) + +Removing 4: (10, 2) +Removing 0: (7, 7) +Removing 3: (5, 2) +Removing 2: (2, 5) +Removing 1: (-1, -1) + diff --git a/tests/unit/global_transitivity.c b/tests/unit/global_transitivity.c new file mode 100644 index 0000000..d471c74 --- /dev/null +++ b/tests/unit/global_transitivity.c @@ -0,0 +1,113 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_real_t global, global2, global3; + + /* Small graphs */ + + printf("Null graph: "); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_transitivity_undirected(&g, &global, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global, "%g"); + printf("\n"); + igraph_destroy(&g); + + printf("\nSingleton graph: "); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + igraph_transitivity_undirected(&g, &global, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global, "%g"); + printf("\n"); + igraph_destroy(&g); + + printf("\nTwo connected vertices: "); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + igraph_transitivity_undirected(&g, &global, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global, "%g"); + printf("\n"); + igraph_destroy(&g); + + printf("\nTriangle: "); + igraph_full(&g, 3, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_transitivity_undirected(&g, &global, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global, "%g"); + printf("\n"); + igraph_destroy(&g); + + printf("\nTwo-star: "); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,2, 0,1, -1); + igraph_transitivity_undirected(&g, &global, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global, "%g"); + printf("\n"); + igraph_destroy(&g); + + printf("\nZachary karate club: "); + igraph_famous(&g, "Zachary"); + igraph_transitivity_undirected(&g, &global, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global, "%g"); + printf("\n"); + igraph_destroy(&g); + + printf("\nDirected and multigraphs:\n"); + + igraph_small(&g, 20, IGRAPH_DIRECTED, + 15, 12, 12, 10, 15, 0, 11, 10, 2, 8, 8, 6, 13, 17, 10, 10, 17, 2, 14, + 0, 16, 13, 14, 14, 0, 5, 6, 4, 0, 9, 0, 6, 10, 9, 16, 4, 14, 5, 17, + 15, 14, 9, 17, 17, 1, 4, 10, 16, 7, 0, 11, 12, 6, 13, 2, 17, 4, 0, 0, + 14, 4, 0, 6, 16, 16, 14, 13, 13, 12, 11, 3, 11, 11, 3, 6, 7, 4, 14, + 10, 8, 13, 7, 14, 2, 5, 2, 0, 14, 3, 15, 5, 5, 7, 2, 14, 15, 5, 10, + 10, 16, 7, 9, 14, 0, 15, 7, 13, 1, 15, 1, 4, 5, 4, 6, 16, 13, 6, 17, + 8, 6, 9, 3, 8, 6, 6, 14, 11, 14, 6, 10, 10, 5, 1, 0, 16, 17, 9, 1, 5, + 0, 5, 15, 8, 0, 0, 8, 5, 3, 9, 4, 13, 12, 11, 0, 11, 0, 10, 6, 4, 13, + 8, 9, 11, 11, 3, 16, 1, 2, 16, 0, 9, 8, 3, 8, 8, 7, 12, 10, 9, 3, 13, + 5, 3, 9, 6, 2, 11, 10, 1, 16, 0, 2, 10, 17, 16, 8, 11, 5, 13, 0, 19, 19, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1); + + printf("\nDirected multi: "); + igraph_transitivity_undirected(&g, &global, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global, "%.10g"); + printf("\n"); + + printf("Undirected multi: "); + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_COLLAPSE, NULL); + igraph_transitivity_undirected(&g, &global2, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global2, "%.10g"); + printf("\n"); + + printf("Simple: "); + igraph_simplify(&g, true, true, NULL); + igraph_transitivity_undirected(&g, &global3, IGRAPH_TRANSITIVITY_NAN); + print_real(stdout, global3, "%.10g"); + printf("\n"); + + IGRAPH_ASSERT(global == global2); + IGRAPH_ASSERT(global == global3); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/global_transitivity.out b/tests/unit/global_transitivity.out new file mode 100644 index 0000000..4121901 --- /dev/null +++ b/tests/unit/global_transitivity.out @@ -0,0 +1,17 @@ +Null graph: NaN + +Singleton graph: NaN + +Two connected vertices: NaN + +Triangle: 1 + +Two-star: 0 + +Zachary karate club: 0.255682 + +Directed and multigraphs: + +Directed multi: 0.4357541899 +Undirected multi: 0.4357541899 +Simple: 0.4357541899 diff --git a/tests/unit/glpk_error.c b/tests/unit/glpk_error.c new file mode 100644 index 0000000..b943e97 --- /dev/null +++ b/tests/unit/glpk_error.c @@ -0,0 +1,73 @@ + +#include + +#include + +#include "test_utilities.h" + +static clock_t start; + +/* Wait for at least a second before attempting interruption */ +igraph_error_t interruption_handler(void *data) { + IGRAPH_UNUSED(data); + if ( ((double) (clock() - start)) / CLOCKS_PER_SEC > 1.0 ) { + IGRAPH_FINALLY_FREE(); + return IGRAPH_INTERRUPTED; + } else { + return IGRAPH_SUCCESS; + } +} + +int main(void) { + igraph_t graph; + igraph_vector_int_t res; + igraph_error_handler_t *ehandler; + + igraph_vector_int_init(&res, 0); + + /* Skip test when igraph does not have GLPK support. */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, -1); + ehandler = igraph_set_error_handler(igraph_error_handler_ignore); + if (igraph_feedback_arc_set(&graph, &res, NULL, IGRAPH_FAS_EXACT_IP) == IGRAPH_UNIMPLEMENTED) { + igraph_destroy(&graph); + igraph_vector_int_destroy(&res); + return 77; + } + igraph_set_error_handler(ehandler); + igraph_destroy(&graph); + + + /* Current versions of GLPK will error if more than 100 million rows (MAX_M) are added. + The graph size of 700 is chosen to just exceed this size. If future GLPK + versions relax this restriction, the test will need to be updated accordinly. */ + igraph_full(&graph, 700, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + + ehandler = igraph_set_error_handler(igraph_error_handler_printignore); + IGRAPH_ASSERT(igraph_feedback_arc_set(&graph, &res, NULL, IGRAPH_FAS_EXACT_IP) == IGRAPH_EGLP); + igraph_set_error_handler(ehandler); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&res); + + VERIFY_FINALLY_STACK(); + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_int_init(&res, 0); + igraph_erdos_renyi_game_gnm(&graph, 100, 200, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + + igraph_set_interruption_handler(interruption_handler); + ehandler = igraph_set_error_handler(igraph_error_handler_printignore); + start = clock(); + IGRAPH_ASSERT(igraph_feedback_arc_set(&graph, &res, NULL, IGRAPH_FAS_EXACT_IP) == IGRAPH_INTERRUPTED); + igraph_set_error_handler(ehandler); + igraph_set_interruption_handler(NULL); + + igraph_destroy(&graph); + + igraph_vector_int_destroy(&res); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/gml.c b/tests/unit/gml.c new file mode 100644 index 0000000..2a92aa5 --- /dev/null +++ b/tests/unit/gml.c @@ -0,0 +1,56 @@ +/* + 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 +*/ + +#include +#include + +#include "test_utilities.h" + +void test_input(const char *filename) { + igraph_t graph; + FILE *ifile; + + ifile = fopen(filename, "r"); + if (! ifile) { + IGRAPH_FATALF("Cannot open '%s'.\n", filename); + } + + printf("===== %s =====\n", filename); + + IGRAPH_ASSERT(igraph_read_graph_gml(&graph, ifile) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_write_graph_gml(&graph, stdout, IGRAPH_WRITE_GML_DEFAULT_SW, NULL, "igraph") == IGRAPH_SUCCESS); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + printf("======================\n\n"); +} + +int main(void) { + + /* Enable attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + test_input("graph1.gml"); + test_input("graph2.gml"); + test_input("graph3.gml"); + + return 0; +} diff --git a/tests/unit/gml.out b/tests/unit/gml.out new file mode 100644 index 0000000..9a63918 --- /dev/null +++ b/tests/unit/gml.out @@ -0,0 +1,79 @@ +===== graph1.gml ===== +Creator "igraph" +Version 1 +graph +[ + directed 1 + AttOne "x" + AttTwo 3.14 + node + [ + id 1 + b "2" + c 5.6 + nan "" + ] + node + [ + id 2 + a 2 + b "asd" + d -Inf + nan "foo" + ] + node + [ + id 5 + b "" + nan "" + ] + edge + [ + source 5 + target 1 + weight -0.45 + label "-0.45" + ] + edge + [ + source 2 + target 5 + label "Tom & Jerry's "friendship"" + num1 Inf + ] +] +====================== + +===== graph2.gml ===== +Creator "igraph" +Version 1 +graph +[ + directed 1 + node + [ + id 0 + Foo "" + ] + node + [ + id 1 + Foo "" + ] + node + [ + id 2 + Foo "bar & baz" + ] +] +====================== + +===== graph3.gml ===== +Creator "igraph" +Version 1 +graph +[ + directed 0 +] +====================== + diff --git a/tests/unit/graph1.gml b/tests/unit/graph1.gml new file mode 100644 index 0000000..eea87d0 --- /dev/null +++ b/tests/unit/graph1.gml @@ -0,0 +1,44 @@ +# comment lines are ignored +Version 1 +graph [ + directed 1 + node [ + id 1 + a [ b 1 ] + b +2 + c 5.6 + graphics [ x 0 y 0 ] + ] + node [ + a 1 + id 2 + a 2 + b "asd" + d -Inf + nan "foo" + ] + edge [ + source 5 target 1 + weight -0.45 + label -0.45 + num1 NAN + ] + node [ + id 5 + c [ str "bar" ] + ] + edge [ + source 2 target 5 + label "Tom & Jerry's "friendship"" +# The 'sou_rce' attribute will be ignored by the GML writer as it would conflict with +# 'source' after stripping the '_' character. + sou_rce 1.0 + num1 +inF + ] + AttOne 1 + AttOne "x" + AttTwo 3.14 +] + +# second graph is ignored +graph [ node [ ] ] diff --git a/tests/unit/graph2.gml b/tests/unit/graph2.gml new file mode 100644 index 0000000..31b089d --- /dev/null +++ b/tests/unit/graph2.gml @@ -0,0 +1,11 @@ + +# This graph contains nodes with no id fields, +# as well as invalid version and directedness. +# There is no newline at the end of this file. + +Version 3 +graph [ + directed 2 + node [ ] node [ ] node [ Foo "bar & baz" ] + composite [ a 1 ] +] diff --git a/tests/unit/graph3.gml b/tests/unit/graph3.gml new file mode 100644 index 0000000..5858ebf --- /dev/null +++ b/tests/unit/graph3.gml @@ -0,0 +1,4 @@ + +# Null graph, undirected + +graph [] diff --git a/tests/unit/graphlets.c b/tests/unit/graphlets.c new file mode 100644 index 0000000..e4e989a --- /dev/null +++ b/tests/unit/graphlets.c @@ -0,0 +1,150 @@ +/* -*- 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 + +*/ + +#include + +#include "test_utilities.h" + +void print_cliques_and_thresholds(igraph_vector_int_list_t* cliques, igraph_vector_t* thresholds) { + printf("Cliques:\n"); + print_vector_int_list(cliques); + printf("Thresholds:\n"); + print_vector(thresholds); +} + +void print_cliques_and_mu(igraph_vector_int_list_t* cliques, igraph_vector_t* mu) { + printf("Cliques:\n"); + print_vector_int_list(cliques); + printf("Mu:\n"); + print_vector_format(mu, stdout, "%.5f"); +} + +void test_graphlets_candidate_basis_simple(void) { + igraph_t g; + igraph_vector_int_list_t cliques; + igraph_vector_t thresholds; + igraph_vector_t weights; + igraph_integer_t eid; + + igraph_full(&g, 5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_int_list_init(&cliques, 0); + igraph_vector_init(&thresholds, 0); + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1); + + igraph_graphlets_candidate_basis(&g, &weights, &cliques, &thresholds); + print_cliques_and_thresholds(&cliques, &thresholds); + + igraph_get_eid(&g, &eid, 0, 1, IGRAPH_UNDIRECTED, /* error = */ 0); + VECTOR(weights)[eid] = 2; + + igraph_graphlets_candidate_basis(&g, &weights, &cliques, &thresholds); + print_cliques_and_thresholds(&cliques, &thresholds); + + igraph_vector_destroy(&weights); + igraph_vector_int_list_destroy(&cliques); + igraph_vector_destroy(&thresholds); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); +} + +void test_graphlets_filtering(void) { + igraph_t g; + igraph_vector_int_list_t cliques; + igraph_vector_t thresholds; + igraph_vector_t weights_vec; + igraph_real_t weights[] = { 8, 8, 8, 5, 5, 5, 5, 5 }; + + igraph_small(&g, 5, IGRAPH_UNDIRECTED, 0, 1, 0, 2, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, -1); + igraph_vector_int_list_init(&cliques, 0); + igraph_vector_init(&thresholds, 0); + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + + igraph_graphlets_candidate_basis(&g, &weights_vec, &cliques, &thresholds); + print_cliques_and_thresholds(&cliques, &thresholds); + + igraph_vector_int_list_destroy(&cliques); + igraph_vector_destroy(&thresholds); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); +} + +void test_zachary_random_weights(void) { + igraph_t g; + igraph_vector_int_list_t cliques; + igraph_vector_t thresholds; + igraph_vector_t weights_vec; + igraph_real_t weights[] = { + 1, 5, 1, 1, 2, 4, 2, 2, 1, 4, 1, 5, 4, 2, 2, 3, 1, 1, 3, 4, 5, 5, 5, 4, + 2, 4, 3, 2, 1, 2, 3, 2, 4, 4, 2, 5, 4, 5, 4, 2, 2, 3, 1, 5, 2, 2, 2, 4, + 3, 5, 2, 2, 2, 5, 1, 1, 4, 5, 2, 1, 5, 4, 4, 1, 3, 3, 5, 5, 4, 5, 4, 2, + 2, 1, 2, 5, 5, 4 + }; + + igraph_famous(&g, "zachary"); + igraph_vector_int_list_init(&cliques, 0); + igraph_vector_init(&thresholds, 0); + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + + igraph_graphlets_candidate_basis(&g, &weights_vec, &cliques, &thresholds); + print_cliques_and_thresholds(&cliques, &thresholds); + + igraph_vector_int_list_destroy(&cliques); + igraph_vector_destroy(&thresholds); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + +} + +void test_projection(void) { + igraph_t g; + igraph_vector_int_list_t cliques; + igraph_vector_t mu; + igraph_vector_t weights_vec; + igraph_real_t weights[] = { 2, 2, 3, 1, 1, 4, 4, 4 }; + + igraph_small(&g, 5, IGRAPH_UNDIRECTED, 0, 1, 0, 2, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, -1); + igraph_vector_int_list_init(&cliques, 0); + igraph_vector_init(&mu, 0); + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + + igraph_graphlets(&g, &weights_vec, &cliques, &mu, 1000); + print_cliques_and_mu(&cliques, &mu); + + igraph_vector_int_list_destroy(&cliques); + igraph_vector_destroy(&mu); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); +} + +int main(void) { + test_graphlets_candidate_basis_simple(); + test_graphlets_filtering(); + test_zachary_random_weights(); + test_projection(); + + return 0; +} diff --git a/tests/unit/graphlets.out b/tests/unit/graphlets.out new file mode 100644 index 0000000..3138de0 --- /dev/null +++ b/tests/unit/graphlets.out @@ -0,0 +1,111 @@ +Cliques: +{ + 0: ( 0 1 2 3 4 ) +} +Thresholds: +( 1 ) +Cliques: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 0 1 ) +} +Thresholds: +( 1 2 ) +Cliques: +{ + 0: ( 0 1 2 ) + 1: ( 1 2 3 4 ) +} +Thresholds: +( 8 5 ) +Cliques: +{ + 0: ( 0 11 ) + 1: ( 9 33 ) + 2: ( 2 9 ) + 3: ( 14 32 33 ) + 4: ( 15 32 33 ) + 5: ( 5 6 16 ) + 6: ( 0 1 17 ) + 7: ( 0 3 12 ) + 8: ( 26 29 33 ) + 9: ( 20 32 33 ) + 10: ( 0 1 21 ) + 11: ( 22 32 33 ) + 12: ( 18 32 33 ) + 13: ( 23 27 33 ) + 14: ( 23 29 32 33 ) + 15: ( 23 25 ) + 16: ( 24 25 31 ) + 17: ( 24 27 ) + 18: ( 2 27 ) + 19: ( 28 31 33 ) + 20: ( 2 28 ) + 21: ( 0 4 6 ) + 22: ( 0 4 10 ) + 23: ( 31 32 33 ) + 24: ( 0 31 ) + 25: ( 0 5 6 ) + 26: ( 0 5 10 ) + 27: ( 19 33 ) + 28: ( 0 1 19 ) + 29: ( 13 33 ) + 30: ( 8 30 32 33 ) + 31: ( 1 30 ) + 32: ( 0 1 2 3 7 ) + 33: ( 0 1 2 3 13 ) + 34: ( 2 8 32 ) + 35: ( 0 2 8 ) + 36: ( 14 33 ) + 37: ( 15 33 ) + 38: ( 5 6 ) + 39: ( 0 17 ) + 40: ( 1 17 ) + 41: ( 3 12 ) + 42: ( 26 33 ) + 43: ( 26 29 ) + 44: ( 20 32 ) + 45: ( 0 21 ) + 46: ( 1 21 ) + 47: ( 22 33 ) + 48: ( 27 33 ) + 49: ( 23 29 33 ) + 50: ( 29 32 33 ) + 51: ( 23 29 ) + 52: ( 23 33 ) + 53: ( 24 25 ) + 54: ( 28 31 ) + 55: ( 4 6 ) + 56: ( 4 10 ) + 57: ( 31 33 ) + 58: ( 31 32 ) + 59: ( 0 6 ) + 60: ( 5 10 ) + 61: ( 0 19 ) + 62: ( 1 19 ) + 63: ( 8 30 33 ) + 64: ( 8 30 ) + 65: ( 8 33 ) + 66: ( 1 7 ) + 67: ( 0 2 7 ) + 68: ( 2 3 7 ) + 69: ( 2 7 ) + 70: ( 3 7 ) + 71: ( 1 13 ) + 72: ( 0 2 13 ) + 73: ( 2 3 13 ) + 74: ( 0 13 ) + 75: ( 0 2 ) + 76: ( 2 8 ) +} +Thresholds: +( 4 2 2 2 3 2 1 1 2 1 1 1 2 2 1 5 3 1 3 4 2 1 1 4 3 2 1 2 1 2 1 4 1 1 1 2 4 5 5 4 5 4 5 5 5 2 5 4 4 2 2 4 5 4 5 5 4 5 5 4 4 2 5 2 3 5 3 2 2 4 4 4 2 2 5 5 3 ) +Cliques: +{ + 0: ( 2 3 4 ) + 1: ( 0 1 2 ) + 2: ( 1 2 3 4 ) + 3: ( 1 2 ) +} +Mu: +( 1.13842 0.92554 0.86148 0.00000 ) diff --git a/tests/unit/graphml-default-attrs.xml b/tests/unit/graphml-default-attrs.xml new file mode 100644 index 0000000..5adca45 --- /dev/null +++ b/tests/unit/graphml-default-attrs.xml @@ -0,0 +1,25 @@ + + + + TRUE + + + male + + + 20 + + + FALSE + + + FALSE30 + female + + + + + diff --git a/tests/unit/graphml-hsa05010.xml b/tests/unit/graphml-hsa05010.xml new file mode 100644 index 0000000..507750e --- /dev/null +++ b/tests/unit/graphml-hsa05010.xml @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + hsa + 05010 + + compound + 1 + cpd:C00027 + C00027 + + + + + + compound + 2 + cpd:C00070 + C00070... + + + + + + compound + 2 + cpd:C14818 + C00070... + + + + + + gene + 4 + hsa:51107 + APH1A + Q96BI3 + APH1A + GO:0005515,GO:0016021,GO:0016485,GO:0043085 + + + gene + 6 + hsa:4311 + MME + P08473 + MME + GO:0004245,GO:0016021,GO:0016787,GO:0008237,GO:0007267,GO:0006508,GO:0005887,GO:0005886,GO:0016020 + + + gene + 7 + hsa:2932 + GSK3B + P49841 + GSK3B + GO:0016301,GO:0016740,GO:0005977,GO:0004696,GO:0005524,GO:0004674,GO:0006468,GO:0004672 + + + gene + 8 + hsa:4137 + MAPT + + + + + + gene + 9 + hsa:836 + CASP3 + P42574 + CASP3 + GO:0016787,GO:0008233,GO:0006917,GO:0030693,GO:0008234,GO:0006915,GO:0006508 + + + gene + 11 + hsa:840 + CASP7 + P55210 + CASP7 + GO:0008233,GO:0016787,GO:0008632,GO:0008234,GO:0006915,GO:0005737,GO:0006508 + + + gene + 12 + hsa:55851 + PSENEN + Q9NZ42 + PEN2 + + + + gene + 13 + hsa:6622 + SNCA + P37840 + SNCA + GO:0005737,GO:0007417,GO:0006916 + + + gene + 14 + hsa:5663 + PSEN1... + P49768 + + GO:0016021,GO:0007059,GO:0007001,GO:0006916,GO:0000776,GO:0000775,GO:0005639,GO:0005624,GO:0005783,GO:0007242 + + + gene + 14 + hsa:5664 + PSEN1... + P49810 + PSEN2 + GO:0016021,GO:0008632,GO:0007059,GO:0007001,GO:0000776,GO:0005639,GO:0005783,GO:0007242 + + + gene + 15 + hsa:836 + CASP3 + P42574 + CASP3 + GO:0016787,GO:0008233,GO:0006917,GO:0030693,GO:0008234,GO:0006915,GO:0006508 + + + gene + 16 + hsa:23385 + NCSTN + Q92542 + NCSTN + GO:0016021,GO:0016485,GO:0006508 + + + gene + 17 + hsa:2 + A2M + P01023 + A2M + GO:0017114,GO:0004866,GO:0051260,GO:0019899,GO:0008320,GO:0006886,GO:0004867 + + + gene + 18 + hsa:3416 + IDE + P14735 + IDE + GO:0004231,GO:0016787,GO:0008237,GO:0007548,GO:0007267,GO:0007165,GO:0006508,GO:0005777,GO:0005625,GO:0005615,GO:0004871,GO:0003824,GO:0004222 + + + gene + 19 + hsa:8883 + APPBP1 + Q13564 + APPBP1 + GO:0007165,GO:0005737,GO:0003824 + + + gene + 20 + hsa:23621 + BACE1... + P56817 + BACE1 + GO:0004190,GO:0008233,GO:0009049,GO:0016021,GO:0016787,GO:0050435,GO:0005768,GO:0006508,GO:0005794,GO:0006509,GO:0008798,GO:0005887,GO:0004194 + + + gene + 20 + hsa:25825 + BACE1... + Q9Y5Z0 + + GO:0004190,GO:0009049,GO:0016021,GO:0016787,GO:0009306,GO:0006464,GO:0005624,GO:0006508,GO:0004194 + + + gene + 22 + hsa:351 + APP, AD1 + P05067 + APP + GO:0008201,GO:0007155,GO:0006915,GO:0005905,GO:0006897,GO:0016021,GO:0005515,GO:0005887,GO:0005576,GO:0004867 + + + gene + 23 + hsa:2597 + GAPDH, GAPD + P04406 + + + + + gene + 24 + hsa:348 + APOE, AD2 + P02649 + APOE + GO:0001540,GO:0008015,GO:0007271,GO:0005737,GO:0005319,GO:0008201,GO:0006869,GO:0008289 + + + gene + 25 + hsa:322 + APBB1, RIR + O00213 + APBB1 + GO:0007165,GO:0001540,GO:0008134,GO:0045449,GO:0050821,GO:0005634,GO:0030308,GO:0045749,GO:0035035,GO:0007050,GO:0030048,GO:0045202,GO:0030027,GO:0030426,GO:0007409,GO:0050760 + + + gene + 26 + hsa:4023 + LPL + P06858 + LPL + GO:0005319,GO:0004465,GO:0016787,GO:0016042,GO:0008201,GO:0008015,GO:0006631,GO:0005576,GO:0006629,GO:0003824 + + + gene + 27 + hsa:4035 + LRP1, APR, A2MR + Q07954 + LRP1 + GO:0016021,GO:0004872,GO:0016020,GO:0008283,GO:0008034,GO:0006629,GO:0005887,GO:0005624,GO:0005509,GO:0005319,GO:0006897,GO:0005905 + + + 4.95265 + 0.693152 + 0.185704 + 0.670769 + 0.145403 + 0.05698 + 0.172033 + 0.00546283 + 2.93737 + 0.556617 + 0.176068 + 0.483953 + + + 0.50493 + 0.112413 + 0.111437 + 0.033196 + 0.145403 + 0.0605613 + 0.181454 + 0.00580618 + -0 + -0 + 0.171988 + -0 + + + 7.8977 + 1 + 1 + 0.995807 + 9.64739 + 1 + 1 + 0.998753 + 2.93737 + 0.400174 + 0.136512 + 0.347932 + + + 3.5307 + 0.498171 + 0.123255 + 0.455066 + 4.0529 + 0.366988 + 0.0822633 + 0.344877 + + + 5.41325 + 1 + 1 + 0.976533 + -0 + -0 + 0.151863 + -0 + 9.56439 + 1 + 1 + 0.998679 + + + 3.20433 + 0.383125 + 0.0883496 + 0.341558 + 0.145403 + 0.0605613 + 0.181454 + 0.00580618 + 10.0077 + 1 + 1 + 0.999029 + + + + + 0.50493 + 0.0888891 + 0.0880977 + 0.0262494 + 5.53428 + 1 + 1 + 0.978422 + -0 + -0 + 0.137908 + -0 + + + 3.17114 + 0.346041 + 0.0972198 + 0.307624 + 2.60224 + 1 + 1 + 0.835317 + -0 + -0 + 0.137908 + -0 + + + + + diff --git a/tests/unit/graphml-lenient.xml b/tests/unit/graphml-lenient.xml new file mode 100644 index 0000000..9130abe --- /dev/null +++ b/tests/unit/graphml-lenient.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tests/unit/graphml-namespace.xml b/tests/unit/graphml-namespace.xml new file mode 100644 index 0000000..83e5203 --- /dev/null +++ b/tests/unit/graphml-namespace.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/tests/unit/graphml-whitespace.xml b/tests/unit/graphml-whitespace.xml new file mode 100644 index 0000000..681ca80 --- /dev/null +++ b/tests/unit/graphml-whitespace.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + 1 + spam + true + + + + + + + + + + 3 + ham + true + + + + 4 + bacon + true + + + + 5 + eggs + true + + + + + + + + + + + diff --git a/tests/unit/graphml-yed.xml b/tests/unit/graphml-yed.xml new file mode 100644 index 0000000..bd66572 --- /dev/null +++ b/tests/unit/graphml-yed.xml @@ -0,0 +1,177 @@ + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + B + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + X + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/unit/harmonic_centrality.c b/tests/unit/harmonic_centrality.c new file mode 100644 index 0000000..426fd9b --- /dev/null +++ b/tests/unit/harmonic_centrality.c @@ -0,0 +1,138 @@ +/* + IGraph library. + Copyright (C) 2020-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, see . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_t res; + igraph_vector_t weights; + + igraph_vector_init(&res, 0); + + /* Path graph */ + igraph_ring(&graph, 7, IGRAPH_DIRECTED, 0, /* circular */ 0); + + printf("Unweighted undirected:\n"); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ NULL, /* normalized= */ 1); + print_vector(&res); + printf("Unweighted directed:\n"); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_OUT, /* weights= */ NULL, /* normalized= */ 1); + print_vector(&res); + + printf("Unweighted undirected, cutoff=0:\n"); + igraph_harmonic_centrality_cutoff(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ NULL, /* normalized= */ 1, 0); + print_vector(&res); + printf("Unweighted undirected, cutoff=1:\n"); + igraph_harmonic_centrality_cutoff(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ NULL, /* normalized= */ 1, 1); + print_vector(&res); + + igraph_vector_init(&weights, igraph_ecount(&graph)); + igraph_vector_fill(&weights, 1.0); + + printf("Unit-weighted undirected:\n"); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ &weights, /* normalized= */ 1); + print_vector(&res); + printf("Unit-weighted directed:\n"); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_OUT, /* weights= */ &weights, /* normalized= */ 1); + print_vector(&res); + + printf("Unit-weighted undirected, cutoff=0:\n"); + igraph_harmonic_centrality_cutoff(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ &weights, /* normalized= */ 1, 0); + print_vector(&res); + printf("Unit-weighted undirected, cutoff=1:\n"); + igraph_harmonic_centrality_cutoff(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ &weights, /* normalized= */ 1, 1); + print_vector(&res); + + igraph_vector_destroy(&weights); + + igraph_vector_init_range(&weights, 1, igraph_ecount(&graph) + 1); + printf("Weighted undirected:\n"); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ &weights, /* normalized= */ 1); + print_vector(&res); + printf("Weighted directed:\n"); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_OUT, /* weights= */ &weights, /* normalized= */ 1); + print_vector(&res); + igraph_vector_destroy(&weights); + + igraph_destroy(&graph); + + /* Graphs with no edges */ + + igraph_vector_init(&weights, 0); + + /* Null graph */ + + printf("Null graph:\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ NULL, /* normalized= */ 1); + print_vector(&res); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ &weights, /* normalized= */ 1); + print_vector(&res); + igraph_destroy(&graph); + + /* Singleton graph */ + + printf("Singleton graph:\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ NULL, /* normalized= */ 1); + print_vector(&res); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ &weights, /* normalized= */ 1); + print_vector(&res); + igraph_destroy(&graph); + + /* Empty graph with two vertices */ + + printf("Empty graph with two vertices:\n"); + igraph_empty(&graph, 2, IGRAPH_UNDIRECTED); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ NULL, /* normalized= */ 1); + print_vector(&res); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ &weights, /* normalized= */ 1); + print_vector(&res); + igraph_destroy(&graph); + + igraph_vector_destroy(&weights); + + /* Graph with multiple connected components and isolated vertices */ + + printf("Multiple components, unweighted:\n"); + igraph_small(&graph, 20, IGRAPH_UNDIRECTED, + 1, 2, 2, 3, 1, 3, 4, 5, 5, 6, 6, 7, 5, 8, 8, 9, 6, 10, 10, 11, 7, 11, + 9, 13, 9, 14, 9, 15, 9, 16, 13, 14, 13, 15, 13, 16, 14, 15, 14, 16, + 15, 16, 17, 18, 4, 19, 4, 20, + -1); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ NULL, /* normalized= */ 1); + print_vector(&res); + + printf("Multiple components, constant weight vector:\n"); + igraph_vector_init(&weights, igraph_ecount(&graph)); + igraph_vector_fill(&weights, 1.0 / 7); + igraph_harmonic_centrality(&graph, &res, igraph_vss_all(), IGRAPH_ALL, /* weights= */ &weights, /* normalized= */ 1); + print_vector(&res); + igraph_vector_destroy(&weights); + + igraph_destroy(&graph); + + igraph_vector_destroy(&res); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/harmonic_centrality.out b/tests/unit/harmonic_centrality.out new file mode 100644 index 0000000..b4d218b --- /dev/null +++ b/tests/unit/harmonic_centrality.out @@ -0,0 +1,33 @@ +Unweighted undirected: +( 0.408333 0.547222 0.597222 0.611111 0.597222 0.547222 0.408333 ) +Unweighted directed: +( 0.408333 0.380556 0.347222 0.305556 0.25 0.166667 0 ) +Unweighted undirected, cutoff=0: +( 0 0 0 0 0 0 0 ) +Unweighted undirected, cutoff=1: +( 0.166667 0.333333 0.333333 0.333333 0.333333 0.333333 0.166667 ) +Unit-weighted undirected: +( 0.408333 0.547222 0.597222 0.611111 0.597222 0.547222 0.408333 ) +Unit-weighted directed: +( 0.408333 0.380556 0.347222 0.305556 0.25 0.166667 0 ) +Unit-weighted undirected, cutoff=0: +( 0 0 0 0 0 0 0 ) +Unit-weighted undirected, cutoff=1: +( 0.166667 0.333333 0.333333 0.333333 0.333333 0.333333 0.166667 ) +Weighted undirected: +( 0.285714 0.32209 0.241402 0.187963 0.149146 0.116534 0.0795695 ) +Weighted directed: +( 0.285714 0.155423 0.102513 0.0712963 0.0484848 0.0277778 0 ) +Null graph: +( ) +( ) +Singleton graph: +( 0 ) +( 0 ) +Empty graph with two vertices: +( 0 0 ) +( 0 0 ) +Multiple components, unweighted: +( 0 0.1 0.1 0.1 0.3125 0.358333 0.325 0.260833 0.329167 0.368333 0.260833 0.23 0 0.315 0.315 0.315 0.315 0.05 0.05 0.220833 0.220833 ) +Multiple components, constant weight vector: +( 0 0.7 0.7 0.7 2.1875 2.50833 2.275 1.82583 2.30417 2.57833 1.82583 1.61 0 2.205 2.205 2.205 2.205 0.35 0.35 1.54583 1.54583 ) diff --git a/tests/unit/heap.c b/tests/unit/heap.c new file mode 100644 index 0000000..c8542c7 --- /dev/null +++ b/tests/unit/heap.c @@ -0,0 +1,145 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_heap_t h_max; + igraph_heap_min_t h_min; + igraph_integer_t i; + igraph_real_t list[] = {-2, -9.999, 0, 6, 235, -2, -1000, -1, 4, 2000, 6, 0.5, 1, -9, 10}; + const igraph_integer_t l_size = sizeof(list) / sizeof(list[0]); + + /* max heap init & destroy*/ + printf("Create empty max heap & destroy\n"); + igraph_heap_init(&h_max, 0); + IGRAPH_ASSERT(igraph_heap_empty(&h_max)); + igraph_heap_destroy(&h_max); + printf("Create empty max heap but allocate size for some elements\n"); + igraph_heap_init(&h_max, 10); + IGRAPH_ASSERT(igraph_heap_empty(&h_max)); + igraph_heap_destroy(&h_max); + + /* min heap init & destroy*/ + printf("Create empty min heap & destroy\n"); + igraph_heap_min_init(&h_min, 0); + IGRAPH_ASSERT(igraph_heap_min_empty(&h_min)); + igraph_heap_min_destroy(&h_min); + printf("Create empty min heap but allocate size for some elements\n"); + igraph_heap_min_init(&h_min, 10); + IGRAPH_ASSERT(igraph_heap_min_empty(&h_min)); + igraph_heap_min_destroy(&h_min); + + /* max heap_reserve, heap_size and heap_empty*/ + printf("Test max heap_reserve, heap_size and heap_empty\n"); + igraph_heap_init(&h_max, 5); + IGRAPH_ASSERT(igraph_heap_empty(&h_max)); + IGRAPH_ASSERT(igraph_heap_size(&h_max) == 0); + igraph_heap_reserve(&h_max, 10); + IGRAPH_ASSERT(igraph_heap_empty(&h_max)); + IGRAPH_ASSERT(igraph_heap_size(&h_max) == 0); + for (i=0; i < 15; i++){ + igraph_heap_push(&h_max,i); + } + IGRAPH_ASSERT(igraph_heap_size(&h_max) == 15); + IGRAPH_ASSERT(!igraph_heap_empty(&h_max)); + igraph_heap_reserve(&h_max, 5); + IGRAPH_ASSERT(igraph_heap_size(&h_max) == 15); + IGRAPH_ASSERT(!igraph_heap_empty(&h_max)); + igraph_heap_destroy(&h_max); + + /* min heap reserve, heap_size and heap_empty*/ + printf("Test min heap_reserve, heap_size and heap_empty\n"); + igraph_heap_min_init(&h_min, 5); + IGRAPH_ASSERT(igraph_heap_min_empty(&h_min)); + IGRAPH_ASSERT(igraph_heap_min_size(&h_min) == 0); + igraph_heap_min_reserve(&h_min, 10); + IGRAPH_ASSERT(igraph_heap_min_empty(&h_min)); + IGRAPH_ASSERT(igraph_heap_min_size(&h_min) == 0); + for (i=0; i < 15; i++){ + igraph_heap_min_push(&h_min, i); + } + IGRAPH_ASSERT(igraph_heap_min_size(&h_min) == 15); + IGRAPH_ASSERT(!igraph_heap_min_empty(&h_min)); + igraph_heap_min_reserve(&h_min, 5); + IGRAPH_ASSERT(igraph_heap_min_size(&h_min) == 15); + IGRAPH_ASSERT(!igraph_heap_min_empty(&h_min)); + igraph_heap_min_destroy(&h_min); + + /* max heap init_array and delete_top */ + printf("Test max heap_init array and delete_top\n"); + igraph_heap_init_array(&h_max,list,l_size); + while (igraph_heap_size(&h_max) > 0){ + printf("%g ", igraph_heap_delete_top(&h_max)); + } + printf("\n"); + igraph_heap_destroy(&h_max); + + /* min heap init_array and delete_top */ + printf("Test min heap init_array and delete_top\n"); + igraph_heap_min_init_array(&h_min, list, l_size); + while (igraph_heap_min_size(&h_min) > 0) { + printf("%g ", igraph_heap_min_delete_top(&h_min)); + } + printf("\n"); + igraph_heap_min_destroy(&h_min); + + /* max heap top and push */ + printf("Test max heap top and push\n"); + igraph_heap_init(&h_max, 0); + for (i=0; i < l_size; i++){ + igraph_heap_push(&h_max, list[i]); + printf("%g ", igraph_heap_top(&h_max)); + } + printf("\n"); + while (igraph_heap_size(&h_max)>0){ + printf("%g ", igraph_heap_delete_top(&h_max)); + } + printf("\n"); + + /* min heap top and push */ + printf("Test min heap top and push\n"); + igraph_heap_min_init(&h_min, 0); + for (i=0; i < l_size; i++){ + igraph_heap_min_push(&h_min, list[i]); + printf("%g ", igraph_heap_min_top(&h_min)); + } + printf("\n"); + while (igraph_heap_min_size(&h_min) > 0){ + printf("%g ", igraph_heap_min_delete_top(&h_min)); + } + printf("\n"); + + igraph_heap_destroy(&h_max); + igraph_heap_min_destroy(&h_min); + + /* Test initializing empty heap from array. */ + igraph_heap_init_array(&h_max, list, 0); + IGRAPH_ASSERT(igraph_heap_empty(&h_max)); + igraph_heap_push(&h_max, 3.0); + IGRAPH_ASSERT(! igraph_heap_empty(&h_max)); + IGRAPH_ASSERT(igraph_heap_top(&h_max) == 3.0); + igraph_heap_destroy(&h_max); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/heap.out b/tests/unit/heap.out new file mode 100644 index 0000000..28bab2f --- /dev/null +++ b/tests/unit/heap.out @@ -0,0 +1,16 @@ +Create empty max heap & destroy +Create empty max heap but allocate size for some elements +Create empty min heap & destroy +Create empty min heap but allocate size for some elements +Test max heap_reserve, heap_size and heap_empty +Test min heap_reserve, heap_size and heap_empty +Test max heap_init array and delete_top +2000 235 10 6 6 4 1 0.5 0 -1 -2 -2 -9 -9.999 -1000 +Test min heap init_array and delete_top +-1000 -9.999 -9 -2 -2 -1 0 0.5 1 4 6 6 10 235 2000 +Test max heap top and push +-2 -2 0 6 235 235 235 235 235 2000 2000 2000 2000 2000 2000 +2000 235 10 6 6 4 1 0.5 0 -1 -2 -2 -9 -9.999 -1000 +Test min heap top and push +-2 -9.999 -9.999 -9.999 -9.999 -9.999 -1000 -1000 -1000 -1000 -1000 -1000 -1000 -1000 -1000 +-1000 -9.999 -9 -2 -2 -1 0 0.5 1 4 6 6 10 235 2000 diff --git a/tests/unit/hub_and_authority.c b/tests/unit/hub_and_authority.c new file mode 100644 index 0000000..21e4c9b --- /dev/null +++ b/tests/unit/hub_and_authority.c @@ -0,0 +1,148 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + + +void print_hub_and_authority(igraph_t *g, igraph_vector_t *weights, igraph_bool_t scale, igraph_bool_t use_options) { + igraph_arpack_options_t options; + igraph_vector_t hub_vector, authority_vector; + igraph_real_t value; + + igraph_arpack_options_init(&options); + igraph_vector_init(&hub_vector, 0); + igraph_vector_init(&authority_vector, 0); + + printf("--------------------------------------------------\n"); + + igraph_hub_and_authority_scores(g, &hub_vector, &authority_vector, &value, + scale, weights, use_options ? &options : NULL); + + vector_chop(&hub_vector, 10e-10); + vector_chop(&authority_vector, 10e-10); + printf("hub:\n"); + print_vector(&hub_vector); + printf("authority:\n"); + print_vector(&authority_vector); + printf("value:\n"); + print_real(stdout, value, "%g"); + printf("\n"); + printf("--------------------------------------------------\n\n\n"); + igraph_vector_destroy(&hub_vector); + igraph_vector_destroy(&authority_vector); +} + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_arpack_options_t options; + igraph_real_t value; + + igraph_arpack_options_init(&options); + + printf("Null graph:\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, -1); + print_hub_and_authority(&g, NULL, false, true); + igraph_destroy(&g); + + printf("Singleton graph with loop:\n"); + igraph_small(&g, 1, IGRAPH_DIRECTED, 0,0, -1); + print_hub_and_authority(&g, NULL, false, true); + igraph_destroy(&g); + + printf("Singleton graph with three loops:\n"); + igraph_small(&g, 1, IGRAPH_DIRECTED, 0,0, 0,0, 0,0, -1); + print_hub_and_authority(&g, NULL, false, true); + igraph_destroy(&g); + + printf("Three vertices, no links:\n"); + igraph_small(&g, 3, IGRAPH_DIRECTED, -1); + print_hub_and_authority(&g, NULL, false, true); + igraph_destroy(&g); + + printf("Two hubs and one authority:\n"); + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0,2, 1,2, -1); + igraph_vector_init_int(&weights, 2, + 1, 1); + print_hub_and_authority(&g, &weights, false, true); + igraph_destroy(&g); + igraph_vector_destroy(&weights); + + /* https://nlp.stanford.edu/IR-book/html/htmledition/hubs-and-authorities-1.html */ + /* with different normalization */ + printf("Stanford example:\n"); + igraph_small(&g, 7, IGRAPH_DIRECTED, + 0,2, 1,1, 1,2, 2,0, 2,2, 2,3, 3,3, 3,4, 4,6, 5,5, + 5,6, 6,3, 6,4, 6,6, -1); + igraph_vector_init_int(&weights, 14, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 2, 1, 1); + print_hub_and_authority(&g, &weights, false, false); + igraph_destroy(&g); + igraph_vector_destroy(&weights); + + printf("Same example with scaling:\n"); + igraph_small(&g, 7, IGRAPH_DIRECTED, + 0,2, 1,1, 1,2, 2,0, 2,2, 2,3, 3,3, 3,4, 4,6, 5,5, + 5,6, 6,3, 6,4, 6,6, -1); + igraph_vector_init_int(&weights, 14, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 2, 1, 1); + print_hub_and_authority(&g, &weights, true, false); + igraph_destroy(&g); + igraph_vector_destroy(&weights); + + /* Verify that self-loops are counted twice in undirected graphs. */ + printf("Undirected graph with self-loops and multi-edges:\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 2, 2, 3, 2, 3, + -1); + print_hub_and_authority(&g, NULL, true, false); + igraph_destroy(&g); + + printf("Degenerate example:\n"); + igraph_small(&g, 4, IGRAPH_DIRECTED, + 0,1, 1,0, 1,2, 2,1, 2,3, 3,0, -1); + igraph_hub_and_authority_scores(&g, NULL, NULL, &value, + 0, NULL, &options); + printf("--------------------------------------------------\n"); + printf("value:\n"); + print_real(stdout, value, "%g"); + printf("\n"); + printf("--------------------------------------------------\n\n\n"); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Checking invalid weight vector.\n"); + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0,2, 1,2, -1); + igraph_vector_init_int(&weights, 3, + 1, 1, 1); + IGRAPH_ASSERT(igraph_hub_and_authority_scores(&g, NULL, NULL, NULL, + 0, &weights, &options) == IGRAPH_EINVAL); + igraph_destroy(&g); + igraph_vector_destroy(&weights); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/hub_and_authority.out b/tests/unit/hub_and_authority.out new file mode 100644 index 0000000..ec671a9 --- /dev/null +++ b/tests/unit/hub_and_authority.out @@ -0,0 +1,96 @@ +Null graph: +-------------------------------------------------- +hub: +( ) +authority: +( ) +value: +NaN +-------------------------------------------------- + + +Singleton graph with loop: +-------------------------------------------------- +hub: +( 1 ) +authority: +( 1 ) +value: +1 +-------------------------------------------------- + + +Singleton graph with three loops: +-------------------------------------------------- +hub: +( 1 ) +authority: +( 1 ) +value: +9 +-------------------------------------------------- + + +Three vertices, no links: +-------------------------------------------------- +hub: +( 1 1 1 ) +authority: +( 1 1 1 ) +value: +NaN +-------------------------------------------------- + + +Two hubs and one authority: +-------------------------------------------------- +hub: +( 0.707107 0.707107 0 ) +authority: +( 0 0 1 ) +value: +2 +-------------------------------------------------- + + +Stanford example: +-------------------------------------------------- +hub: +( 0.06742 0.0738169 0.63676 0.345405 0.0713449 0.0781142 0.673829 ) +authority: +( 0.187448 0.0217301 0.229025 0.873297 0.30004 0.0229951 0.242358 ) +value: +11.5396 +-------------------------------------------------- + + +Same example with scaling: +-------------------------------------------------- +hub: +( 0.100055 0.109548 0.944987 0.5126 0.10588 0.115926 1 ) +authority: +( 0.214644 0.0248828 0.262253 1 0.343572 0.0263314 0.277521 ) +value: +11.5396 +-------------------------------------------------- + + +Undirected graph with self-loops and multi-edges: +-------------------------------------------------- +hub: +( 0.0906902 0.314507 1 0.576713 ) +authority: +( 0.0906902 0.314507 1 0.576713 ) +value: +12.0266 +-------------------------------------------------- + + +Degenerate example: +-------------------------------------------------- +value: +2.61803 +-------------------------------------------------- + + +Checking invalid weight vector. diff --git a/tests/unit/igraph_add_edges.c b/tests/unit/igraph_add_edges.c new file mode 100644 index 0000000..e8abce3 --- /dev/null +++ b/tests/unit/igraph_add_edges.c @@ -0,0 +1,74 @@ +/* -*- 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 +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_vector_int_t v; + + /* Create graph */ + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 2, 2, + -1); + + /* Add edges */ + igraph_vector_int_init_int(&v, 4, 2, 1, 3, 3); + igraph_add_edges(&g, &v, 0); + + /* Check result */ + igraph_get_edgelist(&g, &v, 0); + print_vector_int(&v); + + /* Error, vector length */ + igraph_vector_int_resize(&v, 3); + VECTOR(v)[0] = 0; + VECTOR(v)[1] = 1; + VECTOR(v)[2] = 2; + CHECK_ERROR(igraph_add_edges(&g, &v, 0), IGRAPH_EINVEVECTOR); + + /* Check result */ + igraph_get_edgelist(&g, &v, 0); + print_vector_int(&v); + + /* Error, vector IDs */ + igraph_vector_int_resize(&v, 4); + VECTOR(v)[0] = 0; + VECTOR(v)[1] = 1; + VECTOR(v)[2] = 2; + VECTOR(v)[3] = 4; + CHECK_ERROR(igraph_add_edges(&g, &v, 0), IGRAPH_EINVVID); + + /* Check result */ + igraph_get_edgelist(&g, &v, 0); + print_vector_int(&v); + + igraph_vector_int_destroy(&v); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_add_edges.out b/tests/unit/igraph_add_edges.out new file mode 100644 index 0000000..7db1ba9 --- /dev/null +++ b/tests/unit/igraph_add_edges.out @@ -0,0 +1,3 @@ +( 0 1 1 2 2 3 2 2 2 1 3 3 ) +( 0 1 1 2 2 3 2 2 2 1 3 3 ) +( 0 1 1 2 2 3 2 2 2 1 3 3 ) diff --git a/tests/unit/igraph_add_vertices.c b/tests/unit/igraph_add_vertices.c new file mode 100644 index 0000000..54e9650 --- /dev/null +++ b/tests/unit/igraph_add_vertices.c @@ -0,0 +1,61 @@ +/* -*- 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 +#include "test_utilities.h" + +int main(void) { + + igraph_t g1; + igraph_vector_int_t v1; + + /* Create a graph */ + igraph_vector_int_init(&v1, 8); + VECTOR(v1)[0] = 0; + VECTOR(v1)[1] = 1; + VECTOR(v1)[2] = 1; + VECTOR(v1)[3] = 2; + VECTOR(v1)[4] = 2; + VECTOR(v1)[5] = 3; + VECTOR(v1)[6] = 2; + VECTOR(v1)[7] = 2; + igraph_create(&g1, &v1, 0, 0); + igraph_vector_int_destroy(&v1); + + /* Add more vertices */ + igraph_add_vertices(&g1, 10, 0); + IGRAPH_ASSERT(igraph_vcount(&g1) == 14); + + /* Add more vertices */ + igraph_add_vertices(&g1, 0, 0); + IGRAPH_ASSERT(igraph_vcount(&g1) == 14); + + /* Error */ + CHECK_ERROR(igraph_add_vertices(&g1, -1, 0), IGRAPH_EINVAL); + + igraph_destroy(&g1); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_adhesion.c b/tests/unit/igraph_adhesion.c new file mode 100644 index 0000000..8da15c3 --- /dev/null +++ b/tests/unit/igraph_adhesion.c @@ -0,0 +1,49 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_integer_t value; + igraph_bool_t checks = 1; + + igraph_small(&g, 7, IGRAPH_DIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 3, 4, 3, 5, 4, 5, 1, 6, 6, 3, -1); + + igraph_adhesion(&g, &value, checks); + + IGRAPH_ASSERT(value == 0); + + igraph_destroy(&g); + + igraph_small(&g, 7, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 3, 4, 3, 5, 4, 5, 1, 6, 6, 3, -1); + + igraph_adhesion(&g, &value, checks); + + IGRAPH_ASSERT(value == 2); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_adjacency.c b/tests/unit/igraph_adjacency.c new file mode 100644 index 0000000..49e9c3a --- /dev/null +++ b/tests/unit/igraph_adjacency.c @@ -0,0 +1,257 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +void print_destroy(igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, igraph_loops_t loops) { + igraph_t g; + igraph_t g_sparse; + igraph_sparsemat_t sparse_adjmatrix, sparse_adjmatrix_comp; + igraph_bool_t same; + + igraph_adjacency(&g, adjmatrix, mode, loops); + + igraph_matrix_as_sparsemat(&sparse_adjmatrix, adjmatrix, 0.0001); + igraph_sparsemat_compress(&sparse_adjmatrix, &sparse_adjmatrix_comp); + igraph_sparse_adjacency(&g_sparse, &sparse_adjmatrix_comp, mode, loops); + print_graph_canon(&g); + + igraph_is_same_graph(&g, &g_sparse, &same); + if (!same) { + printf("Sparse graph differs from non-sparse:\n"); + print_graph_canon(&g_sparse); + exit(1); + } + + igraph_matrix_destroy(adjmatrix); + igraph_sparsemat_destroy(&sparse_adjmatrix); + igraph_sparsemat_destroy(&sparse_adjmatrix_comp); + igraph_destroy(&g); + igraph_destroy(&g_sparse); +} + +void check_error(igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, igraph_loops_t loops, igraph_error_t error) { + igraph_t g; + igraph_sparsemat_t sparse_adjmatrix, sparse_adjmatrix_comp; + + igraph_matrix_as_sparsemat(&sparse_adjmatrix, adjmatrix, 0.0001); + igraph_sparsemat_compress(&sparse_adjmatrix, &sparse_adjmatrix_comp); + + CHECK_ERROR(igraph_adjacency(&g, adjmatrix, mode, loops), error); + CHECK_ERROR(igraph_sparse_adjacency(&g, &sparse_adjmatrix_comp, mode, loops), error); + + igraph_sparsemat_destroy(&sparse_adjmatrix); + igraph_sparsemat_destroy(&sparse_adjmatrix_comp); +} + +int main(void) { + igraph_matrix_t adjmatrix; + + printf("\n0x0 matrix:\n"); + matrix_init_int_row_major(&adjmatrix, 0, 0, NULL); + print_destroy(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_LOOPS_ONCE); + + printf("\n1x1 matrix, no loops:\n"); + { + int e[] = {1}; + matrix_init_int_row_major(&adjmatrix, 1, 1, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_NO_LOOPS); + } + printf("\n1x1 matrix, loops once:\n"); + { + int e[] = {1}; + matrix_init_int_row_major(&adjmatrix, 1, 1, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_LOOPS_ONCE); + } + printf("\n1x1 matrix, loops twice:\n"); + { + int e[] = {2}; + matrix_init_int_row_major(&adjmatrix, 1, 1, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_LOOPS_TWICE); + } + + printf("\n3x3 matrix, IGRAPH_ADJ_DIRECTED, no loops:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_NO_LOOPS); + } + printf("\n3x3 matrix, IGRAPH_ADJ_DIRECTED, loops once:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_LOOPS_ONCE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_DIRECTED, loops twice:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_LOOPS_TWICE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_UNDIRECTED, no loops:\n"); + { + int e[] = {4, 2, 0, 2, 0, 4, 0, 4, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_UNDIRECTED, IGRAPH_NO_LOOPS); + } + printf("\n3x3 matrix, IGRAPH_ADJ_UNDIRECTED, loops once:\n"); + { + int e[] = {4, 2, 0, 2, 0, 4, 0, 4, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_UNDIRECTED, IGRAPH_LOOPS_ONCE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_UNDIRECTED, loops twice:\n"); + { + int e[] = {4, 2, 0, 2, 0, 4, 0, 4, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_UNDIRECTED, IGRAPH_LOOPS_TWICE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_MAX, no loops:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_MAX, IGRAPH_NO_LOOPS); + } + printf("\n3x3 matrix, IGRAPH_ADJ_MAX, loops once:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_MAX, IGRAPH_LOOPS_ONCE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_MAX, loops twice:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_MAX, IGRAPH_LOOPS_TWICE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_MIN, no loops:\n"); + { + int e[] = {4, 2, 0, 3, 0, 5, 0, 4, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_MIN, IGRAPH_NO_LOOPS); + } + printf("\n3x3 matrix, IGRAPH_ADJ_MIN, loops once:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_MIN, IGRAPH_LOOPS_ONCE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_MIN, loops twice:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_MIN, IGRAPH_LOOPS_TWICE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_PLUS, no loops:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_PLUS, IGRAPH_NO_LOOPS); + } + printf("\n3x3 matrix, IGRAPH_ADJ_PLUS, loops once:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_PLUS, IGRAPH_LOOPS_ONCE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_PLUS, loops twice:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_PLUS, IGRAPH_LOOPS_TWICE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_UPPER, no loops:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_UPPER, IGRAPH_NO_LOOPS); + } + printf("\n3x3 matrix, IGRAPH_ADJ_UPPER, loops once:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_UPPER, IGRAPH_LOOPS_ONCE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_UPPER, loops twice:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_UPPER, IGRAPH_LOOPS_TWICE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_LOWER, no loops:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_LOWER, IGRAPH_NO_LOOPS); + } + printf("\n3x3 matrix, IGRAPH_ADJ_LOWER, loops once:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_LOWER, IGRAPH_LOOPS_ONCE); + } + printf("\n3x3 matrix, IGRAPH_ADJ_LOWER, loops twice:\n"); + { + int e[] = {4, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + print_destroy(&adjmatrix, IGRAPH_ADJ_LOWER, IGRAPH_LOOPS_TWICE); + } + + VERIFY_FINALLY_STACK(); + + printf("\nCheck handling of non-square matrix error.\n"); + { + int e[] = {1, 2, 0}; + matrix_init_int_row_major(&adjmatrix, 3, 1, e); + check_error(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_NO_LOOPS, IGRAPH_NONSQUARE); + igraph_matrix_destroy(&adjmatrix); + } + printf("\nCheck handling of negative number of edges error.\n"); + { + int e[] = {1, 2, 0, -3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + check_error(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_NO_LOOPS, IGRAPH_EINVAL); + igraph_matrix_destroy(&adjmatrix); + } + printf("\nCheck handling of odd number in diagonal.\n"); + { + int e[] = {1, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + check_error(&adjmatrix, IGRAPH_ADJ_DIRECTED, IGRAPH_LOOPS_TWICE, IGRAPH_EINVAL); + igraph_matrix_destroy(&adjmatrix); + } + printf("\nCheck handling of invalid adjacency mode.\n"); + { + int e[] = {0, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + check_error(&adjmatrix, (igraph_adjacency_t) 42, IGRAPH_LOOPS_TWICE, IGRAPH_EINVAL); + igraph_matrix_destroy(&adjmatrix); + } + printf("\nCheck handling of non-symmetric matrix for IGRAPH_ADJ_UNDIRECTED.\n"); + { + int e[] = {0, 2, 0, 3, 0, 4, 0, 5, 6}; + matrix_init_int_row_major(&adjmatrix, 3, 3, e); + check_error(&adjmatrix, IGRAPH_ADJ_UNDIRECTED, IGRAPH_LOOPS_ONCE, IGRAPH_EINVAL); + igraph_matrix_destroy(&adjmatrix); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_adjacency.out b/tests/unit/igraph_adjacency.out new file mode 100644 index 0000000..45d6dad --- /dev/null +++ b/tests/unit/igraph_adjacency.out @@ -0,0 +1,453 @@ + +0x0 matrix: +directed: true +vcount: 0 +edges: { +} + +1x1 matrix, no loops: +directed: true +vcount: 1 +edges: { +} + +1x1 matrix, loops once: +directed: true +vcount: 1 +edges: { +0 0 +} + +1x1 matrix, loops twice: +directed: true +vcount: 1 +edges: { +0 0 +} + +3x3 matrix, IGRAPH_ADJ_DIRECTED, no loops: +directed: true +vcount: 3 +edges: { +0 1 +0 1 +1 0 +1 0 +1 0 +1 2 +1 2 +1 2 +1 2 +2 1 +2 1 +2 1 +2 1 +2 1 +} + +3x3 matrix, IGRAPH_ADJ_DIRECTED, loops once: +directed: true +vcount: 3 +edges: { +0 0 +0 0 +0 0 +0 0 +0 1 +0 1 +1 0 +1 0 +1 0 +1 2 +1 2 +1 2 +1 2 +2 1 +2 1 +2 1 +2 1 +2 1 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_DIRECTED, loops twice: +directed: true +vcount: 3 +edges: { +0 0 +0 0 +0 1 +0 1 +1 0 +1 0 +1 0 +1 2 +1 2 +1 2 +1 2 +2 1 +2 1 +2 1 +2 1 +2 1 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_UNDIRECTED, no loops: +directed: false +vcount: 3 +edges: { +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +} + +3x3 matrix, IGRAPH_ADJ_UNDIRECTED, loops once: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 0 +0 0 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_UNDIRECTED, loops twice: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_MAX, no loops: +directed: false +vcount: 3 +edges: { +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +} + +3x3 matrix, IGRAPH_ADJ_MAX, loops once: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 0 +0 0 +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_MAX, loops twice: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_MIN, no loops: +directed: false +vcount: 3 +edges: { +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +} + +3x3 matrix, IGRAPH_ADJ_MIN, loops once: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 0 +0 0 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_MIN, loops twice: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_PLUS, no loops: +directed: false +vcount: 3 +edges: { +0 1 +0 1 +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +} + +3x3 matrix, IGRAPH_ADJ_PLUS, loops once: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 0 +0 0 +0 1 +0 1 +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_PLUS, loops twice: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 1 +0 1 +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_UPPER, no loops: +directed: false +vcount: 3 +edges: { +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +} + +3x3 matrix, IGRAPH_ADJ_UPPER, loops once: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 0 +0 0 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_UPPER, loops twice: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_LOWER, no loops: +directed: false +vcount: 3 +edges: { +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +} + +3x3 matrix, IGRAPH_ADJ_LOWER, loops once: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 0 +0 0 +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +} + +3x3 matrix, IGRAPH_ADJ_LOWER, loops twice: +directed: false +vcount: 3 +edges: { +0 0 +0 0 +0 1 +0 1 +0 1 +1 2 +1 2 +1 2 +1 2 +1 2 +2 2 +2 2 +2 2 +} + +Check handling of non-square matrix error. + +Check handling of negative number of edges error. + +Check handling of odd number in diagonal. + +Check handling of invalid adjacency mode. + +Check handling of non-symmetric matrix for IGRAPH_ADJ_UNDIRECTED. diff --git a/tests/unit/igraph_adjacency_spectral_embedding.c b/tests/unit/igraph_adjacency_spectral_embedding.c new file mode 100644 index 0000000..2a5e286 --- /dev/null +++ b/tests/unit/igraph_adjacency_spectral_embedding.c @@ -0,0 +1,73 @@ +/* -*- 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 + +#include "test_utilities.h" + +/* + + R + library(igraph) + g <- graph.tree(10, 3, mode="out") + A <- get.adjacency(g) + svd(A + .5 * degree(g) * diag(vcount(g))) + +*/ + +int main(void) { + + igraph_t graph; + igraph_matrix_t U, V; + igraph_vector_t cvec; + + igraph_kary_tree(&graph, /*n=*/ 14, /*children=*/ 4, IGRAPH_TREE_OUT); + + igraph_matrix_init(&U, 0, 0); + igraph_matrix_init(&V, 0, 0); + + igraph_vector_init(&cvec, 0); + igraph_strength(&graph, &cvec, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, 0); + igraph_vector_scale(&cvec, .5); + + igraph_adjacency_spectral_embedding(&graph, 4, /*weights=*/ 0, + IGRAPH_EIGEN_LA, + /*scaled=*/ 0, &U, &V, /*D=*/ 0, + &cvec, /*options=*/ 0); + + /* eigenvectors are in the columns of U and V; make sure that the + * first row contains positive values */ + print_matrix_first_row_positive(&U, "%8.4f"); + printf("--\n"); + print_matrix_first_row_positive(&V, "%8.4f"); + + igraph_vector_destroy(&cvec); + igraph_matrix_destroy(&V); + igraph_matrix_destroy(&U); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_adjacency_spectral_embedding.out b/tests/unit/igraph_adjacency_spectral_embedding.out new file mode 100644 index 0000000..6248d05 --- /dev/null +++ b/tests/unit/igraph_adjacency_spectral_embedding.out @@ -0,0 +1,29 @@ + 0.5897 0.0000 0.7766 0.1946 + 0.5678 0.7037 -0.4090 -0.0547 + 0.5678 -0.7037 -0.4090 -0.0547 + 0.0541 0.0000 0.2133 -0.9350 + 0.0233 0.0000 0.0714 0.0576 + 0.0224 0.0348 -0.0376 -0.0162 + 0.0224 0.0348 -0.0376 -0.0162 + 0.0224 0.0348 -0.0376 -0.0162 + 0.0224 0.0348 -0.0376 -0.0162 + 0.0224 -0.0348 -0.0376 -0.0162 + 0.0224 -0.0348 -0.0376 -0.0162 + 0.0224 -0.0348 -0.0376 -0.0162 + 0.0224 -0.0348 -0.0376 -0.0162 + 0.0021 0.0000 0.0196 -0.2767 +-- + 0.3281 0.0000 0.6513 0.2795 + 0.5588 0.5468 -0.1031 0.0416 + 0.5588 -0.5468 -0.1031 0.0416 + 0.1791 0.0000 0.4151 -0.5316 + 0.1673 0.0000 0.3406 0.1604 + 0.1610 0.2241 -0.1794 -0.0451 + 0.1610 0.2241 -0.1794 -0.0451 + 0.1610 0.2241 -0.1794 -0.0451 + 0.1610 0.2241 -0.1794 -0.0451 + 0.1610 -0.2241 -0.1794 -0.0451 + 0.1610 -0.2241 -0.1794 -0.0451 + 0.1610 -0.2241 -0.1794 -0.0451 + 0.1610 -0.2241 -0.1794 -0.0451 + 0.0153 0.0000 0.0935 -0.7706 diff --git a/tests/unit/igraph_adjacent_triangles.c b/tests/unit/igraph_adjacent_triangles.c new file mode 100644 index 0000000..c72762c --- /dev/null +++ b/tests/unit/igraph_adjacent_triangles.c @@ -0,0 +1,75 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_t res; + igraph_vs_t vertices; + + igraph_vector_init(&res, 0); + + igraph_small(&g, 20, IGRAPH_DIRECTED, + 15, 12, 12, 10, 15, 0, 11, 10, 2, 8, 8, 6, 13, 17, 10, 10, 17, 2, 14, + 0, 16, 13, 14, 14, 0, 5, 6, 4, 0, 9, 0, 6, 10, 9, 16, 4, 14, 5, 17, + 15, 14, 9, 17, 17, 1, 4, 10, 16, 7, 0, 11, 12, 6, 13, 2, 17, 4, 0, 0, + 14, 4, 0, 6, 16, 16, 14, 13, 13, 12, 11, 3, 11, 11, 3, 6, 7, 4, 14, + 10, 8, 13, 7, 14, 2, 5, 2, 0, 14, 3, 15, 5, 5, 7, 2, 14, 15, 5, 10, + 10, 16, 7, 9, 14, 0, 15, 7, 13, 1, 15, 1, 4, 5, 4, 6, 16, 13, 6, 17, + 8, 6, 9, 3, 8, 6, 6, 14, 11, 14, 6, 10, 10, 5, 1, 0, 16, 17, 9, 1, 5, + 0, 5, 15, 8, 0, 0, 8, 5, 3, 9, 4, 13, 12, 11, 0, 11, 0, 10, 6, 4, 13, + 8, 9, 11, 11, 3, 16, 1, 2, 16, 0, 9, 8, 3, 8, 8, 7, 12, 10, 9, 3, 13, + 5, 3, 9, 6, 2, 11, 10, 1, 16, 0, 2, 10, 17, 16, 8, 11, 5, 13, 0, 19, 19, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1); + + igraph_vs_range(&vertices, 0, igraph_vcount(&g)); + + printf("\nDirected multi:\n"); + igraph_adjacent_triangles(&g, &res, igraph_vss_all()); + print_vector(&res); + igraph_adjacent_triangles(&g, &res, vertices); + print_vector(&res); + + printf("\nUndirected multi:\n"); + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_COLLAPSE, NULL); + igraph_adjacent_triangles(&g, &res, igraph_vss_all()); + print_vector(&res); + igraph_adjacent_triangles(&g, &res, vertices); + print_vector(&res); + + printf("\nSimple:\n"); + igraph_simplify(&g, true, true, NULL); + igraph_adjacent_triangles(&g, &res, igraph_vss_all()); + print_vector(&res); + igraph_adjacent_triangles(&g, &res, vertices); + print_vector(&res); + + igraph_vs_destroy(&vertices); + + igraph_destroy(&g); + + igraph_vector_destroy(&res); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_adjacent_triangles.out b/tests/unit/igraph_adjacent_triangles.out new file mode 100644 index 0000000..36b45f1 --- /dev/null +++ b/tests/unit/igraph_adjacent_triangles.out @@ -0,0 +1,12 @@ + +Directed multi: +( 37 10 12 4 18 14 24 11 15 10 8 6 1 15 17 6 20 6 0 0 ) +( 37 10 12 4 18 14 24 11 15 10 8 6 1 15 17 6 20 6 0 0 ) + +Undirected multi: +( 37 10 12 4 18 14 24 11 15 10 8 6 1 15 17 6 20 6 0 0 ) +( 37 10 12 4 18 14 24 11 15 10 8 6 1 15 17 6 20 6 0 0 ) + +Simple: +( 37 10 12 4 18 14 24 11 15 10 8 6 1 15 17 6 20 6 0 0 ) +( 37 10 12 4 18 14 24 11 15 10 8 6 1 15 17 6 20 6 0 0 ) diff --git a/tests/unit/igraph_adjlist_init_complementer.c b/tests/unit/igraph_adjlist_init_complementer.c new file mode 100644 index 0000000..8412fd2 --- /dev/null +++ b/tests/unit/igraph_adjlist_init_complementer.c @@ -0,0 +1,45 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_adjlist_t adjlist; + + printf("Graph with loops and mutltiple edges, IGRAPH_IN, loops = 0:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + igraph_adjlist_init_complementer(&g, &adjlist, IGRAPH_IN, 0); + igraph_adjlist_print(&adjlist); + igraph_adjlist_destroy(&adjlist); + + printf("Same graph, loops = 1:\n"); + igraph_adjlist_init_complementer(&g, &adjlist, IGRAPH_IN, 1); + igraph_adjlist_print(&adjlist); + igraph_adjlist_destroy(&adjlist); + + printf("Same graph, IGRAPH_OUT:\n"); + igraph_adjlist_init_complementer(&g, &adjlist, IGRAPH_OUT, 0); + igraph_adjlist_fprint(&adjlist, stdout); /* to check fprint too */ + igraph_adjlist_destroy(&adjlist); + + igraph_destroy(&g); + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_adjlist_init_complementer.out b/tests/unit/igraph_adjlist_init_complementer.out new file mode 100644 index 0000000..fc413cb --- /dev/null +++ b/tests/unit/igraph_adjlist_init_complementer.out @@ -0,0 +1,21 @@ +Graph with loops and mutltiple edges, IGRAPH_IN, loops = 0: +1 3 4 5 +2 3 4 5 +1 3 4 5 +0 4 5 +0 1 2 5 +0 1 2 3 4 +Same graph, loops = 1: +0 1 3 4 5 +2 3 4 5 +1 2 3 4 5 +0 3 4 5 +0 1 2 4 5 +0 1 2 3 4 5 +Same graph, IGRAPH_OUT: +3 4 5 +0 2 4 5 +1 4 5 +0 1 2 5 +0 1 2 3 5 +0 1 2 3 4 diff --git a/tests/unit/igraph_adjlist_simplify.c b/tests/unit/igraph_adjlist_simplify.c new file mode 100644 index 0000000..b64bba4 --- /dev/null +++ b/tests/unit/igraph_adjlist_simplify.c @@ -0,0 +1,47 @@ +/* + 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 . + */ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g) { + igraph_adjlist_t al; + igraph_adjlist_init(g, &al, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + printf("Before:\n"); + igraph_adjlist_print(&al); + igraph_adjlist_simplify(&al); + printf("After:\n"); + igraph_adjlist_print(&al); + igraph_destroy(g); + igraph_adjlist_destroy(&al); +} + +int main(void) { + igraph_t g; + + printf("Graph with no vertices:\n"); + igraph_small(&g, 0, 1, -1); + print_and_destroy(&g); + + printf("\nGraph with loops and multiple edges:\n"); + igraph_small(&g, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_adjlist_simplify.out b/tests/unit/igraph_adjlist_simplify.out new file mode 100644 index 0000000..5148a2a --- /dev/null +++ b/tests/unit/igraph_adjlist_simplify.out @@ -0,0 +1,19 @@ +Graph with no vertices: +Before: +After: + +Graph with loops and multiple edges: +Before: +1 2 2 +0 1 1 3 +0 0 3 +1 2 4 4 +3 3 + +After: +1 2 +0 3 +0 3 +1 2 4 +3 + diff --git a/tests/unit/igraph_all_st_cuts.c b/tests/unit/igraph_all_st_cuts.c new file mode 100644 index 0000000..889a365 --- /dev/null +++ b/tests/unit/igraph_all_st_cuts.c @@ -0,0 +1,404 @@ +/* -*- 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 + +#include "core/marked_queue.h" +#include "core/estack.h" + +#include "flow/flow_internal.h" + +#include "test_utilities.h" + +int test_all_st_cuts(const igraph_t *graph, + igraph_integer_t source, + igraph_integer_t target) { + igraph_vector_int_list_t cuts, partition1s; + igraph_integer_t n, i; + + igraph_vector_int_list_init(&cuts, 0); + igraph_vector_int_list_init(&partition1s, 0); + igraph_all_st_cuts(graph, &cuts, &partition1s, + source, target); + + n = igraph_vector_int_list_size(&partition1s); + printf("Partitions and cuts:\n"); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&partition1s, i); + igraph_vector_int_t *v2 = igraph_vector_int_list_get_ptr(&cuts, i); + printf("P: "); + igraph_vector_int_print(v); + printf("C: "); + igraph_vector_int_print(v2); + } + igraph_vector_int_list_destroy(&partition1s); + igraph_vector_int_list_destroy(&cuts); + + return 0; +} + +int main(void) { + igraph_t g; + igraph_vector_int_list_t cuts, partition1s; + igraph_integer_t i, n; + + igraph_marked_queue_int_t S; + igraph_estack_t T; + igraph_integer_t v; + igraph_vector_int_t Isv; + + /* ----------------------------------------------------------- */ + /* This is the example from the Provan-Shier paper, + for calculating the dominator tree and finding the right pivot + element */ + + igraph_small(&g, 12, IGRAPH_DIRECTED, + /* a->b */ 0, 1, + /* b->t */ 1, 11, + /* c->b */ 2, 1, /* c->d */ 2, 3, + /* d->e */ 3, 4, /* d->i */ 3, 8, + /* e->c */ 4, 2, + /* f->c */ 5, 2, /* f->e */ 5, 4, + /* g->d */ 6, 3, /* g->e */ 6, 4, /* g->f */ 6, 5, + /* g->j */ 6, 9, + /* h->g */ 7, 6, /* h->t */ 7, 11, + /* i->a */ 8, 0, + /* j->i */ 9, 8, + /* s->a */ 10, 0, /* s->c */ 10, 2, /* s->h */ 10, 7, + -1); + + /* S={s,a} */ + igraph_marked_queue_int_init(&S, igraph_vcount(&g)); + igraph_marked_queue_int_start_batch(&S); + igraph_marked_queue_int_push(&S, 10); + igraph_marked_queue_int_push(&S, 0); + + /* T={t} */ + igraph_estack_init(&T, igraph_vcount(&g), 1); + igraph_estack_push(&T, 11); + + igraph_vector_int_init(&Isv, 0); + igraph_i_all_st_cuts_pivot(&g, &S, &T, + /*source=*/ 10, /*target=*/ 11, + &v, &Isv, NULL); + + /* Expected result: v=c, Isv={c,d,e,i} */ + printf("%" IGRAPH_PRId "; ", v); + igraph_vector_int_print(&Isv); + + igraph_vector_int_destroy(&Isv); + igraph_estack_destroy(&T); + igraph_marked_queue_int_destroy(&S); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0, 1, 1, 2, + -1); + + /* S={}, T={} */ + igraph_marked_queue_int_init(&S, igraph_vcount(&g)); + igraph_estack_init(&T, igraph_vcount(&g), 3); + + igraph_vector_int_init(&Isv, 0); + igraph_i_all_st_cuts_pivot(&g, &S, &T, + /*source=*/ 0, /*target=*/ 2, + &v, &Isv, NULL); + printf("%" IGRAPH_PRId "; ", v); + igraph_vector_int_print(&Isv); + + igraph_vector_int_destroy(&Isv); + igraph_estack_destroy(&T); + igraph_marked_queue_int_destroy(&S); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0, 1, 1, 2, + -1); + + /* S={}, T={0} */ + igraph_marked_queue_int_init(&S, igraph_vcount(&g)); + + igraph_estack_init(&T, igraph_vcount(&g), 3); + igraph_estack_push(&T, 0); + + igraph_vector_int_init(&Isv, 0); + igraph_i_all_st_cuts_pivot(&g, &S, &T, + /*source=*/ 0, /*target=*/ 2, + &v, &Isv, NULL); + printf("%" IGRAPH_PRId "; ", v); + igraph_vector_int_print(&Isv); + + igraph_vector_int_destroy(&Isv); + igraph_estack_destroy(&T); + igraph_marked_queue_int_destroy(&S); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0, 1, 1, 2, + -1); + + /* S={0}, T={} */ + igraph_marked_queue_int_init(&S, igraph_vcount(&g)); + igraph_marked_queue_int_push(&S, 0); + + igraph_estack_init(&T, igraph_vcount(&g), 3); + + igraph_vector_int_init(&Isv, 0); + igraph_i_all_st_cuts_pivot(&g, &S, &T, + /*source=*/ 0, /*target=*/ 2, + &v, &Isv, NULL); + printf("%" IGRAPH_PRId "; ", v); + igraph_vector_int_print(&Isv); + + igraph_vector_int_destroy(&Isv); + igraph_estack_destroy(&T); + igraph_marked_queue_int_destroy(&S); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0, 1, 1, 2, + -1); + + /* S={0}, T={1} */ + igraph_marked_queue_int_init(&S, igraph_vcount(&g)); + igraph_marked_queue_int_push(&S, 0); + + igraph_estack_init(&T, igraph_vcount(&g), 3); + igraph_estack_push(&T, 1); + + igraph_vector_int_init(&Isv, 0); + igraph_i_all_st_cuts_pivot(&g, &S, &T, + /*source=*/ 0, /*target=*/ 2, + &v, &Isv, NULL); + printf("%" IGRAPH_PRId "; ", v); + igraph_vector_int_print(&Isv); + + igraph_vector_int_destroy(&Isv); + igraph_estack_destroy(&T); + igraph_marked_queue_int_destroy(&S); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0, 1, 1, 2, + -1); + + /* S={0,1}, T={} */ + igraph_marked_queue_int_init(&S, igraph_vcount(&g)); + igraph_marked_queue_int_push(&S, 0); + igraph_marked_queue_int_push(&S, 1); + + igraph_estack_init(&T, igraph_vcount(&g), 3); + + igraph_vector_int_init(&Isv, 0); + igraph_i_all_st_cuts_pivot(&g, &S, &T, + /*source=*/ 0, /*target=*/ 2, + &v, &Isv, NULL); + printf("%" IGRAPH_PRId "; ", v); + igraph_vector_int_print(&Isv); + + igraph_vector_int_destroy(&Isv); + igraph_estack_destroy(&T); + igraph_marked_queue_int_destroy(&S); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0, 1, 1, 2, + -1); + + igraph_vector_int_list_init(&partition1s, 0); + igraph_all_st_cuts(&g, /*cuts=*/ 0, &partition1s, + /*source=*/ 0, /*target=*/ 2); + print_vector_int_list(&partition1s); + igraph_vector_int_list_destroy(&partition1s); + + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 1, 2, 1, 3, 2, 4, 3, 4, + -1); + + igraph_vector_int_list_init(&partition1s, 0); + igraph_all_st_cuts(&g, /*cuts=*/ 0, &partition1s, + /*source=*/ 0, /*target=*/ 4); + print_vector_int_list(&partition1s); + igraph_vector_int_list_destroy(&partition1s); + + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0, 1, 1, 2, 1, 3, 2, 4, 3, 4, 1, 5, 5, 4, + -1); + + igraph_vector_int_list_init(&cuts, 0); + igraph_vector_int_list_init(&partition1s, 0); + igraph_all_st_cuts(&g, &cuts, &partition1s, + /*source=*/ 0, /*target=*/ 4); + + n = igraph_vector_int_list_size(&partition1s); + printf("Partitions and cuts:\n"); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&partition1s, i); + igraph_vector_int_t *v2 = igraph_vector_int_list_get_ptr(&cuts, i); + printf("P: "); + igraph_vector_int_print(v); + printf("C: "); + igraph_vector_int_print(v2); + } + igraph_vector_int_list_destroy(&partition1s); + igraph_vector_int_list_destroy(&cuts); + + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 3, IGRAPH_DIRECTED, + 0, 2, 1, 2, + -1); + + igraph_vector_int_list_init(&cuts, 0); + igraph_vector_int_list_init(&partition1s, 0); + igraph_all_st_cuts(&g, &cuts, &partition1s, + /*source=*/ 1, /*target=*/ 2); + + n = igraph_vector_int_list_size(&partition1s); + printf("Partitions and cuts:\n"); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&partition1s, i); + igraph_vector_int_t *v2 = igraph_vector_int_list_get_ptr(&cuts, i); + printf("P: "); + igraph_vector_int_print(v); + printf("C: "); + igraph_vector_int_print(v2); + } + igraph_vector_int_list_destroy(&partition1s); + igraph_vector_int_list_destroy(&cuts); + + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 3, 1, + -1); + + igraph_vector_int_list_init(&cuts, 0); + igraph_vector_int_list_init(&partition1s, 0); + igraph_all_st_cuts(&g, &cuts, &partition1s, + /*source=*/ 0, /*target=*/ 4); + + n = igraph_vector_int_list_size(&partition1s); + printf("Partitions and cuts:\n"); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&partition1s, i); + igraph_vector_int_t *v2 = igraph_vector_int_list_get_ptr(&cuts, i); + printf("P: "); + igraph_vector_int_print(v); + printf("C: "); + igraph_vector_int_print(v2); + } + igraph_vector_int_list_destroy(&partition1s); + igraph_vector_int_list_destroy(&cuts); + + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 7, IGRAPH_DIRECTED, + 0, 1, 0, 2, 1, 3, 2, 3, + 1, 4, 1, 5, 1, 6, + 4, 2, 5, 2, 6, 2, + -1); + + igraph_vector_int_list_init(&cuts, 0); + igraph_vector_int_list_init(&partition1s, 0); + igraph_all_st_cuts(&g, &cuts, &partition1s, + /*source=*/ 0, /*target=*/ 3); + + n = igraph_vector_int_list_size(&partition1s); + printf("Partitions and cuts:\n"); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&partition1s, i); + igraph_vector_int_t *v2 = igraph_vector_int_list_get_ptr(&cuts, i); + printf("P: "); + igraph_vector_int_print(v); + printf("C: "); + igraph_vector_int_print(v2); + } + igraph_vector_int_list_destroy(&partition1s); + igraph_vector_int_list_destroy(&cuts); + + /* Check whether it also works if we don't provide partition1s */ + igraph_vector_int_list_init(&cuts, 0); + igraph_vector_int_list_init(&partition1s, 0); + igraph_all_st_cuts(&g, &cuts, /*partition1s=*/ 0, + /*source=*/ 0, /*target=*/ 3); + + n = igraph_vector_int_list_size(&cuts); + printf("Cuts only (no partitions):\n"); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v2 = igraph_vector_int_list_get_ptr(&cuts, i); + printf("C: "); + igraph_vector_int_print(v2); + } + igraph_vector_int_list_destroy(&partition1s); + igraph_vector_int_list_destroy(&cuts); + + igraph_destroy(&g); + + /* ----------------------------------------------------------- + * Check problematic cases in issue #1102 + * ----------------------------------------------------------- */ + + igraph_small(&g, 4, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, + -1); + test_all_st_cuts(&g, 0, 2); + igraph_destroy(&g); + + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, + -1); + test_all_st_cuts(&g, 0, 2); + test_all_st_cuts(&g, 1, 3); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_all_st_cuts.out b/tests/unit/igraph_all_st_cuts.out new file mode 100644 index 0000000..ec7ea93 --- /dev/null +++ b/tests/unit/igraph_all_st_cuts.out @@ -0,0 +1,98 @@ +2; 2 3 4 8 +0; 0 +0; +1; 1 +1; +1; +{ + 0: ( 0 ) + 1: ( 0 1 ) +} +{ + 0: ( 0 ) + 1: ( 0 1 ) + 2: ( 0 1 3 ) + 3: ( 0 1 2 ) + 4: ( 0 1 2 3 ) +} +Partitions and cuts: +P: 0 +C: 0 +P: 0 1 +C: 1 2 5 +P: 0 1 5 +C: 1 2 6 +P: 0 1 3 +C: 1 4 5 +P: 0 1 3 5 +C: 1 4 6 +P: 0 1 2 +C: 2 3 5 +P: 0 1 2 5 +C: 2 3 6 +P: 0 1 2 3 +C: 3 4 5 +P: 0 1 2 3 5 +C: 3 4 6 +Partitions and cuts: +P: 1 +C: 1 +Partitions and cuts: +P: 0 +C: 0 +P: 0 1 +C: 1 +P: 0 1 2 +C: 2 +P: 0 1 2 3 +C: 3 +Partitions and cuts: +P: 0 +C: 0 1 +P: 0 2 +C: 0 3 +P: 0 1 +C: 1 2 4 5 6 +P: 0 1 6 +C: 1 2 4 5 9 +P: 0 1 5 +C: 1 2 4 6 8 +P: 0 1 5 6 +C: 1 2 4 8 9 +P: 0 1 4 +C: 1 2 5 6 7 +P: 0 1 4 6 +C: 1 2 5 7 9 +P: 0 1 4 5 +C: 1 2 6 7 8 +P: 0 1 4 5 6 +C: 1 2 7 8 9 +P: 0 1 4 5 6 2 +C: 2 3 +Cuts only (no partitions): +C: 0 1 +C: 0 3 +C: 1 2 4 5 6 +C: 1 2 4 5 9 +C: 1 2 4 6 8 +C: 1 2 4 8 9 +C: 1 2 5 6 7 +C: 1 2 5 7 9 +C: 1 2 6 7 8 +C: 1 2 7 8 9 +C: 2 3 +Partitions and cuts: +P: 0 +C: 0 +P: 0 1 +C: 1 +Partitions and cuts: +P: 0 +C: 0 +P: 0 1 +C: 1 +Partitions and cuts: +P: 1 +C: 1 +P: 1 2 +C: 2 diff --git a/tests/unit/igraph_all_st_mincuts.c b/tests/unit/igraph_all_st_mincuts.c new file mode 100644 index 0000000..ae1feef --- /dev/null +++ b/tests/unit/igraph_all_st_mincuts.c @@ -0,0 +1,191 @@ +/* + IGraph library. + Copyright (C) 2023-2024 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 +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g, + igraph_real_t value, + igraph_vector_int_list_t *partitions, + igraph_vector_int_list_t *cuts) { + igraph_integer_t i, e, m, n = igraph_vector_int_list_size(partitions); + printf("Found %" IGRAPH_PRId " cuts, value: %g\n", n, value); + for (i = 0; i < n; i++) { + igraph_vector_int_t *vec = igraph_vector_int_list_get_ptr(partitions, i); + igraph_vector_int_t *vec2 = cuts ? igraph_vector_int_list_get_ptr(cuts, i) : 0; + printf("Partition %" IGRAPH_PRId ": ", i); + igraph_vector_int_print(vec); + if (vec2) { + printf("Cut %" IGRAPH_PRId ":\n", i); + m = igraph_vector_int_size(vec2); + for (e = 0; e < m; e++) { + igraph_integer_t from = IGRAPH_FROM(g, VECTOR(*vec2)[e]), to = IGRAPH_TO(g, VECTOR(*vec2)[e]); + if (igraph_is_directed(g)) { + printf(" %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", from, to); + } else { + printf(" %" IGRAPH_PRId " -- %" IGRAPH_PRId "\n", from, to); + } + } + } + } + + igraph_vector_int_list_destroy(partitions); + if (cuts) { + igraph_vector_int_list_destroy(cuts); + } + printf("\n"); +} + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t partitions; + igraph_vector_int_list_t cuts; + igraph_real_t value; + + printf("Graph 1:\n"); + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, + -1); + + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 0, /*target=*/ 4, + /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + /* ---------------------------------------------------------------- */ + + printf("Graph 2:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 1, 3, 2, 4, 3, 4, 4, 5, -1); + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 0, /*target=*/ 5, /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + /* ---------------------------------------------------------------- */ + + printf("Graph 3:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 1, 3, 2, 4, 3, 4, 4, 5, -1); + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 0, /*target=*/ 4, /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + /* ---------------------------------------------------------------- */ + + printf("Graph 4:\n"); + igraph_small(&g, 9, IGRAPH_DIRECTED, 0, 1, 0, 2, 1, 3, 2, 3, + 1, 4, 4, 2, 1, 5, 5, 2, 1, 6, 6, 2, 1, 7, 7, 2, 1, 8, 8, 2, + -1); + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 0, /*target=*/ 3, /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + /* ---------------------------------------------------------------- */ + + printf("Graph 5:\n"); + igraph_small(&g, 4, IGRAPH_DIRECTED, + 1, 0, 2, 0, 2, 1, 3, 2, + -1); + + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 2, /*target=*/ 0, /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + /* ---------------------------------------------------------------- */ + + printf("Graph 6:\n"); + igraph_small(&g, 4, IGRAPH_DIRECTED, + 1, 0, 2, 0, 2, 1, 2, 3, + -1); + + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 2, /*target=*/ 0, /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + /* ---------------------------------------------------------------- */ + + printf("Graph 7:\n"); + igraph_small(&g, 9, IGRAPH_DIRECTED, + 0, 4, 0, 7, 1, 6, 2, 1, 3, 8, 4, 0, 4, 2, + 4, 5, 5, 0, 5, 3, 6, 7, 7, 8, + -1); + + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 0, /*target=*/ 8, /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + /* ---------------------------------------------------------------- */ + + printf("Graph 8:\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0, 2, 3, 0, 1, 2, 5, 1, 2, 4, 3, 5, 5, 4, + -1); + + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 3, /*target=*/ 4, /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + /* ---------------------------------------------------------------- */ + + printf("Graph 9:\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0, 1, 2, 0, 3, 0, 4, 0, 0, 5, 3, 1, 1, 4, 3, 2, 2, 5, 4, 3, 5, 3, 5, 4, + -1); + + igraph_vector_int_list_init(&partitions, 0); + igraph_vector_int_list_init(&cuts, 0); + igraph_all_st_mincuts(&g, &value, &cuts, &partitions, + /*source=*/ 3, /*target=*/ 0, /*capacity=*/ NULL); + + print_and_destroy(&g, value, &partitions, &cuts); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_all_st_mincuts.out b/tests/unit/igraph_all_st_mincuts.out new file mode 100644 index 0000000..f563591 --- /dev/null +++ b/tests/unit/igraph_all_st_mincuts.out @@ -0,0 +1,132 @@ +Graph 1: +Found 4 cuts, value: 1 +Partition 0: 0 +Cut 0: + 0 -> 1 +Partition 1: 0 1 +Cut 1: + 1 -> 2 +Partition 2: 0 1 2 +Cut 2: + 2 -> 3 +Partition 3: 0 1 2 3 +Cut 3: + 3 -> 4 + +Graph 2: +Found 2 cuts, value: 1 +Partition 0: 0 +Cut 0: + 0 -> 1 +Partition 1: 0 4 3 2 1 +Cut 1: + 4 -> 5 + +Graph 3: +Found 1 cuts, value: 1 +Partition 0: 0 +Cut 0: + 0 -> 1 + +Graph 4: +Found 3 cuts, value: 2 +Partition 0: 0 +Cut 0: + 0 -> 1 + 0 -> 2 +Partition 1: 0 2 +Cut 1: + 0 -> 1 + 2 -> 3 +Partition 2: 0 2 1 8 7 6 5 4 +Cut 2: + 1 -> 3 + 2 -> 3 + +Graph 5: +Found 2 cuts, value: 2 +Partition 0: 2 +Cut 0: + 2 -> 0 + 2 -> 1 +Partition 1: 2 1 +Cut 1: + 1 -> 0 + 2 -> 0 + +Graph 6: +Found 2 cuts, value: 2 +Partition 0: 2 3 +Cut 0: + 2 -> 0 + 2 -> 1 +Partition 1: 2 3 1 +Cut 1: + 1 -> 0 + 2 -> 0 + +Graph 7: +Found 5 cuts, value: 2 +Partition 0: 0 +Cut 0: + 0 -> 4 + 0 -> 7 +Partition 1: 0 7 +Cut 1: + 0 -> 4 + 7 -> 8 +Partition 2: 0 7 4 2 1 6 +Cut 2: + 4 -> 5 + 7 -> 8 +Partition 3: 0 7 4 2 1 6 5 +Cut 3: + 5 -> 3 + 7 -> 8 +Partition 4: 0 7 4 2 1 6 5 3 +Cut 4: + 3 -> 8 + 7 -> 8 + +Graph 8: +Found 4 cuts, value: 2 +Partition 0: 3 +Cut 0: + 3 -> 0 + 3 -> 5 +Partition 1: 3 0 +Cut 1: + 0 -> 2 + 3 -> 5 +Partition 2: 3 0 2 +Cut 2: + 2 -> 4 + 3 -> 5 +Partition 3: 3 0 2 5 1 +Cut 3: + 2 -> 4 + 5 -> 4 + +Graph 9: +Found 4 cuts, value: 3 +Partition 0: 3 +Cut 0: + 3 -> 0 + 3 -> 1 + 3 -> 2 +Partition 1: 3 1 +Cut 1: + 3 -> 0 + 1 -> 4 + 3 -> 2 +Partition 2: 3 1 4 +Cut 2: + 3 -> 0 + 4 -> 0 + 3 -> 2 +Partition 3: 3 1 4 2 5 +Cut 3: + 2 -> 0 + 3 -> 0 + 4 -> 0 + diff --git a/tests/unit/igraph_almost_equals.c b/tests/unit/igraph_almost_equals.c new file mode 100644 index 0000000..dfc92af --- /dev/null +++ b/tests/unit/igraph_almost_equals.c @@ -0,0 +1,191 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +/* This file is ported from Java; the original source is here: + https://floating-point-gui.de/errors/NearlyEqualsTest.java */ + +const double EPS = 0.00001; + +void assert_almost_equal_with_eps(double a, double b, double eps, int line) { + if (!igraph_almost_equals(a, b, eps)) { + igraph_fatalf("Assertion failed: %g == %g with eps = %g", IGRAPH_FILE_BASENAME, line, a, b, eps); + } +} + +void assert_not_equal_with_eps(double a, double b, double eps, int line) { + if (igraph_almost_equals(a, b, eps)) { + igraph_fatalf("Assertion failed: %g != %g with eps = %g", IGRAPH_FILE_BASENAME, line, a, b, eps); + } +} + +#define ASSERT_ALMOST_EQUAL_WITH_EPS(a, b, eps) assert_almost_equal_with_eps(a, b, eps, __LINE__) +#define ASSERT_ALMOST_EQUAL(a, b) assert_almost_equal_with_eps(a, b, EPS, __LINE__) +#define ASSERT_NOT_EQUAL_WITH_EPS(a, b, eps) assert_not_equal_with_eps(a, b, eps, __LINE__) +#define ASSERT_NOT_EQUAL(a, b) assert_not_equal_with_eps(a, b, EPS, __LINE__) + +void test_large_numbers(void) { + ASSERT_ALMOST_EQUAL(1000000, 1000001); + ASSERT_ALMOST_EQUAL(1000001, 1000000); + ASSERT_NOT_EQUAL(10000, 10001); + ASSERT_NOT_EQUAL(10001, 10000); +} + +void test_large_negative_numbers(void) { + ASSERT_ALMOST_EQUAL(-1000000, -1000001); + ASSERT_ALMOST_EQUAL(-1000001, -1000000); + ASSERT_NOT_EQUAL(-10000, -10001); + ASSERT_NOT_EQUAL(-10001, -10000); +} + +void test_numbers_around_one(void) { + ASSERT_ALMOST_EQUAL(1.0000001, 1.0000002); + ASSERT_ALMOST_EQUAL(1.0000002, 1.0000001); + ASSERT_NOT_EQUAL(1.0002, 1.0001); + ASSERT_NOT_EQUAL(1.0001, 1.0002); +} + +void test_numbers_around_minus_one(void) { + ASSERT_ALMOST_EQUAL(-1.0000001, -1.0000002); + ASSERT_ALMOST_EQUAL(-1.0000002, -1.0000001); + ASSERT_NOT_EQUAL(-1.0002, -1.0001); + ASSERT_NOT_EQUAL(-1.0001, -1.0002); +} + +void test_small_numbers(void) { + ASSERT_ALMOST_EQUAL(0.000000001000001, 0.000000001000002); + ASSERT_ALMOST_EQUAL(0.000000001000002, 0.000000001000001); + ASSERT_NOT_EQUAL(0.000000000001002, 0.000000000001001); + ASSERT_NOT_EQUAL(0.000000000001001, 0.000000000001002); +} + +void test_small_negative_numbers(void) { + ASSERT_ALMOST_EQUAL(-0.000000001000001, -0.000000001000002); + ASSERT_ALMOST_EQUAL(-0.000000001000002, -0.000000001000001); + ASSERT_NOT_EQUAL(-0.000000000001002, -0.000000000001001); + ASSERT_NOT_EQUAL(-0.000000000001001, -0.000000000001002); +} + +void test_small_differences_away_from_zero(void) { + ASSERT_ALMOST_EQUAL(0.3, 0.30000003); + ASSERT_ALMOST_EQUAL(-0.3, -0.30000003); +} + +void test_comparisons_involving_zero(void) { + ASSERT_ALMOST_EQUAL(0, 0); + ASSERT_ALMOST_EQUAL(0.0, -0.0); + ASSERT_ALMOST_EQUAL(-0.0, -0.0); + ASSERT_NOT_EQUAL(0.00000001, 0.0); + ASSERT_NOT_EQUAL(0.0, 0.00000001); + ASSERT_NOT_EQUAL(-0.00000001, 0.0); + ASSERT_NOT_EQUAL(0.0, -0.00000001); + + /* original test contained 1e-40 here, which is a denormalized number in + * single-precision float world. An equivalent value for doubles is ~1e-320, + * see : https://docs.oracle.com/javase/8/docs/api/constant-values.html#java.lang.Double.MIN_VALUE + * The value must be between Double.MIN_VALUE and Double.MIN_NORMAL */ + ASSERT_ALMOST_EQUAL_WITH_EPS(0.0, 1e-320, 0.01); + ASSERT_ALMOST_EQUAL_WITH_EPS(1e-320, 0.0, 0.01); +} + +void test_extreme_values(void) { + ASSERT_ALMOST_EQUAL(DBL_MAX, DBL_MAX); + ASSERT_NOT_EQUAL(DBL_MAX, -DBL_MAX); + ASSERT_NOT_EQUAL(-DBL_MAX, DBL_MAX); + ASSERT_NOT_EQUAL(DBL_MAX, DBL_MAX / 2); + ASSERT_NOT_EQUAL(DBL_MAX, -DBL_MAX / 2); + ASSERT_NOT_EQUAL(-DBL_MAX, DBL_MAX / 2); +} + +void test_infinities(void) { + ASSERT_ALMOST_EQUAL(IGRAPH_POSINFINITY, IGRAPH_POSINFINITY); + ASSERT_ALMOST_EQUAL(IGRAPH_NEGINFINITY, IGRAPH_NEGINFINITY); + ASSERT_NOT_EQUAL(IGRAPH_NEGINFINITY, IGRAPH_POSINFINITY); + ASSERT_NOT_EQUAL(IGRAPH_POSINFINITY, DBL_MAX); + ASSERT_NOT_EQUAL(IGRAPH_NEGINFINITY, -DBL_MAX); +} + +void test_nans(void) { + ASSERT_NOT_EQUAL(IGRAPH_NAN, IGRAPH_NAN); + ASSERT_NOT_EQUAL(IGRAPH_NAN, 0); + ASSERT_NOT_EQUAL(-0.0, IGRAPH_NAN); + ASSERT_NOT_EQUAL(IGRAPH_NAN, -0.0); + ASSERT_NOT_EQUAL(IGRAPH_NAN, IGRAPH_POSINFINITY); + ASSERT_NOT_EQUAL(IGRAPH_POSINFINITY, IGRAPH_NAN); + ASSERT_NOT_EQUAL(IGRAPH_NAN, IGRAPH_NEGINFINITY); + ASSERT_NOT_EQUAL(IGRAPH_NEGINFINITY, IGRAPH_NAN); + ASSERT_NOT_EQUAL(IGRAPH_NAN, DBL_MAX); + ASSERT_NOT_EQUAL(DBL_MAX, IGRAPH_NAN); + ASSERT_NOT_EQUAL(IGRAPH_NAN, -DBL_MAX); + ASSERT_NOT_EQUAL(-DBL_MAX, IGRAPH_NAN); + ASSERT_NOT_EQUAL(IGRAPH_NAN, DBL_MIN); + ASSERT_NOT_EQUAL(DBL_MIN, IGRAPH_NAN); + ASSERT_NOT_EQUAL(IGRAPH_NAN, -DBL_MIN); + ASSERT_NOT_EQUAL(-DBL_MIN, IGRAPH_NAN); +} + +void test_opposite_sides_of_zero(void) { + ASSERT_NOT_EQUAL(1.000000001, -1); + ASSERT_NOT_EQUAL(-1, 1.000000001); + ASSERT_NOT_EQUAL(-1.000000001, 1); + ASSERT_NOT_EQUAL(1, -1.000000001); + + /* These tests from NearlyEqualsTest.java involved denormalized numbers in + * Java world, and they were defined as floats. We use doubles, so I converted + * the values manually by looking up Double.MIN_VALUE */ + ASSERT_ALMOST_EQUAL(49e-324, -49e-324); +} + +void test_very_close_to_zero(void) { +#define DBL_DENORM_MIN 4.9e-324 + ASSERT_ALMOST_EQUAL(DBL_DENORM_MIN, DBL_DENORM_MIN); + ASSERT_ALMOST_EQUAL(DBL_DENORM_MIN, -DBL_DENORM_MIN); + ASSERT_ALMOST_EQUAL(-DBL_DENORM_MIN, DBL_DENORM_MIN); + ASSERT_ALMOST_EQUAL(DBL_DENORM_MIN, 0); + ASSERT_ALMOST_EQUAL(0, DBL_DENORM_MIN); + ASSERT_ALMOST_EQUAL(-DBL_DENORM_MIN, 0); + ASSERT_ALMOST_EQUAL(0, -DBL_DENORM_MIN); + + ASSERT_NOT_EQUAL(0.000000001, -DBL_DENORM_MIN); + ASSERT_NOT_EQUAL(0.000000001, DBL_DENORM_MIN); + ASSERT_NOT_EQUAL(DBL_DENORM_MIN, 0.000000001); + ASSERT_NOT_EQUAL(-DBL_DENORM_MIN, 0.000000001); +} + +int main(void) { + test_large_numbers(); + test_large_negative_numbers(); + test_numbers_around_one(); + test_numbers_around_minus_one(); + test_small_numbers(); + test_small_negative_numbers(); + test_small_differences_away_from_zero(); + test_comparisons_involving_zero(); + test_extreme_values(); + test_infinities(); + test_nans(); + test_opposite_sides_of_zero(); + test_very_close_to_zero(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_are_connected.c b/tests/unit/igraph_are_connected.c new file mode 100644 index 0000000..dd3b6f7 --- /dev/null +++ b/tests/unit/igraph_are_connected.c @@ -0,0 +1,69 @@ +/* -*- 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_bool_t connected; + + /* Complete graph. Any two distinct vertices are connected. */ + + igraph_full(&g, 10, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_are_adjacent(&g, 2, 7, &connected); + IGRAPH_ASSERT(connected); + + igraph_are_adjacent(&g, 0, 0, &connected); + IGRAPH_ASSERT(! connected); + + igraph_destroy(&g); + + /* Complete graph with self-loops. */ + + igraph_full(&g, 10, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + igraph_are_adjacent(&g, 1, 7, &connected); + IGRAPH_ASSERT(connected); + + igraph_are_adjacent(&g, 2, 2, &connected); + IGRAPH_ASSERT(connected); + + igraph_destroy(&g); + + /* Graph with no edges. Any two distinct vertices are disconnected. */ + + igraph_empty(&g, 10, IGRAPH_DIRECTED); + igraph_are_adjacent(&g, 3, 6, &connected); + IGRAPH_ASSERT(! connected); + igraph_destroy(&g); + + /* Invalid vertex ID, expecting an error */ + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0, 2,3, -1); + igraph_set_error_handler(igraph_error_handler_ignore); + IGRAPH_ASSERT(igraph_are_adjacent(&g, 0, igraph_vcount(&g) + 2 /* vertex ID out of range */, &connected) == IGRAPH_EINVVID); + igraph_set_error_handler(igraph_error_handler_abort); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_arpack_rnsolve.c b/tests/unit/igraph_arpack_rnsolve.c new file mode 100644 index 0000000..d1f2064 --- /dev/null +++ b/tests/unit/igraph_arpack_rnsolve.c @@ -0,0 +1,233 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +typedef struct cb2_data_t { + igraph_matrix_t *A; +} cb2_data_t; + +igraph_error_t cb2(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { + IGRAPH_UNUSED(n); + cb2_data_t *data = (cb2_data_t*) extra; + igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0, + data->A, from, /*beta=*/ 0.0, to); + return IGRAPH_SUCCESS; +} + +int check_eigenvector( + const char* test_name, + igraph_matrix_t* A, igraph_matrix_t* values, igraph_matrix_t* vectors, + int eval_idx, int evec_col_idx +) { + igraph_complex_t eval, prod; + igraph_complex_t *evec; + int i, j, n = igraph_matrix_nrow(A); + + eval = igraph_complex(MATRIX(*values, eval_idx, 0), MATRIX(*values, eval_idx, 1)); + evec = (igraph_complex_t*) calloc(n, sizeof(igraph_complex_t)); + if (IGRAPH_IMAG(eval) == 0) { + /* Real eigenvalue, so we have a real eigenvector */ + for (i = 0; i < n; i++) { + evec[i] = igraph_complex(MATRIX(*vectors, i, evec_col_idx), 0); + } + } else { + /* Complex eigenvalue pair, so we have a complex eigenvector pair */ + /* ARPACK always stores the eigenvector corresponding to the eigenvalue + * with a positive imaginary part. If the imaginary part is negative, we + * need to multiply the imaginary part of the eigenvector by -1 */ + for (i = 0; i < n; i++) { + evec[i] = igraph_complex( + MATRIX(*vectors, i, evec_col_idx), + MATRIX(*vectors, i, evec_col_idx + 1) * ( + IGRAPH_IMAG(eval) < 0 ? -1 : 1 + ) + ); + } + } + + /* Multiply matrix with eigenvector */ + for (i = 0; i < n; i++) { + prod = igraph_complex(0, 0); + for (j = 0; j < n; j++) { + prod = igraph_complex_add( + igraph_complex_mul_real(evec[j], MATRIX(*A, i, j)), + prod + ); + } + prod = igraph_complex_div(prod, eval); + if (!igraph_complex_almost_equals(prod, evec[i], 1e-12)) { + prod = igraph_complex_sub(prod, evec[i]); + printf("%s: vector corresponding to eigenvalue (%.4f + %.4f*i) is not an " + "eigenvector, coordinate %d differs by %.4f + %.4f*i\n", + test_name, IGRAPH_REAL(eval), IGRAPH_IMAG(eval), + i, IGRAPH_REAL(prod), IGRAPH_IMAG(prod)); + + free(evec); + return 1; + } + } + + /* Free stuff */ + free(evec); + + return 0; +} + +int check_eigenvectors( + const char* test_name, + igraph_matrix_t* A, igraph_matrix_t* values, igraph_matrix_t* vectors +) { + int i, j; + int nev = igraph_matrix_nrow(values); + int errors = 0; + igraph_bool_t conjugate_pair_will_come = 0; + + for (i = 0, j = 0; i < nev; i++) { + errors += check_eigenvector(test_name, A, values, vectors, i, j); + if (MATRIX(*values, i, 1) != 0) { + /* Complex eigenvalue */ + if (conjugate_pair_will_come) { + j += 2; + conjugate_pair_will_come = 0; + } else { + conjugate_pair_will_come = 1; + } + } else { + /* Real eigenvalue */ + j++; + } + } + return (errors > 0) ? 1 : 0; +} + +#define DIM 10 + +int main(void) { + igraph_matrix_t A; + igraph_matrix_t values, vectors; + igraph_arpack_options_t options; + cb2_data_t data = { &A }; + int i, j; + + /* Note: igraph_arpack_rnsolve() uses the RNG to generate a random + * starting vector for ARPACK. */ + igraph_rng_seed(igraph_rng_default(), 42 * 42); + + igraph_matrix_init(&A, DIM, DIM); + + for (i = 0; i < DIM; i++) { + for (j = 0; j < DIM; j++) { + MATRIX(A, i, j) = igraph_rng_get_integer(igraph_rng_default(), -12, 12); + } + } + + printf("Input matrix:\n"); + igraph_matrix_print(&A); + printf("\n"); + + printf("\n4 largest eigenvalues by magnitude:\n"); + + igraph_arpack_options_init(&options); + options.n = DIM; + options.start = 0; + options.nev = 4; + options.ncv = 9; + options.which[0] = 'L' ; + options.which[1] = 'M'; + + igraph_matrix_init(&values, 0, 0); + igraph_matrix_init(&vectors, options.n, 1); + + igraph_arpack_rnsolve(cb2, /*extra=*/ &data, &options, /*storage=*/ 0, + &values, &vectors); + printf("\nEigenvalues:\n"); + igraph_matrix_print(&values); + if (check_eigenvectors("LM #1", &A, &values, &vectors)) { + printf("\nEigenvectors:\n"); + igraph_matrix_print(&vectors); + } + + /* -------------- */ + + printf("\n3 largest eigenvalues by magnitude:\n"); + + options.nev = 3; + options.which[0] = 'L' ; + options.which[1] = 'M'; + + igraph_arpack_rnsolve(cb2, /*extra=*/ &data, &options, /*storage=*/ 0, + &values, &vectors); + printf("\nEigenvalues:\n"); + igraph_matrix_print(&values); + if (check_eigenvectors("LM #2", &A, &values, &vectors)) { + printf("\nEigenvectors:\n"); + igraph_matrix_print(&vectors); + } + + /* -------------- */ + + printf("\n3 smallest eigenvalues by real part:\n"); + + options.nev = 3; + options.which[0] = 'S' ; + options.which[1] = 'R'; + + igraph_arpack_rnsolve(cb2, /*extra=*/ &data, &options, /*storage=*/ 0, + &values, &vectors); + printf("\nEigenvalues:\n"); + igraph_matrix_print(&values); + if (check_eigenvectors("SR", &A, &values, &vectors)) { + printf("\nEigenvectors:\n"); + igraph_matrix_print(&vectors); + } + + /* -------------- */ + + printf("\n3 smallest eigenvalues by imaginary part:\n"); + + options.nev = 3; + options.which[0] = 'L' ; + options.which[1] = 'I'; + + igraph_arpack_rnsolve(cb2, /*extra=*/ &data, &options, /*storage=*/ 0, + &values, &vectors); + printf("\nEigenvalues:\n"); + igraph_matrix_print(&values); + if (check_eigenvectors("LI", &A, &values, &vectors)) { + printf("\nEigenvectors:\n"); + igraph_matrix_print(&vectors); + } + + /* -------------- */ + + igraph_matrix_destroy(&values); + igraph_matrix_destroy(&vectors); + igraph_matrix_destroy(&A); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_arpack_rnsolve.out b/tests/unit/igraph_arpack_rnsolve.out new file mode 100644 index 0000000..b97de59 --- /dev/null +++ b/tests/unit/igraph_arpack_rnsolve.out @@ -0,0 +1,41 @@ +Input matrix: + 3 -6 -4 -3 5 -1 -5 1 -3 0 + 7 -12 11 5 3 -4 -10 -12 -9 1 + 0 -4 9 10 7 -7 -10 -10 10 3 +-1 4 6 -7 -2 -7 8 12 9 -3 +-7 -7 -9 12 11 -6 -7 -4 -8 5 +-6 9 -2 -5 1 7 -1 12 -10 -5 + 6 -2 7 3 -8 -10 3 -4 7 8 +12 -8 7 -4 -8 9 4 12 -3 9 + 2 -10 9 -7 -12 7 5 -11 4 8 +-7 1 -8 3 3 -4 -1 8 -1 -9 + + +4 largest eigenvalues by magnitude: + +Eigenvalues: + 24.0492 0 +-23.9674 0 + 13.8157 10.1107 + 13.8157 -10.1107 + +3 largest eigenvalues by magnitude: + +Eigenvalues: + 24.0492 0 +-23.9674 0 + 13.8157 10.1107 + +3 smallest eigenvalues by real part: + +Eigenvalues: +-23.9674 0 +-11.1679 0 + -7.1 3.73906 + +3 smallest eigenvalues by imaginary part: + +Eigenvalues: +4.04984 15.1474 +4.04984 -15.1474 +13.8157 10.1107 diff --git a/tests/unit/igraph_arpack_unpack_complex.c b/tests/unit/igraph_arpack_unpack_complex.c new file mode 100644 index 0000000..0b49048 --- /dev/null +++ b/tests/unit/igraph_arpack_unpack_complex.c @@ -0,0 +1,103 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_matrix_t *vectors, igraph_matrix_t *values, igraph_integer_t nev, igraph_error_t error) { + printf("vectors in:\n"); + print_matrix_format(vectors, stdout, "%6.2f"); + printf("values in:\n"); + print_matrix_format(values, stdout, "%6.2f"); + IGRAPH_ASSERT(igraph_arpack_unpack_complex(vectors, values, nev) == error); + printf("vectors out:\n"); + print_matrix_format(vectors, stdout, "%6.2f"); + printf("values out:\n"); + print_matrix_format(values, stdout, "%6.2f"); + igraph_matrix_destroy(vectors); + igraph_matrix_destroy(values); + printf("\n"); +} + +int main(void) { + igraph_matrix_t vectors; + igraph_matrix_t values; + + printf("Empty vectors and values:\n"); + matrix_init_int_row_major(&vectors, 0, 0, NULL); + matrix_init_int_row_major(&values, 0, 0, NULL); + print_and_destroy(&vectors, &values, 0, IGRAPH_SUCCESS); + + { + printf("Real vectors and values:\n"); + int vectors_elem[4] = {-1, 0, 9, 10}; + int values_elem[4] = {-6, 0, 3, 0}; + matrix_init_int_row_major(&vectors, 2, 2, vectors_elem); + matrix_init_int_row_major(&values, 2, 2, values_elem); + print_and_destroy(&vectors, &values, 2, IGRAPH_SUCCESS); + } + { + printf("Complex vectors and values:\n"); + igraph_real_t vectors_elem[36] = { + 0.123938, 0.3411, 0.114301, -0.134822, -0.421672, -0.484969, + -0.268889, 0.00766665, 0.413844, 0.200565, -0.0336028, -0.133362, + -0.192782, -0.140134, 0.579782, -0.0853149, -0.0684855, 0.117105, + 0.175547, 0.1833, 0.156218, 0.0623488, 0.422265, -0.257261, + -0.266691, 0.404647, -0.462498, -0.0885737, 0.203893, -0.135195, + 0.662813, -0.022972, -0.193704, 0.355354, -0.0405741, 0.493652}; + igraph_real_t values_elem[12] = { + -2.58338, 9.66092, -2.58338, -9.66092, 7.07998, 6.51033, + 7.07998, -6.51033, -7.9966, 2.74368, -7.9966, -2.74368}; + matrix_init_real_row_major(&vectors, 6, 6, vectors_elem); + matrix_init_real_row_major(&values, 6, 2, values_elem); + print_and_destroy(&vectors, &values, 6, IGRAPH_SUCCESS); + } + { + printf("Both complex and real vectors and values:\n"); + int vectors_elem[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + int values_elem[8] = {1, 0, 2, 1, 2, -1, 3, 0}; + matrix_init_int_row_major(&vectors, 4, 4, vectors_elem); + matrix_init_int_row_major(&values, 4, 2, values_elem); + print_and_destroy(&vectors, &values, 4, IGRAPH_SUCCESS); + } + { + printf("Both complex and real vectors and values, but nev = 2:\n"); + int vectors_elem[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + int values_elem[8] = {1, 0, 2, 1, 2, -1, 3, 0}; + matrix_init_int_row_major(&vectors, 4, 4, vectors_elem); + matrix_init_int_row_major(&values, 4, 2, values_elem); + print_and_destroy(&vectors, &values, 2, IGRAPH_SUCCESS); + } + { + printf("No vectors but there are values:\n"); + int values_elem[8] = {1, 0, 2, 1, 2, -1, 3, 0}; + matrix_init_int_row_major(&vectors, 0, 0, NULL); + matrix_init_int_row_major(&values, 4, 2, values_elem); + print_and_destroy(&vectors, &values, 4, IGRAPH_SUCCESS); + } + { + printf("No values, but there are vectors:\n"); + int vectors_elem[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + matrix_init_int_row_major(&vectors, 4, 4, vectors_elem); + matrix_init_int_row_major(&values, 0, 0, NULL); + print_and_destroy(&vectors, &values, 0, IGRAPH_SUCCESS); + } + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_arpack_unpack_complex.out b/tests/unit/igraph_arpack_unpack_complex.out new file mode 100644 index 0000000..eff24c7 --- /dev/null +++ b/tests/unit/igraph_arpack_unpack_complex.out @@ -0,0 +1,125 @@ +Empty vectors and values: +vectors in: +[ 0-by-0 ] +values in: +[ 0-by-0 ] +vectors out: +[ 0-by-0 ] +values out: +[ 0-by-0 ] + +Real vectors and values: +vectors in: +[ -1.00 0.00 + 9.00 10.00 ] +values in: +[ -6.00 0.00 + 3.00 0.00 ] +vectors out: +[ -1.00 0.00 0.00 0.00 + 9.00 0.00 10.00 0.00 ] +values out: +[ -6.00 0.00 + 3.00 0.00 ] + +Complex vectors and values: +vectors in: +[ 0.12 0.34 0.11 -0.13 -0.42 -0.48 + -0.27 0.01 0.41 0.20 -0.03 -0.13 + -0.19 -0.14 0.58 -0.09 -0.07 0.12 + 0.18 0.18 0.16 0.06 0.42 -0.26 + -0.27 0.40 -0.46 -0.09 0.20 -0.14 + 0.66 -0.02 -0.19 0.36 -0.04 0.49 ] +values in: +[ -2.58 9.66 + -2.58 -9.66 + 7.08 6.51 + 7.08 -6.51 + -8.00 2.74 + -8.00 -2.74 ] +vectors out: +[ 0.12 0.34 0.12 -0.34 0.11 -0.13 0.11 0.13 -0.42 -0.48 -0.42 0.48 + -0.27 0.01 -0.27 -0.01 0.41 0.20 0.41 -0.20 -0.03 -0.13 -0.03 0.13 + -0.19 -0.14 -0.19 0.14 0.58 -0.09 0.58 0.09 -0.07 0.12 -0.07 -0.12 + 0.18 0.18 0.18 -0.18 0.16 0.06 0.16 -0.06 0.42 -0.26 0.42 0.26 + -0.27 0.40 -0.27 -0.40 -0.46 -0.09 -0.46 0.09 0.20 -0.14 0.20 0.14 + 0.66 -0.02 0.66 0.02 -0.19 0.36 -0.19 -0.36 -0.04 0.49 -0.04 -0.49 ] +values out: +[ -2.58 9.66 + -2.58 -9.66 + 7.08 6.51 + 7.08 -6.51 + -8.00 2.74 + -8.00 -2.74 ] + +Both complex and real vectors and values: +vectors in: +[ 1.00 2.00 3.00 4.00 + 5.00 6.00 7.00 8.00 + 9.00 10.00 11.00 12.00 + 13.00 14.00 15.00 16.00 ] +values in: +[ 1.00 0.00 + 2.00 1.00 + 2.00 -1.00 + 3.00 0.00 ] +vectors out: +[ 1.00 0.00 2.00 3.00 2.00 -3.00 4.00 0.00 + 5.00 0.00 6.00 7.00 6.00 -7.00 8.00 0.00 + 9.00 0.00 10.00 11.00 10.00 -11.00 12.00 0.00 + 13.00 0.00 14.00 15.00 14.00 -15.00 16.00 0.00 ] +values out: +[ 1.00 0.00 + 2.00 1.00 + 2.00 -1.00 + 3.00 0.00 ] + +Both complex and real vectors and values, but nev = 2: +vectors in: +[ 1.00 2.00 3.00 4.00 + 5.00 6.00 7.00 8.00 + 9.00 10.00 11.00 12.00 + 13.00 14.00 15.00 16.00 ] +values in: +[ 1.00 0.00 + 2.00 1.00 + 2.00 -1.00 + 3.00 0.00 ] +vectors out: +[ 1.00 0.00 2.00 3.00 + 5.00 0.00 6.00 7.00 + 9.00 0.00 10.00 11.00 + 13.00 0.00 14.00 15.00 ] +values out: +[ 1.00 0.00 + 2.00 1.00 ] + +No vectors but there are values: +vectors in: +[ 0-by-0 ] +values in: +[ 1.00 0.00 + 2.00 1.00 + 2.00 -1.00 + 3.00 0.00 ] +vectors out: +[ 0-by-8 ] +values out: +[ 1.00 0.00 + 2.00 1.00 + 2.00 -1.00 + 3.00 0.00 ] + +No values, but there are vectors: +vectors in: +[ 1.00 2.00 3.00 4.00 + 5.00 6.00 7.00 8.00 + 9.00 10.00 11.00 12.00 + 13.00 14.00 15.00 16.00 ] +values in: +[ 0-by-0 ] +vectors out: +[ 4-by-0 ] +values out: +[ 0-by-0 ] + diff --git a/tests/unit/igraph_array.c b/tests/unit/igraph_array.c new file mode 100644 index 0000000..01d155d --- /dev/null +++ b/tests/unit/igraph_array.c @@ -0,0 +1,72 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +void print_array(const igraph_array3_t *a) { + igraph_integer_t i, j, k; + for (k = 0; k < igraph_array3_n(a, 3); k++) { + for (i = 0; i < igraph_array3_n(a, 1); i++) { + for (j = 0; j < igraph_array3_n(a, 2); j++) { + printf(" %g", (double) ARRAY3(*a, i, j, k)); + } + printf("\n"); + } + printf("\n"); + } +} + +int main(void) { + igraph_array3_t a; + igraph_integer_t i, j, k; + igraph_integer_t s = 1; + + igraph_array3_init(&a, 5, 4, 3); + igraph_array3_destroy(&a); + + igraph_array3_init(&a, 5, 4, 3); + print_array(&a); + IGRAPH_ASSERT(igraph_array3_n(&a, 1) == 5); + IGRAPH_ASSERT(igraph_array3_n(&a, 2) == 4); + IGRAPH_ASSERT(igraph_array3_n(&a, 3) == 3); + igraph_array3_destroy(&a); + + igraph_array3_init(&a, 5, 4, 3); + for (k = 0; k < igraph_array3_n(&a, 3); k++) { + for (j = 0; j < igraph_array3_n(&a, 2); j++) { + for (i = 0; i < igraph_array3_n(&a, 1); i++) { + ARRAY3(a, i, j, k) = s++; + } + } + } + print_array(&a); + print_vector_format(&a.data, stdout, "%g"); + igraph_array3_destroy(&a); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_array.out b/tests/unit/igraph_array.out new file mode 100644 index 0000000..d0acd7c --- /dev/null +++ b/tests/unit/igraph_array.out @@ -0,0 +1,37 @@ + 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 + 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 + + 1 6 11 16 + 2 7 12 17 + 3 8 13 18 + 4 9 14 19 + 5 10 15 20 + + 21 26 31 36 + 22 27 32 37 + 23 28 33 38 + 24 29 34 39 + 25 30 35 40 + + 41 46 51 56 + 42 47 52 57 + 43 48 53 58 + 44 49 54 59 + 45 50 55 60 + +( 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 52 53 54 55 56 57 58 59 60 ) diff --git a/tests/unit/igraph_atlas.c b/tests/unit/igraph_atlas.c new file mode 100644 index 0000000..077691d --- /dev/null +++ b/tests/unit/igraph_atlas.c @@ -0,0 +1,30 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + + CHECK_ERROR(igraph_atlas(&g, -1), IGRAPH_EINVAL); + CHECK_ERROR(igraph_atlas(&g, 1253), IGRAPH_EINVAL); + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_attribute_combination_remove.c b/tests/unit/igraph_attribute_combination_remove.c new file mode 100644 index 0000000..2bb3f25 --- /dev/null +++ b/tests/unit/igraph_attribute_combination_remove.c @@ -0,0 +1,71 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_comb(igraph_attribute_combination_t *comb) { + int i; + igraph_attribute_combination_record_t *r; + for (i = 0; i < igraph_vector_ptr_size(&comb->list); i++) { + r = VECTOR(comb->list)[i]; + if (r->name) { + printf("name: %s", r->name); + } else { + printf("name: NULL"); + } + printf(", type: %d\n", r->type); + } + printf("\n"); +} + +int main(void) { + igraph_attribute_combination_t comb; + + igraph_attribute_combination(&comb, + "weight", IGRAPH_ATTRIBUTE_COMBINE_SUM, + "type", IGRAPH_ATTRIBUTE_COMBINE_FIRST, + "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, + IGRAPH_NO_MORE_ATTRIBUTES); + + printf("starting combinations:\n"); + print_comb(&comb); + + printf("Removing nonexistent combination:\n"); + igraph_attribute_combination_remove(&comb, "nonexistent_name"); + print_comb(&comb); + + printf("Removing weight:\n"); + igraph_attribute_combination_remove(&comb, "weight"); + print_comb(&comb); + + printf("Removing type and NULL:\n"); + igraph_attribute_combination_remove(&comb, "type"); + igraph_attribute_combination_remove(&comb, NULL); + print_comb(&comb); + + printf("Removing nonexistent combination again:\n"); + igraph_attribute_combination_remove(&comb, "nonexistent_name"); + igraph_attribute_combination_remove(&comb, NULL); + print_comb(&comb); + + igraph_attribute_combination_destroy(&comb); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_attribute_combination_remove.out b/tests/unit/igraph_attribute_combination_remove.out new file mode 100644 index 0000000..1e8397c --- /dev/null +++ b/tests/unit/igraph_attribute_combination_remove.out @@ -0,0 +1,18 @@ +starting combinations: +name: weight, type: 3 +name: type, type: 8 +name: NULL, type: 0 + +Removing nonexistent combination: +name: weight, type: 3 +name: type, type: 8 +name: NULL, type: 0 + +Removing weight: +name: type, type: 8 +name: NULL, type: 0 + +Removing type and NULL: + +Removing nonexistent combination again: + diff --git a/tests/unit/igraph_average_path_length.c b/tests/unit/igraph_average_path_length.c new file mode 100644 index 0000000..dddc77f --- /dev/null +++ b/tests/unit/igraph_average_path_length.c @@ -0,0 +1,80 @@ +/* + 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 . +*/ + +#include +#include + +#include "test_utilities.h" + +void print_and_destroy(igraph_t *graph, igraph_bool_t unconn) { + igraph_real_t res, unconn_pairs; + + igraph_average_path_length(graph, &res, &unconn_pairs, IGRAPH_DIRECTED, unconn); + printf("Average shortest path length: "); + print_real(stdout, res, "%g"); + printf("\nNo. of unconnected pairs: %g\n", unconn_pairs); + + igraph_destroy(graph); +} + +int main(void) { + igraph_t graph; + + printf("Null graph:\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + print_and_destroy(&graph, 1); + + printf("\nSingleton graph:\n"); + igraph_empty(&graph, 1, IGRAPH_DIRECTED); + print_and_destroy(&graph, 1); + + printf("\n2-vertex empty graph, unconn=true:\n"); + igraph_empty(&graph, 2, IGRAPH_UNDIRECTED); + print_and_destroy(&graph, 1); + + printf("\n2-vertex empty graph, unconn=false:\n"); + igraph_empty(&graph, 2, IGRAPH_UNDIRECTED); + print_and_destroy(&graph, 0); + + printf("\nSmallest bifurcating directed tree, unconn=true:\n"); + igraph_small(&graph, 2, IGRAPH_DIRECTED, + 0,1, 0,2, -1); + print_and_destroy(&graph, 1); + + printf("\nSmallest bifurcating directed tree, unconn=false:\n"); + igraph_small(&graph, 2, IGRAPH_DIRECTED, + 0,1, 0,2, -1); + print_and_destroy(&graph, 1); + + printf("\nUndirected 11-cycle:\n"); + igraph_ring(&graph, 11, IGRAPH_UNDIRECTED, 0, 1); + print_and_destroy(&graph, 1); + + printf("\nDirected 6-cycle:\n"); + igraph_ring(&graph, 11, IGRAPH_DIRECTED, 0, 1); + print_and_destroy(&graph, 1); + + /* Result verified by szhorvat on 2021-03-04. */ + printf("\nDe Bruijn graph, n=3 and m=5:\n"); + igraph_de_bruijn(&graph, 3, 5); + print_and_destroy(&graph, 1); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_average_path_length.out b/tests/unit/igraph_average_path_length.out new file mode 100644 index 0000000..892148b --- /dev/null +++ b/tests/unit/igraph_average_path_length.out @@ -0,0 +1,35 @@ +Null graph: +Average shortest path length: NaN +No. of unconnected pairs: 0 + +Singleton graph: +Average shortest path length: NaN +No. of unconnected pairs: 0 + +2-vertex empty graph, unconn=true: +Average shortest path length: NaN +No. of unconnected pairs: 2 + +2-vertex empty graph, unconn=false: +Average shortest path length: Inf +No. of unconnected pairs: 2 + +Smallest bifurcating directed tree, unconn=true: +Average shortest path length: 1 +No. of unconnected pairs: 4 + +Smallest bifurcating directed tree, unconn=false: +Average shortest path length: 1 +No. of unconnected pairs: 4 + +Undirected 11-cycle: +Average shortest path length: 3 +No. of unconnected pairs: 0 + +Directed 6-cycle: +Average shortest path length: 5.5 +No. of unconnected pairs: 0 + +De Bruijn graph, n=3 and m=5: +Average shortest path length: 4.34405 +No. of unconnected pairs: 0 diff --git a/tests/unit/igraph_average_path_length_dijkstra.c b/tests/unit/igraph_average_path_length_dijkstra.c new file mode 100644 index 0000000..6042092 --- /dev/null +++ b/tests/unit/igraph_average_path_length_dijkstra.c @@ -0,0 +1,99 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void compute_and_print(igraph_t *graph, igraph_vector_t *weights, igraph_bool_t directed, igraph_bool_t unconn) { + igraph_real_t result; + igraph_real_t unconn_pairs; + + IGRAPH_ASSERT(igraph_average_path_length_dijkstra(graph, &result, &unconn_pairs, weights, + directed, unconn) == IGRAPH_SUCCESS); + + printf("Result: "); + print_real(stdout, result, "%8g"); + printf("\nUnconnected pairs: "); + print_real(stdout, unconn_pairs, "%8g"); + printf("\n\n"); +} + +int main(void) { + igraph_t g_0, g_1, g_2, g_3, g_lm; + igraph_vector_t weights_0, weights_3, weights_lm, weights_lm_neg; + igraph_real_t result; + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_2, 2, 0, -1); + igraph_small(&g_3, 2, 1, 0,1, 0,2, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + + igraph_vector_init(&weights_0, 0); + igraph_vector_init_int(&weights_3, 2, 1, 1); + igraph_vector_init_int(&weights_lm, 8, 0, 1, 2, 3, 4, 5, 6, 7); + igraph_vector_init_int(&weights_lm_neg, 8, -10, 1, 2, 3, 4, 5, 6, 7); + + printf("No vertices:\n"); + compute_and_print(&g_0, &weights_0, 1, 1); + + printf("One vertex:\n"); + compute_and_print(&g_1, &weights_0, 1, 1); + + printf("Two vertices:\n"); + compute_and_print(&g_2, &weights_0, 1, 1); + + printf("Two vertices, inf for unconnected pairs:\n"); + compute_and_print(&g_2, &weights_0, 1, 0); + + printf("Smallest bifurcating directed tree:\n"); + compute_and_print(&g_3, &weights_3, 1, 1); + + printf("Smallest bifurcating directed tree, inf for unconnected pairs:\n"); + compute_and_print(&g_3, &weights_3, 1, 0); + + printf("Graph with loops and multiple edges:\n"); + compute_and_print(&g_lm, &weights_lm, 1, 1); + + printf("Graph with loops and multiple edges, ignoring direction:\n"); + compute_and_print(&g_lm, &weights_lm, 0, 1); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Checking incorrect weight length error handling.\n"); + IGRAPH_ASSERT(igraph_average_path_length_dijkstra(&g_lm, &result, NULL, &weights_0, + 1, 1) == IGRAPH_EINVAL); + + printf("Checking negative weight error handling.\n"); + IGRAPH_ASSERT(igraph_average_path_length_dijkstra(&g_lm, &result, NULL, &weights_lm_neg, + 1, 1) == IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_2); + igraph_destroy(&g_3); + igraph_destroy(&g_lm); + igraph_vector_destroy(&weights_0); + igraph_vector_destroy(&weights_3); + igraph_vector_destroy(&weights_lm); + igraph_vector_destroy(&weights_lm_neg); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_average_path_length_dijkstra.out b/tests/unit/igraph_average_path_length_dijkstra.out new file mode 100644 index 0000000..53d5efd --- /dev/null +++ b/tests/unit/igraph_average_path_length_dijkstra.out @@ -0,0 +1,34 @@ +No vertices: +Result: NaN +Unconnected pairs: 0 + +One vertex: +Result: NaN +Unconnected pairs: 0 + +Two vertices: +Result: NaN +Unconnected pairs: 2 + +Two vertices, inf for unconnected pairs: +Result: Inf +Unconnected pairs: 2 + +Smallest bifurcating directed tree: +Result: 1 +Unconnected pairs: 4 + +Smallest bifurcating directed tree, inf for unconnected pairs: +Result: Inf +Unconnected pairs: 4 + +Graph with loops and multiple edges: +Result: 5 +Unconnected pairs: 19 + +Graph with loops and multiple edges, ignoring direction: +Result: 4.6 +Unconnected pairs: 10 + +Checking incorrect weight length error handling. +Checking negative weight error handling. diff --git a/tests/unit/igraph_barabasi_aging_game.c b/tests/unit/igraph_barabasi_aging_game.c new file mode 100644 index 0000000..74deb42 --- /dev/null +++ b/tests/unit/igraph_barabasi_aging_game.c @@ -0,0 +1,146 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t outseq; + igraph_rng_seed(igraph_rng_default(), 42); + igraph_bool_t tree; + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 0, /*m: edges_per_step*/ 1, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 1, /*aging_exp*/ 1, /*aging_bin*/ 1, + /*zero_deg_appeal*/ 0, /*zero_age_appeal*/ 0, /*deg_coef*/ 1.0, + /*age_coef */ 1.0, /*directed*/ 0) == IGRAPH_SUCCESS); + print_graph(&g); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + IGRAPH_ASSERT(!igraph_is_directed(&g)); + igraph_destroy(&g); + + printf("No edges:\n"); + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 0, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 1, /*aging_exp*/ 1, /*aging_bin*/ 1, + /*zero_deg_appeal*/ 0, /*zero_age_appeal*/ 0, /*deg_coef*/ 1.0, + /*age_coef */ 1.0, /*directed*/ 0) == IGRAPH_SUCCESS); + print_graph(&g); + IGRAPH_ASSERT(igraph_vcount(&g) == 5); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + IGRAPH_ASSERT(!igraph_is_directed(&g)); + igraph_destroy(&g); + + /*one edge per step makes a tree*/ + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 10, /*m: edges_per_step*/ 1, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 0.5, /*aging_exp*/ -0.5, /*aging_bin*/ 2, + /*zero_deg_appeal*/ 0.1, /*zero_age_appeal*/ 0, /*deg_coef*/ 0.1, + /*age_coef */ 0.1, /*directed*/ 1) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 9); + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_is_tree(&g, &tree, /* root*/ NULL, IGRAPH_IN); + IGRAPH_ASSERT(tree); + igraph_destroy(&g); + + printf("Prefer old vertices to make a star of triple edges:\n"); + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 3, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 1, + /*pa_exp*/ 0.0, /*aging_exp*/ 10, /*aging_bin*/ 6, + /*zero_deg_appeal*/ 1.0, /*zero_age_appeal*/ 0, /*deg_coef*/ 0.0, + /*age_coef */ 1, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Prefer new vertices to make a line of double edges:\n"); + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 2, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 0.0, /*aging_exp*/ -10, /*aging_bin*/ 6, + /*zero_deg_appeal*/ 0.1, /*zero_age_appeal*/ 0, /*deg_coef*/ 0.1, + /*age_coef */ 1, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Increasing thickness of the line using outseq:\n"); + igraph_vector_int_init_int(&outseq, 5, 1, 2, 3, 4, 5); + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 2, + /*outseq: edges per step as vector*/ &outseq, /*outpref*/ 0, + /*pa_exp*/ 0.1, /*aging_exp*/ -10, /*aging_bin*/ 6, + /*zero_deg_appeal*/ 0.1, /*zero_age_appeal*/ 0, /*deg_coef*/ 0.1, + /*age_coef */ 10, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + igraph_vector_int_destroy(&outseq); + + printf("Prefer more edges to make a star:\n"); + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 2, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 10.0, /*aging_exp*/ 0.0, /*aging_bin*/ 6, + /*zero_deg_appeal*/ 0.0, /*zero_age_appeal*/ 1.0, /*deg_coef*/ 1.0, + /*age_coef */ 0.0, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + /* Non-negative age coefficients are required*/ + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 2, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 1.0, /*aging_exp*/ 0.0, /*aging_bin*/ 6, + /*zero_deg_appeal*/ 1.0, /*zero_age_appeal*/ 1.0, /*deg_coef*/ 1.0, + /*age_coef */ -1.0, /*directed*/ 1) == IGRAPH_EINVAL); + + /* Non-negative degree coefficients are required*/ + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 2, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 1.0, /*aging_exp*/ 0.0, /*aging_bin*/ 6, + /*zero_deg_appeal*/ 1.0, /*zero_age_appeal*/ 1.0, /*deg_coef*/ -1.0, + /*age_coef */ 0.0, /*directed*/ 1) == IGRAPH_EINVAL); + + /* Non negative zero degree appeals are required*/ + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 2, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 1.0, /*aging_exp*/ 0.0, /*aging_bin*/ 6, + /*zero_deg_appeal*/ -1.0, /*zero_age_appeal*/ 1.0, /*deg_coef*/ 1.0, + /*age_coef */ 0.0, /*directed*/ 1) == IGRAPH_EINVAL); + + /* Non negative zero age appeals are required*/ + IGRAPH_ASSERT(igraph_barabasi_aging_game( + &g, /*nodes*/ 5, /*m: edges_per_step*/ 2, + /*outseq: edges per step as vector*/ NULL, /*outpref*/ 0, + /*pa_exp*/ 1.0, /*aging_exp*/ 0.0, /*aging_bin*/ 6, + /*zero_deg_appeal*/ 1.0, /*zero_age_appeal*/ -1.0, /*deg_coef*/ 1.0, + /*age_coef */ 0.0, /*directed*/ 1) == IGRAPH_EINVAL); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_barabasi_aging_game.out b/tests/unit/igraph_barabasi_aging_game.out new file mode 100644 index 0000000..f4acdff --- /dev/null +++ b/tests/unit/igraph_barabasi_aging_game.out @@ -0,0 +1,72 @@ +No vertices: +directed: false +vcount: 0 +edges: { +} +No edges: +directed: false +vcount: 5 +edges: { +} +Prefer old vertices to make a star of triple edges: +directed: true +vcount: 5 +edges: { +1 0 +1 0 +1 0 +2 0 +2 0 +2 0 +3 0 +3 0 +3 0 +4 0 +4 0 +4 0 +} +Prefer new vertices to make a line of double edges: +directed: true +vcount: 5 +edges: { +1 0 +1 0 +2 1 +2 1 +3 2 +3 2 +4 3 +4 3 +} +Increasing thickness of the line using outseq: +directed: true +vcount: 5 +edges: { +1 0 +1 0 +2 1 +2 1 +2 1 +3 2 +3 2 +3 2 +3 2 +4 3 +4 3 +4 3 +4 3 +4 3 +} +Prefer more edges to make a star: +directed: true +vcount: 5 +edges: { +1 0 +1 0 +2 0 +2 0 +3 0 +3 0 +4 0 +4 0 +} diff --git a/tests/unit/igraph_barabasi_game.c b/tests/unit/igraph_barabasi_game.c new file mode 100644 index 0000000..4966cae --- /dev/null +++ b/tests/unit/igraph_barabasi_game.c @@ -0,0 +1,70 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void run_test(igraph_integer_t n, igraph_bool_t directed, const igraph_vector_int_t *outseq, const igraph_t *start) { + igraph_barabasi_algorithm_t algos[] = { IGRAPH_BARABASI_BAG, IGRAPH_BARABASI_PSUMTREE, IGRAPH_BARABASI_PSUMTREE_MULTIPLE }; + igraph_t g; + igraph_bool_t simple; + + for (size_t i=0; i < sizeof(algos) / sizeof(algos[0]); i++) { + igraph_barabasi_algorithm_t algo = algos[i]; + igraph_barabasi_game(&g, n, 1, 2, outseq, true, 1, directed, algo, start); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + IGRAPH_ASSERT(igraph_is_directed(&g) == directed); + if (algo == IGRAPH_BARABASI_PSUMTREE) { + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + } + igraph_destroy(&g); + } +} + +int main(void) { + igraph_vector_int_t v; + igraph_t g; + + run_test(10, true, NULL, NULL); + run_test(10, false, NULL, NULL); + + igraph_ring(&g, 5, IGRAPH_DIRECTED, false, true); + run_test(13, true, NULL, &g); + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_EACH, NULL); + run_test(13, false, NULL, &g); + igraph_destroy(&g); + + igraph_vector_int_init_int(&v, 4, + 1, 2, 1, 3); + run_test(4, true, &v, NULL); + run_test(4, false, &v, NULL); + igraph_vector_int_destroy(&v); + + CHECK_ERROR(igraph_barabasi_game(&g, -10, /*power=*/ 1, 1, NULL, false, /*A=*/ 1, IGRAPH_UNDIRECTED, + IGRAPH_BARABASI_BAG, /*start_from=*/ NULL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_barabasi_game(&g, 10, /*power=*/ 1, -2, NULL, false, /*A=*/ 1, IGRAPH_UNDIRECTED, + IGRAPH_BARABASI_BAG, /*start_from=*/ NULL), IGRAPH_EINVAL); + igraph_vector_int_init(&v, 9); + CHECK_ERROR(igraph_barabasi_game(&g, 10, /*power=*/ 1, 0, &v, false, /*A=*/ 1, IGRAPH_UNDIRECTED, + IGRAPH_BARABASI_BAG, /*start_from=*/ NULL), IGRAPH_EINVAL); + igraph_vector_int_destroy(&v); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_betweenness.c b/tests/unit/igraph_betweenness.c new file mode 100644 index 0000000..6a55bbc --- /dev/null +++ b/tests/unit/igraph_betweenness.c @@ -0,0 +1,318 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2021 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 "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_vector_t bet, bet2, weights; + igraph_vector_int_t edges; + igraph_real_t cutoff = 0.0; + + igraph_integer_t nontriv[] = { 0, 19, 0, 16, 0, 20, 1, 19, 2, 5, 3, 7, 3, 8, + 4, 15, 4, 11, 5, 8, 5, 19, 6, 7, 6, 10, 6, 8, + 6, 9, 7, 20, 9, 10, 9, 20, 10, 19, + 11, 12, 11, 20, 12, 15, 13, 15, + 14, 18, 14, 16, 14, 17, 15, 16, 17, 18 + }; + + igraph_real_t nontriv_weights[] = { 0.5249, 1, 0.1934, 0.6274, 0.5249, + 0.0029, 0.3831, 0.05, 0.6274, 0.3831, + 0.5249, 0.0587, 0.0579, 0.0562, 0.0562, + 0.1934, 0.6274, 0.6274, 0.6274, 0.0418, + 0.6274, 0.3511, 0.3511, 0.1486, 1, 1, + 0.0711, 0.2409 + }; + + /*******************************************************/ + + printf("BA graph\n"); + printf("==========================================================\n"); + igraph_barabasi_game(/* graph= */ &g, + /* n= */ 1000, + /* power= */ 1, + /* m= */ 3, + /* outseq= */ 0, + /* outpref= */ 0, + /* A= */ 1, + /* directed= */ 0, + /* algo= */ IGRAPH_BARABASI_BAG, + /* start_from= */ 0); + + igraph_simplify(&g, /* remove_multiple= */ true, /* remove_loops= */ true, /*edge_comb=*/ NULL); + + igraph_vector_init(&bet, 0); + + igraph_betweenness_cutoff(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* weights= */ 0, + /* cutoff= */ 2); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + + printf("\nTree\n"); + printf("==========================================================\n"); + igraph_kary_tree(&g, 20000, 10, IGRAPH_TREE_UNDIRECTED); + + igraph_vector_init(&bet, 0); + + igraph_betweenness_cutoff(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* weights= */ 0, + /* cutoff= */ 3); + + printf("Max betweenness: %f\n", igraph_vector_max(&bet)); + + igraph_vector_init(&bet2, 0); + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1.0); + + igraph_betweenness_cutoff(/* graph= */ &g, + /* res= */ &bet2, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* weights= */ &weights, + /* cutoff= */ 3); + + IGRAPH_ASSERT(igraph_vector_all_e(&bet, &bet2)); + + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + printf("\nSmall undirected graph with multiple and loop edges\n"); + printf("==========================================================\n"); + igraph_small(&g, 4, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 1, 2, 1, 1, 2, 3, 3, 0, 3, 3, -1); + igraph_vector_init(&bet, 0); + igraph_betweenness(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* weights= */ 0); + print_vector(&bet); + igraph_vector_destroy(&bet); + igraph_destroy(&g); + + printf("\nNon-trivial weighted graph\n"); + printf("==========================================================\n"); + igraph_vector_int_view(&edges, nontriv, sizeof(nontriv) / sizeof(nontriv[0])); + igraph_create(&g, &edges, 0, /* directed= */ 0); + igraph_vector_view(&weights, nontriv_weights, + sizeof(nontriv_weights) / sizeof(nontriv_weights[0])); + igraph_vector_init(&bet, 0); + + igraph_betweenness(/*graph=*/ &g, /*res=*/ &bet, /*vids=*/ igraph_vss_all(), + /*directed=*/0, /*weights=*/ &weights); + + print_vector(&bet); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + + printf("\nCorner case cutoff 0.0\n"); + printf("==========================================================\n"); + igraph_kary_tree(&g, 20, 3, IGRAPH_TREE_UNDIRECTED); + + /* unweighted */ + igraph_vector_init(&bet, 0); + igraph_betweenness_cutoff(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* weights= */ 0, + /* cutoff= */ 0); + + igraph_vector_init(&bet2, 0); + igraph_betweenness_cutoff(/* graph= */ &g, + /* res= */ &bet2, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* weights= */ 0, + /* cutoff= */ -1); + + print_vector(&bet); + print_vector(&bet2); + + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + + /* weighted */ + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 2.0); + + igraph_vector_init(&bet, 0); + igraph_betweenness_cutoff(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* weights= */ &weights, + /* cutoff= */ 0); + + igraph_vector_init(&bet2, 0); + igraph_betweenness_cutoff(/* graph= */ &g, + /* res= */ &bet2, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* weights= */ &weights, + /* cutoff= */ -1); + + print_vector(&bet); + print_vector(&bet2); + + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + printf("\nSingle path graph cutoff\n"); + printf("==========================================================\n"); + igraph_small(&g, 5, IGRAPH_UNDIRECTED, + 0, 1, + 1, 2, + 2, 3, + 3, 4, -1); + igraph_vector_init(&bet, igraph_vcount(&g)); + igraph_vector_init(&bet2, igraph_vcount(&g)); + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1); + + for (cutoff = -1.0; cutoff < 5.0; cutoff += 1) + { + printf("Cutoff %.0f\n", cutoff); + printf("Unweighted\n"); + igraph_betweenness_cutoff(&g, &bet, + igraph_vss_all(), IGRAPH_UNDIRECTED, + /* weights */ NULL, + /* cutoff */ cutoff); + print_vector(&bet); + + printf("Weighted\n"); + igraph_betweenness_cutoff(&g, &bet2, + igraph_vss_all(), IGRAPH_UNDIRECTED, + /* weights */ &weights, + /* cutoff */ cutoff); + print_vector(&bet2); + printf("\n"); + + IGRAPH_ASSERT(igraph_vector_all_e(&bet, &bet2)); + } + + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + printf("\nCycle graph\n"); + printf("==========================================================\n"); + igraph_small(&g, 4, IGRAPH_UNDIRECTED, + 0, 1, + 0, 2, + 1, 3, + 2, 3, -1); + igraph_vector_init(&bet, igraph_vcount(&g)); + igraph_vector_init(&bet2, igraph_vcount(&g)); + igraph_vector_init(&weights, igraph_ecount(&g)); + VECTOR(weights)[0] = 1.01; + VECTOR(weights)[1] = 2; + VECTOR(weights)[2] = 0.99; + VECTOR(weights)[3] = 2; + + for (cutoff = -1.0; cutoff < 5.0; cutoff += 1) + { + printf("Cutoff %.0f\n", cutoff); + printf("Unweighted\n"); + igraph_betweenness_cutoff(&g, &bet, + igraph_vss_all(), IGRAPH_UNDIRECTED, + /* weights */ NULL, + /* cutoff */ cutoff); + print_vector(&bet); + + printf("Weighted\n"); + igraph_betweenness_cutoff(&g, &bet2, + igraph_vss_all(), IGRAPH_UNDIRECTED, + /* weights */ &weights, + /* cutoff */ cutoff); + print_vector(&bet2); + printf("\n"); + } + + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + printf("\nNull graph\n"); + printf("==========================================================\n"); + + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_vector_init(&bet, 3); /* purposefully larger than zero, as igraph_betweenness must resize it */ + igraph_betweenness(&g, &bet, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL); + print_vector(&bet); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + + printf("\nEmpty graph\n"); + printf("==========================================================\n"); + + igraph_empty(&g, 2, IGRAPH_UNDIRECTED); + igraph_vector_init(&bet, 0); + igraph_betweenness(&g, &bet, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL); + print_vector(&bet); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + + printf("\n37x37 grid graph\n"); + printf("==========================================================\n"); + + { + igraph_vector_int_t dims; + + igraph_vector_int_init(&dims, 2); + VECTOR(dims)[0] = 37; + VECTOR(dims)[1] = 37; + + igraph_square_lattice(&g, &dims, 1, IGRAPH_UNDIRECTED, /* mutual */ 0, /* periodic */ 0); + + igraph_vector_init(&bet, 0); + igraph_betweenness(&g, &bet, igraph_vss_all(), IGRAPH_UNDIRECTED, NULL); + printf("Max betweenness: %f\n", igraph_vector_max(&bet)); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + igraph_vector_int_destroy(&dims); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_betweenness.out b/tests/unit/igraph_betweenness.out new file mode 100644 index 0000000..e65888c --- /dev/null +++ b/tests/unit/igraph_betweenness.out @@ -0,0 +1,111 @@ +BA graph +========================================================== + +Tree +========================================================== +Max betweenness: 1155.000000 + +Small undirected graph with multiple and loop edges +========================================================== +( 0.333333 0.666667 0.666667 0.333333 ) + +Non-trivial weighted graph +========================================================== +( 20 0 0 0 0 19 80 85 32 0 10 75 70 0 36 81 60 0 19 19 86 ) + +Corner case cutoff 0.0 +========================================================== +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 104 122 51 51 51 51 18 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 0 ) +( 104 122 51 51 51 51 18 0 0 0 0 0 0 0 0 0 0 0 0 0 ) + +Single path graph cutoff +========================================================== +Cutoff -1 +Unweighted +( 0 3 4 3 0 ) +Weighted +( 0 3 4 3 0 ) + +Cutoff 0 +Unweighted +( 0 0 0 0 0 ) +Weighted +( 0 0 0 0 0 ) + +Cutoff 1 +Unweighted +( 0 0 0 0 0 ) +Weighted +( 0 0 0 0 0 ) + +Cutoff 2 +Unweighted +( 0 1 1 1 0 ) +Weighted +( 0 1 1 1 0 ) + +Cutoff 3 +Unweighted +( 0 2 3 2 0 ) +Weighted +( 0 2 3 2 0 ) + +Cutoff 4 +Unweighted +( 0 3 4 3 0 ) +Weighted +( 0 3 4 3 0 ) + + +Cycle graph +========================================================== +Cutoff -1 +Unweighted +( 0.5 0.5 0.5 0.5 ) +Weighted +( 0 1 0 1 ) + +Cutoff 0 +Unweighted +( 0 0 0 0 ) +Weighted +( 0 0 0 0 ) + +Cutoff 1 +Unweighted +( 0 0 0 0 ) +Weighted +( 0 0 0 0 ) + +Cutoff 2 +Unweighted +( 0.5 0.5 0.5 0.5 ) +Weighted +( 0 1 0 0 ) + +Cutoff 3 +Unweighted +( 0.5 0.5 0.5 0.5 ) +Weighted +( 0 1 0 1 ) + +Cutoff 4 +Unweighted +( 0.5 0.5 0.5 0.5 ) +Weighted +( 0 1 0 1 ) + + +Null graph +========================================================== +( ) + +Empty graph +========================================================== +( 0 0 ) + +37x37 grid graph +========================================================== +Max betweenness: 36094.891693 diff --git a/tests/unit/igraph_betweenness_subset.c b/tests/unit/igraph_betweenness_subset.c new file mode 100644 index 0000000..0ee4878 --- /dev/null +++ b/tests/unit/igraph_betweenness_subset.c @@ -0,0 +1,362 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + + +int main(void) { + igraph_t g; + igraph_vector_int_t edges; + igraph_vector_t bet, bet2, weights; + igraph_vector_int_t node_vec, source_vec, target_vec; + igraph_vs_t vs, vs_source, vs_target; + igraph_integer_t i, n; + + igraph_integer_t nontriv[] = { 0, 19, 0, 16, 0, 20, 1, 19, 2, 5, 3, 7, 3, 8, + 4, 15, 4, 11, 5, 8, 5, 19, 6, 7, 6, 10, 6, 8, + 6, 9, 7, 20, 9, 10, 9, 20, 10, 19, + 11, 12, 11, 20, 12, 15, 13, 15, + 14, 18, 14, 16, 14, 17, 15, 16, 17, 18 + }; + + igraph_real_t nontriv_weights[] = { 0.5249, 1, 0.1934, 0.6274, 0.5249, + 0.0029, 0.3831, 0.05, 0.6274, 0.3831, + 0.5249, 0.0587, 0.0579, 0.0562, 0.0562, + 0.1934, 0.6274, 0.6274, 0.6274, 0.0418, + 0.6274, 0.3511, 0.3511, 0.1486, 1, 1, + 0.0711, 0.2409 + }; + + printf("BA graph (smoke test)\n"); + printf("==========================================================\n"); + igraph_barabasi_game(/* graph= */ &g, + /* n= */ 1000, + /* power= */ 1, + /* m= */ 3, + /* outseq= */ 0, + /* outpref= */ 0, + /* A= */ 1, + /* directed= */ IGRAPH_UNDIRECTED, + /* algo= */ IGRAPH_BARABASI_BAG, + /* start_from= */ 0); + + igraph_simplify(&g, /* remove_multiple= */ true, /* remove_loops= */ true, /*edge_comb=*/ NULL); + + igraph_vector_init(&bet, 0); + igraph_vs_range(&vs_source, 0, 501); + igraph_vs_range(&vs_target, 500, 1000); + + igraph_betweenness_subset(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ vs_target, + /* weights= */ NULL); + + igraph_vector_destroy(&bet); + igraph_vs_destroy(&vs_source); + igraph_vs_destroy(&vs_target); + igraph_destroy(&g); + + printf("\nTree\n"); + printf("==========================================================\n"); + igraph_kary_tree(&g, 11111, 10, IGRAPH_TREE_UNDIRECTED); + + /* We are including the rightmost 200 vertices from the lowermost layer + * (layer 5) of the tree. These have 20 parents in layer 4, 2 grandparents + * in layer 3, and a single grand-grandparent in layer 2. None of the + * shortest paths we consider should therefore pass through the root, the + * first 9 vertices of layer 2, the first 98 vertices of layer 3 or the + * first 980 vertices of layer 4; the betweenness of these vertices should + * all be zeros. + * + * Also, the betweenness of the common grand-grandparent in layer 2 is easy + * to calculate as any shortest path going between grand-grandchildren + * reachable via its left child and via its right child should pass through + * it. This gives us a betweenness of 100 * 100 = 10000 for this node. + * Similar calculations reveal that the betweenness of its two children + * will be 100 * 100 + 100 * 90 / 2 = 14500 (the same paths as above plus any + * path that goes from any of its subtree to any other subtree). These are + * also the maximal betweennesses. The betwennesses in the remaining layers + * are 1945 and 199 if the vertex being considered is a descendant of the + * common grand-grandparent in layer 2, and zero otherwise. */ + + igraph_vs_range(&vs_source, 10911, 11111); + igraph_vs_range(&vs_target, 10911, 11111); + igraph_vector_init(&bet, 0); + + igraph_betweenness_subset( + /* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ vs_target, + /* weights= */ NULL + ); + + printf("Max betweenness: %f\n", igraph_vector_max(&bet)); + + n = igraph_vcount(&g); + for (i = 0; i < n; i++) { + igraph_integer_t expected; + + if (i >= 10911) { + /* layer 5, in the subset. There are 199 shortest paths that + * contain these nodes, but the nodes are the _endpoints_ of the + * paths so they don't count in the betweenness score */ + expected = 0; + } else if (i >= 1111) { + /* layer 5, not in the subset */ + expected = 0; + } else if (i >= 1091) { + /* layer 4, rightmost 20 nodes */ + expected = 1945; + } else if (i >= 111) { + /* layer 4, remaining nodes */ + expected = 0; + } else if (i >= 109) { + /* layer 3, rightmost 2 nodes */ + expected = 14500; + } else if (i >= 11) { + /* layer 3, remaining nodes */ + expected = 0; + } else if (i == 10) { + /* layer 2, rightmost node */ + expected = 10000; + } else { + expected = 0; + } + + if (VECTOR(bet)[i] != expected) { + printf( + "Invalid betweenness for vertex %" IGRAPH_PRId ", expected %" IGRAPH_PRId ", got %" IGRAPH_PRId "\n", + i, expected, (igraph_integer_t) VECTOR(bet)[i] + ); + break; + } + } + + igraph_vector_init(&bet2, 0); + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1.0); + + igraph_betweenness_subset(/* graph= */ &g, + /* res= */ &bet2, + /* vids= */ igraph_vss_all(), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ vs_target, + /* weights= */ &weights); + + IGRAPH_ASSERT(igraph_vector_all_e(&bet, &bet2)); + + igraph_vector_destroy(&weights); + igraph_vs_destroy(&vs_source); + igraph_vs_destroy(&vs_target); + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + igraph_destroy(&g); + + printf("\nNon-trivial weighted graph using subset algorithm\n"); + printf("==========================================================\n"); + igraph_vector_int_view(&edges, nontriv, sizeof(nontriv) / sizeof(nontriv[0])); + igraph_create(&g, &edges, 0, /* directed= */ IGRAPH_UNDIRECTED); + igraph_vector_view(&weights, nontriv_weights, + sizeof(nontriv_weights) / sizeof(nontriv_weights[0])); + + igraph_vector_init(&bet, 0); + igraph_betweenness_subset(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ igraph_vss_all(), + /* target = */ igraph_vss_all(), + /* weights= */ &weights); + + print_vector(&bet); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + + printf("\nSingle path graph of subset\n"); + printf("==========================================================\n"); + igraph_small(&g, 5, IGRAPH_UNDIRECTED, + 0, 1, + 1, 2, + 2, 3, + 3, 4, -1); + igraph_vector_init(&bet, igraph_vcount(&g)); + igraph_vector_init(&bet2, igraph_vcount(&g)); + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1); + + for (i = 0; i < 5; i++) + { + igraph_vector_int_init_range(&node_vec, 0, 5); + igraph_vector_int_remove(&node_vec, i); + igraph_vs_vector(&vs, &node_vec); + igraph_vector_int_init_range(&source_vec, 0, 5); + igraph_vector_int_remove(&source_vec, i); + igraph_vs_vector(&vs_source, &source_vec); + printf("subset without %" IGRAPH_PRId "\n", i); + printf("Unweighted\n"); + igraph_betweenness_subset(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ vs, + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ igraph_vss_all(), + /* weights= */ NULL); + print_vector(&bet); + + printf("Weighted\n"); + igraph_betweenness_subset(/* graph= */ &g, + /* res= */ &bet2, + /* vids= */ vs, + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ igraph_vss_all(), + /* weights */ &weights); + print_vector(&bet2); + printf("\n"); + + IGRAPH_ASSERT(igraph_vector_all_e(&bet, &bet2)); + igraph_vs_destroy(&vs); + igraph_vector_int_destroy(&node_vec); + igraph_vs_destroy(&vs_source); + igraph_vector_int_destroy(&source_vec); + } + + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + printf("\nCycle graph subset\n"); + printf("==========================================================\n"); + igraph_small(&g, 4, IGRAPH_UNDIRECTED, + 0, 1, + 0, 2, + 1, 3, + 2, 3, -1); + igraph_vector_init(&bet, igraph_vcount(&g)); + igraph_vector_init(&bet2, igraph_vcount(&g)); + igraph_vector_init(&weights, igraph_ecount(&g)); + VECTOR(weights)[0] = 1.01; + VECTOR(weights)[1] = 2; + VECTOR(weights)[2] = 0.99; + VECTOR(weights)[3] = 2; + + for (i = 0; i < 3; i++) + { + igraph_vector_int_init_range(&target_vec, 0, 4); + igraph_vector_int_remove(&target_vec, i); + igraph_vs_vector(&vs_target, &target_vec); + printf("subset without %" IGRAPH_PRId "\n", i); + printf("Unweighted\n"); + igraph_betweenness_subset(/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* sources = */ igraph_vss_all(), + /* target = */ vs_target, + /* weights */ NULL); + print_vector(&bet); + + printf("Weighted\n"); + igraph_betweenness_subset(/* graph= */ &g, + /* res= */ &bet2, + /* vids= */ igraph_vss_all(), + /* directed = */ 0, + /* sources = */ igraph_vss_all(), + /* target = */ vs_target, + /* weights */ &weights); + print_vector(&bet2); + printf("\n"); + + igraph_vs_destroy(&vs_target); + igraph_vector_int_destroy(&target_vec); + } + + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + printf("\nEmpty graph\n"); + printf("==========================================================\n"); + igraph_empty(&g, 2, IGRAPH_UNDIRECTED); + igraph_vector_init(&bet, 0); + igraph_betweenness_subset (/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ igraph_vss_all(), + /* target = */ igraph_vss_all(), + /* weights= */ NULL); + + print_vector(&bet); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + + printf("\n37x37 grid graph\n"); + printf("==========================================================\n"); + + { + igraph_vector_int_t dims; + + igraph_vector_int_init(&dims, 2); + VECTOR(dims)[0] = 37; + VECTOR(dims)[1] = 37; + + igraph_square_lattice(&g, &dims, 1, IGRAPH_UNDIRECTED, /* mutual */ 0, /* periodic */ 0); + + igraph_vector_init(&bet, 0); + igraph_vector_int_init_range(&target_vec, 0, igraph_vcount(&g)); + igraph_vector_int_remove(&target_vec, 0); + igraph_vs_vector(&vs_target, &target_vec); + igraph_vector_int_init_range(&source_vec, 0, igraph_vcount(&g)); + igraph_vector_int_remove(&source_vec, 0); + igraph_vs_vector(&vs_source, &source_vec); + + igraph_betweenness_subset (/* graph= */ &g, + /* res= */ &bet, + /* vids= */ igraph_vss_all(), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ vs_target, + /* weights= */ NULL);; + printf("Max betweenness: %f\n", igraph_vector_max(&bet)); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + igraph_vector_int_destroy(&dims); + igraph_vs_destroy(&vs_target); + igraph_vector_int_destroy(&target_vec); + igraph_vs_destroy(&vs_source); + igraph_vector_int_destroy(&source_vec); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_betweenness_subset.out b/tests/unit/igraph_betweenness_subset.out new file mode 100644 index 0000000..2734ddd --- /dev/null +++ b/tests/unit/igraph_betweenness_subset.out @@ -0,0 +1,72 @@ +BA graph (smoke test) +========================================================== + +Tree +========================================================== +Max betweenness: 14500.000000 + +Non-trivial weighted graph using subset algorithm +========================================================== +( 20 0 0 0 0 19 80 85 32 0 10 75 70 0 36 81 60 0 19 19 86 ) + +Single path graph of subset +========================================================== +subset without 0 +Unweighted +( 1.5 3 2.5 0 ) +Weighted +( 1.5 3 2.5 0 ) + +subset without 1 +Unweighted +( 0 3 2.5 0 ) +Weighted +( 0 3 2.5 0 ) + +subset without 2 +Unweighted +( 0 2.5 2.5 0 ) +Weighted +( 0 2.5 2.5 0 ) + +subset without 3 +Unweighted +( 0 2.5 3 0 ) +Weighted +( 0 2.5 3 0 ) + +subset without 4 +Unweighted +( 0 2.5 3 1.5 ) +Weighted +( 0 2.5 3 1.5 ) + + +Cycle graph subset +========================================================== +subset without 0 +Unweighted +( 0.5 0.25 0.25 0.5 ) +Weighted +( 0 0.5 0 1 ) + +subset without 1 +Unweighted +( 0.25 0.5 0.5 0.25 ) +Weighted +( 0 1 0 0.5 ) + +subset without 2 +Unweighted +( 0.25 0.5 0.5 0.25 ) +Weighted +( 0 1 0 0.5 ) + + +Empty graph +========================================================== +( 0 0 ) + +37x37 grid graph +========================================================== +Max betweenness: 36050.526458 diff --git a/tests/unit/igraph_biadjacency.c b/tests/unit/igraph_biadjacency.c new file mode 100644 index 0000000..d42f01d --- /dev/null +++ b/tests/unit/igraph_biadjacency.c @@ -0,0 +1,99 @@ +/* + 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, see . + */ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_matrix_t *incidence, igraph_bool_t directed, igraph_neimode_t mode, igraph_bool_t multiple) { + igraph_t g; + igraph_vector_bool_t types; + igraph_vector_bool_init(&types, 0); + + igraph_biadjacency(&g, &types, incidence, directed, mode, multiple); + + print_graph_canon(&g); + printf("types: "); + + igraph_vector_bool_print(&types); + igraph_matrix_destroy(incidence); + igraph_destroy(&g); + igraph_vector_bool_destroy(&types); +} + +int main(void) { + igraph_t g; + igraph_vector_bool_t types; + igraph_vector_bool_init(&types, 0); + igraph_matrix_t incidence; + + + printf("Incidence matrix with no rows and no columns:\n"); + igraph_matrix_init(&incidence, 0, 0); + print_and_destroy(&incidence, IGRAPH_DIRECTED, IGRAPH_ALL, 0); + + { + printf("\nIncidence matrix for two vertices:\n"); + int elem[] = {5}; + matrix_init_int_row_major(&incidence, 1, 1, elem); + print_and_destroy(&incidence, IGRAPH_DIRECTED, IGRAPH_ALL, 0); + } + + { + printf("\nIncidence matrix for two vertices, multiple = true:\n"); + int elem[] = {5}; + matrix_init_int_row_major(&incidence, 1, 1, elem); + print_and_destroy(&incidence, IGRAPH_DIRECTED, IGRAPH_ALL, 1); + } + + { + printf("\nIncidence matrix for five vertices:\n"); + int elem[] = {0, 1, 2, 3, 4, 5}; + matrix_init_int_row_major(&incidence, 2, 3, elem); + print_and_destroy(&incidence, IGRAPH_DIRECTED, IGRAPH_ALL, 1); + } + + { + printf("\nSame graph, IGRAPH_OUT:\n"); + int elem[] = {0, 1, 2, 3, 4, 5}; + matrix_init_int_row_major(&incidence, 2, 3, elem); + print_and_destroy(&incidence, IGRAPH_DIRECTED, IGRAPH_OUT, 1); + } + + { + printf("\nSame graph, undirected:\n"); + int elem[] = {0, 1, 2, 3, 4, 5}; + matrix_init_int_row_major(&incidence, 2, 3, elem); + print_and_destroy(&incidence, IGRAPH_UNDIRECTED, IGRAPH_OUT, 1); + } + + VERIFY_FINALLY_STACK(); + + { + printf("\nCheck error for negative element.\n"); + int elem[] = {-5}; + matrix_init_int_row_major(&incidence, 1, 1, elem); + CHECK_ERROR(igraph_biadjacency(&g, &types, &incidence, IGRAPH_DIRECTED, + IGRAPH_ALL, 1), IGRAPH_EINVAL); + igraph_matrix_destroy(&incidence); + } + + igraph_vector_bool_destroy(&types); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_biadjacency.out b/tests/unit/igraph_biadjacency.out new file mode 100644 index 0000000..690e5a7 --- /dev/null +++ b/tests/unit/igraph_biadjacency.out @@ -0,0 +1,115 @@ +Incidence matrix with no rows and no columns: +directed: true +vcount: 0 +edges: { +} +types: + +Incidence matrix for two vertices: +directed: true +vcount: 2 +edges: { +0 1 +1 0 +} +types: 0 1 + +Incidence matrix for two vertices, multiple = true: +directed: true +vcount: 2 +edges: { +0 1 +0 1 +0 1 +0 1 +0 1 +1 0 +1 0 +1 0 +1 0 +1 0 +} +types: 0 1 + +Incidence matrix for five vertices: +directed: true +vcount: 5 +edges: { +0 3 +0 4 +0 4 +1 2 +1 2 +1 2 +1 3 +1 3 +1 3 +1 3 +1 4 +1 4 +1 4 +1 4 +1 4 +2 1 +2 1 +2 1 +3 0 +3 1 +3 1 +3 1 +3 1 +4 0 +4 0 +4 1 +4 1 +4 1 +4 1 +4 1 +} +types: 0 0 1 1 1 + +Same graph, IGRAPH_OUT: +directed: true +vcount: 5 +edges: { +0 3 +0 4 +0 4 +1 2 +1 2 +1 2 +1 3 +1 3 +1 3 +1 3 +1 4 +1 4 +1 4 +1 4 +1 4 +} +types: 0 0 1 1 1 + +Same graph, undirected: +directed: false +vcount: 5 +edges: { +0 3 +0 4 +0 4 +1 2 +1 2 +1 2 +1 3 +1 3 +1 3 +1 3 +1 4 +1 4 +1 4 +1 4 +1 4 +} +types: 0 0 1 1 1 + +Check error for negative element. diff --git a/tests/unit/igraph_biconnected_components.c b/tests/unit/igraph_biconnected_components.c new file mode 100644 index 0000000..b8457b5 --- /dev/null +++ b/tests/unit/igraph_biconnected_components.c @@ -0,0 +1,63 @@ +/* + IGraph library. + Copyright (C) 2006-2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_list_t result; + igraph_vector_int_t ap; + igraph_integer_t no; + + igraph_vector_int_list_init(&result, 0); + igraph_vector_int_init(&ap, 0); + igraph_small(&g, 10 /* extra isolated vertex */, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,3, 3,0, + 2,4, 4,5, 5,2, + 5,6, + 7,8, + -1); + + printf("Vertices in biconnected components:\n"); + igraph_biconnected_components(&g, &no, NULL, NULL, /* components */ &result, NULL); + IGRAPH_ASSERT(no == igraph_vector_int_list_size(&result)); + print_vector_int_list(&result); + + printf("Edges in biconnected components:\n"); + igraph_biconnected_components(&g, &no, NULL, /* component_edges */ &result, NULL, NULL); + IGRAPH_ASSERT(no == igraph_vector_int_list_size(&result)); + print_vector_int_list(&result); + + printf("Edges in biconnected component spanning trees:\n"); + igraph_biconnected_components(&g, &no, /* tree_edges */ &result, NULL, NULL, &ap); + IGRAPH_ASSERT(no == igraph_vector_int_list_size(&result)); + print_vector_int_list(&result); + + printf("Articulation points:\n"); + print_vector_int(&ap); + + igraph_destroy(&g); + igraph_vector_int_destroy(&ap); + igraph_vector_int_list_destroy(&result); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_biconnected_components.out b/tests/unit/igraph_biconnected_components.out new file mode 100644 index 0000000..38ae9e9 --- /dev/null +++ b/tests/unit/igraph_biconnected_components.out @@ -0,0 +1,23 @@ +Vertices in biconnected components: +{ + 0: ( 6 5 ) + 1: ( 5 4 2 ) + 2: ( 3 2 1 0 ) + 3: ( 8 7 ) +} +Edges in biconnected components: +{ + 0: ( 7 ) + 1: ( 6 5 4 ) + 2: ( 3 2 1 0 ) + 3: ( 8 ) +} +Edges in biconnected component spanning trees: +{ + 0: ( 7 ) + 1: ( 5 4 ) + 2: ( 2 1 0 ) + 3: ( 8 ) +} +Articulation points: +( 5 2 ) diff --git a/tests/unit/igraph_bipartite_create.c b/tests/unit/igraph_bipartite_create.c new file mode 100644 index 0000000..dd1a20f --- /dev/null +++ b/tests/unit/igraph_bipartite_create.c @@ -0,0 +1,40 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_integer_t edges3[] = {0, 1, 1, 2, 3, 4, 5, 6, 6, 5, 2, 4, 1, 6, 0, 3 }; + igraph_vector_int_t edges; + igraph_vector_bool_t types; + igraph_t g; + igraph_integer_t i; + + igraph_vector_int_view(&edges, edges3, sizeof(edges3) / sizeof(edges3[0])); + igraph_vector_bool_init(&types, igraph_vector_int_max(&edges) + 1); + for (i = 0; i < igraph_vector_bool_size(&types); i++) { + VECTOR(types)[i] = i % 2; + } + /* Not a bipartite graph, vertices 2 and 4 are connected */ + CHECK_ERROR(igraph_create_bipartite(&g, &types, &edges, /*directed=*/ 1), IGRAPH_EINVAL); + + igraph_vector_bool_destroy(&types); + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_bipartite_game.c b/tests/unit/igraph_bipartite_game.c new file mode 100644 index 0000000..cdcaf4c --- /dev/null +++ b/tests/unit/igraph_bipartite_game.c @@ -0,0 +1,165 @@ + +#include + +#include "test_utilities.h" + +void check_partitions(const igraph_t *g, const igraph_vector_bool_t *types, igraph_neimode_t mode) { + igraph_integer_t m = igraph_ecount(g); + + for (igraph_integer_t i=0; i < m; i++) { + switch (mode) { + case IGRAPH_OUT: + IGRAPH_ASSERT(VECTOR(*types)[IGRAPH_FROM(g, i)] == false); + IGRAPH_ASSERT(VECTOR(*types)[IGRAPH_TO(g, i)] == true); + break; + case IGRAPH_IN: + IGRAPH_ASSERT(VECTOR(*types)[IGRAPH_FROM(g, i)] == true); + IGRAPH_ASSERT(VECTOR(*types)[IGRAPH_TO(g, i)] == false); + break; + case IGRAPH_ALL: + IGRAPH_ASSERT(VECTOR(*types)[IGRAPH_FROM(g, i)] != VECTOR(*types)[IGRAPH_TO(g, i)]); + break; + } + } +} + +int main(void) { + igraph_t graph; + igraph_vector_bool_t types; + igraph_bool_t bipartite; + igraph_integer_t n1, n2, m; + igraph_neimode_t modes[] = { IGRAPH_OUT, IGRAPH_IN, IGRAPH_ALL }; + + igraph_rng_seed(igraph_rng_default(), 947); + + igraph_vector_bool_init(&types, 0); + + /* G(n,m) */ + + /* undirected */ + + n1 = 10; n2 = 20; m = 80; + igraph_bipartite_game_gnm(&graph, &types, + n1, n2, m, + IGRAPH_UNDIRECTED, IGRAPH_ALL); + + igraph_is_bipartite(&graph, &bipartite, NULL); + + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(! igraph_is_directed(&graph)); + IGRAPH_ASSERT(igraph_vcount(&graph) == n1 + n2); + IGRAPH_ASSERT(igraph_ecount(&graph) == m); + + check_partitions(&graph, &types, IGRAPH_ALL); + + igraph_destroy(&graph); + + /* complete graph */ + + n1 = 5; n2 = 6; m = 30; + igraph_bipartite_game_gnm(&graph, &types, + n1, n2, m, + IGRAPH_UNDIRECTED, IGRAPH_ALL); + + igraph_is_bipartite(&graph, &bipartite, NULL); + + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(! igraph_is_directed(&graph)); + IGRAPH_ASSERT(igraph_vcount(&graph) == n1 + n2); + IGRAPH_ASSERT(igraph_ecount(&graph) == m); + + check_partitions(&graph, &types, IGRAPH_ALL); + + igraph_destroy(&graph); + + /* empty graph */ + + n1 = 5; n2 = 6; m = 0; + igraph_bipartite_game_gnm(&graph, &types, + n1, n2, m, + IGRAPH_UNDIRECTED, IGRAPH_ALL); + + igraph_is_bipartite(&graph, &bipartite, NULL); + + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(! igraph_is_directed(&graph)); + IGRAPH_ASSERT(igraph_vcount(&graph) == n1 + n2); + IGRAPH_ASSERT(igraph_ecount(&graph) == m); + + check_partitions(&graph, &types, IGRAPH_ALL); + + igraph_destroy(&graph); + + /* directed */ + + for (int i=0; i < sizeof(modes) / sizeof(modes[0]); i++) { + igraph_bipartite_game_gnm(&graph, &types, + n1, n2, m, + IGRAPH_DIRECTED, modes[i]); + + igraph_is_bipartite(&graph, &bipartite, NULL); + + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(igraph_is_directed(&graph)); + IGRAPH_ASSERT(igraph_vcount(&graph) == n1 + n2); + IGRAPH_ASSERT(igraph_ecount(&graph) == m); + + check_partitions(&graph, &types, modes[i]); + + igraph_destroy(&graph); + } + + /* G(n,p) */ + + /* undirected */ + + n1 = 8; n2 = 15; + igraph_bipartite_game_gnp(&graph, &types, + n1, n2, 0.8, + IGRAPH_UNDIRECTED, IGRAPH_ALL); + + igraph_is_bipartite(&graph, &bipartite, NULL); + + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(! igraph_is_directed(&graph)); + IGRAPH_ASSERT(igraph_vcount(&graph) == n1 + n2); + IGRAPH_ASSERT(igraph_ecount(&graph) > 0); /* 0 is exceedingly unlikely */ + + igraph_destroy(&graph); + + /* directed */ + + for (int i=0; i < sizeof(modes) / sizeof(modes[0]); i++) { + igraph_bipartite_game_gnp(&graph, &types, + n1, n2, 0.8, + IGRAPH_DIRECTED, modes[i]); + + igraph_is_bipartite(&graph, &bipartite, NULL); + + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(igraph_is_directed(&graph)); + IGRAPH_ASSERT(igraph_vcount(&graph) == n1 + n2); + IGRAPH_ASSERT(igraph_ecount(&graph) > 0); /* 0 is exceedingly unlikely */ + + check_partitions(&graph, &types, modes[i]); + + igraph_destroy(&graph); + } + + igraph_vector_bool_destroy(&types); + + VERIFY_FINALLY_STACK(); + + CHECK_ERROR(igraph_bipartite_game_gnm(&graph, NULL, 0, 10, 20, IGRAPH_DIRECTED, IGRAPH_ALL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_bipartite_game_gnm(&graph, NULL, 10, 10, 201, IGRAPH_DIRECTED, IGRAPH_ALL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_bipartite_game_gnm(&graph, NULL, -1, 10, 20, IGRAPH_DIRECTED, IGRAPH_ALL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_bipartite_game_gnm(&graph, NULL, 10, -1, 20, IGRAPH_DIRECTED, IGRAPH_ALL), IGRAPH_EINVAL); + + CHECK_ERROR(igraph_bipartite_game_gnp(&graph, NULL, -1, 10, 0.1, IGRAPH_UNDIRECTED, IGRAPH_ALL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_bipartite_game_gnp(&graph, NULL, 10, -1, 0.9, IGRAPH_UNDIRECTED, IGRAPH_ALL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_bipartite_game_gnp(&graph, NULL, 10, 10, 1.1, IGRAPH_UNDIRECTED, IGRAPH_ALL), IGRAPH_EINVAL); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_bipartite_projection.c b/tests/unit/igraph_bipartite_projection.c new file mode 100644 index 0000000..fa1ac47 --- /dev/null +++ b/tests/unit/igraph_bipartite_projection.c @@ -0,0 +1,183 @@ +/* -*- 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 +#include +#include "test_utilities.h" + +int check_projection(const igraph_t *graph, + const igraph_vector_bool_t *types, + const igraph_t *proj1, + const igraph_t *proj2) { + igraph_integer_t vcount1, ecount1, vcount2, ecount2; + igraph_bipartite_projection_size(graph, types, &vcount1, &ecount1, + &vcount2, &ecount2); + if (proj1 && igraph_vcount(proj1) != vcount1) { + exit(10); + } + if (proj1 && igraph_ecount(proj1) != ecount1) { + exit(11); + } + if (proj2 && igraph_vcount(proj2) != vcount2) { + exit(12); + } + if (proj2 && igraph_ecount(proj2) != ecount2) { + exit(13); + } + return 0; +} + +int main(void) { + + igraph_t g, p1, p2, full, ring; + igraph_vector_bool_t types; + igraph_bool_t iso; + igraph_integer_t i, m2 = 0, w, f, t; + igraph_vector_int_t mult1, mult2; + + /*******************************************************/ + /* Full bipartite graph -> full graphs */ + /*******************************************************/ + + igraph_vector_bool_init(&types, 0); + igraph_full_bipartite(&g, &types, 5, 3, /*directed=*/ 0, + /*mode=*/ IGRAPH_ALL); + + /* Get both projections */ + igraph_bipartite_projection(&g, &types, &p1, &p2, 0, 0, /*probe1=*/ -1); + check_projection(&g, &types, &p1, &p2); + + /* Check first projection */ + igraph_full(&full, igraph_vcount(&p1), /*directed=*/0, /*loops=*/0); + igraph_isomorphic_bliss(&p1, &full, 0, 0, &iso, 0, 0, + IGRAPH_BLISS_FM, 0, 0); + if (!iso) { + return 1; + } + igraph_destroy(&full); + + /* Check second projection */ + igraph_full(&full, igraph_vcount(&p2), /*directed=*/0, /*loops=*/0); + igraph_isomorphic_bliss(&p2, &full, 0, 0, &iso, 0, 0, + IGRAPH_BLISS_FM, 0, 0); + if (!iso) { + return 2; + } + igraph_destroy(&full); + + igraph_destroy(&p1); + igraph_destroy(&p2); + igraph_destroy(&g); + igraph_vector_bool_destroy(&types); + + /*******************************************************/ + /* More sophisticated test */ + /*******************************************************/ + + igraph_ring(&g, 100, /*directed=*/ 1, /*mutual=*/ 1, + /*circular=*/ 1); + igraph_vector_bool_init(&types, igraph_vcount(&g)); + for (i = 0; i < igraph_vector_bool_size(&types); i++) { + VECTOR(types)[i] = i % 2 ? 0 : 1; + } + + /* Get both projections */ + igraph_bipartite_projection(&g, &types, &p1, &p2, 0, 0, /*probe1=*/ -1); + check_projection(&g, &types, &p1, &p2); + + /* Check first projection */ + igraph_ring(&ring, igraph_vcount(&g) / 2, /*directed=*/ 0, + /*mutual=*/ 0, /*circular=*/ 1); + igraph_isomorphic_bliss(&p1, &ring, 0, 0, &iso, 0, 0, + IGRAPH_BLISS_FM, 0, 0); + if (!iso) { + return 1; + } + + /* Check second projection */ + igraph_isomorphic_bliss(&p2, &ring, 0, 0, &iso, 0, 0, + IGRAPH_BLISS_FM, 0, 0); + if (!iso) { + return 2; + } + igraph_destroy(&ring); + + igraph_destroy(&p1); + igraph_destroy(&p2); + igraph_destroy(&g); + igraph_vector_bool_destroy(&types); + + /*******************************************************/ + /* Multiplicity test */ + /*******************************************************/ + + igraph_small(&g, 10, IGRAPH_UNDIRECTED, + 0, 8, 1, 8, 2, 8, 3, 8, 4, 8, 4, 9, 5, 9, 6, 9, 7, 9, 0, 9, + -1); + igraph_vector_bool_init(&types, igraph_vcount(&g)); + igraph_vector_bool_fill(&types, true); + VECTOR(types)[8] = VECTOR(types)[9] = 0; + + igraph_vector_int_init(&mult1, 0); + igraph_vector_int_init(&mult2, 0); + igraph_bipartite_projection(&g, &types, &p1, &p2, &mult1, &mult2, + /*probe=*/ -1); + check_projection(&g, &types, &p1, &p2); + + if (igraph_vector_int_size(&mult1) != igraph_ecount(&p1)) { + return 21; + } + if (igraph_vector_int_size(&mult2) != igraph_ecount(&p2)) { + return 22; + } + if (VECTOR(mult1)[0] != 2) { + return 23; + } + for (i = 0; i < igraph_vector_int_size(&mult2); i++) { + if (VECTOR(mult2)[i] != 1 && VECTOR(mult2)[i] != 2) { + return 24; + } + if (VECTOR(mult2)[i] == 2) { + m2++; + w = i; + } + } + if (m2 != 1) { + return 25; + } + f = IGRAPH_FROM(&p2, w); + t = IGRAPH_TO(&p2, w); + if (fmin(f, t) != 0 || fmax(f, t) != 4) { + return 26; + } + + igraph_vector_int_destroy(&mult1); + igraph_vector_int_destroy(&mult2); + igraph_destroy(&p1); + igraph_destroy(&p2); + igraph_destroy(&g); + igraph_vector_bool_destroy(&types); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_blas_dgemm.c b/tests/unit/igraph_blas_dgemm.c new file mode 100644 index 0000000..efa382b --- /dev/null +++ b/tests/unit/igraph_blas_dgemm.c @@ -0,0 +1,81 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_matrix_t a, b, c, d, e; + + igraph_matrix_init(&c, 0, 0); + + igraph_real_t elemA[] = {1, 2, 3, 4}; + igraph_real_t elemB[] = {5, 6, 7, 8}; + igraph_real_t elemD[] = {5, 6, 7, 8, 9, 10}; + matrix_init_real_row_major(&a, 2, 2, elemA); + matrix_init_real_row_major(&b, 2, 2, elemB); + matrix_init_real_row_major(&d, 2, 3, elemD); + matrix_init_real_row_major(&e, 3, 2, elemD); + + printf("matrix multiplication, A={{1,2},{3,4}} B ={{5,6},{7,8}}\n"); + igraph_blas_dgemm(0, 0, 1, &a, &b, 0, &c); + igraph_matrix_print(&c); + + printf("transpose a first\n"); + igraph_blas_dgemm(1, 0, 1, &a, &b, 0, &c); + igraph_matrix_print(&c); + + printf("transpose b first\n"); + igraph_blas_dgemm(0, 1, 1, &a, &b, 0, &c); + igraph_matrix_print(&c); + + printf("transpose both matrices first\n"); + igraph_blas_dgemm(1, 1, 1, &a, &b, 0, &c); + igraph_matrix_print(&c); + + printf("multiply by 0.5\n"); + igraph_blas_dgemm(0, 0, 0.5, &a, &b, 0, &c); + igraph_matrix_print(&c); + + printf("multiply by 1.5 by using previous result\n"); + igraph_blas_dgemm(0, 0, 1, &a, &b, 1, &c); + igraph_matrix_print(&c); + + printf("matrix multiplication, A={{1,2},{3,4}} B={{5,6,7},{8,9,10}}\n"); + igraph_blas_dgemm(0, 0, 1, &a, &d, 0, &c); + igraph_matrix_print(&c); + + printf("matrix multiplication, A={{5,8},{6,9},{7,10}} B={{1,2},{3,4}}\n"); + igraph_blas_dgemm(1, 0, 1, &d, &a, 0, &c); + igraph_matrix_print(&c); + + printf("check error when matrix sizes don't match\n"); + CHECK_ERROR(igraph_blas_dgemm(0, 0, 1, &d, &a, 0, &c), IGRAPH_EINVAL); + CHECK_ERROR(igraph_blas_dgemm(0, 1, 1, &a, &d, 0, &c), IGRAPH_EINVAL); + CHECK_ERROR(igraph_blas_dgemm(0, 0, 1, &a, &b, 1, &d), IGRAPH_EINVAL); + CHECK_ERROR(igraph_blas_dgemm(0, 0, 1, &a, &b, 1, &e), IGRAPH_EINVAL); + + igraph_matrix_destroy(&a); + igraph_matrix_destroy(&b); + igraph_matrix_destroy(&c); + igraph_matrix_destroy(&d); + igraph_matrix_destroy(&e); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_blas_dgemm.out b/tests/unit/igraph_blas_dgemm.out new file mode 100644 index 0000000..51ab5b1 --- /dev/null +++ b/tests/unit/igraph_blas_dgemm.out @@ -0,0 +1,26 @@ +matrix multiplication, A={{1,2},{3,4}} B ={{5,6},{7,8}} +19 22 +43 50 +transpose a first +26 30 +38 44 +transpose b first +17 23 +39 53 +transpose both matrices first +23 31 +34 46 +multiply by 0.5 + 9.5 11 +21.5 25 +multiply by 1.5 by using previous result +28.5 33 +64.5 75 +matrix multiplication, A={{1,2},{3,4}} B={{5,6,7},{8,9,10}} +21 24 27 +47 54 61 +matrix multiplication, A={{5,8},{6,9},{7,10}} B={{1,2},{3,4}} +29 42 +33 48 +37 54 +check error when matrix sizes don't match diff --git a/tests/unit/igraph_bridges.c b/tests/unit/igraph_bridges.c new file mode 100644 index 0000000..c916912 --- /dev/null +++ b/tests/unit/igraph_bridges.c @@ -0,0 +1,59 @@ + +#include +#include + +#include "test_utilities.h" + +void sort_and_print_vector(igraph_vector_int_t *v) { + igraph_vector_int_sort(v); + print_vector_int(v); +} + +int main(void) { + igraph_t graph; + igraph_vector_int_t bridges; + + igraph_vector_int_init(&bridges, 0); + + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_bridges(&graph, &bridges); + sort_and_print_vector(&bridges); + igraph_destroy(&graph); + + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_bridges(&graph, &bridges); + sort_and_print_vector(&bridges); + igraph_destroy(&graph); + + igraph_empty(&graph, 2, IGRAPH_UNDIRECTED); + igraph_bridges(&graph, &bridges); + sort_and_print_vector(&bridges); + igraph_destroy(&graph); + + igraph_small(&graph, /* num_nodes = */ 7, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 0, 2, 0, 3, 3, 4, 4, 5, 3, 5, 4, 6, -1); + igraph_bridges(&graph, &bridges); + sort_and_print_vector(&bridges); + igraph_destroy(&graph); + + /* Test with disconnected graph. */ + igraph_small(&graph, /* num_nodes = */ 16, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 1, 3, 4, 5, 5, 6, 4, 6, 4, 7, 7, 8, 4, 8, 9, 10, 10, 11, + 11, 12, 9, 12, 9, 13, 13, 14, -1); + igraph_bridges(&graph, &bridges); + sort_and_print_vector(&bridges); + igraph_destroy(&graph); + + /* Test with multi-edges and self-loops. */ + igraph_small(&graph, /* num_nodes = */ 3, IGRAPH_UNDIRECTED, + 0, 1, 0, 1, 1, 2, 2, 2, -1); + igraph_bridges(&graph, &bridges); + sort_and_print_vector(&bridges); + igraph_destroy(&graph); + + igraph_vector_int_destroy(&bridges); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_bridges.out b/tests/unit/igraph_bridges.out new file mode 100644 index 0000000..4a7a3ce --- /dev/null +++ b/tests/unit/igraph_bridges.out @@ -0,0 +1,6 @@ +( ) +( ) +( ) +( 3 7 ) +( 0 1 2 13 14 ) +( 2 ) diff --git a/tests/unit/igraph_callaway_traits_game.c b/tests/unit/igraph_callaway_traits_game.c new file mode 100644 index 0000000..e03aff2 --- /dev/null +++ b/tests/unit/igraph_callaway_traits_game.c @@ -0,0 +1,99 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void init_vm(igraph_vector_t *type_dist, + int v0, int v1, + igraph_matrix_t *pref_matrix, + int m00, int m10, int m01, int m11) { + igraph_vector_init_int_end(type_dist, -1, v0, v1, -1); + igraph_matrix_init(pref_matrix, 2, 2); + MATRIX(*pref_matrix, 0, 0) = m00; + MATRIX(*pref_matrix, 1, 0) = m10; + MATRIX(*pref_matrix, 0, 1) = m01; + MATRIX(*pref_matrix, 1, 1) = m11; +} + +#define DESTROY_GVM() do { \ + igraph_destroy(&g); \ + igraph_vector_destroy(&type_dist); \ + igraph_matrix_destroy(&pref_matrix); \ + } while(0) + +int main(void) { + igraph_t g; + igraph_vector_t type_dist; + igraph_vector_int_t node_types; + igraph_matrix_t pref_matrix; + igraph_bool_t bipartite; + + igraph_rng_seed(igraph_rng_default(), 42); + + /*Zero matrix elements for only possible vertex type means no edges*/ + init_vm(&type_dist, 1, 0, &pref_matrix, 0, 0, 0, 1); + IGRAPH_ASSERT(igraph_callaway_traits_game(&g, /*nodes*/ 20, /*types*/ 2, /*edges_per_step*/ 5, &type_dist, &pref_matrix, /*directed*/ 0, NULL) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + IGRAPH_ASSERT(igraph_vcount(&g) == 20); + DESTROY_GVM(); + + /*No vertices*/ + init_vm(&type_dist, 1, 0, &pref_matrix, 0, 0, 0, 1); + IGRAPH_ASSERT(igraph_callaway_traits_game(&g, /*nodes*/ 0, /*types*/ 2, /*edges_per_step*/ 0, &type_dist, &pref_matrix, /*directed*/ 0, NULL) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + IGRAPH_ASSERT(!igraph_is_directed(&g)); + DESTROY_GVM(); + + /*Two types with only cross terms makes a bipartite graph*/ + init_vm(&type_dist, 2, 1, &pref_matrix, 0, 1, 1, 0); + igraph_vector_int_init(&node_types, 0); + IGRAPH_ASSERT(igraph_callaway_traits_game(&g, /*nodes*/ 20, /*types*/ 2, /*edges_per_step*/ 5, &type_dist, &pref_matrix, /*directed*/ 1, &node_types) == IGRAPH_SUCCESS); + igraph_is_bipartite(&g, &bipartite, NULL); + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vector_int_size(&node_types) == igraph_vcount(&g)); + IGRAPH_ASSERT(igraph_vector_int_min(&node_types) == 0); + IGRAPH_ASSERT(igraph_vector_int_max(&node_types) == 1); + igraph_vector_int_destroy(&node_types); + DESTROY_GVM(); + + /*Automatically determined type_dist*/ + init_vm(&type_dist, 0, 0, &pref_matrix, 0, 1, 1, 0); + igraph_vector_int_init(&node_types, 0); + IGRAPH_ASSERT(igraph_callaway_traits_game(&g, /*nodes*/ 20, /*types*/ 2, /*edges_per_step*/ 3, /*type_dist*/ NULL, &pref_matrix, /*directed*/ 0, &node_types) == IGRAPH_SUCCESS); + igraph_is_bipartite(&g, &bipartite, NULL); + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(!igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vector_int_size(&node_types) == igraph_vcount(&g)); + IGRAPH_ASSERT(igraph_vector_int_min(&node_types) == 0); + IGRAPH_ASSERT(igraph_vector_int_max(&node_types) == 1); + igraph_vector_int_destroy(&node_types); + DESTROY_GVM(); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + /*Distribution of types should have at least one positive value*/ + init_vm(&type_dist, 0, 0, &pref_matrix, 0, 1, 1, 0); + IGRAPH_ASSERT(igraph_callaway_traits_game(&g, /*nodes*/ 20, /* types*/ 2, /*edges_per_step*/ 5, &type_dist, &pref_matrix, /*directed*/ 0, NULL) == IGRAPH_EINVAL); + DESTROY_GVM(); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_chung_lu_game.c b/tests/unit/igraph_chung_lu_game.c new file mode 100644 index 0000000..6727f72 --- /dev/null +++ b/tests/unit/igraph_chung_lu_game.c @@ -0,0 +1,178 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_vector_t outdeg, indeg; + igraph_bool_t simple, multi; + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Zero-legth input */ + + igraph_vector_init(&outdeg, 0); + + igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &outdeg, true, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_destroy(&g); + + igraph_vector_destroy(&outdeg); + + /* ORIGINAL */ + + /* Must use floating point literals here! 2.0 instead of 2 */ + igraph_vector_init_real_end(&outdeg, -1.0, + 1.0, 0.0, 2.5, 2.0, 3.0, 2.0, 1.5, + -1.0); + igraph_vector_init_real_end(&indeg, -1.0, + 2.0, 2.0, 2.0, 2.0, 0.0, 2.0, 2.0, + -1.0); + + igraph_chung_lu_game(&g, &outdeg, NULL, false, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, false, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, true, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + igraph_vector_destroy(&indeg); + igraph_vector_destroy(&outdeg); + + /* GRG */ + + /* Must use floating point literals here! 2.0 instead of 2 */ + igraph_vector_init_real_end(&outdeg, -1.0, + 189.0, 0.0, 2.5, 12.0, 3.0, 2.0, 1.5, + -1.0); + igraph_vector_init_real_end(&indeg, -1.0, + 2.0, 2.0, 2.0, 2.0, 0.0, 200.0, 2.0, + -1.0); + + igraph_chung_lu_game(&g, &outdeg, NULL, false, IGRAPH_CHUNG_LU_MAXENT); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_MAXENT); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, false, IGRAPH_CHUNG_LU_MAXENT); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, true, IGRAPH_CHUNG_LU_MAXENT); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + /* NR */ + + igraph_chung_lu_game(&g, &outdeg, NULL, false, IGRAPH_CHUNG_LU_NR); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_NR); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, false, IGRAPH_CHUNG_LU_NR); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, true, IGRAPH_CHUNG_LU_NR); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + /* Invalid input */ + + /* Bad variant */ + CHECK_ERROR(igraph_chung_lu_game(&g, &outdeg, &indeg, true, (igraph_chung_lu_t) -1), IGRAPH_EINVAL); + + /* Inconsistent sum */ + VECTOR(outdeg)[0] = 0; + CHECK_ERROR(igraph_chung_lu_game(&g, &outdeg, &indeg, true, IGRAPH_CHUNG_LU_ORIGINAL), IGRAPH_EINVAL); + + /* Negative weight */ + VECTOR(outdeg)[0] = -1; + CHECK_ERROR(igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_ORIGINAL), IGRAPH_EINVAL); + + /* Non-finite weight */ + VECTOR(outdeg)[0] = IGRAPH_INFINITY; + CHECK_ERROR(igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_ORIGINAL), IGRAPH_EINVAL); + + igraph_vector_destroy(&indeg); + igraph_vector_destroy(&outdeg); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_circulant.c b/tests/unit/igraph_circulant.c new file mode 100644 index 0000000..45da7f4 --- /dev/null +++ b/tests/unit/igraph_circulant.c @@ -0,0 +1,178 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t graph, graph_test; + igraph_vector_int_t shifts; + igraph_bool_t iso, same; + igraph_integer_t i; + + /* Testing invalid values */ + + /* n = -3, shifts = [1], undirected */ + igraph_vector_int_init_int(&shifts, 1, 1); + CHECK_ERROR(igraph_circulant(&graph, -3, &shifts, IGRAPH_UNDIRECTED), IGRAPH_EINVAL); + igraph_vector_int_destroy(&shifts); + + /* Testing n = 0 case */ + /* n = 0, shifts = [1], undirected */ + igraph_vector_int_init_int(&shifts, 1, 1); + IGRAPH_ASSERT(igraph_circulant(&graph, 0, &shifts, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 0); + IGRAPH_ASSERT(igraph_ecount(&graph) == 0); + igraph_vector_int_destroy(&shifts); + igraph_destroy(&graph); + + /* Testing n = 1 case */ + /* n = 1, shifts = [1], undirected */ + igraph_vector_int_init_int(&shifts, 1, 1); + IGRAPH_ASSERT(igraph_circulant(&graph, 1, &shifts, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 1, IGRAPH_UNDIRECTED, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&graph, &graph_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing n = 2 case */ + /* n = 2, shifts = [1], undirected */ + igraph_vector_int_init_int(&shifts, 1, 1); + IGRAPH_ASSERT(igraph_circulant(&graph, 2, &shifts, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 2, IGRAPH_UNDIRECTED, 0, 1, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&graph, &graph_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing empty list case */ + /* n = 5, shifts = [], undirected */ + igraph_vector_int_init(&shifts, 0); + IGRAPH_ASSERT(igraph_circulant(&graph, 5, &shifts, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 5, IGRAPH_UNDIRECTED, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&graph, &graph_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing typical use case */ + /* Test n = 5, shifts = [1, 2], undirected */ + igraph_vector_int_init_int(&shifts, 2, 1, 2); + IGRAPH_ASSERT(igraph_circulant(&graph, 5, &shifts, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 5, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 2, + 2, 4, 4, 1, 1, 3, 3, 0, -1); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing simplification */ + /* n = 6, shifts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], undirected */ + igraph_vector_int_init(&shifts, 0); + for (i = 1; i <= 15; i++) { + igraph_vector_int_push_back(&shifts, i); + } + IGRAPH_ASSERT(igraph_circulant(&graph, 6, &shifts, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + igraph_full(&graph_test, 6, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing simplification with negative values */ + /* n = 7, shifts = [-15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, + -4, -3, -2, -1, 0, 1, 2, 3, 4], undirected */ + igraph_vector_int_init(&shifts, 0); + for (i = -15; i <= 4; i++) { + igraph_vector_int_push_back(&shifts, i); + } + IGRAPH_ASSERT(igraph_circulant(&graph, 7, &shifts, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + igraph_full(&graph_test, 7, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing simplification when n is even, the offset = n/2, and the graph is undirected */ + /* n = 6, shifts = [3], undirected */ + igraph_vector_int_init_int(&shifts, 1, 3); + IGRAPH_ASSERT(igraph_circulant(&graph, 6, &shifts, IGRAPH_UNDIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 6, IGRAPH_UNDIRECTED, 0, 3, 1, 4, 2, 5, -1); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing directed graph */ + /* n = 7, shifts = [1, -2], directed */ + igraph_vector_int_init_int(&shifts, 2, 1, -2); + IGRAPH_ASSERT(igraph_circulant(&graph, 7, &shifts, IGRAPH_DIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 7, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 0, + 0, 5, 1, 6, 2, 0, 3, 1, 4, 2, 5, 3, 6, 4, -1); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing simplification works for directed too */ + /* n = 5, shifts = [-4, 1, 6], directed */ + igraph_vector_int_init_int(&shifts, 3, -4, 1, 6); + IGRAPH_ASSERT(igraph_circulant(&graph, 5, &shifts, IGRAPH_DIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 5, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, -1); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* Testing that things that are normally simplified for undirected are not simplified for directed */ + /* n = 5, shifts = [1, -1], directed */ + igraph_vector_int_init_int(&shifts, 2, 1, -1); + IGRAPH_ASSERT(igraph_circulant(&graph, 5, &shifts, IGRAPH_DIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 5, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, + 1, 0, 2, 1, 3, 2, 4, 3, 0, 4, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&graph, &graph_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + /* n = 6, shifts = [3], directed */ + igraph_vector_int_init_int(&shifts, 1, 3); + IGRAPH_ASSERT(igraph_circulant(&graph, 6, &shifts, IGRAPH_DIRECTED) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 6, IGRAPH_DIRECTED, 0, 3, 1, 4, 2, 5, 3, 0, 4, 1, 5, 2, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&graph, &graph_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + igraph_vector_int_destroy(&shifts); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_cited_type_game.c b/tests/unit/igraph_cited_type_game.c new file mode 100644 index 0000000..b8b981d --- /dev/null +++ b/tests/unit/igraph_cited_type_game.c @@ -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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_t pref; + igraph_vector_int_t types; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No nodes:\n"); + igraph_vector_init_int(&pref, 2, 1, 1); + igraph_vector_int_init_int(&types, 0); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 0, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + printf("No edges:\n"); + igraph_vector_init_int(&pref, 2, 1, 1); + igraph_vector_int_init_int(&types, 3, 1, 1, 1); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 3, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 0, /*directed*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + printf("Make a star of double edges:\n"); + igraph_vector_init_real(&pref, 3, 1.0, 0.0, 0.0); + igraph_vector_int_init_int(&types, 5, 0, 1, 1, 1, 1); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 5, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 2, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + printf("Make a line:\n"); + igraph_vector_init_real(&pref, 7, 1.0e-30, 1.0e-20, 1.0e-10, 1.0, 1.0e+10, 1.0e+20, 0.0); + igraph_vector_int_init_int(&types, 7, 0, 1, 2, 3, 4, 5, 6); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 7, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 1, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Checking negative number of nodes error handling.\n"); + igraph_vector_init_real(&pref, 2, 1.0, 1.0); + igraph_vector_int_init_int(&types, 2, 0, 1); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ -5, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_EINVAL); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + printf("Checking too few types error handling.\n"); + igraph_vector_init_real(&pref, 1, 1.0); + igraph_vector_int_init_int(&types, 0); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 1, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_EINVAL); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + printf("Checking too many types error handling.\n"); + igraph_vector_init_real(&pref, 3, 1.0, 1.0, 1.0); + igraph_vector_int_init_int(&types, 2, 0, 1); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 1, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_EINVAL); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + printf("Checking negative type for error handling.\n"); + igraph_vector_init_real(&pref, 2, 1.0, 1.0); + igraph_vector_int_init_int(&types, 2, 0, -5); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 2, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_EINVAL); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + printf("Checking too big type for error handling.\n"); + igraph_vector_init_real(&pref, 2, 1.0, 1.0); + igraph_vector_int_init_int(&types, 2, 0, 5); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 2, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_EINVAL); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + printf("Checking negative preference error handling.\n"); + igraph_vector_init_real(&pref, 2, 1.0, -1.0); + igraph_vector_int_init_int(&types, 2, 0, 1); + IGRAPH_ASSERT(igraph_cited_type_game(&g, /*nodes*/ 2, /*types*/ &types, /*pref*/ &pref, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_EINVAL); + igraph_vector_destroy(&pref); + igraph_vector_int_destroy(&types); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_cited_type_game.out b/tests/unit/igraph_cited_type_game.out new file mode 100644 index 0000000..4d9fc0c --- /dev/null +++ b/tests/unit/igraph_cited_type_game.out @@ -0,0 +1,40 @@ +No nodes: +directed: false +vcount: 0 +edges: { +} +No edges: +directed: false +vcount: 3 +edges: { +} +Make a star of double edges: +directed: true +vcount: 5 +edges: { +1 0 +1 0 +2 0 +2 0 +3 0 +3 0 +4 0 +4 0 +} +Make a line: +directed: true +vcount: 7 +edges: { +1 0 +2 1 +3 2 +4 3 +5 4 +6 5 +} +Checking negative number of nodes error handling. +Checking too few types error handling. +Checking too many types error handling. +Checking negative type for error handling. +Checking too big type for error handling. +Checking negative preference error handling. diff --git a/tests/unit/igraph_citing_cited_type_game.c b/tests/unit/igraph_citing_cited_type_game.c new file mode 100644 index 0000000..a34ee36 --- /dev/null +++ b/tests/unit/igraph_citing_cited_type_game.c @@ -0,0 +1,87 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_matrix_t pref_empty, pref_bipartite, pref_line; + igraph_vector_int_t types_empty, types_bipartite, types_line; + igraph_bool_t bipartite; + int bipartite_elem[] = {0, 1, 1, 0}; + int line_elem[] = {0, 0, 1, 0, 0, + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, + }; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_matrix_init(&pref_empty, 0, 0); + igraph_vector_int_init(&types_empty, 0); + + matrix_init_int_row_major(&pref_bipartite, 2, 2, bipartite_elem); + igraph_vector_int_init_int(&types_bipartite, 10, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0); + + matrix_init_int_row_major(&pref_line, 5, 5, line_elem); + igraph_vector_int_init_int(&types_line, 5, 0, 1, 2, 3, 4); + + printf("No nodes.\n"); + IGRAPH_ASSERT(igraph_citing_cited_type_game(&g, /*nodes*/ 0, &types_empty, &pref_empty, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + printf("Bipartite graph.\n"); + IGRAPH_ASSERT(igraph_citing_cited_type_game(&g, /*nodes*/ 10, &types_bipartite, &pref_bipartite, /*edges_per_step*/ 5, /*directed*/ 0) == IGRAPH_SUCCESS); + igraph_is_bipartite(&g, &bipartite, NULL); + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 45); + igraph_destroy(&g); + + printf("No edges.\n"); + IGRAPH_ASSERT(igraph_citing_cited_type_game(&g, /*nodes*/ 10, &types_bipartite, &pref_bipartite, /*edges_per_step*/ 0, /*directed*/ 0) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + igraph_destroy(&g); + + printf("A line.\n"); + IGRAPH_ASSERT(igraph_citing_cited_type_game(&g, /*nodes*/ 5, &types_line, &pref_line, /*edges_per_step*/ 1, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Too few types for nodes.\n"); + IGRAPH_ASSERT(igraph_citing_cited_type_game(&g, /*nodes*/ 5, &types_empty, &pref_empty, /*edges_per_step*/ 1, /*directed*/ 1) == IGRAPH_EINVAL); + + printf("Too few prefs.\n"); + IGRAPH_ASSERT(igraph_citing_cited_type_game(&g, /*nodes*/ 5, &types_line, &pref_empty, /*edges_per_step*/ 1, /*directed*/ 1) == IGRAPH_EINVAL); + + igraph_matrix_destroy(&pref_empty); + igraph_vector_int_destroy(&types_empty); + igraph_matrix_destroy(&pref_bipartite); + igraph_vector_int_destroy(&types_bipartite); + igraph_matrix_destroy(&pref_line); + igraph_vector_int_destroy(&types_line); + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_citing_cited_type_game.out b/tests/unit/igraph_citing_cited_type_game.out new file mode 100644 index 0000000..712f939 --- /dev/null +++ b/tests/unit/igraph_citing_cited_type_game.out @@ -0,0 +1,14 @@ +No nodes. +Bipartite graph. +No edges. +A line. +directed: true +vcount: 5 +edges: { +1 0 +2 1 +3 2 +4 3 +} +Too few types for nodes. +Too few prefs. diff --git a/tests/unit/igraph_clique_size_hist.c b/tests/unit/igraph_clique_size_hist.c new file mode 100644 index 0000000..cb419cb --- /dev/null +++ b/tests/unit/igraph_clique_size_hist.c @@ -0,0 +1,57 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g, int min, int max) { + igraph_vector_t result; + igraph_vector_init(&result, 0); + IGRAPH_ASSERT(igraph_clique_size_hist(g, &result, min, max) == IGRAPH_SUCCESS); + print_vector(&result); + igraph_vector_destroy(&result); +} + + +int main(void) { + igraph_t g_empty, g_lm; + + igraph_small(&g_empty, 0, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,2, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices:\n"); + print_and_destroy(&g_empty, 0, 0); + + printf("Graph with loops and multiple edges:\n"); + print_and_destroy(&g_lm, 0, 0); + + printf("Same graph, minimum clique size 2:\n"); + print_and_destroy(&g_lm, 2, 0); + + printf("Same graph, maximum clique size 2:\n"); + print_and_destroy(&g_lm, 0, 2); + + printf("Same graph, minimum and maximum clique size 10:\n"); + print_and_destroy(&g_lm, 10, 10); + + igraph_destroy(&g_empty); + igraph_destroy(&g_lm); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_clique_size_hist.out b/tests/unit/igraph_clique_size_hist.out new file mode 100644 index 0000000..8c968ee --- /dev/null +++ b/tests/unit/igraph_clique_size_hist.out @@ -0,0 +1,10 @@ +No vertices: +( ) +Graph with loops and multiple edges: +( 6 6 2 ) +Same graph, minimum clique size 2: +( 0 6 2 ) +Same graph, maximum clique size 2: +( 6 6 ) +Same graph, minimum and maximum clique size 10: +( ) diff --git a/tests/unit/igraph_closeness.c b/tests/unit/igraph_closeness.c new file mode 100644 index 0000000..aa430e3 --- /dev/null +++ b/tests/unit/igraph_closeness.c @@ -0,0 +1,345 @@ +#include +#include +#include "test_utilities.h" + + +void simple_test_case_no_weights_undirected(void) { + + igraph_t g; + igraph_vector_t vector_actual_results; + + printf("Simple test case, no weights, undirected\n"); + + igraph_vector_init(&vector_actual_results, 0); + + igraph_small(&g, 0, IGRAPH_DIRECTED, 0,1 , 1,2, -1); + + /* NOT NORMALISED TEST BELOW */ + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_ALL /*graph is "undirected"*/, + NULL /*unweighted*/, /*not normalised*/ 0); + + printf("Non normalised results below\n"); + print_vector(&vector_actual_results); + + /* NORMALISED TEST BELOW */ + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_ALL /*graph is "undirected"*/, + NULL, /*normalised*/ 1); + + printf("\nNormalised results below\n"); + print_vector(&vector_actual_results); + + igraph_vector_destroy(&vector_actual_results); + igraph_destroy(&g); +} + +void simple_test_case_with_weights_undirected(void) { + + igraph_t g; + igraph_vector_int_t vector_edges; + igraph_vector_t vector_weights, vector_actual_results; + + igraph_integer_t real_edges[] = {0,1 , 1,2}; + igraph_real_t real_weights[] = {3, 5}; + + printf("\nSimple test case, with weights, undirected\n"); + + igraph_vector_init(&vector_actual_results, 0); + igraph_vector_int_view(&vector_edges, real_edges, sizeof(real_edges)/sizeof(real_edges[0])); + igraph_create(&g, &vector_edges, /*number of vertices*/ 2, IGRAPH_DIRECTED); + + igraph_vector_view(&vector_weights, real_weights, sizeof(real_weights)/sizeof(real_weights[0])); + + /* NOT NORMALISED TEST BELOW */ + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_ALL /*graph is "undirected"*/, + &vector_weights, /*not normalised*/ 0); + + printf("Non normalised test below\n"); + + print_vector(&vector_actual_results); + + /* NORMALISED TEST BELOW */ + + printf("\nNormalised test below\n"); + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_ALL /*graph is "undirected"*/, + &vector_weights, /*normalised*/ 1); + + print_vector(&vector_actual_results); + + igraph_vector_destroy(&vector_actual_results); + igraph_destroy(&g); +} + +void advanced_test_case_no_weights_undirected(void) { + + igraph_t g; + igraph_vector_t vector_actual_results; + + /* note, denominatory calculated as (shortest dist)*(n-1) for no normalisation + normalisation excludes n-1 as part of the denominator */ + + printf("\nAdvanced test case, no weights, undirected\n"); + + igraph_vector_init(&vector_actual_results, 0); + + igraph_small(&g, 0, IGRAPH_DIRECTED, 1,0 , 0,5 , 5,6 , 5,4, 4,1 , 1,2 , 2,4 , 4,6 , + 2,3 , 3,7 , 7,6 , 2,6 , -1); + + /* NOT NORMALISED TEST BELOW*/ + + printf("Non normalised test below\n"); + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_ALL /*graph is "undirected"*/, + NULL, /*not normalised*/ 0); + + print_vector(&vector_actual_results); + + /* NORMALISED TEST BELOW*/ + + printf("\nNormalised test below\n"); + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_ALL /*graph is "undirected"*/, + NULL, /*normalised*/ 1); + + print_vector(&vector_actual_results); + + igraph_vector_destroy(&vector_actual_results); + igraph_destroy(&g); +} + +void advanced_test_case_with_weights(void) { + + igraph_t g; + igraph_vector_int_t vector_edges; + igraph_vector_t vector_weights, vector_actual_results; + + igraph_integer_t real_edges[] = {1,0 , 0,5 , 5,6 , 5,4, 4,1 , 1,2 , 2,4 , 4,6 , + 2,3 , 3,7 , 7,6 , 6,2}; + + igraph_real_t real_weights[] = {4, 9, 2, 2, 2, 3, 1, 1, 8, 7, 5, 5}; + + printf("\nAdvanced test case, with weights\n"); + + + igraph_vector_init(&vector_actual_results, 0); + igraph_vector_int_view(&vector_edges, real_edges, sizeof(real_edges) / sizeof(real_edges[0])); + igraph_create(&g, &vector_edges, /*number of vertices*/ 2, IGRAPH_DIRECTED); + + igraph_vector_view(&vector_weights, real_weights, sizeof(real_weights) / sizeof(real_weights[0])); + + /* TEST FOR UNDIRECTED GRAPH */ + + printf("Undirected graph test below\n"); + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_ALL /*graph is "undirected"*/, + &vector_weights, /*not normalised*/ 0); + + print_vector(&vector_actual_results); + + /* TEST FOR DIRECTED GRAPH + OUT means the min distance from the curr node to the other node */ + + printf("\nDirected graph test below for OUT\n"); + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_OUT /*graph is "out directed"*/, + &vector_weights, /*not normalised*/ 0); + + print_vector(&vector_actual_results); + + /* IN means the min distance from a node to the curr node */ + + printf("\nDirected graph test below for IN\n"); + + igraph_closeness(&g, &vector_actual_results /*store results here*/, + NULL, NULL, + /*calculating for all vectors in the graph*/ igraph_vss_all(), + IGRAPH_IN /*graph is "in directed"*/, + &vector_weights, /*not normalised*/ 0); + + print_vector(&vector_actual_results); + + igraph_vector_destroy(&vector_actual_results); + igraph_destroy(&g); +} + +void test_cutoff(void) { + + igraph_t g; + igraph_vector_t closeness; + igraph_vector_int_t reachable; + igraph_bool_t all_reachable; + size_t i; + igraph_real_t cutoff_vec[] = { -1.0, 0.0, 1.0, 2.9, 3.0, 3.1 }; + + printf("\n\nUnweighted undirected with cutoff\n"); + + igraph_ring(&g, 4, IGRAPH_UNDIRECTED, 0, 0); + + igraph_vector_init(&closeness, 0); + igraph_vector_int_init(&reachable, 0); + + for (i=0; i < sizeof(cutoff_vec) / sizeof(cutoff_vec[0]); ++i) { + printf("\nRange-limited closeness with cutoff %g\n", cutoff_vec[i]); + igraph_closeness_cutoff(&g, &closeness, &reachable, &all_reachable, + igraph_vss_all(), IGRAPH_ALL, NULL, /* normalized */ 1, + cutoff_vec[i]); + printf("Closeness: "); + print_vector(&closeness); + printf("Reachable: "); + print_vector_int(&reachable); + printf("All reachable: %s\n", all_reachable ? "true" : "false"); + } + + igraph_vector_int_destroy(&reachable); + igraph_vector_destroy(&closeness); + + igraph_destroy(&g); +} + +void test_cutoff_directed(void) { + + igraph_t g; + igraph_vector_t closeness; + igraph_vector_int_t reachable; + igraph_bool_t all_reachable; + size_t i; + igraph_real_t cutoff_vec[] = { -1.0, 0.0, 1.0, 2.9, 3.0, 3.1 }; + + printf("\n\nUnweighted directed with cutoff\n"); + + igraph_ring(&g, 4, IGRAPH_DIRECTED, 0, 0); + + igraph_vector_init(&closeness, 0); + igraph_vector_int_init(&reachable, 0); + + for (i=0; i < sizeof(cutoff_vec) / sizeof(cutoff_vec[0]); ++i) { + printf("\nRange-limited directed closeness with cutoff %g\n", cutoff_vec[i]); + igraph_closeness_cutoff(&g, &closeness, &reachable, &all_reachable, + igraph_vss_all(), IGRAPH_OUT, NULL, /* normalized */ 1, + cutoff_vec[i]); + printf("Closeness: "); + print_vector(&closeness); + printf("Reachable: "); + print_vector_int(&reachable); + printf("All reachable: %s\n", all_reachable ? "true" : "false"); + } + + igraph_vector_int_destroy(&reachable); + igraph_vector_destroy(&closeness); + + igraph_destroy(&g); +} + +void test_cutoff_weighted(void) { + + igraph_t g; + igraph_vector_t closeness; + igraph_vector_int_t reachable; + igraph_bool_t all_reachable; + size_t i; + igraph_real_t cutoff_vec[] = { -1.0, 0.0, 1.0, 2.9, 3.0, 5.0, 6.0 }; + igraph_vector_t weights; + + printf("\n\nWeighted undirected with cutoff\n"); + + igraph_ring(&g, 4, IGRAPH_UNDIRECTED, 0, 0); + + igraph_vector_init(&closeness, 0); + igraph_vector_int_init(&reachable, 0); + igraph_vector_init_range(&weights, 1, 4); + + for (i=0; i < sizeof(cutoff_vec) / sizeof(cutoff_vec[0]); ++i) { + printf("\nRange-limited weighted closeness with cutoff %g\n", cutoff_vec[i]); + igraph_closeness_cutoff(&g, &closeness, &reachable, &all_reachable, + igraph_vss_all(), IGRAPH_ALL, &weights, /* normalized */ 1, + cutoff_vec[i]); + printf("Closeness: "); + print_vector(&closeness); + printf("Reachable: "); + print_vector_int(&reachable); + printf("All reachable: %s\n", all_reachable ? "true" : "false"); + } + + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&reachable); + igraph_vector_destroy(&closeness); + + igraph_destroy(&g); +} + +void test_edge_cases(void) { + + igraph_t g; + igraph_vector_t closeness; + igraph_vector_int_t reachable; + igraph_bool_t all_reachable; + int n; + + printf("\n\nEdgeless graphs\n"); + + igraph_vector_init(&closeness, 0); + igraph_vector_int_init(&reachable, 0); + + for (n=0; n <= 2; ++n) { + printf("\nEdgeless graph with %d vertices\n", n); + igraph_empty(&g, n, IGRAPH_UNDIRECTED); + + igraph_closeness(&g, &closeness, &reachable, &all_reachable, igraph_vss_all(), IGRAPH_ALL, NULL, 1); + printf("Closeness: "); + print_vector(&closeness); + printf("Reachable: "); + print_vector_int(&reachable); + printf("All reachable: %s\n", all_reachable ? "true" : "false"); + + igraph_destroy(&g); + } + + igraph_vector_int_destroy(&reachable); + igraph_vector_destroy(&closeness); + +} + +int main(void) { + + simple_test_case_no_weights_undirected(); + simple_test_case_with_weights_undirected(); + advanced_test_case_no_weights_undirected(); + advanced_test_case_with_weights(); + + test_cutoff(); + test_cutoff_directed(); + test_cutoff_weighted(); + + test_edge_cases(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_closeness.out b/tests/unit/igraph_closeness.out new file mode 100644 index 0000000..72b89a9 --- /dev/null +++ b/tests/unit/igraph_closeness.out @@ -0,0 +1,152 @@ +Simple test case, no weights, undirected +Non normalised results below +( 0.333333 0.5 0.333333 ) + +Normalised results below +( 0.666667 1 0.666667 ) + +Simple test case, with weights, undirected +Non normalised test below +( 0.0909091 0.125 0.0769231 ) + +Normalised test below +( 0.181818 0.25 0.153846 ) + +Advanced test case, no weights, undirected +Non normalised test below +( 0.0714286 0.0833333 0.1 0.0714286 0.1 0.0833333 0.1 0.0714286 ) + +Normalised test below +( 0.5 0.583333 0.7 0.5 0.7 0.583333 0.7 0.5 ) + +Advanced test case, with weights +Undirected graph test below +( 0.0169492 0.0285714 0.0322581 0.0140845 0.037037 0.027027 0.0333333 0.0192308 ) + +Directed graph test below for OUT +( 0.00869565 0.0172414 0.0192308 0.00763359 0.016129 0.0166667 0.0117647 0.01 ) + +Directed graph test below for IN +( 0.0128205 0.015873 0.015873 0.00980392 0.0188679 0.0075188 0.0263158 0.0075188 ) + + +Unweighted undirected with cutoff + +Range-limited closeness with cutoff -1 +Closeness: ( 0.5 0.75 0.75 0.5 ) +Reachable: ( 3 3 3 3 ) +All reachable: true + +Range-limited closeness with cutoff 0 +Closeness: ( NaN NaN NaN NaN ) +Reachable: ( 0 0 0 0 ) +All reachable: false + +Range-limited closeness with cutoff 1 +Closeness: ( 1 1 1 1 ) +Reachable: ( 1 2 2 1 ) +All reachable: false + +Range-limited closeness with cutoff 2.9 +Closeness: ( 0.666667 0.75 0.75 0.666667 ) +Reachable: ( 2 3 3 2 ) +All reachable: false + +Range-limited closeness with cutoff 3 +Closeness: ( 0.5 0.75 0.75 0.5 ) +Reachable: ( 3 3 3 3 ) +All reachable: true + +Range-limited closeness with cutoff 3.1 +Closeness: ( 0.5 0.75 0.75 0.5 ) +Reachable: ( 3 3 3 3 ) +All reachable: true + + +Unweighted directed with cutoff + +Range-limited directed closeness with cutoff -1 +Closeness: ( 0.5 0.666667 1 NaN ) +Reachable: ( 3 2 1 0 ) +All reachable: false + +Range-limited directed closeness with cutoff 0 +Closeness: ( NaN NaN NaN NaN ) +Reachable: ( 0 0 0 0 ) +All reachable: false + +Range-limited directed closeness with cutoff 1 +Closeness: ( 1 1 1 NaN ) +Reachable: ( 1 1 1 0 ) +All reachable: false + +Range-limited directed closeness with cutoff 2.9 +Closeness: ( 0.666667 0.666667 1 NaN ) +Reachable: ( 2 2 1 0 ) +All reachable: false + +Range-limited directed closeness with cutoff 3 +Closeness: ( 0.5 0.666667 1 NaN ) +Reachable: ( 3 2 1 0 ) +All reachable: false + +Range-limited directed closeness with cutoff 3.1 +Closeness: ( 0.5 0.666667 1 NaN ) +Reachable: ( 3 2 1 0 ) +All reachable: false + + +Weighted undirected with cutoff + +Range-limited weighted closeness with cutoff -1 +Closeness: ( 0.3 0.375 0.375 0.214286 ) +Reachable: ( 3 3 3 3 ) +All reachable: true + +Range-limited weighted closeness with cutoff 0 +Closeness: ( NaN NaN NaN NaN ) +Reachable: ( 0 0 0 0 ) +All reachable: false + +Range-limited weighted closeness with cutoff 1 +Closeness: ( 1 1 NaN NaN ) +Reachable: ( 1 1 0 0 ) +All reachable: false + +Range-limited weighted closeness with cutoff 2.9 +Closeness: ( 1 0.666667 0.5 NaN ) +Reachable: ( 1 2 1 0 ) +All reachable: false + +Range-limited weighted closeness with cutoff 3 +Closeness: ( 0.5 0.666667 0.375 0.333333 ) +Reachable: ( 2 2 3 1 ) +All reachable: false + +Range-limited weighted closeness with cutoff 5 +Closeness: ( 0.5 0.375 0.375 0.25 ) +Reachable: ( 2 3 3 2 ) +All reachable: false + +Range-limited weighted closeness with cutoff 6 +Closeness: ( 0.3 0.375 0.375 0.214286 ) +Reachable: ( 3 3 3 3 ) +All reachable: true + + +Edgeless graphs + +Edgeless graph with 0 vertices +Closeness: ( ) +Reachable: ( ) +All reachable: true + +Edgeless graph with 1 vertices +Closeness: ( NaN ) +Reachable: ( 0 ) +All reachable: true + +Edgeless graph with 2 vertices +Closeness: ( NaN NaN ) +Reachable: ( 0 0 ) +All reachable: false diff --git a/tests/unit/igraph_cohesion.c b/tests/unit/igraph_cohesion.c new file mode 100644 index 0000000..894f47f --- /dev/null +++ b/tests/unit/igraph_cohesion.c @@ -0,0 +1,49 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_integer_t value; + igraph_bool_t checks = 1; + + igraph_small(&g, 7, IGRAPH_DIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 3, 4, 3, 5, 4, 5, 1, 6, 6, 3, 5, 0, -1); + + igraph_cohesion(&g, &value, checks); + + IGRAPH_ASSERT(value == 1); + + igraph_destroy(&g); + + igraph_small(&g, 7, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 3, 4, 3, 5, 4, 5, 1, 6, 6, 3, -1); + + igraph_cohesion(&g, &value, checks); + + IGRAPH_ASSERT(value == 2); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_cohesive_blocks.c b/tests/unit/igraph_cohesive_blocks.c new file mode 100644 index 0000000..f9a9e69 --- /dev/null +++ b/tests/unit/igraph_cohesive_blocks.c @@ -0,0 +1,125 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void doit(igraph_t *g) { + + igraph_vector_int_list_t blocks; + igraph_vector_int_t cohesion; + igraph_vector_int_t parent; + igraph_t block_tree; + igraph_integer_t i; + + igraph_vector_int_list_init(&blocks, 0); + igraph_vector_int_init(&cohesion, 0); + igraph_vector_int_init(&parent, 0); + + igraph_cohesive_blocks(g, &blocks, &cohesion, &parent, + &block_tree); + + printf("Blocks:\n"); + for (i = 0; i < igraph_vector_int_list_size(&blocks); i++) { + igraph_vector_int_t *sg = igraph_vector_int_list_get_ptr(&blocks, i); + printf(" "); + igraph_vector_int_print(sg); + } + printf("Cohesion:\n "); + igraph_vector_int_print(&cohesion); + printf("Parents:\n "); + igraph_vector_int_print(&parent); + printf("Block graph:\n"); + igraph_write_graph_edgelist(&block_tree, stdout); + + igraph_vector_int_list_destroy(&blocks); + igraph_vector_int_destroy(&cohesion); + igraph_vector_int_destroy(&parent); + igraph_destroy(&block_tree); + +} + +int main(void) { + + igraph_t g; + + printf("The graph from the Moody-White paper:\n"); + igraph_small(&g, 23, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, + 1, 2, 1, 3, 1, 4, 1, 6, + 2, 3, 2, 5, 2, 6, + 3, 4, 3, 5, 3, 6, + 4, 5, 4, 6, 4, 20, + 5, 6, + 6, 7, 6, 10, 6, 13, 6, 18, + 7, 8, 7, 10, 7, 13, + 8, 9, + 9, 11, 9, 12, + 10, 11, 10, 13, + 11, 15, + 12, 15, + 13, 14, + 14, 15, + 16, 17, 16, 18, 16, 19, + 17, 19, 17, 20, + 18, 19, 18, 21, 18, 22, + 19, 20, + 20, 21, 20, 22, + 21, 22, + -1); + + doit(&g); + igraph_destroy(&g); + printf("--\n"); + + printf("Tricky graph, where the separators themselves form a block:\n"); + + igraph_small(&g, 8, IGRAPH_UNDIRECTED, + 0, 1, 0, 4, 0, 5, 1, 2, 1, 4, 1, 5, 1, 6, 2, 3, 2, 5, 2, 6, 2, 7, + 3, 6, 3, 7, 4, 5, 5, 6, 6, 7, + -1); + + doit(&g); + igraph_destroy(&g); + printf("--\n"); + + printf("The science camp graph from http://intersci.ss.uci.edu/wiki/index.php/Cohesive_blocking\n"); + igraph_small(&g, 18, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, + 1, 2, 1, 3, 1, 16, 1, 17, + 2, 3, + 3, 17, + 4, 5, 4, 6, 4, 7, 4, 8, + 5, 6, 5, 7, + 6, 7, 6, 8, + 7, 8, 7, 16, + 8, 9, 8, 10, + 9, 11, 9, 12, 9, 13, 9, 14, + 10, 11, 10, 12, 10, 13, + 11, 14, + 12, 13, 12, 14, 12, 15, + 15, 16, 15, 17, + 16, 17, + -1); + + doit(&g); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_cohesive_blocks.out b/tests/unit/igraph_cohesive_blocks.out new file mode 100644 index 0000000..300f34b --- /dev/null +++ b/tests/unit/igraph_cohesive_blocks.out @@ -0,0 +1,46 @@ +The graph from the Moody-White paper: +Blocks: + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 + 0 1 2 3 4 5 6 16 17 18 19 20 21 22 + 6 7 8 9 10 11 12 13 14 15 + 0 1 2 3 4 5 6 + 6 7 10 13 +Cohesion: + 1 2 2 5 3 +Parents: + -1 0 0 1 2 +Block graph: +0 1 +0 2 +1 3 +2 4 +-- +Tricky graph, where the separators themselves form a block: +Blocks: + 0 1 2 3 4 5 6 7 + 0 1 4 5 + 2 3 6 7 + 1 2 5 6 +Cohesion: + 2 3 3 3 +Parents: + -1 0 0 0 +Block graph: +0 1 +0 2 +0 3 +-- +The science camp graph from http://intersci.ss.uci.edu/wiki/index.php/Cohesive_blocking +Blocks: + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + 0 1 2 3 + 4 5 6 7 8 + 9 10 11 12 13 14 +Cohesion: + 2 3 3 3 +Parents: + -1 0 0 0 +Block graph: +0 1 +0 2 +0 3 diff --git a/tests/unit/igraph_coloring.c b/tests/unit/igraph_coloring.c new file mode 100644 index 0000000..6ebce35 --- /dev/null +++ b/tests/unit/igraph_coloring.c @@ -0,0 +1,228 @@ +/* + 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, see . +*/ + +#include + +#include "test_utilities.h" + +/* Verify that the colouring is valid, i.e. no two adjacent vertices have the same colour. */ +void verify_coloring(igraph_t *graph, const igraph_vector_int_t *colors) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + for (igraph_integer_t i = 0; i < no_of_edges; ++i) { + if (IGRAPH_FROM(graph, i) == IGRAPH_TO(graph, i)) { + continue; + } + if ( VECTOR(*colors)[ IGRAPH_FROM(graph, i) ] == VECTOR(*colors)[ IGRAPH_TO(graph, i) ] ) { + IGRAPH_FATALF("Inconsistent coloring! Vertices %" IGRAPH_PRId " and %" IGRAPH_PRId " are adjacent but have the same color.\n", + IGRAPH_FROM(graph, i), IGRAPH_TO(graph, i)); + } + } + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t color = VECTOR(*colors)[i]; + if (color < 0 || color >= no_of_nodes) { + IGRAPH_FATALF("The vertex %" IGRAPH_PRId " has invalid color %" IGRAPH_PRId "\n", i, color); + } + } +} + +void print_result( + const char* result_name, const igraph_vector_int_t *colors, + igraph_bool_t print_color_vector +) { + printf("%s", result_name); + if (!igraph_vector_int_empty(colors)) { + printf("Number of colors used: %" IGRAPH_PRId "\n", igraph_vector_int_max(colors) + 1); + } + if (print_color_vector) { + print_vector_int(colors); + } +} + +void run_tests(igraph_t *graph, igraph_bool_t print_color_vector) { + igraph_vector_int_t colors; + igraph_vector_int_init(&colors, 0); + + igraph_vector_int_fill(&colors, -1); + igraph_vertex_coloring_greedy(graph, &colors, IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS); + verify_coloring(graph, &colors); + print_result("testing greedy coloring with COLORED_NEIGHBORS heuristic\n", &colors, print_color_vector); + + igraph_vector_int_fill(&colors, -1); + igraph_vertex_coloring_greedy(graph, &colors, IGRAPH_COLORING_GREEDY_DSATUR); + verify_coloring(graph, &colors); + print_result("testing greedy coloring with DSATUR heuristic\n", &colors, print_color_vector); + printf("\n"); + + igraph_vector_int_destroy(&colors); +} + +void test_null_graph(void) { + igraph_t graph; + + /* run it on the null graph */ + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + printf("Testing null graph\n"); + run_tests(&graph, true); + + igraph_destroy(&graph); +} + +void test_graph_1_vertex(void) { + igraph_t graph; + + igraph_empty(&graph, 1, IGRAPH_DIRECTED); + printf("Testing singleton graph\n"); + run_tests(&graph, true); + + igraph_destroy(&graph); +} + +void test_graph_large(void) { + igraph_t graph; + + /* simple large undirected graph */ + igraph_erdos_renyi_game_gnm(&graph, 1000, 10000, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + printf("Testing large simple graph\n"); + run_tests(&graph, false); + + /* graph with loops and parallel edges */ + igraph_rewire_edges(&graph, 1.0, true, true); + printf("Testing large graph with loops and multi-edges\n"); + run_tests(&graph, false); + + igraph_destroy(&graph); +} + +// This graph has chromatic number of 3. +void test_graph_small(void) { + igraph_t graph; + + igraph_small(&graph, 8, IGRAPH_UNDIRECTED, + 0, 3, 0, 4, 1, 4, 2, 4, 1, 5, 2, 5, 3, 5, 0, 6, 1, 6, 2, 6, 3, 6, 0, 7, 1, 7, 2, 7, 4, 7, 5, 7, + -1); + printf("Testing small simple graph\n"); + run_tests(&graph, true); + + igraph_destroy(&graph); +} + +// Same as the graph above with multi-edges and self-loops added +void test_graph_small_multi(void) { + igraph_t graph; + + igraph_small(&graph, 8, IGRAPH_UNDIRECTED, + 0, 3, 0, 4, 1, 4, 2, 4, 1, 5, 2, 5, 3, 5, 0, 6, 1, 6, 2, 6, 3, 6, 0, 7, 1, 7, 2, 7, 4, 7, 5, 7, + 0, 4, 0, 4, 3, 5, 3, 5, 3, 5, 0, 0, 0, 0, 1, 1, + -1); + printf("Testing small multigraph\n"); + run_tests(&graph, true); + + igraph_destroy(&graph); +} + +// Note: This graph has chromatic number 4. +// Currently implemented greedy heuristics don't produce an exact result. +void test_graph_lcf(void) { + igraph_t graph; + + igraph_lcf(&graph, /* n= */ 8, /* shifts= */ 2, /* repeats= */ 8, 0); + printf("Testing small LCF graph\n"); + run_tests(&graph, true); + + igraph_destroy(&graph); +} + +// Isolated vertices must be assigned color 0. +void test_isolated_vertices(void) { + igraph_t graph; + + igraph_small(&graph, 4, IGRAPH_UNDIRECTED, 0, 1, -1); + printf("Testing graph with isolated vertices\n"); + run_tests(&graph, true); + + igraph_destroy(&graph); +} + +// Isolated vertices must be assigned color 0. +void test_isolated_vertices_with_loops(void) { + igraph_t graph; + + igraph_small(&graph, 3, IGRAPH_UNDIRECTED, + 0,0, 2,2, 2,2, + -1); + printf("Testing graph with isolated vertices and self-loops\n"); + run_tests(&graph, true); + + igraph_destroy(&graph); +} + +// Wheel graphs on odd/even number of vertices have chromatic number of 3/4. +// DSatur is expected to find an exact minimum coloring. +void test_wheel_graph(void) { + igraph_t graph_odd, graph_even; + + igraph_wheel(&graph_odd, 11, IGRAPH_WHEEL_UNDIRECTED, 0); + printf("Testing wheel graph with odd number of vertices\n"); + run_tests(&graph_odd, true); + + igraph_wheel(&graph_even, 12, IGRAPH_WHEEL_UNDIRECTED, 0); + printf("Testing wheel graph with even number of vertices\n"); + run_tests(&graph_even, true); + + igraph_destroy(&graph_odd); + igraph_destroy(&graph_even); +} + +// Bipartite graphs have a chromatic number of 2 (by definition). +// DSatur is expected to find an exact minimum coloring. +void test_bipartite_graph(void) { + igraph_t graph_1, graph_2; + + igraph_bipartite_game_gnm(&graph_1, 0, 100, 100, 10000, IGRAPH_UNDIRECTED, IGRAPH_ALL); + printf("Testing complete bipartite graph\n"); + run_tests(&graph_1, false); + + igraph_bipartite_game_gnm(&graph_2, 0, 100, 100, 10000 - 100, IGRAPH_UNDIRECTED, IGRAPH_ALL); + printf("Testing large bipartite graph\n"); + run_tests(&graph_2, false); + + igraph_destroy(&graph_1); + igraph_destroy(&graph_2); +} + +int main(void) { + igraph_rng_seed(igraph_rng_default(), 42); + + test_null_graph(); + test_graph_1_vertex(); + test_graph_large(); + test_graph_small(); + test_graph_small_multi(); + test_graph_lcf(); + test_isolated_vertices(); + test_isolated_vertices_with_loops(); + test_wheel_graph(); + test_bipartite_graph(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_coloring.out b/tests/unit/igraph_coloring.out new file mode 100644 index 0000000..fb9f6e9 --- /dev/null +++ b/tests/unit/igraph_coloring.out @@ -0,0 +1,94 @@ +Testing null graph +testing greedy coloring with COLORED_NEIGHBORS heuristic +( ) +testing greedy coloring with DSATUR heuristic +( ) + +Testing singleton graph +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 1 +( 0 ) +testing greedy coloring with DSATUR heuristic +Number of colors used: 1 +( 0 ) + +Testing large simple graph +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 11 +testing greedy coloring with DSATUR heuristic +Number of colors used: 8 + +Testing large graph with loops and multi-edges +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 11 +testing greedy coloring with DSATUR heuristic +Number of colors used: 8 + +Testing small simple graph +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 4 +( 2 2 2 3 1 1 0 0 ) +testing greedy coloring with DSATUR heuristic +Number of colors used: 3 +( 2 2 2 0 1 1 1 0 ) + +Testing small multigraph +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 4 +( 3 1 1 1 0 0 0 2 ) +testing greedy coloring with DSATUR heuristic +Number of colors used: 3 +( 2 2 2 0 1 1 1 0 ) + +Testing small LCF graph +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 5 +( 0 4 3 2 1 0 2 1 ) +testing greedy coloring with DSATUR heuristic +Number of colors used: 5 +( 4 3 2 1 0 2 1 0 ) + +Testing graph with isolated vertices +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 2 +( 0 1 0 0 ) +testing greedy coloring with DSATUR heuristic +Number of colors used: 2 +( 1 0 0 0 ) + +Testing graph with isolated vertices and self-loops +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 1 +( 0 0 0 ) +testing greedy coloring with DSATUR heuristic +Number of colors used: 1 +( 0 0 0 ) + +Testing wheel graph with odd number of vertices +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 3 +( 0 2 1 2 1 2 1 2 1 2 1 ) +testing greedy coloring with DSATUR heuristic +Number of colors used: 3 +( 0 2 1 2 1 2 1 2 1 2 1 ) + +Testing wheel graph with even number of vertices +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 4 +( 0 3 2 1 2 1 2 1 2 1 2 1 ) +testing greedy coloring with DSATUR heuristic +Number of colors used: 4 +( 0 3 2 1 2 1 2 1 2 1 2 1 ) + +Testing complete bipartite graph +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 2 +testing greedy coloring with DSATUR heuristic +Number of colors used: 2 + +Testing large bipartite graph +testing greedy coloring with COLORED_NEIGHBORS heuristic +Number of colors used: 2 +testing greedy coloring with DSATUR heuristic +Number of colors used: 2 + diff --git a/tests/unit/igraph_community_eb_get_merges.c b/tests/unit/igraph_community_eb_get_merges.c new file mode 100644 index 0000000..2ee1df3 --- /dev/null +++ b/tests/unit/igraph_community_eb_get_merges.c @@ -0,0 +1,127 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g, igraph_bool_t directed, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_matrix_int_t *res, igraph_vector_int_t *bridges, + igraph_vector_t *modularity, igraph_vector_int_t *membership) { + igraph_community_eb_get_merges(g, 1, edges, weights, res, bridges, modularity, membership); + if (bridges) { + printf("Bridges:"); + igraph_vector_int_print(bridges); + } + if (modularity) { + printf("Modularity:"); + print_vector(modularity); + } + if (membership) { + printf("Membership:"); + igraph_vector_int_print(membership); + } + if (res) { + printf("Merges:\n"); + igraph_matrix_int_print(res); + } + printf("\n"); + igraph_destroy(g); +} + +int main(void) { + igraph_t g; + igraph_vector_int_t edges; + igraph_vector_t weights; + igraph_matrix_int_t res; + igraph_vector_int_t bridges; + igraph_vector_t modularity; + igraph_vector_int_t membership; + + igraph_matrix_int_init(&res, 0, 0); + igraph_vector_init(&weights, 0); + igraph_vector_int_init(&bridges, 0); + igraph_vector_init(&modularity, 0); + igraph_vector_int_init(&membership, 0); + + { + printf("Graph with no vertices:\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, -1); + igraph_vector_int_view(&edges, NULL, 0); + print_and_destroy(&g, 1, &edges, &weights, &res, &bridges, &modularity, &membership); + } + { + printf("Graph with one vertex:\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, -1); + igraph_vector_int_view(&edges, NULL, 0); + print_and_destroy(&g, 1, &edges, &weights, &res, &bridges, &modularity, &membership); + } + { + printf("Graph with two vertices, one edge:\n"); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + igraph_integer_t edge_array[] = {0}; + igraph_vector_int_view(&edges, edge_array, 1); + print_and_destroy(&g, 1, &edges, NULL, &res, &bridges, &modularity, &membership); + } + { + printf("Triangle, remove three edges:\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,2, -1); + igraph_integer_t edge_array[] = {0, 1, 2}; + igraph_vector_int_view(&edges, edge_array, 3); + print_and_destroy(&g, 1, &edges, NULL, &res, &bridges, &modularity, &membership); + } + { + printf("Two connected triangles, remove everything:\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0, 2,3, 3,4, 4,5, 5,3, -1); + igraph_integer_t edge_array[] = {3, 0, 1, 2, 4, 5, 6}; + igraph_vector_int_view(&edges, edge_array, 7); + print_and_destroy(&g, 1, &edges, NULL, &res, &bridges, NULL, &membership); + } + { + printf("Two connected triangles, remove everything, only check merges:\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0, 2,3, 3,4, 4,5, 5,3, -1); + igraph_integer_t edge_array[] = {3, 0, 1, 2, 4, 5, 6}; + igraph_vector_int_view(&edges, edge_array, 7); + print_and_destroy(&g, 1, &edges, NULL, &res, NULL, NULL, NULL); + } + + VERIFY_FINALLY_STACK(); + + { + printf("Check error when edge id is out of bounds.\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, -1); + igraph_integer_t edge_array[] = {3, 0, 1, 2, 4, 5, 6}; + igraph_vector_int_view(&edges, edge_array, 7); + CHECK_ERROR(igraph_community_eb_get_merges(&g, 1, &edges, NULL, NULL, NULL, NULL, NULL), IGRAPH_EINVAL); + igraph_destroy(&g); + } + { + printf("Check error when too few edges removed.\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,2, -1); + igraph_integer_t edge_array[] = {0, 1}; + igraph_vector_int_view(&edges, edge_array, 2); + CHECK_ERROR(igraph_community_eb_get_merges(&g, 1, &edges, NULL, NULL, NULL, NULL, NULL), IGRAPH_EINVAL); + igraph_destroy(&g); + } + + igraph_matrix_int_destroy(&res); + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&bridges); + igraph_vector_destroy(&modularity); + igraph_vector_int_destroy(&membership); + + VERIFY_FINALLY_STACK(); +} diff --git a/tests/unit/igraph_community_eb_get_merges.out b/tests/unit/igraph_community_eb_get_merges.out new file mode 100644 index 0000000..63320e3 --- /dev/null +++ b/tests/unit/igraph_community_eb_get_merges.out @@ -0,0 +1,47 @@ +Graph with no vertices: +Bridges: +Modularity:( NaN ) +Membership: +Merges: + +Graph with one vertex: +Bridges: +Modularity:( NaN ) +Membership: +Merges: + +Graph with two vertices, one edge: +Bridges:0 +Modularity:( -0.5 0 ) +Membership:0 0 +Merges: +1 0 + +Triangle, remove three edges: +Bridges:2 1 +Modularity:( -0.333333 -0.222222 0 ) +Membership:0 0 0 +Merges: +2 1 +3 0 + +Two connected triangles, remove everything: +Bridges:6 5 3 2 0 +Membership:0 1 2 3 4 5 +Merges: +5 3 +6 4 +2 0 +8 1 +7 9 + +Two connected triangles, remove everything, only check merges: +Merges: +3 5 +4 6 +0 2 +1 8 +9 7 + +Check error when edge id is out of bounds. +Check error when too few edges removed. diff --git a/tests/unit/igraph_community_edge_betweenness.c b/tests/unit/igraph_community_edge_betweenness.c new file mode 100644 index 0000000..d5bc08d --- /dev/null +++ b/tests/unit/igraph_community_edge_betweenness.c @@ -0,0 +1,196 @@ +/* -*- 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 +#include "test_utilities.h" + +int igraph_vector_between(const igraph_vector_t* v, const igraph_vector_t* lo, + const igraph_vector_t* hi) { + return igraph_vector_all_le(lo, v) && igraph_vector_all_ge(hi, v); +} + +void test_unweighted(void) { + igraph_t g; + igraph_vector_int_t edges; + igraph_vector_t eb; + igraph_integer_t i; + igraph_integer_t no_of_edges; + + /* Zachary Karate club */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + + igraph_vector_int_init(&edges, 0); + igraph_vector_init(&eb, 0); + igraph_community_edge_betweenness(&g, &edges, &eb, 0 /*merges */, + 0 /*bridges */, /*modularity=*/ 0, + /*membership=*/ 0, + IGRAPH_UNDIRECTED, + /*weights=*/ 0); + + no_of_edges = igraph_ecount(&g); + for (i = 0; i < no_of_edges; i++) { + printf("%" IGRAPH_PRId " ", VECTOR(edges)[i]); + } + printf("\n"); + + for (i = 0; i < no_of_edges; i++) { + printf("%.2f ", VECTOR(eb)[i]); + } + printf("\n"); + + /* Try it once again without storage space for edges */ + igraph_community_edge_betweenness(&g, 0, &eb, 0 /*merges */, + 0 /*bridges */, /*modularity=*/ 0, + /*membership=*/ 0, + IGRAPH_UNDIRECTED, + /*weights=*/ 0); + for (i = 0; i < no_of_edges; i++) { + printf("%.2f ", VECTOR(eb)[i]); + } + printf("\n"); + + igraph_vector_destroy(&eb); + igraph_vector_int_destroy(&edges); + igraph_destroy(&g); +} + +#define EPS 1e-4 + +void test_weighted(void) { + igraph_t g; + igraph_vector_int_t edges; + igraph_vector_t eb, weights; + igraph_real_t weights_array[] = { 4, 1, 3, 2, 5, 8, 6, 7 }; + + igraph_integer_t edges_array1[] = { 2, 3, 0, 1, 4, 7, 5, 6 }; + igraph_integer_t edges_array2[] = { 2, 3, 6, 5, 0, 1, 4, 7 }; + igraph_real_t eb_array1_lo[] = { 4, 5, 3 + 1 / 3.0 - EPS, 4, 2.5, 4, 1, 1 }; + igraph_real_t eb_array1_hi[] = { 4, 5, 3 + 1 / 3.0 + EPS, 4, 2.5, 4, 1, 1 }; + igraph_real_t eb_array2_lo[] = { 4, 5, 3 + 1 / 3.0 - EPS, 6, 1.5, 2, 1, 1 }; + igraph_real_t eb_array2_hi[] = { 4, 5, 3 + 1 / 3.0 + EPS, 6, 1.5, 2, 1, 1 }; + + igraph_vector_int_t edges_sol1, edges_sol2; + igraph_vector_t eb_sol1_lo, eb_sol1_hi, eb_sol2_lo, eb_sol2_hi; + + igraph_vector_int_view(&edges_sol1, edges_array1, sizeof(edges_array1) / sizeof(edges_array1[0])); + igraph_vector_int_view(&edges_sol2, edges_array2, sizeof(edges_array2) / sizeof(edges_array2[0])); + igraph_vector_view(&eb_sol1_lo, eb_array1_lo, sizeof(eb_array1_lo) / sizeof(eb_array1_lo[0])); + igraph_vector_view(&eb_sol2_lo, eb_array2_lo, sizeof(eb_array2_lo) / sizeof(eb_array2_lo[0])); + igraph_vector_view(&eb_sol1_hi, eb_array1_hi, sizeof(eb_array1_hi) / sizeof(eb_array1_hi[0])); + igraph_vector_view(&eb_sol2_hi, eb_array2_hi, sizeof(eb_array2_hi) / sizeof(eb_array2_hi[0])); + + /* Small graph as follows: A--B--C--A, A--D--E--A, B--D, C--E */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 2, 4, 3, 4, -1); + igraph_vector_view(&weights, weights_array, igraph_ecount(&g)); + + igraph_vector_int_init(&edges, 0); + igraph_vector_init(&eb, 0); + igraph_community_edge_betweenness(&g, &edges, &eb, 0 /*merges */, + 0 /*bridges */, /*modularity=*/ 0, + /*membership=*/ 0, + IGRAPH_UNDIRECTED, + &weights); + + if (!igraph_vector_int_all_e(&edges_sol1, &edges) && + !igraph_vector_int_all_e(&edges_sol2, &edges)) { + printf("Error, edges vector was: \n"); + igraph_vector_int_print(&edges); + exit(2); + } + if (!igraph_vector_between(&eb, &eb_sol1_lo, &eb_sol1_hi) && + !igraph_vector_between(&eb, &eb_sol2_lo, &eb_sol2_hi)) { + printf("Error, eb vector was: \n"); + igraph_vector_print(&eb); + exit(2); + } + + /* Try it once again without storage space for edges */ + igraph_community_edge_betweenness(&g, 0, &eb, 0 /*merges */, + 0 /*bridges */, /*modularity=*/ 0, + /*membership=*/ 0, + IGRAPH_UNDIRECTED, + &weights); + + if (!igraph_vector_between(&eb, &eb_sol1_lo, &eb_sol1_hi) && + !igraph_vector_between(&eb, &eb_sol2_lo, &eb_sol2_hi)) { + printf("Error, eb vector was: \n"); + igraph_vector_print(&eb); + exit(2); + } + + igraph_vector_destroy(&eb); + igraph_vector_int_destroy(&edges); + igraph_destroy(&g); +} + +void test_zero_edge_graph(void) { + igraph_t g; + igraph_vector_t eb; + igraph_vector_int_t res; + + igraph_full(&g, 1, 0, 0); + igraph_vector_int_init(&res, igraph_ecount(&g)); + igraph_vector_init(&eb, igraph_ecount(&g)); + + igraph_community_edge_betweenness(&g, + &res, // result + &eb, // edge_betweenness result + NULL, // merges result + NULL, // bridges + NULL, // modularity + NULL, // membership + IGRAPH_UNDIRECTED, // directed + NULL // weights + ); + + igraph_vector_destroy(&eb); + printf("No crash\n"); + igraph_vector_int_destroy(&res); + igraph_destroy(&g); +} + +int main(void) { + test_unweighted(); + test_weighted(); + test_zero_edge_graph(); + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_community_edge_betweenness.out b/tests/unit/igraph_community_edge_betweenness.out new file mode 100644 index 0000000..f0f3bea --- /dev/null +++ b/tests/unit/igraph_community_edge_betweenness.out @@ -0,0 +1,4 @@ +15 1 7 45 52 31 23 16 24 25 28 44 68 27 4 5 3 8 76 75 70 57 58 26 9 67 66 10 33 46 47 48 49 63 69 50 51 12 20 53 54 13 21 35 38 55 56 14 22 29 42 43 41 73 74 6 18 32 0 2 11 17 19 30 34 36 37 39 40 59 60 61 62 64 65 71 72 77 +71.39 66.90 77.32 82.00 123.23 100.21 143.63 109.25 107.67 142.75 285.00 16.83 18.18 18.00 15.33 25.33 25.00 50.00 14.50 22.37 25.62 29.65 40.67 72.00 9.00 9.00 11.00 5.50 8.00 5.00 10.00 4.50 9.00 4.50 9.00 4.00 8.00 3.50 7.00 3.50 7.00 3.00 6.00 3.00 6.00 3.00 6.00 2.50 5.00 2.00 2.00 3.50 5.00 2.00 4.00 1.33 2.00 4.00 1.00 1.50 3.00 1.00 2.00 1.00 1.00 1.00 1.00 2.00 1.00 1.00 1.50 3.00 1.00 2.00 1.00 1.00 2.00 1.00 +71.39 66.90 77.32 82.00 123.23 100.21 143.63 109.25 107.67 142.75 285.00 16.83 18.18 18.00 15.33 25.33 25.00 50.00 14.50 22.37 25.62 29.65 40.67 72.00 9.00 9.00 11.00 5.50 8.00 5.00 10.00 4.50 9.00 4.50 9.00 4.00 8.00 3.50 7.00 3.50 7.00 3.00 6.00 3.00 6.00 3.00 6.00 2.50 5.00 2.00 2.00 3.50 5.00 2.00 4.00 1.33 2.00 4.00 1.00 1.50 3.00 1.00 2.00 1.00 1.00 1.00 1.00 2.00 1.00 1.00 1.50 3.00 1.00 2.00 1.00 1.00 2.00 1.00 +No crash diff --git a/tests/unit/igraph_community_fastgreedy.c b/tests/unit/igraph_community_fastgreedy.c new file mode 100644 index 0000000..60ca159 --- /dev/null +++ b/tests/unit/igraph_community_fastgreedy.c @@ -0,0 +1,219 @@ +/* -*- 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 +#include "test_utilities.h" + +void show_results(igraph_t *g, igraph_vector_t *mod, igraph_matrix_int_t *merges, + igraph_vector_int_t *membership, FILE* f) { + igraph_integer_t i = 0; + igraph_vector_int_t our_membership; + + igraph_vector_int_init(&our_membership, 0); + + if (mod != 0) { + i = igraph_vector_which_max(mod); + fprintf(f, "Modularity: "); + igraph_real_fprintf(f, VECTOR(*mod)[i]); + fprintf(f, "\n"); + } else { + fprintf(f, "Modularity: ---\n"); + } + + if (membership != 0) { + igraph_vector_int_update(&our_membership, membership); + } else if (merges != 0) { + igraph_community_to_membership(merges, igraph_vcount(g), i, &our_membership, 0); + } + + printf("Membership: "); + for (i = 0; i < igraph_vector_int_size(&our_membership); i++) { + printf("%" IGRAPH_PRId " ", VECTOR(our_membership)[i]); + } + printf("\n"); + + igraph_vector_int_destroy(&our_membership); +} + +int main(void) { + igraph_t g; + igraph_vector_t modularity, weights; + igraph_vector_int_t membership; + igraph_matrix_int_t merges; + + igraph_vector_init(&modularity, 0); + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_init(&weights, 0); + igraph_vector_int_init(&membership, 0); + + /* Simple unweighted graph */ + igraph_small(&g, 10, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 5, 6, 5, 7, 5, 8, 5, 9, 6, 7, 6, 8, 6, 9, 7, 8, 7, 9, 8, 9, + 0, 5, -1); + igraph_community_fastgreedy(&g, 0, &merges, &modularity, /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + + /* Same simple graph, with uniform edge weights */ + igraph_vector_resize(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 2); + igraph_community_fastgreedy(&g, &weights, &merges, &modularity, + /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_destroy(&g); + + /* Simple nonuniform weighted graph, with and without weights */ + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5, -1); + igraph_vector_resize(&weights, 8); + igraph_vector_fill(&weights, 1); + VECTOR(weights)[0] = 10; + VECTOR(weights)[1] = 10; + igraph_community_fastgreedy(&g, 0, &merges, &modularity, /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_community_fastgreedy(&g, &weights, &merges, &modularity, + /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_destroy(&g); + + /* Zachary Karate club */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + igraph_community_fastgreedy(&g, 0, &merges, &modularity, + /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_destroy(&g); + + /* Simple disconnected graph with isolates */ + igraph_small(&g, 9, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, + 4, 5, 4, 6, 4, 7, 5, 6, 5, 7, 6, 7, + -1); + igraph_community_fastgreedy(&g, 0, &merges, &modularity, /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_destroy(&g); + + /* Disjoint union of two rings */ + igraph_small(&g, 20, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 0, 9, + 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 10, 19, -1); + igraph_community_fastgreedy(&g, 0, &merges, &modularity, /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_destroy(&g); + + /* Completely empty graph */ + igraph_small(&g, 10, IGRAPH_UNDIRECTED, -1); + igraph_community_fastgreedy(&g, 0, &merges, &modularity, /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_destroy(&g); + + /* Ring graph with loop edges */ + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 0, 0, 0, 2, 2, -1); + igraph_community_fastgreedy(&g, 0, &merges, &modularity, /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_destroy(&g); + + /* Regression test -- graph with two vertices and two edges */ + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0, 0, 1, 1, -1); + igraph_community_fastgreedy(&g, 0, &merges, &modularity, /*membership=*/ 0); + show_results(&g, &modularity, &merges, 0, stdout); + igraph_destroy(&g); + + /* Regression test -- asking for optimal membership vector but not + * providing a modularity vector */ + igraph_small(&g, 10, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 5, 6, 5, 7, 5, 8, 5, 9, 6, 7, 6, 8, 6, 9, 7, 8, 7, 9, 8, 9, + 0, 5, -1); + igraph_community_fastgreedy(&g, 0, &merges, 0, &membership); + show_results(&g, 0, &merges, &membership, stdout); + igraph_destroy(&g); + + /* Regression test -- asking for optimal membership vector but not + * providing a merge matrix */ + igraph_small(&g, 10, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 5, 6, 5, 7, 5, 8, 5, 9, 6, 7, 6, 8, 6, 9, 7, 8, 7, 9, 8, 9, + 0, 5, -1); + igraph_community_fastgreedy(&g, 0, 0, &modularity, &membership); + show_results(&g, &modularity, 0, &membership, stdout); + + /* Regression test -- asking for optimal membership vector but not + * providing a merge matrix or a modularity vector */ + igraph_community_fastgreedy(&g, 0, 0, 0, &membership); + show_results(&g, 0, 0, &membership, stdout); + + /* Regression test -- asking for modularity but nothing else */ + igraph_community_fastgreedy(&g, 0, 0, &modularity, 0); + show_results(&g, &modularity, 0, 0, stdout); + + igraph_destroy(&g); + + printf("Testing a trivial disconnected graph\n"); + + /* Test a disconnected graph */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0,1, 2,3, + -1); + + /* Asking for all output to verify the result from the following + * two tests. */ + igraph_community_fastgreedy(&g, 0, &merges, &modularity, &membership); + show_results(&g, &modularity, &merges, &membership, stdout); + + /* Regression test -- asking for optimal membership vector but not + * providing a merge matrix or a modularity vector */ + igraph_community_fastgreedy(&g, 0, 0, 0, &membership); + show_results(&g, 0, 0, &membership, stdout); + + /* Regression test -- asking for modularity but nothing else */ + igraph_community_fastgreedy(&g, 0, 0, &modularity, 0); + show_results(&g, &modularity, 0, 0, stdout); + + igraph_destroy(&g); + + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&modularity); + igraph_vector_destroy(&weights); + igraph_matrix_int_destroy(&merges); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_community_fastgreedy.out b/tests/unit/igraph_community_fastgreedy.out new file mode 100644 index 0000000..f342a14 --- /dev/null +++ b/tests/unit/igraph_community_fastgreedy.out @@ -0,0 +1,35 @@ +Modularity: 0.452381 +Membership: 1 1 1 1 1 0 0 0 0 0 +Modularity: 0.452381 +Membership: 1 1 1 1 1 0 0 0 0 0 +Modularity: 0.179688 +Membership: 1 1 0 0 0 0 +Modularity: 0.170858 +Membership: 1 1 1 0 0 0 +Modularity: 0.380671 +Membership: 0 2 2 2 0 0 0 2 1 2 0 0 2 2 1 1 0 2 1 0 1 2 1 1 1 1 1 1 1 1 1 1 1 1 +Modularity: 0.5 +Membership: 1 1 1 1 0 0 0 0 2 +Modularity: 0.54 +Membership: 1 1 1 1 3 3 3 3 1 1 0 0 0 0 0 0 2 2 2 2 +Modularity: NaN +Membership: 0 1 2 3 4 5 6 7 8 9 +Modularity: 0.28125 +Membership: 0 1 1 2 2 0 +Modularity: 0.5 +Membership: 0 1 +Modularity: --- +Membership: 1 1 1 1 1 0 0 0 0 0 +Modularity: 0.452381 +Membership: 1 1 1 1 1 0 0 0 0 0 +Modularity: --- +Membership: 1 1 1 1 1 0 0 0 0 0 +Modularity: 0.452381 +Membership: +Testing a trivial disconnected graph +Modularity: 0.5 +Membership: 1 1 0 0 +Modularity: --- +Membership: 1 1 0 0 +Modularity: 0.5 +Membership: diff --git a/tests/unit/igraph_community_fluid_communities.c b/tests/unit/igraph_community_fluid_communities.c new file mode 100644 index 0000000..f80a998 --- /dev/null +++ b/tests/unit/igraph_community_fluid_communities.c @@ -0,0 +1,92 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_integer_t k; + igraph_vector_int_t membership; + + igraph_rng_seed(igraph_rng_default(), 247); + + /* Empty graph */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, -1); + igraph_vector_int_init(&membership, 0); + igraph_vector_int_push_back(&membership, 1); + igraph_community_fluid_communities(&g, 2, &membership); + if (igraph_vector_int_size(&membership) != 0) { + return 2; + } + igraph_vector_int_destroy(&membership); + igraph_destroy(&g); + + /* Graph with one vertex only */ + igraph_small(&g, 1, IGRAPH_UNDIRECTED, -1); + igraph_vector_int_init(&membership, 0); + igraph_community_fluid_communities(&g, 2, &membership); + if (igraph_vector_int_size(&membership) != 1 || VECTOR(membership)[0] != 0) { + return 3; + } + igraph_vector_int_destroy(&membership); + igraph_destroy(&g); + + /* Zachary Karate club -- this is just a quick smoke test */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + + igraph_vector_int_init(&membership, 0); + k = 2; + igraph_community_fluid_communities(&g, k, &membership); + if (!igraph_vector_int_contains(&membership, 0) || !igraph_vector_int_contains(&membership, 1)) { + printf("Resulting graph does not have exactly 2 communities as expected.\n"); + igraph_vector_int_print(&membership); + return 1; + } + + igraph_destroy(&g); + igraph_vector_int_destroy(&membership); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_community_fluid_communities.out b/tests/unit/igraph_community_fluid_communities.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/igraph_community_infomap.c b/tests/unit/igraph_community_infomap.c new file mode 100644 index 0000000..af7366b --- /dev/null +++ b/tests/unit/igraph_community_infomap.c @@ -0,0 +1,269 @@ +/* -*- 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 + +#include "test_utilities.h" + + +void gsummary(const igraph_t * g) { + printf("|V|=%" IGRAPH_PRId " |E|=%" IGRAPH_PRId " directed=%d\n", + igraph_vcount(g), igraph_ecount(g), (int) igraph_is_directed(g)); +} + +void show_results(igraph_vector_int_t * membership, igraph_real_t codelength) { + int i; + printf("Codelength: %0.5f (in %" IGRAPH_PRId " modules)\n", codelength, igraph_vector_int_max(membership) + 1 ); + printf("Membership: "); + for (i = 0; i < igraph_vector_int_size(membership); i++) { + printf("%" IGRAPH_PRId " ", VECTOR(*membership)[i] ); + } + printf("\n"); +} + +void show_results_lite(igraph_vector_int_t * membership, igraph_real_t codelength) { + int i; + printf("Codelength: %0.5f (in %" IGRAPH_PRId " modules)\n", codelength, igraph_vector_int_max(membership) + 1 ); + printf("Membership (1/100 of vertices): "); + for (i = 0; i < igraph_vector_int_size(membership); i += 100) { + printf("%" IGRAPH_PRId " ", VECTOR(*membership)[i] ); + } + printf("\n"); +} + +igraph_real_t infomap_weighted_test(const igraph_t * g, const igraph_vector_t *weights, igraph_bool_t smoke_test) { + igraph_vector_int_t membership; + igraph_real_t codelength = 1000; + igraph_vector_int_init(&membership, 0); + + igraph_community_infomap(/*in */ g, /*e_weight=*/ weights, NULL, /*nb_trials=*/5, + /*out*/ &membership, &codelength); + if (!smoke_test) { + if (igraph_vcount(g) > 500) { + show_results_lite(&membership, codelength); + } else { + show_results(&membership, codelength); + } + } + + igraph_vector_int_destroy(&membership); + + return codelength; +} + + +igraph_real_t infomap_test(const igraph_t * g, igraph_bool_t smoke_test) { + return infomap_weighted_test(g, 0, smoke_test); +} + + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_real_t codelength; + FILE *wikt; + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Two triangles connected by one edge */ + printf("# Two triangles connected by one edge\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 0, + 3, 4, 4, 5, 5, 3, + 0, 5, + -1); + infomap_test(&g, /* smoke_test = */ 0); + igraph_destroy(&g); + + /* Two 4-cliques (0123 and 4567) connected by two edges (0-4 and 1-5) */ + printf("# Two 4-cliques (0123 and 4567) connected by two edges (0-4 and 1-5)\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, /* 4-clique 0,1,2,3 */ + 7, 4, 7, 5, 7, 6, 4, 5, 4, 6, 5, 6, /* 4-clique 4,5,6,7 */ + 0, 4, 1, 5, /* 8, 0, 8, 4, */ + -1); + infomap_test(&g, /* smoke_test = */ 0); + igraph_destroy(&g); + + /* Zachary Karate club -- this is just a quick smoke test */ + printf("# Zachary Karate club\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, /* 0, 5, 0, 5, 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + infomap_test(&g, /* smoke_test = */ 0); + igraph_destroy(&g); + + /* Flow.net that come in infomap_dir.tgz */ + printf("# Flow (from infomap_dir.tgz)\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 3, 0, 1, 4, + 4, 5, 5, 6, 6, 7, 7, 4, 5, 8, + 8, 9, 9, 10, 10, 11, 11, 8, 9, 12, + 12, 13, 13, 14, 14, 15, 15, 12, 13, 0, + -1); + infomap_test(&g, /* smoke_test = */ 0); + igraph_destroy(&g); + + /* MultiphysChemBioEco40W_weighted_dir.net */ + printf("# MultiphysChemBioEco40W_weighted_dir.net (from infomap_dir.tgz)\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, + 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, + 8, 0, 9, 0, 16, 0, 18, 0, 0, 1, 2, 1, 3, 1, + 5, 1, 6, 1, 7, 1, 9, 1, 10, 1, 16, 1, 18, 1, + 0, 2, 3, 2, 4, 2, 5, 2, 6, 2, 7, 2, 0, 3, + 1, 3, 2, 3, 4, 3, 5, 3, 6, 3, 7, 3, 8, 3, + 9, 3, 10, 3, 11, 3, 13, 3, 14, 3, 16, 3, 17, 3, + 18, 3, 19, 3, 26, 3, 30, 3, 1, 4, 3, 4, 5, 4, + 6, 4, 13, 4, 18, 4, 0, 5, 1, 5, 2, 5, 3, 5, + 6, 5, 7, 5, 9, 5, 1, 6, 3, 6, 7, 6, 9, 6, + 16, 6, 0, 7, 1, 7, 2, 7, 3, 7, 5, 7, 6, 7, + 9, 7, 3, 8, 5, 8, 3, 9, 7, 9, 12, 10, 13, 10, + 14, 10, 15, 10, 16, 10, 17, 10, 18, 10, 19, 10, + 21, 10, 3, 11, 18, 11, 10, 12, 14, 12, 16, 12, + 17, 12, 18, 12, 3, 13, 10, 13, 14, 13, 16, 13, + 10, 14, 12, 14, 13, 14, 15, 14, 16, 14, 17, 14, + 18, 14, 10, 15, 14, 15, 18, 15, 0, 16, 2, 16, + 3, 16, 6, 16, 10, 16, 12, 16, 13, 16, 14, 16, + 17, 16, 18, 16, 10, 17, 12, 17, 14, 17, 18, 17, + 3, 18, 10, 18, 12, 18, 14, 18, 15, 18, 16, 18, + 17, 18, 19, 18, 21, 18, 11, 19, 16, 19, 17, 19, + 16, 20, 18, 20, 21, 20, 22, 20, 23, 20, 24, 20, + 25, 20, 26, 20, 27, 20, 28, 20, 29, 20, 3, 21, + 14, 21, 18, 21, 20, 21, 22, 21, 23, 21, 24, 21, + 25, 21, 26, 21, 27, 21, 28, 21, 29, 21, 35, 21, + 36, 21, 38, 21, 18, 22, 20, 22, 21, 22, 23, 22, + 24, 22, 25, 22, 26, 22, 27, 22, 29, 22, 3, 23, + 20, 23, 21, 23, 22, 23, 24, 23, 25, 23, 26, 23, + 27, 23, 28, 23, 29, 23, 35, 23, 38, 23, 39, 23, + 20, 24, 21, 24, 23, 24, 25, 24, 26, 24, 27, 24, + 28, 24, 29, 24, 9, 25, 20, 25, 21, 25, 22, 25, + 23, 25, 24, 25, 26, 25, 27, 25, 28, 25, 29, 25, + 18, 26, 20, 26, 21, 26, 22, 26, 23, 26, 25, 26, + 27, 26, 28, 26, 29, 26, 30, 26, 32, 26, 35, 26, + 36, 26, 38, 26, 39, 26, 3, 27, 14, 27, 20, 27, + 21, 27, 22, 27, 23, 27, 24, 27, 25, 27, 26, 27, + 28, 27, 29, 27, 38, 27, 3, 28, 18, 28, 20, 28, + 21, 28, 23, 28, 24, 28, 25, 28, 26, 28, 27, 28, + 29, 28, 35, 28, 14, 29, 16, 29, 18, 29, 20, 29, + 21, 29, 22, 29, 23, 29, 24, 29, 25, 29, 26, 29, + 27, 29, 28, 29, 31, 30, 32, 30, 33, 30, 34, 30, + 35, 30, 36, 30, 38, 30, 39, 30, 30, 31, 32, 31, + 34, 31, 36, 31, 30, 32, 34, 32, 35, 32, 36, 32, + 30, 33, 32, 33, 34, 33, 35, 33, 36, 33, 38, 33, + 30, 34, 31, 34, 32, 34, 33, 34, 35, 34, 36, 34, + 38, 34, 39, 34, 26, 35, 30, 35, 32, 35, 33, 35, + 34, 35, 36, 35, 38, 35, 39, 35, 30, 36, 34, 36, + 35, 36, 38, 36, 39, 36, 34, 37, 26, 38, 30, 38, + 32, 38, 33, 38, 34, 38, 35, 38, 36, 38, 39, 38, + 26, 39, 30, 39, 33, 39, 34, 39, 35, 39, 36, 39, + 38, 39, + -1); + igraph_vector_init_real(&weights, 306, + 5.0, 3.0, 130.0, 4.0, 15.0, 9.0, + 7.0, 1.0, 1.0, 3.0, 1.0, 1.0, + 1.0, 34.0, 38.0, 2.0, 23.0, 1.0, + 1.0, 3.0, 2.0, 2.0, 16.0, 1.0, + 3.0, 1.0, 3.0, 63.0, 92.0, 72.0, + 25.0, 447.0, 121.0, 65.0, 4.0, 16.0, + 35.0, 1.0, 19.0, 1.0, 78.0, 1.0, + 45.0, 1.0, 3.0, 1.0, 1.0, 25.0, + 1.0, 3.0, 1.0, 1.0, 3.0, 36.0, + 19.0, 136.0, 41.0, 96.0, 1.0, 7.0, + 26.0, 1.0, 2.0, 2.0, 3.0, 2.0, 2.0, + 23.0, 52.0, 4.0, 1.0, 2.0, 1.0, 3.0, + 1.0, 11.0, 2.0, 17.0, 1.0, 5.0, 18.0, + 86.0, 5.0, 1.0, 1.0, 1.0, 6.0, 1.0, + 2.0, 2.0, 20.0, 4.0, 5.0, 1.0, 5.0, + 12.0, 4.0, 1.0, 1.0, 4.0, 9.0, 40.0, + 2.0, 1.0, 4.0, 1.0, 1.0, 48.0, 2.0, + 18.0, 1.0, 7.0, 2.0, 2.0, 53.0, 25.0, + 9.0, 1.0, 23.0, 8.0, 62.0, 29.0, 35.0, + 4.0, 34.0, 35.0, 3.0, 1.0, 24.0, 1.0, + 6.0, 2.0, 2.0, 22.0, 7.0, 2.0, 5.0, + 14.0, 3.0, 28.0, 14.0, 20.0, 3.0, 1.0, + 5.0, 77.0, 20.0, 25.0, 35.0, 55.0, 35.0, + 115.0, 68.0, 105.0, 2.0, 2.0, 2.0, 4.0, + 2.0, 17.0, 12.0, 3.0, 3.0, 11.0, 10.0, + 7.0, 2.0, 12.0, 31.0, 11.0, 5.0, 11.0, + 65.0, 39.0, 17.0, 26.0, 3.0, 4.0, 2.0, + 3.0, 6.0, 4.0, 8.0, 1.0, 7.0, 7.0, + 6.0, 1.0, 39.0, 42.0, 9.0, 6.0, 9.0, + 5.0, 45.0, 43.0, 26.0, 1.0, 2.0, 6.0, + 2.0, 15.0, 3.0, 9.0, 2.0, 1.0, 1.0, + 1.0, 4.0, 2.0, 9.0, 2.0, 1.0, 2.0, + 28.0, 80.0, 10.0, 18.0, 13.0, 17.0, + 28.0, 40.0, 76.0, 1.0, 2.0, 1.0, 11.0, + 37.0, 5.0, 11.0, 14.0, 4.0, 14.0, 10.0, + 1.0, 1.0, 1.0, 1.0, 41.0, 121.0, 6.0, + 21.0, 12.0, 30.0, 6.0, 141.0, 43.0, 2.0, + 12.0, 6.0, 35.0, 10.0, 7.0, 2.0, 12.0, + 6.0, 2.0, 11.0, 1.0, 7.0, 6.0, 5.0, 3.0, + 1.0, 2.0, 1.0, 1.0, 1.0, 1.0, 67.0, 9.0, + 9.0, 11.0, 10.0, 21.0, 7.0, 12.0, 9.0, + 16.0, 7.0, 4.0, 11.0, 17.0, 37.0, 32.0, + 9.0, 2.0, 2.0, 5.0, 4.0, 2.0, 7.0, 3.0, + 3.0, 5.0, 8.0, 14.0, 3.0, 38.0, 3.0, 9.0, + 2.0, 8.0, 21.0, 18.0, 58.0); + infomap_weighted_test(&g, &weights, /* smoke_test = */ 0); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* Wiktionary English verbs -- this one is a bit flaky, igraph reports + * 1444 or 1445 modules, depending on the platform, so let's just run it as + * a quick smoke test but don't check the results too thoroughly as some + * changes are expected. We only check the codelength of the partition, + * this is more reliable. */ + printf("# Wiktionary english verbs (synonymy 2008)\n"); + wikt = fopen("wikti_en_V_syn.elist", "r"); + IGRAPH_ASSERT(wikt != NULL); + igraph_read_graph_edgelist(&g, wikt, 0, IGRAPH_UNDIRECTED); + fclose(wikt); + gsummary(&g); + codelength = infomap_test(&g, /* smoke_test = */ 1); + if (fabs(codelength - 5.708) >= 1e-3) { + printf("Codelength was %0.5f, expected %0.5f\n", codelength, 5.708); + } else { + printf("Codelength OK.\n"); + } + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_community_infomap.out b/tests/unit/igraph_community_infomap.out new file mode 100644 index 0000000..affa78c --- /dev/null +++ b/tests/unit/igraph_community_infomap.out @@ -0,0 +1,18 @@ +# Two triangles connected by one edge +Codelength: 2.51787 (in 2 modules) +Membership: 0 0 0 1 1 1 +# Two 4-cliques (0123 and 4567) connected by two edges (0-4 and 1-5) +Codelength: 2.94884 (in 2 modules) +Membership: 0 0 0 0 1 1 1 1 +# Zachary Karate club +Codelength: 4.60606 (in 3 modules) +Membership: 0 0 0 0 1 1 1 0 2 0 1 0 0 0 2 2 1 0 2 0 2 0 2 2 2 2 2 2 2 2 2 2 2 2 +# Flow (from infomap_dir.tgz) +Codelength: 3.32773 (in 4 modules) +Membership: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 +# MultiphysChemBioEco40W_weighted_dir.net (from infomap_dir.tgz) +Codelength: 3.87095 (in 5 modules) +Membership: 0 0 0 0 0 0 0 0 0 0 1 1 1 0 1 1 0 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 4 3 3 +# Wiktionary english verbs (synonymy 2008) +|V|=7339 |E|=8293 directed=0 +Codelength OK. diff --git a/tests/unit/igraph_community_leading_eigenvector2.c b/tests/unit/igraph_community_leading_eigenvector2.c new file mode 100644 index 0000000..5cd943a --- /dev/null +++ b/tests/unit/igraph_community_leading_eigenvector2.c @@ -0,0 +1,99 @@ +/* -*- 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 + +#include "test_utilities.h" + + +int main(void) { + + igraph_t g; + igraph_matrix_int_t merges; + igraph_vector_int_t membership; + igraph_vector_t x; + igraph_arpack_options_t options; + igraph_vector_t weights; + + /* Zachary Karate club */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 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, 8, 2, 9, 2, 13, 2, 27, + 2, 28, 2, 32, 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, 29, + 23, 32, 23, 33, 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, + -1); + + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_int_init(&membership, 0); + igraph_vector_init(&x, 0); + igraph_arpack_options_init(&options); + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1); + + igraph_community_leading_eigenvector(&g, &weights, &merges, + &membership, 1, + /* options = */ 0, /*modularity=*/ 0, + /*start=*/ 0, /*eigenvalues=*/ 0, + /*eigenvectors=*/ 0, /*history=*/ 0, + /*callback=*/ 0, + /*callback_extra=*/ 0); + + igraph_matrix_int_print(&merges); + print_vector_int(&membership); + + printf("\n"); + + /* Make all the steps, and use an explicit options object */ + igraph_community_leading_eigenvector(&g, &weights, &merges, + &membership, igraph_vcount(&g), + &options, /*modularity=*/ 0, + /*start=*/ 0, /*eigenvalues=*/ 0, + /*eigenvectors=*/ 0, /*history=*/ 0, + /*callback=*/ 0, + /*callback_extra=*/ 0); + + igraph_matrix_int_print(&merges); + print_vector_int(&membership); + + igraph_vector_destroy(&weights); + igraph_vector_destroy(&x); + igraph_vector_int_destroy(&membership); + igraph_matrix_int_destroy(&merges); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_community_leading_eigenvector2.out b/tests/unit/igraph_community_leading_eigenvector2.out new file mode 100644 index 0000000..e931b79 --- /dev/null +++ b/tests/unit/igraph_community_leading_eigenvector2.out @@ -0,0 +1,7 @@ +0 1 +( 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 ) + +1 3 +0 2 +5 4 +( 0 2 2 2 0 0 0 2 1 1 0 0 2 2 1 1 0 2 1 2 1 2 1 3 3 3 1 3 3 1 1 3 1 1 ) diff --git a/tests/unit/igraph_community_voronoi.c b/tests/unit/igraph_community_voronoi.c new file mode 100644 index 0000000..ac7283f --- /dev/null +++ b/tests/unit/igraph_community_voronoi.c @@ -0,0 +1,120 @@ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t graph; + igraph_vector_int_t membership, generators; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_int_init(&membership, 0); + igraph_vector_int_init(&generators, 0); + + printf("Null graph:\n"); + + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + + igraph_community_voronoi(&graph, &membership, &generators, NULL, NULL, NULL, IGRAPH_ALL, -1); + + printf("Membership: "); print_vector_int(&membership); + printf("Generators: "); print_vector_int(&generators); + + igraph_destroy(&graph); + + printf("\nSingleton graph:\n"); + + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + + igraph_community_voronoi(&graph, &membership, &generators, NULL, NULL, NULL, IGRAPH_ALL, -1); + + printf("Membership: "); print_vector_int(&membership); + printf("Generators: "); print_vector_int(&generators); + + igraph_destroy(&graph); + + printf("\nTwo isolated nodes:\n"); + + igraph_empty(&graph, 2, IGRAPH_UNDIRECTED); + + igraph_community_voronoi(&graph, &membership, &generators, NULL, NULL, NULL, IGRAPH_ALL, -1); + + printf("Membership: "); print_vector_int(&membership); + printf("Generators: "); print_vector_int(&generators); + + igraph_destroy(&graph); + + printf("\nZachary:\n"); + + igraph_famous(&graph, "Zachary"); + + igraph_community_voronoi(&graph, &membership, &generators, NULL, NULL, NULL, IGRAPH_ALL, -1); + + printf("Membership: "); print_vector_int(&membership); + printf("Generators: "); print_vector_int(&generators); + + { + igraph_vector_t betw, ibetw; + + igraph_vector_init(&betw, 0); + + igraph_edge_betweenness(&graph, &betw, IGRAPH_UNDIRECTED, NULL); + + igraph_integer_t n = igraph_vector_size(&betw); + igraph_vector_init_copy(&ibetw, &betw); + for (igraph_integer_t i=0; i < n; i++) { + VECTOR(ibetw)[i] = 1.0 / VECTOR(betw)[i]; + } + + printf("\nZachary, betweenness weighted:\n"); + + igraph_community_voronoi(&graph, &membership, &generators, NULL, /*lengths=*/ &betw, /*weights=*/ &ibetw, IGRAPH_ALL, -1); + + printf("Membership: "); print_vector_int(&membership); + printf("Generators: "); print_vector_int(&generators); + + igraph_vector_destroy(&ibetw); + igraph_vector_destroy(&betw); + } + + igraph_destroy(&graph); + + { + igraph_t g1, g2; + + igraph_erdos_renyi_game_gnm(&g1, 10, 30, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_erdos_renyi_game_gnm(&g2, 10, 30, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + + igraph_disjoint_union(&graph, &g1, &g2); + igraph_add_edge(&graph, 9, 10); + igraph_add_edge(&graph, 10, 9); + + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("\nDirected two-block, in:\n"); + + igraph_community_voronoi(&graph, &membership, &generators, NULL, NULL, NULL, IGRAPH_IN, -1); + + printf("Membership: "); print_vector_int(&membership); + printf("Generators: "); print_vector_int(&generators); + + printf("\nDirected two-block, out:\n"); + + igraph_community_voronoi(&graph, &membership, &generators, NULL, NULL, NULL, IGRAPH_OUT, -1); + + printf("Membership: "); print_vector_int(&membership); + printf("Generators: "); print_vector_int(&generators); + + igraph_destroy(&graph); + } + + igraph_vector_int_destroy(&generators); + igraph_vector_int_destroy(&membership); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_community_voronoi.out b/tests/unit/igraph_community_voronoi.out new file mode 100644 index 0000000..1df8de4 --- /dev/null +++ b/tests/unit/igraph_community_voronoi.out @@ -0,0 +1,27 @@ +Null graph: +Membership: ( ) +Generators: ( ) + +Singleton graph: +Membership: ( 0 ) +Generators: ( 0 ) + +Two isolated nodes: +Membership: ( 0 1 ) +Generators: ( 0 1 ) + +Zachary: +Membership: ( 1 1 1 1 1 1 1 1 0 0 1 1 1 1 0 0 1 1 0 1 0 1 0 0 2 2 0 0 0 0 0 2 0 0 ) +Generators: ( 33 0 24 ) + +Zachary, betweenness weighted: +Membership: ( 1 1 1 1 2 2 2 1 1 0 2 1 1 1 0 0 2 1 0 1 0 1 0 0 3 3 0 3 0 0 0 0 0 0 ) +Generators: ( 33 1 6 24 ) + +Directed two-block, in: +Membership: ( 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 ) +Generators: ( 13 4 ) + +Directed two-block, out: +Membership: ( 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 ) +Generators: ( 13 0 ) diff --git a/tests/unit/igraph_compare_communities.c b/tests/unit/igraph_compare_communities.c new file mode 100644 index 0000000..93cdf48 --- /dev/null +++ b/tests/unit/igraph_compare_communities.c @@ -0,0 +1,101 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +char *names[] = { + [IGRAPH_COMMCMP_VI] = "VI", + [IGRAPH_COMMCMP_NMI] = "NMI", + [IGRAPH_COMMCMP_SPLIT_JOIN] = "Split-join", + [IGRAPH_COMMCMP_RAND] = "Rand", + [IGRAPH_COMMCMP_ADJUSTED_RAND] = "Adjusted Rand", +}; + + +void compare_and_print(igraph_vector_int_t *comm1, igraph_vector_int_t *comm2, igraph_community_comparison_t t, igraph_error_t e) { + igraph_real_t result; + printf("%s result: ", names[t]); + IGRAPH_ASSERT(igraph_compare_communities(comm1, comm2, &result, t) == e); + if (e == IGRAPH_EINVAL) { + printf("failed as expected\n"); + } else { + print_real(stdout, result, "%g"); + printf("\n"); + } +} + + +int main(void) { + igraph_vector_int_t comm1, comm2; + + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Only one member, both partitions equal to whole set:\n"); + igraph_vector_int_init_int(&comm1, 1, 0); + igraph_vector_int_init_int(&comm2, 1, 0); + + + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_VI, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_RAND, IGRAPH_EINVAL); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_ADJUSTED_RAND, IGRAPH_EINVAL); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_NMI, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_SPLIT_JOIN, IGRAPH_SUCCESS); + + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + printf("\nEmpty sets:\n"); + igraph_vector_int_init(&comm1, 0); + igraph_vector_int_init(&comm2, 0); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_VI, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_RAND, IGRAPH_EINVAL); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_ADJUSTED_RAND, IGRAPH_EINVAL); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_NMI, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_SPLIT_JOIN, IGRAPH_SUCCESS); + + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + printf("\nTwo equal, differenly labeled partitions:\n"); + igraph_vector_int_init_int(&comm1, 2, 0, 1); + igraph_vector_int_init_int(&comm2, 2, 1, 0); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_VI, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_RAND, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_ADJUSTED_RAND, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_NMI, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_SPLIT_JOIN, IGRAPH_SUCCESS); + + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + printf("\nTwo different partitions: ((5,1), (8,3,4), (0,6,7,2,9)) and ((5,8), (1,3,4,0), (6,7,2,9))\n"); + igraph_vector_int_init_int(&comm1, 10, 2, 0, 2, 1, 1, 0, 2, 2, 1, 2); + igraph_vector_int_init_int(&comm2, 10, 1, 1, 2, 1, 1, 0, 2, 2, 0, 2); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_VI, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_RAND, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_ADJUSTED_RAND, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_NMI, IGRAPH_SUCCESS); + compare_and_print(&comm1, &comm2, IGRAPH_COMMCMP_SPLIT_JOIN, IGRAPH_SUCCESS); + + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_compare_communities.out b/tests/unit/igraph_compare_communities.out new file mode 100644 index 0000000..fc01149 --- /dev/null +++ b/tests/unit/igraph_compare_communities.out @@ -0,0 +1,27 @@ +Only one member, both partitions equal to whole set: +VI result: 0 +Rand result: failed as expected +Adjusted Rand result: failed as expected +NMI result: 1 +Split-join result: 0 + +Empty sets: +VI result: 0 +Rand result: failed as expected +Adjusted Rand result: failed as expected +NMI result: 1 +Split-join result: 0 + +Two equal, differenly labeled partitions: +VI result: 0 +Rand result: 1 +Adjusted Rand result: NaN +NMI result: 1 +Split-join result: 0 + +Two different partitions: ((5,1), (8,3,4), (0,6,7,2,9)) and ((5,8), (1,3,4,0), (6,7,2,9)) +VI result: 1.1343 +Rand result: 0.711111 +Adjusted Rand result: 0.312573 +NMI result: 0.455859 +Split-join result: 6 diff --git a/tests/unit/igraph_complex.c b/tests/unit/igraph_complex.c new file mode 100644 index 0000000..5cec0cd --- /dev/null +++ b/tests/unit/igraph_complex.c @@ -0,0 +1,184 @@ +/* -*- 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 + +/* Ensure that math constants are available. */ +#include "core/math.h" + +#include "test_utilities.h" + +#define ARE 4 +#define AIM 5 +#define BRE 6 +#define BIM 2 + +int main(void) { + + igraph_complex_t a = igraph_complex(ARE, AIM); + igraph_complex_t b = igraph_complex(BRE, BIM); + igraph_complex_t c, d, e; + + /* polar, mod, arg */ + c = igraph_complex_polar(igraph_complex_abs(a), igraph_complex_arg(a)); + IGRAPH_ASSERT(igraph_complex_almost_equals(a, c, 1e-14)); + + /* add */ + c = igraph_complex_add(a, b); + IGRAPH_ASSERT(IGRAPH_REAL(c) == ARE + BRE && IGRAPH_IMAG(c) == AIM + BIM); + + /* sub */ + c = igraph_complex_sub(a, b); + IGRAPH_ASSERT(IGRAPH_REAL(c) == ARE - BRE && IGRAPH_IMAG(c) == AIM - BIM); + + /* mul */ + c = igraph_complex_mul(a, b); + IGRAPH_ASSERT(IGRAPH_REAL(c) == ARE * BRE - AIM * BIM); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == ARE * BIM + AIM * BRE); + + /* div */ + c = igraph_complex_div(a, b); + c = igraph_complex_mul(c, b); + IGRAPH_ASSERT(igraph_complex_almost_equals(a, c, 1e-14)); + + /* add_real */ + c = igraph_complex_add_real(a, IGRAPH_REAL(b)); + IGRAPH_ASSERT(IGRAPH_REAL(c) == IGRAPH_REAL(a) + IGRAPH_REAL(b)); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == IGRAPH_IMAG(a)); + + /* add_imag */ + c = igraph_complex_add_imag(a, IGRAPH_IMAG(b)); + IGRAPH_ASSERT(IGRAPH_REAL(c) == IGRAPH_REAL(a)); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == IGRAPH_IMAG(a) + IGRAPH_IMAG(b)); + + /* sub_real */ + c = igraph_complex_sub_real(a, IGRAPH_REAL(b)); + IGRAPH_ASSERT(IGRAPH_REAL(c) == IGRAPH_REAL(a) - IGRAPH_REAL(b)); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == IGRAPH_IMAG(a)); + + /* sub_imag */ + c = igraph_complex_sub_imag(a, IGRAPH_IMAG(b)); + IGRAPH_ASSERT(IGRAPH_REAL(c) == IGRAPH_REAL(a)); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == IGRAPH_IMAG(a) - IGRAPH_IMAG(b)); + + /* mul_real */ + c = igraph_complex_mul_real(a, IGRAPH_REAL(b)); + IGRAPH_ASSERT(IGRAPH_REAL(c) == IGRAPH_REAL(a) * IGRAPH_REAL(b)); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == IGRAPH_IMAG(a) * IGRAPH_REAL(b)); + + /* mul_imag */ + c = igraph_complex_mul_imag(a, IGRAPH_REAL(b)); + IGRAPH_ASSERT(IGRAPH_REAL(c) == - IGRAPH_IMAG(a) * IGRAPH_REAL(b)); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == IGRAPH_REAL(a) * IGRAPH_REAL(b)); + + /* div_real */ + c = igraph_complex_div_real(a, IGRAPH_REAL(b)); + IGRAPH_ASSERT(fabs(IGRAPH_REAL(c) - IGRAPH_REAL(a) / IGRAPH_REAL(b)) < 1e-15); + IGRAPH_ASSERT(fabs(IGRAPH_IMAG(c) - IGRAPH_IMAG(a) / IGRAPH_REAL(b)) < 1e-15); + + /* div_imag */ + c = igraph_complex_div_imag(a, IGRAPH_IMAG(b)); + IGRAPH_ASSERT(IGRAPH_REAL(c) == IGRAPH_IMAG(a) / IGRAPH_IMAG(b)); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == - IGRAPH_REAL(a) / IGRAPH_IMAG(b)); + + /* conj */ + c = igraph_complex_conj(a); + IGRAPH_ASSERT(IGRAPH_REAL(c) == ARE && IGRAPH_IMAG(c) == -AIM); + + /* neg */ + c = igraph_complex_neg(a); + IGRAPH_ASSERT(IGRAPH_REAL(c) == - IGRAPH_REAL(a)); + IGRAPH_ASSERT(IGRAPH_IMAG(c) == - IGRAPH_IMAG(a)); + + /* inv */ + c = igraph_complex_inv(a); + d = igraph_complex(1.0, 0.0); + e = igraph_complex_div(d, a); + IGRAPH_ASSERT(igraph_complex_almost_equals(c, e, 1e-14)); + + /* logabs */ + + /* sqrt */ + c = igraph_complex_sqrt(a); + d = igraph_complex_mul(c, c); + IGRAPH_ASSERT(igraph_complex_almost_equals(a, d, 1e-14)); + + /* sqrt_real */ + c = igraph_complex_sqrt(igraph_complex(-1.0, 0.0)); + d = igraph_complex_sqrt_real(-1.0); + IGRAPH_ASSERT(igraph_complex_almost_equals(c, d, 1e-14)); + + /* exp */ + c = igraph_complex_exp(igraph_complex(0.0, M_PI)); + IGRAPH_ASSERT(igraph_complex_almost_equals(c, igraph_complex(-1.0, 0.0), 1e-14)); + + /* pow */ + c = igraph_complex_pow(igraph_complex(M_E, 0.0), igraph_complex(0.0, M_PI)); + IGRAPH_ASSERT(igraph_complex_almost_equals(c, igraph_complex(-1.0, 0.0), 1e-14)); + + /* pow_real */ + c = igraph_complex_pow_real(a, 2.0); + d = igraph_complex_mul(a, a); + IGRAPH_ASSERT(igraph_complex_almost_equals(c, d, 1e-14)); + + /* log */ + c = igraph_complex_exp(igraph_complex_log(a)); + IGRAPH_ASSERT(igraph_complex_almost_equals(a, c, 1e-14)); + + /* log10 */ + c = igraph_complex_pow(igraph_complex(10.0, 0), igraph_complex_log10(a)); + IGRAPH_ASSERT(igraph_complex_almost_equals(a, c, 1e-14)); + + /* log_b */ + c = igraph_complex_pow(b, igraph_complex_log_b(a, b)); + IGRAPH_ASSERT(igraph_complex_almost_equals(a, c, 1e-14)); + + /* sin, cos */ + c = igraph_complex_sin(a); + d = igraph_complex_cos(a); + e = igraph_complex_add(igraph_complex_mul(c, c), igraph_complex_mul(d, d)); + IGRAPH_ASSERT(igraph_complex_almost_equals(e, igraph_complex(1.0, 0.0), 1e-12)); + + /* tan */ + c = igraph_complex_tan(a); + d = igraph_complex_div(igraph_complex_sin(a), igraph_complex_cos(a)); + IGRAPH_ASSERT(igraph_complex_almost_equals(c, d, 1e-14)); + + /* sec */ + c = igraph_complex_sec(a); + d = igraph_complex_inv(igraph_complex_cos(a)); + IGRAPH_ASSERT(igraph_complex_almost_equals(c, d, 1e-14)); + + /* csc */ + c = igraph_complex_csc(a); + d = igraph_complex_inv(igraph_complex_sin(a)); + IGRAPH_ASSERT(igraph_complex_almost_equals(c, d, 1e-14)); + + /* cot */ + c = igraph_complex_tan(a); + d = igraph_complex_div(igraph_complex_sin(a), igraph_complex_cos(a)); + IGRAPH_ASSERT(igraph_complex_almost_equals(d, c, 1e-14)); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_connect_neighborhood.c b/tests/unit/igraph_connect_neighborhood.c new file mode 100644 index 0000000..56673a3 --- /dev/null +++ b/tests/unit/igraph_connect_neighborhood.c @@ -0,0 +1,74 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g, igraph_integer_t order, igraph_neimode_t mode) { + igraph_connect_neighborhood(g, order, mode); + print_graph_canon(g); + igraph_destroy(g); +} + +int main(void) { + igraph_t g; + + printf("Graph with no vertices:\n"); + igraph_small(&g, IGRAPH_UNDIRECTED, 0, -1); + print_and_destroy(&g, 10, IGRAPH_ALL); + + printf("Directed graph with loops and multiple edges, order 0, IGRAPH_OUT:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, 0, IGRAPH_OUT); + + printf("Same graph, order 1:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, 1, IGRAPH_OUT); + + printf("Same graph, order 2:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, 2, IGRAPH_OUT); + + printf("Same starting graph, order 2, IGRAPH_IN:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, 2, IGRAPH_IN); + + printf("Same starting graph, order 2, IGRAPH_ALL:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, 2, IGRAPH_ALL); + + printf("Same starting graph, order 3, IGRAPH_OUT:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, 3, IGRAPH_OUT); + + printf("Same starting graph, order 12, IGRAPH_IN:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, 12, IGRAPH_IN); + + printf("Same starting graph, but undirected, order 2, IGRAPH_OUT:\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, 2, IGRAPH_OUT); + + VERIFY_FINALLY_STACK(); + + printf("Check negative order error.\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + CHECK_ERROR(igraph_connect_neighborhood(&g, -1, IGRAPH_OUT), IGRAPH_EINVAL); + igraph_destroy(&g); + + return 0; +} diff --git a/tests/unit/igraph_connect_neighborhood.out b/tests/unit/igraph_connect_neighborhood.out new file mode 100644 index 0000000..9e62ba7 --- /dev/null +++ b/tests/unit/igraph_connect_neighborhood.out @@ -0,0 +1,124 @@ +Graph with no vertices: +directed: false +vcount: 0 +edges: { +} +Directed graph with loops and multiple edges, order 0, IGRAPH_OUT: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +1 1 +1 3 +2 3 +3 4 +3 4 +} +Same graph, order 1: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +1 1 +1 3 +2 3 +3 4 +3 4 +} +Same graph, order 2: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +0 3 +1 1 +1 3 +1 4 +2 3 +2 4 +3 4 +3 4 +} +Same starting graph, order 2, IGRAPH_IN: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +0 3 +1 1 +1 3 +1 4 +2 3 +2 4 +3 4 +3 4 +} +Same starting graph, order 2, IGRAPH_ALL: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +0 3 +1 1 +1 2 +1 3 +1 4 +2 3 +2 4 +3 4 +3 4 +} +Same starting graph, order 3, IGRAPH_OUT: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +0 3 +0 4 +1 1 +1 3 +1 4 +2 3 +2 4 +3 4 +3 4 +} +Same starting graph, order 12, IGRAPH_IN: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +0 3 +0 4 +1 1 +1 3 +1 4 +2 3 +2 4 +3 4 +3 4 +} +Same starting graph, but undirected, order 2, IGRAPH_OUT: +directed: false +vcount: 6 +edges: { +0 1 +0 2 +0 3 +1 1 +1 2 +1 3 +1 4 +2 3 +2 4 +3 4 +3 4 +} +Check negative order error. diff --git a/tests/unit/igraph_constraint.c b/tests/unit/igraph_constraint.c new file mode 100644 index 0000000..822229e --- /dev/null +++ b/tests/unit/igraph_constraint.c @@ -0,0 +1,104 @@ +/* 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, igraph_vs_t vids, igraph_vector_t *weights) { + igraph_vector_t result; + igraph_vector_init(&result, 0); + IGRAPH_ASSERT(igraph_constraint(graph, &result, vids, weights) == IGRAPH_SUCCESS); + print_vector(&result); + igraph_vector_destroy(&result); + printf("\n"); +} + + +int main(void) { + igraph_t g_0, g_1, g_4_full, g_4_full_split, g_4_full_loop, g_4_line, g_hole; + igraph_vector_t weights_full, weights_full_split, weights_line, result; + + igraph_vector_init_real(&weights_line, 6, 1., 0., 0., 1., 0., 1.); + igraph_vector_init_real(&weights_full, 6, 1., 1., 1., 1., 1., 1.); + igraph_vector_init_real(&weights_full_split, 9, .5, .5, 1., 1., 1., 1./3., 1./3., 1./3., 1.); + igraph_vector_init(&result, 0); + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_4_full, 4, IGRAPH_UNDIRECTED, 0,1, 0,2, 0,3, 1,2, 1,3, 2,3, -1); + igraph_small(&g_4_full_loop, 4, IGRAPH_UNDIRECTED, 0,0, 0,1, 0,2, 0,3, 1,2, 1,3, 2,3, -1); + igraph_small(&g_4_full_split, 4, IGRAPH_UNDIRECTED, 0,1, 0,1, 0,2, 0,3, 1,2, + 1,3, 1,3, 1,3, 2,3, -1); + igraph_small(&g_4_line, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, -1); + igraph_small(&g_hole, 9, IGRAPH_UNDIRECTED, 0,1, 0,2, 0,3, 1,2, 1,3, 2,3, 3,4, 4,5, + 5,6, 5,7, 5,8, 6,7, 6,8, 7,8, -1); + + printf("No vertices:\n"); + call_and_print(&g_0, igraph_vss_none(), NULL); + + printf("One vertex:\n"); + call_and_print(&g_1, igraph_vss_1(0), NULL); + + printf("Line graph, 4 vertices:\n"); + call_and_print(&g_4_line, igraph_vss_all(), NULL); + + printf("Full graph, 4 vertices, weights make it a line:\n"); + call_and_print(&g_4_full, igraph_vss_all(), &weights_line); + + /* + each p_ij and p_ji is equal to 1/3, because each node has 3 connections with + equal weight and p_ij is z_ij/sum_j(z_ij) where z are the edge weights. + so C_ij = (1/3 + (1/3)^2) for each j, which means C_i = (1/3 + 2/9)^2 *3 + = 0.925925925925926 + */ + printf("Full graph, 4 vertices, all same weights:\n"); + call_and_print(&g_4_full, igraph_vss_all(), &weights_full); + + printf("Full graph, 4 vertices, all same weights, but split over multiple edges:\n"); + call_and_print(&g_4_full_split, igraph_vss_all(), &weights_full_split); + + printf("Full graph, 4 vertices, no weights:\n"); + call_and_print(&g_4_full, igraph_vss_all(), NULL); + + printf("Full graph, 4 vertices, no weights, with loop:\n"); + call_and_print(&g_4_full_loop, igraph_vss_all(), NULL); + + /* + for node 0, each p_ij is again equal to 1/3, but p_ji equals 1/4 for j = 3, + because that's connected to 4 other nodes, so for example for C_01 the contribution + is + C_01 = (p_01 + p_02p_21 + p_03p_31)^2 = (1/3 + 1/9 + 1/12)^2 + and you end up with: + ((1/3 + 2/9)^2 ) + ((1/3 + 1/9 + 1/12)^2 * 2) = 0.8657407407407408 + */ + printf("Hole in middle of two clusters:\n"); + call_and_print(&g_hole, igraph_vss_all(), NULL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_4_full); + igraph_destroy(&g_4_full_loop); + igraph_destroy(&g_4_full_split); + igraph_destroy(&g_4_line); + igraph_destroy(&g_hole); + igraph_vector_destroy(&weights_line); + igraph_vector_destroy(&weights_full); + igraph_vector_destroy(&weights_full_split); + igraph_vector_destroy(&result); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_constraint.out b/tests/unit/igraph_constraint.out new file mode 100644 index 0000000..881eb51 --- /dev/null +++ b/tests/unit/igraph_constraint.out @@ -0,0 +1,27 @@ +No vertices: +( ) + +One vertex: +( NaN ) + +Line graph, 4 vertices: +( 1 0.5 0.5 1 ) + +Full graph, 4 vertices, weights make it a line: +( 1.25 0.5625 0.5625 1.25 ) + +Full graph, 4 vertices, all same weights: +( 0.925926 0.925926 0.925926 0.925926 ) + +Full graph, 4 vertices, all same weights, but split over multiple edges: +( 0.925926 0.925926 0.925926 0.925926 ) + +Full graph, 4 vertices, no weights: +( 0.925926 0.925926 0.925926 0.925926 ) + +Full graph, 4 vertices, no weights, with loop: +( 0.925926 0.925926 0.925926 0.925926 ) + +Hole in middle of two clusters: +( 0.865741 0.865741 0.865741 0.583333 0.5 0.583333 0.865741 0.865741 0.865741 ) + diff --git a/tests/unit/igraph_contract_vertices.c b/tests/unit/igraph_contract_vertices.c new file mode 100644 index 0000000..c1fe983 --- /dev/null +++ b/tests/unit/igraph_contract_vertices.c @@ -0,0 +1,117 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t mapping; + igraph_attribute_combination_t comb; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_attribute_combination(&comb, + "an", IGRAPH_ATTRIBUTE_COMBINE_SUM, + "ab", IGRAPH_ATTRIBUTE_COMBINE_FIRST, + "as", IGRAPH_ATTRIBUTE_COMBINE_LAST, + "ean", IGRAPH_ATTRIBUTE_COMBINE_PROD, + IGRAPH_NO_MORE_ATTRIBUTES); + + printf("Graph with no vertices:\n"); + { + igraph_small(&g, 0, IGRAPH_DIRECTED, -1); + igraph_vector_int_view(&mapping, NULL, 0); + igraph_contract_vertices(&g, &mapping, &comb); + igraph_destroy(&g); + } + + printf("Graph with loops and multiple edges:\n"); + { + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + igraph_bool_t booleans[6] = {0, 1, 0, 1, 0, 1}; + igraph_real_t real_numbers[6] = {0, 1, 2, 3, 4, 5}; + igraph_real_t real_numbers_edges[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + igraph_vector_t reals; + igraph_vector_t reals_edges; + igraph_vector_bool_t bools; + + igraph_vector_view(&reals, real_numbers, 6); + igraph_vector_view(&reals_edges, real_numbers_edges, 8); + igraph_vector_bool_view(&bools, booleans, 6); + + SETVABV(&g, "ab", &bools); + SETVANV(&g, "an", &reals); + SETVAS(&g, "as", 0, "zero"); + SETVAS(&g, "as", 1, "one"); + SETVAS(&g, "as", 2, "two"); + SETVAS(&g, "as", 3, "three"); + SETVAS(&g, "as", 4, "four"); + SETVAS(&g, "as", 5, "five"); + SETEANV(&g, "ean", &reals_edges); + + { + printf("No contraction:\n"); + igraph_integer_t mappings[6] = {0, 1, 2, 3, 4, 5}; + igraph_vector_int_view(&mapping, mappings, 6); + igraph_contract_vertices(&g, &mapping, &comb); + print_attributes(&g); + } + { + printf("Contract 5 into 4:\n"); + igraph_integer_t mappings[6] = {0, 1, 2, 3, 4, 4}; + igraph_vector_int_view(&mapping, mappings, 6); + igraph_contract_vertices(&g, &mapping, &comb); + print_attributes(&g); + } + { + printf("Contract 4 into 3:\n"); + igraph_integer_t mappings[5] = {0, 1, 2, 3, 3}; + igraph_vector_int_view(&mapping, mappings, 5); + igraph_contract_vertices(&g, &mapping, &comb); + print_attributes(&g); + } + { + printf("Contract 3 into 2:\n"); + igraph_integer_t mappings[4] = {0, 1, 2, 2}; + igraph_vector_int_view(&mapping, mappings, 4); + igraph_contract_vertices(&g, &mapping, &comb); + print_attributes(&g); + } + { + printf("Contract 2 into 0:\n"); + igraph_integer_t mappings[3] = {0, 1, 0}; + igraph_vector_int_view(&mapping, mappings, 3); + igraph_contract_vertices(&g, &mapping, &comb); + print_attributes(&g); + } + } + + VERIFY_FINALLY_STACK(); + + { + printf("Check incorrect mapping size error.\n"); + igraph_integer_t mappings[3] = {0, 1, 0}; + igraph_vector_int_view(&mapping, mappings, 3); + CHECK_ERROR(igraph_contract_vertices(&g, &mapping, &comb), IGRAPH_EINVAL); + } + + igraph_attribute_combination_destroy(&comb); + igraph_destroy(&g); + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_contract_vertices.out b/tests/unit/igraph_contract_vertices.out new file mode 100644 index 0000000..f2377a2 --- /dev/null +++ b/tests/unit/igraph_contract_vertices.out @@ -0,0 +1,73 @@ +Graph with no vertices: +Graph with loops and multiple edges: +No contraction: +Vertex 0: ab=0 an=0 as="zero" +Vertex 1: ab=1 an=1 as="one" +Vertex 2: ab=0 an=2 as="two" +Vertex 3: ab=1 an=3 as="three" +Vertex 4: ab=0 an=4 as="four" +Vertex 5: ab=1 an=5 as="five" +Edge 0 (0-1): ean=0 +Edge 1 (0-2): ean=1 +Edge 2 (1-1): ean=2 +Edge 3 (1-3): ean=3 +Edge 4 (2-0): ean=4 +Edge 5 (2-3): ean=5 +Edge 6 (3-4): ean=6 +Edge 7 (3-4): ean=7 + +Contract 5 into 4: +Vertex 0: ab=0 an=0 as="zero" +Vertex 1: ab=1 an=1 as="one" +Vertex 2: ab=0 an=2 as="two" +Vertex 3: ab=1 an=3 as="three" +Vertex 4: ab=0 an=9 as="five" +Edge 0 (0-1): ean=0 +Edge 1 (0-2): ean=1 +Edge 2 (1-1): ean=2 +Edge 3 (1-3): ean=3 +Edge 4 (2-0): ean=4 +Edge 5 (2-3): ean=5 +Edge 6 (3-4): ean=6 +Edge 7 (3-4): ean=7 + +Contract 4 into 3: +Vertex 0: ab=0 an=0 as="zero" +Vertex 1: ab=1 an=1 as="one" +Vertex 2: ab=0 an=2 as="two" +Vertex 3: ab=1 an=12 as="five" +Edge 0 (0-1): ean=0 +Edge 1 (0-2): ean=1 +Edge 2 (1-1): ean=2 +Edge 3 (1-3): ean=3 +Edge 4 (2-0): ean=4 +Edge 5 (2-3): ean=5 +Edge 6 (3-3): ean=6 +Edge 7 (3-3): ean=7 + +Contract 3 into 2: +Vertex 0: ab=0 an=0 as="zero" +Vertex 1: ab=1 an=1 as="one" +Vertex 2: ab=0 an=14 as="five" +Edge 0 (0-1): ean=0 +Edge 1 (0-2): ean=1 +Edge 2 (1-1): ean=2 +Edge 3 (1-2): ean=3 +Edge 4 (2-0): ean=4 +Edge 5 (2-2): ean=5 +Edge 6 (2-2): ean=6 +Edge 7 (2-2): ean=7 + +Contract 2 into 0: +Vertex 0: ab=0 an=14 as="five" +Vertex 1: ab=1 an=1 as="one" +Edge 0 (0-1): ean=0 +Edge 1 (0-0): ean=1 +Edge 2 (1-1): ean=2 +Edge 3 (1-0): ean=3 +Edge 4 (0-0): ean=4 +Edge 5 (0-0): ean=5 +Edge 6 (0-0): ean=6 +Edge 7 (0-0): ean=7 + +Check incorrect mapping size error. diff --git a/tests/unit/igraph_convergence_degree.c b/tests/unit/igraph_convergence_degree.c new file mode 100644 index 0000000..0f03e35 --- /dev/null +++ b/tests/unit/igraph_convergence_degree.c @@ -0,0 +1,57 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_vector_t result; + igraph_integer_t i; + + igraph_vector_init(&result, 0); + + igraph_small(&g, 7, 0, 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, 3, 4, 4, 5, 4, 6, 5, 6, -1); + igraph_convergence_degree(&g, &result, 0, 0); + for (i = 0; i < igraph_ecount(&g); i++) { + printf("%.4f ", igraph_vector_get(&result, i)); + } + printf("\n"); + igraph_destroy(&g); + + igraph_small(&g, 6, 1, 1, 0, 2, 0, 3, 0, 4, 0, 0, 5, -1); + igraph_convergence_degree(&g, &result, 0, 0); + for (i = 0; i < igraph_ecount(&g); i++) { + printf("%.4f ", igraph_vector_get(&result, i)); + } + printf("\n"); + igraph_destroy(&g); + + igraph_vector_destroy(&result); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_convergence_degree.out b/tests/unit/igraph_convergence_degree.out new file mode 100644 index 0000000..fa1eae6 --- /dev/null +++ b/tests/unit/igraph_convergence_degree.out @@ -0,0 +1,2 @@ +0.0000 0.0000 0.6000 0.0000 0.6000 0.6000 0.1429 0.6667 0.6667 0.0000 +-0.3333 -0.3333 -0.3333 -0.3333 0.6667 diff --git a/tests/unit/igraph_convex_hull.c b/tests/unit/igraph_convex_hull.c new file mode 100644 index 0000000..1c156ad --- /dev/null +++ b/tests/unit/igraph_convex_hull.c @@ -0,0 +1,159 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2022 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 "test_utilities.h" + +void check_convex_hull(igraph_matrix_t *coords) { + igraph_vector_int_t result; + igraph_matrix_t resmat; + + /* Testing with index output mode */ + igraph_vector_int_init(&result, 1); + igraph_convex_hull(coords, &result, 0); + + print_vector_int(&result); + igraph_vector_int_destroy(&result); + + /* Testing with coordinate output mode */ + igraph_matrix_init(&resmat, 0, 0); + igraph_convex_hull(coords, 0, &resmat); + + print_matrix(&resmat); + igraph_matrix_destroy(&resmat); +} + +void test_simple(void) { + igraph_real_t coords_array[][2] = { + {3, 2}, {5, 1}, {4, 4}, {6, 4}, {4, 3}, + {2, 5}, {1, 3}, {2, 4}, {6, 3}, {9, 2} + }; + igraph_matrix_t coords; + + printf("test_simple\n"); + + igraph_matrix_init(&coords, 10, 2); + for (igraph_integer_t i = 0; i < 20; i++) { + MATRIX(coords, i / 2, i % 2) = coords_array[i / 2][i % 2]; + } + check_convex_hull(&coords); + igraph_matrix_destroy(&coords); +} + +void test_collinear(void) { + igraph_real_t coords_array[][2] = + {{3, 2}, {5, 1}, {7, 0}, {9, -1}, {11, -2}}; + igraph_matrix_t coords; + + printf("test_collinear\n"); + + igraph_matrix_init(&coords, 5, 2); + for (igraph_integer_t i = 0; i < 10; i++) { + MATRIX(coords, i / 2, i % 2) = coords_array[i / 2][i % 2]; + } + check_convex_hull(&coords); + igraph_matrix_destroy(&coords); +} + +void test_degenerate(void) { + igraph_matrix_t coords; + + printf("test_degenerate\n"); + + igraph_matrix_init(&coords, 2, 2); + MATRIX(coords, 0, 0) = 3; + MATRIX(coords, 0, 1) = 2; + MATRIX(coords, 1, 0) = 5; + MATRIX(coords, 1, 1) = 1; + check_convex_hull(&coords); + + igraph_matrix_resize(&coords, 1, 2); + MATRIX(coords, 0, 0) = 3; + MATRIX(coords, 0, 1) = 2; + check_convex_hull(&coords); + + igraph_matrix_resize(&coords, 0, 2); + check_convex_hull(&coords); + + igraph_matrix_destroy(&coords); +} + +void test_bug_805(void) { + igraph_real_t coords_array[][2] = { + {0, 0}, {1, 0}, {0.707, 0.707}, {0, 1}, {-0.707, 0.707}, {-1, 0}, + {-0.707, -0.707}, {0, -1}, {0.707, -0.707}, {2, 0}, {1.414, 1.414}, {0, 2}, + {-1.414, 1.414}, {-2, 0}, {-1.414, -1.414}, {0, -2}, {1.414, -1.414}, {3, 0}, + {2.121, 2.121}, {0, 3}, {-2.121, 2.121}, {-3, 0}, {-2.121, -2.121}, {0, -3}, + {2.121, -2.121}, {4, 0}, {2.828, 2.828}, {0, 4}, {-2.828, 2.828}, {-4, 0}, + {-2.828, -2.828}, {0, -4}, {2.828, -2.828} + }; + igraph_matrix_t coords; + + printf("test_bug_805\n"); + + igraph_matrix_init(&coords, 33, 2); + for (igraph_integer_t i = 0; i < 66; i++) { + MATRIX(coords, i / 2, i % 2) = coords_array[i / 2][i % 2]; + } + check_convex_hull(&coords); + igraph_matrix_destroy(&coords); +} + +void test_bug_1115(void) { + igraph_real_t coords_array[][2] = { + {37, 52}, {49, 49}, {52, 64}, {20, 26}, {40, 30}, {21, 47}, {17, 63}, {31, 62}, + {52, 33}, {51, 21}, {42, 41}, {31, 32}, {5, 25}, {12, 42}, {36, 16}, {52, 41}, + {27, 23}, {17, 33}, {13, 13}, {57, 58}, {62, 42}, {42, 57}, {16, 57}, {8, 52}, + {7, 38}, {27, 68}, {30, 48}, {43, 67}, {58, 48}, {58, 27}, {37, 69}, {38, 46}, + {46, 10}, {61, 33}, {62, 63}, {63, 69}, {32, 22}, {45, 35}, {59, 15}, {5, 6}, + {10, 17}, {21, 10}, {5, 64}, {30, 15}, {39, 10}, {32, 39}, {25, 32}, {25, 55}, + {48, 28}, {56, 37}, {30, 40} + }; + igraph_matrix_t coords; + + printf("test_bug_1115\n"); + + igraph_matrix_init(&coords, 51, 2); + for (igraph_integer_t i = 0; i < 102; i++) { + MATRIX(coords, i / 2, i % 2) = coords_array[i / 2][i % 2]; + } + check_convex_hull(&coords); + igraph_matrix_destroy(&coords); +} + +int main(void) { + + test_simple(); + + test_collinear(); + + test_degenerate(); + + test_bug_805(); + + test_bug_1115(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_convex_hull.out b/tests/unit/igraph_convex_hull.out new file mode 100644 index 0000000..ae96c1e --- /dev/null +++ b/tests/unit/igraph_convex_hull.out @@ -0,0 +1,39 @@ +test_simple +( 1 6 5 3 9 ) +[ 5 1 + 1 3 + 2 5 + 6 4 + 9 2 ] +test_collinear +( 4 0 ) +[ 11 -2 + 3 2 ] +test_degenerate +( 1 0 ) +[ 5 1 + 3 2 ] +( 0 ) +[ 3 2 ] +( ) +[ 0-by-2 ] +test_bug_805 +( 31 30 29 28 27 26 25 32 ) +[ 0 -4 + -2.828 -2.828 + -4 0 + -2.828 2.828 + 0 4 + 2.828 2.828 + 4 0 + 2.828 -2.828 ] +test_bug_1115 +( 39 42 25 30 35 20 38 32 ) +[ 5 6 + 5 64 + 27 68 + 37 69 + 63 69 + 62 42 + 59 15 + 46 10 ] diff --git a/tests/unit/igraph_correlated_game.c b/tests/unit/igraph_correlated_game.c new file mode 100644 index 0000000..a136497 --- /dev/null +++ b/tests/unit/igraph_correlated_game.c @@ -0,0 +1,45 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g1, g2; + + igraph_rng_seed(igraph_rng_default(), 9275); + + igraph_erdos_renyi_game_gnp(&g1, 10, .3, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_correlated_game(&g1, &g2, .9, .3, /* permutation=*/ 0); + + IGRAPH_ASSERT(igraph_vcount(&g1) == igraph_vcount(&g2)); + + igraph_destroy(&g2); + igraph_destroy(&g1); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_correlated_pair_game.c b/tests/unit/igraph_correlated_pair_game.c new file mode 100644 index 0000000..5bcfa85 --- /dev/null +++ b/tests/unit/igraph_correlated_pair_game.c @@ -0,0 +1,46 @@ +/* + 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 . + */ + +#include +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g1, g2; + igraph_bool_t same; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_correlated_pair_game(&g1, &g2, 0, /*corr*/ 1, /*p*/ 0.99, + /*directed*/ 1, /*permutation*/ NULL); + print_graph_canon(&g1); + print_graph_canon(&g2); + igraph_destroy(&g1); + igraph_destroy(&g2); + + igraph_correlated_pair_game(&g1, &g2, 10, /*corr*/ 1, /*p*/ 0.5, + /*directed*/ 1, /*permutation*/ NULL); + igraph_is_same_graph(&g1, &g2, &same); + IGRAPH_ASSERT(same); + + igraph_destroy(&g1); + igraph_destroy(&g2); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_correlated_pair_game.out b/tests/unit/igraph_correlated_pair_game.out new file mode 100644 index 0000000..2f08890 --- /dev/null +++ b/tests/unit/igraph_correlated_pair_game.out @@ -0,0 +1,8 @@ +directed: true +vcount: 0 +edges: { +} +directed: true +vcount: 0 +edges: { +} diff --git a/tests/unit/igraph_count_multiple.c b/tests/unit/igraph_count_multiple.c new file mode 100644 index 0000000..ea769e9 --- /dev/null +++ b/tests/unit/igraph_count_multiple.c @@ -0,0 +1,37 @@ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_int_t counts; + + igraph_vector_int_init(&counts, 0); + + /* undirected case */ + igraph_small(&graph, 2, IGRAPH_UNDIRECTED, + 0,1, 0,1, 0,1, 0,0, 1,1, 1,1, + -1); + + igraph_count_multiple(&graph, &counts, igraph_ess_all(IGRAPH_EDGEORDER_ID)); + print_vector_int(&counts); + + igraph_destroy(&graph); + + /* directed case */ + igraph_small(&graph, 2, IGRAPH_DIRECTED, + 0,1, 0,1, 0,1, 0,0, 1,1, 1,1, + -1); + + igraph_count_multiple(&graph, &counts, igraph_ess_all(IGRAPH_EDGEORDER_ID)); + print_vector_int(&counts); + + igraph_destroy(&graph); + + igraph_vector_int_destroy(&counts); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_count_multiple.out b/tests/unit/igraph_count_multiple.out new file mode 100644 index 0000000..e5e3345 --- /dev/null +++ b/tests/unit/igraph_count_multiple.out @@ -0,0 +1,2 @@ +( 3 3 3 1 2 2 ) +( 3 3 3 1 2 2 ) diff --git a/tests/unit/igraph_create.c b/tests/unit/igraph_create.c new file mode 100644 index 0000000..98391cd --- /dev/null +++ b/tests/unit/igraph_create.c @@ -0,0 +1,40 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_vector_int_t v; + igraph_t g; + + igraph_vector_int_init(&v, 9); + /* error: IGRAPH_EINVEVECTOR */ + VECTOR(v)[8] = 0; + CHECK_ERROR(igraph_create(&g, &v, 0, 0), IGRAPH_EINVEVECTOR); + + /* error: IGRAPH_EINVVID */ + igraph_vector_int_resize(&v, 8); + VECTOR(v)[7] = -1; + CHECK_ERROR(igraph_create(&g, &v, 10, 1), IGRAPH_EINVVID); + + igraph_vector_int_destroy(&v); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_decompose_strong.c b/tests/unit/igraph_decompose_strong.c new file mode 100644 index 0000000..5931b70 --- /dev/null +++ b/tests/unit/igraph_decompose_strong.c @@ -0,0 +1,63 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t ring, g; + igraph_graph_list_t complist; + igraph_integer_t i; + + igraph_graph_list_init(&complist, 0); + + /* A directed ring, a single strongly connected component */ + igraph_ring(&ring, 10, IGRAPH_DIRECTED, 0, 1); + igraph_decompose(&ring, &complist, IGRAPH_STRONG, -1, 0); + print_graph(igraph_graph_list_get_ptr(&complist, 0)); + igraph_destroy(&ring); + + /* a toy graph, three components maximum, with at least 2 vertices each */ + /* 0 >-> 1 >-> 3 >-> 4 + ^ v + \< 2 < / */ + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 0, + 1, 3, + 3, 4, + -1); + igraph_decompose(&g, &complist, IGRAPH_STRONG, 3, 2); + for (i = 0; i < igraph_graph_list_size(&complist); i++) { + print_graph(igraph_graph_list_get_ptr(&complist, i)); + } + igraph_destroy(&g); + + igraph_graph_list_destroy(&complist); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_decompose_strong.out b/tests/unit/igraph_decompose_strong.out new file mode 100644 index 0000000..3194273 --- /dev/null +++ b/tests/unit/igraph_decompose_strong.out @@ -0,0 +1,21 @@ +directed: true +vcount: 10 +edges: { +0 1 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 0 +} +directed: true +vcount: 3 +edges: { +0 1 +1 2 +2 0 +} diff --git a/tests/unit/igraph_degree.c b/tests/unit/igraph_degree.c new file mode 100644 index 0000000..f5b6d36 --- /dev/null +++ b/tests/unit/igraph_degree.c @@ -0,0 +1,106 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void all_degs(const igraph_t *g, igraph_vector_int_t *res, igraph_neimode_t mode, igraph_bool_t loops) { + igraph_integer_t n = igraph_vcount(g); + igraph_vector_int_resize(res, n); + for (igraph_integer_t i=0; i < n; i++) { + igraph_degree_1(g, &VECTOR(*res)[i], i, mode, loops); + } +} + +int main(void) { + igraph_vector_int_t v, v2, seq; + igraph_t g; + + igraph_vector_int_init(&seq, 3); + igraph_vector_int_init(&v, 0); + igraph_vector_int_init(&v2, 0); + VECTOR(seq)[0] = 2; + VECTOR(seq)[1] = 0; + VECTOR(seq)[2] = 2; + + igraph_small(&g, 4, IGRAPH_DIRECTED, 0,1, 1,2, 2,3, 2,2, 3,2, 2,3, -1); + + igraph_vector_int_clear(&v); + igraph_vector_int_clear(&v2); + printf("out, loops: "); + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + print_vector_int(&v); + all_degs(&g, &v2, IGRAPH_OUT, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vector_int_all_e(&v, &v2)); + + igraph_vector_int_clear(&v); + igraph_vector_int_clear(&v2); + printf(" in, loops: "); + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); + print_vector_int(&v); + all_degs(&g, &v2, IGRAPH_IN, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vector_int_all_e(&v, &v2)); + + igraph_vector_int_clear(&v); + igraph_vector_int_clear(&v2); + printf("all, loops: "); + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + print_vector_int(&v); + all_degs(&g, &v2, IGRAPH_ALL, IGRAPH_LOOPS); + IGRAPH_ASSERT(igraph_vector_int_all_e(&v, &v2)); + + igraph_vector_int_clear(&v); + igraph_vector_int_clear(&v2); + printf("out, no loops: "); + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_OUT, IGRAPH_NO_LOOPS); + print_vector_int(&v); + all_degs(&g, &v2, IGRAPH_OUT, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_vector_int_all_e(&v, &v2)); + + igraph_vector_int_clear(&v); + igraph_vector_int_clear(&v2); + printf(" in, no loops: "); + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_IN, IGRAPH_NO_LOOPS); + print_vector_int(&v); + all_degs(&g, &v2, IGRAPH_IN, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_vector_int_all_e(&v, &v2)); + + igraph_vector_int_clear(&v); + igraph_vector_int_clear(&v2); + printf("all, no loops: "); + igraph_degree(&g, &v, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS); + print_vector_int(&v); + all_degs(&g, &v2, IGRAPH_ALL, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(igraph_vector_int_all_e(&v, &v2)); + + /* Invalid mode */ + CHECK_ERROR(igraph_degree(&g, &v, igraph_vss_vector(&seq), (igraph_neimode_t)0, + IGRAPH_LOOPS), IGRAPH_EINVMODE); + + /* Vertex does not exist */ + VECTOR(seq)[0] = 4; + CHECK_ERROR(igraph_degree(&g, &v, igraph_vss_vector(&seq), IGRAPH_ALL, IGRAPH_LOOPS), IGRAPH_EINVVID); + + igraph_destroy(&g); + igraph_vector_int_destroy(&v2); + igraph_vector_int_destroy(&v); + igraph_vector_int_destroy(&seq); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_degree.out b/tests/unit/igraph_degree.out new file mode 100644 index 0000000..cc35509 --- /dev/null +++ b/tests/unit/igraph_degree.out @@ -0,0 +1,6 @@ +out, loops: ( 1 1 3 1 ) + in, loops: ( 0 1 3 2 ) +all, loops: ( 1 2 6 3 ) +out, no loops: ( 1 1 2 1 ) + in, no loops: ( 0 1 2 2 ) +all, no loops: ( 1 2 4 3 ) diff --git a/tests/unit/igraph_degree_sequence_game.c b/tests/unit/igraph_degree_sequence_game.c new file mode 100644 index 0000000..e252ccf --- /dev/null +++ b/tests/unit/igraph_degree_sequence_game.c @@ -0,0 +1,246 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +igraph_bool_t compare_degrees(const igraph_vector_int_t* expected, const igraph_vector_int_t *observed) { + igraph_integer_t i, n = igraph_vector_int_size(expected); + + if (igraph_vector_int_size(observed) != n) { + return 0; + } + + for (i = 0; i < n; i++) { + if (VECTOR(*expected)[i] != VECTOR(*observed)[i]) { + return 0; + } + } + + return 1; +} + +int main(void) { + igraph_t g; + igraph_vector_int_t outdeg, indeg, empty; + igraph_vector_int_t degrees; + igraph_bool_t is_simple, is_connected; + + igraph_integer_t outarr[] = {2, 3, 2, 3, 3, 3, 3, 1, 4, 4}; + igraph_integer_t inarr[] = {3, 6, 2, 0, 2, 2, 4, 3, 3, 3}; + + igraph_integer_t n = sizeof(outarr) / sizeof(outarr[0]); + + igraph_rng_seed(igraph_rng_default(), 333); + + igraph_vector_int_view(&outdeg, outarr, n); + igraph_vector_int_view(&indeg, inarr, n); + + igraph_vector_int_init(&empty, 0); + + /* This vector is used to check that the degrees of the result + * match the requested degrees. */ + igraph_vector_int_init(°rees, 0); + + /* Configuration model, undirected non-simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, NULL, IGRAPH_DEGSEQ_CONFIGURATION); + + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_destroy(&g); + + igraph_degree_sequence_game(&g, &empty, NULL, IGRAPH_DEGSEQ_CONFIGURATION); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + + /* Configuration model, directed non-simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_CONFIGURATION); + + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&indeg, °rees)); + + igraph_destroy(&g); + + igraph_degree_sequence_game(&g, &empty, &empty, IGRAPH_DEGSEQ_CONFIGURATION); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + + /* Configuration model, undirected simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE); + + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + + igraph_is_simple(&g, &is_simple); + IGRAPH_ASSERT(is_simple); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_destroy(&g); + + igraph_degree_sequence_game(&g, &empty, NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + + /* Configuration model, directed simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE); + + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + + igraph_is_simple(&g, &is_simple); + IGRAPH_ASSERT(is_simple); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&indeg, °rees)); + + igraph_destroy(&g); + + igraph_degree_sequence_game(&g, &empty, &empty, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + + /* Fast heuristic method, undirected simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, NULL, IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE); + + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + + igraph_is_simple(&g, &is_simple); + IGRAPH_ASSERT(is_simple); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_destroy(&g); + + igraph_degree_sequence_game(&g, &empty, NULL, IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + + /* Fast heuristic method, directed simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE); + + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + + igraph_is_simple(&g, &is_simple); + IGRAPH_ASSERT(is_simple); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&indeg, °rees)); + + igraph_destroy(&g); + + igraph_degree_sequence_game(&g, &empty, &empty, IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + + /* Edge-swithching MCMC, undirected, simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, NULL, IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE); + + igraph_is_simple(&g, &is_simple); + IGRAPH_ASSERT(is_simple); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_destroy(&g); + + /* Edge-swithching MCMC, directed, simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE); + + igraph_is_simple(&g, &is_simple); + IGRAPH_ASSERT(is_simple); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&indeg, °rees)); + + igraph_destroy(&g); + + + /* Viger-Latapy method, undirected connected simple graphs */ + + igraph_degree_sequence_game(&g, &outdeg, NULL, IGRAPH_DEGSEQ_VL); + + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + + igraph_is_simple(&g, &is_simple); + IGRAPH_ASSERT(is_simple); + + igraph_is_connected(&g, &is_connected, IGRAPH_WEAK); + IGRAPH_ASSERT(is_connected); + + igraph_degree(&g, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + IGRAPH_ASSERT(compare_degrees(&outdeg, °rees)); + + igraph_destroy(&g); + + igraph_degree_sequence_game(&g, &empty, NULL, IGRAPH_DEGSEQ_VL); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + /* This degree sequence contains a zero degree, so it cannot be realized by a connected graph. */ + CHECK_ERROR(igraph_degree_sequence_game(&g, &indeg, NULL, IGRAPH_DEGSEQ_VL), IGRAPH_EINVAL); + + igraph_vector_int_destroy(°rees); + igraph_vector_int_destroy(&empty); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_delete_edges.c b/tests/unit/igraph_delete_edges.c new file mode 100644 index 0000000..68b56a0 --- /dev/null +++ b/tests/unit/igraph_delete_edges.c @@ -0,0 +1,56 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_es_t es; + + igraph_small(&g, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, -1); + igraph_es_pairs_small(&es, IGRAPH_DIRECTED, 3, 2, -1); + + /* error test, no such edge to delete */ + CHECK_ERROR(igraph_delete_edges(&g, es), IGRAPH_EINVAL); + if (igraph_ecount(&g) != 3) { + return 3; + } + + /* error test, invalid vertex ID */ + igraph_es_destroy(&es); + igraph_es_pairs_small(&es, IGRAPH_DIRECTED, 10, 2, -1); + CHECK_ERROR(igraph_delete_edges(&g, es), IGRAPH_EINVVID); + if (igraph_ecount(&g) != 3) { + return 5; + } + + /* error test, invalid (odd) length */ + igraph_es_destroy(&es); + igraph_es_pairs_small(&es, IGRAPH_DIRECTED, 0, 1, 2, -1); + CHECK_ERROR(igraph_delete_edges(&g, es), IGRAPH_EINVAL); + if (igraph_ecount(&g) != 3) { + return 7; + } + + igraph_es_destroy(&es); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_delete_vertices.c b/tests/unit/igraph_delete_vertices.c new file mode 100644 index 0000000..9831539 --- /dev/null +++ b/tests/unit/igraph_delete_vertices.c @@ -0,0 +1,32 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,0, -1); + /* vertex does not exist */ + CHECK_ERROR(igraph_delete_vertices(&g, igraph_vss_1(3)), IGRAPH_EINVVID); + + igraph_destroy(&g); + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_density.c b/tests/unit/igraph_density.c new file mode 100644 index 0000000..4e62b91 --- /dev/null +++ b/tests/unit/igraph_density.c @@ -0,0 +1,155 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + +#include "test_utilities.h" + +void test_density(const igraph_t *graph, igraph_bool_t loops) { + igraph_real_t density; + + if (igraph_density(graph, &density, loops)) { + printf("FAILED!\n"); + return; + } + + if (isnan(density)) { + printf("nan\n"); + } else { + printf("%.4f\n", density); + } +} + +int main(void) { + + igraph_t g; + igraph_vector_int_t v; + + igraph_vector_int_init(&v, 0); + + /* Test graphs with no vertices and no edges */ + igraph_create(&g, &v, 0, IGRAPH_UNDIRECTED); + test_density(&g, 0); + test_density(&g, 1); + igraph_destroy(&g); + igraph_create(&g, &v, 0, IGRAPH_DIRECTED); + test_density(&g, 0); + test_density(&g, 1); + igraph_destroy(&g); + printf("======\n"); + + /* Test graphs with one vertex and no edges */ + igraph_create(&g, &v, 1, IGRAPH_UNDIRECTED); + test_density(&g, 0); + test_density(&g, 1); + igraph_destroy(&g); + igraph_create(&g, &v, 1, IGRAPH_DIRECTED); + test_density(&g, 0); + test_density(&g, 1); + igraph_destroy(&g); + printf("======\n"); + + /* Test graphs with one vertex and a loop edge */ + igraph_vector_int_resize(&v, 2); + VECTOR(v)[0] = 0; + VECTOR(v)[1] = 0; + igraph_create(&g, &v, 1, IGRAPH_UNDIRECTED); + test_density(&g, 1); + igraph_destroy(&g); + igraph_create(&g, &v, 1, IGRAPH_DIRECTED); + test_density(&g, 1); + igraph_destroy(&g); + printf("======\n"); + + /* Test graphs with one vertex and two loop edges */ + igraph_vector_int_resize(&v, 4); + VECTOR(v)[0] = 0; + VECTOR(v)[1] = 0; + VECTOR(v)[2] = 0; + VECTOR(v)[3] = 0; + igraph_create(&g, &v, 1, IGRAPH_UNDIRECTED); + test_density(&g, 1); + igraph_destroy(&g); + igraph_create(&g, &v, 1, IGRAPH_DIRECTED); + test_density(&g, 1); + igraph_destroy(&g); + printf("======\n"); + + /* Test graphs with two vertices and one edge between them */ + igraph_vector_int_resize(&v, 2); + VECTOR(v)[0] = 0; + VECTOR(v)[1] = 1; + igraph_create(&g, &v, 2, IGRAPH_UNDIRECTED); + test_density(&g, 0); + test_density(&g, 1); + igraph_destroy(&g); + igraph_create(&g, &v, 1, IGRAPH_DIRECTED); + test_density(&g, 0); + test_density(&g, 1); + igraph_destroy(&g); + printf("======\n"); + + /* Test graphs with two vertices, one edge between them and a loop on one + * of them */ + igraph_vector_int_resize(&v, 4); + VECTOR(v)[0] = 0; + VECTOR(v)[1] = 1; + VECTOR(v)[2] = 1; + VECTOR(v)[3] = 1; + igraph_create(&g, &v, 2, IGRAPH_UNDIRECTED); + test_density(&g, 1); + igraph_destroy(&g); + igraph_create(&g, &v, 1, IGRAPH_DIRECTED); + test_density(&g, 1); + igraph_destroy(&g); + printf("======\n"); + + /* Test graphs with two vertices, one edge between them and a loop on both + * of them */ + igraph_vector_int_resize(&v, 6); + VECTOR(v)[0] = 0; + VECTOR(v)[1] = 1; + VECTOR(v)[2] = 1; + VECTOR(v)[3] = 1; + VECTOR(v)[4] = 0; + VECTOR(v)[5] = 0; + igraph_create(&g, &v, 2, IGRAPH_UNDIRECTED); + test_density(&g, 1); + igraph_destroy(&g); + igraph_create(&g, &v, 1, IGRAPH_DIRECTED); + test_density(&g, 1); + igraph_destroy(&g); + printf("======\n"); + + /* Zachary karate club graph */ + igraph_famous(&g, "zachary"); + test_density(&g, 0); + test_density(&g, 1); + igraph_destroy(&g); + + igraph_vector_int_destroy(&v); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_density.out b/tests/unit/igraph_density.out new file mode 100644 index 0000000..cbd7583 --- /dev/null +++ b/tests/unit/igraph_density.out @@ -0,0 +1,29 @@ +nan +nan +nan +nan +====== +nan +0.0000 +nan +0.0000 +====== +1.0000 +1.0000 +====== +2.0000 +2.0000 +====== +1.0000 +0.3333 +0.5000 +0.2500 +====== +0.6667 +0.5000 +====== +1.0000 +0.7500 +====== +0.1390 +0.1311 diff --git a/tests/unit/igraph_deterministic_optimal_imitation.c b/tests/unit/igraph_deterministic_optimal_imitation.c new file mode 100644 index 0000000..da07b3e --- /dev/null +++ b/tests/unit/igraph_deterministic_optimal_imitation.c @@ -0,0 +1,57 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g, h; + igraph_vector_t quant; + igraph_vector_int_t strat; + + /* nonempty graph */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + igraph_empty(&h, 0, 0); /* empty graph */ + igraph_vector_init(&quant, 1); /* quantities vector */ + igraph_vector_int_init(&strat, 2); /* strategies vector */ + + /* test parameters */ + /*--graph--vertex--optimality--quantities--strategies--mode--retval--*/ + /* null pointer for graph */ + CHECK_ERROR(igraph_deterministic_optimal_imitation( NULL, 0, IGRAPH_MINIMUM, NULL, NULL, IGRAPH_ALL), IGRAPH_EINVAL ); + /* null pointer for quantities vector */ + CHECK_ERROR(igraph_deterministic_optimal_imitation( &g, 0, IGRAPH_MINIMUM, NULL, NULL, IGRAPH_ALL), IGRAPH_EINVAL ); + /* null pointer for strategies vector */ + CHECK_ERROR(igraph_deterministic_optimal_imitation( &g, 0, IGRAPH_MINIMUM, &quant, NULL, IGRAPH_ALL), IGRAPH_EINVAL ); + /* empty graph */ + CHECK_ERROR(igraph_deterministic_optimal_imitation(&h, 0, IGRAPH_MINIMUM, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL ); + /* length of quantities vector different from number of vertices */ + CHECK_ERROR(igraph_deterministic_optimal_imitation(&g, 0, IGRAPH_MINIMUM, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL ); + /* length of strategies vector different from number of vertices */ + CHECK_ERROR(igraph_deterministic_optimal_imitation(&g, 0, IGRAPH_MINIMUM, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL ); + + /* clean up */ + igraph_destroy(&g); + igraph_destroy(&h); + igraph_vector_destroy(&quant); + igraph_vector_int_destroy(&strat); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_diameter.c b/tests/unit/igraph_diameter.c new file mode 100644 index 0000000..dacd14e --- /dev/null +++ b/tests/unit/igraph_diameter.c @@ -0,0 +1,94 @@ +/* -*- 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, see . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_real_t result; + igraph_integer_t from, to; + igraph_vector_int_t path_edge, path_vertex, edge_vec; + igraph_vector_t weights_vec; + igraph_real_t weights[] = { 1, 2, 3, 4, 5, 1, 1, 1, 1}; + igraph_integer_t vec[] = {2,8}; + igraph_es_t edge_sele; + + printf("diameter of Barabasi graph:\n"); + + igraph_rng_seed(igraph_rng_default(), 1234); + igraph_barabasi_game(&g, 30, /*power=*/ 1, 30, 0, 0, /*A=*/ 1, + IGRAPH_DIRECTED, IGRAPH_BARABASI_BAG, + /*start_from=*/ 0); + igraph_diameter(&g, &result, NULL, NULL, NULL, NULL, IGRAPH_UNDIRECTED, 1); + + printf("Diameter: %" IGRAPH_PRId "\n", (igraph_integer_t) result); + + igraph_destroy(&g); + + printf("diameter of ring and the path in terms of edges and vertices \n"); + + igraph_ring(&g, 10, IGRAPH_DIRECTED, 0, 0); + igraph_vector_int_init(&path_vertex, 0); + igraph_vector_int_init(&path_edge, 0); + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + igraph_diameter(&g, &result, &from, &to, &path_vertex, &path_edge, IGRAPH_DIRECTED, 1); + printf("diameter: %g, from %" IGRAPH_PRId " to %" IGRAPH_PRId "\n", result, + from, to); + print_vector_int(&path_vertex); + print_vector_int(&path_edge); + igraph_vector_int_destroy(&path_vertex); + igraph_vector_int_destroy(&path_edge); + + //disconnected graph + printf("disconnected ring graph\n"); + igraph_vector_int_view(&edge_vec, vec, sizeof(vec) / sizeof(vec[0])); + igraph_es_vector(&edge_sele, &edge_vec); + igraph_delete_edges(&g, edge_sele); + printf("The largest path in one connected component\n"); + igraph_diameter(&g, &result, NULL, NULL, NULL, NULL, IGRAPH_DIRECTED, 1); + print_real(stdout, result, "%g"); + printf("\nuconn = False \n"); + igraph_diameter(&g, &result, NULL, NULL, NULL, NULL, IGRAPH_DIRECTED, 0); + print_real(stdout, result, "%g"); + + igraph_es_destroy(&edge_sele); + igraph_destroy(&g); + + // test graph with zero nodes + printf("\ngraph with zero nodes\n"); + igraph_empty(&g, 0, IGRAPH_DIRECTED); + igraph_diameter(&g, &result, &from, &to, NULL, NULL, IGRAPH_DIRECTED, 1); + print_real(stdout, result, "%g"); + printf("\nfrom = %" IGRAPH_PRId ", to = %" IGRAPH_PRId "\n", from, to); + igraph_destroy(&g); + + //test graph with one node + printf("graph with one node\n"); + igraph_empty(&g, 1, IGRAPH_DIRECTED); + igraph_diameter(&g, &result, &from, &to, NULL, NULL, IGRAPH_DIRECTED, 1); + print_real(stdout, result, "%g"); + printf("\nfrom = %" IGRAPH_PRId ", to = %" IGRAPH_PRId "\n", from, to); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_diameter.out b/tests/unit/igraph_diameter.out new file mode 100644 index 0000000..d0d4578 --- /dev/null +++ b/tests/unit/igraph_diameter.out @@ -0,0 +1,17 @@ +diameter of Barabasi graph: +Diameter: 2 +diameter of ring and the path in terms of edges and vertices +diameter: 9, from 0 to 9 +( 0 1 2 3 4 5 6 7 8 9 ) +( 0 1 2 3 4 5 6 7 8 ) +disconnected ring graph +The largest path in one connected component +5 +uconn = False +Inf +graph with zero nodes +NaN +from = -1, to = -1 +graph with one node +0 +from = 0, to = 0 diff --git a/tests/unit/igraph_diameter_dijkstra.c b/tests/unit/igraph_diameter_dijkstra.c new file mode 100644 index 0000000..ec2c87f --- /dev/null +++ b/tests/unit/igraph_diameter_dijkstra.c @@ -0,0 +1,113 @@ +/* -*- 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, see . +*/ + +#include + +#include "test_utilities.h" + +void call_and_print(igraph_t *g, igraph_vector_t *weights, igraph_bool_t unconn, igraph_bool_t directed) { + igraph_vector_int_t path_vertex, path_edge; + igraph_real_t result; + igraph_integer_t from, to; + + igraph_vector_int_init(&path_edge, 0); + igraph_vector_int_init(&path_vertex, 0); + + igraph_diameter_dijkstra(g, weights, &result, &from, &to, &path_vertex, &path_edge, directed, unconn); + + printf("Diameter: "); + print_real(stdout, result, "%g"); + printf(", from %" IGRAPH_PRId " to %" IGRAPH_PRId "\n", from, to); + printf("Edges:\n"); + print_vector_int(&path_edge); + printf("Vertices:\n"); + print_vector_int(&path_vertex); + + igraph_vector_int_destroy(&path_edge); + igraph_vector_int_destroy(&path_vertex); + printf("\n"); +} + +int main(void) { + igraph_t g_ring, g_0, g_1, g_2, g_lm; + igraph_vector_t weights, weights_neg, weights_0; + + igraph_vector_init_int(&weights, 9, 1, 2, 3, 4, 5, 1, 1, 1, 1); + igraph_vector_init_int(&weights_neg, 9, -1, 2, 3, 4, 5, 1, 1, 1, 1); + igraph_vector_init(&weights_0, 0); + + igraph_empty(&g_0, 0, IGRAPH_DIRECTED); + igraph_empty(&g_1, 1, IGRAPH_DIRECTED); + igraph_empty(&g_2, 2, IGRAPH_DIRECTED); + igraph_ring(&g_ring, 10, IGRAPH_DIRECTED, 0, 0); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 4,3, 4,3, -1); + + printf("Graph with zero nodes:\n"); + call_and_print(&g_0, NULL, 1, 1); + + printf("Graph with zero nodes, weighted:\n"); + call_and_print(&g_0, &weights_0, 1, 1); + + printf("Graph with one node:\n"); + call_and_print(&g_1, NULL, 1, 1); + + printf("Graph with one node, weighted:\n"); + call_and_print(&g_1, &weights_0, 1, 1); + + printf("Graph with one node, returns inf for unconnected:\n"); + call_and_print(&g_1, NULL, 0, 1); + + printf("Graph with two nodes:\n"); + call_and_print(&g_2, NULL, 1, 1); + + printf("Graph with two nodes, returns inf for unconnected:\n"); + call_and_print(&g_2, NULL, 0, 1); + + printf("Ring without weights:\n"); + call_and_print(&g_ring, NULL, 1, 1); + + printf("Ring with weights:\n"); + call_and_print(&g_ring, &weights, 1, 1); + + printf("Graph with loops and multiple edges:\n"); + call_and_print(&g_lm, NULL, 1, 1); + + printf("Graph with loops and multiple edges, direction ignored:\n"); + call_and_print(&g_lm, NULL, 1, 0); + + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Ring with some negative weights:\n"); + igraph_diameter_dijkstra(&g_ring, &weights_neg, NULL, NULL, NULL, NULL, NULL, IGRAPH_DIRECTED, 1); + + printf("Ring with wrong weight vector size:\n"); + igraph_diameter_dijkstra(&g_ring, &weights_0, NULL, NULL, NULL, NULL, NULL, IGRAPH_DIRECTED, 1); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_2); + igraph_destroy(&g_ring); + igraph_destroy(&g_lm); + igraph_vector_destroy(&weights); + igraph_vector_destroy(&weights_neg); + igraph_vector_destroy(&weights_0); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_diameter_dijkstra.out b/tests/unit/igraph_diameter_dijkstra.out new file mode 100644 index 0000000..aeeac59 --- /dev/null +++ b/tests/unit/igraph_diameter_dijkstra.out @@ -0,0 +1,79 @@ +Graph with zero nodes: +Diameter: NaN, from -1 to -1 +Edges: +( ) +Vertices: +( ) + +Graph with zero nodes, weighted: +Diameter: NaN, from -1 to -1 +Edges: +( ) +Vertices: +( ) + +Graph with one node: +Diameter: 0, from 0 to 0 +Edges: +( ) +Vertices: +( 0 ) + +Graph with one node, weighted: +Diameter: 0, from 0 to 0 +Edges: +( ) +Vertices: +( 0 ) + +Graph with one node, returns inf for unconnected: +Diameter: 0, from 0 to 0 +Edges: +( ) +Vertices: +( 0 ) + +Graph with two nodes: +Diameter: 0, from 0 to 0 +Edges: +( ) +Vertices: +( 0 ) + +Graph with two nodes, returns inf for unconnected: +Diameter: Inf, from -1 to -1 +Edges: +( ) +Vertices: +( ) + +Ring without weights: +Diameter: 9, from 0 to 9 +Edges: +( 0 1 2 3 4 5 6 7 8 ) +Vertices: +( 0 1 2 3 4 5 6 7 8 9 ) + +Ring with weights: +Diameter: 19, from 0 to 9 +Edges: +( 0 1 2 3 4 5 6 7 8 ) +Vertices: +( 0 1 2 3 4 5 6 7 8 9 ) + +Graph with loops and multiple edges: +Diameter: 2, from 0 to 3 +Edges: +( 0 3 ) +Vertices: +( 0 1 3 ) + +Graph with loops and multiple edges, direction ignored: +Diameter: 3, from 0 to 4 +Edges: +( 0 3 7 ) +Vertices: +( 0 1 3 4 ) + +Ring with some negative weights: +Ring with wrong weight vector size: diff --git a/tests/unit/igraph_disjoint_union.c b/tests/unit/igraph_disjoint_union.c new file mode 100644 index 0000000..4da98c8 --- /dev/null +++ b/tests/unit/igraph_disjoint_union.c @@ -0,0 +1,69 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2024 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 +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t left, right, uni; + igraph_vector_ptr_t glist; + igraph_integer_t i, n; + + igraph_small(&left, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,3, -1); + igraph_small(&right, 5, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,4, -1); + + igraph_disjoint_union(&uni, &left, &right); + print_graph(&uni); + printf("\n"); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&uni); + + /* Empty graph list; the result is the directed null graph. */ + igraph_vector_ptr_init(&glist, 0); + igraph_disjoint_union_many(&uni, &glist); + print_graph(&uni); + igraph_vector_ptr_destroy(&glist); + igraph_destroy(&uni); + + /* Non-empty graph list. */ + igraph_vector_ptr_init(&glist, 10); + n = igraph_vector_ptr_size(&glist); + for (i = 0; i < n; i++) { + VECTOR(glist)[i] = IGRAPH_CALLOC(1, igraph_t); + igraph_small(VECTOR(glist)[i], 2, IGRAPH_DIRECTED, 0,1, 1,0, -1); + } + igraph_disjoint_union_many(&uni, &glist); + print_graph(&uni); + printf("\n"); + + /* Destroy and free the graph list. */ + n = igraph_vector_ptr_size(&glist); + for (i = 0; i < n; i++) { + igraph_destroy(VECTOR(glist)[i]); + free(VECTOR(glist)[i]); + } + igraph_vector_ptr_destroy(&glist); + igraph_destroy(&uni); + + return 0; +} diff --git a/tests/unit/igraph_disjoint_union.out b/tests/unit/igraph_disjoint_union.out new file mode 100644 index 0000000..ed670b1 --- /dev/null +++ b/tests/unit/igraph_disjoint_union.out @@ -0,0 +1,42 @@ +directed: false +vcount: 9 +edges: { +1 0 +2 1 +2 2 +3 2 +5 4 +6 5 +6 6 +8 6 +} + +directed: true +vcount: 0 +edges: { +} +directed: true +vcount: 20 +edges: { +0 1 +1 0 +2 3 +3 2 +4 5 +5 4 +6 7 +7 6 +8 9 +9 8 +10 11 +11 10 +12 13 +13 12 +14 15 +15 14 +16 17 +17 16 +18 19 +19 18 +} + diff --git a/tests/unit/igraph_distances_floyd_warshall.c b/tests/unit/igraph_distances_floyd_warshall.c new file mode 100644 index 0000000..1408bb6 --- /dev/null +++ b/tests/unit/igraph_distances_floyd_warshall.c @@ -0,0 +1,119 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_matrix_t d, d2; + igraph_vector_t weights; + + igraph_matrix_init(&d, 0, 0); + igraph_matrix_init(&d2, 0, 0); + + printf("Null graph\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL); + print_matrix(&d); + igraph_destroy(&g); + + printf("\nSingleton graph\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL); + print_matrix(&d); + igraph_destroy(&g); + + igraph_small(&g, 9, IGRAPH_DIRECTED, + 0,1, 1,2, 2,2, 2,3, 3,0, 0,4, 4,5, 3,0, 6,7, 5,4, 9,9, + -1); + + printf("\nUnweighted directed\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL); + print_matrix(&d); + + igraph_distances(&g, &d2, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + printf("\nUnweighted directed, 'in' mode\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_IN, IGRAPH_FLOYD_WARSHALL_ORIGINAL); + print_matrix(&d); + + igraph_distances(&g, &d2, igraph_vss_all(), igraph_vss_all(), IGRAPH_IN); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + printf("\nUnweighted undirected\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_ALL, IGRAPH_FLOYD_WARSHALL_ORIGINAL); + print_matrix(&d); + + igraph_distances(&g, &d2, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + igraph_vector_init_int(&weights, igraph_ecount(&g), + 2, 1, 5, 1, 2, 6, 8, 3, 3, 2, 3); + + printf("\nWeighted directed\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL); + print_matrix(&d); + + igraph_distances_bellman_ford(&g, &d2, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + printf("\nWeighted undirected\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_ALL, IGRAPH_FLOYD_WARSHALL_ORIGINAL); + print_matrix(&d); + + igraph_distances_bellman_ford(&g, &d2, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_ALL); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + VECTOR(weights)[1] = -2; + printf("\nNegative weight, directed\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL); + print_matrix(&d); + + igraph_distances_bellman_ford(&g, &d2, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + /* Check bad inputs */ + + /* Negative weight edge in undirected graph */ + CHECK_ERROR(igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_ALL, IGRAPH_FLOYD_WARSHALL_ORIGINAL), IGRAPH_ENEGLOOP); + + /* Negative cycle in directed graph */ + VECTOR(weights)[1] = -10; + CHECK_ERROR(igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), IGRAPH_ENEGLOOP); + + /* Negative self-loop in directed graph */ + VECTOR(weights)[1] = 1; + VECTOR(weights)[10] = -1; + CHECK_ERROR(igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), IGRAPH_ENEGLOOP); + + /* NaN weight */ + VECTOR(weights)[1] = IGRAPH_NAN; + CHECK_ERROR(igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), IGRAPH_EINVAL); + + igraph_vector_destroy(&weights); + + igraph_destroy(&g); + + igraph_matrix_destroy(&d2); + igraph_matrix_destroy(&d); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_distances_floyd_warshall.out b/tests/unit/igraph_distances_floyd_warshall.out new file mode 100644 index 0000000..18220ad --- /dev/null +++ b/tests/unit/igraph_distances_floyd_warshall.out @@ -0,0 +1,77 @@ +Null graph +[ 0-by-0 ] + +Singleton graph +[ 0 ] + +Unweighted directed +[ 0 1 2 3 1 2 Inf Inf Inf Inf + 3 0 1 2 4 5 Inf Inf Inf Inf + 2 3 0 1 3 4 Inf Inf Inf Inf + 1 2 3 0 2 3 Inf Inf Inf Inf + Inf Inf Inf Inf 0 1 Inf Inf Inf Inf + Inf Inf Inf Inf 1 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 1 Inf Inf + Inf Inf Inf Inf Inf Inf Inf 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Unweighted directed, 'in' mode +[ 0 3 2 1 Inf Inf Inf Inf Inf Inf + 1 0 3 2 Inf Inf Inf Inf Inf Inf + 2 1 0 3 Inf Inf Inf Inf Inf Inf + 3 2 1 0 Inf Inf Inf Inf Inf Inf + 1 4 3 2 0 1 Inf Inf Inf Inf + 2 5 4 3 1 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 Inf Inf Inf + Inf Inf Inf Inf Inf Inf 1 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Unweighted undirected +[ 0 1 2 1 1 2 Inf Inf Inf Inf + 1 0 1 2 2 3 Inf Inf Inf Inf + 2 1 0 1 3 4 Inf Inf Inf Inf + 1 2 1 0 2 3 Inf Inf Inf Inf + 1 2 3 2 0 1 Inf Inf Inf Inf + 2 3 4 3 1 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 1 Inf Inf + Inf Inf Inf Inf Inf Inf 1 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Weighted directed +[ 0 2 3 4 6 14 Inf Inf Inf Inf + 4 0 1 2 10 18 Inf Inf Inf Inf + 3 5 0 1 9 17 Inf Inf Inf Inf + 2 4 5 0 8 16 Inf Inf Inf Inf + Inf Inf Inf Inf 0 8 Inf Inf Inf Inf + Inf Inf Inf Inf 2 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 3 Inf Inf + Inf Inf Inf Inf Inf Inf Inf 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Weighted undirected +[ 0 2 3 2 6 8 Inf Inf Inf Inf + 2 0 1 2 8 10 Inf Inf Inf Inf + 3 1 0 1 9 11 Inf Inf Inf Inf + 2 2 1 0 8 10 Inf Inf Inf Inf + 6 8 9 8 0 2 Inf Inf Inf Inf + 8 10 11 10 2 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 3 Inf Inf + Inf Inf Inf Inf Inf Inf 3 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Negative weight, directed +[ 0 2 0 1 6 14 Inf Inf Inf Inf + 1 0 -2 -1 7 15 Inf Inf Inf Inf + 3 5 0 1 9 17 Inf Inf Inf Inf + 2 4 2 0 8 16 Inf Inf Inf Inf + Inf Inf Inf Inf 0 8 Inf Inf Inf Inf + Inf Inf Inf Inf 2 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 3 Inf Inf + Inf Inf Inf Inf Inf Inf Inf 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] diff --git a/tests/unit/igraph_distances_floyd_warshall_speedup.c b/tests/unit/igraph_distances_floyd_warshall_speedup.c new file mode 100644 index 0000000..e0010eb --- /dev/null +++ b/tests/unit/igraph_distances_floyd_warshall_speedup.c @@ -0,0 +1,126 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_matrix_t d, d2; + igraph_vector_t weights; + + igraph_matrix_init(&d, 0, 0); + igraph_matrix_init(&d2, 0, 0); + + printf("Null graph\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE); + print_matrix(&d); + igraph_destroy(&g); + + printf("\nSingleton graph\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE); + print_matrix(&d); + igraph_destroy(&g); + + igraph_small(&g, 9, IGRAPH_DIRECTED, + 0,1, 1,2, 2,2, 2,3, 3,0, 0,4, 4,5, 3,0, 6,7, 5,4, 9,9, + -1); + + printf("\nUnweighted directed\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE); + print_matrix(&d); + + igraph_distances(&g, &d2, igraph_vss_all(), igraph_vss_all(), IGRAPH_OUT); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + printf("\nUnweighted directed, 'in' mode\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_IN, IGRAPH_FLOYD_WARSHALL_TREE); + print_matrix(&d); + + igraph_distances(&g, &d2, igraph_vss_all(), igraph_vss_all(), IGRAPH_IN); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + printf("\nUnweighted undirected\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_ALL, IGRAPH_FLOYD_WARSHALL_TREE); + print_matrix(&d); + + igraph_distances(&g, &d2, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + igraph_vector_init_int(&weights, igraph_ecount(&g), + 2, 1, 5, 1, 2, 6, 8, 3, 3, 2, 3); + + printf("\nWeighted directed\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE); + print_matrix(&d); + + igraph_distances_bellman_ford(&g, &d2, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + printf("\nWeighted undirected\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_ALL, IGRAPH_FLOYD_WARSHALL_TREE); + print_matrix(&d); + + igraph_distances_bellman_ford(&g, &d2, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_ALL); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + VECTOR(weights)[1] = -2; + printf("\nNegative weight, directed\n"); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE); + print_matrix(&d); + + igraph_distances_bellman_ford(&g, &d2, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + /* Check bad inputs */ + + /* Negative weight edge in undirected graph */ + CHECK_ERROR(igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_ALL, IGRAPH_FLOYD_WARSHALL_TREE), IGRAPH_ENEGLOOP); + + /* Negative cycle in directed graph */ + VECTOR(weights)[1] = -10; + CHECK_ERROR(igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), IGRAPH_ENEGLOOP); + + /* Negative self-loop in directed graph */ + VECTOR(weights)[1] = 1; + VECTOR(weights)[10] = -1; + CHECK_ERROR(igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), IGRAPH_ENEGLOOP); + + /* NaN weight */ + VECTOR(weights)[1] = IGRAPH_NAN; + CHECK_ERROR(igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), IGRAPH_EINVAL); + igraph_destroy(&g); + + /* Unweighted directed - larger graph */ + igraph_erdos_renyi_game_gnp(&g, 100, 0.1, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_distances_floyd_warshall(&g, &d, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE); + igraph_distances_dijkstra(&g, &d2, igraph_vss_all(), igraph_vss_all(), NULL, IGRAPH_OUT); + IGRAPH_ASSERT(igraph_matrix_all_e(&d, &d2)); + + igraph_vector_destroy(&weights); + + igraph_destroy(&g); + + igraph_matrix_destroy(&d2); + igraph_matrix_destroy(&d); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_distances_floyd_warshall_speedup.out b/tests/unit/igraph_distances_floyd_warshall_speedup.out new file mode 100644 index 0000000..18220ad --- /dev/null +++ b/tests/unit/igraph_distances_floyd_warshall_speedup.out @@ -0,0 +1,77 @@ +Null graph +[ 0-by-0 ] + +Singleton graph +[ 0 ] + +Unweighted directed +[ 0 1 2 3 1 2 Inf Inf Inf Inf + 3 0 1 2 4 5 Inf Inf Inf Inf + 2 3 0 1 3 4 Inf Inf Inf Inf + 1 2 3 0 2 3 Inf Inf Inf Inf + Inf Inf Inf Inf 0 1 Inf Inf Inf Inf + Inf Inf Inf Inf 1 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 1 Inf Inf + Inf Inf Inf Inf Inf Inf Inf 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Unweighted directed, 'in' mode +[ 0 3 2 1 Inf Inf Inf Inf Inf Inf + 1 0 3 2 Inf Inf Inf Inf Inf Inf + 2 1 0 3 Inf Inf Inf Inf Inf Inf + 3 2 1 0 Inf Inf Inf Inf Inf Inf + 1 4 3 2 0 1 Inf Inf Inf Inf + 2 5 4 3 1 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 Inf Inf Inf + Inf Inf Inf Inf Inf Inf 1 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Unweighted undirected +[ 0 1 2 1 1 2 Inf Inf Inf Inf + 1 0 1 2 2 3 Inf Inf Inf Inf + 2 1 0 1 3 4 Inf Inf Inf Inf + 1 2 1 0 2 3 Inf Inf Inf Inf + 1 2 3 2 0 1 Inf Inf Inf Inf + 2 3 4 3 1 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 1 Inf Inf + Inf Inf Inf Inf Inf Inf 1 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Weighted directed +[ 0 2 3 4 6 14 Inf Inf Inf Inf + 4 0 1 2 10 18 Inf Inf Inf Inf + 3 5 0 1 9 17 Inf Inf Inf Inf + 2 4 5 0 8 16 Inf Inf Inf Inf + Inf Inf Inf Inf 0 8 Inf Inf Inf Inf + Inf Inf Inf Inf 2 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 3 Inf Inf + Inf Inf Inf Inf Inf Inf Inf 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Weighted undirected +[ 0 2 3 2 6 8 Inf Inf Inf Inf + 2 0 1 2 8 10 Inf Inf Inf Inf + 3 1 0 1 9 11 Inf Inf Inf Inf + 2 2 1 0 8 10 Inf Inf Inf Inf + 6 8 9 8 0 2 Inf Inf Inf Inf + 8 10 11 10 2 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 3 Inf Inf + Inf Inf Inf Inf Inf Inf 3 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] + +Negative weight, directed +[ 0 2 0 1 6 14 Inf Inf Inf Inf + 1 0 -2 -1 7 15 Inf Inf Inf Inf + 3 5 0 1 9 17 Inf Inf Inf Inf + 2 4 2 0 8 16 Inf Inf Inf Inf + Inf Inf Inf Inf 0 8 Inf Inf Inf Inf + Inf Inf Inf Inf 2 0 Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf 0 3 Inf Inf + Inf Inf Inf Inf Inf Inf Inf 0 Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf 0 ] diff --git a/tests/unit/igraph_distances_johnson.c b/tests/unit/igraph_distances_johnson.c new file mode 100644 index 0000000..c9d1094 --- /dev/null +++ b/tests/unit/igraph_distances_johnson.c @@ -0,0 +1,76 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g_empty, g_empty_dir, g_lm; + igraph_matrix_t result; + igraph_vs_t vids; + igraph_vector_t weights_empty, weights_lm, weights_lm_neg_loop; + + igraph_matrix_init(&result, 0, 0); + igraph_vs_all(&vids); + igraph_vector_init(&weights_empty, 0); + igraph_vector_init_int(&weights_lm_neg_loop, 9, -4, -3, -2, -1, 0, 1, 2, 3, 4); + igraph_vector_init_int(&weights_lm, 9, -1, 0, 1, -2, 2, 3, 4, 5, 6); + igraph_small(&g_empty, 0, 0, -1); + igraph_small(&g_empty_dir, 0, 1, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,2, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + + igraph_set_error_handler(igraph_error_handler_printignore); + + printf("No vertices, not directed:\n"); + IGRAPH_ASSERT(igraph_distances_johnson(&g_empty, &result, vids, vids, &weights_empty) == IGRAPH_SUCCESS); + print_matrix(&result); + + printf("No vertices, directed:\n"); + IGRAPH_ASSERT(igraph_distances_johnson(&g_empty_dir, &result, vids, vids, &weights_empty) == IGRAPH_SUCCESS); + print_matrix(&result); + + printf("Directed graph with loops and multi-edges with negative loop:\n"); + IGRAPH_ASSERT(igraph_distances_johnson(&g_lm, &result, vids, vids, &weights_lm_neg_loop) == IGRAPH_ENEGLOOP); + + printf("Directed graph with loops and multi-edges:\n"); + IGRAPH_ASSERT(igraph_distances_johnson(&g_lm, &result, vids, vids, &weights_lm) == IGRAPH_SUCCESS); + print_matrix(&result); + + printf("Directed graph with loops and multi-edges, select vertices 1 and 2:\n"); + IGRAPH_ASSERT(igraph_distances_johnson(&g_lm, &result, igraph_vss_range(1, 3), igraph_vss_range(1, 3), &weights_lm) == IGRAPH_SUCCESS); + print_matrix(&result); + + printf("Directed graph with loops and multi-edges, select 0 -> 2:\n"); + IGRAPH_ASSERT(igraph_distances_johnson(&g_lm, &result, igraph_vss_1(0), igraph_vss_1(2), &weights_lm) == IGRAPH_SUCCESS); + print_matrix(&result); + + printf("Directed graph with loops and multi-edges, select none:\n"); + IGRAPH_ASSERT(igraph_distances_johnson(&g_lm, &result, igraph_vss_none(), igraph_vss_none(), &weights_lm) == IGRAPH_SUCCESS); + print_matrix(&result); + + igraph_matrix_destroy(&result); + igraph_destroy(&g_empty); + igraph_destroy(&g_empty_dir); + igraph_destroy(&g_lm); + igraph_vector_destroy(&weights_empty); + igraph_vector_destroy(&weights_lm); + igraph_vector_destroy(&weights_lm_neg_loop); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_distances_johnson.out b/tests/unit/igraph_distances_johnson.out new file mode 100644 index 0000000..a0073b6 --- /dev/null +++ b/tests/unit/igraph_distances_johnson.out @@ -0,0 +1,19 @@ +No vertices, not directed: +[ 0-by-0 ] +No vertices, directed: +[ 0-by-0 ] +Directed graph with loops and multi-edges with negative loop: +Directed graph with loops and multi-edges: +[ 0 -1 -3 1 6 Inf + 1 0 -2 2 7 Inf + 3 2 0 4 9 Inf + Inf Inf Inf 0 5 Inf + Inf Inf Inf Inf 0 Inf + Inf Inf Inf Inf Inf 0 ] +Directed graph with loops and multi-edges, select vertices 1 and 2: +[ 0 -2 + 2 0 ] +Directed graph with loops and multi-edges, select 0 -> 2: +[ -3 ] +Directed graph with loops and multi-edges, select none: +[ 0-by-0 ] diff --git a/tests/unit/igraph_diversity.c b/tests/unit/igraph_diversity.c new file mode 100644 index 0000000..6aa549c --- /dev/null +++ b/tests/unit/igraph_diversity.c @@ -0,0 +1,87 @@ +/* vim:set ts=4 sw=4 sts=4 et: */ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_t result; + igraph_vector_t weights; + + igraph_vector_init(&result, 0); + + /* null graph */ + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_vector_init(&weights, 0); + + printf("Null graph:\n"); + igraph_diversity(&g, &weights, &result, igraph_vss_all()); + print_vector(&result); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* graph with no edges */ + igraph_empty(&g, 5, IGRAPH_UNDIRECTED); + igraph_vector_init(&weights, 0); + + printf("Empty graph:\n"); + igraph_diversity(&g, &weights, &result, igraph_vss_all()); + print_vector(&result); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* real graph */ + igraph_small(&g, 4, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,2, 1,3, 2,3, -1); + igraph_vector_init_int_end(&weights, -1, 3, 2, 8, 1, 1, -1); + + printf("Graph with 4 nodes and 5 edges:\n"); + igraph_diversity(&g, &weights, &result, igraph_vss_all()); + print_vector(&result); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* degree-one vertices */ + igraph_kary_tree(&g, 10, 2, IGRAPH_TREE_UNDIRECTED); + igraph_vector_init_range(&weights, 1, igraph_ecount(&g) + 1); + + printf("Tree (having degree-one vertices):\n"); + igraph_diversity(&g, &weights, &result, igraph_vss_all()); + print_vector(&result); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* error conditions are tested from now on */ + VERIFY_FINALLY_STACK(); + + /* graph with multiple edges */ + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,1, 0,2, 2,0, -1); + igraph_vector_init_int_end(&weights, -1, 3, 2, 8, -1); + CHECK_ERROR(igraph_diversity(&g, &weights, &result, igraph_vss_all()), IGRAPH_EINVAL); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* negative weights */ + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,1, 0,2, 2,1, -1); + igraph_vector_init_int_end(&weights, -1, 3, -2, 8, -1); + CHECK_ERROR(igraph_diversity(&g, &weights, &result, igraph_vss_all()), IGRAPH_EINVAL); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* directed graph */ + igraph_small(&g, 3, IGRAPH_DIRECTED, 0,1, 0,2, -1); + igraph_vector_init_int_end(&weights, -1, 3, 2, -1); + CHECK_ERROR(igraph_diversity(&g, &weights, &result, igraph_vss_all()), IGRAPH_EINVAL); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + igraph_vector_destroy(&result); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_diversity.out b/tests/unit/igraph_diversity.out new file mode 100644 index 0000000..fffff67 --- /dev/null +++ b/tests/unit/igraph_diversity.out @@ -0,0 +1,8 @@ +Null graph: +( ) +Empty graph: +( NaN NaN NaN NaN NaN ) +Graph with 4 nodes and 5 edges: +( 0.970951 0.75 0.69137 1 ) +Tree (having degree-one vertices): +( 0.918296 0.88686 0.921463 0.934206 0.890492 0 0 0 0 0 ) diff --git a/tests/unit/igraph_dominator_tree.c b/tests/unit/igraph_dominator_tree.c new file mode 100644 index 0000000..e8580fa --- /dev/null +++ b/tests/unit/igraph_dominator_tree.c @@ -0,0 +1,132 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g, domtree; + igraph_vector_int_t dom; + igraph_vector_int_t leftout; + + igraph_vector_int_init(&dom, 0); + igraph_small(&g, 13, IGRAPH_DIRECTED, + 0, 1, 0, 7, 0, 10, + 1, 2, 1, 5, + 2, 3, + 3, 4, + 4, 3, 4, 0, + 5, 3, 5, 6, + 6, 3, + 7, 8, 7, 10, 7, 11, + 8, 9, + 9, 4, 9, 8, + 10, 11, + 11, 12, + 12, 9, + -1); + + /* Check NULL vector arguments */ + igraph_dominator_tree(&g, /*root=*/ 0, /*dom=*/ 0, /*domtree=*/ 0, + /*leftout=*/ 0, /*mode=*/ IGRAPH_OUT); + + /* Proper calculation */ + igraph_dominator_tree(&g, /*root=*/ 0, &dom, /*domtree=*/ 0, + /*leftout=*/ 0, /*mode=*/ IGRAPH_OUT); + igraph_vector_int_print(&dom); + + /* Tree calculation */ + igraph_dominator_tree(&g, /*root=*/ 0, /*dom=*/ 0, /*domtree=*/ &domtree, + /*leftout=*/ 0, /*mode=*/ IGRAPH_OUT); + igraph_write_graph_edgelist(&domtree, stdout); + + igraph_vector_int_destroy(&dom); + igraph_destroy(&domtree); + igraph_destroy(&g); + + /* -------------------------------------------------------------------*/ + + igraph_vector_int_init(&dom, 0); + igraph_small(&g, 13, IGRAPH_DIRECTED, + 1, 0, 2, 0, 3, 0, + 4, 1, + 1, 2, 4, 2, 5, 2, + 6, 3, 7, 3, + 12, 4, + 8, 5, + 9, 6, + 9, 7, 10, 7, + 5, 8, 11, 8, + 11, 9, + 9, 10, + 9, 11, 0, 11, + 8, 12, + -1); + + /* Proper calculation */ + igraph_dominator_tree(&g, /*root=*/ 0, &dom, /*domtree=*/ 0, + /*leftout=*/ 0, /*mode=*/ IGRAPH_IN); + igraph_vector_int_print(&dom); + + /* Tree calculation */ + igraph_dominator_tree(&g, /*root=*/ 0, /*dom=*/ 0, /*domtree=*/ &domtree, + /*leftout=*/ 0, /*mode=*/ IGRAPH_IN); + igraph_write_graph_edgelist(&domtree, stdout); + + igraph_vector_int_destroy(&dom); + igraph_destroy(&domtree); + igraph_destroy(&g); + + /* -------------------------------------------------------------------*/ + + igraph_vector_int_init(&dom, 0); + igraph_vector_int_init(&leftout, 0); + + /* Check a graph with more components */ + igraph_small(&g, 20, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 3, + 1, 4, + 2, 1, 2, 4, 2, 8, + 3, 9, 3, 10, + 4, 15, + 8, 11, + 9, 12, + 10, 12, 10, 13, + 11, 8, 11, 14, + 12, 14, + 13, 12, + 14, 12, 14, 0, + 15, 11, + -1); + + igraph_dominator_tree(&g, /*root=*/ 0, &dom, &domtree, + &leftout, /*mode=*/ IGRAPH_OUT); + igraph_vector_int_print(&dom); + igraph_vector_int_print(&leftout); + igraph_write_graph_edgelist(&domtree, stdout); + + igraph_vector_int_destroy(&dom); + igraph_vector_int_destroy(&leftout); + igraph_destroy(&domtree); + igraph_destroy(&g); + + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_dominator_tree.out b/tests/unit/igraph_dominator_tree.out new file mode 100644 index 0000000..a4ba233 --- /dev/null +++ b/tests/unit/igraph_dominator_tree.out @@ -0,0 +1,40 @@ +-1 0 1 0 0 1 5 0 0 0 0 0 11 +0 1 +0 3 +0 4 +0 7 +0 8 +0 9 +0 10 +0 11 +1 2 +1 5 +5 6 +11 12 +-1 0 0 0 0 0 3 3 0 0 7 0 4 +1 0 +2 0 +3 0 +4 0 +5 0 +6 3 +7 3 +8 0 +9 0 +10 7 +11 0 +12 4 +-1 0 0 0 0 -2 -2 -2 0 3 3 0 0 10 0 4 -2 -2 -2 -2 +5 6 7 16 17 18 19 +0 1 +0 2 +0 3 +0 4 +0 8 +0 11 +0 12 +0 14 +3 9 +3 10 +4 15 +10 13 diff --git a/tests/unit/igraph_dot_product_game.c b/tests/unit/igraph_dot_product_game.c new file mode 100644 index 0000000..7735c01 --- /dev/null +++ b/tests/unit/igraph_dot_product_game.c @@ -0,0 +1,54 @@ +/* + 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 . + */ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_matrix_t vecs; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No vertices:\n"); + { + matrix_init_real_row_major(&vecs, 0, 0, NULL); + igraph_dot_product_game(&g, &vecs, 1); + print_graph_canon(&g); + igraph_matrix_destroy(&vecs); + igraph_destroy(&g); + } + + printf("5 vertices, 0-1, 0-4, 1-4 are connected:\n"); + { + igraph_real_t elems[] = { + 1, 0, 0, -10, 100, + 0, 0.01, 0, -100, 100, + 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0 + }; + matrix_init_real_row_major(&vecs, 4, 5, elems); + igraph_dot_product_game(&g, &vecs, 1); + print_graph_canon(&g); + igraph_matrix_destroy(&vecs); + igraph_destroy(&g); + } + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_dot_product_game.out b/tests/unit/igraph_dot_product_game.out new file mode 100644 index 0000000..f4e0f2e --- /dev/null +++ b/tests/unit/igraph_dot_product_game.out @@ -0,0 +1,16 @@ +No vertices: +directed: true +vcount: 0 +edges: { +} +5 vertices, 0-1, 0-4, 1-4 are connected: +directed: true +vcount: 5 +edges: { +0 1 +0 4 +1 0 +1 4 +4 0 +4 1 +} diff --git a/tests/unit/igraph_dyad_census.c b/tests/unit/igraph_dyad_census.c new file mode 100644 index 0000000..97baffb --- /dev/null +++ b/tests/unit/igraph_dyad_census.c @@ -0,0 +1,63 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph) { + igraph_real_t mut, asym, null; + IGRAPH_ASSERT(igraph_dyad_census(graph, &mut, &asym, &null) == IGRAPH_SUCCESS); + printf("Mutual: %.f ", mut); + printf("asymmetric: %.f ", asym); + printf("null: %.f\n\n", null); +} + + +int main(void) { + igraph_t g_0, g_1, g_2, g_lm, g_lmu; + + igraph_small(&g_0, 0, IGRAPH_DIRECTED, -1); + igraph_small(&g_1, 1, IGRAPH_DIRECTED, -1); + igraph_small(&g_2, 2, IGRAPH_DIRECTED, 0,1, -1); + igraph_small(&g_lm, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_lmu, 6, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices:\n"); + call_and_print(&g_0); + + printf("One vertex:\n"); + call_and_print(&g_1); + + printf("Two vertices:\n"); + call_and_print(&g_2); + + printf("Graph with loops and multiple edges:\n"); + call_and_print(&g_lm); + + printf("Same graph, but undirected:\n"); + call_and_print(&g_lmu); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_2); + igraph_destroy(&g_lm); + igraph_destroy(&g_lmu); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_dyad_census.out b/tests/unit/igraph_dyad_census.out new file mode 100644 index 0000000..f9a1a26 --- /dev/null +++ b/tests/unit/igraph_dyad_census.out @@ -0,0 +1,15 @@ +No vertices: +Mutual: 0 asymmetric: 0 null: 0 + +One vertex: +Mutual: 0 asymmetric: 0 null: 0 + +Two vertices: +Mutual: 0 asymmetric: 1 null: 0 + +Graph with loops and multiple edges: +Mutual: 1 asymmetric: 4 null: 10 + +Same graph, but undirected: +Mutual: 5 asymmetric: 0 null: 10 + diff --git a/tests/unit/igraph_ecc.c b/tests/unit/igraph_ecc.c new file mode 100644 index 0000000..6a07759 --- /dev/null +++ b/tests/unit/igraph_ecc.c @@ -0,0 +1,219 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +/* Compare vector for equality, allowing for NaN elements. + * Note that NaN != NaN, thus we cannot use vector_all_e(). */ +igraph_bool_t vec_equal(const igraph_vector_t *v1, const igraph_vector_t *v2) { + igraph_integer_t n = igraph_vector_size(v1); + + if (igraph_vector_size(v2) != n) return false; + + for (igraph_integer_t i=0; i < n; i++) { + igraph_real_t x1 = VECTOR(*v1)[i], x2 = VECTOR(*v2)[i]; + + if (isnan(x1)) { + if (! isnan(x2)) { + return false; + } + } else if (x1 != x2) { + return false; + } + } + return true; +} + +/* This is a trivial implementation of ECC for k=3 using igraph_list_triangles() */ +void get_ecc3(const igraph_t *g, igraph_vector_t *res, igraph_bool_t offset, igraph_bool_t normalize) { + igraph_vector_int_t triangles_vec, eids; + igraph_matrix_int_t triangles; + + igraph_vector_resize(res, igraph_ecount(g)); + igraph_vector_null(res); + + igraph_vector_int_init(&triangles_vec, 0); + igraph_list_triangles(g, &triangles_vec); + + igraph_matrix_int_view_from_vector(&triangles, &triangles_vec, 3); + + igraph_vector_int_init(&eids, 0); + + for (igraph_integer_t i=0; i < igraph_matrix_int_ncol(&triangles); i++) { + igraph_integer_t u, v, w; + igraph_integer_t ec; + + u = MATRIX(triangles, 0, i); + v = MATRIX(triangles, 1, i); + w = MATRIX(triangles, 2, i); + + igraph_get_all_eids_between(g, &eids, u, v, IGRAPH_UNDIRECTED); + ec = igraph_vector_int_size(&eids); + for (igraph_integer_t j=0; j < ec; j++) { + VECTOR(*res)[ VECTOR(eids)[j] ] += 1; + } + + igraph_get_all_eids_between(g, &eids, v, w, IGRAPH_UNDIRECTED); + ec = igraph_vector_int_size(&eids); + for (igraph_integer_t j=0; j < ec; j++) { + VECTOR(*res)[ VECTOR(eids)[j] ] += 1; + } + + igraph_get_all_eids_between(g, &eids, w, u, IGRAPH_UNDIRECTED); + ec = igraph_vector_int_size(&eids); + for (igraph_integer_t j=0; j < ec; j++) { + VECTOR(*res)[ VECTOR(eids)[j] ] += 1; + } + } + + igraph_vector_int_t degree; + igraph_vector_int_init(°ree, 0); + igraph_degree(g, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + + for (igraph_integer_t e=0; e < igraph_ecount(g); e++) { + igraph_integer_t v1 = IGRAPH_FROM(g, e), v2 = IGRAPH_TO(g, e); + igraph_real_t s; + if (v1 == v2) { + s = 0.0; + } else { + igraph_integer_t d1 = VECTOR(degree)[v1], d2 = VECTOR(degree)[v2]; + s = (d1 < d2 ? d1 : d2) - 1.0; + } + if (offset) VECTOR(*res)[e] += 1; + if (normalize) VECTOR(*res)[e] /= s; + } + + igraph_vector_int_destroy(°ree); + + igraph_vector_int_destroy(&eids); + igraph_vector_int_destroy(&triangles_vec); +} + +/* These are globals, as they're used both from test_ecc() and main() */ +igraph_vector_t ecc1, ecc2, ecc3; + +void test_ecc(const igraph_t *g) { + printf("k=3\n"); + igraph_ecc(g, &ecc1, igraph_ess_all(IGRAPH_EDGEORDER_ID), 3, false, true); + print_vector(&ecc1); + + igraph_ecc(g, &ecc2, igraph_ess_range(0, igraph_ecount(g)), 3, false, true); + print_vector(&ecc2); + IGRAPH_ASSERT(vec_equal(&ecc1, &ecc2)); + + get_ecc3(g, &ecc3, false, true); + print_vector(&ecc3); + + IGRAPH_ASSERT(vec_equal(&ecc1, &ecc3)); + + printf("\nk=4\n"); + igraph_ecc(g, &ecc1, igraph_ess_all(IGRAPH_EDGEORDER_ID), 4, false, true); + print_vector(&ecc1); + + igraph_ecc(g, &ecc2, igraph_ess_range(0, igraph_ecount(g)), 4, false, true); + print_vector(&ecc2); + IGRAPH_ASSERT(vec_equal(&ecc1, &ecc2)); +} + +int main(void) { + igraph_t g; + + igraph_vector_init(&ecc1, 0); + igraph_vector_init(&ecc2, 0); + igraph_vector_init(&ecc3, 0); + + /* Null graph */ + + printf("Null graph:\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + test_ecc(&g); + igraph_destroy(&g); + + /* Singleton */ + + printf("\nSingleton graph:\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + test_ecc(&g); + igraph_destroy(&g); + + /* P_2 */ + + printf("\nP_2 graph\n"); + igraph_full(&g, 2, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + test_ecc(&g); + igraph_destroy(&g); + + /* K_5 */ + + printf("\nK_5 graph:\n"); + igraph_full(&g, 5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + test_ecc(&g); + igraph_destroy(&g); + + printf("\nK_5 graph with self-loops:\n"); + igraph_full(&g, 5, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + test_ecc(&g); + igraph_destroy(&g); + + /* Multigraph */ + + printf("\nMultigraph:\n"); + igraph_small(&g, 5, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, 0,1, 1,3, 3,4, 4,0, 0,5, 5,5, 5,5, 1,4, + -1); + test_ecc(&g); + igraph_destroy(&g); + + /* Large degrees */ + { + igraph_vector_int_t deg; + printf("\nGraph with large degrees:\n"); + igraph_vector_int_init_range(°, 0, 30); + for (igraph_integer_t i=0; i < igraph_vector_int_size(°); i++) { + VECTOR(deg)[i] /= 2; + VECTOR(deg)[i] += 1; + } + igraph_realize_degree_sequence(&g, °, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + test_ecc(&g); + igraph_destroy(&g); + igraph_vector_int_destroy(°); + } + + /* Karate club */ + + printf("\nZachary karate club:\n"); + igraph_famous(&g, "Zachary"); + + test_ecc(&g); + + /* Check invalid input */ + + CHECK_ERROR(igraph_ecc(&g, &ecc1, igraph_ess_all(IGRAPH_EDGEORDER_ID), 2, false, true), IGRAPH_EINVAL); + CHECK_ERROR(igraph_ecc(&g, &ecc1, igraph_ess_all(IGRAPH_EDGEORDER_ID), 5, false, true), IGRAPH_UNIMPLEMENTED); + + igraph_destroy(&g); + + igraph_vector_destroy(&ecc3); + igraph_vector_destroy(&ecc2); + igraph_vector_destroy(&ecc1); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_ecc.out b/tests/unit/igraph_ecc.out new file mode 100644 index 0000000..5f957e9 --- /dev/null +++ b/tests/unit/igraph_ecc.out @@ -0,0 +1,79 @@ +Null graph: +k=3 +( ) +( ) +( ) + +k=4 +( ) +( ) + +Singleton graph: +k=3 +( ) +( ) +( ) + +k=4 +( ) +( ) + +P_2 graph +k=3 +( NaN ) +( NaN ) +( NaN ) + +k=4 +( NaN ) +( NaN ) + +K_5 graph: +k=3 +( 1 1 1 1 1 1 1 1 1 1 ) +( 1 1 1 1 1 1 1 1 1 1 ) +( 1 1 1 1 1 1 1 1 1 1 ) + +k=4 +( 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 ) +( 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 0.666667 ) + +K_5 graph with self-loops: +k=3 +( NaN 0.6 0.6 0.6 0.6 NaN 0.6 0.6 0.6 NaN 0.6 0.6 NaN 0.6 NaN ) +( NaN 0.6 0.6 0.6 0.6 NaN 0.6 0.6 0.6 NaN 0.6 0.6 NaN 0.6 NaN ) +( NaN 0.6 0.6 0.6 0.6 NaN 0.6 0.6 0.6 NaN 0.6 0.6 NaN 0.6 NaN ) + +k=4 +( NaN 0.24 0.24 0.24 0.24 NaN 0.24 0.24 0.24 NaN 0.24 0.24 NaN 0.24 NaN ) +( NaN 0.24 0.24 0.24 0.24 NaN 0.24 0.24 0.24 NaN 0.24 0.24 NaN 0.24 NaN ) + +Multigraph: +k=3 +( 0.5 1 1 0.5 1 1 0.5 0 NaN NaN 1 ) +( 0.5 1 1 0.5 1 1 0.5 0 NaN NaN 1 ) +( 0.5 1 1 0.5 1 1 0.5 0 NaN NaN 1 ) + +k=4 +( 0.0625 0.25 0.25 0.0625 0.25 0.5 0.25 0 NaN NaN 0.125 ) +( 0.0625 0.25 0.25 0.0625 0.25 0.5 0.25 0 NaN NaN 0.125 ) + +Graph with large degrees: +k=3 +( NaN NaN 1 1 0 0 0 0.5 0.5 0 0.5 0.5 0.333333 0.666667 0.666667 1 0.666667 1 0.666667 1 0.5 1 0.75 1 0.75 0.75 1 1 0.75 0.5 0.8 1 0.8 0.6 0.4 0.4 0.8 0.6 0.6 0.6 0.4 0.2 0.666667 0.666667 0.5 0.5 0.5 0.166667 0.333333 0.666667 0.5 0.5 0.5 0.333333 0.166667 0.333333 0.571429 0.428571 0.571429 0.428571 0.428571 0.142857 0.428571 0.428571 0.428571 0.285714 0.428571 0.285714 0.428571 0.571429 0.428571 0.571429 0.5 0.25 0.25 0.25 0.25 0.375 0.625 0.625 0.625 0.75 0.375 0.625 0.5 0.375 0.222222 0.444444 0.444444 0.666667 0.444444 0.333333 0.555556 0.444444 0.333333 0.222222 0.7 0.7 0.2 0.6 0.3 0.2 0.3 0.6 0.7 0.818182 0.727273 0.727273 0.363636 0.363636 0.818182 0.727273 0.25 0.666667 0.5 0.416667 0.833333 0.230769 0.538462 0.5 ) +( NaN NaN 1 1 0 0 0 0.5 0.5 0 0.5 0.5 0.333333 0.666667 0.666667 1 0.666667 1 0.666667 1 0.5 1 0.75 1 0.75 0.75 1 1 0.75 0.5 0.8 1 0.8 0.6 0.4 0.4 0.8 0.6 0.6 0.6 0.4 0.2 0.666667 0.666667 0.5 0.5 0.5 0.166667 0.333333 0.666667 0.5 0.5 0.5 0.333333 0.166667 0.333333 0.571429 0.428571 0.571429 0.428571 0.428571 0.142857 0.428571 0.428571 0.428571 0.285714 0.428571 0.285714 0.428571 0.571429 0.428571 0.571429 0.5 0.25 0.25 0.25 0.25 0.375 0.625 0.625 0.625 0.75 0.375 0.625 0.5 0.375 0.222222 0.444444 0.444444 0.666667 0.444444 0.333333 0.555556 0.444444 0.333333 0.222222 0.7 0.7 0.2 0.6 0.3 0.2 0.3 0.6 0.7 0.818182 0.727273 0.727273 0.363636 0.363636 0.818182 0.727273 0.25 0.666667 0.5 0.416667 0.833333 0.230769 0.538462 0.5 ) +( NaN NaN 1 1 0 0 0 0.5 0.5 0 0.5 0.5 0.333333 0.666667 0.666667 1 0.666667 1 0.666667 1 0.5 1 0.75 1 0.75 0.75 1 1 0.75 0.5 0.8 1 0.8 0.6 0.4 0.4 0.8 0.6 0.6 0.6 0.4 0.2 0.666667 0.666667 0.5 0.5 0.5 0.166667 0.333333 0.666667 0.5 0.5 0.5 0.333333 0.166667 0.333333 0.571429 0.428571 0.571429 0.428571 0.428571 0.142857 0.428571 0.428571 0.428571 0.285714 0.428571 0.285714 0.428571 0.571429 0.428571 0.571429 0.5 0.25 0.25 0.25 0.25 0.375 0.625 0.625 0.625 0.75 0.375 0.625 0.5 0.375 0.222222 0.444444 0.444444 0.666667 0.444444 0.333333 0.555556 0.444444 0.333333 0.222222 0.7 0.7 0.2 0.6 0.3 0.2 0.3 0.6 0.7 0.818182 0.727273 0.727273 0.363636 0.363636 0.818182 0.727273 0.25 0.666667 0.5 0.416667 0.833333 0.230769 0.538462 0.5 ) + +k=4 +( NaN NaN 0.428571 0.428571 0.538462 0.538462 0.384615 0.5 0.321429 0.357143 0.541667 0.458333 0.285714 0.5 0.555556 0.410256 0.358974 0.47619 0.606061 0.545455 0.326923 0.464286 0.613636 0.590909 0.428571 0.4375 0.5625 0.5 0.575 0.525 0.383333 0.516667 0.507692 0.58 0.56 0.4 0.385714 0.581818 0.636364 0.457143 0.6 0.511111 0.369048 0.530303 0.590909 0.452381 0.555556 0.555556 0.375 0.430556 0.461538 0.516667 0.55 0.423077 0.583333 0.458333 0.392857 0.428571 0.457143 0.528571 0.428571 0.589286 0.482143 0.265306 0.428571 0.467532 0.387755 0.539683 0.52381 0.416667 0.380952 0.296703 0.397727 0.465909 0.375 0.513889 0.527778 0.375 0.275 0.2625 0.288462 0.330357 0.454545 0.465909 0.348214 0.458333 0.411111 0.307692 0.293651 0.234568 0.240741 0.444444 0.324074 0.358974 0.455556 0.367521 0.341667 0.292308 0.214286 0.341667 0.263636 0.327273 0.264286 0.325 0.246154 0.297521 0.24026 0.272727 0.227273 0.300699 0.207792 0.305195 0.269231 0.277778 0.307692 0.217949 0.282051 0.175824 0.192308 0.219388 ) +( NaN NaN 0.428571 0.428571 0.538462 0.538462 0.384615 0.5 0.321429 0.357143 0.541667 0.458333 0.285714 0.5 0.555556 0.410256 0.358974 0.47619 0.606061 0.545455 0.326923 0.464286 0.613636 0.590909 0.428571 0.4375 0.5625 0.5 0.575 0.525 0.383333 0.516667 0.507692 0.58 0.56 0.4 0.385714 0.581818 0.636364 0.457143 0.6 0.511111 0.369048 0.530303 0.590909 0.452381 0.555556 0.555556 0.375 0.430556 0.461538 0.516667 0.55 0.423077 0.583333 0.458333 0.392857 0.428571 0.457143 0.528571 0.428571 0.589286 0.482143 0.265306 0.428571 0.467532 0.387755 0.539683 0.52381 0.416667 0.380952 0.296703 0.397727 0.465909 0.375 0.513889 0.527778 0.375 0.275 0.2625 0.288462 0.330357 0.454545 0.465909 0.348214 0.458333 0.411111 0.307692 0.293651 0.234568 0.240741 0.444444 0.324074 0.358974 0.455556 0.367521 0.341667 0.292308 0.214286 0.341667 0.263636 0.327273 0.264286 0.325 0.246154 0.297521 0.24026 0.272727 0.227273 0.300699 0.207792 0.305195 0.269231 0.277778 0.307692 0.217949 0.282051 0.175824 0.192308 0.219388 ) + +Zachary karate club: +k=3 +( 0.875 0.555556 1 1 0.666667 0.666667 1 0.25 1 NaN 1 0.75 1 0.5 1 0 0.5 0.8 1 0.75 1 0.5 1 0 0.8 1 0 0 0.111111 0 0.5 0.75 1 1 0.75 0.5 0.5 0.666667 0.5 1 1 0.666667 0.75 0.5 0 0 1 1 1 1 1 1 0 1 1 1 1 0 0.333333 0.5 0.75 0.666667 0.5 0 0.5 0.5 1 1 0.333333 0.5 0.5 0.666667 1 0.666667 0.666667 0.2 0.4 0.909091 ) +( 0.875 0.555556 1 1 0.666667 0.666667 1 0.25 1 NaN 1 0.75 1 0.5 1 0 0.5 0.8 1 0.75 1 0.5 1 0 0.8 1 0 0 0.111111 0 0.5 0.75 1 1 0.75 0.5 0.5 0.666667 0.5 1 1 0.666667 0.75 0.5 0 0 1 1 1 1 1 1 0 1 1 1 1 0 0.333333 0.5 0.75 0.666667 0.5 0 0.5 0.5 1 1 0.333333 0.5 0.5 0.666667 1 0.666667 0.666667 0.2 0.4 0.909091 ) +( 0.875 0.555556 1 1 0.666667 0.666667 1 0.25 1 NaN 1 0.75 1 0.5 1 0 0.5 0.8 1 0.75 1 0.5 1 0 0.8 1 0 0 0.111111 0 0.5 0.75 1 1 0.75 0.5 0.5 0.666667 0.5 1 1 0.666667 0.75 0.5 0 0 1 1 1 1 1 1 0 1 1 1 1 0 0.333333 0.5 0.75 0.666667 0.5 0 0.5 0.5 1 1 0.333333 0.5 0.5 0.666667 1 0.666667 0.666667 0.2 0.4 0.909091 ) + +k=4 +( 0.108333 0.125926 0.186667 0.0666667 0.0666667 0.0666667 0.311111 0.166667 0.0666667 NaN 0.266667 0.283333 0.4 0.3 0.4 0.08 0.222222 0.35 0.5 0.4375 0.75 0.5 0.75 0.208333 0.266667 0.37037 0.222222 0.388889 0.111111 0.555556 0.305556 0.416667 0.666667 0.8 0.5 0.5 0.75 0.333333 0.5 0.333333 0.333333 0.416667 0.272727 0.28125 0.3125 0.15625 0.818182 0.5625 0.818182 0.5625 0.818182 0.5625 0.15625 0.818182 0.5625 0.818182 0.5625 0.375 0.333333 0.295455 0.1875 0.333333 0.25 0.333333 0.1 0.2 0.666667 0.125 0.166667 0.3 0.1875 0.333333 0.229167 0.363636 0.25 0.254545 0.175 0.0681818 ) +( 0.108333 0.125926 0.186667 0.0666667 0.0666667 0.0666667 0.311111 0.166667 0.0666667 NaN 0.266667 0.283333 0.4 0.3 0.4 0.08 0.222222 0.35 0.5 0.4375 0.75 0.5 0.75 0.208333 0.266667 0.37037 0.222222 0.388889 0.111111 0.555556 0.305556 0.416667 0.666667 0.8 0.5 0.5 0.75 0.333333 0.5 0.333333 0.333333 0.416667 0.272727 0.28125 0.3125 0.15625 0.818182 0.5625 0.818182 0.5625 0.818182 0.5625 0.15625 0.818182 0.5625 0.818182 0.5625 0.375 0.333333 0.295455 0.1875 0.333333 0.25 0.333333 0.1 0.2 0.666667 0.125 0.166667 0.3 0.1875 0.333333 0.229167 0.363636 0.25 0.254545 0.175 0.0681818 ) diff --git a/tests/unit/igraph_eccentricity.c b/tests/unit/igraph_eccentricity.c new file mode 100644 index 0000000..550273b --- /dev/null +++ b/tests/unit/igraph_eccentricity.c @@ -0,0 +1,86 @@ +/* + 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 . +*/ + + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_vector_t ecc; + + igraph_vector_init(&ecc, 0); + + printf("Null graph:\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + print_vector(&ecc); + igraph_destroy(&g); + + printf("\nSingleton graph:\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + print_vector(&ecc); + igraph_destroy(&g); + + printf("\nPath with isolated vertex:\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, + 0,2, + -1); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + print_vector(&ecc); + igraph_destroy(&g); + + printf("\nUndirected path graph:\n"); + igraph_ring(&g, 5, IGRAPH_UNDIRECTED, /* mutual */ 0, /* circular */ 0); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + print_vector(&ecc); + igraph_destroy(&g); + + printf("\nDirected path graph:\n"); + igraph_ring(&g, 5, IGRAPH_DIRECTED, /* mutual */ 0, /* circular */ 0); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + print_vector(&ecc); + igraph_destroy(&g); + + printf("\nUndirected star:\n"); + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + print_vector(&ecc); + igraph_destroy(&g); + + printf("\nOut-star:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_ALL); + print_vector(&ecc); + igraph_destroy(&g); + + printf("\nOut-star, IGRAPH_OUT:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_eccentricity(&g, &ecc, igraph_vss_all(), IGRAPH_OUT); + print_vector(&ecc); + igraph_destroy(&g); + + igraph_vector_destroy(&ecc); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eccentricity.out b/tests/unit/igraph_eccentricity.out new file mode 100644 index 0000000..6b37c3d --- /dev/null +++ b/tests/unit/igraph_eccentricity.out @@ -0,0 +1,23 @@ +Null graph: +( ) + +Singleton graph: +( 0 ) + +Path with isolated vertex: +( 1 0 1 ) + +Undirected path graph: +( 4 3 2 3 4 ) + +Directed path graph: +( 4 3 2 1 0 ) + +Undirected star: +( 1 2 2 2 2 2 2 2 2 2 ) + +Out-star: +( 1 2 2 2 2 2 2 2 2 2 ) + +Out-star, IGRAPH_OUT: +( 1 0 0 0 0 0 0 0 0 0 ) diff --git a/tests/unit/igraph_eccentricity_dijkstra.c b/tests/unit/igraph_eccentricity_dijkstra.c new file mode 100644 index 0000000..4adaa87 --- /dev/null +++ b/tests/unit/igraph_eccentricity_dijkstra.c @@ -0,0 +1,117 @@ +/* + 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 . +*/ + + +#include + +#include "test_utilities.h" + +void print_and_destroy_weighted(igraph_t *graph, igraph_neimode_t mode, igraph_vector_t *weights) { + igraph_vector_t ecc; + igraph_vector_init(&ecc, 0); + igraph_eccentricity_dijkstra(graph, weights, &ecc, igraph_vss_all(), mode); + print_vector(&ecc); + igraph_destroy(graph); + igraph_vector_destroy(&ecc); + if (weights) { + igraph_vector_destroy(weights); + } +} + +void print_and_destroy(igraph_t *graph, igraph_neimode_t mode) { + igraph_vector_t weights; + igraph_vector_init(&weights, igraph_ecount(graph)); + for (int i = 0; i < igraph_ecount(graph); i++) { + VECTOR(weights)[i] = 1; + } + print_and_destroy_weighted(graph, mode, &weights); +} + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_vector_t ecc; + igraph_vector_init(&ecc, 0); + + printf("Null graph:\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + print_and_destroy(&g, IGRAPH_OUT); + + printf("\nSingleton graph:\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + print_and_destroy(&g, IGRAPH_OUT); + + printf("\nPath with isolated vertex:\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, + 0,2, + -1); + print_and_destroy(&g, IGRAPH_OUT); + + printf("\nUndirected path graph:\n"); + igraph_ring(&g, 5, IGRAPH_UNDIRECTED, /* mutual */ 0, /* circular */ 0); + print_and_destroy(&g, IGRAPH_OUT); + + printf("\nDirected path graph:\n"); + igraph_ring(&g, 5, IGRAPH_DIRECTED, /* mutual */ 0, /* circular */ 0); + print_and_destroy(&g, IGRAPH_OUT); + + printf("\nUndirected star:\n"); + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + print_and_destroy(&g, IGRAPH_OUT); + + printf("\nOut-star:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + print_and_destroy(&g, IGRAPH_ALL); + + printf("\nOut-star, IGRAPH_OUT:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + print_and_destroy(&g, IGRAPH_OUT); + + printf("\nOut-star with NULL weights:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + print_and_destroy_weighted(&g, IGRAPH_ALL, NULL); + + printf("\nOut-star with weights:\n"); + igraph_vector_init_real(&weights, 9, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9); + igraph_small(&g, 10, IGRAPH_DIRECTED, 0,1, 0,2, 0,3, 0,4, 0,5, 0,6, 0,7, 0,8, 0,9, -1); + print_and_destroy_weighted(&g, IGRAPH_ALL, &weights); + + VERIFY_FINALLY_STACK(); + + printf("\nCheck wrong number of weights error.\n"); + igraph_vector_init(&weights, 1); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + CHECK_ERROR(igraph_eccentricity_dijkstra(&g, &weights, &ecc, igraph_vss_all(), IGRAPH_OUT), IGRAPH_EINVAL); + + printf("Check NaN weight error.\n"); + igraph_destroy(&g); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + VECTOR(weights)[0] = IGRAPH_NAN; + CHECK_ERROR(igraph_eccentricity_dijkstra(&g, &weights, &ecc, igraph_vss_all(), IGRAPH_OUT), IGRAPH_EINVAL); + + printf("Check negative weight error.\n"); + VECTOR(weights)[0] = -1; + CHECK_ERROR(igraph_eccentricity_dijkstra(&g, &weights, &ecc, igraph_vss_all(), IGRAPH_OUT), IGRAPH_EINVAL); + + igraph_vector_destroy(&ecc); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eccentricity_dijkstra.out b/tests/unit/igraph_eccentricity_dijkstra.out new file mode 100644 index 0000000..d1f550c --- /dev/null +++ b/tests/unit/igraph_eccentricity_dijkstra.out @@ -0,0 +1,33 @@ +Null graph: +( ) + +Singleton graph: +( 0 ) + +Path with isolated vertex: +( 1 0 1 ) + +Undirected path graph: +( 4 3 2 3 4 ) + +Directed path graph: +( 4 3 2 1 0 ) + +Undirected star: +( 1 2 2 2 2 2 2 2 2 2 ) + +Out-star: +( 1 2 2 2 2 2 2 2 2 2 ) + +Out-star, IGRAPH_OUT: +( 1 0 0 0 0 0 0 0 0 0 ) + +Out-star with NULL weights: +( 1 2 2 2 2 2 2 2 2 2 ) + +Out-star with weights: +( 0.9 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.7 ) + +Check wrong number of weights error. +Check NaN weight error. +Check negative weight error. diff --git a/tests/unit/igraph_edge_betweenness.c b/tests/unit/igraph_edge_betweenness.c new file mode 100644 index 0000000..0be857f --- /dev/null +++ b/tests/unit/igraph_edge_betweenness.c @@ -0,0 +1,196 @@ +/* -*- 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, see . +*/ + +#include +#include "test_utilities.h" + + +/* https://github.com/igraph/igraph/issues/950 */ +void test_bug950(void) { + /* Testing the case of weighted graphs with multiple alternate + * paths to the same node with slightly different weights due to + * floating point inaccuracies. */ + igraph_t g; + igraph_vector_t eb; + igraph_vector_t weights; + igraph_integer_t from, to; + igraph_integer_t no_of_edges, i; + + igraph_full(&g, 6, 0, 0); + no_of_edges = igraph_ecount(&g); + + igraph_vector_init(&weights, no_of_edges); + + for (i = 0; i < no_of_edges; i++) { + igraph_edge(&g, i, &from, &to); + if((from < 3 && to < 3) || (from >= 3 && to >= 3)) + VECTOR(weights)[i] = 1; + else + VECTOR(weights)[i] = 0.1; + } + + igraph_vector_init(&eb, 0); + + igraph_edge_betweenness(&g, &eb, IGRAPH_UNDIRECTED, &weights); + print_vector(&eb); + + igraph_vector_destroy(&eb); + igraph_vector_destroy(&weights); + igraph_destroy(&g); +} + + +/* https://github.com/igraph/igraph/issues/1050 */ +void test_bug1050(void) { + /* compare cutoff = -1 with cutoff = 0 */ + igraph_t g; + igraph_vector_t eb, eb2; + igraph_vector_t weights; + + igraph_full(&g, 6, 0, 0); + + /* unweighted */ + igraph_vector_init(&eb, igraph_ecount(&g)); + igraph_vector_init(&eb2, igraph_ecount(&g)); + + igraph_edge_betweenness_cutoff(&g, &eb, IGRAPH_UNDIRECTED, /* weights */ 0, /* cutoff */ -1); + igraph_edge_betweenness_cutoff(&g, &eb2, IGRAPH_UNDIRECTED, /* weights */ 0, /* cutoff */ 0); + + /* results must differ */ + IGRAPH_ASSERT(! igraph_vector_all_e(&eb, &eb2)); + + igraph_vector_destroy(&eb); + igraph_vector_destroy(&eb2); + + /* weighted */ + igraph_vector_init(&eb, igraph_ecount(&g)); + igraph_vector_init(&eb2, igraph_ecount(&g)); + + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1); + VECTOR(weights)[0] = 2; + + igraph_edge_betweenness_cutoff(&g, &eb, IGRAPH_UNDIRECTED, &weights, /* cutoff */ -1); + igraph_edge_betweenness_cutoff(&g, &eb2, IGRAPH_UNDIRECTED, &weights, /* cutoff */ 0); + + /* results must differ */ + IGRAPH_ASSERT(! igraph_vector_all_e(&eb, &eb2)); + + igraph_vector_destroy(&eb); + igraph_vector_destroy(&eb2); + igraph_vector_destroy(&weights); + igraph_destroy(&g); +} + + +int main(void) { + igraph_t g; + igraph_vector_t eb, eb2; + igraph_vector_t weights; + + igraph_vector_init(&eb, 0); + + printf("Null graph\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_edge_betweenness(&g, &eb, IGRAPH_UNDIRECTED, NULL); + print_vector(&eb); + igraph_destroy(&g); + + printf("\nEdgeless graph on 3 vertices\n"); + igraph_empty(&g, 3, IGRAPH_DIRECTED); + igraph_edge_betweenness(&g, &eb, IGRAPH_DIRECTED, NULL); + print_vector(&eb); + igraph_destroy(&g); + + igraph_vector_destroy(&eb); + + printf("\nNo cutoff, undirected, unweighted\n"); + igraph_famous(&g, "zachary"); + igraph_vector_init(&eb, 0); + igraph_edge_betweenness(&g, &eb, IGRAPH_UNDIRECTED, /*weights=*/ 0); + print_vector(&eb); + + printf("\nNo cutoff, undirected, unit weighted\n"); + igraph_vector_init(&eb2, 0); + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1.0); + igraph_edge_betweenness(&g, &eb2, IGRAPH_UNDIRECTED, &weights); + print_vector(&eb2); + + /* check that weighted and unweighted calculations give the same result */ + igraph_vector_scale(&eb2, -1); + igraph_vector_add(&eb, &eb2); + igraph_vector_abs(&eb); + IGRAPH_ASSERT(igraph_vector_max(&eb) < 1e-13); + + igraph_vector_destroy(&weights); + igraph_vector_destroy(&eb2); + igraph_vector_destroy(&eb); + igraph_destroy(&g); + + printf("\nSmall directed graph, unweighted\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, + 1,0, 2,0, 0,3, 3,4, 4,5, 5,0, 5,6, + -1); + igraph_vector_init(&eb, 0); + igraph_edge_betweenness(&g, &eb, IGRAPH_DIRECTED, /* weights */ NULL); + print_vector(&eb); + igraph_vector_destroy(&eb); + igraph_destroy(&g); + + printf("\nSmall undirected graph 1, unweighted, cutoff=2\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 1, 4, -1); + igraph_vector_init(&eb, 0); + igraph_edge_betweenness_cutoff(&g, &eb, IGRAPH_UNDIRECTED, /*weights=*/ 0, /*cutoff=*/2); + print_vector(&eb); + igraph_vector_destroy(&eb); + igraph_destroy(&g); + + printf("\nSmall undirected graph 2, unweighted, cutoff=2\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 3, 1, 2, 1, 4, 2, 5, 3, 4, 3, 6, 4, 5, 4, 7, 5, 8, + 6, 7, 7, 8, -1); + igraph_vector_init(&eb, 0); + igraph_edge_betweenness_cutoff(&g, &eb, IGRAPH_UNDIRECTED, /*weights=*/ 0, /*cutoff=*/2); + print_vector(&eb); + igraph_vector_destroy(&eb); + igraph_destroy(&g); + + printf("\nSmall undirected graph 3, unweighted, with multiple and loop edges\n"); + igraph_small(&g, 4, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 1, 2, 1, 1, 2, 3, 3, 0, 3, 3, -1); + igraph_vector_init(&eb, 0); + igraph_edge_betweenness(/* graph= */ &g, + /* res= */ &eb, + /* directed = */ IGRAPH_UNDIRECTED, + /* weights= */ 0); + print_vector(&eb); + igraph_vector_destroy(&eb); + igraph_destroy(&g); + + printf("\nTesting bug 950, tolerances\n"); + test_bug950(); + + printf("\nTesting bug 1050, cutoff values\n"); + test_bug1050(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_edge_betweenness.out b/tests/unit/igraph_edge_betweenness.out new file mode 100644 index 0000000..e07d4c9 --- /dev/null +++ b/tests/unit/igraph_edge_betweenness.out @@ -0,0 +1,28 @@ +Null graph +( ) + +Edgeless graph on 3 vertices +( ) + +No cutoff, undirected, unweighted +( 14.1667 43.6389 11.5 29.3333 43.8333 43.8333 12.8024 41.6484 29.3333 33 26.1 23.7706 22.5095 25.7706 22.5095 71.3929 13.0333 4.33333 4.16429 6.95952 10.4905 8.20952 10.4905 18.1095 12.5833 14.1452 23.1087 12.781 38.7016 17.281 5.14762 4.28095 1.8881 6.9 8.37143 2.66667 1.66667 1.66667 2.66667 16.5 16.5 5.5 17.0778 22.6849 16.6143 38.0492 13.5111 19.4889 13.5111 19.4889 13.5111 19.4889 33.3135 13.5111 19.4889 13.5111 19.4889 11.0944 5.91111 12.5333 18.3278 3.73333 2.36667 10.4667 22.5 23.5944 2.54286 30.4571 17.0976 8.33333 13.781 13.0873 16.7222 9.56667 15.0429 23.2444 29.954 4.61429 ) + +No cutoff, undirected, unit weighted +( 14.1667 43.6389 11.5 29.3333 43.8333 43.8333 12.8024 41.6484 29.3333 33 26.1 23.7706 22.5095 25.7706 22.5095 71.3929 13.0333 4.33333 4.16429 6.95952 10.4905 8.20952 10.4905 18.1095 12.5833 14.1452 23.1087 12.781 38.7016 17.281 5.14762 4.28095 1.8881 6.9 8.37143 2.66667 1.66667 1.66667 2.66667 16.5 16.5 5.5 17.0778 22.6849 16.6143 38.0492 13.5111 19.4889 13.5111 19.4889 13.5111 19.4889 33.3135 13.5111 19.4889 13.5111 19.4889 11.0944 5.91111 12.5333 18.3278 3.73333 2.36667 10.4667 22.5 23.5944 2.54286 30.4571 17.0976 8.33333 13.781 13.0873 16.7222 9.56667 15.0429 23.2444 29.954 4.61429 ) + +Small directed graph, unweighted +( 5 5 15 14 13 6 6 ) + +Small undirected graph 1, unweighted, cutoff=2 +( 4 3 3 2 ) + +Small undirected graph 2, unweighted, cutoff=2 +( 3 3 3 4 3 4 3 4 4 3 3 3 ) + +Small undirected graph 3, unweighted, with multiple and loop edges +( 2 1.16667 1.16667 0 2 1.66667 0 ) + +Testing bug 950, tolerances +( 0 0 2.33333 2.33333 2.33333 0 2.33333 2.33333 2.33333 2.33333 2.33333 2.33333 0 0 0 ) + +Testing bug 1050, cutoff values diff --git a/tests/unit/igraph_edge_betweenness_subset.c b/tests/unit/igraph_edge_betweenness_subset.c new file mode 100644 index 0000000..c4f25e8 --- /dev/null +++ b/tests/unit/igraph_edge_betweenness_subset.c @@ -0,0 +1,315 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + + +/* https://github.com/igraph/igraph/issues/950 */ +void test_bug950_edge(void) { + /* Testing the case of weighted graphs with multiple alternate + * paths to the same node with slightly different weights due to + * floating point inaccuracies. */ + igraph_t g; + igraph_vector_t eb; + igraph_vector_t weights; + igraph_integer_t from, to; + igraph_integer_t no_of_edges, i; + + igraph_full(&g, 6, 0, 0); + no_of_edges = igraph_ecount(&g); + + igraph_vector_init(&weights, no_of_edges); + + for (i = 0; i < no_of_edges; i++) { + igraph_edge(&g, i, &from, &to); + if((from < 3 && to < 3) || (from >= 3 && to >= 3)) + VECTOR(weights)[i] = 1; + else + VECTOR(weights)[i] = 0.1; + } + + printf("\nTesting bug 950 source and targets = vss_all()\n"); + printf("==========================================================\n"); + igraph_vector_init(&eb, 0); + + igraph_edge_betweenness_subset(/* graph= */ &g, + /* res= */ &eb, + /* eids= */ igraph_ess_all(IGRAPH_EDGEORDER_ID), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ igraph_vss_all(), + /* target = */ igraph_vss_all(), + /* weights= */ &weights); + + print_vector(&eb); + igraph_vector_destroy(&eb); + igraph_vector_destroy(&weights); + igraph_destroy(&g); +} + +int main(void) { + igraph_t g; + igraph_es_t es; + igraph_vector_t eb, bet, bet2, weights; + igraph_vector_int_t node_vec, source_vec, target_vec; + igraph_vs_t vs_source, vs_target; + igraph_integer_t i, n; + + /* edge betweenness test */ + + printf("Tree\n"); + printf("==========================================================\n"); + igraph_kary_tree(&g, 11111, 10, IGRAPH_TREE_UNDIRECTED); + + /* We are including the rightmost 200 vertices from the lowermost layer + * (layer 5) of the tree. These have 20 parents in layer 4, 2 grandparents + * in layer 3, and a single grand-grandparent in layer 2. None of the + * shortest paths we consider should therefore pass through the root, the + * first 9 vertices of layer 2, the first 98 vertices of layer 3 or the + * first 980 vertices of layer 4; the edge betweenness of the "upward" + * pointing edges from these vertices should all be zeros. + * + * (Note that due to how igraph constructs tree graphs, edge i is always the + * edge that leads from vertex i+1 towards the root). + * + * Also, the edge betweenness of the edges leading to children of the common + * grand-grandparent in layer 2 is easy to calculate as any shortest path + * going between grand-grandchildren reachable via its left child and via + * its right child should pass through them. This gives us a betweenness of + * 100 * 100 = 10000 for both of these edges. + * + * Similar calculations reveal that the edge betwennesses in subsequent + * layers are 1900 (10 * 190) and 199 (1 * 199) if the edge being considered + * leads to a descendant of the common grand-grandparent in layer 2, and + * zero otherwise. */ + + igraph_vs_range(&vs_source, 10911, 11111); + igraph_vs_range(&vs_target, 10911, 11111); + igraph_vector_init(&bet, 0); + + igraph_edge_betweenness_subset( + /* graph= */ &g, + /* res= */ &bet, + /* eids= */ igraph_ess_all(IGRAPH_EDGEORDER_ID), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ vs_target, + /* weights= */ NULL + ); + + printf("Max edge betweenness: %f\n", igraph_vector_max(&bet)); + + n = igraph_ecount(&g); + for (i = 0; i < n; i++) { + igraph_integer_t expected; + igraph_integer_t vid = i + 1; + + if (vid >= 10911) { + /* edge leading to layer 5, in the subset. There are 199 shortest + * paths that pass through this edge, one path to any _other_ + * node in the selected subset of 200 nodes */ + expected = 199; + } else if (vid >= 1111) { + /* edge leading to layer 5, not in the subset */ + expected = 0; + } else if (vid >= 1091) { + /* edge leading to layer 4, rightmost 20 nodes */ + expected = 1900; + } else if (vid >= 111) { + /* edge leading to layer 4, remaining nodes */ + expected = 0; + } else if (vid >= 109) { + /* edge leading to layer 3, rightmost 2 nodes */ + expected = 10000; + } else if (vid >= 11) { + /* edge leading to layer 3, remaining nodes */ + expected = 0; + } else if (vid == 10) { + /* edge leading to layer 2, rightmost node */ + expected = 0; + } else { + expected = 0; + } + + if (VECTOR(bet)[i] != expected) { + printf( + "Invalid betweenness for edge %" IGRAPH_PRId " (from vertex %" IGRAPH_PRId " towards the " + "root), expected %" IGRAPH_PRId ", got %" IGRAPH_PRId "\n", + i, vid, expected, (igraph_integer_t) VECTOR(bet)[i] + ); + break; + } + } + + igraph_vector_init(&bet2, 0); + igraph_vector_init(&weights, igraph_ecount(&g)); + igraph_vector_fill(&weights, 1.0); + + igraph_edge_betweenness_subset( + /* graph= */ &g, + /* res= */ &bet2, + /* eids= */ igraph_ess_all(IGRAPH_EDGEORDER_ID), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ vs_target, + /* weights= */ &weights + ); + + IGRAPH_ASSERT(igraph_vector_all_e(&bet, &bet2)); + + igraph_vector_destroy(&weights); + igraph_vs_destroy(&vs_source); + igraph_vs_destroy(&vs_target); + igraph_vector_destroy(&bet); + igraph_vector_destroy(&bet2); + igraph_destroy(&g); + + printf("\nZachary karate club, unweighted graph, edge betweenness\n"); + printf("==========================================================\n"); + igraph_famous(&g, "zachary"); + igraph_vector_int_init_range(&source_vec, 0, 33); + igraph_vs_vector(&vs_source, &source_vec); + igraph_vector_int_init_range(&target_vec, 1, 34); + igraph_vs_vector(&vs_target, &target_vec); + igraph_vector_init(&eb, 0); + + igraph_edge_betweenness_subset (/* graph= */ &g, + /* res= */ &eb, + /* eids= */ igraph_ess_all(IGRAPH_EDGEORDER_ID), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ vs_target, + /* weights= */ NULL); + + print_vector(&eb); + igraph_vector_destroy(&eb); + igraph_destroy(&g); + igraph_vs_destroy(&vs_source); + igraph_vector_int_destroy(&source_vec); + igraph_vs_destroy(&vs_target); + igraph_vector_int_destroy(&target_vec); + + printf("\nSmall unweighted graph, edge betweenness\n"); + printf("==========================================================\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 1, 4, -1); + + igraph_vector_init(&eb, 0); + igraph_vector_int_init_range(&node_vec, 0, 4); + igraph_es_vector(&es, &node_vec); + igraph_vector_int_init_range(&target_vec, 1, 5); + igraph_vs_vector(&vs_target, &target_vec); + igraph_edge_betweenness_subset (/* graph= */ &g, + /* res= */ &eb, + /* eids= */ es, + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ igraph_vss_all(), + /* target = */ vs_target, + /* weights= */ NULL); + + print_vector(&eb); + igraph_vector_destroy(&eb); + igraph_es_destroy(&es); + igraph_vector_int_destroy(&node_vec); + igraph_vs_destroy(&vs_target); + igraph_vector_int_destroy(&target_vec); + igraph_destroy(&g); + + printf("\nSmall unweighted graph, edge betweenness with subset of sources\n"); + printf("==========================================================\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0, 1, 0, 3, 1, 2, 1, 4, 2, 5, 3, 4, 3, 6, 4, 5, 4, 7, 5, 8, + 6, 7, 7, 8, -1); + igraph_vector_init(&eb, 0); + igraph_vector_int_init_range(&source_vec, 1, 9); + igraph_vs_vector(&vs_source, &source_vec); + + igraph_edge_betweenness_subset (/* graph= */ &g, + /* res= */ &eb, + /* eids= */ igraph_ess_all(IGRAPH_EDGEORDER_ID), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ igraph_vss_all(), + /* weights= */ NULL); + print_vector(&eb); + igraph_vector_destroy(&eb); + igraph_vs_destroy(&vs_source); + igraph_vector_int_destroy(&source_vec); + igraph_destroy(&g); + + test_bug950_edge(); + + printf("\nEmpty graph\n"); + printf("==========================================================\n"); + igraph_empty(&g, 2, IGRAPH_UNDIRECTED); + + igraph_vector_init(&bet, 0); + igraph_edge_betweenness_subset (/* graph= */ &g, + /* res= */ &bet, + /* eids= */ igraph_ess_all(IGRAPH_EDGEORDER_ID), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ igraph_vss_all(), + /* target = */ igraph_vss_all(), + /* weights= */ NULL); + print_vector(&bet); + igraph_vector_destroy(&bet); + + igraph_destroy(&g); + + printf("\n37x37 grid graph\n"); + printf("==========================================================\n"); + + { + igraph_vector_int_t dims; + + igraph_vector_int_init(&dims, 2); + VECTOR(dims)[0] = 37; + VECTOR(dims)[1] = 37; + + igraph_square_lattice(&g, &dims, 1, IGRAPH_UNDIRECTED, /* mutual */ 0, /* periodic */ 0); + + igraph_vector_init(&bet, 0); + igraph_vector_int_init_range(&target_vec, 0, igraph_vcount(&g)); + igraph_vector_int_remove(&target_vec, 0); + igraph_vs_vector(&vs_target, &target_vec); + igraph_vector_int_init_range(&source_vec, 0, igraph_vcount(&g)); + igraph_vector_int_remove(&source_vec, 0); + igraph_vs_vector(&vs_source, &source_vec); + + igraph_edge_betweenness_subset (/* graph= */ &g, + /* res= */ &bet, + /* eids= */ igraph_ess_all(IGRAPH_EDGEORDER_ID), + /* directed = */ IGRAPH_UNDIRECTED, + /* sources = */ vs_source, + /* target = */ vs_target, + /* weights= */ NULL); + printf("Max edge betweenness: %f\n", igraph_vector_max(&bet)); + + igraph_vector_destroy(&bet); + igraph_destroy(&g); + igraph_vector_int_destroy(&dims); + igraph_vs_destroy(&vs_target); + igraph_vector_int_destroy(&target_vec); + igraph_vs_destroy(&vs_source); + igraph_vector_int_destroy(&source_vec); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_edge_betweenness_subset.out b/tests/unit/igraph_edge_betweenness_subset.out new file mode 100644 index 0000000..b6071e3 --- /dev/null +++ b/tests/unit/igraph_edge_betweenness_subset.out @@ -0,0 +1,27 @@ +Tree +========================================================== +Max edge betweenness: 10000.000000 + +Zachary karate club, unweighted graph, edge betweenness +========================================================== +( 13.4167 41.1825 11 28.3333 42.3333 42.3333 12.1595 38.4849 28.3333 32 25.2 21.5079 21.7238 23.5079 21.7238 67.1738 13.0333 4.33333 4.05714 6.61429 10.2762 7.86429 10.2762 17.5143 12.5833 13.931 22.4341 12.4119 37.9317 16.6619 5.02857 4.1619 1.85238 6.8 7.73571 2.66667 1.66667 1.66667 2.66667 16 16 5.25 16.427 20.3032 15.9952 34.6865 13.2968 18.7032 13.2968 18.7032 13.2968 18.7032 30.7056 13.2968 18.7032 13.2968 18.7032 10.7889 5.85556 12.3667 17.3556 3.73333 2.36667 10.2167 21.75 22.7889 2.54286 29.4571 16.2286 8.08333 13.1619 12.873 15.9365 9.56667 14.1976 22.5937 27.1913 3.99524 ) + +Small unweighted graph, edge betweenness +========================================================== +( 5 3.5 3.5 3.5 ) + +Small unweighted graph, edge betweenness with subset of sources +========================================================== +( 3.33333 3.33333 4.58333 6.58333 5.08333 6.58333 4.58333 6.83333 6.83333 5.08333 5.08333 5.08333 ) + +Testing bug 950 source and targets = vss_all() +========================================================== +( 0 0 2.33333 2.33333 2.33333 0 2.33333 2.33333 2.33333 2.33333 2.33333 2.33333 0 0 0 ) + +Empty graph +========================================================== +( ) + +37x37 grid graph +========================================================== +Max edge betweenness: 18367.263229 diff --git a/tests/unit/igraph_edge_disjoint_paths.c b/tests/unit/igraph_edge_disjoint_paths.c new file mode 100644 index 0000000..0ee1579 --- /dev/null +++ b/tests/unit/igraph_edge_disjoint_paths.c @@ -0,0 +1,52 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_integer_t value; + + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0,1, 0,2, 1,2, 1,3, 2,4, 3,4, 3,5, 4,5, 3,3, -1); + + igraph_edge_disjoint_paths(&g, &value, 0, 5); + IGRAPH_ASSERT(value == 2); + + igraph_edge_disjoint_paths(&g, &value, 0, 3); + IGRAPH_ASSERT(value == 1); + + igraph_edge_disjoint_paths(&g, &value, 3, 0); + IGRAPH_ASSERT(value == 0); + + igraph_edge_disjoint_paths(&g, &value, 3, 5); + IGRAPH_ASSERT(value == 2); + + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_EACH, NULL); + + igraph_edge_disjoint_paths(&g, &value, 4, 3); + IGRAPH_ASSERT(value == 3); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_edges.c b/tests/unit/igraph_edges.c new file mode 100644 index 0000000..73bb331 --- /dev/null +++ b/tests/unit/igraph_edges.c @@ -0,0 +1,53 @@ +/* + 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, see . + */ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g) { + igraph_es_t eids; + igraph_vector_int_t edges; + + igraph_es_all(&eids, IGRAPH_EDGEORDER_ID); + igraph_vector_int_init(&edges, 0); + igraph_edges(g, eids, &edges); + igraph_vector_int_print(&edges); + igraph_destroy(g); + igraph_vector_int_destroy(&edges); +} + +int main(void) { + igraph_t g; + + printf("No edges:\n"); + igraph_small(&g, 0, 1, -1); + print_and_destroy(&g); + + printf("Directed graph with loops and multiple edges:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 4,3, 4,3, -1); + print_and_destroy(&g); + + printf("Undirected graph with loops and multiple edges:\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 4,3, 4,3, -1); + print_and_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_edges.out b/tests/unit/igraph_edges.out new file mode 100644 index 0000000..2aba5dc --- /dev/null +++ b/tests/unit/igraph_edges.out @@ -0,0 +1,6 @@ +No edges: + +Directed graph with loops and multiple edges: +0 1 0 2 1 1 1 3 2 0 2 3 4 3 4 3 +Undirected graph with loops and multiple edges: +0 1 0 2 1 1 1 3 0 2 2 3 3 4 3 4 diff --git a/tests/unit/igraph_eigen_matrix.c b/tests/unit/igraph_eigen_matrix.c new file mode 100644 index 0000000..f59e6d5 --- /dev/null +++ b/tests/unit/igraph_eigen_matrix.c @@ -0,0 +1,134 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_integer_t nodes = 10; + igraph_real_t triplets[] = { 1, 0, 1 / 4.0, 0, 1, 1 / 3.0, + 2, 0, 1 / 4.0, 0, 2, 1 / 3.0, + 3, 0, 1.0, 0, 3, 1 / 3.0, + 4, 1, 1.0, 1, 4, 1 / 4.0, + 5, 1, 1.0, 1, 5, 1 / 4.0, + 6, 1, 1.0, 1, 6, 1 / 4.0, + 7, 2, 1.0, 2, 7, 1 / 4.0, + 8, 2, 1.0, 2, 8, 1 / 4.0, + 9, 2, 1.0, 2, 9, 1 / 4.0 + }; + + igraph_sparsemat_t mat; + igraph_integer_t i, j, n = sizeof(triplets) / sizeof(triplets[0]); + igraph_eigen_which_t which; + igraph_vector_complex_t values, values2; + igraph_matrix_complex_t vectors, vectors2; + igraph_matrix_t mat2; + + igraph_sparsemat_init(&mat, nodes, nodes, n / 3); + for (i = 0; i < n; i += 3) { + igraph_sparsemat_entry(&mat, triplets[i], triplets[i + 1], triplets[i + 2]); + } + + which.pos = IGRAPH_EIGEN_LM; + which.howmany = 1; + + igraph_vector_complex_init(&values, 0); + igraph_matrix_complex_init(&vectors, 0, 0); + + igraph_eigen_matrix(/*matrix=*/ 0, /*sparsemat=*/ &mat, /*fun=*/ 0, + nodes, /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values, &vectors); + + if (IGRAPH_REAL(MATRIX(vectors, 0, 0)) < 0) { + igraph_matrix_complex_scale(&vectors, igraph_complex(-1.0, -0.0 )); + } + + igraph_vector_complex_print(&values); + igraph_matrix_complex_print(&vectors); + + igraph_sparsemat_destroy(&mat); + + /* Calcualate all eigenvalues, using SM and LM and then check that they + are the same, in opposite order. We use a random matrix this time. */ + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_matrix_init(&mat2, nodes, nodes); + for (i = 0; i < nodes; i++) { + for (j = 0; j < nodes; j++) { + MATRIX(mat2, i, j) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + which.pos = IGRAPH_EIGEN_LM; + which.howmany = nodes; + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values, &vectors); + + which.pos = IGRAPH_EIGEN_SM; + which.howmany = nodes; + igraph_vector_complex_init(&values2, 0); + igraph_matrix_complex_init(&vectors2, 0, 0); + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values2, &vectors2); + +#define DUMP() do { \ + printf("Element-wise difference of the following two vectors is too large:\n"); \ + igraph_vector_complex_print(&values); \ + igraph_vector_complex_print(&values2); \ + } while(0) + + for (i = 0; i < nodes; i++) { + igraph_real_t d = + igraph_complex_abs(igraph_complex_sub(VECTOR(values)[i], + VECTOR(values2)[nodes - i - 1])); + if (d > 1e-15) { + DUMP(); + return 2; + } + for (j = 0; j < nodes; j++) { + igraph_real_t d = + igraph_complex_abs(igraph_complex_sub(MATRIX(vectors, j, i), + MATRIX(vectors2, j, + nodes - i - 1))); + if (d > 1e-15) { + DUMP(); + return 3; + } + } + } + + igraph_vector_complex_destroy(&values); + igraph_matrix_complex_destroy(&vectors); + igraph_vector_complex_destroy(&values2); + igraph_matrix_complex_destroy(&vectors2); + + igraph_matrix_destroy(&mat2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eigen_matrix.out b/tests/unit/igraph_eigen_matrix.out new file mode 100644 index 0000000..c24fe02 --- /dev/null +++ b/tests/unit/igraph_eigen_matrix.out @@ -0,0 +1,11 @@ +1+0i +0.316228+0i +0.316228+0i +0.316228+0i +0.316228+0i +0.316228+0i +0.316228+0i +0.316228+0i +0.316228+0i +0.316228+0i +0.316228+0i diff --git a/tests/unit/igraph_eigen_matrix2.c b/tests/unit/igraph_eigen_matrix2.c new file mode 100644 index 0000000..ed6abce --- /dev/null +++ b/tests/unit/igraph_eigen_matrix2.c @@ -0,0 +1,117 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +#define DUMP() do { \ + igraph_vector_complex_print(&values); \ + igraph_vector_complex_print(&values2); \ + } while(0) + +int main(void) { + + const int nodes = 10; + igraph_matrix_t mat2; + igraph_vector_complex_t values, values2; + igraph_matrix_complex_t vectors, vectors2; + igraph_eigen_which_t which; + int i; + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_matrix_init(&mat2, nodes, nodes); + for (i = 0; i < nodes; i++) { + int j; + for (j = 0; j < nodes; j++) { + MATRIX(mat2, i, j) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + /* Test LR, a single eigenvalue first */ + + igraph_vector_complex_init(&values, 0); + igraph_matrix_complex_init(&vectors, 0, 0); + which.pos = IGRAPH_EIGEN_LR; + which.howmany = 1; + + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values, &vectors); + + igraph_vector_complex_print(&values); + igraph_matrix_complex_print(&vectors); + + igraph_vector_complex_destroy(&values); + igraph_matrix_complex_destroy(&vectors); + + /* LR, and SR, all eigenvalues */ + + igraph_vector_complex_init(&values, 0); + igraph_matrix_complex_init(&vectors, 0, 0); + which.pos = IGRAPH_EIGEN_LR; + which.howmany = nodes; + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values, &vectors); + + igraph_vector_complex_init(&values2, 0); + igraph_matrix_complex_init(&vectors2, 0, 0); + which.pos = IGRAPH_EIGEN_SR; + which.howmany = nodes; + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values2, &vectors2); + + for (i = 0; i < nodes; i++) { + int j; + igraph_real_t d = + igraph_complex_abs(igraph_complex_sub(VECTOR(values)[i], + VECTOR(values2)[nodes - i - 1])); + if (d > 1e-15) { + DUMP(); + return 2; + } + for (j = 0; j < nodes; j++) { + igraph_real_t d = + igraph_complex_abs(igraph_complex_sub(MATRIX(vectors, j, i), + MATRIX(vectors2, j, + nodes - i - 1))); + if (d > 1e-15) { + DUMP(); + return 3; + } + } + } + + igraph_vector_complex_destroy(&values); + igraph_matrix_complex_destroy(&vectors); + igraph_vector_complex_destroy(&values2); + igraph_matrix_complex_destroy(&vectors2); + + igraph_matrix_destroy(&mat2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eigen_matrix2.out b/tests/unit/igraph_eigen_matrix2.out new file mode 100644 index 0000000..ef0de5a --- /dev/null +++ b/tests/unit/igraph_eigen_matrix2.out @@ -0,0 +1,11 @@ +59.7723+0i +0.326586+0i +0.361886+0i +0.298836+0i +0.288278+0i +0.239871+0i + 0.35571+0i +0.336723+0i +0.407276+0i +0.224727+0i +0.275946+0i diff --git a/tests/unit/igraph_eigen_matrix3.c b/tests/unit/igraph_eigen_matrix3.c new file mode 100644 index 0000000..e10bcf9 --- /dev/null +++ b/tests/unit/igraph_eigen_matrix3.c @@ -0,0 +1,98 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +#define DUMP() do { \ + igraph_vector_complex_print(&values); \ + igraph_vector_complex_print(&values2); \ + } while(0) + +int main(void) { + + const int nodes = 10, skip = 3; + igraph_matrix_t mat2; + igraph_vector_complex_t values, values2; + igraph_matrix_complex_t vectors, vectors2; + igraph_eigen_which_t which; + int i; + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_matrix_init(&mat2, nodes, nodes); + for (i = 0; i < nodes; i++) { + int j; + for (j = 0; j < nodes; j++) { + MATRIX(mat2, i, j) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + which.pos = IGRAPH_EIGEN_SELECT; + which.il = skip; + which.iu = nodes - skip; + + igraph_vector_complex_init(&values, 0); + igraph_matrix_complex_init(&vectors, 0, 0); + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values, &vectors); + + which.pos = IGRAPH_EIGEN_ALL; + + igraph_vector_complex_init(&values2, 0); + igraph_matrix_complex_init(&vectors2, 0, 0); + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values2, &vectors2); + + for (i = 0; i < nodes - skip * 2 + 1; i++) { + int j; + igraph_real_t d = + igraph_complex_abs(igraph_complex_sub(VECTOR(values)[i], + VECTOR(values2)[i + skip - 1])); + if (d > 1e-15) { + DUMP(); + return 2; + } + for (j = 0; j < nodes; j++) { + igraph_real_t d = + igraph_complex_abs(igraph_complex_sub(MATRIX(vectors, j, i), + MATRIX(vectors2, j, i + skip - 1))); + if (d > 1e-15) { + DUMP(); + return 3; + } + } + } + + igraph_vector_complex_destroy(&values); + igraph_matrix_complex_destroy(&vectors); + igraph_vector_complex_destroy(&values2); + igraph_matrix_complex_destroy(&vectors2); + igraph_matrix_destroy(&mat2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eigen_matrix3.out b/tests/unit/igraph_eigen_matrix3.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/igraph_eigen_matrix4.c b/tests/unit/igraph_eigen_matrix4.c new file mode 100644 index 0000000..10bae43 --- /dev/null +++ b/tests/unit/igraph_eigen_matrix4.c @@ -0,0 +1,100 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +#define DUMP() do { \ + igraph_vector_complex_print(&values); \ + igraph_vector_complex_print(&values2); \ + } while(0) + +int main(void) { + + const int nodes = 10; + igraph_matrix_t mat2; + igraph_vector_complex_t values, values2; + igraph_matrix_complex_t vectors, vectors2; + igraph_eigen_which_t which; + int i; + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_matrix_init(&mat2, nodes, nodes); + for (i = 0; i < nodes; i++) { + int j; + for (j = 0; j < nodes; j++) { + MATRIX(mat2, i, j) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + igraph_vector_complex_init(&values, 0); + igraph_matrix_complex_init(&vectors, 0, 0); + which.pos = IGRAPH_EIGEN_LI; + which.howmany = nodes; + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values, &vectors); + + igraph_vector_complex_init(&values2, 0); + igraph_matrix_complex_init(&vectors2, 0, 0); + which.pos = IGRAPH_EIGEN_SI; + which.howmany = nodes; + igraph_eigen_matrix(&mat2, /*sparsemat=*/ 0, /*fun=*/ 0, nodes, + /*extra=*/ 0, IGRAPH_EIGEN_LAPACK, &which, + /*options=*/ 0, /*storage=*/ 0, &values2, &vectors2); + + igraph_vector_complex_print(&values); + igraph_vector_complex_print(&values2); + + for (i = 0; i < nodes; i++) { + int j; + igraph_real_t d = + igraph_complex_abs(igraph_complex_sub(VECTOR(values)[i], + VECTOR(values2)[nodes - i - 1])); + if (d > 1e-15) { + DUMP(); + return 2; + } + for (j = 0; j < nodes; j++) { + igraph_real_t d = + igraph_complex_abs(igraph_complex_sub(MATRIX(vectors, j, i), + MATRIX(vectors2, j, + nodes - i - 1))); + if (d > 1e-15) { + DUMP(); + return 3; + } + } + } + + igraph_vector_complex_destroy(&values); + igraph_matrix_complex_destroy(&vectors); + igraph_vector_complex_destroy(&values2); + igraph_matrix_complex_destroy(&vectors2); + igraph_matrix_destroy(&mat2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eigen_matrix4.out b/tests/unit/igraph_eigen_matrix4.out new file mode 100644 index 0000000..35bb1db --- /dev/null +++ b/tests/unit/igraph_eigen_matrix4.out @@ -0,0 +1,2 @@ +-6.09451+6.92801i 1.3402+5.63701i 4.37096+2.48743i 59.7723+0i 1.62036+0i -1.50609+0i -5.11985+0i 4.37096-2.48743i 1.3402-5.63701i -6.09451-6.92801i +-6.09451-6.92801i 1.3402-5.63701i 4.37096-2.48743i -5.11985+0i -1.50609+0i 1.62036+0i 59.7723+0i 4.37096+2.48743i 1.3402+5.63701i -6.09451+6.92801i diff --git a/tests/unit/igraph_eigen_matrix_symmetric.c b/tests/unit/igraph_eigen_matrix_symmetric.c new file mode 100644 index 0000000..c28b5b9 --- /dev/null +++ b/tests/unit/igraph_eigen_matrix_symmetric.c @@ -0,0 +1,127 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +#define DIM 10 + +int check_ev(const igraph_matrix_t *A, const igraph_vector_t *values, + const igraph_matrix_t *vectors) { + + int i, n = igraph_matrix_nrow(A); + int ne = igraph_matrix_ncol(vectors); + igraph_vector_t v, lhs, rhs; + + if (ne != igraph_vector_size(values)) { + printf("'values' and 'vectors' sizes do not match\n"); + exit(1); + } + + igraph_vector_init(&lhs, n); + igraph_vector_init(&rhs, n); + + for (i = 0; i < ne; i++) { + igraph_vector_view(&v, &MATRIX(*vectors, 0, i), n); + igraph_blas_dgemv(/*transpose=*/ 0, /*alpha=*/ 1, A, &v, + /*beta=*/ 0, &lhs); + igraph_vector_update(&rhs, &v); + igraph_vector_scale(&rhs, VECTOR(*values)[i]); + if (igraph_vector_maxdifference(&lhs, &rhs) > 1e-10) { + printf("LHS: "); + igraph_vector_print(&lhs); + printf("RHS: "); + igraph_vector_print(&rhs); + exit(2); + } + } + + igraph_vector_destroy(&rhs); + igraph_vector_destroy(&lhs); + + return 0; +} + +int main(void) { + + igraph_matrix_t A; + igraph_vector_t values; + igraph_matrix_t vectors; + int i, j; + igraph_eigen_which_t which; + + igraph_rng_seed(igraph_rng_default(), 42 * 42); + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&vectors, 0, 0); + igraph_vector_init(&values, 0); + + /* All eigenvalues and eigenvectors */ + + for (i = 0; i < DIM; i++) { + for (j = i; j < DIM; j++) { + MATRIX(A, i, j) = MATRIX(A, j, i) = + igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + which.pos = IGRAPH_EIGEN_LM; + which.howmany = 5; + igraph_eigen_matrix_symmetric(&A, /*sA=*/ 0, /*fun=*/ 0, DIM, /*extra=*/ 0, + IGRAPH_EIGEN_LAPACK, &which, /*options=*/ 0, + /*storage=*/ 0, &values, &vectors); + igraph_vector_print(&values); + check_ev(&A, &values, &vectors); + + which.howmany = 8; + igraph_eigen_matrix_symmetric(&A, /*sA=*/ 0, /*fun=*/ 0, DIM, /*extra=*/ 0, + IGRAPH_EIGEN_LAPACK, &which, /*options=*/ 0, + /*storage=*/ 0, &values, &vectors); + igraph_vector_print(&values); + check_ev(&A, &values, &vectors); + + which.pos = IGRAPH_EIGEN_BE; + which.howmany = 5; + igraph_eigen_matrix_symmetric(&A, /*sA=*/ 0, /*fun=*/ 0, DIM, /*extra=*/ 0, + IGRAPH_EIGEN_LAPACK, &which, /*options=*/ 0, + /*storage=*/ 0, &values, &vectors); + igraph_vector_print(&values); + check_ev(&A, &values, &vectors); + + which.pos = IGRAPH_EIGEN_SM; + which.howmany = 5; + igraph_eigen_matrix_symmetric(&A, /*sA=*/ 0, /*fun=*/ 0, DIM, /*extra=*/ 0, + IGRAPH_EIGEN_LAPACK, &which, /*options=*/ 0, + /*storage=*/ 0, &values, &vectors); + igraph_vector_print(&values); + check_ev(&A, &values, &vectors); + + igraph_vector_destroy(&values); + igraph_matrix_destroy(&vectors); + igraph_matrix_destroy(&A); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eigen_matrix_symmetric.out b/tests/unit/igraph_eigen_matrix_symmetric.out new file mode 100644 index 0000000..7e1297d --- /dev/null +++ b/tests/unit/igraph_eigen_matrix_symmetric.out @@ -0,0 +1,4 @@ +54.2836 -14.0379 11.141 -10.6996 -8.73724 +54.2836 -14.0379 11.141 -10.6996 -8.73724 8.62656 7.57286 4.18242 +54.2836 -14.0379 11.141 -10.6996 8.62656 +-1.76375 2.43199 4.18242 7.57286 8.62656 diff --git a/tests/unit/igraph_eigen_matrix_symmetric_arpack.c b/tests/unit/igraph_eigen_matrix_symmetric_arpack.c new file mode 100644 index 0000000..0e8b8ac --- /dev/null +++ b/tests/unit/igraph_eigen_matrix_symmetric_arpack.c @@ -0,0 +1,128 @@ +/* -*- 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 + +#include "test_utilities.h" + +#define DIM 10 + +int check_ev(const igraph_matrix_t *A, const igraph_vector_t *values, + const igraph_matrix_t *vectors, int err_off) { + + int i, n = igraph_matrix_nrow(A); + int ne = igraph_matrix_ncol(vectors); + igraph_vector_t v, lhs, rhs; + + if (ne != igraph_vector_size(values)) { + printf("'values' and 'vectors' sizes do not match\n"); + exit(err_off + 1); + } + + igraph_vector_init(&lhs, n); + igraph_vector_init(&rhs, n); + + for (i = 0; i < ne; i++) { + igraph_vector_view(&v, &MATRIX(*vectors, 0, i), n); + igraph_blas_dgemv(/*transpose=*/ 0, /*alpha=*/ 1, A, &v, + /*beta=*/ 0, &lhs); + igraph_vector_update(&rhs, &v); + igraph_vector_scale(&rhs, VECTOR(*values)[i]); + if (igraph_vector_maxdifference(&lhs, &rhs) > 1e-10) { + printf("LHS %i: ", i); + igraph_vector_print(&lhs); + printf("RHS %i: ", i); + igraph_vector_print(&rhs); + exit(err_off + 2); + } + } + + igraph_vector_destroy(&rhs); + igraph_vector_destroy(&lhs); + + return 0; +} + +int main(void) { + + igraph_matrix_t A; + igraph_vector_t values; + igraph_matrix_t vectors; + int i, j; + igraph_eigen_which_t which; + igraph_arpack_options_t options; + + igraph_rng_seed(igraph_rng_default(), 42 * 42); + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&vectors, 0, 0); + igraph_vector_init(&values, 0); + + igraph_arpack_options_init(&options); + + for (i = 0; i < DIM; i++) { + for (j = i; j < DIM; j++) { + MATRIX(A, i, j) = MATRIX(A, j, i) = + igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + which.pos = IGRAPH_EIGEN_LM; + which.howmany = 2; + igraph_eigen_matrix_symmetric(&A, /*sA=*/ 0, /*fun=*/ 0, DIM, /*extra=*/ 0, + IGRAPH_EIGEN_ARPACK, &which, &options, + /*storage=*/ 0, &values, &vectors); + igraph_vector_print(&values); + check_ev(&A, &values, &vectors, 0); + + which.howmany = 8; + igraph_eigen_matrix_symmetric(&A, /*sA=*/ 0, /*fun=*/ 0, DIM, /*extra=*/ 0, + IGRAPH_EIGEN_ARPACK, &which, &options, + /*storage=*/ 0, &values, &vectors); + igraph_vector_print(&values); + check_ev(&A, &values, &vectors, 10); + + which.pos = IGRAPH_EIGEN_BE; + which.howmany = 5; + igraph_eigen_matrix_symmetric(&A, /*sA=*/ 0, /*fun=*/ 0, DIM, /*extra=*/ 0, + IGRAPH_EIGEN_ARPACK, &which, /*options=*/ 0, + /*storage=*/ 0, &values, &vectors); + igraph_vector_print(&values); + check_ev(&A, &values, &vectors, 20); + + which.pos = IGRAPH_EIGEN_SM; + which.howmany = 5; + igraph_eigen_matrix_symmetric(&A, /*sA=*/ 0, /*fun=*/ 0, DIM, /*extra=*/ 0, + IGRAPH_EIGEN_ARPACK, &which, /*options=*/ 0, + /*storage=*/ 0, &values, &vectors); + igraph_vector_print(&values); + check_ev(&A, &values, &vectors, 30); + + igraph_vector_destroy(&values); + igraph_matrix_destroy(&vectors); + igraph_matrix_destroy(&A); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eigen_matrix_symmetric_arpack.out b/tests/unit/igraph_eigen_matrix_symmetric_arpack.out new file mode 100644 index 0000000..82a9ce4 --- /dev/null +++ b/tests/unit/igraph_eigen_matrix_symmetric_arpack.out @@ -0,0 +1,4 @@ +54.2836 -14.0379 +54.2836 -14.0379 11.141 -10.6996 -8.73724 8.62656 7.57286 4.18242 +54.2836 -14.0379 11.141 -10.6996 8.62656 +-1.76375 2.43199 4.18242 7.57286 8.62656 diff --git a/tests/unit/igraph_eigenvector_centrality.c b/tests/unit/igraph_eigenvector_centrality.c new file mode 100644 index 0000000..b45c6f7 --- /dev/null +++ b/tests/unit/igraph_eigenvector_centrality.c @@ -0,0 +1,119 @@ +/* -*- 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, see . +*/ + +#include "igraph.h" +#include "test_utilities.h" + +#include + +void print_and_destroy(igraph_t *g, igraph_vector_t *weights) +{ + igraph_vector_t v; + igraph_real_t value; + + igraph_vector_init(&v, 0); + igraph_eigenvector_centrality(g, &v, &value, /* directed */ true, + /* scale */ true, weights, + /* options */ NULL); + + printf("Eigenvalue: %g\n", value); + printf("Eigenvector:\n"); + igraph_vector_print(&v); + printf("\n"); + + igraph_destroy(g); + igraph_vector_destroy(&v); +} + +int main(void) { + + igraph_t g; + igraph_vector_t weights; + + printf("Undirected graph with no vertices:\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, -1); + print_and_destroy(&g, NULL); + + printf("Directed graph with no vertices:\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, -1); + print_and_destroy(&g, NULL); + + printf("Undirected graph with no edges:\n"); + igraph_small(&g, 5, IGRAPH_UNDIRECTED, -1); + print_and_destroy(&g, NULL); + + printf("Directed graph with no edges:\n"); + igraph_small(&g, 5, IGRAPH_DIRECTED, -1); + print_and_destroy(&g, NULL); + + printf("Undirected full graph:\n"); + igraph_full(&g, 5, IGRAPH_UNDIRECTED, /* loops */ false); + print_and_destroy(&g, NULL); + + printf("Directed full graph:\n"); + igraph_full(&g, 5, IGRAPH_DIRECTED, /* loops */ false); + print_and_destroy(&g, NULL); + + printf("Undirected full graph with weights:\n"); + igraph_vector_init(&weights, 10); + igraph_vector_fill(&weights, 1); + igraph_full(&g, 5, IGRAPH_UNDIRECTED, /* loops */ false); + print_and_destroy(&g, &weights); + igraph_vector_destroy(&weights); + + printf("Directed full graph with weights:\n"); + igraph_full(&g, 5, IGRAPH_DIRECTED, /* loops */ false); + igraph_vector_init(&weights, 20); + igraph_vector_fill(&weights, 1); + print_and_destroy(&g, &weights); + igraph_vector_destroy(&weights); + + printf("Undirected star graph with weights:\n"); + igraph_vector_init(&weights, 4); + igraph_vector_fill(&weights, 1); + igraph_star(&g, 5, IGRAPH_STAR_UNDIRECTED, 0); + print_and_destroy(&g, &weights); + igraph_vector_destroy(&weights); + + printf("Directed star graph with weights:\n"); + igraph_star(&g, 5, IGRAPH_STAR_OUT, 0); + igraph_vector_init(&weights, 4); + igraph_vector_fill(&weights, 1); + print_and_destroy(&g, &weights); + igraph_vector_destroy(&weights); + + VERIFY_FINALLY_STACK(); + + printf("Check handling of wrong number of weights.\n"); + igraph_vector_init(&weights, 2); + igraph_vector_fill(&weights, 1); + igraph_full(&g, 5, IGRAPH_DIRECTED, /* loops */ false); + CHECK_ERROR(igraph_eigenvector_centrality(&g, NULL, NULL, /* directed */ true, + /* scale */ true, &weights, + /* options */ NULL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_eigenvector_centrality(&g, NULL, NULL, /* directed */ false, + /* scale */ true, &weights, + /* options */ NULL), IGRAPH_EINVAL); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eigenvector_centrality.out b/tests/unit/igraph_eigenvector_centrality.out new file mode 100644 index 0000000..3fa59f8 --- /dev/null +++ b/tests/unit/igraph_eigenvector_centrality.out @@ -0,0 +1,51 @@ +Undirected graph with no vertices: +Eigenvalue: 0 +Eigenvector: + + +Directed graph with no vertices: +Eigenvalue: 0 +Eigenvector: + + +Undirected graph with no edges: +Eigenvalue: 0 +Eigenvector: +1 1 1 1 1 + +Directed graph with no edges: +Eigenvalue: 0 +Eigenvector: +1 1 1 1 1 + +Undirected full graph: +Eigenvalue: 4 +Eigenvector: +1 1 1 1 1 + +Directed full graph: +Eigenvalue: 4 +Eigenvector: +1 1 1 1 1 + +Undirected full graph with weights: +Eigenvalue: 4 +Eigenvector: +1 1 1 1 1 + +Directed full graph with weights: +Eigenvalue: 4 +Eigenvector: +1 1 1 1 1 + +Undirected star graph with weights: +Eigenvalue: 2 +Eigenvector: +1 0.5 0.5 0.5 0.5 + +Directed star graph with weights: +Eigenvalue: 0 +Eigenvector: +0 0 0 0 0 + +Check handling of wrong number of weights. diff --git a/tests/unit/igraph_empty.c b/tests/unit/igraph_empty.c new file mode 100644 index 0000000..767cf26 --- /dev/null +++ b/tests/unit/igraph_empty.c @@ -0,0 +1,61 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + + /* empty directed graph, zero vertices */ + igraph_empty(&g, 0, 1); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + igraph_destroy(&g); + + /* empty undirected graph, zero vertices */ + igraph_empty(&g, 0, 0); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + igraph_destroy(&g); + + /* empty directed graph, 20 vertices */ + igraph_empty(&g, 20, 1); + IGRAPH_ASSERT(igraph_vcount(&g) == 20); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + igraph_destroy(&g); + + /* empty undirected graph, 30 vertices */ + igraph_empty(&g, 30, 0); + IGRAPH_ASSERT(igraph_vcount(&g) == 30); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + igraph_destroy(&g); + + /* error: negative number of vertices */ + CHECK_ERROR(igraph_empty(&g, -1, 0), IGRAPH_EINVAL); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_es_all_between.c b/tests/unit/igraph_es_all_between.c new file mode 100644 index 0000000..eeada7c --- /dev/null +++ b/tests/unit/igraph_es_all_between.c @@ -0,0 +1,202 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +igraph_error_t test_directed(void) { + igraph_t g; + igraph_es_t es; + igraph_eit_t eit; + igraph_integer_t size; + + IGRAPH_CHECK(igraph_small(&g, 5, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 1, 2, 3, 4, 1, 2, 4, 0, -1)); + IGRAPH_CHECK(igraph_add_edge(&g, 1, 2)); + IGRAPH_CHECK(igraph_add_edge(&g, 1, 2)); + IGRAPH_CHECK(igraph_add_edge(&g, 1, 2)); + IGRAPH_CHECK(igraph_add_edge(&g, 3, 4)); + IGRAPH_CHECK(igraph_add_edge(&g, 1, 2)); + IGRAPH_CHECK(igraph_add_edge(&g, 1, 2)); + + /* single edge between 0 and 1 */ + igraph_es_all_between(&es, 0, 1, IGRAPH_DIRECTED); + IGRAPH_CHECK(igraph_eit_create(&g, es, &eit)); + IGRAPH_CHECK(igraph_es_size(&g, &es, &size)); + IGRAPH_ASSERT(size == 1); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + IGRAPH_CHECK(igraph_edge(&g, edge, &from, &to)); + IGRAPH_ASSERT(from == 0); + IGRAPH_ASSERT(to == 1); + IGRAPH_EIT_NEXT(eit); + size--; + } + IGRAPH_ASSERT(size == 0); + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + + /* no edge between 3 and 0 */ + igraph_es_all_between(&es, 3, 0, IGRAPH_DIRECTED); + IGRAPH_CHECK(igraph_eit_create(&g, es, &eit)); + IGRAPH_CHECK(igraph_es_size(&g, &es, &size)); + IGRAPH_ASSERT(size == 0); + IGRAPH_ASSERT(IGRAPH_EIT_END(eit)); + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + + /* many edges between 1 and 2 */ + igraph_es_all_between(&es, 1, 2, IGRAPH_DIRECTED); + IGRAPH_CHECK(igraph_eit_create(&g, es, &eit)); + IGRAPH_CHECK(igraph_es_size(&g, &es, &size)); + IGRAPH_ASSERT(size == 8); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + IGRAPH_CHECK(igraph_edge(&g, edge, &from, &to)); + IGRAPH_ASSERT(from == 1); + IGRAPH_ASSERT(to == 2); + IGRAPH_EIT_NEXT(eit); + size--; + } + IGRAPH_ASSERT(size == 0); + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + + /* two edges between 3 and 4, using IGRAPH_UNDIRECTED */ + igraph_es_all_between(&es, 4, 3, IGRAPH_UNDIRECTED); + IGRAPH_CHECK(igraph_eit_create(&g, es, &eit)); + IGRAPH_CHECK(igraph_es_size(&g, &es, &size)); + IGRAPH_ASSERT(size == 2); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + IGRAPH_CHECK(igraph_edge(&g, edge, &from, &to)); + IGRAPH_ASSERT(from == 3); + IGRAPH_ASSERT(to == 4); + IGRAPH_EIT_NEXT(eit); + size--; + } + IGRAPH_ASSERT(size == 0); + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return IGRAPH_SUCCESS; +} + +igraph_error_t test_undirected(void) { + igraph_t g; + igraph_es_t es; + igraph_eit_t eit; + igraph_integer_t size; + + IGRAPH_CHECK(igraph_small(&g, 5, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 1, 2, 3, 4, 1, 2, 4, 0, -1)); + IGRAPH_CHECK(igraph_add_edge(&g, 1, 2)); + IGRAPH_CHECK(igraph_add_edge(&g, 2, 1)); + IGRAPH_CHECK(igraph_add_edge(&g, 1, 2)); + IGRAPH_CHECK(igraph_add_edge(&g, 3, 4)); + IGRAPH_CHECK(igraph_add_edge(&g, 2, 1)); + IGRAPH_CHECK(igraph_add_edge(&g, 1, 2)); + + /* single edge between 0 and 1 */ + igraph_es_all_between(&es, 0, 1, IGRAPH_UNDIRECTED); + IGRAPH_CHECK(igraph_eit_create(&g, es, &eit)); + IGRAPH_CHECK(igraph_es_size(&g, &es, &size)); + IGRAPH_ASSERT(size == 1); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + IGRAPH_CHECK(igraph_edge(&g, edge, &from, &to)); + IGRAPH_ASSERT(from == 0); + IGRAPH_ASSERT(to == 1); + IGRAPH_EIT_NEXT(eit); + size--; + } + IGRAPH_ASSERT(size == 0); + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + + /* no edge between 3 and 0 */ + igraph_es_all_between(&es, 3, 0, IGRAPH_UNDIRECTED); + IGRAPH_CHECK(igraph_eit_create(&g, es, &eit)); + IGRAPH_CHECK(igraph_es_size(&g, &es, &size)); + IGRAPH_ASSERT(size == 0); + IGRAPH_ASSERT(IGRAPH_EIT_END(eit)); + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + + /* many edges between 1 and 2 */ + igraph_es_all_between(&es, 1, 2, IGRAPH_UNDIRECTED); + IGRAPH_CHECK(igraph_eit_create(&g, es, &eit)); + IGRAPH_CHECK(igraph_es_size(&g, &es, &size)); + IGRAPH_ASSERT(size == 8); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + IGRAPH_CHECK(igraph_edge(&g, edge, &from, &to)); + IGRAPH_ASSERT(from == 1); + IGRAPH_ASSERT(to == 2); + IGRAPH_EIT_NEXT(eit); + size--; + } + IGRAPH_ASSERT(size == 0); + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + + /* two edges between 3 and 4 */ + igraph_es_all_between(&es, 4, 3, IGRAPH_UNDIRECTED); + IGRAPH_CHECK(igraph_eit_create(&g, es, &eit)); + IGRAPH_CHECK(igraph_es_size(&g, &es, &size)); + IGRAPH_ASSERT(size == 2); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + IGRAPH_CHECK(igraph_edge(&g, edge, &from, &to)); + IGRAPH_ASSERT(from == 3); + IGRAPH_ASSERT(to == 4); + IGRAPH_EIT_NEXT(eit); + size--; + } + IGRAPH_ASSERT(size == 0); + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return IGRAPH_SUCCESS; +} + +int main(void) { + IGRAPH_ASSERT(test_undirected() == IGRAPH_SUCCESS); + IGRAPH_ASSERT(test_directed() == IGRAPH_SUCCESS); + + return 0; +} diff --git a/tests/unit/igraph_es_path.c b/tests/unit/igraph_es_path.c new file mode 100644 index 0000000..d036124 --- /dev/null +++ b/tests/unit/igraph_es_path.c @@ -0,0 +1,75 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_es_t es; + igraph_eit_t eit; + igraph_integer_t size; + + /* DIRECTED */ + + igraph_ring(&g, 10, IGRAPH_DIRECTED, 0, 1); + igraph_es_path_small(&es, IGRAPH_DIRECTED, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, -1); + igraph_eit_create(&g, es, &eit); + igraph_es_size(&g, &es, &size); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + igraph_edge(&g, edge, &from, &to); + IGRAPH_EIT_NEXT(eit); + size--; + } + IGRAPH_ASSERT(size == 0); + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + igraph_destroy(&g); + + /* UNDIRECTED */ + + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, 0, 1); + igraph_es_path_small(&es, IGRAPH_DIRECTED, + 0, 1, 2, 3, 4, 3, 2, 3, 4, 5, 6, 5, 4, 5, 6, 7, 8, 9, 0, 1, 0, 9, -1); + igraph_eit_create(&g, es, &eit); + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from, to; + igraph_edge(&g, edge, &from, &to); + IGRAPH_EIT_NEXT(eit); + } + + igraph_eit_destroy(&eit); + igraph_es_destroy(&es); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_establishment_game.c b/tests/unit/igraph_establishment_game.c new file mode 100644 index 0000000..9203cbe --- /dev/null +++ b/tests/unit/igraph_establishment_game.c @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void init_vm(igraph_vector_t *type_dist, + int v0, int v1, + igraph_matrix_t *pref_matrix, + int m00, int m10, int m01, int m11) { + igraph_vector_init_int_end(type_dist, -1, v0, v1, -1); + igraph_matrix_init(pref_matrix, 2, 2); + MATRIX(*pref_matrix, 0, 0) = m00; + MATRIX(*pref_matrix, 1, 0) = m10; + MATRIX(*pref_matrix, 0, 1) = m01; + MATRIX(*pref_matrix, 1, 1) = m11; +} + +#define DESTROY_GVM() do { \ + igraph_destroy(&g); \ + igraph_vector_destroy(&type_dist); \ + igraph_matrix_destroy(&pref_matrix); \ + } while(0) + +int main(void) { + igraph_t g; + igraph_vector_t type_dist; + igraph_vector_int_t node_type_vec; + igraph_matrix_t pref_matrix; + igraph_bool_t bipartite; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_int_init(&node_type_vec, 0); + /*No vertices*/ + init_vm(&type_dist, 1, 0, &pref_matrix, 0, 0, 0, 1); + IGRAPH_ASSERT(igraph_establishment_game(&g, /*nodes*/ 0, /*types*/ 2, /*edges_per_step*/ 0, &type_dist, &pref_matrix, /*directed*/ 0, /*node_type_vec*/ NULL) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + IGRAPH_ASSERT(!igraph_is_directed(&g)); + DESTROY_GVM(); + + /*Zero matrix elements for only possible vertex type means no edges*/ + init_vm(&type_dist, 1, 0, &pref_matrix, 0, 0, 0, 1); + IGRAPH_ASSERT(igraph_establishment_game(&g, /*nodes*/ 20, /*types*/ 2, /*edges_per_step*/ 5, &type_dist, &pref_matrix, /*directed*/ 0, /*node_type_vec*/ NULL) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + IGRAPH_ASSERT(igraph_vcount(&g) == 20); + DESTROY_GVM(); + + /*Two types with only cross terms makes a bipartite graph*/ + init_vm(&type_dist, 1, 1, &pref_matrix, 0, 1, 1, 0); + IGRAPH_ASSERT(igraph_establishment_game(&g, /*nodes*/ 20, /*types*/ 2, /*edges_per_step*/ 5, &type_dist, &pref_matrix, /*directed*/ 1, &node_type_vec) == IGRAPH_SUCCESS); + igraph_is_bipartite(&g, &bipartite, NULL); + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vector_int_min(&node_type_vec) == 0); + IGRAPH_ASSERT(igraph_vector_int_max(&node_type_vec) == 1); + IGRAPH_ASSERT(igraph_vector_int_size(&node_type_vec) == 20); + DESTROY_GVM(); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + /*Distribution of types should have at least one possible value*/ + igraph_vector_init(&type_dist, 0); + igraph_matrix_init(&pref_matrix, 0, 0); + IGRAPH_ASSERT(igraph_establishment_game(&g, /*nodes*/ 20, /* types*/ 2, /*edges_per_step*/ 5, &type_dist, &pref_matrix, /*directed*/ 0, /*node_type_vec*/ NULL) == IGRAPH_EINVAL); + DESTROY_GVM(); + + igraph_vector_int_destroy(&node_type_vec); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_eulerian_cycle.c b/tests/unit/igraph_eulerian_cycle.c new file mode 100644 index 0000000..813965f --- /dev/null +++ b/tests/unit/igraph_eulerian_cycle.c @@ -0,0 +1,183 @@ + +#include +#include +#include "test_utilities.h" + +int main(void) { + + igraph_t graph; + igraph_vector_int_t edge_res, vertex_res; + igraph_es_t es; + igraph_vs_t vs; + + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_init(&vertex_res, 0); +/* + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); +*/ + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_vs_1(&vs, 1); + igraph_delete_vertices(&graph, vs); + igraph_vs_1(&vs, 0); + igraph_delete_vertices(&graph, vs); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&edge_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, 3,4, 4,5, 5,2, + 2,6, 6,4, 4,8, 2,8, 2,7, 0,7, -1); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,0, 0,0, 0,0, -1); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_vs_1(&vs, 1); + igraph_delete_vertices(&graph, vs); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_vs_1(&vs, 1); + igraph_delete_vertices(&graph, vs); + igraph_vs_1(&vs, 0); + igraph_delete_vertices(&graph, vs); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, -1); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,3, 3,4, 4,0, 0,2, 2,1, 1,0, -1); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,6, 6,4, 4,5, 5,0, 0,1, 1,2, + 2,3, 3,4, 4,2, 2,0, -1); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vertex_res); + igraph_vector_int_init(&vertex_res, 0); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_vs_1(&vs, 1); + igraph_delete_vertices(&graph, vs); + igraph_eulerian_cycle(&graph, &edge_res, &vertex_res); + print_vector_int(&edge_res); + print_vector_int(&vertex_res); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_destroy(&vertex_res); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eulerian_cycle.out b/tests/unit/igraph_eulerian_cycle.out new file mode 100644 index 0000000..337c125 --- /dev/null +++ b/tests/unit/igraph_eulerian_cycle.out @@ -0,0 +1,32 @@ +( ) +( ) + +( 0 1 2 3 4 5 6 7 8 9 10 11 ) +( 0 1 2 3 4 5 2 6 4 8 2 7 0 ) + +( 2 1 0 ) +( 0 0 0 0 ) + +( ) +( ) + +( ) +( ) + +( ) +( ) + +( 0 1 2 ) +( 0 1 2 0 ) + +( 3 4 5 0 1 2 ) +( 0 2 1 0 3 4 0 ) + +( 4 5 9 0 1 8 6 7 2 3 ) +( 0 1 2 0 6 4 2 3 4 5 0 ) + +( ) +( ) + +( ) +( ) diff --git a/tests/unit/igraph_eulerian_path.c b/tests/unit/igraph_eulerian_path.c new file mode 100644 index 0000000..9862bc4 --- /dev/null +++ b/tests/unit/igraph_eulerian_path.c @@ -0,0 +1,443 @@ + +#include +#include +#include "test_utilities.h" + +int main(void) { + + igraph_t graph; + igraph_vector_int_t edge_res, vector_res; + igraph_es_t es; + igraph_vs_t vs; + + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_init(&vector_res, 0); + /* + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + printf("\n"); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + */ + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_vs_1(&vs, 1); + igraph_delete_vertices(&graph, vs); + igraph_vs_1(&vs, 0); + igraph_delete_vertices(&graph, vs); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1 , 1,2, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0 , -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,0 , + 1,5, 5,4, 4,1, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,1, 1,0, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1 , 1,0 , 1,2, 2,3, 3,4, 4,1 ,-1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, 3,4, 4,5, 5,2, + 2,6, 6,4, 4,8, 2,8, 2,7, 0,7, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1 , 1,2, 2,3, 3,4 , 2,4 , 1,5, + 0,5 , -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,4, 3,4, 1,3, 2,5, 4,5, 2,6, 1,6, 0,4, 6,5, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 7,8, 8,9, 9,7, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 1,2, 2,3, 3,4, 4,5, 5,6, 6,3, + 3,7, 7,5, 5,9, 3,9, 3,8, 1,8, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1 , 1,2 , 2,0 , 0,3 , 3,2 , + 2,5 , 5,3 , 3,4 , 4,5 , 1,4 , 0,4 ,-1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 2,3 , 3,4 , 4,2 , 2,5 , 5,4 , + 4,7 , 7,5 , 5,6 , 6,7 , 3,6 , 2,6 ,-1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1 , 1,2, 2,5 , 5,4 , 5,6 , 6,2 , + 2,3 , 3,4 , 4,8, 8,2 , 2,7, 7,0 , -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,4 , 4,0, 1,2 , 2,3 , 3,5, 5,2, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_vs_1(&vs, 1); + igraph_delete_vertices(&graph, vs); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_vs_1(&vs, 1); + igraph_delete_vertices(&graph, vs); + igraph_vs_1(&vs, 0); + igraph_delete_vertices(&graph, vs); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1 , 1,2, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,0, 0,0, 0,0, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1 , 1,3, 3,2, 2,0 , 2,1, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,3, 3,4, 4,0, 0,2, 2,1, 1,0, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,6, 6,4, 4,5, 5,0, 0,1, 1,2, + 2,3, 3,4, 4,2, 2,0, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 3,4, 1,3, 2,1, 1,2, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 3,4, 1,3, 2,1, 1,2, 0,0, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,2 , 2,4 , 4,5 , 5,2 , 2,0 , 0,1 , + 1,1 , 1,3 , 1,3 , 3,2 , 2,1 , 3,5 , -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 1,3 , 3,5 , 5,6 , 6,3 , 3,1 , 1,2 , + 2,2 , 2,4 , 2,4 , 4,3 , 3,2 , 4,6 , -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 7,8, 8,9, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 7,8, 8,9, 9,7, -1); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_init(&edge_res, 0); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + printf("\n"); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edge_res); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_init(&vector_res, 0); + igraph_vector_int_init(&edge_res, 0); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, -1); + igraph_es_1(&es, 0); + igraph_delete_edges(&graph, es); + igraph_vs_1(&vs, 1); + igraph_delete_vertices(&graph, vs); + igraph_eulerian_path(&graph, &edge_res, &vector_res); + print_vector_int(&edge_res); + print_vector_int(&vector_res); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&vector_res); + igraph_vector_int_destroy(&edge_res); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_eulerian_path.out b/tests/unit/igraph_eulerian_path.out new file mode 100644 index 0000000..e64ed15 --- /dev/null +++ b/tests/unit/igraph_eulerian_path.out @@ -0,0 +1,95 @@ +( ) +( ) + +( 0 1 ) +( 0 1 2 ) + +( 0 1 2 ) +( 0 1 2 0 ) + +( 0 5 4 3 1 2 ) +( 0 1 4 5 1 2 0 ) + +( 3 2 1 0 ) +( 0 1 2 1 0 ) + +( 1 2 3 4 5 0 ) +( 0 1 2 3 4 1 0 ) + +( 0 1 2 3 4 5 6 7 8 9 10 11 ) +( 0 1 2 3 4 5 2 6 4 8 2 7 0 ) + +( 0 6 5 1 2 3 4 ) +( 1 0 5 1 2 3 4 2 ) + +( 5 1 0 9 2 7 8 4 3 6 10 ) +( 5 2 1 0 4 2 6 1 3 4 5 6 ) + +( 0 1 2 ) +( 7 8 9 7 ) + +( 0 1 2 3 4 5 6 7 8 9 10 11 ) +( 1 2 3 4 5 6 3 7 5 9 3 8 1 ) + +( 0 2 1 9 10 3 4 5 6 7 8 ) +( 1 0 2 1 4 0 3 2 5 3 4 5 ) + +( 0 2 1 9 10 3 4 5 6 7 8 ) +( 3 2 4 3 6 2 5 4 7 5 6 7 ) + +( 7 6 1 0 11 10 2 3 8 9 5 4 ) +( 4 3 2 1 0 7 2 5 4 8 2 6 5 ) + +( 0 2 1 3 4 5 6 ) +( 1 0 4 1 2 3 5 2 ) + +( ) +( ) + +( ) +( ) + +( ) +( ) + +( 0 1 ) +( 0 1 2 ) + +( 2 1 0 ) +( 0 0 0 0 ) + +( 0 1 2 ) +( 0 1 2 0 ) + +( 3 0 1 2 4 ) +( 2 0 1 3 2 1 ) + +( 3 4 5 0 1 2 ) +( 0 2 1 0 3 4 0 ) + +( 4 5 9 0 1 8 6 7 2 3 ) +( 0 1 2 0 6 4 2 3 4 5 0 ) + +( 0 4 3 2 1 ) +( 0 1 2 1 3 4 ) + +( 5 0 4 3 2 1 ) +( 0 0 1 2 1 3 4 ) + +( 5 6 8 9 4 0 10 7 11 3 1 2 ) +( 0 1 1 3 2 0 2 1 3 5 2 4 5 ) + +( 5 6 8 9 4 0 10 7 11 3 1 2 ) +( 1 2 2 4 3 1 3 2 4 6 3 5 6 ) + +( 0 1 ) +( 7 8 9 ) + +( 0 1 2 ) +( 7 8 9 7 ) + +( ) +( ) + +( ) +( ) diff --git a/tests/unit/igraph_extended_chordal_ring.c b/tests/unit/igraph_extended_chordal_ring.c new file mode 100644 index 0000000..d9e5eca --- /dev/null +++ b/tests/unit/igraph_extended_chordal_ring.c @@ -0,0 +1,74 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g, g_rev, g_test; + igraph_bool_t same; + igraph_matrix_int_t W; + int i, j; + + /* Directed, pentagram with ring, both clockwise */ + igraph_matrix_int_init(&W, 1, 1); + igraph_matrix_int_set(&W, 0, 0, 2); + IGRAPH_ASSERT(igraph_extended_chordal_ring(&g, /* nodes */ 5, &W, 1 /*directed*/) == IGRAPH_SUCCESS); + igraph_small(&g_test, 5, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 2, 1, 3, 2, 4, 3, 0, 4, 1, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + + /* Use negative matrix value for same specification */ + igraph_matrix_int_set(&W, 0, 0, -3); + IGRAPH_ASSERT(igraph_extended_chordal_ring(&g_rev, /* nodes */ 5, &W, 1 /*directed*/) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_is_same_graph(&g_rev, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g); + igraph_destroy(&g_rev); + igraph_destroy(&g_test); + igraph_matrix_int_destroy(&W); + + + /* From article, should give double edges for chords in igraph */ + igraph_matrix_int_init(&W, 2, 2); + int m[2][2] = {{4, 2}, + {8, 10}}; + for (i=0; i < 2; i++) { + for (j=0; j < 2; j++) { + MATRIX(W, i, j) = m[i][j]; + } + } + IGRAPH_ASSERT(igraph_extended_chordal_ring(&g, /* nodes */ 12, &W, 0 /*undirected*/) == IGRAPH_SUCCESS); + igraph_small(&g_test, 12, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 0, + 0, 4, 2, 6, 4, 8, 6, 10, 8, 0, 10, 2, + 0, 4, 2, 6, 4, 8, 6, 10, 8, 0, 10, 2, + 1, 3, 3, 5, 5, 7, 7, 9, 9, 11, 11, 1, + 1, 3, 3, 5, 5, 7, 7, 9, 9, 11, 11, 1, + -1); + + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g); + igraph_destroy(&g_test); + igraph_matrix_int_destroy(&W); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_feedback_arc_set_undirected.c b/tests/unit/igraph_feedback_arc_set_undirected.c new file mode 100644 index 0000000..255a135 --- /dev/null +++ b/tests/unit/igraph_feedback_arc_set_undirected.c @@ -0,0 +1,59 @@ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t graph; + igraph_vector_int_t result; + igraph_bool_t acyclic; + + igraph_rng_seed(igraph_rng_default(), 137); + + igraph_vector_int_init(&result, 0); + + /* Null graph */ + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_feedback_arc_set(&graph, &result, NULL, IGRAPH_FAS_APPROX_EADES); + IGRAPH_ASSERT(igraph_vector_int_size(&result) == 0); + igraph_destroy(&graph); + + /* Singleton graph */ + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_feedback_arc_set(&graph, &result, NULL, IGRAPH_FAS_APPROX_EADES); + IGRAPH_ASSERT(igraph_vector_int_size(&result) == 0); + igraph_destroy(&graph); + + /* Two isolated vertices */ + igraph_empty(&graph, 2, IGRAPH_UNDIRECTED); + igraph_feedback_arc_set(&graph, &result, NULL, IGRAPH_FAS_APPROX_EADES); + IGRAPH_ASSERT(igraph_vector_int_size(&result) == 0); + igraph_destroy(&graph); + + /* Single vertex with loop */ + acyclic = 0; + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, + 0,0, + -1); + igraph_feedback_arc_set(&graph, &result, NULL, IGRAPH_FAS_APPROX_EADES); + igraph_delete_edges(&graph, igraph_ess_vector(&result)); + igraph_is_forest(&graph, &acyclic, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + /* Random graph */ + acyclic = 0; + igraph_erdos_renyi_game_gnm(&graph, 20, 40, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + igraph_feedback_arc_set(&graph, &result, NULL, IGRAPH_FAS_APPROX_EADES); + igraph_delete_edges(&graph, igraph_ess_vector(&result)); + igraph_is_forest(&graph, &acyclic, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + igraph_vector_int_destroy(&result); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_forest_fire_game.c b/tests/unit/igraph_forest_fire_game.c new file mode 100644 index 0000000..67c2ae8 --- /dev/null +++ b/tests/unit/igraph_forest_fire_game.c @@ -0,0 +1,67 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_forest_fire_game(&g, /*number of vertices*/0, /*fw_prob*/ 0.0, + /*bw_factor*/ 0.0, /*pambs*/1, /*directed*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("No ambassadors:\n"); + IGRAPH_ASSERT(igraph_forest_fire_game(&g, /*number of vertices*/10, /*fw_prob*/ 0.0, + /*bw_factor*/ 0.0, /*pambs*/0, /*directed*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("More ambassadors than nodes:\n"); + IGRAPH_ASSERT(igraph_forest_fire_game(&g, /*number of vertices*/5, /*fw_prob*/ 0.0, + /*bw_factor*/ 0.0, /*pambs*/100, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Some normal inputs, just to check for memory problems, no output checking.\n"); + IGRAPH_ASSERT(igraph_forest_fire_game(&g, /*number of vertices*/50, /*fw_prob*/ 0.5, + /*bw_factor*/ 0.5, /*pambs*/3, /*directed*/ 1) == IGRAPH_SUCCESS); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Negative fw_prob.\n"); + IGRAPH_ASSERT(igraph_forest_fire_game(&g, /*number of vertices*/5, /*fw_prob*/ -0.5, + /*bw_factor*/ 0.0, /*pambs*/100, /*directed*/ 1) == IGRAPH_EINVAL); + + printf("Negative bw_factor.\n"); + IGRAPH_ASSERT(igraph_forest_fire_game(&g, /*number of vertices*/5, /*fw_prob*/ 0.5, + /*bw_factor*/ -0.5, /*pambs*/100, /*directed*/ 0) == IGRAPH_EINVAL); + + printf("Negative number of ambassadors.\n"); + IGRAPH_ASSERT(igraph_forest_fire_game(&g, /*number of vertices*/5, /*fw_prob*/ 0.5, + /*bw_factor*/ 0.5, /*pambs*/-100, /*directed*/ 1) == IGRAPH_EINVAL); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_forest_fire_game.out b/tests/unit/igraph_forest_fire_game.out new file mode 100644 index 0000000..d08a97e --- /dev/null +++ b/tests/unit/igraph_forest_fire_game.out @@ -0,0 +1,29 @@ +No vertices: +directed: false +vcount: 0 +edges: { +} +No ambassadors: +directed: false +vcount: 10 +edges: { +} +More ambassadors than nodes: +directed: true +vcount: 5 +edges: { +1 0 +2 0 +2 1 +3 0 +3 1 +3 2 +4 0 +4 1 +4 2 +4 3 +} +Some normal inputs, just to check for memory problems, no output checking. +Negative fw_prob. +Negative bw_factor. +Negative number of ambassadors. diff --git a/tests/unit/igraph_from_prufer.c b/tests/unit/igraph_from_prufer.c new file mode 100644 index 0000000..562d848 --- /dev/null +++ b/tests/unit/igraph_from_prufer.c @@ -0,0 +1,42 @@ + +#include +#include + +#include "test_utilities.h" + + +int main(void) { + igraph_t graph; + igraph_integer_t prufer1[] = {2, 3, 2, 3}; + igraph_integer_t prufer2[] = {0, 2, 4, 1, 1, 0}; + igraph_vector_int_t prufer; + igraph_bool_t tree; + + igraph_vector_int_view(&prufer, prufer1, sizeof(prufer1) / sizeof(prufer1[0])); + igraph_from_prufer(&graph, &prufer); + igraph_is_tree(&graph, &tree, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(tree); + print_graph(&graph); + igraph_destroy(&graph); + + igraph_vector_int_view(&prufer, prufer2, sizeof(prufer2) / sizeof(prufer2[0])); + igraph_from_prufer(&graph, &prufer); + igraph_is_tree(&graph, &tree, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(tree); + print_graph(&graph); + igraph_destroy(&graph); + + /* For a zero-length array, we cannot use the same pattern as above because + standard C does not allow raw zero-length arrays. */ + igraph_vector_int_init(&prufer, 0); + igraph_from_prufer(&graph, &prufer); + igraph_is_tree(&graph, &tree, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(tree); + print_graph(&graph); + igraph_destroy(&graph); + igraph_vector_int_destroy(&prufer); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_from_prufer.out b/tests/unit/igraph_from_prufer.out new file mode 100644 index 0000000..41b5f4c --- /dev/null +++ b/tests/unit/igraph_from_prufer.out @@ -0,0 +1,25 @@ +directed: false +vcount: 6 +edges: { +2 0 +3 1 +4 2 +3 2 +5 3 +} +directed: false +vcount: 8 +edges: { +3 0 +5 2 +4 2 +4 1 +6 1 +1 0 +7 0 +} +directed: false +vcount: 2 +edges: { +1 0 +} diff --git a/tests/unit/igraph_full_citation.c b/tests/unit/igraph_full_citation.c new file mode 100644 index 0000000..4150484 --- /dev/null +++ b/tests/unit/igraph_full_citation.c @@ -0,0 +1,57 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g, g_test; + igraph_bool_t same; + igraph_integer_t n_vertices = 4; + + /* Undirected, should be a full graph */ + IGRAPH_ASSERT(igraph_full_citation(&g, n_vertices, 0 /*undirected*/) == IGRAPH_SUCCESS); + igraph_small(&g_test, 4, IGRAPH_UNDIRECTED, 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g); + igraph_destroy(&g_test); + + /* Directed, only edges from i->j if i > j */ + IGRAPH_ASSERT(igraph_full_citation(&g, n_vertices, 1 /*directed*/) == IGRAPH_SUCCESS); + igraph_small(&g_test, 4, IGRAPH_DIRECTED, 1, 0, 2, 0, 3, 0, 2, 1, 3, 1, 3, 2, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g); + igraph_destroy(&g_test); + + /* Directed, 1 vertex, should be edgeless */ + IGRAPH_ASSERT(igraph_full_citation(&g, 1 /*n_vertices*/, 1 /*directed*/) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + igraph_destroy(&g); + + /* Directed, 0 vertices, empty graph */ + IGRAPH_ASSERT(igraph_full_citation(&g, 0 /*n_vertices*/, 1 /*directed*/) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; + +} diff --git a/tests/unit/igraph_full_multipartite.c b/tests/unit/igraph_full_multipartite.c new file mode 100644 index 0000000..f484dc6 --- /dev/null +++ b/tests/unit/igraph_full_multipartite.c @@ -0,0 +1,158 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t partitions; + igraph_vector_int_t types; + + printf("Empty directed graph, zero vertices:"); + igraph_vector_int_init(&partitions, 0); + igraph_vector_int_init(&types, 0); + igraph_full_multipartite(&g, &types, &partitions, IGRAPH_DIRECTED, IGRAPH_ALL); + + print_graph_canon(&g); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types); + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types); + igraph_destroy(&g); + + printf("\nDirected graph with one partition, 4 vertices:"); + igraph_vector_int_init(&partitions, 1); + igraph_vector_int_init(&types, 0); + + VECTOR(partitions)[0] = 4; + + igraph_full_multipartite(&g, &types, &partitions, IGRAPH_DIRECTED, IGRAPH_ALL); + + print_graph_canon(&g); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types); + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types); + igraph_destroy(&g); + + printf("\nDirected graph with 3 partitions:"); + igraph_vector_int_init(&partitions, 3); + igraph_vector_int_init(&types, 0); + + VECTOR(partitions)[0] = 2; + VECTOR(partitions)[1] = 3; + VECTOR(partitions)[2] = 3; + + igraph_full_multipartite(&g, &types, &partitions, IGRAPH_DIRECTED, IGRAPH_ALL); + + print_graph_canon(&g); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types); + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types); + igraph_destroy(&g); + + printf("\nDirected graph, 4 partitions, mode=IN:"); + igraph_vector_int_init(&partitions, 4); + igraph_vector_int_init(&types, 0); + + VECTOR(partitions)[0] = 2; + VECTOR(partitions)[1] = 3; + VECTOR(partitions)[2] = 4; + VECTOR(partitions)[3] = 2; + + igraph_full_multipartite(&g, &types, &partitions, IGRAPH_DIRECTED, IGRAPH_IN); + + print_graph_canon(&g); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types); + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types); + igraph_destroy(&g); + + printf("\nUndirected graph, 4 partitions:"); + igraph_vector_int_init(&partitions, 4); + igraph_vector_int_init(&types, 0); + + VECTOR(partitions)[0] = 2; + VECTOR(partitions)[1] = 3; + VECTOR(partitions)[2] = 4; + VECTOR(partitions)[3] = 2; + + igraph_full_multipartite(&g, &types, &partitions, IGRAPH_UNDIRECTED, IGRAPH_ALL); + + print_graph_canon(&g); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types); + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types); + igraph_destroy(&g); + + printf("\nDirected graph with 3 partitions, all partitions with size zero:"); + igraph_vector_int_init(&partitions, 3); + igraph_vector_int_init(&types, 0); + + VECTOR(partitions)[0] = 0; + VECTOR(partitions)[1] = 0; + VECTOR(partitions)[2] = 0; + + igraph_full_multipartite(&g, &types, &partitions, IGRAPH_DIRECTED, IGRAPH_ALL); + + print_graph_canon(&g); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types); + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types); + igraph_destroy(&g); + + printf("\nDirected graph with 3 partitions, one parition with size zero:"); + igraph_vector_int_init(&partitions, 3); + igraph_vector_int_init(&types, 0); + + VECTOR(partitions)[0] = 2; + VECTOR(partitions)[1] = 0; + VECTOR(partitions)[2] = 3; + + igraph_full_multipartite(&g, &types, &partitions, IGRAPH_DIRECTED, IGRAPH_ALL); + + print_graph_canon(&g); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types); + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return IGRAPH_SUCCESS; +} diff --git a/tests/unit/igraph_full_multipartite.out b/tests/unit/igraph_full_multipartite.out new file mode 100644 index 0000000..4b38d33 --- /dev/null +++ b/tests/unit/igraph_full_multipartite.out @@ -0,0 +1,197 @@ +Empty directed graph, zero vertices:directed: true +vcount: 0 +edges: { +} + +Partition type: + + +Directed graph with one partition, 4 vertices:directed: true +vcount: 4 +edges: { +} + +Partition type: +0 0 0 0 + +Directed graph with 3 partitions:directed: true +vcount: 8 +edges: { +0 2 +0 3 +0 4 +0 5 +0 6 +0 7 +1 2 +1 3 +1 4 +1 5 +1 6 +1 7 +2 0 +2 1 +2 5 +2 6 +2 7 +3 0 +3 1 +3 5 +3 6 +3 7 +4 0 +4 1 +4 5 +4 6 +4 7 +5 0 +5 1 +5 2 +5 3 +5 4 +6 0 +6 1 +6 2 +6 3 +6 4 +7 0 +7 1 +7 2 +7 3 +7 4 +} + +Partition type: +0 0 1 1 1 2 2 2 + +Directed graph, 4 partitions, mode=IN:directed: true +vcount: 11 +edges: { +2 0 +2 1 +3 0 +3 1 +4 0 +4 1 +5 0 +5 1 +5 2 +5 3 +5 4 +6 0 +6 1 +6 2 +6 3 +6 4 +7 0 +7 1 +7 2 +7 3 +7 4 +8 0 +8 1 +8 2 +8 3 +8 4 +9 0 +9 1 +9 2 +9 3 +9 4 +9 5 +9 6 +9 7 +9 8 +10 0 +10 1 +10 2 +10 3 +10 4 +10 5 +10 6 +10 7 +10 8 +} + +Partition type: +0 0 1 1 1 2 2 2 2 3 3 + +Undirected graph, 4 partitions:directed: false +vcount: 11 +edges: { +0 2 +0 3 +0 4 +0 5 +0 6 +0 7 +0 8 +0 9 +0 10 +1 2 +1 3 +1 4 +1 5 +1 6 +1 7 +1 8 +1 9 +1 10 +2 5 +2 6 +2 7 +2 8 +2 9 +2 10 +3 5 +3 6 +3 7 +3 8 +3 9 +3 10 +4 5 +4 6 +4 7 +4 8 +4 9 +4 10 +5 9 +5 10 +6 9 +6 10 +7 9 +7 10 +8 9 +8 10 +} + +Partition type: +0 0 1 1 1 2 2 2 2 3 3 + +Directed graph with 3 partitions, all partitions with size zero:directed: true +vcount: 0 +edges: { +} + +Partition type: + + +Directed graph with 3 partitions, one parition with size zero:directed: true +vcount: 5 +edges: { +0 2 +0 3 +0 4 +1 2 +1 3 +1 4 +2 0 +2 1 +3 0 +3 1 +4 0 +4 1 +} + +Partition type: +0 0 1 1 1 diff --git a/tests/unit/igraph_generalized_petersen.c b/tests/unit/igraph_generalized_petersen.c new file mode 100644 index 0000000..f246aae --- /dev/null +++ b/tests/unit/igraph_generalized_petersen.c @@ -0,0 +1,55 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph, graph_test; + igraph_bool_t iso; + + /* Compares G(5,2) with Petersen Graph in igraph_famous */ + IGRAPH_ASSERT(igraph_generalized_petersen(&graph, /* n */ 5, /* k */ 2) == IGRAPH_SUCCESS); + igraph_famous(&graph_test, "Petersen"); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + + /* Compares G(9,3) with expected small graph */ + IGRAPH_ASSERT(igraph_generalized_petersen(&graph, /* n */ 9, /* k */ 3) == IGRAPH_SUCCESS); + igraph_small(&graph_test, 18, IGRAPH_UNDIRECTED, 0, 3, 0, 6, 0, 9, 1, 4, 1, 7, 1, 10, 2, 5, 2, + 8, 2, 11, 3, 6, 3, 12, 4, 7, 4, 13, 5, 8, 5, 14, 6, 15, 7, 16, 8, 17, 9, 10, 9, 17, 10, + 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, -1); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + + /* Compares G(10,2) with Dodecahedral Graph in igraph_famous */ + IGRAPH_ASSERT(igraph_generalized_petersen(&graph, /* n */ 10, /* k */ 2) == IGRAPH_SUCCESS); + igraph_famous(&graph_test, "Dodecahedral"); + IGRAPH_ASSERT(igraph_isomorphic(&graph, &graph_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&graph); + igraph_destroy(&graph_test); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_adjacency.c b/tests/unit/igraph_get_adjacency.c new file mode 100644 index 0000000..62192a4 --- /dev/null +++ b/tests/unit/igraph_get_adjacency.c @@ -0,0 +1,200 @@ +/* + 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, see . +*/ + +#include +#include + +#include "test_utilities.h" + +void test_undirected(void) { + igraph_t graph; + igraph_real_t weights_array[] = { 5, 4, 3, 2, 1, 6, 3, 2 }; + igraph_vector_t weights; + igraph_matrix_t m; + + igraph_small(&graph, 5, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 3, 2, 2, 0, 1, -1); + igraph_vector_view(&weights, weights_array, igraph_ecount(&graph)); + + igraph_matrix_init(&m, 2, 2); + + printf("Undirected, unweighted, upper, no loops:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, NULL, IGRAPH_NO_LOOPS); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, unweighted, upper, loops once:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, NULL, IGRAPH_LOOPS_ONCE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, unweighted, upper, loops twice:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, NULL, IGRAPH_LOOPS_TWICE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, unweighted, lower, no loops:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, NULL, IGRAPH_NO_LOOPS); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, unweighted, lower, loops once:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, NULL, IGRAPH_LOOPS_ONCE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, unweighted, lower, loops twice:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, NULL, IGRAPH_LOOPS_TWICE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, unweighted, both, no loops:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_NO_LOOPS); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, unweighted, both, loops once:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, unweighted, both, loops twice:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_TWICE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, upper, no loops:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, &weights, IGRAPH_NO_LOOPS); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, upper, loops once:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, &weights, IGRAPH_LOOPS_ONCE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, upper, loops twice:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, &weights, IGRAPH_LOOPS_TWICE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, lower, no loops:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, &weights, IGRAPH_NO_LOOPS); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, lower, loops once:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, &weights, IGRAPH_LOOPS_ONCE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, lower, loops twice:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, &weights, IGRAPH_LOOPS_TWICE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, both, no loops:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_NO_LOOPS); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, both, loops once:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_LOOPS_ONCE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Undirected, weighted, both, loops twice:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_LOOPS_TWICE); + igraph_matrix_print(&m); + printf("========\n"); + + igraph_matrix_destroy(&m); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); +} + +void test_directed(void) { + igraph_t graph; + igraph_real_t weights_array[] = { 5, 4, 3, 2, 1, 6, 3, 2 }; + igraph_vector_t weights; + igraph_matrix_t m; + + igraph_small(&graph, 5, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 3, 2, 2, 0, 1, -1); + igraph_vector_view(&weights, weights_array, igraph_ecount(&graph)); + + igraph_matrix_init(&m, 2, 2); + + printf("Directed, unweighted, no loops:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_NO_LOOPS); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Directed, unweighted, loops once:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Directed, unweighted, loops twice (same as once):\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_TWICE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Directed, weighted, no loops:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_NO_LOOPS); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Directed, weighted, loops once:\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_LOOPS_ONCE); + igraph_matrix_print(&m); + printf("========\n"); + + printf("Directed, weighted, loops twice (same as once):\n"); + igraph_get_adjacency(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_LOOPS_TWICE); + igraph_matrix_print(&m); + printf("========\n"); + + igraph_matrix_destroy(&m); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); +} + +void test_errors(void) { + igraph_t graph; + igraph_matrix_t m; + + igraph_small(&graph, 5, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 3, 2, 2, 0, 1, -1); + igraph_matrix_init(&m, 2, 2); + + CHECK_ERROR(igraph_get_adjacency(&graph, &m, (igraph_get_adjacency_t) 42, NULL, IGRAPH_LOOPS_ONCE), IGRAPH_EINVAL); + + igraph_matrix_destroy(&m); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); +} + +int main(void) { + + test_undirected(); + test_directed(); + test_errors(); + + return 0; +} diff --git a/tests/unit/igraph_get_adjacency.out b/tests/unit/igraph_get_adjacency.out new file mode 100644 index 0000000..39b1bb2 --- /dev/null +++ b/tests/unit/igraph_get_adjacency.out @@ -0,0 +1,168 @@ +Undirected, unweighted, upper, no loops: +0 2 0 1 1 +0 0 1 0 0 +0 0 0 1 0 +0 0 0 0 1 +0 0 0 0 0 +======== +Undirected, unweighted, upper, loops once: +0 2 0 1 1 +0 0 1 0 0 +0 0 1 1 0 +0 0 0 0 1 +0 0 0 0 0 +======== +Undirected, unweighted, upper, loops twice: +0 2 0 1 1 +0 0 1 0 0 +0 0 2 1 0 +0 0 0 0 1 +0 0 0 0 0 +======== +Undirected, unweighted, lower, no loops: +0 0 0 0 0 +2 0 0 0 0 +0 1 0 0 0 +1 0 1 0 0 +1 0 0 1 0 +======== +Undirected, unweighted, lower, loops once: +0 0 0 0 0 +2 0 0 0 0 +0 1 1 0 0 +1 0 1 0 0 +1 0 0 1 0 +======== +Undirected, unweighted, lower, loops twice: +0 0 0 0 0 +2 0 0 0 0 +0 1 2 0 0 +1 0 1 0 0 +1 0 0 1 0 +======== +Undirected, unweighted, both, no loops: +0 2 0 1 1 +2 0 1 0 0 +0 1 0 1 0 +1 0 1 0 1 +1 0 0 1 0 +======== +Undirected, unweighted, both, loops once: +0 2 0 1 1 +2 0 1 0 0 +0 1 1 1 0 +1 0 1 0 1 +1 0 0 1 0 +======== +Undirected, unweighted, both, loops twice: +0 2 0 1 1 +2 0 1 0 0 +0 1 2 1 0 +1 0 1 0 1 +1 0 0 1 0 +======== +Undirected, weighted, upper, no loops: +0 7 0 6 1 +0 0 4 0 0 +0 0 0 3 0 +0 0 0 0 2 +0 0 0 0 0 +======== +Undirected, weighted, upper, loops once: +0 7 0 6 1 +0 0 4 0 0 +0 0 3 3 0 +0 0 0 0 2 +0 0 0 0 0 +======== +Undirected, weighted, upper, loops twice: +0 7 0 6 1 +0 0 4 0 0 +0 0 6 3 0 +0 0 0 0 2 +0 0 0 0 0 +======== +Undirected, weighted, lower, no loops: +0 0 0 0 0 +7 0 0 0 0 +0 4 0 0 0 +6 0 3 0 0 +1 0 0 2 0 +======== +Undirected, weighted, lower, loops once: +0 0 0 0 0 +7 0 0 0 0 +0 4 3 0 0 +6 0 3 0 0 +1 0 0 2 0 +======== +Undirected, weighted, lower, loops twice: +0 0 0 0 0 +7 0 0 0 0 +0 4 6 0 0 +6 0 3 0 0 +1 0 0 2 0 +======== +Undirected, weighted, both, no loops: +0 7 0 6 1 +7 0 4 0 0 +0 4 0 3 0 +6 0 3 0 2 +1 0 0 2 0 +======== +Undirected, weighted, both, loops once: +0 7 0 6 1 +7 0 4 0 0 +0 4 3 3 0 +6 0 3 0 2 +1 0 0 2 0 +======== +Undirected, weighted, both, loops twice: +0 7 0 6 1 +7 0 4 0 0 +0 4 6 3 0 +6 0 3 0 2 +1 0 0 2 0 +======== +Directed, unweighted, no loops: +0 2 0 1 0 +0 0 1 0 0 +0 0 0 1 0 +0 0 0 0 1 +1 0 0 0 0 +======== +Directed, unweighted, loops once: +0 2 0 1 0 +0 0 1 0 0 +0 0 1 1 0 +0 0 0 0 1 +1 0 0 0 0 +======== +Directed, unweighted, loops twice (same as once): +0 2 0 1 0 +0 0 1 0 0 +0 0 1 1 0 +0 0 0 0 1 +1 0 0 0 0 +======== +Directed, weighted, no loops: +0 7 0 6 0 +0 0 4 0 0 +0 0 0 3 0 +0 0 0 0 2 +1 0 0 0 0 +======== +Directed, weighted, loops once: +0 7 0 6 0 +0 0 4 0 0 +0 0 3 3 0 +0 0 0 0 2 +1 0 0 0 0 +======== +Directed, weighted, loops twice (same as once): +0 7 0 6 0 +0 0 4 0 0 +0 0 3 3 0 +0 0 0 0 2 +1 0 0 0 0 +======== diff --git a/tests/unit/igraph_get_adjacency_sparse.c b/tests/unit/igraph_get_adjacency_sparse.c new file mode 100644 index 0000000..68c9ed2 --- /dev/null +++ b/tests/unit/igraph_get_adjacency_sparse.c @@ -0,0 +1,210 @@ +/* + 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, see . +*/ + +#include +#include + +#include "test_utilities.h" + +void print_sparse_matrix(const igraph_sparsemat_t* sm) { + igraph_matrix_t m; + + igraph_matrix_init(&m, 0, 0); + igraph_sparsemat_as_matrix(&m, sm); + igraph_matrix_print(&m); + igraph_matrix_destroy(&m); +} + + +void test_undirected(void) { + igraph_t graph; + igraph_real_t weights_array[] = { 5, 4, 3, 2, 1, 6, 3, 2 }; + igraph_vector_t weights; + igraph_sparsemat_t m; + + igraph_small(&graph, 5, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 3, 2, 2, 0, 1, -1); + igraph_vector_view(&weights, weights_array, igraph_ecount(&graph)); + + igraph_sparsemat_init(&m, 2, 2, 0); + + printf("Undirected, unweighted, upper, no loops:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, NULL, IGRAPH_NO_LOOPS); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, unweighted, upper, loops once:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, NULL, IGRAPH_LOOPS_ONCE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, unweighted, upper, loops twice:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, NULL, IGRAPH_LOOPS_TWICE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, unweighted, lower, no loops:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, NULL, IGRAPH_NO_LOOPS); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, unweighted, lower, loops once:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, NULL, IGRAPH_LOOPS_ONCE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, unweighted, lower, loops twice:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, NULL, IGRAPH_LOOPS_TWICE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, unweighted, both, no loops:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_NO_LOOPS); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, unweighted, both, loops once:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, unweighted, both, loops twice:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_TWICE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, upper, no loops:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, &weights, IGRAPH_NO_LOOPS); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, upper, loops once:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, &weights, IGRAPH_LOOPS_ONCE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, upper, loops twice:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_UPPER, &weights, IGRAPH_LOOPS_TWICE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, lower, no loops:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, &weights, IGRAPH_NO_LOOPS); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, lower, loops once:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, &weights, IGRAPH_LOOPS_ONCE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, lower, loops twice:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_LOWER, &weights, IGRAPH_LOOPS_TWICE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, both, no loops:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_NO_LOOPS); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, both, loops once:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_LOOPS_ONCE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Undirected, weighted, both, loops twice:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_LOOPS_TWICE); + print_sparse_matrix(&m); + printf("========\n"); + + igraph_sparsemat_destroy(&m); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); +} + +void test_directed(void) { + igraph_t graph; + igraph_real_t weights_array[] = { 5, 4, 3, 2, 1, 6, 3, 2 }; + igraph_vector_t weights; + igraph_sparsemat_t m; + + igraph_small(&graph, 5, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 3, 2, 2, 0, 1, -1); + igraph_vector_view(&weights, weights_array, igraph_ecount(&graph)); + + igraph_sparsemat_init(&m, 2, 2, 0); + + printf("Directed, unweighted, no loops:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_NO_LOOPS); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Directed, unweighted, loops once:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Directed, unweighted, loops twice (same as once):\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_TWICE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Directed, weighted, no loops:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_NO_LOOPS); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Directed, weighted, loops once:\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_LOOPS_ONCE); + print_sparse_matrix(&m); + printf("========\n"); + + printf("Directed, weighted, loops twice (same as once):\n"); + igraph_get_adjacency_sparse(&graph, &m, IGRAPH_GET_ADJACENCY_BOTH, &weights, IGRAPH_LOOPS_TWICE); + print_sparse_matrix(&m); + printf("========\n"); + + igraph_sparsemat_destroy(&m); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); +} + +void test_errors(void) { + igraph_t graph; + igraph_sparsemat_t m; + + igraph_small(&graph, 5, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 3, 2, 2, 0, 1, -1); + igraph_sparsemat_init(&m, 2, 2, 0); + + CHECK_ERROR(igraph_get_adjacency_sparse(&graph, &m, (igraph_get_adjacency_t) 42, NULL, IGRAPH_LOOPS_ONCE), IGRAPH_EINVAL); + + igraph_sparsemat_destroy(&m); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); +} + +int main(void) { + + test_undirected(); + test_directed(); + test_errors(); + + return 0; +} diff --git a/tests/unit/igraph_get_adjacency_sparse.out b/tests/unit/igraph_get_adjacency_sparse.out new file mode 100644 index 0000000..39b1bb2 --- /dev/null +++ b/tests/unit/igraph_get_adjacency_sparse.out @@ -0,0 +1,168 @@ +Undirected, unweighted, upper, no loops: +0 2 0 1 1 +0 0 1 0 0 +0 0 0 1 0 +0 0 0 0 1 +0 0 0 0 0 +======== +Undirected, unweighted, upper, loops once: +0 2 0 1 1 +0 0 1 0 0 +0 0 1 1 0 +0 0 0 0 1 +0 0 0 0 0 +======== +Undirected, unweighted, upper, loops twice: +0 2 0 1 1 +0 0 1 0 0 +0 0 2 1 0 +0 0 0 0 1 +0 0 0 0 0 +======== +Undirected, unweighted, lower, no loops: +0 0 0 0 0 +2 0 0 0 0 +0 1 0 0 0 +1 0 1 0 0 +1 0 0 1 0 +======== +Undirected, unweighted, lower, loops once: +0 0 0 0 0 +2 0 0 0 0 +0 1 1 0 0 +1 0 1 0 0 +1 0 0 1 0 +======== +Undirected, unweighted, lower, loops twice: +0 0 0 0 0 +2 0 0 0 0 +0 1 2 0 0 +1 0 1 0 0 +1 0 0 1 0 +======== +Undirected, unweighted, both, no loops: +0 2 0 1 1 +2 0 1 0 0 +0 1 0 1 0 +1 0 1 0 1 +1 0 0 1 0 +======== +Undirected, unweighted, both, loops once: +0 2 0 1 1 +2 0 1 0 0 +0 1 1 1 0 +1 0 1 0 1 +1 0 0 1 0 +======== +Undirected, unweighted, both, loops twice: +0 2 0 1 1 +2 0 1 0 0 +0 1 2 1 0 +1 0 1 0 1 +1 0 0 1 0 +======== +Undirected, weighted, upper, no loops: +0 7 0 6 1 +0 0 4 0 0 +0 0 0 3 0 +0 0 0 0 2 +0 0 0 0 0 +======== +Undirected, weighted, upper, loops once: +0 7 0 6 1 +0 0 4 0 0 +0 0 3 3 0 +0 0 0 0 2 +0 0 0 0 0 +======== +Undirected, weighted, upper, loops twice: +0 7 0 6 1 +0 0 4 0 0 +0 0 6 3 0 +0 0 0 0 2 +0 0 0 0 0 +======== +Undirected, weighted, lower, no loops: +0 0 0 0 0 +7 0 0 0 0 +0 4 0 0 0 +6 0 3 0 0 +1 0 0 2 0 +======== +Undirected, weighted, lower, loops once: +0 0 0 0 0 +7 0 0 0 0 +0 4 3 0 0 +6 0 3 0 0 +1 0 0 2 0 +======== +Undirected, weighted, lower, loops twice: +0 0 0 0 0 +7 0 0 0 0 +0 4 6 0 0 +6 0 3 0 0 +1 0 0 2 0 +======== +Undirected, weighted, both, no loops: +0 7 0 6 1 +7 0 4 0 0 +0 4 0 3 0 +6 0 3 0 2 +1 0 0 2 0 +======== +Undirected, weighted, both, loops once: +0 7 0 6 1 +7 0 4 0 0 +0 4 3 3 0 +6 0 3 0 2 +1 0 0 2 0 +======== +Undirected, weighted, both, loops twice: +0 7 0 6 1 +7 0 4 0 0 +0 4 6 3 0 +6 0 3 0 2 +1 0 0 2 0 +======== +Directed, unweighted, no loops: +0 2 0 1 0 +0 0 1 0 0 +0 0 0 1 0 +0 0 0 0 1 +1 0 0 0 0 +======== +Directed, unweighted, loops once: +0 2 0 1 0 +0 0 1 0 0 +0 0 1 1 0 +0 0 0 0 1 +1 0 0 0 0 +======== +Directed, unweighted, loops twice (same as once): +0 2 0 1 0 +0 0 1 0 0 +0 0 1 1 0 +0 0 0 0 1 +1 0 0 0 0 +======== +Directed, weighted, no loops: +0 7 0 6 0 +0 0 4 0 0 +0 0 0 3 0 +0 0 0 0 2 +1 0 0 0 0 +======== +Directed, weighted, loops once: +0 7 0 6 0 +0 0 4 0 0 +0 0 3 3 0 +0 0 0 0 2 +1 0 0 0 0 +======== +Directed, weighted, loops twice (same as once): +0 7 0 6 0 +0 0 4 0 0 +0 0 3 3 0 +0 0 0 0 2 +1 0 0 0 0 +======== diff --git a/tests/unit/igraph_get_all_shortest_paths_dijkstra.c b/tests/unit/igraph_get_all_shortest_paths_dijkstra.c new file mode 100644 index 0000000..d2fb974 --- /dev/null +++ b/tests/unit/igraph_get_all_shortest_paths_dijkstra.c @@ -0,0 +1,184 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void check_nrgeo(const igraph_t *graph, igraph_vs_t vs, + const igraph_vector_int_list_t *paths, + const igraph_vector_int_t *nrgeo) { + igraph_integer_t i, n; + igraph_vector_int_t nrgeo2, *path; + igraph_vit_t vit; + + n = igraph_vcount(graph); + igraph_vector_int_init(&nrgeo2, n); + if (igraph_vector_int_size(nrgeo) != n) { + printf("nrgeo vector length must be %" IGRAPH_PRId ", was %" IGRAPH_PRId, n, igraph_vector_int_size(nrgeo)); + return; + } + + n = igraph_vector_int_list_size(paths); + for (i = 0; i < n; i++) { + path = igraph_vector_int_list_get_ptr(paths, i); + if (path == 0) { + printf("Null path found in result vector at index %" IGRAPH_PRId "\n", i); + return; + } + if (igraph_vector_int_size(path) == 0) { + printf("Empty path found in result vector at index %" IGRAPH_PRId "\n", i); + return; + } + VECTOR(nrgeo2)[igraph_vector_int_tail(path)] += 1; + } + + igraph_vit_create(graph, vs, &vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + if (VECTOR(*nrgeo)[node] - VECTOR(nrgeo2)[node]) { + printf("nrgeo[%" IGRAPH_PRId "] invalid, observed = %" IGRAPH_PRId ", expected = %" IGRAPH_PRId "\n", + node, VECTOR(*nrgeo)[node], VECTOR(nrgeo2)[node]); + } + } + igraph_vit_destroy(&vit); + + igraph_vector_int_destroy(&nrgeo2); +} + +void print_and_destroy_items(igraph_vector_int_list_t* vec) { + igraph_integer_t i; + + for (i = 0; i < igraph_vector_int_list_size(vec); i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(vec, i)); + } + + igraph_vector_int_list_clear(vec); +} + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t vertices, edges; + + igraph_real_t weights[] = { 1, 2, 3, 4, 5, 1, 1, 1, 1, 1 }; + igraph_real_t weights2[] = { 0, 2, 1, 0, 5, 2, 1, 1, 0, 2, 2, 8, 1, 1, 3, 1, 1, 4, 2, 1 }; + igraph_integer_t dim[] = { 4, 4 }; + + igraph_vector_t weights_vec; + igraph_vector_int_t nrgeo; + igraph_vector_int_t dim_vec; + igraph_vs_t vs; + + igraph_vector_int_init(&nrgeo, 0); + + /* Simple ring graph without weights */ + + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, 0, 1); + + igraph_vector_int_list_init(&vertices, 0); + igraph_vector_int_list_init(&edges, 0); + igraph_vs_vector_small(&vs, 1, 3, 4, 5, 2, 1, -1); + + igraph_get_all_shortest_paths_dijkstra( + &g, + /*vertices=*/ &vertices, /*edges=*/ &edges, /*nrgeo=*/ &nrgeo, + /*from=*/ 0, /*to=*/ vs, + /*weights=*/ NULL, /*mode=*/ IGRAPH_OUT); + check_nrgeo(&g, vs, &vertices, &nrgeo); + print_and_destroy_items(&vertices); + print_and_destroy_items(&edges); + + /* Same ring, but with weights */ + + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + igraph_get_all_shortest_paths_dijkstra( + &g, + /*vertices=*/ &vertices, /*edges=*/ NULL, /*nrgeo=*/ &nrgeo, + /*from=*/ 0, /*to=*/ vs, + /*weights=*/ &weights_vec, /*mode=*/ IGRAPH_OUT); + check_nrgeo(&g, vs, &vertices, &nrgeo); + print_and_destroy_items(&vertices); + + /* we are now testing the combination of vertices == NULL and edges != NUL */ + + igraph_get_all_shortest_paths_dijkstra( + &g, + /*vertices=*/ NULL, /*edges=*/ &edges, /*nrgeo=*/ &nrgeo, + /*from=*/ 0, /*to=*/ vs, + /*weights=*/ &weights_vec, /*mode=*/ IGRAPH_OUT); + print_and_destroy_items(&edges); + + igraph_destroy(&g); + + /* More complicated example */ + + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 4, 1, 5, + 2, 3, 2, 6, 3, 2, 3, 6, + 4, 5, 4, 7, 5, 6, 5, 8, 5, 9, + 7, 5, 7, 8, 8, 9, + 5, 2, + 2, 1, + -1); + + igraph_vector_view(&weights_vec, weights2, sizeof(weights2) / sizeof(weights2[0])); + igraph_get_all_shortest_paths_dijkstra( + &g, + /*vertices=*/ &vertices, /*edges=*/ &edges, /*nrgeo=*/ &nrgeo, + /*from=*/ 0, /*to=*/ vs, + /*weights=*/ &weights_vec, /*mode=*/ IGRAPH_OUT); + + check_nrgeo(&g, vs, &vertices, &nrgeo); + + /* Sort the paths in a deterministic manner to avoid problems with + * different qsort() implementations on different platforms */ + igraph_vector_int_list_sort(&vertices, igraph_vector_int_colex_cmp); + igraph_vector_int_list_sort(&edges, igraph_vector_int_colex_cmp); + print_and_destroy_items(&vertices); + print_and_destroy_items(&edges); + + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + /* Regular lattice with some heavyweight edges */ + igraph_vector_int_view(&dim_vec, dim, sizeof(dim) / sizeof(dim[0])); + igraph_square_lattice(&g, &dim_vec, 1, 0, 0, 0); + igraph_vs_vector_small(&vs, 3, 12, 15, -1); + igraph_vector_init(&weights_vec, 24); + igraph_vector_fill(&weights_vec, 1); + VECTOR(weights_vec)[2] = 100; + VECTOR(weights_vec)[8] = 100; /* 1-->2, 4-->8 */ + igraph_get_all_shortest_paths_dijkstra( + &g, + /*vertices=*/ 0, /*edges=*/ 0, /*nrgeo=*/ &nrgeo, + /*from=*/ 0, /*to=*/ vs, + /*weights=*/ &weights_vec, /*mode=*/ IGRAPH_OUT); + igraph_vector_destroy(&weights_vec); + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + printf("%" IGRAPH_PRId " ", VECTOR(nrgeo)[3]); + printf("%" IGRAPH_PRId " ", VECTOR(nrgeo)[12]); + printf("%" IGRAPH_PRId "\n", VECTOR(nrgeo)[15]); + + igraph_vector_int_list_destroy(&vertices); + igraph_vector_int_list_destroy(&edges); + igraph_vector_int_destroy(&nrgeo); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_all_shortest_paths_dijkstra.out b/tests/unit/igraph_get_all_shortest_paths_dijkstra.out new file mode 100644 index 0000000..6140ede --- /dev/null +++ b/tests/unit/igraph_get_all_shortest_paths_dijkstra.out @@ -0,0 +1,37 @@ +0 1 +0 1 2 +0 1 2 3 +0 1 2 3 4 +0 9 8 7 6 5 +0 1 2 3 4 5 +0 +0 1 +0 1 2 +0 1 2 3 +9 8 7 6 5 +0 1 2 3 4 +0 1 +0 1 2 +0 1 2 3 +0 1 2 3 4 +0 9 8 7 6 5 4 +0 9 8 7 6 5 +0 +0 1 +0 1 2 +0 1 2 3 +9 8 7 6 5 4 +9 8 7 6 5 +0 1 +0 1 2 +0 3 +0 1 2 3 +0 1 4 +0 1 5 +0 +2 +0 3 +0 4 +0 5 +0 3 6 +4 4 12 diff --git a/tests/unit/igraph_get_all_simple_paths.c b/tests/unit/igraph_get_all_simple_paths.c new file mode 100644 index 0000000..985163b --- /dev/null +++ b/tests/unit/igraph_get_all_simple_paths.c @@ -0,0 +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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t res, res_all; + igraph_integer_t i; + + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 5, + 0, 3, 3, 4, 4, 5, + 3, 2, 3, 5, + -1); + + igraph_vector_int_init(&res, 0); + + for (i = 0; i <= 5; i++) { + igraph_get_all_simple_paths(&g, &res, 0, igraph_vss_1(5), i, IGRAPH_ALL); + + printf("Paths for cutoff %" IGRAPH_PRId ":\n", i); + igraph_vector_int_print(&res); + } + + igraph_vector_int_init(&res_all, 0); + + igraph_get_all_simple_paths(&g, &res_all, 0, igraph_vss_1(5), -1, IGRAPH_ALL); + + IGRAPH_ASSERT(igraph_vector_int_all_e(&res, &res_all) && "Paths of all lengths does not equal result for maximum cutoff."); + + igraph_vector_int_destroy(&res_all); + igraph_vector_int_destroy(&res); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_get_all_simple_paths.out b/tests/unit/igraph_get_all_simple_paths.out new file mode 100644 index 0000000..73dd692 --- /dev/null +++ b/tests/unit/igraph_get_all_simple_paths.out @@ -0,0 +1,12 @@ +Paths for cutoff 0: + +Paths for cutoff 1: + +Paths for cutoff 2: +0 3 5 -1 +Paths for cutoff 3: +0 1 2 5 -1 0 3 2 5 -1 0 3 4 5 -1 0 3 5 -1 +Paths for cutoff 4: +0 1 2 3 5 -1 0 1 2 5 -1 0 3 2 5 -1 0 3 4 5 -1 0 3 5 -1 +Paths for cutoff 5: +0 1 2 3 4 5 -1 0 1 2 3 5 -1 0 1 2 5 -1 0 3 2 5 -1 0 3 4 5 -1 0 3 5 -1 diff --git a/tests/unit/igraph_get_biadjacency.c b/tests/unit/igraph_get_biadjacency.c new file mode 100644 index 0000000..f094bd9 --- /dev/null +++ b/tests/unit/igraph_get_biadjacency.c @@ -0,0 +1,89 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, igraph_vector_bool_t *types) { + igraph_matrix_t result; + igraph_vector_int_t row_ids; + igraph_vector_int_t col_ids; + igraph_matrix_init(&result, 0, 0); + igraph_vector_int_init(&row_ids, 0); + igraph_vector_int_init(&col_ids, 0); + IGRAPH_ASSERT(igraph_get_biadjacency(graph, types, &result, &row_ids, &col_ids) == IGRAPH_SUCCESS); + printf("Bipartite adjacency matrix:\n"); + print_matrix(&result); + printf("Row ids:\n"); + print_vector_int(&row_ids); + printf("Col ids:\n"); + print_vector_int(&col_ids); + printf("\n"); + igraph_vector_int_destroy(&row_ids); + igraph_vector_int_destroy(&col_ids); + igraph_matrix_destroy(&result); +} + + +int main(void) { + igraph_t g_0, g_1, g_mu, g_mun; + igraph_vector_bool_t t_0, t_1, t_mu; + igraph_matrix_t result; + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_mu, 6, 0, 0,1, 0,2, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_mun, 6, 0, 0,1, 0,2, 0,3, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + + igraph_vector_bool_init(&t_0, 0); + igraph_vector_bool_init_int(&t_1, 1, 1); + igraph_vector_bool_init_int(&t_mu, 6, 0, 1, 1, 0, 1, 0); + + igraph_matrix_init(&result, 0, 0); + + printf("No vertices:\n"); + call_and_print(&g_0, &t_0); + + printf("One vertex:\n"); + call_and_print(&g_1, &t_1); + + printf("Disconnected graph with multiple edges:\n"); + call_and_print(&g_mu, &t_mu); + + printf("Checking non-bipartite graph.\n"); + call_and_print(&g_mun, &t_mu); + + VERIFY_FINALLY_STACK(); + + printf("Checking wrong type vector size error handling.\n"); + CHECK_ERROR(igraph_get_biadjacency(&g_mu, &t_0, &result, NULL, NULL), IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_mu); + igraph_destroy(&g_mun); + + igraph_vector_bool_destroy(&t_0); + igraph_vector_bool_destroy(&t_1); + igraph_vector_bool_destroy(&t_mu); + + igraph_matrix_destroy(&result); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_biadjacency.out b/tests/unit/igraph_get_biadjacency.out new file mode 100644 index 0000000..f294bf8 --- /dev/null +++ b/tests/unit/igraph_get_biadjacency.out @@ -0,0 +1,37 @@ +No vertices: +Bipartite adjacency matrix: +[ 0-by-0 ] +Row ids: +( ) +Col ids: +( ) + +One vertex: +Bipartite adjacency matrix: +[ 0-by-1 ] +Row ids: +( ) +Col ids: +( 0 ) + +Disconnected graph with multiple edges: +Bipartite adjacency matrix: +[ 1 3 0 + 1 1 2 + 0 0 0 ] +Row ids: +( 0 3 5 ) +Col ids: +( 1 2 4 ) + +Checking non-bipartite graph. +Bipartite adjacency matrix: +[ 1 3 0 + 1 1 2 + 0 0 0 ] +Row ids: +( 0 3 5 ) +Col ids: +( 1 2 4 ) + +Checking wrong type vector size error handling. diff --git a/tests/unit/igraph_get_eid.c b/tests/unit/igraph_get_eid.c new file mode 100644 index 0000000..6220461 --- /dev/null +++ b/tests/unit/igraph_get_eid.c @@ -0,0 +1,41 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_integer_t eid; + + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + + /* NON-EXISTENT EDGE */ + CHECK_ERROR(igraph_get_eid(&g, &eid, 5, 6, IGRAPH_UNDIRECTED, /*error=*/ 1), IGRAPH_EINVAL); + + /* INVALID VERTEX ID */ + CHECK_ERROR(igraph_get_eid(&g, &eid, 171, 6, IGRAPH_UNDIRECTED, /*error=*/ 1), IGRAPH_EINVVID); + + /* INVALID VERTEX ID even if error == 0 */ + CHECK_ERROR(igraph_get_eid(&g, &eid, 171, 6, IGRAPH_UNDIRECTED, /*error=*/ 0), IGRAPH_EINVVID); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_get_isomorphisms_vf2.c b/tests/unit/igraph_get_isomorphisms_vf2.c new file mode 100644 index 0000000..71d45e1 --- /dev/null +++ b/tests/unit/igraph_get_isomorphisms_vf2.c @@ -0,0 +1,137 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +/* Vertices/edges with the same parity match */ +igraph_bool_t compat_parity(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_integer_t g1_num, + const igraph_integer_t g2_num, + void *arg) { + IGRAPH_UNUSED(graph1); + IGRAPH_UNUSED(graph2); + IGRAPH_UNUSED(arg); + return (g1_num % 2) == (g2_num % 2); +} + +igraph_bool_t compat_not_arg(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_integer_t g1_num, + const igraph_integer_t g2_num, + void *arg) { + IGRAPH_UNUSED(graph1); + IGRAPH_UNUSED(graph2); + IGRAPH_UNUSED(arg); + return g1_num != *(int*)arg + g2_num; +} + +void print_and_destroy_maps(igraph_vector_int_list_t *vp) { + print_vector_int_list(vp); + igraph_vector_int_list_destroy(vp); +} + +void check_print_destroy(igraph_t *g1, + igraph_t *g2, + igraph_vector_int_t *vertex_color1, + igraph_vector_int_t *vertex_color2, + igraph_vector_int_t *edge_color1, + igraph_vector_int_t *edge_color2, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg, + int error) { + igraph_vector_int_list_t maps; + igraph_vector_int_list_init(&maps, 0); + IGRAPH_ASSERT(igraph_get_isomorphisms_vf2(g1, g2, vertex_color1, vertex_color2, edge_color1, edge_color2, &maps, node_compat_fn, edge_compat_fn, arg) == error); + print_and_destroy_maps(&maps); + printf("\n"); + +} + +void check_print_destroy_simple(igraph_t *g1, igraph_t *g2) { + check_print_destroy(g1, g2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, IGRAPH_SUCCESS); +} + +int main(void) { + igraph_t ring, ring_dir, ring_loop; + igraph_t g_0, g_1; + igraph_vector_int_t coloring; + int three = 3; + + igraph_vector_int_init_int(&coloring, 5, 0, 1, 0, 1, 0); + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_ring(&ring, 5, /*directed*/ 0, /*mutual*/ 0, /*circular*/ 1); + igraph_ring(&ring_dir, 5, /*directed*/ 1, /*mutual*/ 0, /*circular*/ 1); + igraph_ring(&ring_loop, 5, /*directed*/ 0, /*mutual*/ 0, /*circular*/ 1); + igraph_add_edge(&ring_loop, 2, 2); + + printf("Two empty graphs:\n"); + check_print_destroy_simple(&g_0, &g_0); + + printf("Two singleton graphs:\n"); + check_print_destroy_simple(&g_1, &g_1); + + printf("Empty and singleton graphs:\n"); + check_print_destroy_simple(&g_0, &g_1); + + printf("Two rings:\n"); + check_print_destroy_simple(&ring, &ring); + + printf("Two directed rings:\n"); + check_print_destroy_simple(&ring_dir, &ring_dir); + + printf("Two rings where node parity should be equal:\n"); + check_print_destroy(&ring, &ring, NULL, NULL, NULL, NULL, &compat_parity, NULL, NULL, IGRAPH_SUCCESS); + + printf("Two rings where edge parity should be equal:\n"); + check_print_destroy(&ring, &ring, NULL, NULL, NULL, NULL, NULL, &compat_parity, NULL, IGRAPH_SUCCESS); + + printf("Two rings with only one vertex coloring:\n"); + check_print_destroy(&ring, &ring, &coloring, NULL, NULL, NULL, NULL, NULL, NULL, IGRAPH_SUCCESS); + + printf("Two rings with vertex coloring:\n"); + check_print_destroy(&ring, &ring, &coloring, &coloring, NULL, NULL, NULL, NULL, NULL, IGRAPH_SUCCESS); + + printf("Two rings with edge coloring:\n"); + check_print_destroy(&ring, &ring, NULL, NULL, &coloring, &coloring, NULL, NULL, NULL, IGRAPH_SUCCESS); + + printf("Two rings where node of graph 1 should not be 3 higher than node of graph 2:\n"); + check_print_destroy(&ring, &ring, NULL, NULL, NULL, NULL, &compat_not_arg, NULL, &three, IGRAPH_SUCCESS); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Two rings with different directedness.\n"); + check_print_destroy(&ring, &ring_dir, NULL, NULL, NULL, NULL, NULL, NULL, NULL, IGRAPH_EINVAL); + + printf("Graph with loop edges.\n"); + check_print_destroy(&ring, &ring_loop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&ring); + igraph_destroy(&ring_dir); + igraph_destroy(&ring_loop); + igraph_vector_int_destroy(&coloring); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_isomorphisms_vf2.out b/tests/unit/igraph_get_isomorphisms_vf2.out new file mode 100644 index 0000000..b081c84 --- /dev/null +++ b/tests/unit/igraph_get_isomorphisms_vf2.out @@ -0,0 +1,94 @@ +Two empty graphs: +{ + 0: ( ) +} + +Two singleton graphs: +{ + 0: ( 0 ) +} + +Empty and singleton graphs: +{ +} + +Two rings: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 0 4 3 2 1 ) + 2: ( 1 0 4 3 2 ) + 3: ( 1 2 3 4 0 ) + 4: ( 2 1 0 4 3 ) + 5: ( 2 3 4 0 1 ) + 6: ( 3 2 1 0 4 ) + 7: ( 3 4 0 1 2 ) + 8: ( 4 0 1 2 3 ) + 9: ( 4 3 2 1 0 ) +} + +Two directed rings: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 1 2 3 4 0 ) + 2: ( 2 3 4 0 1 ) + 3: ( 3 4 0 1 2 ) + 4: ( 4 0 1 2 3 ) +} + +Two rings where node parity should be equal: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 4 3 2 1 0 ) +} + +Two rings where edge parity should be equal: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 0 4 3 2 1 ) +} + +Two rings with only one vertex coloring: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 0 4 3 2 1 ) + 2: ( 1 0 4 3 2 ) + 3: ( 1 2 3 4 0 ) + 4: ( 2 1 0 4 3 ) + 5: ( 2 3 4 0 1 ) + 6: ( 3 2 1 0 4 ) + 7: ( 3 4 0 1 2 ) + 8: ( 4 0 1 2 3 ) + 9: ( 4 3 2 1 0 ) +} + +Two rings with vertex coloring: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 4 3 2 1 0 ) +} + +Two rings with edge coloring: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 0 4 3 2 1 ) +} + +Two rings where node of graph 1 should not be 3 higher than node of graph 2: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 1 0 4 3 2 ) + 2: ( 1 2 3 4 0 ) + 3: ( 2 1 0 4 3 ) + 4: ( 2 3 4 0 1 ) + 5: ( 4 0 1 2 3 ) + 6: ( 4 3 2 1 0 ) +} + +Two rings with different directedness. +{ +} + +Graph with loop edges. +{ +} + diff --git a/tests/unit/igraph_get_k_shortest_paths.c b/tests/unit/igraph_get_k_shortest_paths.c new file mode 100644 index 0000000..1649311 --- /dev/null +++ b/tests/unit/igraph_get_k_shortest_paths.c @@ -0,0 +1,159 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print( + igraph_t *graph, igraph_vector_t *weights, igraph_integer_t k, + igraph_integer_t from, igraph_integer_t to, igraph_neimode_t mode +) { + igraph_vector_int_list_t vertex_paths; + igraph_vector_int_list_t edge_paths; + igraph_vector_int_list_t paths_for_verification; + igraph_integer_t i, n; + + igraph_vector_int_list_init(&vertex_paths, 0); + igraph_vector_int_list_init(&edge_paths, 0); + igraph_vector_int_list_init(&paths_for_verification, 0); + + IGRAPH_ASSERT(igraph_get_k_shortest_paths(graph, weights, &vertex_paths, &edge_paths, k, from, to, mode) == IGRAPH_SUCCESS); + + printf("result (vertex IDs): \n"); + print_vector_int_list(&vertex_paths); + + printf("result (edge IDs): \n"); + print_vector_int_list(&edge_paths); + + /* now we execute the same test but with only one of vertex_paths or edge_paths, + * and we check that we get the same result */ + IGRAPH_ASSERT(igraph_get_k_shortest_paths(graph, weights, NULL, &paths_for_verification, k, from, to, mode) == IGRAPH_SUCCESS); + n = igraph_vector_int_list_size(&paths_for_verification); + IGRAPH_ASSERT(n == igraph_vector_int_list_size(&edge_paths)); + for (i = 0; i < n; i++) { + IGRAPH_ASSERT(igraph_vector_int_is_equal( + igraph_vector_int_list_get_ptr(&edge_paths, i), + igraph_vector_int_list_get_ptr(&paths_for_verification, i) + )); + } + + IGRAPH_ASSERT(igraph_get_k_shortest_paths(graph, weights, &paths_for_verification, NULL, k, from, to, mode) == IGRAPH_SUCCESS); + n = igraph_vector_int_list_size(&paths_for_verification); + IGRAPH_ASSERT(n == igraph_vector_int_list_size(&vertex_paths)); + for (i = 0; i < n; i++) { + IGRAPH_ASSERT(igraph_vector_int_is_equal( + igraph_vector_int_list_get_ptr(&vertex_paths, i), + igraph_vector_int_list_get_ptr(&paths_for_verification, i) + )); + } + + igraph_vector_int_list_destroy(&paths_for_verification); + igraph_vector_int_list_destroy(&vertex_paths); + igraph_vector_int_list_destroy(&edge_paths); + printf("\n"); + + VERIFY_FINALLY_STACK(); +} + + +int main(void) { + /* Wiki example taken from https://en.wikipedia.org/wiki/Yen's_algorithm */ + igraph_t g_0, g_1, g_2, g_2c, g_wiki, g_wiki_u, g_sz, g_lattice; + igraph_vector_t weights, weights_wiki, weights_inf; + igraph_vector_int_t dims; + igraph_vector_int_list_t paths; + + igraph_vector_int_list_init(&paths, 0); + + igraph_small(&g_0, 0, IGRAPH_UNDIRECTED, -1); + igraph_small(&g_1, 1, IGRAPH_UNDIRECTED, -1); + igraph_small(&g_2, 2, IGRAPH_UNDIRECTED, -1); + igraph_small(&g_2c, 2, IGRAPH_UNDIRECTED, 0,1, -1); + igraph_small(&g_wiki, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,3, 2,1, 2,3, 2,4, 3,4, 3,5, 4,5, -1); + igraph_small(&g_wiki_u, 6, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,3, 2,1, 2,3, 2,4, 3,4, 3,5, 4,5, -1); + igraph_small(&g_sz, 5, IGRAPH_DIRECTED, 0, 1, 1, 2, 1, 2, 2, 0, 0, 3, 3, 4, 4, 2, 2, 2, 2, 4, 0, 4, -1); + + igraph_vector_int_init_int(&dims, 2, 3, 3); + igraph_square_lattice(&g_lattice, &dims, 1, IGRAPH_UNDIRECTED, /* mutual = */ 0, /* periodic = */ NULL); + igraph_vector_int_destroy(&dims); + + igraph_vector_init(&weights, 0); + igraph_vector_init_int(&weights_wiki, 9, 3, 2, 4, 1, 2, 3, 2, 1, 2); + igraph_vector_init_real(&weights_inf, 1, IGRAPH_INFINITY); + + printf("One vertex:\n"); + call_and_print(&g_1, &weights, 2, 0, 0, IGRAPH_ALL); + + printf("Two disconnected vertices:\n"); + call_and_print(&g_2, &weights, 2, 0, 1, IGRAPH_ALL); + + printf("Two vertices with infinite weight edge in between:\n"); + call_and_print(&g_2c, &weights_inf, 2, 0, 1, IGRAPH_ALL); + + printf("Wiki example:\n"); + call_and_print(&g_wiki, &weights_wiki, 10, 0, 5, IGRAPH_OUT); + + printf("Wiki example, 0 shortest paths:\n"); + call_and_print(&g_wiki, &weights_wiki, 0, 0, 5, IGRAPH_OUT); + + printf("Wiki example, 2 shortest paths:\n"); + call_and_print(&g_wiki, &weights_wiki, 2, 0, 5, IGRAPH_OUT); + + printf("Wiki example, other direction:\n"); + call_and_print(&g_wiki, &weights_wiki, 10, 5, 0, IGRAPH_IN); + + printf("Wiki example, direction ignored:\n"); + call_and_print(&g_wiki, &weights_wiki, 20, 5, 0, IGRAPH_ALL); + + printf("Wiki example, undirected:\n"); + call_and_print(&g_wiki_u, &weights_wiki, 20, 5, 0, IGRAPH_ALL); + + printf("Wiki example, no weights:\n"); + call_and_print(&g_wiki, NULL, 10, 0, 5, IGRAPH_OUT); + + printf("Directed unweighted graph:\n"); + call_and_print(&g_sz, NULL, 4, 0, 4, IGRAPH_OUT); + + printf("3x3 square lattice:\n"); + call_and_print(&g_lattice, NULL, 6, 0, 8, IGRAPH_OUT); + + printf("Zero vertices, from and to don't exist:\n"); + CHECK_ERROR(igraph_get_k_shortest_paths(&g_0, &weights, NULL, &paths, 4, 0, 0, IGRAPH_ALL), IGRAPH_EINVVID); + + printf("Wrong weights length:\n"); + CHECK_ERROR(igraph_get_k_shortest_paths(&g_wiki, &weights, NULL, &paths, 4, 0, 5, IGRAPH_ALL), IGRAPH_EINVAL); + + printf("Non-existent mode:\n"); + CHECK_ERROR(igraph_get_k_shortest_paths(&g_1, &weights, NULL, &paths, 4, 0, 0, (igraph_neimode_t) 100), IGRAPH_EINVMODE); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_2); + igraph_destroy(&g_2c); + igraph_destroy(&g_wiki); + igraph_destroy(&g_wiki_u); + igraph_destroy(&g_sz); + igraph_destroy(&g_lattice); + igraph_vector_destroy(&weights); + igraph_vector_destroy(&weights_wiki); + igraph_vector_destroy(&weights_inf); + igraph_vector_int_list_destroy(&paths); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_k_shortest_paths.out b/tests/unit/igraph_get_k_shortest_paths.out new file mode 100644 index 0000000..3b6ab42 --- /dev/null +++ b/tests/unit/igraph_get_k_shortest_paths.out @@ -0,0 +1,219 @@ +One vertex: +result (vertex IDs): +{ + 0: ( 0 ) +} +result (edge IDs): +{ + 0: ( ) +} + +Two disconnected vertices: +result (vertex IDs): +{ +} +result (edge IDs): +{ +} + +Two vertices with infinite weight edge in between: +result (vertex IDs): +{ +} +result (edge IDs): +{ +} + +Wiki example: +result (vertex IDs): +{ + 0: ( 0 2 3 5 ) + 1: ( 0 2 4 5 ) + 2: ( 0 1 3 5 ) + 3: ( 0 2 1 3 5 ) + 4: ( 0 2 3 4 5 ) + 5: ( 0 1 3 4 5 ) + 6: ( 0 2 1 3 4 5 ) +} +result (edge IDs): +{ + 0: ( 1 4 7 ) + 1: ( 1 5 8 ) + 2: ( 0 2 7 ) + 3: ( 1 3 2 7 ) + 4: ( 1 4 6 8 ) + 5: ( 0 2 6 8 ) + 6: ( 1 3 2 6 8 ) +} + +Wiki example, 0 shortest paths: +result (vertex IDs): +{ +} +result (edge IDs): +{ +} + +Wiki example, 2 shortest paths: +result (vertex IDs): +{ + 0: ( 0 2 3 5 ) + 1: ( 0 2 4 5 ) +} +result (edge IDs): +{ + 0: ( 1 4 7 ) + 1: ( 1 5 8 ) +} + +Wiki example, other direction: +result (vertex IDs): +{ + 0: ( 5 3 2 0 ) + 1: ( 5 4 2 0 ) + 2: ( 5 3 1 0 ) + 3: ( 5 4 3 2 0 ) + 4: ( 5 3 1 2 0 ) + 5: ( 5 4 3 1 0 ) + 6: ( 5 4 3 1 2 0 ) +} +result (edge IDs): +{ + 0: ( 7 4 1 ) + 1: ( 8 5 1 ) + 2: ( 7 2 0 ) + 3: ( 8 6 4 1 ) + 4: ( 7 2 3 1 ) + 5: ( 8 6 2 0 ) + 6: ( 8 6 2 3 1 ) +} + +Wiki example, direction ignored: +result (vertex IDs): +{ + 0: ( 5 3 2 0 ) + 1: ( 5 4 2 0 ) + 2: ( 5 3 2 1 0 ) + 3: ( 5 3 1 0 ) + 4: ( 5 4 3 2 0 ) + 5: ( 5 3 1 2 0 ) + 6: ( 5 3 4 2 0 ) + 7: ( 5 4 2 1 0 ) + 8: ( 5 3 4 2 1 0 ) + 9: ( 5 4 3 2 1 0 ) + 10: ( 5 4 3 1 0 ) + 11: ( 5 4 3 1 2 0 ) + 12: ( 5 4 2 3 1 0 ) +} +result (edge IDs): +{ + 0: ( 7 4 1 ) + 1: ( 8 5 1 ) + 2: ( 7 4 3 0 ) + 3: ( 7 2 0 ) + 4: ( 8 6 4 1 ) + 5: ( 7 2 3 1 ) + 6: ( 7 6 5 1 ) + 7: ( 8 5 3 0 ) + 8: ( 7 6 5 3 0 ) + 9: ( 8 6 4 3 0 ) + 10: ( 8 6 2 0 ) + 11: ( 8 6 2 3 1 ) + 12: ( 8 5 4 2 0 ) +} + +Wiki example, undirected: +result (vertex IDs): +{ + 0: ( 5 3 2 0 ) + 1: ( 5 4 2 0 ) + 2: ( 5 3 2 1 0 ) + 3: ( 5 3 1 0 ) + 4: ( 5 4 3 2 0 ) + 5: ( 5 3 1 2 0 ) + 6: ( 5 3 4 2 0 ) + 7: ( 5 4 2 1 0 ) + 8: ( 5 3 4 2 1 0 ) + 9: ( 5 4 3 2 1 0 ) + 10: ( 5 4 3 1 0 ) + 11: ( 5 4 3 1 2 0 ) + 12: ( 5 4 2 3 1 0 ) +} +result (edge IDs): +{ + 0: ( 7 4 1 ) + 1: ( 8 5 1 ) + 2: ( 7 4 3 0 ) + 3: ( 7 2 0 ) + 4: ( 8 6 4 1 ) + 5: ( 7 2 3 1 ) + 6: ( 7 6 5 1 ) + 7: ( 8 5 3 0 ) + 8: ( 7 6 5 3 0 ) + 9: ( 8 6 4 3 0 ) + 10: ( 8 6 2 0 ) + 11: ( 8 6 2 3 1 ) + 12: ( 8 5 4 2 0 ) +} + +Wiki example, no weights: +result (vertex IDs): +{ + 0: ( 0 1 3 5 ) + 1: ( 0 2 4 5 ) + 2: ( 0 2 3 5 ) + 3: ( 0 1 3 4 5 ) + 4: ( 0 2 3 4 5 ) + 5: ( 0 2 1 3 5 ) + 6: ( 0 2 1 3 4 5 ) +} +result (edge IDs): +{ + 0: ( 0 2 7 ) + 1: ( 1 5 8 ) + 2: ( 1 4 7 ) + 3: ( 0 2 6 8 ) + 4: ( 1 4 6 8 ) + 5: ( 1 3 2 7 ) + 6: ( 1 3 2 6 8 ) +} + +Directed unweighted graph: +result (vertex IDs): +{ + 0: ( 0 4 ) + 1: ( 0 3 4 ) + 2: ( 0 1 2 4 ) + 3: ( 0 1 2 4 ) +} +result (edge IDs): +{ + 0: ( 9 ) + 1: ( 4 5 ) + 2: ( 0 2 8 ) + 3: ( 0 1 8 ) +} + +3x3 square lattice: +result (vertex IDs): +{ + 0: ( 0 1 2 5 8 ) + 1: ( 0 3 4 5 8 ) + 2: ( 0 1 4 7 8 ) + 3: ( 0 3 4 7 8 ) + 4: ( 0 1 4 5 8 ) + 5: ( 0 3 6 7 8 ) +} +result (edge IDs): +{ + 0: ( 0 2 4 9 ) + 1: ( 1 5 7 9 ) + 2: ( 0 3 8 11 ) + 3: ( 1 5 8 11 ) + 4: ( 0 3 7 9 ) + 5: ( 1 6 10 11 ) +} + +Zero vertices, from and to don't exist: +Wrong weights length: +Non-existent mode: diff --git a/tests/unit/igraph_get_laplacian.c b/tests/unit/igraph_get_laplacian.c new file mode 100644 index 0000000..09e8cbb --- /dev/null +++ b/tests/unit/igraph_get_laplacian.c @@ -0,0 +1,116 @@ +/* -*- 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, see . +*/ + +#include +#include "test_utilities.h" + +void test_laplacian(igraph_t *g, const igraph_vector_t *w, igraph_bool_t dir, igraph_laplacian_normalization_t normalization) { + igraph_matrix_t m, m_converted_sparse; + igraph_sparsemat_t m_sparse; + + igraph_matrix_init(&m, 0, 0); + igraph_matrix_init(&m_converted_sparse, 0, 0); + igraph_sparsemat_init(&m_sparse, 0, 0, 0); + + igraph_get_laplacian(g, &m, IGRAPH_OUT, normalization, w); + igraph_get_laplacian_sparse(g, &m_sparse, IGRAPH_OUT, normalization, w); + igraph_matrix_print(&m); + igraph_sparsemat_as_matrix(&m_converted_sparse, &m_sparse); + IGRAPH_ASSERT(igraph_matrix_all_almost_e(&m, &m_converted_sparse, 1e-15)); + + igraph_matrix_destroy(&m); + igraph_matrix_destroy(&m_converted_sparse); + igraph_sparsemat_destroy(&m_sparse); +} + +int main(void) { + igraph_t g_e_un, g_e_dir, g_un, g_dir; + igraph_vector_t weights, weights_e; + char *n[] = {"none", "symmetric", "left", "right"}; + + igraph_vector_init_int(&weights, 9, 1, 2, 3, 4, 5, 2, 2, 3, 3); + igraph_vector_init_int(&weights_e, 0); + igraph_small(&g_e_un, 0, IGRAPH_UNDIRECTED, -1); + igraph_small(&g_e_dir, 0, IGRAPH_DIRECTED, -1); + igraph_small(&g_un, 6, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, 3,4, 4,0, 1,1, 2,2, 1,2, 3,4, -1); + igraph_small(&g_dir, 6, IGRAPH_DIRECTED, 0,1, 1,2, 2,3, 3,4, 4,0, 1,1, 2,2, 1,2, 3,4, -1); + + for (int normalization = 0; normalization < 4; normalization++) { + for (int weighted = 0; weighted < 2; weighted++) { + for (int directed = 0; directed < 2; directed++) { + printf("=== normalization: %s, %sweighted, %sdirected\n", + n[normalization], + (weighted ? "" : "un"), + (directed ? "" : "un") + ); + + test_laplacian(directed ? &g_e_dir : &g_e_un, weighted ? &weights_e : NULL, directed, (igraph_laplacian_normalization_t) normalization); + test_laplacian(directed ? &g_dir : &g_un, weighted ? &weights : NULL, directed, (igraph_laplacian_normalization_t) normalization); + } + } + } + + igraph_vector_destroy(&weights_e); + igraph_destroy(&g_e_un); + igraph_destroy(&g_e_dir); + igraph_vector_destroy(&weights); + igraph_destroy(&g_un); + igraph_destroy(&g_dir); + + VERIFY_FINALLY_STACK() + + { + igraph_t g; + igraph_matrix_t m; + igraph_sparsemat_t m_sparse; + + igraph_matrix_init(&m, 0, 0); + igraph_sparsemat_init(&m_sparse, 0, 0, 0); + + printf("Check errors for zero out/in and non-zero in/out degree.\n"); + /* For IGRAPH_OUT, SYMMETRIC and RIGHT should fail, + for IGRAPH_IN, SYMMETRIC and LEFT should fail. */ + + igraph_small(&g, 2, IGRAPH_DIRECTED, 0,1, -1); + for (int mode_n = 0; mode_n < 2; mode_n++) { + for (int lsr = mode_n; lsr < 2 + mode_n; lsr++) { + igraph_neimode_t mode = mode_n ? IGRAPH_OUT : IGRAPH_IN; + igraph_laplacian_normalization_t normalization; + if (lsr == 0) { + normalization = IGRAPH_LAPLACIAN_LEFT; + } else if (lsr == 1) { + normalization = IGRAPH_LAPLACIAN_SYMMETRIC; + } else { + normalization = IGRAPH_LAPLACIAN_RIGHT; + } + CHECK_ERROR(igraph_get_laplacian(&g, &m, mode, + normalization, NULL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_get_laplacian_sparse(&g, &m_sparse, mode, + normalization, NULL), IGRAPH_EINVAL);; + } + } + igraph_destroy(&g); + igraph_matrix_destroy(&m); + igraph_sparsemat_destroy(&m_sparse); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_get_laplacian.out b/tests/unit/igraph_get_laplacian.out new file mode 100644 index 0000000..51f3b23 --- /dev/null +++ b/tests/unit/igraph_get_laplacian.out @@ -0,0 +1,113 @@ +=== normalization: none, unweighted, undirected + 2 -1 0 0 -1 0 +-1 3 -2 0 0 0 + 0 -2 3 -1 0 0 + 0 0 -1 3 -2 0 +-1 0 0 -2 3 0 + 0 0 0 0 0 0 +=== normalization: none, unweighted, directed + 1 -1 0 0 0 0 + 0 2 -2 0 0 0 + 0 0 1 -1 0 0 + 0 0 0 2 -2 0 +-1 0 0 0 1 0 + 0 0 0 0 0 0 +=== normalization: none, weighted, undirected + 6 -1 0 0 -5 0 +-1 6 -5 0 0 0 + 0 -5 8 -3 0 0 + 0 0 -3 10 -7 0 +-5 0 0 -7 12 0 + 0 0 0 0 0 0 +=== normalization: none, weighted, directed + 1 -1 0 0 0 0 + 0 5 -5 0 0 0 + 0 0 3 -3 0 0 + 0 0 0 7 -7 0 +-5 0 0 0 5 0 + 0 0 0 0 0 0 +=== normalization: symmetric, unweighted, undirected + 1 -0.316228 0 0 -0.408248 0 +-0.316228 0.6 -0.4 0 0 0 + 0 -0.4 0.6 -0.258199 0 0 + 0 0 -0.258199 1 -0.666667 0 +-0.408248 0 0 -0.666667 1 0 + 0 0 0 0 0 0 +=== normalization: symmetric, unweighted, directed + 1 -0.57735 0 0 0 0 + 0 0.666667 -0.816497 0 0 0 + 0 0 0.5 -0.5 0 0 + 0 0 0 1 -1.41421 0 +-1 0 0 0 1 0 + 0 0 0 0 0 0 +=== normalization: symmetric, weighted, undirected + 1 -0.129099 0 0 -0.589256 0 +-0.129099 0.6 -0.456435 0 0 0 + 0 -0.456435 0.666667 -0.273861 0 0 + 0 0 -0.273861 1 -0.63901 0 +-0.589256 0 0 -0.63901 1 0 + 0 0 0 0 0 0 +=== normalization: symmetric, weighted, directed + 1 -0.377964 0 0 0 0 + 0 0.714286 -0.845154 0 0 0 + 0 0 0.6 -0.507093 0 0 + 0 0 0 1 -1.18322 0 +-2.23607 0 0 0 1 0 + 0 0 0 0 0 0 +=== normalization: left, unweighted, undirected + 1 -0.5 0 0 -0.5 0 + -0.2 0.6 -0.4 0 0 0 + 0 -0.4 0.6 -0.2 0 0 + 0 0 -0.333333 1 -0.666667 0 +-0.333333 0 0 -0.666667 1 0 + 0 0 0 0 0 0 +=== normalization: left, unweighted, directed + 1 -1 0 0 0 0 + 0 0.666667 -0.666667 0 0 0 + 0 0 0.5 -0.5 0 0 + 0 0 0 1 -1 0 +-1 0 0 0 1 0 + 0 0 0 0 0 0 +=== normalization: left, weighted, undirected + 1 -0.166667 0 0 -0.833333 0 + -0.1 0.6 -0.5 0 0 0 + 0 -0.416667 0.666667 -0.25 0 0 + 0 0 -0.3 1 -0.7 0 +-0.416667 0 0 -0.583333 1 0 + 0 0 0 0 0 0 +=== normalization: left, weighted, directed + 1 -1 0 0 0 0 + 0 0.714286 -0.714286 0 0 0 + 0 0 0.6 -0.6 0 0 + 0 0 0 1 -1 0 +-1 0 0 0 1 0 + 0 0 0 0 0 0 +=== normalization: right, unweighted, undirected + 1 -0.2 0 0 -0.333333 0 +-0.5 0.6 -0.4 0 0 0 + 0 -0.4 0.6 -0.333333 0 0 + 0 0 -0.2 1 -0.666667 0 +-0.5 0 0 -0.666667 1 0 + 0 0 0 0 0 0 +=== normalization: right, unweighted, directed + 1 -0.333333 0 0 0 0 + 0 0.666667 -1 0 0 0 + 0 0 0.5 -0.5 0 0 + 0 0 0 1 -2 0 +-1 0 0 0 1 0 + 0 0 0 0 0 0 +=== normalization: right, weighted, undirected + 1 -0.1 0 0 -0.416667 0 +-0.166667 0.6 -0.416667 0 0 0 + 0 -0.5 0.666667 -0.3 0 0 + 0 0 -0.25 1 -0.583333 0 +-0.833333 0 0 -0.7 1 0 + 0 0 0 0 0 0 +=== normalization: right, weighted, directed + 1 -0.142857 0 0 0 0 + 0 0.714286 -1 0 0 0 + 0 0 0.6 -0.428571 0 0 + 0 0 0 1 -1.4 0 +-5 0 0 0 1 0 + 0 0 0 0 0 0 +Check errors for zero out/in and non-zero in/out degree. diff --git a/tests/unit/igraph_get_shortest_path_astar.c b/tests/unit/igraph_get_shortest_path_astar.c new file mode 100644 index 0000000..d866168 --- /dev/null +++ b/tests/unit/igraph_get_shortest_path_astar.c @@ -0,0 +1,231 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +#define LENGTH 10 + +int check_edges(const igraph_t *graph, const igraph_vector_int_t *vertices, + const igraph_vector_int_t *edges, int error_code) { + + igraph_bool_t directed = igraph_is_directed(graph); + + igraph_integer_t j, n2 = igraph_vector_int_size(edges); + if (igraph_vector_int_size(vertices) == 0 && n2 == 0) { + return 0; + } + if (igraph_vector_int_size(vertices) != n2 + 1) { + exit(error_code + 2); + } + for (j = 0; j < n2; j++) { + igraph_integer_t edge = VECTOR(*edges)[j]; + igraph_integer_t from = VECTOR(*vertices)[j]; + igraph_integer_t to = VECTOR(*vertices)[j + 1]; + if (directed) { + if (from != IGRAPH_FROM(graph, edge) || + to != IGRAPH_TO (graph, edge)) { + exit(error_code); + } + } else { + igraph_integer_t from2 = IGRAPH_FROM(graph, edge); + igraph_integer_t to2 = IGRAPH_TO(graph, edge); + igraph_integer_t min1 = from < to ? from : to; + igraph_integer_t max1 = from < to ? to : from; + igraph_integer_t min2 = from2 < to2 ? from2 : to2; + igraph_integer_t max2 = from2 < to2 ? to2 : from2; + if (min1 != min2 || max1 != max2) { + exit(error_code + 3); + } + } + } + + return 0; +} + +igraph_error_t lattice_heuristic(igraph_real_t *result, igraph_integer_t source_id, igraph_integer_t target_id, void *extra) { + int x[4]; + for (int i = 0; i < 4; i++) { + x[i] = source_id % LENGTH; + source_id /= LENGTH; + } + *result = abs(LENGTH - 1 - x[0]) + x[1] + x[2] + x[3]; + return IGRAPH_SUCCESS; +} + +struct xyt { + igraph_vector_t x; + igraph_vector_t y; +}; + +igraph_error_t euclidean_heuristic(igraph_real_t *result, igraph_integer_t source_id, igraph_integer_t target_id, void *extra) { + struct xyt *xyp = extra; + igraph_real_t xt, xf, yt, yf; + xt = VECTOR(xyp->x)[target_id]; + yt = VECTOR(xyp->y)[target_id]; + xf = VECTOR(xyp->x)[source_id]; + yf = VECTOR(xyp->y)[source_id]; + *result = sqrt((xt-xf)*(xt-xf) + (yt-yf)*(yt-yf)); + return IGRAPH_SUCCESS; +} + +int main(void) { + + igraph_t g; + igraph_vector_int_t vertices, edges; + igraph_real_t weights[] = { 1, 2, 3, 4, 5, 1, 1, 1, 1, 1 }; + igraph_vector_t weights_vec; + struct xyt xy; + igraph_vector_int_t dimvector; + igraph_integer_t dims[] = {LENGTH, LENGTH, LENGTH, LENGTH}; + + //set seed for grg random graph generation + igraph_rng_seed(igraph_rng_default(), 42); + + /* Simple ring graph without weights */ + + + igraph_vector_int_init(&vertices, 0); + igraph_vector_int_init(&edges, 0); + + + printf("Astar on singleton, unweighted, no heuristic:\n"); + igraph_small(&g, 1, IGRAPH_UNDIRECTED, -1); + igraph_get_shortest_path_astar(&g, /*vertices=*/ &vertices, + /*edges=*/ &edges, /*from=*/ 0, /*to=*/ 0, + /*weights=*/ NULL, /*mode=*/ IGRAPH_OUT, + /*heuristic=*/ NULL, NULL); + + check_edges(&g, &vertices, &edges, /*error code*/10); + + igraph_vector_int_print(&vertices); + + igraph_destroy(&g); + + printf("Astar on ring, unweighted, no heuristic:\n"); + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, 0, 1); + igraph_get_shortest_path_astar(&g, /*vertices=*/ &vertices, + /*edges=*/ &edges, /*from=*/ 0, /*to=*/ 5, + /*weights=*/ NULL, /*mode=*/ IGRAPH_OUT, + /*heuristic=*/ NULL, NULL); + + check_edges(&g, &vertices, &edges, /*error code*/10); + + igraph_vector_int_print(&vertices); + + /* Same ring, but with weights */ + + printf("Astar, weighted, no heuristic:\n"); + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + igraph_get_shortest_path_astar(&g, /*vertices=*/ &vertices, + /*edges=*/ &edges, /*from=*/ 0, /*to=*/ 5, + &weights_vec, IGRAPH_OUT, + /*heuristic=*/ NULL, NULL); + + check_edges(&g, &vertices, &edges, 20); + + igraph_vector_int_print(&vertices); + + igraph_destroy(&g); + + printf("Astar, unweighted, lattice with manhattan distance heuristic:\n"); + igraph_vector_int_view(&dimvector, dims, sizeof(dims)/sizeof(dims[0])); + + igraph_square_lattice(&g, &dimvector, /*nei*/ 1, IGRAPH_UNDIRECTED, /*mutual*/ false, /*periodic*/NULL); + + //print_graph_canon(&g); + igraph_get_shortest_path_astar(&g, /*vertices=*/ &vertices, + /*edges=*/ &edges, /*from=*/ 0, /*to=*/ LENGTH-1, + /*weights*/NULL, IGRAPH_OUT, + lattice_heuristic, NULL); + igraph_vector_int_print(&vertices); + check_edges(&g, &vertices, &edges, /*error code*/60); + + igraph_destroy(&g); + + printf("Astar, unweighted, grg with euclidean distance heuristic:\n"); + igraph_vector_init(&xy.x, 0); + igraph_vector_init(&xy.y, 0); + + igraph_grg_game(&g, /*nodes*/100, /*radius*/0.2, /*torus*/ false, &xy.x, &xy.y); + igraph_vector_init(&weights_vec, igraph_ecount(&g)); + + for (int i = 0; i < igraph_ecount(&g); i++) { + igraph_real_t xt, xf, yt, yf; + xt = VECTOR(xy.x)[IGRAPH_TO(&g, i)]; + xf = VECTOR(xy.x)[IGRAPH_FROM(&g, i)]; + yt = VECTOR(xy.y)[IGRAPH_TO(&g, i)]; + yf = VECTOR(xy.y)[IGRAPH_FROM(&g, i)]; + VECTOR(weights_vec)[i] = sqrt((xt-xf)*(xt-xf) + (yt-yf)*(yt-yf)); + } + + igraph_get_shortest_path_astar(&g, /*vertices=*/ &vertices, + /*edges=*/ &edges, /*from=*/ 0, /*to=*/ LENGTH-1, + /*weights*/&weights_vec, IGRAPH_OUT, + euclidean_heuristic, &xy); + + igraph_vector_int_print(&vertices); + check_edges(&g, &vertices, &edges, /*error code*/80); + + printf("Check with dijkstra:\n"); + igraph_get_shortest_path_dijkstra(&g, /*vertices=*/ &vertices, + /*edges=*/ &edges, /*from=*/ 0, /*to=*/ LENGTH-1, + /*weights*/&weights_vec, IGRAPH_OUT); + + igraph_vector_int_print(&vertices); + + printf("Same situation but through shortest_path_astar interface:\n"); + igraph_get_shortest_path_astar(&g, &vertices, + &edges, /*from=*/ 0, /*to=*/ LENGTH - 1, + /*weights*/&weights_vec, IGRAPH_OUT, + euclidean_heuristic, &xy); + + igraph_vector_int_print(&vertices); + + printf("Checking from out of range.\n"); + igraph_destroy(&g); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0, 1, -1); + CHECK_ERROR(igraph_get_shortest_path_astar(&g, NULL, NULL, 10, 1, NULL, IGRAPH_ALL, NULL, NULL), IGRAPH_EINVVID); + + printf("Checking to out of range.\n"); + igraph_destroy(&g); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0, 1, -1); + CHECK_ERROR(igraph_get_shortest_path_astar(&g, NULL, NULL, 0, 10, NULL, IGRAPH_ALL, NULL, NULL), IGRAPH_EINVVID); + + printf("Checking wrong weight length error.\n"); + igraph_vector_destroy(&weights_vec); + igraph_vector_init_int(&weights_vec, 0); + CHECK_ERROR(igraph_get_shortest_path_astar(&g, NULL, NULL, 0, 1, &weights_vec, IGRAPH_ALL, NULL, NULL), IGRAPH_EINVAL); + + printf("Checking negative weight error.\n"); + igraph_vector_destroy(&weights_vec); + igraph_vector_init_int(&weights_vec, 1, -1); + CHECK_ERROR(igraph_get_shortest_path_astar(&g, NULL, NULL, 0, 1, &weights_vec, IGRAPH_ALL, NULL, NULL), IGRAPH_EINVAL); + + igraph_vector_int_destroy(&vertices); + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&vertices); + igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&weights_vec); + igraph_vector_destroy(&xy.x); + igraph_vector_destroy(&xy.y); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_shortest_path_astar.out b/tests/unit/igraph_get_shortest_path_astar.out new file mode 100644 index 0000000..fda6a83 --- /dev/null +++ b/tests/unit/igraph_get_shortest_path_astar.out @@ -0,0 +1,18 @@ +Astar on singleton, unweighted, no heuristic: +0 +Astar on ring, unweighted, no heuristic: +0 1 2 3 4 5 +Astar, weighted, no heuristic: +0 9 8 7 6 5 +Astar, unweighted, lattice with manhattan distance heuristic: +0 1 2 3 4 5 6 7 8 9 +Astar, unweighted, grg with euclidean distance heuristic: +0 13 17 24 20 9 +Check with dijkstra: +0 13 17 24 20 9 +Same situation but through shortest_path_astar interface: +0 13 17 24 20 9 +Checking from out of range. +Checking to out of range. +Checking wrong weight length error. +Checking negative weight error. diff --git a/tests/unit/igraph_get_shortest_path_bellman_ford.c b/tests/unit/igraph_get_shortest_path_bellman_ford.c new file mode 100644 index 0000000..8302c71 --- /dev/null +++ b/tests/unit/igraph_get_shortest_path_bellman_ford.c @@ -0,0 +1,61 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + + +int main(void) { + /*most functionality is currently tested in + igraph_get_shortest_paths_bellman_ford(), which is called by + igraph_get_shortest_path_bellman_ford()*/ + igraph_t g; + igraph_vector_int_t vertices, edges; + + igraph_vector_int_init(&vertices, 0); + igraph_vector_int_init(&edges, 0); + + printf("Basic example, don't ask for vertices and edges.\n"); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + igraph_get_shortest_path_bellman_ford(&g, NULL, NULL, + 0, 1, NULL, IGRAPH_OUT); + + printf("Basic example, ask for vertices and edges:\n"); + igraph_get_shortest_path_bellman_ford(&g, &vertices, &edges, + 0, 1, NULL, IGRAPH_OUT); + printf("vertices: "); + print_vector_int(&vertices); + printf("edges: "); + print_vector_int(&edges); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + printf("Check error when passing null graph.\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + CHECK_ERROR( + igraph_get_shortest_path_bellman_ford(&g, NULL, NULL, 0, 0, NULL, IGRAPH_OUT), + IGRAPH_EINVVID); + + igraph_vector_int_destroy(&vertices); + igraph_vector_int_destroy(&edges); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_shortest_path_bellman_ford.out b/tests/unit/igraph_get_shortest_path_bellman_ford.out new file mode 100644 index 0000000..f063046 --- /dev/null +++ b/tests/unit/igraph_get_shortest_path_bellman_ford.out @@ -0,0 +1,5 @@ +Basic example, don't ask for vertices and edges. +Basic example, ask for vertices and edges: +vertices: ( 0 1 ) +edges: ( 0 ) +Check error when passing null graph. diff --git a/tests/unit/igraph_get_shortest_paths2.c b/tests/unit/igraph_get_shortest_paths2.c new file mode 100644 index 0000000..d60f5a7 --- /dev/null +++ b/tests/unit/igraph_get_shortest_paths2.c @@ -0,0 +1,73 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + const igraph_integer_t edges[] = { 0, 1, 0, 2, 1, 6, 2, 6, 1, 3, 1, 4, 1, 5, + 3, 2, 4, 2, 5, 2 + }; + igraph_t g; + igraph_vector_int_t edgev; + igraph_vector_int_list_t resvertices, resedges; + igraph_vector_int_t parents, inbound_edges; + igraph_integer_t vcount, i; + + igraph_vector_int_view(&edgev, edges, sizeof(edges) / sizeof(edges[0])); + vcount = igraph_vector_int_max(&edgev) + 1; + igraph_create(&g, &edgev, vcount, IGRAPH_DIRECTED); + + igraph_vector_int_list_init(&resvertices, 0); + igraph_vector_int_list_init(&resedges, 0); + igraph_vector_int_init(&parents, 0); + igraph_vector_int_init(&inbound_edges, 0); + + igraph_get_shortest_paths(&g, &resvertices, &resedges, /*from=*/ 0, + /*to=*/ igraph_vss_all(), /*mode=*/ IGRAPH_OUT, + &parents, &inbound_edges); + + for (i = 0; i < vcount; i++) { + igraph_vector_int_t *v1 = igraph_vector_int_list_get_ptr(&resvertices, i); + igraph_vector_int_t *v2 = igraph_vector_int_list_get_ptr(&resedges, i); + printf("%" IGRAPH_PRId " V: ", i); + igraph_vector_int_print(v1); + printf("%" IGRAPH_PRId " E: ", i); + igraph_vector_int_print(v2); + } + printf("pred: "); + igraph_vector_int_print(&parents); + printf("inbe: "); + igraph_vector_int_print(&inbound_edges); + + igraph_vector_int_destroy(&inbound_edges); + igraph_vector_int_destroy(&parents); + igraph_vector_int_list_destroy(&resedges); + igraph_vector_int_list_destroy(&resvertices); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_get_shortest_paths2.out b/tests/unit/igraph_get_shortest_paths2.out new file mode 100644 index 0000000..9a7213f --- /dev/null +++ b/tests/unit/igraph_get_shortest_paths2.out @@ -0,0 +1,16 @@ +0 V: 0 +0 E: +1 V: 0 1 +1 E: 0 +2 V: 0 2 +2 E: 1 +3 V: 0 1 3 +3 E: 0 4 +4 V: 0 1 4 +4 E: 0 5 +5 V: 0 1 5 +5 E: 0 6 +6 V: 0 1 6 +6 E: 0 2 +pred: -1 0 0 1 1 1 1 +inbe: -1 0 1 4 5 6 2 diff --git a/tests/unit/igraph_get_shortest_paths_bellman_ford.c b/tests/unit/igraph_get_shortest_paths_bellman_ford.c new file mode 100644 index 0000000..378ecf9 --- /dev/null +++ b/tests/unit/igraph_get_shortest_paths_bellman_ford.c @@ -0,0 +1,215 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + (C) 2006-2021 The igraph development team 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include "test_utilities.h" +#include + +void check_evecs(const igraph_t *graph, const igraph_vector_int_list_t *vecs, + const igraph_vector_int_list_t *evecs) { + + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t i, n = igraph_vector_int_list_size(vecs); + + IGRAPH_ASSERT(igraph_vector_int_list_size(evecs) == n); + + for (i = 0; i < n; i++) { + igraph_vector_int_t *vvec = igraph_vector_int_list_get_ptr(vecs, i); + igraph_vector_int_t *evec = igraph_vector_int_list_get_ptr(evecs, i); + igraph_integer_t j, n2 = igraph_vector_int_size(evec); + if (igraph_vector_int_size(vvec) == 0 && n2 == 0) { + continue; + } + IGRAPH_ASSERT(igraph_vector_int_size(vvec) == n2 + 1); + + for (j = 0; j < n2; j++) { + igraph_integer_t edge = VECTOR(*evec)[j]; + igraph_integer_t from = VECTOR(*vvec)[j]; + igraph_integer_t to = VECTOR(*vvec)[j + 1]; + if (directed) { + IGRAPH_ASSERT(from == IGRAPH_FROM(graph, edge) && + to == IGRAPH_TO(graph, edge)); + } else { + igraph_integer_t from2 = IGRAPH_FROM(graph, edge); + igraph_integer_t to2 = IGRAPH_TO(graph, edge); + igraph_integer_t min1 = from < to ? from : to; + igraph_integer_t max1 = from < to ? to : from; + igraph_integer_t min2 = from2 < to2 ? from2 : to2; + igraph_integer_t max2 = from2 < to2 ? to2 : from2; + IGRAPH_ASSERT(min1 == min2 && max1 == max2); + } + } + } +} + +void check_parents_inbound(const igraph_t* graph, const igraph_vector_int_t* parents, + const igraph_vector_int_t* inbound, int start) { + + igraph_integer_t i, n = igraph_vcount(graph); + + IGRAPH_ASSERT(igraph_vector_int_size(parents) == n); + IGRAPH_ASSERT(igraph_vector_int_size(inbound) == n); + + IGRAPH_ASSERT(VECTOR(*parents)[start] == -1 && VECTOR(*inbound)[start] == -1); + + for (i = 0; i < n; i++) { + if (VECTOR(*parents)[i] == -2) { + IGRAPH_ASSERT(VECTOR(*inbound)[i] == -1); + + } else if (VECTOR(*parents)[i] == -1) { + IGRAPH_ASSERT(i == start); + + IGRAPH_ASSERT(VECTOR(*inbound)[i] == -1); + + } else { + igraph_integer_t eid = VECTOR(*inbound)[i]; + igraph_integer_t u = IGRAPH_FROM(graph, eid), v = IGRAPH_TO(graph, eid); + if (v != i && !igraph_is_directed(graph)) { + igraph_integer_t dummy = u; + u = v; + v = dummy; + } + IGRAPH_ASSERT(v == i); + IGRAPH_ASSERT(u == VECTOR(*parents)[i]); + } + } +} + +int main(void) { + igraph_t g; + igraph_vector_int_list_t vecs, evecs; + igraph_vector_int_t parents, inbound; + igraph_real_t weights_data_0[] = { 0, 2, 1, 0, 5, 2, 1, 1, 0, 2, 2, 8, 1, 1, 3, 1, 1, 4, 2, 1 }; + igraph_real_t weights_data_1[] = { 6, 7, 8, -4, -2, -3, 9, 2, 7 }; + igraph_real_t weights_data_2[] = { 6, 7, 2, -4, -2, -3, 9, 2, 7 }; + igraph_vector_t weights_vec; + igraph_vs_t vs; + igraph_integer_t vs_size; + + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 4, 1, 5, + 2, 3, 2, 6, 3, 2, 3, 6, + 4, 5, 4, 7, 5, 6, 5, 8, 5, 9, + 7, 5, 7, 8, 8, 9, + 5, 2, + 2, 1, + -1); + + igraph_vector_int_init(&parents, 0); + igraph_vector_int_init(&inbound, 0); + + printf("Paths to only some vertices\n"); + + igraph_vs_vector_small(&vs, 0, 1, 3, 5, 2, 1, -1); + igraph_vs_size(&g, &vs, &vs_size); + + igraph_vector_int_list_init(&vecs, 0); + igraph_vector_int_list_init(&evecs, 0); + + igraph_vector_view(&weights_vec, weights_data_0, sizeof(weights_data_0) / sizeof(weights_data_0[0])); + igraph_get_shortest_paths_bellman_ford(&g, /*vertices=*/ &vecs, /*edges=*/ &evecs, + /*from=*/ 0, /*to=*/ vs, + &weights_vec, IGRAPH_OUT, + &parents, + /*inbound_edges=*/ &inbound); + + check_evecs(&g, &vecs, &evecs); + check_parents_inbound(&g, &parents, &inbound, /* from= */ 0); + + print_vector_int_list(&vecs); + + printf("\nPaths to all vertices\n"); + + vs_size = igraph_vcount(&g); + + igraph_get_shortest_paths_bellman_ford(&g, /*vertices=*/ &vecs, /*edges=*/ &evecs, + /*from=*/ 0, /*to=*/ igraph_vss_all(), + &weights_vec, IGRAPH_OUT, + &parents, + /*inbound_edges=*/ &inbound); + + check_evecs(&g, &vecs, &evecs); + check_parents_inbound(&g, &parents, &inbound, /* from= */ 0); + + print_vector_int_list(&vecs); + + igraph_vector_int_list_destroy(&vecs); + igraph_vector_int_list_destroy(&evecs); + + igraph_vector_int_destroy(&parents); + igraph_vector_int_destroy(&inbound); + + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + + printf("\nGraph with negative weights\n"); + + /***************************************/ + + /* Graph with negative weights */ + + igraph_vector_int_list_init(&vecs, 0); + igraph_vector_int_list_init(&evecs, 0); + igraph_vector_int_init(&parents, 0); + igraph_vector_int_init(&inbound, 0); + + igraph_vs_vector_small(&vs, 0, 1, 3, 2, 1, -1); + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 0, 3, 1, 3, 1, 4, 2, 1, 3, 2, 3, 4, 4, 0, 4, 2, + -1); + + igraph_vector_view(&weights_vec, weights_data_1, sizeof(weights_data_1) / sizeof(weights_data_1[0])); + igraph_get_shortest_paths_bellman_ford(&g, /*vertices=*/ &vecs, /*edges=*/ &evecs, + /*from=*/ 0, /*to=*/ vs, + &weights_vec, IGRAPH_OUT, + &parents, + /*inbound_edges=*/ &inbound); + + check_evecs(&g, &vecs, &evecs); + check_parents_inbound(&g, &parents, &inbound, /* from= */ 0); + + print_vector_int_list(&vecs); + + /***************************************/ + + /* Same graph with negative loop */ + igraph_set_error_handler(igraph_error_handler_ignore); + igraph_vector_view(&weights_vec, weights_data_2, + sizeof(weights_data_2) / sizeof(weights_data_2[0])); + IGRAPH_ASSERT(igraph_get_shortest_paths_bellman_ford(&g, /*vertices=*/ &vecs, /*edges=*/ &evecs, + /*from=*/ 0, /*to=*/ vs, + &weights_vec, IGRAPH_OUT, + &parents, + /*inbound_edges=*/ &inbound) == IGRAPH_ENEGLOOP); + + igraph_vector_int_list_destroy(&vecs); + igraph_vector_int_list_destroy(&evecs); + igraph_vector_int_destroy(&parents); + igraph_vector_int_destroy(&inbound); + + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_shortest_paths_bellman_ford.out b/tests/unit/igraph_get_shortest_paths_bellman_ford.out new file mode 100644 index 0000000..19457f6 --- /dev/null +++ b/tests/unit/igraph_get_shortest_paths_bellman_ford.out @@ -0,0 +1,32 @@ +Paths to only some vertices +{ + 0: ( 0 ) + 1: ( 0 1 ) + 2: ( 0 3 ) + 3: ( 0 1 5 ) + 4: ( 0 1 2 ) + 5: ( 0 1 ) +} + +Paths to all vertices +{ + 0: ( 0 ) + 1: ( 0 1 ) + 2: ( 0 1 2 ) + 3: ( 0 3 ) + 4: ( 0 1 4 ) + 5: ( 0 1 5 ) + 6: ( 0 1 2 6 ) + 7: ( 0 1 4 7 ) + 8: ( 0 1 5 8 ) + 9: ( 0 1 5 9 ) +} + +Graph with negative weights +{ + 0: ( 0 ) + 1: ( 0 3 2 1 ) + 2: ( 0 3 ) + 3: ( 0 3 2 ) + 4: ( 0 3 2 1 ) +} diff --git a/tests/unit/igraph_get_shortest_paths_dijkstra.c b/tests/unit/igraph_get_shortest_paths_dijkstra.c new file mode 100644 index 0000000..ef3be5e --- /dev/null +++ b/tests/unit/igraph_get_shortest_paths_dijkstra.c @@ -0,0 +1,172 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int check_evecs(const igraph_t *graph, const igraph_vector_int_list_t *vecs, + const igraph_vector_int_list_t *evecs, int error_code) { + + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t i, n = igraph_vector_int_list_size(vecs); + if (igraph_vector_int_list_size(evecs) != n) { + exit(error_code + 1); + } + + for (i = 0; i < n; i++) { + igraph_vector_int_t *vvec = igraph_vector_int_list_get_ptr(vecs, i); + igraph_vector_int_t *evec = igraph_vector_int_list_get_ptr(evecs, i); + igraph_integer_t j, n2 = igraph_vector_int_size(evec); + if (igraph_vector_int_size(vvec) == 0 && n2 == 0) { + continue; + } + if (igraph_vector_int_size(vvec) != n2 + 1) { + exit(error_code + 2); + } + for (j = 0; j < n2; j++) { + igraph_integer_t edge = VECTOR(*evec)[j]; + igraph_integer_t from = VECTOR(*vvec)[j]; + igraph_integer_t to = VECTOR(*vvec)[j + 1]; + if (directed) { + if (from != IGRAPH_FROM(graph, edge) || + to != IGRAPH_TO (graph, edge)) { + exit(error_code); + } + } else { + igraph_integer_t from2 = IGRAPH_FROM(graph, edge); + igraph_integer_t to2 = IGRAPH_TO(graph, edge); + igraph_integer_t min1 = from < to ? from : to; + igraph_integer_t max1 = from < to ? to : from; + igraph_integer_t min2 = from2 < to2 ? from2 : to2; + igraph_integer_t max2 = from2 < to2 ? to2 : from2; + if (min1 != min2 || max1 != max2) { + exit(error_code + 3); + } + } + } + } + + return 0; +} + +int check_parents_inbound(const igraph_t* graph, const igraph_vector_int_t* parents, + const igraph_vector_int_t* inbound, int start, int error_code) { + igraph_integer_t i, n = igraph_vcount(graph); + + if (igraph_vector_int_size(parents) != n || + igraph_vector_int_size(inbound) != n) { + exit(error_code); + } + + if (VECTOR(*parents)[start] != -1 || VECTOR(*inbound)[start] != -1) { + printf("%" IGRAPH_PRId "\n", VECTOR(*parents)[start]); + printf("%" IGRAPH_PRId "\n", VECTOR(*inbound)[start]); + exit(error_code + 1); + } + + for (i = 0; i < n; i++) { + if (VECTOR(*parents)[i] == -2) { + if (VECTOR(*inbound)[i] != -1) { + exit(error_code + 2); + } + } else if (VECTOR(*parents)[i] == -1) { + if (i != start) { + exit(error_code + 3); + } + if (VECTOR(*inbound)[i] != -1) { + exit(error_code + 4); + } + } else { + igraph_integer_t eid = VECTOR(*inbound)[i]; + igraph_integer_t u = IGRAPH_FROM(graph, eid), v = IGRAPH_TO(graph, eid); + if (v != i && !igraph_is_directed(graph)) { + igraph_integer_t dummy = u; + u = v; + v = dummy; + } + if (v != i) { + exit(error_code + 5); + } else if (u != VECTOR(*parents)[i]) { + exit(error_code + 6); + } + } + } + + return 0; +} + +int main(void) { + + igraph_t g; + igraph_vector_int_list_t vecs, evecs; + igraph_vector_int_t parents, inbound; + igraph_integer_t i; + igraph_real_t weights[] = { 1, 2, 3, 4, 5, 1, 1, 1, 1, 1 }; + igraph_vector_t weights_vec; + igraph_vs_t vs; + + /* Simple ring graph without weights */ + + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, 0, 1); + + igraph_vector_int_list_init(&vecs, 0); + igraph_vector_int_list_init(&evecs, 0); + igraph_vector_int_init(&parents, 0); + igraph_vector_int_init(&inbound, 0); + + igraph_vs_vector_small(&vs, 0, 1, 3, 5, 2, 1, -1); + + igraph_get_shortest_paths_dijkstra(&g, /*vertices=*/ &vecs, + /*edges=*/ &evecs, /*from=*/ 0, /*to=*/ vs, + /*weights=*/ 0, /*mode=*/ IGRAPH_OUT, + &parents, + /*inbound_edges=*/ &inbound); + + check_evecs(&g, &vecs, &evecs, 10); + check_parents_inbound(&g, &parents, &inbound, /* from= */ 0, 40); + + for (i = 0; i < igraph_vector_int_list_size(&vecs); i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&vecs, i)); + } + + /* Same ring, but with weights */ + + igraph_vector_view(&weights_vec, weights, sizeof(weights) / sizeof(weights[0])); + igraph_get_shortest_paths_dijkstra(&g, /*vertices=*/ &vecs, + /*edges=*/ &evecs, /*from=*/ 0, /*to=*/ vs, + &weights_vec, IGRAPH_OUT, + &parents, + /*inbound_edges=*/ &inbound); + + check_evecs(&g, &vecs, &evecs, 20); + check_parents_inbound(&g, &parents, &inbound, /* from= */ 0, 50); + + for (i = 0; i < igraph_vector_int_list_size(&vecs); i++) { + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&vecs, i)); + } + + igraph_vector_int_list_destroy(&vecs); + igraph_vector_int_list_destroy(&evecs); + igraph_vector_int_destroy(&parents); + igraph_vector_int_destroy(&inbound); + + igraph_vs_destroy(&vs); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_shortest_paths_dijkstra.out b/tests/unit/igraph_get_shortest_paths_dijkstra.out new file mode 100644 index 0000000..e9ce12e --- /dev/null +++ b/tests/unit/igraph_get_shortest_paths_dijkstra.out @@ -0,0 +1,12 @@ +0 +0 1 +0 1 2 3 +0 1 2 3 4 5 +0 1 2 +0 1 +0 +0 1 +0 1 2 3 +0 9 8 7 6 5 +0 1 2 +0 1 diff --git a/tests/unit/igraph_get_stochastic.c b/tests/unit/igraph_get_stochastic.c new file mode 100644 index 0000000..ce7c008 --- /dev/null +++ b/tests/unit/igraph_get_stochastic.c @@ -0,0 +1,98 @@ +/* + 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, see . +*/ + +#include +#include + +#include "test_utilities.h" + +void validate_sums(igraph_matrix_t* m, igraph_bool_t column_wise) { + igraph_vector_t v; + igraph_vector_t expected; + + igraph_vector_init(&v, 0); + igraph_vector_init(&expected, 0); + + if (column_wise) { + igraph_matrix_colsum(m, &v); + } else { + igraph_matrix_rowsum(m, &v); + } + + igraph_vector_resize(&expected, igraph_matrix_nrow(m)); + igraph_vector_fill(&expected, 1); + VECTOR(expected)[igraph_vector_size(&expected) - 1] = 0; + + IGRAPH_ASSERT(igraph_vector_all_almost_e(&v, &expected, 1e-7)); + + igraph_vector_destroy(&expected); + igraph_vector_destroy(&v); +} + +void test_graph(igraph_bool_t directed) { + igraph_t graph; + igraph_real_t weights_array[] = { 5, 4, 3, 2, 1, 6, 3, 2 }; + igraph_vector_t weights; + igraph_matrix_t m; + const char* prefix = directed ? "Directed" : "Undirected"; + + igraph_small( + &graph, 6, directed ? IGRAPH_DIRECTED : IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 3, 2, 2, 0, 1, -1 + ); + igraph_vector_view(&weights, weights_array, igraph_ecount(&graph)); + + igraph_matrix_init(&m, 2, 2); + + printf("%s, unweighted, row-wise:\n", prefix); + igraph_get_stochastic(&graph, &m, /* column_wise = */ 0, NULL); + igraph_matrix_print(&m); + validate_sums(&m, /* column_wise = */ 0); + printf("========\n"); + + printf("%s, unweighted, column-wise:\n", prefix); + igraph_get_stochastic(&graph, &m, /* column_wise = */ 1, NULL); + igraph_matrix_print(&m); + validate_sums(&m, /* column_wise = */ 1); + printf("========\n"); + + printf("%s, weighted, row-wise:\n", prefix); + igraph_get_stochastic(&graph, &m, /* column_wise = */ 0, &weights); + igraph_matrix_print(&m); + validate_sums(&m, /* column_wise = */ 0); + printf("========\n"); + + printf("%s, weighted, column-wise:\n", prefix); + igraph_get_stochastic(&graph, &m, /* column_wise = */ 1, &weights); + igraph_matrix_print(&m); + validate_sums(&m, /* column_wise = */ 1); + printf("========\n"); + + igraph_matrix_destroy(&m); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); +} + +int main(void) { + + test_graph(/* directed = */ 0); + test_graph(/* directed = */ 1); + + return 0; +} diff --git a/tests/unit/igraph_get_stochastic.out b/tests/unit/igraph_get_stochastic.out new file mode 100644 index 0000000..7e7ccb2 --- /dev/null +++ b/tests/unit/igraph_get_stochastic.out @@ -0,0 +1,64 @@ +Undirected, unweighted, row-wise: + 0 0.5 0 0.25 0.25 0 +0.666667 0 0.333333 0 0 0 + 0 0.25 0.5 0.25 0 0 +0.333333 0 0.333333 0 0.333333 0 + 0.5 0 0 0.5 0 0 + 0 0 0 0 0 0 +======== +Undirected, unweighted, column-wise: + 0 0.666667 0 0.333333 0.5 0 + 0.5 0 0.25 0 0 0 + 0 0.333333 0.5 0.333333 0 0 +0.25 0 0.25 0 0.5 0 +0.25 0 0 0.333333 0 0 + 0 0 0 0 0 0 +======== +Undirected, weighted, row-wise: + 0 0.5 0 0.428571 0.0714286 0 +0.636364 0 0.363636 0 0 0 + 0 0.307692 0.461538 0.230769 0 0 +0.545455 0 0.272727 0 0.181818 0 +0.333333 0 0 0.666667 0 0 + 0 0 0 0 0 0 +======== +Undirected, weighted, column-wise: + 0 0.636364 0 0.545455 0.333333 0 + 0.5 0 0.307692 0 0 0 + 0 0.363636 0.461538 0.272727 0 0 + 0.428571 0 0.230769 0 0.666667 0 +0.0714286 0 0 0.181818 0 0 + 0 0 0 0 0 0 +======== +Directed, unweighted, row-wise: +0 0.666667 0 0.333333 0 0 +0 0 1 0 0 0 +0 0 0.5 0.5 0 0 +0 0 0 0 1 0 +1 0 0 0 0 0 +0 0 0 0 0 0 +======== +Directed, unweighted, column-wise: +0 1 0 0.5 0 0 +0 0 0.5 0 0 0 +0 0 0.5 0.5 0 0 +0 0 0 0 1 0 +1 0 0 0 0 0 +0 0 0 0 0 0 +======== +Directed, weighted, row-wise: +0 0.538462 0 0.461538 0 0 +0 0 1 0 0 0 +0 0 0.5 0.5 0 0 +0 0 0 0 1 0 +1 0 0 0 0 0 +0 0 0 0 0 0 +======== +Directed, weighted, column-wise: +0 1 0 0.666667 0 0 +0 0 0.571429 0 0 0 +0 0 0.428571 0.333333 0 0 +0 0 0 0 1 0 +1 0 0 0 0 0 +0 0 0 0 0 0 +======== diff --git a/tests/unit/igraph_get_stochastic_sparse.c b/tests/unit/igraph_get_stochastic_sparse.c new file mode 100644 index 0000000..9ee9d62 --- /dev/null +++ b/tests/unit/igraph_get_stochastic_sparse.c @@ -0,0 +1,50 @@ +/* + 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, see . + */ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_sparsemat_t res; + + igraph_sparsemat_init(&res, 0, 0, 0); + + printf("Graph with no vertices:\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, -1); + igraph_get_stochastic_sparse(&g, &res, /* column_wise = */ 0, /* weights = */ NULL); + igraph_sparsemat_print(&res, stdout); + igraph_destroy(&g); + + printf("\nGraph with 4 vertices, rowwise:\n"); + igraph_small(&g, 4, IGRAPH_DIRECTED, 0,1, 1,3, 1,3, 2,2, 2,2, 2,3, 3,0, -1); + igraph_get_stochastic_sparse(&g, &res, /* column_wise = */ 0, /* weights = */ NULL); + igraph_sparsemat_print(&res, stdout); + igraph_destroy(&g); + + printf("\nColumnwise:\n"); + igraph_small(&g, 4, IGRAPH_DIRECTED, 0,1, 1,3, 1,3, 2,2, 2,2, 2,3, 3,0, -1); + igraph_get_stochastic_sparse(&g, &res, /* column_wise = */ 1, /* weights = */ NULL); + igraph_sparsemat_print(&res, stdout); + igraph_destroy(&g); + + igraph_sparsemat_destroy(&res); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_stochastic_sparse.out b/tests/unit/igraph_get_stochastic_sparse.out new file mode 100644 index 0000000..cd43595 --- /dev/null +++ b/tests/unit/igraph_get_stochastic_sparse.out @@ -0,0 +1,19 @@ +Graph with no vertices: + +Graph with 4 vertices, rowwise: +0 1 : 1 +1 3 : 0.5 +1 3 : 0.5 +2 2 : 0.333333 +2 2 : 0.333333 +2 3 : 0.333333 +3 0 : 1 + +Columnwise: +0 1 : 1 +1 3 : 0.333333 +1 3 : 0.333333 +2 2 : 0.5 +2 2 : 0.5 +2 3 : 0.333333 +3 0 : 1 diff --git a/tests/unit/igraph_get_subisomorphisms_vf2.c b/tests/unit/igraph_get_subisomorphisms_vf2.c new file mode 100644 index 0000000..3a5dd79 --- /dev/null +++ b/tests/unit/igraph_get_subisomorphisms_vf2.c @@ -0,0 +1,151 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +/* Vertices/edges with the same parity match */ +igraph_bool_t compat_parity(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_integer_t g1_num, + const igraph_integer_t g2_num, + void *arg) { + IGRAPH_UNUSED(graph1); + IGRAPH_UNUSED(graph2); + IGRAPH_UNUSED(arg); + return (g1_num % 2) == (g2_num % 2); +} + +igraph_bool_t compat_not_arg(const igraph_t *graph1, + const igraph_t *graph2, + const igraph_integer_t g1_num, + const igraph_integer_t g2_num, + void *arg) { + IGRAPH_UNUSED(graph1); + IGRAPH_UNUSED(graph2); + IGRAPH_UNUSED(arg); + return g1_num != *(int*)arg + g2_num; +} + +void print_and_destroy_maps(igraph_vector_int_list_t *vp) { + print_vector_int_list(vp); + igraph_vector_int_list_destroy(vp); +} + +void check_print_destroy(igraph_t *g1, + igraph_t *g2, + igraph_vector_int_t *vertex_color1, + igraph_vector_int_t *vertex_color2, + igraph_vector_int_t *edge_color1, + igraph_vector_int_t *edge_color2, + igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, + void *arg, + int error) { + igraph_vector_int_list_t maps; + igraph_vector_int_list_init(&maps, 0); + IGRAPH_ASSERT(igraph_get_subisomorphisms_vf2(g1, g2, vertex_color1, vertex_color2, edge_color1, edge_color2, &maps, node_compat_fn, edge_compat_fn, arg) == error); + print_and_destroy_maps(&maps); + printf("\n"); +} + +void check_print_destroy_simple(igraph_t *g1, igraph_t *g2) { + check_print_destroy(g1, g2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, IGRAPH_SUCCESS); +} + +int main(void) { + igraph_t ring, ring_dir; + igraph_t ring_plus, ring_plus_dir; + igraph_t ring_loop; + igraph_t g_0, g_1; + igraph_vector_int_t coloring; + igraph_vector_int_t plus_edge_coloring; + igraph_vector_int_t plus_vertex_coloring; + int three = 3; + + igraph_vector_int_init_int(&plus_vertex_coloring, 6, 0, 1, 0, 1, 1, 0); + igraph_vector_int_init_int(&coloring, 5, 0, 1, 0, 1, 0); + igraph_vector_int_init_int(&plus_edge_coloring, 6, 0, 0, 1, 0, 1, 0); + igraph_small(&ring_plus, 6, 0, 0,1, 0,3, 2,1, 2,3, 3,5, 5,0, -1); + igraph_small(&ring_plus_dir, 6, 1, 0,1, 0,3, 1,2, 2,3, 3,5, 5,0, -1); + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_ring(&ring, 5, /*directed*/ 0, /*mutual*/ 0, /*circular*/ 1); + igraph_ring(&ring_dir, 5, /*directed*/ 1, /*mutual*/ 0, /*circular*/ 1); + igraph_ring(&ring_loop, 5, /*directed*/ 0, /*mutual*/ 0, /*circular*/ 1); + igraph_add_edge(&ring_loop, 2, 2); + + printf("Two empty graphs:\n"); + check_print_destroy_simple(&g_0, &g_0); + + printf("Two singleton graphs:\n"); + check_print_destroy_simple(&g_1, &g_1); + + printf("Empty and singleton graphs:\n"); + check_print_destroy_simple(&g_0, &g_1); + + printf("Singleton and empty graphs:\n"); + check_print_destroy_simple(&g_1, &g_0); + + printf("Ring with add vertex and edge (ring+) and ring:\n"); + check_print_destroy_simple(&ring_plus, &ring); + + printf("Ring+ and ring, directed:\n"); + check_print_destroy_simple(&ring_plus_dir, &ring_dir); + + printf("Ring+ and ring where node parity should be equal:\n"); + check_print_destroy(&ring_plus, &ring, NULL, NULL, NULL, NULL, &compat_parity, NULL, NULL, IGRAPH_SUCCESS); + + printf("Ring+ and ring where edge parity should be equal:\n"); + check_print_destroy(&ring_plus, &ring, NULL, NULL, NULL, NULL, NULL, &compat_parity, NULL, IGRAPH_SUCCESS); + + printf("Ring+ and ring with only one vertex coloring:\n"); + check_print_destroy(&ring_plus, &ring, &coloring, NULL, NULL, NULL, NULL, NULL, NULL, IGRAPH_SUCCESS); + + printf("Ring+ and ring with vertex coloring:\n"); + check_print_destroy(&ring_plus, &ring, &plus_vertex_coloring, &coloring, NULL, NULL, NULL, NULL, NULL, IGRAPH_SUCCESS); + + printf("Ring+ and ring with edge coloring:\n"); + check_print_destroy(&ring_plus, &ring, NULL, NULL, &plus_edge_coloring, &coloring, NULL, NULL, NULL, IGRAPH_SUCCESS); + + printf("Ring+ and ring where node of graph 1 should not be 3 higher than node of graph 2:\n"); + check_print_destroy(&ring_plus, &ring, NULL, NULL, NULL, NULL, &compat_not_arg, NULL, &three, IGRAPH_SUCCESS); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Ring+ and ring with different directedness.\n"); + check_print_destroy(&ring_plus_dir, &ring, NULL, NULL, NULL, NULL, NULL, NULL, NULL, IGRAPH_EINVAL); + + printf("Graph with loop edges.\n"); + check_print_destroy(&ring, &ring_loop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&ring); + igraph_destroy(&ring_dir); + igraph_destroy(&ring_plus); + igraph_destroy(&ring_plus_dir); + igraph_destroy(&ring_loop); + igraph_vector_int_destroy(&coloring); + igraph_vector_int_destroy(&plus_edge_coloring); + igraph_vector_int_destroy(&plus_vertex_coloring); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_get_subisomorphisms_vf2.out b/tests/unit/igraph_get_subisomorphisms_vf2.out new file mode 100644 index 0000000..f3f6276 --- /dev/null +++ b/tests/unit/igraph_get_subisomorphisms_vf2.out @@ -0,0 +1,96 @@ +Two empty graphs: +{ + 0: ( ) +} + +Two singleton graphs: +{ + 0: ( 0 ) +} + +Empty and singleton graphs: +{ +} + +Singleton and empty graphs: +{ + 0: ( ) +} + +Ring with add vertex and edge (ring+) and ring: +{ + 0: ( 0 1 2 3 5 ) + 1: ( 0 5 3 2 1 ) + 2: ( 1 0 5 3 2 ) + 3: ( 1 2 3 5 0 ) + 4: ( 2 1 0 5 3 ) + 5: ( 2 3 5 0 1 ) + 6: ( 3 2 1 0 5 ) + 7: ( 3 5 0 1 2 ) + 8: ( 5 0 1 2 3 ) + 9: ( 5 3 2 1 0 ) +} + +Ring+ and ring, directed: +{ + 0: ( 0 1 2 3 5 ) + 1: ( 1 2 3 5 0 ) + 2: ( 2 3 5 0 1 ) + 3: ( 3 5 0 1 2 ) + 4: ( 5 0 1 2 3 ) +} + +Ring+ and ring where node parity should be equal: +{ +} + +Ring+ and ring where edge parity should be equal: +{ + 0: ( 1 0 5 3 2 ) + 1: ( 1 2 3 5 0 ) +} + +Ring+ and ring with only one vertex coloring: +{ + 0: ( 0 1 2 3 5 ) + 1: ( 0 5 3 2 1 ) + 2: ( 1 0 5 3 2 ) + 3: ( 1 2 3 5 0 ) + 4: ( 2 1 0 5 3 ) + 5: ( 2 3 5 0 1 ) + 6: ( 3 2 1 0 5 ) + 7: ( 3 5 0 1 2 ) + 8: ( 5 0 1 2 3 ) + 9: ( 5 3 2 1 0 ) +} + +Ring+ and ring with vertex coloring: +{ + 0: ( 0 1 2 3 5 ) + 1: ( 5 3 2 1 0 ) +} + +Ring+ and ring with edge coloring: +{ + 0: ( 0 1 2 3 5 ) + 1: ( 0 5 3 2 1 ) +} + +Ring+ and ring where node of graph 1 should not be 3 higher than node of graph 2: +{ + 0: ( 0 1 2 3 5 ) + 1: ( 0 5 3 2 1 ) + 2: ( 1 2 3 5 0 ) + 3: ( 2 1 0 5 3 ) + 4: ( 5 0 1 2 3 ) + 5: ( 5 3 2 1 0 ) +} + +Ring+ and ring with different directedness. +{ +} + +Graph with loop edges. +{ +} + diff --git a/tests/unit/igraph_gomory_hu_tree.c b/tests/unit/igraph_gomory_hu_tree.c new file mode 100644 index 0000000..ccf5301 --- /dev/null +++ b/tests/unit/igraph_gomory_hu_tree.c @@ -0,0 +1,222 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2013 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +igraph_error_t validate_tree(const igraph_t *graph, const igraph_t *tree, + const igraph_vector_t *flow, const igraph_vector_t *capacity) { + igraph_integer_t n = igraph_vcount(graph); + igraph_integer_t no_of_clusters, min_weight_edge_index; + igraph_vector_int_t edges; + igraph_vector_int_t membership; + igraph_real_t min_weight, flow_value; + igraph_t copy; + igraph_integer_t i, j, k, m; + + if (igraph_vcount(tree) != n) { + printf("Gomory-Hu tree should have %" IGRAPH_PRId " vertices\n", n); + return IGRAPH_EINVAL; + } + + if (igraph_ecount(tree) != n - 1) { + printf("Gomory-Hu tree should have %" IGRAPH_PRId " edges\n", n - 1); + return IGRAPH_EINVAL; + } + + if (igraph_is_directed(tree)) { + printf("Gomory-Hu tree should be undirected\n"); + return IGRAPH_EINVAL; + } + + if (n < 2) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); + + for (i = 0; i < n; i++) { + for (j = i + 1; j < n; j++) { + IGRAPH_CHECK(igraph_get_shortest_path(tree, 0, &edges, i, j, IGRAPH_ALL)); + m = igraph_vector_int_size(&edges); + if (m == 0) { + continue; + } + + /* first, check whether the minimum weight along the shortest path + * from i to j is the same as the maximum flow between i and j in + * the original graph */ + min_weight = VECTOR(*flow)[VECTOR(edges)[0]]; + min_weight_edge_index = VECTOR(edges)[0]; + for (k = 1; k < m; k++) { + if (VECTOR(*flow)[VECTOR(edges)[k]] < min_weight) { + min_weight = VECTOR(*flow)[VECTOR(edges)[k]]; + min_weight_edge_index = VECTOR(edges)[k]; + } + } + + IGRAPH_CHECK(igraph_maxflow(graph, &flow_value, 0, 0, 0, 0, i, j, capacity, 0)); + if (flow_value != min_weight) { + printf("Min weight of path %" IGRAPH_PRId " -- %" IGRAPH_PRId " in Gomory-Hu tree is %.4f, " + "expected %.4f from flow calculation\n", i, j, min_weight, flow_value); + return IGRAPH_EINVAL; + } + + /* next, check whether removing an edge s-t from the Gomory-Hu tree would + * partition it exactly the same way as a minimum cut between s and t in + * the original graph */ + IGRAPH_CHECK(igraph_copy(©, tree)); + IGRAPH_FINALLY(igraph_destroy, ©); + + IGRAPH_CHECK(igraph_delete_edges(©, igraph_ess_1(min_weight_edge_index))); + IGRAPH_CHECK(igraph_connected_components(©, &membership, 0, &no_of_clusters, IGRAPH_WEAK)); + + if (no_of_clusters != 2) { + printf( + "Removing edge %" IGRAPH_PRId " -- %" IGRAPH_PRId + " (index %" IGRAPH_PRId ") from the Gomory-Hu tree cuts it " + "in %" IGRAPH_PRId " clusters, expected 2\n", + IGRAPH_FROM(tree, min_weight_edge_index), + IGRAPH_TO(tree, min_weight_edge_index), + min_weight_edge_index, + no_of_clusters + ); + return IGRAPH_EINVAL; + } + + /* finally, check the total capacity of the edges that go between the + * partitions in the original graph; it should be the same as the + * weight of the edge in the Gomory-Hu tree that corresponds to the + * minimum weight along the path we found above */ + m = igraph_ecount(graph); + flow_value = 0.0; + for (j = 0; j < m; j++) { + if (VECTOR(membership)[IGRAPH_FROM(graph, j)] != VECTOR(membership)[IGRAPH_TO(graph, j)]) { + flow_value += capacity ? VECTOR(*capacity)[j] : 1; + } + } + + if (flow_value != VECTOR(*flow)[min_weight_edge_index]) { + printf( + "Edge %" IGRAPH_PRId " -- %" IGRAPH_PRId + " (index %" IGRAPH_PRId ") in the Gomory-Hu tree has weight = %.2f, but " + "the corresponding flow in the original graph has value = %.2f\n", + IGRAPH_FROM(tree, min_weight_edge_index), + IGRAPH_TO(tree, min_weight_edge_index), + min_weight_edge_index, + VECTOR(*flow)[min_weight_edge_index], flow_value + ); + printf("Edge list of original graph:\n"); + print_graph(graph); + if (capacity) { + printf("Capacities of original graph: "); + print_vector(capacity); + } else { + printf("All edges have capacity = 1\n"); + } + printf("Edge list of Gomory-Hu tree:\n"); + print_graph(tree); + printf("Weights of the Gomory-Hu tree: "); + print_vector(flow); + printf("Partition of original graph corresponding to this cut:\n"); + print_vector_int(&membership); + return IGRAPH_EINVAL; + } + + igraph_destroy(©); + IGRAPH_FINALLY_CLEAN(1); + } + } + + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +int main(void) { + + igraph_t g; + igraph_t tree; + igraph_vector_t flow; + igraph_vector_t capacity; + + /* initialize flow and capacity vectors */ + igraph_vector_init(&capacity, 0); + igraph_vector_init(&flow, 0); + + /* empty undirected graph */ + igraph_empty(&g, 0, 0); + IGRAPH_ASSERT(igraph_gomory_hu_tree(&g, &tree, &flow, &capacity) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&tree) == 0); + IGRAPH_ASSERT(igraph_vector_size(&flow) == 0); + igraph_destroy(&tree); + igraph_destroy(&g); + + /* simple undirected graph */ + igraph_small(&g, 6, 0, 0, 1, 0, 2, 1, 2, 1, 3, 1, 4, 2, 4, 3, 4, 3, 5, 4, 5, -1); + igraph_vector_resize(&capacity, 9); + VECTOR(capacity)[0] = 1; + VECTOR(capacity)[1] = 7; + VECTOR(capacity)[2] = 1; + VECTOR(capacity)[3] = 3; + VECTOR(capacity)[4] = 2; + VECTOR(capacity)[5] = 4; + VECTOR(capacity)[6] = 1; + VECTOR(capacity)[7] = 6; + VECTOR(capacity)[8] = 2; + IGRAPH_ASSERT(igraph_gomory_hu_tree(&g, &tree, &flow, &capacity) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(validate_tree(&g, &tree, &flow, &capacity) == IGRAPH_SUCCESS); + igraph_destroy(&tree); + + /* Make sure we don't blow up without an outgoing flow vector */ + IGRAPH_ASSERT(igraph_gomory_hu_tree(&g, &tree, 0, &capacity) == IGRAPH_SUCCESS); + igraph_destroy(&tree); + igraph_destroy(&g); + + /* example from Github issue #1810 */ + igraph_full(&g, 4, /* directed = */ 0, /* loops = */ 0); + IGRAPH_ASSERT(igraph_gomory_hu_tree(&g, &tree, &flow, 0) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(validate_tree(&g, &tree, &flow, 0) == IGRAPH_SUCCESS); + igraph_destroy(&tree); + igraph_destroy(&g); + + /* simple directed graph - should throw an error */ + igraph_small(&g, 6, 1, 0, 1, 0, 2, 1, 2, 1, 3, 1, 4, 2, 4, 3, 4, 3, 5, 4, 5, -1); + igraph_set_error_handler(igraph_error_handler_ignore); + VERIFY_FINALLY_STACK(); + IGRAPH_ASSERT(igraph_gomory_hu_tree(&g, &tree, &flow, &capacity) == IGRAPH_EINVAL); + igraph_set_error_handler(igraph_error_handler_abort); + igraph_destroy(&g); + + /* destroy flow and capacity vectors */ + igraph_vector_destroy(&flow); + igraph_vector_destroy(&capacity); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_graph_center.c b/tests/unit/igraph_graph_center.c new file mode 100644 index 0000000..14b59e0 --- /dev/null +++ b/tests/unit/igraph_graph_center.c @@ -0,0 +1,277 @@ +/* + 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, see . +*/ + + +#include +#include + +#include "test_utilities.h" + +void check_radius(const igraph_t *graph, const igraph_vector_int_t *center, igraph_neimode_t mode) { + igraph_vector_t ecc; + igraph_real_t radius; + igraph_integer_t n = igraph_vector_int_size(center); + + igraph_radius(graph, &radius, mode); + printf("Radius: %g\n", radius); + + if (n == 0) { + /* Null graph has radius NaN */ + IGRAPH_ASSERT(isnan(radius)); + } else { + igraph_vector_init(&ecc, 0); + igraph_eccentricity(graph, &ecc, igraph_vss_vector(center), mode); + for (igraph_integer_t i=0; i < n; i++) { + IGRAPH_ASSERT(VECTOR(ecc)[i] == radius); + } + igraph_vector_destroy(&ecc); + } +} + +void check_radius_dijkstra(const igraph_t *graph, const igraph_vector_t *weights, + const igraph_vector_int_t *center, igraph_neimode_t mode) { + igraph_vector_t ecc; + igraph_real_t radius; + igraph_integer_t n = igraph_vector_int_size(center); + const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; + + igraph_radius_dijkstra(graph, weights, &radius, mode); + printf("Radius: %g\n", radius); + + if (n == 0) { + /* Null graph has radius NaN */ + IGRAPH_ASSERT(isnan(radius)); + } else { + igraph_vector_init(&ecc, 0); + igraph_eccentricity_dijkstra(graph, weights, &ecc, igraph_vss_vector(center), mode); + for (igraph_integer_t i=0; i < n; i++) { + IGRAPH_ASSERT(igraph_cmp_epsilon(VECTOR(ecc)[i], radius, eps) == 0); + } + igraph_vector_destroy(&ecc); + } +} + +int main(void) { + + igraph_t g; + igraph_vector_int_t center; + igraph_vector_t weights, w; + + /* Make sure that this vector has at least as many entries as the + * largest edge count within the test graphs below. */ + igraph_vector_init_range(&weights, 2, 13); + + igraph_vector_int_init(¢er, 0); + + /* Unweighted calculations */ + printf("UNWEIGHTED\n\n"); + + printf("Null graph:\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nSingleton graph:\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nPath with isolated vertex:\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, + 0,2, + -1); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nFour isolated vertices:\n"); + igraph_small(&g, 4, IGRAPH_UNDIRECTED, -1); + igraph_graph_center(&g, ¢er, IGRAPH_OUT); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nUndirected path graph P_5:\n"); + igraph_ring(&g, 5, IGRAPH_UNDIRECTED, /* mutual */ 0, /* circular */ 0); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nUndirected graph\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 0, 2, 3, 0, 4, 1, 2, 5, -1); + igraph_graph_center(&g, ¢er, IGRAPH_OUT); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nDirected path graph P_5:\n"); + igraph_ring(&g, 5, IGRAPH_DIRECTED, /* mutual */ 0, /* circular */ 0); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nUndirected star S_10:\n"); + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nOut-star S_10:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nOut-star S_10, undirected mode:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_ALL) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nIn-star S_10:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + IGRAPH_ASSERT(igraph_graph_center(&g, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius(&g, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + /* Weighted calculations */ + printf("\n\nWEIGHTED\n\n"); + + printf("Null graph:\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + igraph_destroy(&g); + + printf("\nSingleton graph:\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nPath with isolated vertex:\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, + 0,2, + -1); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nFour isolated vertices:\n"); + igraph_small(&g, 4, IGRAPH_UNDIRECTED, -1); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nUndirected path graph P_5:\n"); + igraph_ring(&g, 5, IGRAPH_UNDIRECTED, /* mutual */ 0, /* circular */ 0); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nUndirected graph\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 0, 2, 3, 0, 4, 1, 2, 5, -1); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nDirected path graph P_5:\n"); + igraph_ring(&g, 5, IGRAPH_DIRECTED, /* mutual */ 0, /* circular */ 0); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nUndirected star S_10:\n"); + igraph_star(&g, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nOut-star S_10:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nOut-star S_10, undirected mode:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_ALL) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nIn-star S_10:\n"); + igraph_star(&g, 10, IGRAPH_STAR_OUT, 0); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + igraph_destroy(&g); + + printf("\nDirected cycle C_5\n"); + igraph_ring(&g, 5, IGRAPH_DIRECTED, /* mutual */ false, /* circular */ true); + igraph_vector_view(&w, VECTOR(weights), igraph_ecount(&g)); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_OUT) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_OUT); + + printf("\nDirected cycle C_5, mode=IN\n"); + IGRAPH_ASSERT(igraph_graph_center_dijkstra(&g, &w, ¢er, IGRAPH_IN) == IGRAPH_SUCCESS); + print_vector_int(¢er); + check_radius_dijkstra(&g, &w, ¢er, IGRAPH_IN); + igraph_destroy(&g); + + igraph_vector_int_destroy(¢er); + + igraph_vector_destroy(&weights); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_graph_center.out b/tests/unit/igraph_graph_center.out new file mode 100644 index 0000000..6ec1941 --- /dev/null +++ b/tests/unit/igraph_graph_center.out @@ -0,0 +1,99 @@ +UNWEIGHTED + +Null graph: +( ) +Radius: nan + +Singleton graph: +( 0 ) +Radius: 0 + +Path with isolated vertex: +( 1 ) +Radius: 0 + +Four isolated vertices: +( 0 1 2 3 ) +Radius: 0 + +Undirected path graph P_5: +( 2 ) +Radius: 2 + +Undirected graph +( 0 1 2 ) +Radius: 2 + +Directed path graph P_5: +( 4 ) +Radius: 0 + +Undirected star S_10: +( 0 ) +Radius: 1 + +Out-star S_10: +( 1 2 3 4 5 6 7 8 9 ) +Radius: 0 + +Out-star S_10, undirected mode: +( 0 ) +Radius: 1 + +In-star S_10: +( 1 2 3 4 5 6 7 8 9 ) +Radius: 0 + + +WEIGHTED + +Null graph: +( ) + +Singleton graph: +( 0 ) +Radius: 0 + +Path with isolated vertex: +( 1 ) +Radius: 0 + +Four isolated vertices: +( 0 1 2 3 ) +Radius: 0 + +Undirected path graph P_5: +( 2 3 ) +Radius: 9 + +Undirected graph +( 2 ) +Radius: 9 + +Directed path graph P_5: +( 4 ) +Radius: 0 + +Undirected star S_10: +( 0 ) +Radius: 10 + +Out-star S_10: +( 1 2 3 4 5 6 7 8 9 ) +Radius: 0 + +Out-star S_10, undirected mode: +( 0 ) +Radius: 10 + +In-star S_10: +( 1 2 3 4 5 6 7 8 9 ) +Radius: 0 + +Directed cycle C_5 +( 0 ) +Radius: 14 + +Directed cycle C_5, mode=IN +( 4 ) +Radius: 14 diff --git a/tests/unit/igraph_graph_power.c b/tests/unit/igraph_graph_power.c new file mode 100644 index 0000000..7d53b75 --- /dev/null +++ b/tests/unit/igraph_graph_power.c @@ -0,0 +1,120 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void print_result(igraph_t *g, igraph_integer_t order, igraph_bool_t directed) { + igraph_t res; + igraph_graph_power(g, &res, order, directed); + print_graph_canon(&res); + if ((order == 0 || order == 1) && igraph_has_attribute_table()) { + print_attributes(&res); + } + igraph_destroy(&res); +} + +int main(void) { + igraph_t g; + + + printf("Directed graph with no vertices:\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, -1); + print_result(&g, 10, IGRAPH_DIRECTED); + printf("Ignore edge directions:\n"); + print_result(&g, 10, IGRAPH_UNDIRECTED); + igraph_destroy(&g); + + printf("\nBasic example, A->B->C, directed, order 2:\n"); + igraph_small(&g, 3, IGRAPH_DIRECTED, 0,1, 1,2, -1); + print_result(&g, 2, IGRAPH_DIRECTED); + printf("Ignore edge directions:\n"); + print_result(&g, 2, IGRAPH_UNDIRECTED); + igraph_destroy(&g); + + printf("\nBasic example, A->B->C, undirected, order 2:\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,1, 1,2, -1); + print_result(&g, 2, IGRAPH_DIRECTED); + igraph_destroy(&g); + + printf("\nBasic example, A->B<-C, directed, order 2:\n"); + igraph_small(&g, 3, IGRAPH_DIRECTED, 0,1, 2,1, -1); + print_result(&g, 2, IGRAPH_DIRECTED); + printf("Ignore edge directions:\n"); + print_result(&g, 2, IGRAPH_UNDIRECTED); + igraph_destroy(&g); + + printf("\nBasic example, A<-B<-C, directed, order 2:\n"); + igraph_small(&g, 3, IGRAPH_DIRECTED, 1,0, 2,1, -1); + print_result(&g, 2, IGRAPH_DIRECTED); + printf("Ignore edge directions:\n"); + print_result(&g, 2, IGRAPH_UNDIRECTED); + igraph_destroy(&g); + + printf("\nBasic example, A->B->C->A, directed, order 2:\n"); + igraph_small(&g, 3, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, -1); + print_result(&g, 2, IGRAPH_DIRECTED); + printf("Ignore edge directions:\n"); + print_result(&g, 2, IGRAPH_UNDIRECTED); + igraph_destroy(&g); + + printf("\nBasic example, A->B<->C->A, directed, order 2:\n"); + igraph_small(&g, 3, IGRAPH_DIRECTED, 0,1, 1,2, 2,1, 2,0, -1); + print_result(&g, 2, IGRAPH_DIRECTED); + printf("Ignore edge directions:\n"); + print_result(&g, 2, IGRAPH_UNDIRECTED); + igraph_destroy(&g); + + printf("\nDirected graph with loops and multiple edges, order 0:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_result(&g, 0, IGRAPH_DIRECTED); + igraph_destroy(&g); + + printf("\nDirected graph with loops and multiple edges, order 0, with attributes set:\n"); + igraph_set_attribute_table(&igraph_cattribute_table); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + SETGAB(&g, "bool_graph_attr", 1); + SETEAB(&g, "bool_edge_attr", 0, 1); + SETVAB(&g, "bool_vertex_attr", 0, 1); + + print_result(&g, 0, IGRAPH_DIRECTED); + + printf("Same graph, order 1:\n"); + print_result(&g, 1, IGRAPH_DIRECTED); + + printf("Same starting graph, order 2:\n"); + print_result(&g, 2, IGRAPH_DIRECTED); + + printf("Same starting graph, order 3:\n"); + print_result(&g, 3, IGRAPH_DIRECTED); + + printf("Same starting graph, order 12:\n"); + print_result(&g, 12, IGRAPH_DIRECTED); + + printf("Same starting graph, but undirected, order 2:\n"); + igraph_destroy(&g); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,1, 1,3, 2,3, 3,4, 3,4, -1); + print_result(&g, 2, IGRAPH_DIRECTED); + + VERIFY_FINALLY_STACK(); + + printf("\nCheck negative order error.\n"); + CHECK_ERROR(igraph_graph_power(&g, NULL, -1, IGRAPH_DIRECTED), IGRAPH_EINVAL); + igraph_destroy(&g); + + return 0; +} diff --git a/tests/unit/igraph_graph_power.out b/tests/unit/igraph_graph_power.out new file mode 100644 index 0000000..3115084 --- /dev/null +++ b/tests/unit/igraph_graph_power.out @@ -0,0 +1,210 @@ +Directed graph with no vertices: +directed: true +vcount: 0 +edges: { +} +Ignore edge directions: +directed: false +vcount: 0 +edges: { +} + +Basic example, A->B->C, directed, order 2: +directed: true +vcount: 3 +edges: { +0 1 +0 2 +1 2 +} +Ignore edge directions: +directed: false +vcount: 3 +edges: { +0 1 +0 2 +1 2 +} + +Basic example, A->B->C, undirected, order 2: +directed: false +vcount: 3 +edges: { +0 1 +0 2 +1 2 +} + +Basic example, A->B<-C, directed, order 2: +directed: true +vcount: 3 +edges: { +0 1 +2 1 +} +Ignore edge directions: +directed: false +vcount: 3 +edges: { +0 1 +0 2 +1 2 +} + +Basic example, A<-B<-C, directed, order 2: +directed: true +vcount: 3 +edges: { +1 0 +2 0 +2 1 +} +Ignore edge directions: +directed: false +vcount: 3 +edges: { +0 1 +0 2 +1 2 +} + +Basic example, A->B->C->A, directed, order 2: +directed: true +vcount: 3 +edges: { +0 1 +0 2 +1 0 +1 2 +2 0 +2 1 +} +Ignore edge directions: +directed: false +vcount: 3 +edges: { +0 1 +0 2 +1 2 +} + +Basic example, A->B<->C->A, directed, order 2: +directed: true +vcount: 3 +edges: { +0 1 +0 2 +1 0 +1 2 +2 0 +2 1 +} +Ignore edge directions: +directed: false +vcount: 3 +edges: { +0 1 +0 2 +1 2 +1 2 +} + +Directed graph with loops and multiple edges, order 0: +directed: true +vcount: 6 +edges: { +} + +Directed graph with loops and multiple edges, order 0, with attributes set: +directed: true +vcount: 6 +edges: { +} +bool_graph_attr=1 +Vertex 0: bool_vertex_attr=1 +Vertex 1: bool_vertex_attr=0 +Vertex 2: bool_vertex_attr=0 +Vertex 3: bool_vertex_attr=0 +Vertex 4: bool_vertex_attr=0 +Vertex 5: bool_vertex_attr=0 + +Same graph, order 1: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +1 3 +2 3 +3 4 +} +bool_graph_attr=1 +Vertex 0: bool_vertex_attr=1 +Vertex 1: bool_vertex_attr=0 +Vertex 2: bool_vertex_attr=0 +Vertex 3: bool_vertex_attr=0 +Vertex 4: bool_vertex_attr=0 +Vertex 5: bool_vertex_attr=0 +Edge 0 (0-1): +Edge 1 (0-2): +Edge 2 (1-3): +Edge 3 (2-3): +Edge 4 (3-4): + +Same starting graph, order 2: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +0 3 +1 3 +1 4 +2 3 +2 4 +3 4 +} +Same starting graph, order 3: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +0 3 +0 4 +1 3 +1 4 +2 3 +2 4 +3 4 +} +Same starting graph, order 12: +directed: true +vcount: 6 +edges: { +0 1 +0 2 +0 3 +0 4 +1 3 +1 4 +2 3 +2 4 +3 4 +} +Same starting graph, but undirected, order 2: +directed: false +vcount: 6 +edges: { +0 1 +0 2 +0 3 +1 2 +1 3 +1 4 +2 3 +2 4 +3 4 +} + +Check negative order error. diff --git a/tests/unit/igraph_grg_game.c b/tests/unit/igraph_grg_game.c new file mode 100644 index 0000000..cf4a1d8 --- /dev/null +++ b/tests/unit/igraph_grg_game.c @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 137); + + /* Empty graph */ + igraph_grg_game(&g, 100, 0, 0, 0, 0); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + igraph_destroy(&g); + + /* Full graph */ + igraph_grg_game(&g, 10, sqrt(2.0) / 2, 1, 0, 0); + IGRAPH_ASSERT(igraph_ecount(&g) == igraph_vcount(&g) * (igraph_vcount(&g) - 1) / 2); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_growing_random_game.c b/tests/unit/igraph_growing_random_game.c new file mode 100644 index 0000000..bc4745a --- /dev/null +++ b/tests/unit/igraph_growing_random_game.c @@ -0,0 +1,66 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + + +int main(void) { + igraph_t g; + igraph_vector_int_t degree; + igraph_bool_t tree; + igraph_integer_t i; + + igraph_rng_seed(igraph_rng_default(), 42); + + /* no vertices */ + + igraph_growing_random_game(&g, /* n: vertices */ 0, /* m: edges_per_vertex */ 3, /* directed */ 0, /* citation */ 0); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + IGRAPH_ASSERT(!igraph_is_directed(&g)); + igraph_destroy(&g); + + /* 1 edge per vertex with citation makes a tree */ + + igraph_growing_random_game(&g, /* n: vertices */ 20, /* m: edges_per_vertex */ 1, /* directed */ 0, /* citation */ 1); + igraph_is_tree(&g, &tree, /* root */ NULL, /* unused mode */ IGRAPH_ALL); + IGRAPH_ASSERT(tree); + igraph_destroy(&g); + + /* out degree of citation equals edges per vertex */ + + igraph_growing_random_game(&g, /* n: vertices */ 10, /* m: edges_per_vertex */ 7, /* directed */ 1, /* citation */ 1); + igraph_vector_int_init(°ree, 0); + igraph_degree(&g, °ree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); + for (i = 1; i < 10; i++) { + IGRAPH_ASSERT(VECTOR(degree)[i] == 7); + } + IGRAPH_ASSERT(igraph_is_directed(&g)); + igraph_vector_int_destroy(°ree); + igraph_destroy(&g); + + /* total number of edges is (vertices - 1) * edges */ + + igraph_growing_random_game(&g, /* n: vertices */ 10, /* m: edges_per_vertex */ 7, /* directed */ 1, /* citation */ 0); + IGRAPH_ASSERT(igraph_ecount(&g) == 63); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_has_mutual.c b/tests/unit/igraph_has_mutual.c new file mode 100644 index 0000000..3f5e9ef --- /dev/null +++ b/tests/unit/igraph_has_mutual.c @@ -0,0 +1,82 @@ +/* + 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, see . +*/ + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_bool_t has_mutual; + + /* undirected null graph */ + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_LOOPS); + IGRAPH_ASSERT(! has_mutual); + igraph_destroy(&graph); + + /* undirected edgeless graph */ + igraph_empty(&graph, 3, IGRAPH_UNDIRECTED); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_LOOPS); + IGRAPH_ASSERT(! has_mutual); + igraph_destroy(&graph); + + /* directed null graph */ + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_LOOPS); + IGRAPH_ASSERT(! has_mutual); + igraph_destroy(&graph); + + /* directed edgeless graph */ + igraph_empty(&graph, 3, IGRAPH_DIRECTED); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_LOOPS); + IGRAPH_ASSERT(! has_mutual); + igraph_destroy(&graph); + + /* undirected with edges */ + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, 0,1, -1); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_LOOPS); + IGRAPH_ASSERT(has_mutual); + igraph_destroy(&graph); + + /* directed with no mutual */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 1,2, -1); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_LOOPS); + IGRAPH_ASSERT(! has_mutual); + igraph_destroy(&graph); + + /* directed with mutual */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 1,0, -1); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_LOOPS); + IGRAPH_ASSERT(has_mutual); + igraph_destroy(&graph); + + /* directed with loops, loops considered mutual */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 0,1, 1,1, 1,2, -1); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_LOOPS); + IGRAPH_ASSERT(has_mutual); + igraph_destroy(&graph); + + /* directed with loops, loops not considered mutual */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 0,1, 1,1, 1,2, -1); + igraph_has_mutual(&graph, &has_mutual, IGRAPH_NO_LOOPS); + IGRAPH_ASSERT(!has_mutual); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_hexagonal_lattice.c b/tests/unit/igraph_hexagonal_lattice.c new file mode 100644 index 0000000..0a6e0ec --- /dev/null +++ b/tests/unit/igraph_hexagonal_lattice.c @@ -0,0 +1,102 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_int_t dimvector; + + /* empty graph */ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 2)); + VECTOR(dimvector)[0] = 3; + + IGRAPH_CHECK(igraph_hexagonal_lattice(&graph, &dimvector, true, false)); + printf("Empty graph:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + /* triangular triangular lattice with a single vertex and no edges*/ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 1)); + VECTOR(dimvector)[0] = 1; + + IGRAPH_CHECK(igraph_hexagonal_lattice(&graph, &dimvector, true, false)); + printf("Triangular hexagonal lattice, single hexagon:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + /* triangular hexagonal lattice */ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 1)); + VECTOR(dimvector)[0] = 5; + + IGRAPH_CHECK(igraph_hexagonal_lattice(&graph, &dimvector, true, false)); + printf("Triangular hexagonal lattice:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + /* rectangular hexagonal lattice */ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 2)); + VECTOR(dimvector)[0] = 4; + VECTOR(dimvector)[1] = 5; + + IGRAPH_CHECK(igraph_hexagonal_lattice(&graph, &dimvector, true, true)); + printf("Rectangular hexagonal lattice:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + /* hexagonal hexagonal lattice */ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 3)); + VECTOR(dimvector)[0] = 3; + VECTOR(dimvector)[1] = 4; + VECTOR(dimvector)[2] = 5; + + IGRAPH_CHECK(igraph_hexagonal_lattice(&graph, &dimvector, false, true)); + printf("Hexagonal hexagonal lattice:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + + /* Erroneous calls */ + VECTOR(dimvector)[0] = -3; + CHECK_ERROR(igraph_hexagonal_lattice(&graph, &dimvector, true, true), IGRAPH_EINVAL); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 4)); + VECTOR(dimvector)[0] = 3; + VECTOR(dimvector)[1] = 4; + VECTOR(dimvector)[2] = 5; + VECTOR(dimvector)[3] = 5; + CHECK_ERROR(igraph_hexagonal_lattice(&graph, &dimvector, true, true), IGRAPH_EINVAL); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_hexagonal_lattice.out b/tests/unit/igraph_hexagonal_lattice.out new file mode 100644 index 0000000..f7da9c2 --- /dev/null +++ b/tests/unit/igraph_hexagonal_lattice.out @@ -0,0 +1,374 @@ +Empty graph: +directed: true +vcount: 0 +edges: { +} +Triangular hexagonal lattice, single hexagon: +directed: true +vcount: 6 +edges: { +0 1 +0 3 +1 2 +2 5 +3 4 +4 5 +} +Triangular hexagonal lattice: +directed: true +vcount: 46 +edges: { +0 1 +0 11 +1 2 +2 3 +2 13 +3 4 +4 5 +4 15 +5 6 +6 7 +6 17 +7 8 +8 9 +8 19 +9 10 +10 21 +11 12 +12 13 +12 22 +13 14 +14 15 +14 24 +15 16 +16 17 +16 26 +17 18 +18 19 +18 28 +19 20 +20 21 +20 30 +22 23 +23 24 +23 31 +24 25 +25 26 +25 33 +26 27 +27 28 +27 35 +28 29 +29 30 +29 37 +31 32 +32 33 +32 38 +33 34 +34 35 +34 40 +35 36 +36 37 +36 42 +38 39 +39 40 +39 43 +40 41 +41 42 +41 45 +43 44 +44 45 +} +Rectangular hexagonal lattice: +directed: true +vcount: 58 +edges: { +0 1 +0 12 +1 0 +1 2 +2 1 +2 3 +2 14 +3 2 +3 4 +4 3 +4 5 +4 16 +5 4 +5 6 +6 5 +6 7 +6 18 +7 6 +7 8 +8 7 +8 9 +8 20 +9 8 +9 10 +10 9 +10 22 +11 12 +11 23 +12 0 +12 11 +12 13 +13 12 +13 14 +13 25 +14 2 +14 13 +14 15 +15 14 +15 16 +15 27 +16 4 +16 15 +16 17 +17 16 +17 18 +17 29 +18 6 +18 17 +18 19 +19 18 +19 20 +19 31 +20 8 +20 19 +20 21 +21 20 +21 22 +21 33 +22 10 +22 21 +23 11 +23 24 +24 23 +24 25 +24 36 +25 13 +25 24 +25 26 +26 25 +26 27 +26 38 +27 15 +27 26 +27 28 +28 27 +28 29 +28 40 +29 17 +29 28 +29 30 +30 29 +30 31 +30 42 +31 19 +31 30 +31 32 +32 31 +32 33 +32 44 +33 21 +33 32 +33 34 +34 33 +34 46 +35 36 +35 47 +36 24 +36 35 +36 37 +37 36 +37 38 +37 49 +38 26 +38 37 +38 39 +39 38 +39 40 +39 51 +40 28 +40 39 +40 41 +41 40 +41 42 +41 53 +42 30 +42 41 +42 43 +43 42 +43 44 +43 55 +44 32 +44 43 +44 45 +45 44 +45 46 +45 57 +46 34 +46 45 +47 35 +47 48 +48 47 +48 49 +49 37 +49 48 +49 50 +50 49 +50 51 +51 39 +51 50 +51 52 +52 51 +52 53 +53 41 +53 52 +53 54 +54 53 +54 55 +55 43 +55 54 +55 56 +56 55 +56 57 +57 45 +57 56 +} +Hexagonal hexagonal lattice: +directed: false +vcount: 94 +edges: { +0 1 +0 8 +1 2 +2 3 +2 10 +3 4 +4 5 +4 12 +5 6 +6 14 +7 8 +7 17 +8 9 +9 10 +9 19 +10 11 +11 12 +11 21 +12 13 +13 14 +13 23 +14 15 +15 25 +16 17 +16 28 +17 18 +18 19 +18 30 +19 20 +20 21 +20 32 +21 22 +22 23 +22 34 +23 24 +24 25 +24 36 +25 26 +26 38 +27 28 +27 40 +28 29 +29 30 +29 42 +30 31 +31 32 +31 44 +32 33 +33 34 +33 46 +34 35 +35 36 +35 48 +36 37 +37 38 +37 50 +38 39 +39 52 +40 41 +41 42 +41 54 +42 43 +43 44 +43 56 +44 45 +45 46 +45 58 +46 47 +47 48 +47 60 +48 49 +49 50 +49 62 +50 51 +51 52 +51 64 +52 53 +53 66 +54 55 +55 56 +55 67 +56 57 +57 58 +57 69 +58 59 +59 60 +59 71 +60 61 +61 62 +61 73 +62 63 +63 64 +63 75 +64 65 +65 66 +65 77 +67 68 +68 69 +68 78 +69 70 +70 71 +70 80 +71 72 +72 73 +72 82 +73 74 +74 75 +74 84 +75 76 +76 77 +76 86 +78 79 +79 80 +79 87 +80 81 +81 82 +81 89 +82 83 +83 84 +83 91 +84 85 +85 86 +85 93 +87 88 +88 89 +89 90 +90 91 +91 92 +92 93 +} diff --git a/tests/unit/igraph_hrg.c b/tests/unit/igraph_hrg.c new file mode 100644 index 0000000..c391ad4 --- /dev/null +++ b/tests/unit/igraph_hrg.c @@ -0,0 +1,100 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_t full, tree; + igraph_hrg_t hrg; + igraph_t dendrogram; + igraph_vector_t prob; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_full(&full, 10, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_kary_tree(&tree, 15, /*children=*/ 2, /*type=*/ IGRAPH_TREE_UNDIRECTED); + igraph_disjoint_union(&graph, &full, &tree); + igraph_add_edge(&graph, 0, 10); + + igraph_destroy(&full); + igraph_destroy(&tree); + + // Fit + igraph_hrg_init(&hrg, igraph_vcount(&graph)); + igraph_hrg_fit(&graph, &hrg, /*start=*/ false, /*steps=*/ 0); + + // Create a graph from it + igraph_vector_init(&prob, 0); + igraph_from_hrg_dendrogram(&dendrogram, &hrg, &prob); + + // Print the tree, with labels + igraph_vector_int_t neis; + igraph_vector_int_init(&neis, 0); + for (igraph_integer_t i=0; i < igraph_vcount(&graph)-1; i++) { + printf("Vertex # %2" IGRAPH_PRId ", ", (i+igraph_vcount(&graph))); + igraph_neighbors(&dendrogram, &neis, i+igraph_vcount(&graph), IGRAPH_OUT); + printf("left: # %2" IGRAPH_PRId ", right: # %2" IGRAPH_PRId ", ", VECTOR(neis)[0], VECTOR(neis)[1]); + printf("prob: %6.2g\n", VECTOR(prob)[i+igraph_vcount(&graph)]); + } + igraph_vector_int_destroy(&neis); + + igraph_vector_destroy(&prob); + igraph_destroy(&dendrogram); + igraph_hrg_destroy(&hrg); + igraph_destroy(&graph); + + // test small graph, omit probabilities + igraph_small(&graph, 3, IGRAPH_UNDIRECTED, + 0,1, + -1); + + igraph_hrg_init(&hrg, igraph_vcount(&graph)); + igraph_hrg_fit(&graph, &hrg, /*start=*/ false, /*steps=*/ 0); + igraph_from_hrg_dendrogram(&dendrogram, &hrg, NULL); + igraph_destroy(&dendrogram); + igraph_hrg_destroy(&hrg); + + // test with a specific number of steps + igraph_hrg_init(&hrg, igraph_vcount(&graph)); + igraph_hrg_fit(&graph, &hrg, /*start=*/ false, /*steps=*/ 10); + igraph_from_hrg_dendrogram(&dendrogram, &hrg, NULL); + igraph_destroy(&dendrogram); + igraph_hrg_destroy(&hrg); + + igraph_destroy(&graph); + + // graph must have at least 3 vertices at the moment + igraph_full(&graph, 2, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_hrg_init(&hrg, igraph_vcount(&graph)); + CHECK_ERROR(igraph_hrg_fit(&graph, &hrg, /*start=*/ false, /*steps=*/ 0), IGRAPH_EINVAL); + igraph_hrg_destroy(&hrg); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_hrg2.c b/tests/unit/igraph_hrg2.c new file mode 100644 index 0000000..95fc17c --- /dev/null +++ b/tests/unit/igraph_hrg2.c @@ -0,0 +1,98 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t karate; + igraph_vector_int_t parents; + igraph_vector_t weights; + igraph_integer_t i, n; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_small(&karate, 34, IGRAPH_UNDIRECTED, + 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, + -1); + + igraph_vector_int_init(&parents, 0); + igraph_vector_init(&weights, 0); + igraph_hrg_consensus(&karate, &parents, &weights, /* hrg= */ NULL, + /* start= */ false, /* num_samples= */ 100); + + /* We do some simple validity tests on the results only; the exact results + * are different on i386 vs other platforms due to numerical inaccuracies */ + if (igraph_vector_size(&weights) + igraph_vcount(&karate) != igraph_vector_int_size(&parents)) { + fprintf(stderr, "Vector length mismatch: %" IGRAPH_PRId " + %" IGRAPH_PRId " != %" IGRAPH_PRId "\n", + igraph_vector_size(&weights), igraph_vcount(&karate), + igraph_vector_int_size(&parents) + ); + abort(); + } + + n = igraph_vector_int_size(&parents); + for (i = 0; i < n; i++) { + if (VECTOR(parents)[i] < -1 || VECTOR(parents)[i] >= igraph_vcount(&karate) + igraph_vector_size(&weights)) { + fprintf(stderr, "Invalid parents vector:\n"); + igraph_vector_int_fprint(&parents, stderr); + abort(); + } + } + + igraph_vector_int_destroy(&parents); + igraph_vector_destroy(&weights); + igraph_destroy(&karate); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_hrg3.c b/tests/unit/igraph_hrg3.c new file mode 100644 index 0000000..fc593f4 --- /dev/null +++ b/tests/unit/igraph_hrg3.c @@ -0,0 +1,99 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t karate; + igraph_vector_int_t edges; + igraph_vector_t prob; + igraph_integer_t i, n; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_small(&karate, 34, IGRAPH_UNDIRECTED, + 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, + -1); + + igraph_vector_int_init(&edges, 0); + igraph_vector_init(&prob, 0); + igraph_hrg_predict(&karate, &edges, &prob, /* hrg= */ NULL, /* start= */ false, + /* num_samples= */ 100, /* num_bins= */ 25); + + /* We do some simple validity tests on the results only; the exact results + * are different on i386 vs other platforms due to numerical inaccuracies */ + n = igraph_vector_int_size(&edges); + for (i = 0; i < n; i++) { + if (VECTOR(edges)[i] < 0 || VECTOR(edges)[i] >= igraph_vcount(&karate)) { + printf("Invalid edges vector:\n"); + igraph_vector_int_print(&edges); + return 1; + } + } + n = igraph_vector_size(&prob); + for (i = 0; i < n; i++) { + if (VECTOR(prob)[i] < 0 || VECTOR(prob)[i] > 1) { + printf("Invalid prob vector:\n"); + igraph_vector_print(&prob); + return 2; + } + } + + igraph_vector_destroy(&prob); + igraph_vector_int_destroy(&edges); + + igraph_destroy(&karate); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_hrg_create.c b/tests/unit/igraph_hrg_create.c new file mode 100644 index 0000000..f5888ac --- /dev/null +++ b/tests/unit/igraph_hrg_create.c @@ -0,0 +1,79 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_hrg_t hrg; + igraph_t graph, new_graph; + igraph_vector_t prob; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("Two leaf nodes which are always connected:\n"); + igraph_vector_init_real(&prob, 1, 1.0); + igraph_hrg_init(&hrg, 0); + igraph_small(&graph, 3, IGRAPH_DIRECTED, 0,1, 0,2, -1); + /* the graph and the prob form the hrg dendrogram with probabilities. So you're not just using some graph */ + /* igraph_hrg_fit is used if you just have some graph that you'd want to do HRG analysis on */ + igraph_hrg_create(&hrg, &graph, &prob); + igraph_hrg_game(&new_graph, &hrg); + print_graph_canon(&new_graph); + igraph_destroy(&graph); + igraph_destroy(&new_graph); + igraph_vector_destroy(&prob); + + printf("Four leaf nodes, one node connected to all others:\n"); + igraph_vector_init_real(&prob, 3, 1.0, 0.0, 0.0); + igraph_small(&graph, 7, IGRAPH_DIRECTED, 0,3, 0,1, 1,4, 1,2, 2,5, 2,6, -1); + igraph_hrg_create(&hrg, &graph, &prob); + igraph_hrg_game(&new_graph, &hrg); + print_graph_canon(&new_graph); + + igraph_destroy(&graph); + igraph_destroy(&new_graph); + igraph_vector_destroy(&prob); + + VERIFY_FINALLY_STACK(); + + printf("Check error handling for wrong number of probabilities.\n"); + igraph_vector_init_real(&prob, 3, 1.0, 0.0, 0.0); + igraph_small(&graph, 3, IGRAPH_DIRECTED, 0,1, 0,2, -1); + CHECK_ERROR(igraph_hrg_create(&hrg, &graph, &prob), IGRAPH_EINVAL); + + igraph_destroy(&graph); + igraph_vector_destroy(&prob); + + printf("Check error handling for graph with loop.\n"); + igraph_vector_init_real(&prob, 1, 1.0); + igraph_small(&graph, 3, IGRAPH_DIRECTED, 0,0, 0,2, -1); + CHECK_ERROR(igraph_hrg_create(&hrg, &graph, &prob), IGRAPH_EINVAL); + igraph_destroy(&graph); + + printf("Check error handling for graph with no edges.\n"); + igraph_small(&graph, 3, IGRAPH_DIRECTED, -1); + CHECK_ERROR(igraph_hrg_create(&hrg, &graph, &prob), IGRAPH_EINVAL); + igraph_destroy(&graph); + igraph_vector_destroy(&prob); + igraph_hrg_destroy(&hrg); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_hrg_create.out b/tests/unit/igraph_hrg_create.out new file mode 100644 index 0000000..bdeafb7 --- /dev/null +++ b/tests/unit/igraph_hrg_create.out @@ -0,0 +1,17 @@ +Two leaf nodes which are always connected: +directed: false +vcount: 2 +edges: { +0 1 +} +Four leaf nodes, one node connected to all others: +directed: false +vcount: 4 +edges: { +0 1 +0 2 +0 3 +} +Check error handling for wrong number of probabilities. +Check error handling for graph with loop. +Check error handling for graph with no edges. diff --git a/tests/unit/igraph_hsbm_game.c b/tests/unit/igraph_hsbm_game.c new file mode 100644 index 0000000..ffe8866 --- /dev/null +++ b/tests/unit/igraph_hsbm_game.c @@ -0,0 +1,74 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_integer_t n, igraph_integer_t m, igraph_vector_t *rho, igraph_matrix_t *pref_matrix, igraph_real_t p) { + igraph_t result; + igraph_hsbm_game(&result, n, m, rho, pref_matrix, p); + print_graph_canon(&result); + printf("\n"); + igraph_destroy(&result); +} + + +int main(void) { + igraph_matrix_t pref_matrix; + igraph_vector_t rho; + + igraph_vector_init_int(&rho, 1, 1); + + igraph_matrix_init(&pref_matrix, 1, 1); + MATRIX(pref_matrix, 0, 0) = 1; + + printf("One block, one vertex.\n"); + call_and_print(1, 1, &rho, &pref_matrix, 0); + + igraph_vector_destroy(&rho); + igraph_matrix_destroy(&pref_matrix); + + { + igraph_vector_init_real(&rho, 3, 0.6, 0.4, 0.0); + + igraph_real_t elems[] = {0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + matrix_init_real_row_major(&pref_matrix, 3, 3, elems); + + printf("One block, two clusters, 6 and 4 vertices in cluster, complete bipartite.\n"); + call_and_print(10, 10, &rho, &pref_matrix, 0); + + igraph_vector_destroy(&rho); + igraph_matrix_destroy(&pref_matrix); + } + + { + igraph_vector_init_real(&rho, 3, 0.6, 0.4, 0.0); + + igraph_real_t elems[] = {0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + matrix_init_real_row_major(&pref_matrix, 3, 3, elems); + + printf("Two blocks, two clusters each, 3 and 2 vertices in cluster, each vertex connected to every other, except those in the same cluster.\n"); + call_and_print(10, 5, &rho, &pref_matrix, 1); + + igraph_vector_destroy(&rho); + igraph_matrix_destroy(&pref_matrix); + } + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_hsbm_game.out b/tests/unit/igraph_hsbm_game.out new file mode 100644 index 0000000..c326939 --- /dev/null +++ b/tests/unit/igraph_hsbm_game.out @@ -0,0 +1,79 @@ +One block, one vertex. +directed: false +vcount: 1 +edges: { +} + +One block, two clusters, 6 and 4 vertices in cluster, complete bipartite. +directed: false +vcount: 10 +edges: { +0 6 +0 7 +0 8 +0 9 +1 6 +1 7 +1 8 +1 9 +2 6 +2 7 +2 8 +2 9 +3 6 +3 7 +3 8 +3 9 +4 6 +4 7 +4 8 +4 9 +5 6 +5 7 +5 8 +5 9 +} + +Two blocks, two clusters each, 3 and 2 vertices in cluster, each vertex connected to every other, except those in the same cluster. +directed: false +vcount: 10 +edges: { +0 3 +0 4 +0 5 +0 6 +0 7 +0 8 +0 9 +1 3 +1 4 +1 5 +1 6 +1 7 +1 8 +1 9 +2 3 +2 4 +2 5 +2 6 +2 7 +2 8 +2 9 +3 5 +3 6 +3 7 +3 8 +3 9 +4 5 +4 6 +4 7 +4 8 +4 9 +5 8 +5 9 +6 8 +6 9 +7 8 +7 9 +} + diff --git a/tests/unit/igraph_hsbm_list_game.c b/tests/unit/igraph_hsbm_list_game.c new file mode 100644 index 0000000..c752dbc --- /dev/null +++ b/tests/unit/igraph_hsbm_list_game.c @@ -0,0 +1,95 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_integer_t n, igraph_vector_int_t *mlist, igraph_vector_list_t *rholist, igraph_matrix_list_t *pref_matrix_list, igraph_real_t p) { + igraph_t result; + igraph_hsbm_list_game(&result, n, mlist, rholist, pref_matrix_list, p); + print_graph_canon(&result); + printf("\n"); + igraph_destroy(&result); +} + + +int main(void) { + igraph_matrix_t pref_matrix; + igraph_matrix_list_t pref_matrix_list; + igraph_vector_t rho; + igraph_vector_list_t rholist; + igraph_vector_int_t mlist; + + igraph_vector_list_init(&rholist, 0); + igraph_vector_init_int(&rho, 1, 1); + igraph_vector_list_push_back(&rholist, &rho); + + igraph_matrix_list_init(&pref_matrix_list, 0); + igraph_matrix_init(&pref_matrix, 1, 1); + MATRIX(pref_matrix, 0, 0) = 1; + igraph_matrix_list_push_back(&pref_matrix_list, &pref_matrix); + + igraph_vector_int_init_int(&mlist, 1, 1); + printf("One block, one vertex.\n"); + call_and_print(1, &mlist, &rholist, &pref_matrix_list, 0); + + igraph_vector_list_clear(&rholist); + igraph_matrix_list_clear(&pref_matrix_list); + igraph_vector_int_destroy(&mlist); + + { + igraph_vector_init_real(&rho, 3, 0.6, 0.4, 0.0); + igraph_vector_list_push_back(&rholist, &rho); + + igraph_real_t elems[] = {0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + matrix_init_real_row_major(&pref_matrix, 3, 3, elems); + igraph_matrix_list_push_back(&pref_matrix_list, &pref_matrix); + + igraph_vector_int_init_int(&mlist, 1, 10); + printf("One block, two clusters, 6 and 4 vertices in cluster, complete bipartite.\n"); + call_and_print(10, &mlist, &rholist, &pref_matrix_list, 0); + + igraph_vector_list_clear(&rholist); + igraph_matrix_list_clear(&pref_matrix_list); + igraph_vector_int_destroy(&mlist); + } + + { + igraph_vector_init_real(&rho, 3, 0.6, 0.4, 0.0); + igraph_vector_list_push_back(&rholist, &rho); + igraph_vector_init_real(&rho, 3, 0.6, 0.4, 0.0); + igraph_vector_list_push_back(&rholist, &rho); + + igraph_real_t elems[] = {0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + matrix_init_real_row_major(&pref_matrix, 3, 3, elems); + igraph_matrix_list_push_back(&pref_matrix_list, &pref_matrix); + matrix_init_real_row_major(&pref_matrix, 3, 3, elems); + igraph_matrix_list_push_back(&pref_matrix_list, &pref_matrix); + + igraph_vector_int_init_int(&mlist, 2, 5, 5); + printf("Two blocks, two clusters each, 3 and 2 vertices in cluster, each vertex connected to every other, except those in the same cluster.\n"); + call_and_print(10, &mlist, &rholist, &pref_matrix_list, 1); + + igraph_vector_list_destroy(&rholist); + igraph_matrix_list_destroy(&pref_matrix_list); + igraph_vector_int_destroy(&mlist); + } + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_hsbm_list_game.out b/tests/unit/igraph_hsbm_list_game.out new file mode 100644 index 0000000..c326939 --- /dev/null +++ b/tests/unit/igraph_hsbm_list_game.out @@ -0,0 +1,79 @@ +One block, one vertex. +directed: false +vcount: 1 +edges: { +} + +One block, two clusters, 6 and 4 vertices in cluster, complete bipartite. +directed: false +vcount: 10 +edges: { +0 6 +0 7 +0 8 +0 9 +1 6 +1 7 +1 8 +1 9 +2 6 +2 7 +2 8 +2 9 +3 6 +3 7 +3 8 +3 9 +4 6 +4 7 +4 8 +4 9 +5 6 +5 7 +5 8 +5 9 +} + +Two blocks, two clusters each, 3 and 2 vertices in cluster, each vertex connected to every other, except those in the same cluster. +directed: false +vcount: 10 +edges: { +0 3 +0 4 +0 5 +0 6 +0 7 +0 8 +0 9 +1 3 +1 4 +1 5 +1 6 +1 7 +1 8 +1 9 +2 3 +2 4 +2 5 +2 6 +2 7 +2 8 +2 9 +3 5 +3 6 +3 7 +3 8 +3 9 +4 5 +4 6 +4 7 +4 8 +4 9 +5 8 +5 9 +6 8 +6 9 +7 8 +7 9 +} + diff --git a/tests/unit/igraph_i_incident.c b/tests/unit/igraph_i_incident.c new file mode 100644 index 0000000..827b0d3 --- /dev/null +++ b/tests/unit/igraph_i_incident.c @@ -0,0 +1,109 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" +#include "../../src/graph/internal.h" + +void call_and_print(igraph_t *graph, igraph_integer_t pnode, igraph_neimode_t mode, igraph_loops_t loops) { + igraph_vector_int_t eids; + igraph_vector_int_init(&eids, 0); + IGRAPH_ASSERT(igraph_i_incident(graph, &eids, pnode, mode, loops) == IGRAPH_SUCCESS); + print_vector_int(&eids); + igraph_vector_int_destroy(&eids); +} + + +int main(void) { + igraph_t g_1, g_lm, g_lmu, g_s1, g_s2; + + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_lmu, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_s1, 2, 1, 0,1, 0,1, 1,0, 1,0, -1); + igraph_small(&g_s2, 2, 1, 0,1, 1,0, 1,0, -1); + + igraph_vector_int_t eids; + igraph_vector_int_init(&eids, 0); + + printf("One vertex:\n"); + call_and_print(&g_1, 0, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + + printf("Vertex with multiple edges, IGRAPH_IN:\n"); + call_and_print(&g_lm, 0, IGRAPH_IN, IGRAPH_LOOPS_ONCE); + + printf("Vertex with multiple edges, IGRAPH_OUT:\n"); + call_and_print(&g_lm, 0, IGRAPH_OUT, IGRAPH_LOOPS_ONCE); + + printf("Vertex with multiple edges, IGRAPH_ALL:\n"); + call_and_print(&g_lm, 0, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("Vertex with multiple edges, undirected:\n"); + call_and_print(&g_lmu, 0, IGRAPH_IN, IGRAPH_LOOPS_ONCE); + + printf("Vertex 1 with loop, IGRAPH_OUT, IGRAPH_NO_LOOPS:\n"); + call_and_print(&g_lm, 1, IGRAPH_OUT, IGRAPH_NO_LOOPS); + + printf("Vertex 1 with loop, IGRAPH_ALL, IGRAPH_NO_LOOPS:\n"); + call_and_print(&g_lm, 1, IGRAPH_ALL, IGRAPH_NO_LOOPS); + + printf("Vertex 1 with loop, undirected, IGRAPH_NO_LOOPS:\n"); + call_and_print(&g_lmu, 1, IGRAPH_IN, IGRAPH_NO_LOOPS); + + printf("Vertex 1 with loop, IGRAPH_OUT, IGRAPH_LOOPS_ONCE:\n"); + call_and_print(&g_lm, 1, IGRAPH_OUT, IGRAPH_LOOPS_ONCE); + + printf("Vertex 1 with loop, IGRAPH_ALL, IGRAPH_LOOPS_ONCE:\n"); + call_and_print(&g_lm, 1, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + + printf("Vertex 1 with loop, undirected, IGRAPH_LOOPS_ONCE:\n"); + call_and_print(&g_lmu, 1, IGRAPH_IN, IGRAPH_LOOPS_ONCE); + + printf("Vertex 1 with loop, IGRAPH_ALL, IGRAPH_LOOPS_TWICE:\n"); + call_and_print(&g_lm, 1, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("Vertex 1 with loop, undirected, IGRAPH_LOOPS_TWICE:\n"); + call_and_print(&g_lmu, 1, IGRAPH_IN, IGRAPH_LOOPS_TWICE); + + printf("Graph with 2 edges from 0 to 1, and 2 from 1 to 0, IGRAPH_ALL:\n"); + call_and_print(&g_s1, 0, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + + printf("Graph with 1 edge from 0 to 1, and 2 from 1 to 0, IGRAPH_ALL:\n"); + call_and_print(&g_s2, 0, IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + + VERIFY_FINALLY_STACK(); + + printf("Trying IGRAPH_LOOPS_TWICE with IGRAPH_OUT:\n"); + CHECK_ERROR(igraph_i_incident(&g_lm, &eids, 0, IGRAPH_OUT, IGRAPH_LOOPS_TWICE), IGRAPH_EINVAL); + + printf("Vertex not in graph:\n"); + CHECK_ERROR(igraph_i_neighbors(&g_lm, &eids, 100, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE), IGRAPH_EINVVID); + + printf("Non-existent mode:\n"); + CHECK_ERROR(igraph_i_neighbors(&g_lm, &eids, 0, (igraph_neimode_t) 100, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE), IGRAPH_EINVMODE); + + igraph_destroy(&g_1); + igraph_destroy(&g_lm); + igraph_destroy(&g_lmu); + igraph_destroy(&g_s1); + igraph_destroy(&g_s2); + igraph_vector_int_destroy(&eids); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_i_incident.out b/tests/unit/igraph_i_incident.out new file mode 100644 index 0000000..b6d9312 --- /dev/null +++ b/tests/unit/igraph_i_incident.out @@ -0,0 +1,33 @@ +One vertex: +( ) +Vertex with multiple edges, IGRAPH_IN: +( 5 4 ) +Vertex with multiple edges, IGRAPH_OUT: +( 0 1 ) +Vertex with multiple edges, IGRAPH_ALL: +( 0 1 5 4 ) +Vertex with multiple edges, undirected: +( 0 5 4 1 ) +Vertex 1 with loop, IGRAPH_OUT, IGRAPH_NO_LOOPS: +( 3 ) +Vertex 1 with loop, IGRAPH_ALL, IGRAPH_NO_LOOPS: +( 0 3 ) +Vertex 1 with loop, undirected, IGRAPH_NO_LOOPS: +( 0 3 ) +Vertex 1 with loop, IGRAPH_OUT, IGRAPH_LOOPS_ONCE: +( 2 3 ) +Vertex 1 with loop, IGRAPH_ALL, IGRAPH_LOOPS_ONCE: +( 0 2 3 ) +Vertex 1 with loop, undirected, IGRAPH_LOOPS_ONCE: +( 0 2 3 ) +Vertex 1 with loop, IGRAPH_ALL, IGRAPH_LOOPS_TWICE: +( 0 2 2 3 ) +Vertex 1 with loop, undirected, IGRAPH_LOOPS_TWICE: +( 0 2 2 3 ) +Graph with 2 edges from 0 to 1, and 2 from 1 to 0, IGRAPH_ALL: +( 1 3 0 2 ) +Graph with 1 edge from 0 to 1, and 2 from 1 to 0, IGRAPH_ALL: +( 0 2 1 ) +Trying IGRAPH_LOOPS_TWICE with IGRAPH_OUT: +Vertex not in graph: +Non-existent mode: diff --git a/tests/unit/igraph_i_layout_sphere.c b/tests/unit/igraph_i_layout_sphere.c new file mode 100644 index 0000000..51824c4 --- /dev/null +++ b/tests/unit/igraph_i_layout_sphere.c @@ -0,0 +1,87 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "layout/layout_internal.h" + +#include "test_utilities.h" + +int main(void) { + igraph_integer_t i; + igraph_matrix_t m; + igraph_real_t x, y, z, r; + + igraph_rng_seed(igraph_rng_default(), 42); /* make tests deterministic */ + + RNG_BEGIN(); + + /* 2D */ + igraph_matrix_init(&m, 1000, 2); + for (i = 0; i < igraph_matrix_nrow(&m); i++) { + MATRIX(m, i, 0) = RNG_UNIF01(); + MATRIX(m, i, 1) = RNG_UNIF01(); + } + igraph_i_layout_sphere_2d(&m, &x, &y, &r); + + for (i = 0; i < igraph_matrix_nrow(&m); i++) { + igraph_real_t dist = hypot(MATRIX(m, i, 0) - x, MATRIX(m, i, 1) - y); + if (dist > r) { + printf("x: %f y: %f r: %f\n", x, y, r); + printf("x: %f y: %f dist: %f (%" IGRAPH_PRId ")\n", + MATRIX(m, i, 0), MATRIX(m, i, 1), dist, i); + return 1; + } + } + igraph_matrix_destroy(&m); + + /* 3D */ + igraph_matrix_init(&m, 1000, 3); + for (i = 0; i < igraph_matrix_nrow(&m); i++) { + MATRIX(m, i, 0) = RNG_UNIF01(); + MATRIX(m, i, 1) = RNG_UNIF01(); + MATRIX(m, i, 2) = RNG_UNIF01(); + } + igraph_i_layout_sphere_3d(&m, &x, &y, &z, &r); + + for (i = 0; i < igraph_matrix_nrow(&m); i++) { + igraph_real_t dist = sqrt((MATRIX(m, i, 0) - x) * (MATRIX(m, i, 0) - x) + + (MATRIX(m, i, 1) - y) * (MATRIX(m, i, 1) - y) + + (MATRIX(m, i, 2) - z) * (MATRIX(m, i, 2) - z)); + if (dist > r) { + printf("x: %f y: %f z: %f r: %f\n", x, y, z, r); + printf("x: %f y: %f z: %f dist: %f (%" IGRAPH_PRId ")\n", + MATRIX(m, i, 0), MATRIX(m, i, 1), MATRIX(m, i, 2), dist, i); + return 1; + } + } + igraph_matrix_destroy(&m); + + RNG_END(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_i_neighbors.c b/tests/unit/igraph_i_neighbors.c new file mode 100644 index 0000000..35dfd7f --- /dev/null +++ b/tests/unit/igraph_i_neighbors.c @@ -0,0 +1,122 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" +#include "../../src/graph/internal.h" + +void call_and_print(igraph_t *graph, igraph_integer_t pnode, igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple) { + igraph_vector_int_t neis; + igraph_vector_int_init(&neis, 0); + IGRAPH_ASSERT(igraph_i_neighbors(graph, &neis, pnode, mode, loops, multiple) == IGRAPH_SUCCESS); + print_vector_int(&neis); + igraph_vector_int_destroy(&neis); +} + + +int main(void) { + igraph_t g_1, g_lm, g_lmu, g_s1, g_s2; + + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_lmu, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_s1, 2, 1, 0,1, 0,1, 1,0, 1,0, -1); + igraph_small(&g_s2, 2, 1, 0,1, 1,0, 1,0, -1); + + igraph_vector_int_t neis; + igraph_vector_int_init(&neis, 0); + + printf("One vertex:\n"); + call_and_print(&g_1, 0, IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("Vertex with multiple edges, IGRAPH_IN, IGRAPH_MULTIPLE:\n"); + call_and_print(&g_lm, 0, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("Vertex with multiple edges, IGRAPH_OUT, IGRAPH_MULTIPLE:\n"); + call_and_print(&g_lm, 0, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("Vertex with multiple edges, IGRAPH_ALL, IGRAPH_MULTIPLE:\n"); + call_and_print(&g_lm, 0, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("Vertex with multiple edges, undirected, IGRAPH_MULTIPLE:\n"); + call_and_print(&g_lmu, 0, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("Vertex with multiple edges, IGRAPH_IN, IGRAPH_NO_MULTIPLE:\n"); + call_and_print(&g_lm, 0, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + + printf("Vertex with multiple edges, IGRAPH_OUT, IGRAPH_NO_MULTIPLE:\n"); + call_and_print(&g_lm, 0, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_NO_MULTIPLE); + + printf("Vertex with multiple edges, IGRAPH_ALL, IGRAPH_NO_MULTIPLE:\n"); + call_and_print(&g_lm, 0, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE); + + printf("Vertex with multiple edges, undirected, IGRAPH_NO_MULTIPLE:\n"); + call_and_print(&g_lmu, 0, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + + printf("Vertex 1 with loop, IGRAPH_OUT, IGRAPH_NO_LOOPS:\n"); + call_and_print(&g_lm, 1, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + + printf("Vertex 1 with loop, IGRAPH_ALL, IGRAPH_NO_LOOPS:\n"); + call_and_print(&g_lm, 1, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + + printf("Vertex 1 with loop, undirected, IGRAPH_NO_LOOPS:\n"); + call_and_print(&g_lmu, 1, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + + printf("Vertex 1 with loop, IGRAPH_OUT, IGRAPH_LOOPS_ONCE:\n"); + call_and_print(&g_lm, 1, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("Vertex 1 with loop, IGRAPH_ALL, IGRAPH_LOOPS_ONCE:\n"); + call_and_print(&g_lm, 1, IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("Vertex 1 with loop, undirected, IGRAPH_LOOPS_ONCE:\n"); + call_and_print(&g_lmu, 1, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("Vertex 1 with loop, IGRAPH_ALL, IGRAPH_LOOPS_TWICE:\n"); + call_and_print(&g_lm, 1, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("Vertex 1 with loop, undirected, IGRAPH_LOOPS_TWICE:\n"); + call_and_print(&g_lmu, 1, IGRAPH_IN, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE); + + printf("Graph with 2 edges from 0 to 1, and 2 from 1 to 0, IGRAPH_ALL, IGRAPH_MULTIPLE:\n"); + call_and_print(&g_s1, 0, IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + printf("Graph with 1 edge from 0 to 1, and 2 from 1 to 0, IGRAPH_ALL, IGRAPH_MULTIPLE:\n"); + call_and_print(&g_s2, 0, IGRAPH_ALL, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Trying IGRAPH_LOOPS_TWICE with IGRAPH_OUT:\n"); + IGRAPH_ASSERT(igraph_i_neighbors(&g_lm, &neis, 0, IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_NO_MULTIPLE) == IGRAPH_EINVAL); + + printf("Trying invalid vertex ID:\n"); + IGRAPH_ASSERT(igraph_i_neighbors(&g_lm, &neis, 42, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE) == IGRAPH_EINVVID); + + printf("Trying invalid mode:\n"); + IGRAPH_ASSERT(igraph_i_neighbors(&g_lm, &neis, 0, (igraph_neimode_t) 42, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE) == IGRAPH_EINVMODE); + + igraph_destroy(&g_1); + igraph_destroy(&g_lm); + igraph_destroy(&g_lmu); + igraph_destroy(&g_s1); + igraph_destroy(&g_s2); + igraph_vector_int_destroy(&neis); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_i_neighbors.out b/tests/unit/igraph_i_neighbors.out new file mode 100644 index 0000000..3a2eea4 --- /dev/null +++ b/tests/unit/igraph_i_neighbors.out @@ -0,0 +1,41 @@ +One vertex: +( ) +Vertex with multiple edges, IGRAPH_IN, IGRAPH_MULTIPLE: +( 2 2 ) +Vertex with multiple edges, IGRAPH_OUT, IGRAPH_MULTIPLE: +( 1 2 ) +Vertex with multiple edges, IGRAPH_ALL, IGRAPH_MULTIPLE: +( 1 2 2 2 ) +Vertex with multiple edges, undirected, IGRAPH_MULTIPLE: +( 1 2 2 2 ) +Vertex with multiple edges, IGRAPH_IN, IGRAPH_NO_MULTIPLE: +( 2 ) +Vertex with multiple edges, IGRAPH_OUT, IGRAPH_NO_MULTIPLE: +( 1 2 ) +Vertex with multiple edges, IGRAPH_ALL, IGRAPH_NO_MULTIPLE: +( 1 2 ) +Vertex with multiple edges, undirected, IGRAPH_NO_MULTIPLE: +( 1 2 ) +Vertex 1 with loop, IGRAPH_OUT, IGRAPH_NO_LOOPS: +( 3 ) +Vertex 1 with loop, IGRAPH_ALL, IGRAPH_NO_LOOPS: +( 0 3 ) +Vertex 1 with loop, undirected, IGRAPH_NO_LOOPS: +( 0 3 ) +Vertex 1 with loop, IGRAPH_OUT, IGRAPH_LOOPS_ONCE: +( 1 3 ) +Vertex 1 with loop, IGRAPH_ALL, IGRAPH_LOOPS_ONCE: +( 0 1 3 ) +Vertex 1 with loop, undirected, IGRAPH_LOOPS_ONCE: +( 0 1 3 ) +Vertex 1 with loop, IGRAPH_ALL, IGRAPH_LOOPS_TWICE: +( 0 1 1 3 ) +Vertex 1 with loop, undirected, IGRAPH_LOOPS_TWICE: +( 0 1 1 3 ) +Graph with 2 edges from 0 to 1, and 2 from 1 to 0, IGRAPH_ALL, IGRAPH_MULTIPLE: +( 1 1 1 1 ) +Graph with 1 edge from 0 to 1, and 2 from 1 to 0, IGRAPH_ALL, IGRAPH_MULTIPLE: +( 1 1 1 ) +Trying IGRAPH_LOOPS_TWICE with IGRAPH_OUT: +Trying invalid vertex ID: +Trying invalid mode: diff --git a/tests/unit/igraph_i_umap_fit_ab.c b/tests/unit/igraph_i_umap_fit_ab.c new file mode 100644 index 0000000..7518159 --- /dev/null +++ b/tests/unit/igraph_i_umap_fit_ab.c @@ -0,0 +1,53 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "layout/layout_internal.h" + +#include "test_utilities.h" + +int main(void) { + size_t i; + igraph_real_t a, b; + + igraph_rng_seed(igraph_rng_default(), 42); /* make tests deterministic */ + igraph_real_t min_dists[8] = {0, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1.0}; + + RNG_BEGIN(); + + /* test with various typical min_dist values. Originally there is a scaling sigma + * factor, but it's 1.0 in all default cases so we fix it for now */ + for (i = 0; i < sizeof(min_dists) / sizeof(min_dists[0]); i++) { + igraph_i_umap_fit_ab(min_dists[i], &a, &b); + printf("%g, %.1g, %.1g\n", min_dists[i], a, b); + } + + RNG_END(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_i_umap_fit_ab.o b/tests/unit/igraph_i_umap_fit_ab.o new file mode 100644 index 0000000..de1cee9 --- /dev/null +++ b/tests/unit/igraph_i_umap_fit_ab.o @@ -0,0 +1,8 @@ +0, 1.9, 0.8 +0.001, 1.9, 0.8 +0.003, 1.9, 0.8 +0.01, 1.9, 0.8 +0.03, 1.8, 0.8 +0.1, 1.6, 0.9 +0.3, 1.0, 1.1 +1.0, 0.1, 1.9 diff --git a/tests/unit/igraph_induced_subgraph.c b/tests/unit/igraph_induced_subgraph.c new file mode 100644 index 0000000..97eb4de --- /dev/null +++ b/tests/unit/igraph_induced_subgraph.c @@ -0,0 +1,112 @@ +/* -*- mode: C -*- */ +/* + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g, sub; + igraph_vector_int_t keep; + + /* test with a simple directed graph, copy-and-delete implementation */ + igraph_small(&g, 9, IGRAPH_DIRECTED, 0, 1, 0, 2, 1, 3, 2, 3, + 1, 4, 4, 2, 1, 5, 5, 2, 1, 6, 6, 2, 1, 7, 7, 2, 1, 8, 8, 2, + -1); + igraph_vector_int_init_int_end(&keep, -1, 0, 1, 2, 4, -1); + igraph_induced_subgraph(&g, &sub, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_COPY_AND_DELETE); + print_graph(&sub); + igraph_vector_int_destroy(&keep); + igraph_destroy(&sub); + igraph_destroy(&g); + + printf("==============\n"); + + /* test with a simple directed graph, create-from-scratch implementation */ + igraph_small(&g, 9, IGRAPH_DIRECTED, 0, 1, 0, 2, 1, 3, 2, 3, + 1, 4, 4, 2, 1, 5, 5, 2, 1, 6, 6, 2, 1, 7, 7, 2, 1, 8, 8, 2, + -1); + igraph_vector_int_init_int_end(&keep, -1, 0, 1, 2, 4, -1); + igraph_induced_subgraph(&g, &sub, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH); + print_graph(&sub); + igraph_vector_int_destroy(&keep); + igraph_destroy(&sub); + igraph_destroy(&g); + + printf("==============\n"); + + /* test with a graph that has loop edges, copy-and-delete implementation */ + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0, 1, 0, 2, 1, 1, -1); + igraph_vector_int_init_int_end(&keep, -1, 0, 1, -1); + igraph_induced_subgraph(&g, &sub, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_COPY_AND_DELETE); + print_graph(&sub); + igraph_vector_int_destroy(&keep); + igraph_destroy(&sub); + igraph_destroy(&g); + + printf("==============\n"); + + /* test with a graph that has loop edges, create-from-scratch implementation */ + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0, 1, 0, 2, 1, 1, -1); + igraph_vector_int_init_int_end(&keep, -1, 0, 1, -1); + igraph_induced_subgraph(&g, &sub, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH); + print_graph(&sub); + igraph_vector_int_destroy(&keep); + igraph_destroy(&sub); + igraph_destroy(&g); + + printf("==============\n"); + + /* regression test for issue #2398 on GitHub */ + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0, 1, 1, 2, -1); + igraph_vector_int_init_int_end(&keep, -1, 0, 1, 0, 1, 0, 1, -1); + + igraph_induced_subgraph(&g, &sub, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH); + print_graph(&sub); + igraph_destroy(&sub); + + igraph_induced_subgraph(&g, &sub, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_COPY_AND_DELETE); + print_graph(&sub); + igraph_destroy(&sub); + + igraph_vector_int_destroy(&keep); + igraph_destroy(&g); + + printf("==============\n"); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_induced_subgraph.out b/tests/unit/igraph_induced_subgraph.out new file mode 100644 index 0000000..ff57267 --- /dev/null +++ b/tests/unit/igraph_induced_subgraph.out @@ -0,0 +1,43 @@ +directed: true +vcount: 4 +edges: { +0 1 +0 2 +1 3 +3 2 +} +============== +directed: true +vcount: 4 +edges: { +0 1 +0 2 +1 3 +3 2 +} +============== +directed: false +vcount: 2 +edges: { +1 0 +1 1 +} +============== +directed: false +vcount: 2 +edges: { +1 0 +1 1 +} +============== +directed: false +vcount: 2 +edges: { +1 0 +} +directed: false +vcount: 2 +edges: { +1 0 +} +============== \ No newline at end of file diff --git a/tests/unit/igraph_induced_subgraph_edges.c b/tests/unit/igraph_induced_subgraph_edges.c new file mode 100644 index 0000000..9d01608 --- /dev/null +++ b/tests/unit/igraph_induced_subgraph_edges.c @@ -0,0 +1,54 @@ +/* + 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, see . + */ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_int_t edges; + + igraph_vector_int_init(&edges, 0); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + // 0 1 2 3 4 5 6 7 8 9 10 + 4,4, 0,3, 0,3, 2,3, 1,4, 2,4, 3,4, 2,3, 4,4, 2,3, 1,1, + -1); + + igraph_induced_subgraph_edges(&graph, igraph_vss_range(2,5), &edges); + igraph_vector_int_sort(&edges); /* canonicalize */ + print_vector_int(&edges); + + igraph_induced_subgraph_edges(&graph, igraph_vss_1(0), &edges); + igraph_vector_int_sort(&edges); + print_vector_int(&edges); + + igraph_induced_subgraph_edges(&graph, igraph_vss_1(1), &edges); + igraph_vector_int_sort(&edges); + print_vector_int(&edges); + + igraph_induced_subgraph_edges(&graph, igraph_vss_all(), &edges); + igraph_vector_int_sort(&edges); + print_vector_int(&edges); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&edges); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_induced_subgraph_edges.out b/tests/unit/igraph_induced_subgraph_edges.out new file mode 100644 index 0000000..4856679 --- /dev/null +++ b/tests/unit/igraph_induced_subgraph_edges.out @@ -0,0 +1,4 @@ +( 0 3 5 6 7 8 9 ) +( ) +( 10 ) +( 0 1 2 3 4 5 6 7 8 9 10 ) diff --git a/tests/unit/igraph_induced_subgraph_map.c b/tests/unit/igraph_induced_subgraph_map.c new file mode 100644 index 0000000..dde78c0 --- /dev/null +++ b/tests/unit/igraph_induced_subgraph_map.c @@ -0,0 +1,66 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g, sub; + igraph_vector_int_t map, invmap; + igraph_vector_int_t keep; + igraph_integer_t i; + + igraph_small(&g, 9, IGRAPH_DIRECTED, 0, 1, 0, 2, 1, 3, 2, 3, + 1, 4, 4, 2, 1, 5, 5, 2, 1, 6, 6, 2, 1, 7, 7, 2, 1, 8, 8, 2, + -1); + igraph_vector_int_init(&map, 0); + igraph_vector_int_init(&invmap, 0); + igraph_vector_int_init(&keep, igraph_vcount(&g)); + for (i = 0; i < igraph_vector_int_size(&keep); i++) { + VECTOR(keep)[i] = i; + } + + igraph_induced_subgraph_map(&g, &sub, + igraph_vss_vector(&keep), + IGRAPH_SUBGRAPH_COPY_AND_DELETE, + &map, &invmap); + + printf("Map: "); + igraph_vector_int_print(&map); + printf("Inverse map: "); + igraph_vector_int_print(&invmap); + print_graph(&sub); + + igraph_vector_int_destroy(&keep); + igraph_vector_int_destroy(&map); + igraph_vector_int_destroy(&invmap); + + igraph_destroy(&sub); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_induced_subgraph_map.out b/tests/unit/igraph_induced_subgraph_map.out new file mode 100644 index 0000000..9e9b3d9 --- /dev/null +++ b/tests/unit/igraph_induced_subgraph_map.out @@ -0,0 +1,20 @@ +Map: 1 2 3 4 5 6 7 8 9 +Inverse map: 0 1 2 3 4 5 6 7 8 +directed: true +vcount: 9 +edges: { +0 1 +0 2 +1 3 +2 3 +1 4 +4 2 +1 5 +5 2 +1 6 +6 2 +1 7 +7 2 +1 8 +8 2 +} diff --git a/tests/unit/igraph_intersection.c b/tests/unit/igraph_intersection.c new file mode 100644 index 0000000..9dced9d --- /dev/null +++ b/tests/unit/igraph_intersection.c @@ -0,0 +1,63 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t star, ring, uni, result; + igraph_vector_ptr_t glist; + + igraph_star(&star, 11, IGRAPH_STAR_UNDIRECTED, /*center=*/ 10); + igraph_ring(&ring, 10, IGRAPH_UNDIRECTED, /*mutual=*/ false, /*circular=*/ true); + + igraph_union(&uni, &star, &ring, /*edge_map1=*/ NULL, /*edge_map2=*/ NULL); + + igraph_intersection(&result, &uni, &star, /*edge_map1*/ NULL, /*edge_map2=*/ NULL); + print_graph_canon(&result); + + igraph_destroy(&result); + + /* ---------------------------- */ + + igraph_vector_ptr_init(&glist, 2); + VECTOR(glist)[0] = &uni; + VECTOR(glist)[1] = ☆ + + igraph_intersection_many(&result, &glist, /*edgemaps=*/ NULL); + printf("--\n"); + print_graph_canon(&result); + + igraph_vector_ptr_destroy(&glist); + igraph_destroy(&result); + igraph_destroy(&uni); + igraph_destroy(&ring); + igraph_destroy(&star); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_intersection.out b/tests/unit/igraph_intersection.out new file mode 100644 index 0000000..5946252 --- /dev/null +++ b/tests/unit/igraph_intersection.out @@ -0,0 +1,29 @@ +directed: false +vcount: 11 +edges: { +0 10 +1 10 +2 10 +3 10 +4 10 +5 10 +6 10 +7 10 +8 10 +9 10 +} +-- +directed: false +vcount: 11 +edges: { +0 10 +1 10 +2 10 +3 10 +4 10 +5 10 +6 10 +7 10 +8 10 +9 10 +} diff --git a/tests/unit/igraph_is_acyclic.c b/tests/unit/igraph_is_acyclic.c new file mode 100644 index 0000000..368edfb --- /dev/null +++ b/tests/unit/igraph_is_acyclic.c @@ -0,0 +1,111 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_bool_t acyclic; + + /* Null graph directed */ + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + /* Null graph undirected */ + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + /* Singleton graph directed */ + igraph_empty(&graph, 1, IGRAPH_DIRECTED); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + /* Singleton graph undirected */ + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + /* Directed cyclic */ + igraph_small(&graph, 4, IGRAPH_DIRECTED, + 0,1, 2,0, 1,3, 3,2, + -1); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(!acyclic); + igraph_destroy(&graph); + + /* Directed acyclic */ + igraph_small(&graph, 3, IGRAPH_DIRECTED, + 0,1, 2,0, 1,3, + -1); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + /* Undirected cyclic */ + igraph_small(&graph, 4, IGRAPH_UNDIRECTED, + 0,1, 2,0, 1,3, 3,2, + -1); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(! acyclic); + igraph_destroy(&graph); + + /* Undirected acyclic */ + igraph_small(&graph, 3, IGRAPH_UNDIRECTED, + 0,1, 2,0, 1,3, + -1); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + /* Self loop directed */ + igraph_small(&graph, 4, IGRAPH_DIRECTED, + 0,1, 1,3, 3,2, 2,2, + -1); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(! acyclic); + igraph_destroy(&graph); + + /* Self loop undirected */ + igraph_small(&graph, 4, IGRAPH_UNDIRECTED, + 0,1, 2,0, 1,3, 3,3, + -1); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(! acyclic); + igraph_destroy(&graph); + + /* Directed acyclic graph which would be cyclic if undirected */ + igraph_small(&graph, 3, IGRAPH_DIRECTED, + 0,1, 1,2, 0,2, + -1); + igraph_is_acyclic(&graph, &acyclic); + IGRAPH_ASSERT(acyclic); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; + +} diff --git a/tests/unit/igraph_is_biconnected.c b/tests/unit/igraph_is_biconnected.c new file mode 100644 index 0000000..3e5834d --- /dev/null +++ b/tests/unit/igraph_is_biconnected.c @@ -0,0 +1,135 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_bool_t result; + + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + igraph_empty(&g, 2, IGRAPH_UNDIRECTED); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + igraph_small(&g, 2, IGRAPH_UNDIRECTED, + 0,1, + -1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(result); + igraph_destroy(&g); + + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,3, 3,0, + 2,4, 4,5, 5,2, + -1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + igraph_ring(&g, 10, IGRAPH_UNDIRECTED, 0, 1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(result); + igraph_destroy(&g); + + igraph_small(&g, 7, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, 1,3, + -1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + igraph_small(&g, 5, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, 1,3, 3,4, 4,2, + -1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(result); + igraph_destroy(&g); + + igraph_small(&g, 7, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, 1,3, 3,4, + -1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + /* Two disjoint cycles */ + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, 3,4, 4,5, 5,3, + -1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + /* Cycle + isolated vertex */ + igraph_small(&g, 4, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, + -1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + /* Special case: the root is an articulation point */ + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, 0,3, 3,4, 4,0, + -1); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(!result); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + /* Cache concistency checks */ + + /* K_2 is a special graph: it is biconnected yet it has no cycles. + * Make sure we don't accidentally set or interpret the cache + * incorrectly. */ + + igraph_bool_t acyclic; + + igraph_full(&g, 2, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(result); + igraph_is_acyclic(&g, &acyclic); + IGRAPH_ASSERT(acyclic); + + igraph_invalidate_cache(&g); + + igraph_is_acyclic(&g, &acyclic); + IGRAPH_ASSERT(acyclic); + igraph_is_biconnected(&g, &result); + IGRAPH_ASSERT(result); + + igraph_destroy(&g); + + + return 0; +} diff --git a/tests/unit/igraph_is_bigraphical.c b/tests/unit/igraph_is_bigraphical.c new file mode 100644 index 0000000..95ee5a5 --- /dev/null +++ b/tests/unit/igraph_is_bigraphical.c @@ -0,0 +1,54 @@ + +#include + +#include "test_utilities.h" + +#define BIGRAPHICAL_PRINT_DESTROY(deg1, deg2) \ + igraph_is_bigraphical(&(deg1), &(deg2), IGRAPH_SIMPLE_SW, &simple); \ + igraph_is_bigraphical(&(deg1), &(deg2), IGRAPH_MULTI_SW, &multi); \ + print_vector_int(&(deg1)); \ + print_vector_int(&(deg2)); \ + printf("simple: %s, multi: %s\n\n", simple ? "true" : "false", multi ? "true" : "false"); \ + igraph_vector_int_destroy(&(deg1)); \ + igraph_vector_int_destroy(&(deg2)); + +int main(void) { + igraph_vector_int_t deg1, deg2; + igraph_bool_t simple, multi; + + igraph_vector_int_init(°1, 0); + igraph_vector_int_init(°2, 0); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + igraph_vector_int_init_int_end(°1, -1, 3, 3, -1); + igraph_vector_int_init_int_end(°2, -1, 1, 2, 3, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + igraph_vector_int_init_int_end(°1, -1, 3, 2, 1, -1); + igraph_vector_int_init_int_end(°2, -1, 1, 2, 3, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + igraph_vector_int_init_int_end(°1, -1, 1, 1, 1, 1, -1); + igraph_vector_int_init_int_end(°2, -1, 2, 3, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + igraph_vector_int_init_int_end(°1, -1, 1, 1, 1, 1, -1); + igraph_vector_int_init_int_end(°2, -1, 2, 2, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + igraph_vector_int_init_int_end(°1, -1, 1, 2, 0, 3, 0, -1); + igraph_vector_int_init_int_end(°2, -1, 2, 3, 1, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + igraph_vector_int_init_int_end(°1, -1, 5, 2, -1); + igraph_vector_int_init_int_end(°2, -1, 1, 2, 2, 2, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + igraph_vector_int_init_int_end(°1, -1, -2, 2, 6, -1); + igraph_vector_int_init_int_end(°2, -1, 0, 2, 2, 2, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_bigraphical.out b/tests/unit/igraph_is_bigraphical.out new file mode 100644 index 0000000..1533667 --- /dev/null +++ b/tests/unit/igraph_is_bigraphical.out @@ -0,0 +1,32 @@ +( ) +( ) +simple: true, multi: true + +( 3 3 ) +( 1 2 3 ) +simple: false, multi: true + +( 3 2 1 ) +( 1 2 3 ) +simple: true, multi: true + +( 1 1 1 1 ) +( 2 3 ) +simple: false, multi: false + +( 1 1 1 1 ) +( 2 2 ) +simple: true, multi: true + +( 1 2 0 3 0 ) +( 2 3 1 ) +simple: true, multi: true + +( 5 2 ) +( 1 2 2 2 ) +simple: false, multi: true + +( -2 2 6 ) +( 0 2 2 2 ) +simple: false, multi: false + diff --git a/tests/unit/igraph_is_bipartite.c b/tests/unit/igraph_is_bipartite.c new file mode 100644 index 0000000..f608098 --- /dev/null +++ b/tests/unit/igraph_is_bipartite.c @@ -0,0 +1,79 @@ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t graph; + igraph_bool_t bipartite, acyclic, has_loop; + igraph_vector_bool_t types; + + igraph_vector_bool_init(&types, 5); + + /* Null graph */ + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_is_bipartite(&graph, &bipartite, &types); + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(igraph_vector_bool_size(&types) == igraph_vcount(&graph)); + igraph_destroy(&graph); + + /* Singleton graph */ + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_is_bipartite(&graph, &bipartite, &types); + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(igraph_vector_bool_size(&types) == igraph_vcount(&graph)); + igraph_destroy(&graph); + + /* Singleton with self-loop */ + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, + 0, 0, + -1); + igraph_is_bipartite(&graph, &bipartite, &types); + IGRAPH_ASSERT(! bipartite); + IGRAPH_ASSERT(igraph_vector_bool_size(&types) == igraph_vcount(&graph)); + + /* Test cache usage */ + igraph_has_loop(&graph, &has_loop); + igraph_is_bipartite(&graph, &bipartite, NULL); + IGRAPH_ASSERT(! bipartite); + + igraph_destroy(&graph); + + /* Directed path */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, + 0,1, 1,2, + -1); + + igraph_is_bipartite(&graph, &bipartite, &types); + IGRAPH_ASSERT(bipartite); + IGRAPH_ASSERT(igraph_vector_bool_size(&types) == igraph_vcount(&graph)); + + /* Test cache usage */ + bipartite = false; + igraph_is_forest(&graph, &acyclic, NULL, IGRAPH_ALL); + igraph_is_bipartite(&graph, &bipartite, NULL); + IGRAPH_ASSERT(bipartite); + + /* Odd directed cycle */ + igraph_add_edge(&graph, 2, 0); + igraph_invalidate_cache(&graph); + + igraph_is_bipartite(&graph, &bipartite, &types); + IGRAPH_ASSERT(! bipartite); + IGRAPH_ASSERT(igraph_vector_bool_size(&types) == igraph_vcount(&graph)); + + /* Test cache usage */ + bipartite = true; + igraph_is_forest(&graph, &acyclic, NULL, IGRAPH_ALL); + igraph_is_bipartite(&graph, &bipartite, NULL); + IGRAPH_ASSERT(! bipartite); + + igraph_destroy(&graph); + + igraph_vector_bool_destroy(&types); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_chordal.c b/tests/unit/igraph_is_chordal.c new file mode 100644 index 0000000..759b207 --- /dev/null +++ b/tests/unit/igraph_is_chordal.c @@ -0,0 +1,99 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, igraph_vector_int_t *alpha, igraph_vector_int_t *alpham1, + igraph_bool_t fill, igraph_bool_t ng) { + igraph_bool_t chordal; + igraph_vector_int_t fill_in; + igraph_t newgraph; + igraph_vector_int_init(&fill_in, 0); + IGRAPH_ASSERT(igraph_is_chordal(graph, alpha, alpham1, &chordal, + fill ? &fill_in : NULL, ng? &newgraph : NULL) == IGRAPH_SUCCESS); + printf("Is chordal: %d\nFill in:\n", chordal); + print_vector_int(&fill_in); + if (ng) { + printf("New graph:\n"); + print_graph_canon(&newgraph); + igraph_destroy(&newgraph); + } + printf("\n"); + igraph_vector_int_destroy(&fill_in); +} + + +int main(void) { + igraph_t g_0, g_1, g_lmu; + igraph_bool_t chordal; + igraph_vector_int_t alpha, alpham1; + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_lmu, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices:\n"); + call_and_print(&g_0, NULL, NULL, 1, 1); + + printf("One vertex:\n"); + call_and_print(&g_1, NULL, NULL, 1, 1); + + printf("One vertex, don't calculate anything.\n\n"); + IGRAPH_ASSERT(igraph_is_chordal(&g_1, NULL, NULL, NULL, NULL, NULL) == IGRAPH_SUCCESS); + + printf("Disconnected graph with loops and multiple edges:\n"); + call_and_print(&g_lmu, NULL, NULL, 1, 1); + + printf("Same graph, don't ask for fill_in vector:\n"); + call_and_print(&g_lmu, NULL, NULL, 0, 1); + + printf("Same graph, don't ask for fill_in vector or newgraph:\n"); + call_and_print(&g_lmu, NULL, NULL, 0, 0); + + printf("Same graph, own calculation of alpha and its inverse:\n"); + igraph_vector_int_init(&alpha, 0); + igraph_vector_int_init(&alpham1, 0); + igraph_maximum_cardinality_search(&g_lmu, &alpha, &alpham1); + call_and_print(&g_lmu, &alpha, &alpham1, 1, 1); + + printf("Same graph, own calculation of alpha:\n"); + call_and_print(&g_lmu, &alpha, NULL, 1, 1); + + printf("Same graph, own calculation of inverse alpha:\n"); + call_and_print(&g_lmu, NULL, &alpham1, 1, 1); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Wrong size alpha.\n"); + igraph_vector_int_clear(&alpha); + IGRAPH_ASSERT(igraph_is_chordal(&g_lmu, &alpha, NULL, &chordal, NULL, NULL) == IGRAPH_EINVAL); + + printf("Wrong size alpham1.\n"); + IGRAPH_ASSERT(igraph_is_chordal(&g_lmu, NULL, &alpha, &chordal, NULL, NULL) == IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_lmu); + igraph_vector_int_destroy(&alpha); + igraph_vector_int_destroy(&alpham1); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_is_chordal.out b/tests/unit/igraph_is_chordal.out new file mode 100644 index 0000000..c4e3370 --- /dev/null +++ b/tests/unit/igraph_is_chordal.out @@ -0,0 +1,129 @@ +No vertices: +Is chordal: 1 +Fill in: +( ) +New graph: +directed: false +vcount: 0 +edges: { +} + +One vertex: +Is chordal: 1 +Fill in: +( ) +New graph: +directed: false +vcount: 1 +edges: { +} + +One vertex, don't calculate anything. + +Disconnected graph with loops and multiple edges: +Is chordal: 0 +Fill in: +( 3 0 ) +New graph: +directed: false +vcount: 6 +edges: { +0 1 +0 2 +0 2 +0 2 +0 3 +1 1 +1 3 +2 3 +3 4 +3 4 +} + +Same graph, don't ask for fill_in vector: +Is chordal: 0 +Fill in: +( ) +New graph: +directed: false +vcount: 6 +edges: { +0 1 +0 2 +0 2 +0 2 +0 3 +1 1 +1 3 +2 3 +3 4 +3 4 +} + +Same graph, don't ask for fill_in vector or newgraph: +Is chordal: 0 +Fill in: +( ) + +Same graph, own calculation of alpha and its inverse: +Is chordal: 0 +Fill in: +( 3 0 ) +New graph: +directed: false +vcount: 6 +edges: { +0 1 +0 2 +0 2 +0 2 +0 3 +1 1 +1 3 +2 3 +3 4 +3 4 +} + +Same graph, own calculation of alpha: +Is chordal: 0 +Fill in: +( 3 0 ) +New graph: +directed: false +vcount: 6 +edges: { +0 1 +0 2 +0 2 +0 2 +0 3 +1 1 +1 3 +2 3 +3 4 +3 4 +} + +Same graph, own calculation of inverse alpha: +Is chordal: 0 +Fill in: +( 3 0 ) +New graph: +directed: false +vcount: 6 +edges: { +0 1 +0 2 +0 2 +0 2 +0 3 +1 1 +1 3 +2 3 +3 4 +3 4 +} + +Wrong size alpha. +Wrong size alpham1. diff --git a/tests/unit/igraph_is_clique.c b/tests/unit/igraph_is_clique.c new file mode 100644 index 0000000..01e7f71 --- /dev/null +++ b/tests/unit/igraph_is_clique.c @@ -0,0 +1,145 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_bool_t is_clique, is_indep_set; + igraph_vector_int_t vids; + + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 2, 0, 3, 0, 4, 0, 6, 0, 7, 0, 8, 0, 9, 1, 2, 1, 4, 2, 1, 2, 3, 2, \ + 7, 2, 8, 2, 9, 3, 0, 3, 1, 3, 2, 3, 4, 3, 5, 3, 6, 3, 7, 3, 9, 4, 0, \ + 4, 2, 4, 3, 4, 5, 4, 6, 5, 1, 5, 2, 5, 4, 5, 7, 5, 9, 6, 0, 6, 1, 6, \ + 4, 6, 8, 7, 2, 7, 3, 7, 6, 7, 9, 8, 2, 8, 4, 8, 6, 8, 7, 9, 0, 9, 2, \ + 9, 3, 9, 4, 9, 7, 9, 8, + -1); + + /* Empty set */ + igraph_vector_int_init(&vids, 0); + is_clique = false; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + /* Singleton set */ + is_clique = false; + igraph_is_clique(&g, igraph_vss_1(0), true, &is_clique); + IGRAPH_ASSERT(is_clique); + + igraph_vector_int_init_int_end(&vids, -1, + 1, 2, -1); + is_clique = false; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + /* It is a clique: Duplicates handled? */ + igraph_vector_int_init_int_end(&vids, -1, + 1, 2, 2, 2, 1, -1); + is_clique = false; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + igraph_vector_int_init_int_end(&vids, -1, + 9, 2, 7, 3, -1); + is_clique = false; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + /* Not a clique, in either a directed or undirected sense. */ + igraph_vector_int_init_int_end(&vids, -1, + 3, 8, 5, -1); + is_clique = true; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(! is_clique); + is_clique = true; + igraph_is_clique(&g, igraph_vss_vector(&vids), false, &is_clique); + IGRAPH_ASSERT(! is_clique); + igraph_vector_int_destroy(&vids); + + /* Not a clique: Duplicates handled? */ + igraph_vector_int_init_int_end(&vids, -1, + 5, 3, 4, 3, 4, 5, 5, 5, -1); + is_clique = true; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(! is_clique); + igraph_vector_int_destroy(&vids); + + /* This is only an undirected clique, but not a directed one. */ + igraph_vector_int_init_int_end(&vids, -1, + 4, 0, 8, 9, 2, -1); + is_clique = true; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(! is_clique); + igraph_is_clique(&g, igraph_vss_vector(&vids), false, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + /* Complete vertex set */ + + is_clique = true; + igraph_is_clique(&g, igraph_vss_all(), true, &is_clique); + IGRAPH_ASSERT(! is_clique); + + is_clique = true; + igraph_is_clique(&g, igraph_vss_all(), false, &is_clique); + IGRAPH_ASSERT(! is_clique); + + /* Independent sets */ + + /* Empty set */ + igraph_vector_int_init(&vids, 0); + is_indep_set = false; + igraph_is_independent_vertex_set(&g, igraph_vss_vector(&vids), &is_indep_set); + IGRAPH_ASSERT(is_indep_set); + igraph_vector_int_destroy(&vids); + + /* Singleton set */ + is_indep_set = false; + igraph_is_independent_vertex_set(&g, igraph_vss_1(5), &is_indep_set); + IGRAPH_ASSERT(is_indep_set); + + igraph_vector_int_init_int_end(&vids, -1, + 6, 9, -1); + is_indep_set = false; + igraph_is_independent_vertex_set(&g, igraph_vss_vector(&vids), &is_indep_set); + IGRAPH_ASSERT(is_indep_set); + igraph_vector_int_destroy(&vids); + + igraph_vector_int_init_int_end(&vids, -1, + 5, 6, 9, -1); + is_indep_set = true; + igraph_is_independent_vertex_set(&g, igraph_vss_vector(&vids), &is_indep_set); + IGRAPH_ASSERT(! is_indep_set); + igraph_vector_int_destroy(&vids); + + is_indep_set = true; + igraph_is_independent_vertex_set(&g, igraph_vss_all(), &is_indep_set); + IGRAPH_ASSERT(! is_indep_set); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_complete.c b/tests/unit/igraph_is_complete.c new file mode 100644 index 0000000..be7a847 --- /dev/null +++ b/tests/unit/igraph_is_complete.c @@ -0,0 +1,207 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +/* Check that the null graph is complete */ +static igraph_error_t check_null(void) +{ + igraph_t null; + igraph_bool_t complete; + + igraph_full(&null, 0, false, false); + IGRAPH_ASSERT(!igraph_vcount(&null)); + IGRAPH_ASSERT(!igraph_ecount(&null)); + igraph_is_complete(&null, &complete); + IGRAPH_ASSERT(complete); + + igraph_destroy(&null); + + return IGRAPH_SUCCESS; +} + +/* Check that the singleton graph is complete */ +static igraph_error_t check_singleton(void) +{ + igraph_t singleton; + igraph_bool_t complete; + + igraph_full(&singleton, 1, false, false); + IGRAPH_ASSERT(igraph_vcount(&singleton) == 1); + IGRAPH_ASSERT(!igraph_ecount(&singleton)); + igraph_is_complete(&singleton, &complete); + IGRAPH_ASSERT(complete); + + igraph_destroy(&singleton); + + return IGRAPH_SUCCESS; +} + +/* Run checks on other non-trivial graphs */ +/* - Generating a full graph without any loops, adding the mutuals if needed */ +/* - Checking that it is complete */ +/* - Removing an edge */ +/* - Checking that it is not complete anymore */ +/* - Then doing the same for multiple graphs */ +static igraph_error_t check(const igraph_bool_t directed, const igraph_integer_t vertices) +{ + igraph_t simple, multiple; + igraph_bool_t complete = false; + igraph_integer_t eid = -1; + igraph_integer_t size = -1; + igraph_es_t es; + igraph_vector_bool_t mutuals; + igraph_vector_int_t toadd; + igraph_integer_t i; + + /* generic, simple graph */ + igraph_full(&simple, vertices, directed, false); + + /* igraph_full does not produce complete directed graphs, merely + * **tournaments**. So the mutuals must be added manually in order to get a + * complete graph. */ + + /* So we must find all edges for which the mutual is missing, and add it + ourselves */ + + if (directed) { + + igraph_vector_bool_init(&mutuals, vertices * vertices); + igraph_vector_bool_clear(&mutuals); + + igraph_is_mutual(&simple, &mutuals, igraph_ess_all(IGRAPH_EDGEORDER_ID), + false); + + igraph_vector_int_init(&toadd, vertices * vertices); + igraph_vector_int_clear(&toadd); + + for (i = 0 ; i < igraph_vector_bool_size(&mutuals); ++i) { + /* mutual is missing, adding it */ + if (VECTOR(mutuals)[i] == false) { + igraph_vector_int_push_back(&toadd, IGRAPH_TO(&simple, i)); + igraph_vector_int_push_back(&toadd, IGRAPH_FROM(&simple, i)); + } + } + + igraph_add_edges(&simple, &toadd, NULL); + } + igraph_is_complete(&simple, &complete); + IGRAPH_ASSERT(complete); + + /* Remove a specific edge */ + igraph_delete_edges(&simple, igraph_ess_1(42)); + igraph_is_complete(&simple, &complete); + IGRAPH_ASSERT(!complete); + + /* generic, multiple graph */ + igraph_full(&multiple, vertices, directed, false); + + /* igraph_full does not produce complete directed graphs, merely + * **tournaments**. So the mutuals must be added manually in order to get a + * complete graph. */ + + /* So we must find all edges for which the mutual is missing, and add it + ourselves */ + if (directed) { + + igraph_vector_bool_clear(&mutuals); + + igraph_is_mutual(&multiple, &mutuals, + igraph_ess_all(IGRAPH_EDGEORDER_ID), false); + + igraph_vector_int_clear(&toadd); + + for (i = 0 ; i < igraph_vector_bool_size(&mutuals); ++i) { + /* mutual is missing, adding it */ + if (VECTOR(mutuals)[i] == false) { + igraph_vector_int_push_back(&toadd, IGRAPH_TO(&multiple, i)); + igraph_vector_int_push_back(&toadd, IGRAPH_FROM(&multiple, i)); + } + } + + igraph_add_edges(&multiple, &toadd, NULL); + } + + /* Add two loops */ + igraph_add_edge(&multiple, 41, 41); + igraph_add_edge(&multiple, 42, 42); + + igraph_is_complete(&multiple, &complete); + IGRAPH_ASSERT(complete); + + igraph_invalidate_cache(&multiple); + + /* Add two multiple edges */ + igraph_add_edge(&multiple, IGRAPH_FROM(&multiple, 42), + IGRAPH_TO(&multiple, 42)); + igraph_add_edge(&multiple, IGRAPH_FROM(&multiple, 42), + IGRAPH_TO(&multiple, 42)); + + igraph_is_complete(&multiple, &complete); + IGRAPH_ASSERT(complete); + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Here, we cannot just simply remove any edge anymore */ + /* We must make sure that it is not a loop or an edge that has parallel + edges */ + do { + eid = igraph_rng_get_integer(igraph_rng_default(), 0, + igraph_ecount(&multiple) - 1); + igraph_es_pairs_small(&es, false, IGRAPH_FROM(&multiple, eid), + IGRAPH_TO(&multiple, eid), -1); + + igraph_es_size(&multiple, &es, &size); + + } while (size != 1 || + IGRAPH_FROM(&multiple, eid) == IGRAPH_TO(&multiple, eid)); + + /* Remove the edge we found. There is one necessarily, provided vertices is + high enough */ + /* We only added two loops, and two parallels */ + igraph_delete_edges(&multiple, es); + + igraph_is_complete(&multiple, &complete); + IGRAPH_ASSERT(!complete); + + igraph_destroy(&simple); + igraph_destroy(&multiple); + igraph_es_destroy(&es); + + if (directed) { + igraph_vector_bool_destroy(&mutuals); + igraph_vector_int_destroy(&toadd); + } + + return IGRAPH_SUCCESS; +} + +int main(void) +{ + check_null(); + check_singleton(); + check(false, 50); + check(false, 51); + check(true, 50); + check(true, 51); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_connected.c b/tests/unit/igraph_is_connected.c new file mode 100644 index 0000000..b4d268e --- /dev/null +++ b/tests/unit/igraph_is_connected.c @@ -0,0 +1,102 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_bool_t conn; + + /* Null graph */ + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + + igraph_is_connected(&graph, &conn, IGRAPH_WEAK); + IGRAPH_ASSERT(! conn); + + igraph_is_connected(&graph, &conn, IGRAPH_STRONG); + IGRAPH_ASSERT(! conn); + + igraph_destroy(&graph); + + /* Singleton graph */ + igraph_empty(&graph, 1, IGRAPH_DIRECTED); + + igraph_is_connected(&graph, &conn, IGRAPH_WEAK); + IGRAPH_ASSERT(conn); + + igraph_is_connected(&graph, &conn, IGRAPH_STRONG); + IGRAPH_ASSERT(conn); + + igraph_destroy(&graph); + + /* Two isolated vertices, one with a self-loop */ + igraph_small(&graph, 2, IGRAPH_DIRECTED, + 0,0, -1); + + igraph_is_connected(&graph, &conn, IGRAPH_WEAK); + IGRAPH_ASSERT(! conn); + + igraph_is_connected(&graph, &conn, IGRAPH_STRONG); + IGRAPH_ASSERT(! conn); + + igraph_destroy(&graph); + + /* Two isolated vertices, three self-loops */ + igraph_small(&graph, 2, IGRAPH_DIRECTED, + 0,0, 0,0, 1,1, -1); + + igraph_is_connected(&graph, &conn, IGRAPH_WEAK); + IGRAPH_ASSERT(! conn); + + igraph_is_connected(&graph, &conn, IGRAPH_STRONG); + IGRAPH_ASSERT(! conn); + + igraph_destroy(&graph); + + /* Weakly connected directed */ + igraph_small(&graph, 4, IGRAPH_DIRECTED, + 0,1, 2,0, 1,2, 3,2, + -1); + + igraph_is_connected(&graph, &conn, IGRAPH_WEAK); + IGRAPH_ASSERT(conn); + + igraph_is_connected(&graph, &conn, IGRAPH_STRONG); + IGRAPH_ASSERT(! conn); + + igraph_destroy(&graph); + + /* Directed cycle */ + igraph_small(&graph, 4, IGRAPH_DIRECTED, + 0,1, 2,0, 1,3, 3,2, + -1); + + igraph_is_connected(&graph, &conn, IGRAPH_WEAK); + IGRAPH_ASSERT(conn); + + igraph_is_connected(&graph, &conn, IGRAPH_STRONG); + IGRAPH_ASSERT(conn); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_dag.c b/tests/unit/igraph_is_dag.c new file mode 100644 index 0000000..788ead0 --- /dev/null +++ b/tests/unit/igraph_is_dag.c @@ -0,0 +1,126 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_bool_t is_dag; + + // directed empty graphs + for (igraph_integer_t n=0; n < 3; n++ ){ + igraph_empty(&graph, n, IGRAPH_DIRECTED); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(is_dag); + igraph_destroy(&graph); + } + + // undirected graphs + for (igraph_integer_t n=0; n < 3; n++ ){ + igraph_empty(&graph, n, IGRAPH_UNDIRECTED); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(!is_dag); + igraph_destroy(&graph); + } + + // 1-path + igraph_small(&graph, 2, IGRAPH_DIRECTED, + 0,1, + -1); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(is_dag); + igraph_destroy(&graph); + + // reciprocal edges -- not a DAG + igraph_small(&graph, 2, IGRAPH_DIRECTED, + 0,1, 1,0, + -1); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(! is_dag); + igraph_destroy(&graph); + + // 4-cycle -- not a DAG + igraph_small(&graph, 4, IGRAPH_DIRECTED, + 0,1, 1,2, 2,3, 3,0, + -1); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(!is_dag); + igraph_destroy(&graph); + + // 4-cycle with outgoing edge -- not a DAG + igraph_small(&graph, 5, IGRAPH_DIRECTED, + 0,1, 1,2, 2,3, 3,0, 0,4, + -1); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(!is_dag); + igraph_destroy(&graph); + + // 4-cycle with incoming edge -- not a DAG + igraph_small(&graph, 5, IGRAPH_DIRECTED, + 0,1, 1,2, 2,3, 3,0, 4,0, + -1); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(!is_dag); + igraph_destroy(&graph); + + // X shape, DAG + igraph_small(&graph, 5, IGRAPH_DIRECTED, + 1,0, 2,0, 0,3, 0,4, + -1); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(is_dag); + igraph_destroy(&graph); + + // self-loop present, not a DAG + igraph_small(&graph, 5, IGRAPH_DIRECTED, + 1,0, 2,0, 0,3, 0,4, 0,0, + -1); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(!is_dag); + igraph_destroy(&graph); + + // singleton with self-loop + igraph_small(&graph, 1, IGRAPH_DIRECTED, + 0,0, + -1); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(!is_dag); + igraph_destroy(&graph); + + // out-tree + igraph_kary_tree(&graph, 6, 2, IGRAPH_TREE_OUT); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(is_dag); + igraph_destroy(&graph); + + // in-tree + igraph_kary_tree(&graph, 6, 2, IGRAPH_TREE_IN); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(is_dag); + igraph_destroy(&graph); + + // undirected -- not a DAG + igraph_kary_tree(&graph, 6, 2, IGRAPH_TREE_UNDIRECTED); + igraph_is_dag(&graph, &is_dag); + IGRAPH_ASSERT(!is_dag); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_is_eulerian.c b/tests/unit/igraph_is_eulerian.c new file mode 100644 index 0000000..0d23b7f --- /dev/null +++ b/tests/unit/igraph_is_eulerian.c @@ -0,0 +1,207 @@ + +#include +#include +#include "test_utilities.h" + +int main(void) { + + igraph_t graph; + igraph_bool_t has_path, has_cycle; + + /* undirected cases */ + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1 , 1,2, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 1,2 , 2,3, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 1,2 , 2,3, 3,1, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 0,1,0,1,-1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,0, 0,0,0,0,-1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, 3,4, 4,5, 5,2, + 2,6, 6,4, 4,8, 2,8, 2,7, 0,7, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1 , 1,2, 2,3, 2,4 , 3,5 , 4,5, + 4,6, 0,6, 6,7, 1,7, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1 , 1,2, 2,3, 3,4 , 2,4 , 1,5, + 0,5 , -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,4, 3,4, 1,3, 2,5, 4,5, 2,6, 1,6, 0,4, 6,5, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 1,3 , 3,5 , 5,6 , 6,3 , 3,1 , 1,2 , + 2,2 , 2,4 , 2,4 , 4,3 , 3,2 , 4,6 , -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 7,8, 8,9, 9,7, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 2,3, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,1, 2,3, 3,1, 4,5, 5,6, 6,4, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* two disconnected self loops */ + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 1,1, 2,2, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* one self loop and one disconnected multiedge selfloop */ + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 1,1, 1,1, 2,2, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* multiple self-loop singletons */ + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, 0,0 , 1,1 , 1,1 , -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* no edges, multiple vertices */ + igraph_small(&graph, 4, IGRAPH_UNDIRECTED, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* no edges except one self loop, multiple vertices */ + igraph_small(&graph, 4, IGRAPH_UNDIRECTED, 0,0, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* directed cases*/ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1 , 1,2, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 1,2, 2,0, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1 , 1,2, 1,3, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1 , 1,3, 3,2, 2,0 , 2,1, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,3, 3,4, 4,0, 0,2, 2,1, 1,0, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,6, 6,4, 4,5, 5,0, 0,1, 1,2, + 2,3, 3,4, 4,2, 2,0, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* multiedges */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 0,1, 1,2, 2,1, 1,3, 3,4, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* one self loop */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 0,0, 1,2, 2,1, 1,3, 3,4, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* multiedges and one self loop */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,2 , 2,4 , 4,5 , 5,2 , 2,0 , 0,1 , + 1,1 , 1,3 , 1,3 , 3,2 , 2,1 , 3,5 , -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 1,3 , 3,5 , 5,6 , 6,3 , 3,1 , 1,2 , + 2,2 , 2,4 , 2,4 , 4,3 , 3,2 , 4,6 , -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_DIRECTED, 0,1, 0,1 , 0,1, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* disconnected graphs and self loops, both undirected and directed */ + /* disconnected with singleton vertices */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 8,9, 9,10, 10,8, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* two disconnected self loops, directed */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 1,1, 2,2, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* one self loop and one disconnected multiedge selfloop, directed */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, 1,1, 1,1, 2,2, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* no edges, multiple vertices, directed */ + igraph_small(&graph, 4, IGRAPH_DIRECTED, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + /* no edges except one self loop, multiple vertices, directed */ + igraph_small(&graph, 4, IGRAPH_DIRECTED, 0,0, -1); + igraph_is_eulerian(&graph, &has_path, &has_cycle); + printf("%d %d\n", has_path, has_cycle); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_eulerian.out b/tests/unit/igraph_is_eulerian.out new file mode 100644 index 0000000..86bd6b7 --- /dev/null +++ b/tests/unit/igraph_is_eulerian.out @@ -0,0 +1,34 @@ +1 0 +1 0 +1 1 +1 0 +1 1 +1 1 +0 0 +1 0 +1 0 +1 0 +1 1 +0 0 +0 0 +0 0 +0 0 +0 0 +1 1 +1 1 +1 0 +1 1 +0 0 +1 0 +1 1 +1 1 +0 0 +1 0 +1 0 +1 0 +0 0 +1 1 +0 0 +0 0 +1 1 +1 1 diff --git a/tests/unit/igraph_is_forest.c b/tests/unit/igraph_is_forest.c new file mode 100644 index 0000000..c86471d --- /dev/null +++ b/tests/unit/igraph_is_forest.c @@ -0,0 +1,145 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +void check_output(const igraph_t *graph, igraph_bool_t *res, igraph_neimode_t mode){ + igraph_bool_t result; + igraph_vector_int_t roots; + + igraph_vector_int_init(&roots, 0); + igraph_is_forest(graph, &result, &roots, mode); + if (result) { + printf("Root nodes: "); + igraph_vector_int_print(&roots); + } + else { + printf("Not a forest.\n"); + } + IGRAPH_ASSERT(*res == result); + igraph_vector_int_destroy(&roots); + printf("\n"); +} + +int main(void) { + igraph_t graph; + igraph_bool_t res; + igraph_neimode_t mode; + + printf("Empty Graph\n"); + mode=IGRAPH_ALL; + res=1; + igraph_empty(&graph, 0, 0); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Graph with 0 edges\n"); + mode=IGRAPH_ALL; + res=1; + igraph_small(&graph, 5, IGRAPH_UNDIRECTED, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Graph with 1 vertex\n"); + mode=IGRAPH_ALL; + res=1; + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Undirected Graph\n"); + mode=IGRAPH_ALL; + res=1; + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 3, 4, 3, 5, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Directed Graph out trees\n"); + mode=IGRAPH_OUT; + res=1; + igraph_small(&graph, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 3, 4, 3, 5, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Directed Graph in trees\n"); + mode=IGRAPH_IN; + res=0; + igraph_small(&graph, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 3, 4, 3, 5, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Undirected Graph with cycle\n"); + mode=IGRAPH_ALL; + res=0; + igraph_small(&graph, 7, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 3, 4, 3, 5, 4, 5, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Undirected Graph with ecount>vcount-1\n"); + mode=IGRAPH_ALL; + res=0; + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 3, 5, 4, 5, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Undirected Graph with self-loop\n"); + mode=IGRAPH_ALL; + res=0; + igraph_small(&graph, 5, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 4, 3, 3, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Directed Multigraph\n"); + mode=IGRAPH_OUT; + res=0; + igraph_small(&graph, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 3, 4, 3, 5, 3, 5, -1); + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + printf("Disjoint union of a cycle with two trees, directed\n"); + mode=IGRAPH_OUT; + res=0; + igraph_small(&graph, 10, IGRAPH_DIRECTED, + 0, 1, 1, 2, 1, 3, 4, 5, 5, 6, 6, 7, 7, 4, 8, 9, + -1); + check_output(&graph, &res, mode); + + printf("Disjoint union of a cycle with two trees, undirected\n"); + mode=IGRAPH_ALL; + res=0; + check_output(&graph, &res, mode); + igraph_destroy(&graph); + + /* Cache testing */ + /* 1 <- 0 -> 2 <- 3 */ + igraph_small(&graph, 0, IGRAPH_DIRECTED, + 0,1, 0,2, 3,2, -1); + /* This must not cache that the graph is not a forest, + * as we are only checking the directed case: */ + igraph_is_forest(&graph, &res, NULL, IGRAPH_OUT); + IGRAPH_ASSERT(!res); + igraph_is_forest(&graph, &res, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(res); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_forest.out b/tests/unit/igraph_is_forest.out new file mode 100644 index 0000000..b2a4132 --- /dev/null +++ b/tests/unit/igraph_is_forest.out @@ -0,0 +1,36 @@ +Empty Graph +Root nodes: + +Graph with 0 edges +Root nodes: 0 1 2 3 4 + +Graph with 1 vertex +Root nodes: 0 + +Undirected Graph +Root nodes: 0 3 + +Directed Graph out trees +Root nodes: 0 3 + +Directed Graph in trees +Not a forest. + +Undirected Graph with cycle +Not a forest. + +Undirected Graph with ecount>vcount-1 +Not a forest. + +Undirected Graph with self-loop +Not a forest. + +Directed Multigraph +Not a forest. + +Disjoint union of a cycle with two trees, directed +Not a forest. + +Disjoint union of a cycle with two trees, undirected +Not a forest. + diff --git a/tests/unit/igraph_is_forest2.c b/tests/unit/igraph_is_forest2.c new file mode 100644 index 0000000..d53946f --- /dev/null +++ b/tests/unit/igraph_is_forest2.c @@ -0,0 +1,109 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +/* Direct test for undirected forests: + * Decompose into connected components and check that each one is a tree */ +igraph_bool_t is_forest(const igraph_t *graph) { + igraph_bool_t res = 1; + igraph_graph_list_t components; + + igraph_graph_list_init(&components, 0); + + igraph_decompose(graph, &components, IGRAPH_WEAK, -1, 0); + + igraph_integer_t n = igraph_graph_list_size(&components); + for (igraph_integer_t i=0; i < n; ++i) { + igraph_is_tree(igraph_graph_list_get_ptr(&components, i), &res, NULL, IGRAPH_ALL); + + if (! res) { + break; + } + } + + igraph_graph_list_destroy(&components); + + return res; +} + +/* This test generates 'trials' random graphs, allowing self-loops and + * multi-edges, and exercises the forest detection function on each. */ +int main(void) { + const igraph_integer_t n = 100; /* vertex count */ + const igraph_integer_t m = n / 2; /* edge count */ + const igraph_integer_t trials = 300; + igraph_integer_t true_count; + igraph_bool_t res1, res2; + igraph_vector_int_t edges; + + igraph_rng_seed(igraph_rng_default(), 847532); + + igraph_vector_int_init(&edges, 2 * m); + + RNG_BEGIN(); + + true_count = 0; + for (igraph_integer_t k = 0; k < trials; ++k) { + igraph_t graph; + + for (igraph_integer_t i = 0; i < 2 * m; ++i) { + VECTOR(edges)[i] = RNG_INTEGER(0, n - 1); + } + + igraph_create(&graph, &edges, n, IGRAPH_UNDIRECTED); + + igraph_is_forest(&graph, &res1, NULL, IGRAPH_ALL); + res2 = is_forest(&graph); + + if (res1 != res2) { + printf("Invalid result for the following graph.\nExpected result: %s\nActual result: %s\n\n", + res2 ? "true" : "false", + res1 ? "true" : "false"); + print_graph(&graph); + } + + igraph_destroy(&graph); + + if (res1 != res2) { + break; + } + + if (res1) { + true_count += 1; + } + } + + RNG_END(); + + igraph_vector_int_destroy(&edges); + + /* ensure that test fails if we got an unexpected result */ + IGRAPH_ASSERT(res1 == res2); + + /* Check that the test is covering all cases: what fraction of random graphs was a tree? + * With random multigraphs, this should be around 20-30% when the edge count is half + * that of the vertex count/ */ + printf("Fraction of random multigraphs which were a forest: %g\n", ((double) true_count) / trials); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_graphical.c b/tests/unit/igraph_is_graphical.c new file mode 100644 index 0000000..ef1b5e8 --- /dev/null +++ b/tests/unit/igraph_is_graphical.c @@ -0,0 +1,284 @@ + +#include + +#include "test_utilities.h" + +/* Undirected case */ +void graphical_print_destroy(igraph_vector_int_t *ds) { + int err; + igraph_bool_t simple, loops, multi, multiloops; + + print_vector_int(ds); + + err = igraph_is_graphical(ds, NULL, IGRAPH_SIMPLE_SW, &simple); + if (err != IGRAPH_SUCCESS) { + printf("error!\n\n"); goto cleanup; + } + err = igraph_is_graphical(ds, NULL, IGRAPH_LOOPS_SW, &loops); + if (err != IGRAPH_SUCCESS) { + printf("error!\n\n"); goto cleanup; + } + err = igraph_is_graphical(ds, NULL, IGRAPH_MULTI_SW, &multi); + if (err != IGRAPH_SUCCESS) { + printf("error!\n\n"); goto cleanup; + } + err = igraph_is_graphical(ds, NULL, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, &multiloops); + if (err != IGRAPH_SUCCESS) { + printf("error!\n\n"); goto cleanup; + } + + printf("simple: %s, loops: %s, multi: %s, multiloops: %s\n\n", + simple ? " true" : "false", + loops ? " true" : "false", + multi ? " true" : "false", + multiloops ? " true" : "false"); + +cleanup: + igraph_vector_int_destroy(ds); +} + + +/* Directed case */ +void digraphical_print_destroy(igraph_vector_int_t *ods, igraph_vector_int_t *ids) { + int err; + igraph_bool_t simple, loops, multi, multiloops; + + print_vector_int(ods); + print_vector_int(ids); + + err = igraph_is_graphical(ods, ids, IGRAPH_SIMPLE_SW, &simple); + if (err != IGRAPH_SUCCESS) { + printf("error!\n\n"); goto cleanup; + } + err = igraph_is_graphical(ods, ids, IGRAPH_LOOPS_SW, &loops); + if (err != IGRAPH_SUCCESS) { + printf("error!\n\n"); goto cleanup; + } + err = igraph_is_graphical(ods, ids, IGRAPH_MULTI_SW, &multi); + if (err != IGRAPH_SUCCESS) { + printf("error!\n\n"); goto cleanup; + } + err = igraph_is_graphical(ods, ids, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, &multiloops); + if (err != IGRAPH_SUCCESS) { + printf("error!\n\n"); goto cleanup; + } + + printf("simple: %s, loops: %s, multi: %s, multiloops: %s\n\n", + simple ? " true" : "false", + loops ? " true" : "false", + multi ? " true" : "false", + multiloops ? " true" : "false"); + +cleanup: + igraph_vector_int_destroy(ods); + igraph_vector_int_destroy(ids); +} + + +int main(void) { + igraph_vector_int_t ds, ods, ids; + + igraph_set_error_handler(&igraph_error_handler_ignore); + + /* Undirected case: */ + + /* Empty */ + igraph_vector_int_init(&ds, 0); + graphical_print_destroy(&ds); + + /* All zeros */ + igraph_vector_int_init_int_end(&ds, -1, 0, 0, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 3, 3, 3, 3, 3, 3, 3, 3, -1); + graphical_print_destroy(&ds); + + /* Undirected degree sequence with negative degree */ + igraph_vector_int_init_int_end(&ds, -1, 3, -2, 3, 3, 3, 3, 3, 3, -1); + graphical_print_destroy(&ds); + + /* Undirected degree sequence with uneven sum */ + igraph_vector_int_init_int_end(&ds, -1, 3, 3, 3, 3, 3, 3, 3, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 4, 4, 5, 3, 6, 2, 2, 8, 1, 1, 10, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 3, 3, 2, 4, 1, 5, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 4, 7, 4, 7, 7, 8, 9, 9, 4, 6, 5, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 4, 4, 4, 4, 4, 1, 1, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 4, 4, 4, 4, 4, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 4, 4, 4, 4, 4, 4, 1, 1, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 3, 3, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 4, 4, 4, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 1, 2, 2, 3, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 1, 2, 3, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 1, 2, 5, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 1, 1, 4, -1); + graphical_print_destroy(&ds); + + /* Extra cases for undirected simple with single loops */ + igraph_vector_int_init_int_end(&ds, -1, 7, 7, 3, 2, 2, 1, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 7, 7, 3, 3, 2, 2, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 6, 6, 6, 4, 2, 0, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 6, 6, 6, 4, 4, 0, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 6, 6, 6, 4, 3, 1, -1); + graphical_print_destroy(&ds); + + /* The following two sequences are realizable as simple graphs. + * The algorithm that checks this exits the last loop with these + * two sequences. An earlier buggy version of the function failed + * to set the result in this case. */ + igraph_vector_int_init_int_end(&ds, -1, 2, 2, 2, 2, 4, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 3, 0, 5, 3, 5, 3, 3, -1); + graphical_print_destroy(&ds); + + + /* Directed case: */ + + /* Empty */ + igraph_vector_int_init(&ods, 0); + igraph_vector_int_init(&ids, 0); + digraphical_print_destroy(&ods, &ids); + + /* All zeros */ + igraph_vector_int_init_int_end(&ods, -1, 0, -1); + igraph_vector_int_init_int_end(&ids, -1, 0, -1); + digraphical_print_destroy(&ods, &ids); + + /* Different length, must throw an error */ + igraph_vector_int_init_int_end(&ods, -1, 1, 1, -1); + igraph_vector_int_init_int_end(&ids, -1, 2, -1); + digraphical_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ods, -1, 0, 2, 3, 0, 4, 3, 1, 3, 4, 2, -1); + igraph_vector_int_init_int_end(&ids, -1, 0, 3, 1, 3, 2, 4, 4, 1, 3, 1, -1); + digraphical_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ods, -1, 0, 2, 3, 0, 4, 3, 1, 3, 4, 2, -1); + igraph_vector_int_init_int_end(&ids, -1, 0, 3, 1, -7, 2, 4, 4, 1, 3, 1, -1); + digraphical_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ods, -1, 0, 2, 3, 0, 4, 3, 1, 3, 4, 2, -1); + igraph_vector_int_init_int_end(&ids, -1, 0, 3, 1, 2, 2, 4, 4, 1, 3, 1, -1); + digraphical_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ids, -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, -1); + igraph_vector_int_init_int_end(&ods, -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, -1); + digraphical_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ids, -1, 1, 3, 2, 1, 3, 4, 3, 3, 1, 3, -1); + igraph_vector_int_init_int_end(&ods, -1, 4, 1, 2, 3, 2, 3, 2, 3, 2, 2, -1); + digraphical_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ids, -1, 7, 4, 6, 4, 7, 8, 8, 8, 7, 4, -1); + igraph_vector_int_init_int_end(&ods, -1, 8, 5, 6, 8, 6, 6, 5, 7, 5, 7, -1); + digraphical_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ids, -1, 3, 3, 1, 0, 2, 3, 0, 7, -1); + igraph_vector_int_init_int_end(&ods, -1, 2, 2, 4, 3, 4, 3, 1, 0, -1); + digraphical_print_destroy(&ods, &ids); + + /* Only one vertex with a non-zero out-degree. Regression test for bug #851 */ + igraph_vector_int_init_int_end(&ids, -1, 1, -1); + igraph_vector_int_init_int_end(&ods, -1, 1, -1); + digraphical_print_destroy(&ods, &ids); + + /* Another degree sequence when there is only + * one vertex with a non-zero out-degree. Regression test for bug #851 */ + igraph_vector_int_init_int_end(&ids, -1, 2, 0, -1); + igraph_vector_int_init_int_end(&ods, -1, 0, 2, -1); + digraphical_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ids, -1, 2, 2, -1); + igraph_vector_int_init_int_end(&ods, -1, 2, 2, -1); + digraphical_print_destroy(&ods, &ids); + + /* Valid directed graphical degree sequence. Regression test for bug #1092 */ + igraph_vector_int_init_int_end(&ids, -1, 1, 0, 1, -1); + igraph_vector_int_init_int_end(&ods, -1, 0, 2, 0, -1); + digraphical_print_destroy(&ods, &ids); + + /* Same as above, ids & ods exchanged. */ + igraph_vector_int_init_int_end(&ids, -1, 1, 0, 1, -1); + igraph_vector_int_init_int_end(&ods, -1, 0, 2, 0, -1); + digraphical_print_destroy(&ids, &ods); + + /* single loops: graphical, but multi-eges only: non-graphical */ + igraph_vector_int_init_int_end(&ids, -1, 1, 0, 2, -1); + igraph_vector_int_init_int_end(&ods, -1, 0, 1, 2, -1); + digraphical_print_destroy(&ids, &ods); + + /* Degree sequences of simple threshold digraphs: + * These sequences make good test cases as they have a unique realization. + * These sequnces were constructed based on point 3 of Theorem 1 in https://arxiv.org/abs/1212.1149 + * by computing a "closure" of a random digraphs. + */ + igraph_vector_int_init_int_end(&ids, -1, 4, 3, 0, 3, 4, -1); + igraph_vector_int_init_int_end(&ods, -1, 3, 3, 4, 3, 1, -1); + digraphical_print_destroy(&ids, &ods); + + igraph_vector_int_init_int_end(&ids, -1, 4, 4, 3, 3, 4, -1); + igraph_vector_int_init_int_end(&ods, -1, 4, 4, 4, 4, 2, -1); + digraphical_print_destroy(&ids, &ods); + + igraph_vector_int_init_int_end(&ids, -1, 2, 4, 0, 3, 1, -1); + igraph_vector_int_init_int_end(&ods, -1, 3, 2, 3, 1, 1, -1); + digraphical_print_destroy(&ids, &ods); + + igraph_vector_int_init_int_end(&ids, -1, 11, 0, 0, 0, 7, 0, 9, 0, 13, 0, 0, 0, 0, 0, 0, -1); + igraph_vector_int_init_int_end(&ods, -1, 3, 4, 4, 4, 3, 4, 3, 4, 2, 3, 2, 2, 1, 1, 0, -1); + digraphical_print_destroy(&ids, &ods); + + igraph_vector_int_init_int_end(&ids, -1, 8, 4, 0, 0, 5, 0, 8, 9, 7, 0, 0, 11, 14, 13, 0, -1); + igraph_vector_int_init_int_end(&ods, -1, 8, 8, 9, 9, 8, 8, 6, 5, 6, 4, 3, 2, 1, 1, 1, -1); + digraphical_print_destroy(&ids, &ods); + + /* Some degree sequences with no simple realizations, + * based on incrementing an in- and out-degree of the above. */ + igraph_vector_int_init_int_end(&ids, -1, 8, 4, 1, 0, 5, 0, 8, 9, 7, 0, 0, 11, 14, 13, 0, -1); + igraph_vector_int_init_int_end(&ods, -1, 8, 8, 10, 9, 8, 8, 6, 5, 6, 4, 3, 2, 1, 1, 1, -1); + digraphical_print_destroy(&ids, &ods); + + igraph_vector_int_init_int_end(&ids, -1, 9, 4, 0, 0, 5, 0, 8, 9, 7, 0, 0, 11, 14, 13, 0, -1); + igraph_vector_int_init_int_end(&ods, -1, 8, 9, 9, 9, 8, 8, 6, 5, 6, 4, 3, 2, 1, 1, 1, -1); + digraphical_print_destroy(&ids, &ods); + + igraph_vector_int_init_int_end(&ids, -1, 8, 4, 0, 0, 5, 0, 8, 9, 7, 0, 0, 11, 14, 14, 0, -1); + igraph_vector_int_init_int_end(&ods, -1, 8, 8, 9, 9, 8, 8, 6, 5, 6, 4, 3, 3, 1, 1, 1, -1); + digraphical_print_destroy(&ids, &ods); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_graphical.out b/tests/unit/igraph_is_graphical.out new file mode 100644 index 0000000..a255da2 --- /dev/null +++ b/tests/unit/igraph_is_graphical.out @@ -0,0 +1,168 @@ +( ) +simple: true, loops: true, multi: true, multiloops: true + +( 0 0 ) +simple: true, loops: true, multi: true, multiloops: true + +( 3 3 3 3 3 3 3 3 ) +simple: true, loops: true, multi: true, multiloops: true + +( 3 -2 3 3 3 3 3 3 ) +simple: false, loops: false, multi: false, multiloops: false + +( 3 3 3 3 3 3 3 ) +simple: false, loops: false, multi: false, multiloops: false + +( 4 4 5 3 6 2 2 8 1 1 10 ) +simple: true, loops: true, multi: true, multiloops: true + +( 3 3 2 4 1 5 ) +simple: true, loops: true, multi: true, multiloops: true + +( 4 7 4 7 7 8 9 9 4 6 5 ) +simple: true, loops: true, multi: true, multiloops: true + +( 4 4 4 4 4 1 1 ) +simple: true, loops: true, multi: true, multiloops: true + +( 4 4 4 4 4 ) +simple: true, loops: true, multi: true, multiloops: true + +( 4 4 4 4 4 4 1 1 ) +simple: true, loops: true, multi: true, multiloops: true + +( 3 3 ) +simple: false, loops: true, multi: true, multiloops: true + +( 4 4 4 ) +simple: false, loops: true, multi: true, multiloops: true + +( 1 2 2 3 ) +simple: true, loops: true, multi: true, multiloops: true + +( 1 2 3 ) +simple: false, loops: true, multi: true, multiloops: true + +( 1 2 5 ) +simple: false, loops: false, multi: false, multiloops: true + +( 1 1 4 ) +simple: false, loops: true, multi: false, multiloops: true + +( 7 7 3 2 2 1 ) +simple: false, loops: false, multi: true, multiloops: true + +( 7 7 3 3 2 2 ) +simple: false, loops: true, multi: true, multiloops: true + +( 6 6 6 4 2 0 ) +simple: false, loops: false, multi: true, multiloops: true + +( 6 6 6 4 4 0 ) +simple: false, loops: true, multi: true, multiloops: true + +( 6 6 6 4 3 1 ) +simple: false, loops: true, multi: true, multiloops: true + +( 2 2 2 2 4 ) +simple: true, loops: true, multi: true, multiloops: true + +( 3 0 5 3 5 3 3 ) +simple: true, loops: true, multi: true, multiloops: true + +( ) +( ) +simple: true, loops: true, multi: true, multiloops: true + +( 0 ) +( 0 ) +simple: true, loops: true, multi: true, multiloops: true + +( 1 1 ) +( 2 ) +error! + +( 0 2 3 0 4 3 1 3 4 2 ) +( 0 3 1 3 2 4 4 1 3 1 ) +simple: true, loops: true, multi: true, multiloops: true + +( 0 2 3 0 4 3 1 3 4 2 ) +( 0 3 1 -7 2 4 4 1 3 1 ) +simple: false, loops: false, multi: false, multiloops: false + +( 0 2 3 0 4 3 1 3 4 2 ) +( 0 3 1 2 2 4 4 1 3 1 ) +simple: false, loops: false, multi: false, multiloops: false + +( 3 3 3 3 3 3 3 3 3 ) +( 3 3 3 3 3 3 3 3 3 ) +simple: true, loops: true, multi: true, multiloops: true + +( 4 1 2 3 2 3 2 3 2 2 ) +( 1 3 2 1 3 4 3 3 1 3 ) +simple: true, loops: true, multi: true, multiloops: true + +( 8 5 6 8 6 6 5 7 5 7 ) +( 7 4 6 4 7 8 8 8 7 4 ) +simple: true, loops: true, multi: true, multiloops: true + +( 2 2 4 3 4 3 1 0 ) +( 3 3 1 0 2 3 0 7 ) +simple: true, loops: true, multi: true, multiloops: true + +( 1 ) +( 1 ) +simple: false, loops: true, multi: false, multiloops: true + +( 0 2 ) +( 2 0 ) +simple: false, loops: false, multi: true, multiloops: true + +( 2 2 ) +( 2 2 ) +simple: false, loops: true, multi: true, multiloops: true + +( 0 2 0 ) +( 1 0 1 ) +simple: true, loops: true, multi: true, multiloops: true + +( 1 0 1 ) +( 0 2 0 ) +simple: true, loops: true, multi: true, multiloops: true + +( 1 0 2 ) +( 0 1 2 ) +simple: false, loops: true, multi: false, multiloops: true + +( 4 3 0 3 4 ) +( 3 3 4 3 1 ) +simple: true, loops: true, multi: true, multiloops: true + +( 4 4 3 3 4 ) +( 4 4 4 4 2 ) +simple: true, loops: true, multi: true, multiloops: true + +( 2 4 0 3 1 ) +( 3 2 3 1 1 ) +simple: true, loops: true, multi: true, multiloops: true + +( 11 0 0 0 7 0 9 0 13 0 0 0 0 0 0 ) +( 3 4 4 4 3 4 3 4 2 3 2 2 1 1 0 ) +simple: true, loops: true, multi: true, multiloops: true + +( 8 4 0 0 5 0 8 9 7 0 0 11 14 13 0 ) +( 8 8 9 9 8 8 6 5 6 4 3 2 1 1 1 ) +simple: true, loops: true, multi: true, multiloops: true + +( 8 4 1 0 5 0 8 9 7 0 0 11 14 13 0 ) +( 8 8 10 9 8 8 6 5 6 4 3 2 1 1 1 ) +simple: false, loops: true, multi: true, multiloops: true + +( 9 4 0 0 5 0 8 9 7 0 0 11 14 13 0 ) +( 8 9 9 9 8 8 6 5 6 4 3 2 1 1 1 ) +simple: false, loops: true, multi: true, multiloops: true + +( 8 4 0 0 5 0 8 9 7 0 0 11 14 14 0 ) +( 8 8 9 9 8 8 6 5 6 4 3 3 1 1 1 ) +simple: false, loops: false, multi: true, multiloops: true + diff --git a/tests/unit/igraph_is_mutual.c b/tests/unit/igraph_is_mutual.c new file mode 100644 index 0000000..7be9672 --- /dev/null +++ b/tests/unit/igraph_is_mutual.c @@ -0,0 +1,69 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, igraph_es_t es, igraph_bool_t loops) { + igraph_vector_bool_t result; + igraph_vector_bool_init(&result, 0); + igraph_is_mutual(graph, &result, es, loops); + igraph_vector_bool_print(&result); + printf("\n"); + igraph_vector_bool_destroy(&result); +} + + +int main(void) { + igraph_t g_0, g_lm, g_lmu; + igraph_vector_bool_t result; + + igraph_vector_bool_init(&result, 0); + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_lmu, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices:\n"); + call_and_print(&g_0, igraph_ess_all(IGRAPH_EDGEORDER_ID), IGRAPH_LOOPS); + + printf("Graph with loops and multiple edges, loops considered:\n"); + call_and_print(&g_lm, igraph_ess_all(IGRAPH_EDGEORDER_ID), IGRAPH_LOOPS); + + printf("Graph with loops and multiple edges, loops not considered:\n"); + call_and_print(&g_lm, igraph_ess_all(IGRAPH_EDGEORDER_ID), IGRAPH_NO_LOOPS); + + printf("Same graph, selecting edge 4:\n"); + call_and_print(&g_lm, igraph_ess_1(4), IGRAPH_LOOPS); + + printf("Same graph, but undirected:\n"); + call_and_print(&g_lmu, igraph_ess_all(IGRAPH_EDGEORDER_ID), IGRAPH_LOOPS); + + VERIFY_FINALLY_STACK(); + + printf("Edge out of range.\n"); + CHECK_ERROR(igraph_is_mutual(&g_lm, &result, igraph_ess_1(100), true), IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_lm); + igraph_destroy(&g_lmu); + igraph_vector_bool_destroy(&result); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_is_mutual.out b/tests/unit/igraph_is_mutual.out new file mode 100644 index 0000000..685bb7a --- /dev/null +++ b/tests/unit/igraph_is_mutual.out @@ -0,0 +1,16 @@ +No vertices: + + +Graph with loops and multiple edges, loops considered: +0 1 1 0 1 1 0 0 0 + +Graph with loops and multiple edges, loops not considered: +0 1 0 0 1 1 0 0 0 + +Same graph, selecting edge 4: +1 + +Same graph, but undirected: +1 1 1 1 1 1 1 1 1 + +Edge out of range. diff --git a/tests/unit/igraph_is_same_graph.c b/tests/unit/igraph_is_same_graph.c new file mode 100644 index 0000000..04901c1 --- /dev/null +++ b/tests/unit/igraph_is_same_graph.c @@ -0,0 +1,84 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g1, g2; + igraph_bool_t res; + + /* undirected graphs */ + igraph_small(&g1, 4, 0, + 0, 1, 1, 2, 2, 3, 3, 0, -1); + igraph_small(&g2, 4, 0, + 1, 0, 1, 2, 2, 3, 3, 0, -1); + + /* a graph is always same as itself */ + igraph_is_same_graph(&g1, &g1, &res); + IGRAPH_ASSERT(res); + + /* undirected graphs should be the same no matter + * the direction of the edges (one is swapped in g2 */ + igraph_is_same_graph(&g1, &g2, &res); + IGRAPH_ASSERT(res); + + /* end of undirected */ + igraph_destroy(&g1); + igraph_destroy(&g2); + + /* directed graphs */ + igraph_small(&g1, 4, 1, + 0, 1, 1, 2, 2, 3, 3, 0, -1); + igraph_small(&g2, 4, 1, + 1, 0, 1, 2, 2, 3, 3, 0, -1); + + /* directed graphs should not be the same if an + * edge has the opposite direction */ + igraph_is_same_graph(&g1, &g2, &res); + IGRAPH_ASSERT(!res); + + igraph_destroy(&g2); + + /* change order of edges, they should be reordered by graph->ii */ + igraph_small(&g2, 4, 1, + 1, 2, 0, 1, 2, 3, 3, 0, -1); + igraph_is_same_graph(&g1, &g2, &res); + IGRAPH_ASSERT(res); + + /* end of directed */ + igraph_destroy(&g1); + igraph_destroy(&g2); + + /* undirected vs directed */ + igraph_small(&g1, 4, 0, + 0, 1, 1, 2, 2, 3, 3, 0, -1); + igraph_small(&g2, 4, 1, + 0, 1, 1, 2, 2, 3, 3, 0, -1); + igraph_is_same_graph(&g1, &g2, &res); + IGRAPH_ASSERT(!res); + + /* end of undirected vs directed */ + igraph_destroy(&g1); + igraph_destroy(&g2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_separator.c b/tests/unit/igraph_is_separator.c new file mode 100644 index 0000000..614a5d2 --- /dev/null +++ b/tests/unit/igraph_is_separator.c @@ -0,0 +1,293 @@ +/* + IGraph library. + Copyright (C) 2010-2024 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 + +#include "test_utilities.h" + +/* Brute force test of minimality. */ +igraph_bool_t is_minimal_sep(const igraph_t *graph, const igraph_vector_int_t *S) { + igraph_bool_t res; + igraph_is_separator(graph, igraph_vss_vector(S), &res); + + if (res) { + for (igraph_integer_t i=0; i < igraph_vector_int_size(S); i++) { + igraph_bool_t sep; + igraph_vector_int_t S2; + igraph_vector_int_init_copy(&S2, S); + igraph_vector_int_remove_fast(&S2, i); + igraph_is_separator(graph, igraph_vss_vector(&S2), &sep); + igraph_vector_int_destroy(&S2); + if (sep) { + res = false; + break; + } + } + } + + return res; +} + +int main(void) { + igraph_t graph; + igraph_vector_int_t S; + igraph_bool_t is_sep, is_min; + + /* Simple star graph, remove the center */ + igraph_star(&graph, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_is_separator(&graph, igraph_vss_1(0), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_1(0), &is_min); + IGRAPH_ASSERT(is_min); + + /* Same graph, but another vertex */ + igraph_is_separator(&graph, igraph_vss_1(6), &is_sep); + IGRAPH_ASSERT(! is_sep); + + /* Same graph, all vertices but the center */ + igraph_is_separator(&graph, igraph_vss_range(1, 10), &is_sep); + IGRAPH_ASSERT(! is_sep); + + /* Same graph, all vertices */ + igraph_is_separator(&graph, igraph_vss_range(0, 10), &is_sep); + IGRAPH_ASSERT(! is_sep); + igraph_destroy(&graph); + + /* Same graph, no vertices */ + igraph_is_separator(&graph, igraph_vss_range(0, 0), &is_sep); + IGRAPH_ASSERT(! is_sep); + igraph_destroy(&graph); + + /* Test graph 2 */ + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,0, 0,3, 4,5, 5,6, 1,1, 5,6, + -1); + + igraph_vector_int_init_int_end(&S, -1, + 0, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 5, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 1, 0, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + /* Pass in vertices multiple times */ + igraph_vector_int_init_int_end(&S, -1, + 1, 3, 0, 1, 3, 3, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(!is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 4, 1, 0, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 4, 1, 0, 2, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(! is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 4, 1, 0, 5, 2, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(! is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 1, 0, 5, 2, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 5, 6, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(! is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 5, 0, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_is_separator(&graph, igraph_vss_range(0, 0), &is_sep); + IGRAPH_ASSERT(!is_sep); + + igraph_destroy(&graph); + + /* Test graph 3 */ + + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, + 0,1, 1,3, 0,2, 2,3, 2,4, + -1); + + igraph_vector_int_init_int_end(&S, -1, + 5, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(! is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 1, 2, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + + igraph_add_edge(&graph, 1, 4); + + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(is_min); + igraph_vector_int_destroy(&S); + + /* Pass in vertices multiple times */ + igraph_vector_int_init_int_end(&S, -1, + 1, 2, 1, 2, 2, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(is_min); + igraph_vector_int_destroy(&S); + + igraph_destroy(&graph); + + /* Test graph 4 */ + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,1, 0,2, 0,3, 1,2, 1,3, 2,3, + 4,5, 4,6, 4,7, 5,6, 5,7, 6,7, + 2,5, 3,4, + -1); + + igraph_vector_int_init_int_end(&S, -1, + 2, 3, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 2, 4, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 3, 5, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 2, 3, 5, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(! is_min); + igraph_vector_int_destroy(&S); + + igraph_destroy(&graph); + + /* Karate club network */ + + igraph_famous(&graph, "Zachary"); + + igraph_vector_int_init_int_end(&S, -1, 32, 33, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(is_min); + igraph_vector_int_destroy(&S); + + igraph_vector_int_init_int_end(&S, -1, + 8, 9, 19, 30, 31, -1); + igraph_is_separator(&graph, igraph_vss_vector(&S), &is_min); + IGRAPH_ASSERT(!is_min); + igraph_vector_int_destroy(&S); + + /* Caution: + * igraph_all_minimal_st_separators() returns minimal (s,t) separators, + * i.e. separators that are minimal with respect to separating a specific + * (s,t) pair, but not necessarily minimal separators. See the documentation + * for an example. + */ + + igraph_vector_int_list_t separators; + igraph_vector_int_list_init(&separators, 0); + igraph_all_minimal_st_separators(&graph, &separators); + for (igraph_integer_t i=0; i < igraph_vector_int_list_size(&separators); i++) { + const igraph_vector_int_t *sep = igraph_vector_int_list_get_ptr(&separators, i); + igraph_is_separator(&graph, igraph_vss_vector(sep), &is_sep); + IGRAPH_ASSERT(is_sep); + igraph_is_minimal_separator( &graph, igraph_vss_vector(sep), &is_min); + + igraph_bool_t is_min2 = is_minimal_sep(&graph, sep); + IGRAPH_ASSERT((!is_min) == (! is_min2)); + } + igraph_vector_int_list_destroy(&separators); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_tree.c b/tests/unit/igraph_is_tree.c new file mode 100644 index 0000000..4b14ae0 --- /dev/null +++ b/tests/unit/igraph_is_tree.c @@ -0,0 +1,140 @@ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_bool_t res; + igraph_integer_t root; + + /* the null graph is not a tree */ + igraph_empty(&g, 0, 0); + + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(! res); + + igraph_destroy(&g); + + /* the single-vertex graph is a tree */ + igraph_empty(&g, 1, 0); + + root = -1; + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(res); + IGRAPH_ASSERT(root == 0); + + igraph_destroy(&g); + + /* undirected 4-cycle, not a tree */ + igraph_small(&g, 4, 0, + 0, 1, 1, 2, 2, 3, 3, 0, -1); + + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(! res); + + igraph_destroy(&g); + + /* disconnected graph with the same number of edges a tree would have */ + igraph_small(&g, 4, 0, + 0, 1, 1, 2, 0, 2, 3, 4, -1); + + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(! res); + + igraph_destroy(&g); + + /* disjoint union of an out-tree and two cycles, with the same number + * of edges as a tree would have, and the same in-degrees as an out-tree + * would have */ + igraph_small(&g, 11, IGRAPH_DIRECTED, + 10, 0, 0, 2, 0, 6, 9, 1, 1, 8, 8, 4, 4, 9, 3, 7, 7, 5, 5, 3, + -1); + + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(! res); + + igraph_destroy(&g); + + /* 3-star, tree */ + igraph_small(&g, 4, 0, + 0, 1, 0, 2, 0, 3, -1); + + root = -1; + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(res); + IGRAPH_ASSERT(root == 0); + + igraph_destroy(&g); + + /* out-tree */ + igraph_small(&g, 4, 1, + 0, 1, 1, 2, 1, 3, -1); + + root = -1; + igraph_is_tree(&g, &res, &root, IGRAPH_OUT); + IGRAPH_ASSERT(res); + IGRAPH_ASSERT(root == 0); + + igraph_is_tree(&g, &res, &root, IGRAPH_IN); + IGRAPH_ASSERT(! res); + + root = -1; + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(res); + IGRAPH_ASSERT(root == 0); + + igraph_destroy(&g); + + /* in-tree */ + igraph_small(&g, 4, 1, + 0, 1, 2, 1, 1, 3, -1); + + root = -1; + igraph_is_tree(&g, &res, &root, IGRAPH_IN); + IGRAPH_ASSERT(res); + IGRAPH_ASSERT(root == 3); + + igraph_is_tree(&g, &res, &root, IGRAPH_OUT); + IGRAPH_ASSERT(! res); + + root = -1; + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(res); + IGRAPH_ASSERT(root == 0); + + igraph_destroy(&g); + + /* neither an in-tree, nor an out-ree, but still a tree when ignoring edge-directions */ + igraph_small(&g, 6, 1, + 0, 1, 1, 2, 2, 3, 4, 1, 2, 5, -1); + + root = -1; + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(res); + IGRAPH_ASSERT(root == 0); + + igraph_is_tree(&g, &res, &root, IGRAPH_IN); + IGRAPH_ASSERT(! res); + + igraph_is_tree(&g, &res, &root, IGRAPH_OUT); + IGRAPH_ASSERT(! res); + + igraph_destroy(&g); + + /* Regression test, see: + * https://github.com/szhorvat/IGraphM/issues/90 + * https://github.com/igraph/igraph/pull/1160 + */ + igraph_small(&g, 5, 0, + 0, 3, 0, 4, 1, 3, 1, 4, -1); + + igraph_is_tree(&g, &res, &root, IGRAPH_ALL); + IGRAPH_ASSERT(! res); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_isomorphic.c b/tests/unit/igraph_isomorphic.c new file mode 100644 index 0000000..6e449ce --- /dev/null +++ b/tests/unit/igraph_isomorphic.c @@ -0,0 +1,72 @@ +/* + IGraph library. + Copyright (C) 2023 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 +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g1, igraph_t *g2) { + igraph_bool_t iso; + igraph_isomorphic(g1, g2, &iso); + printf("%d\n", iso); + igraph_destroy(g1); + igraph_destroy(g2); +} + +int main(void) { + igraph_t g1, g2; + igraph_bool_t iso; + printf("Two multigraphs: "); + igraph_small(&g1, 3, IGRAPH_UNDIRECTED, 0,1, 0,1, -1); + igraph_small(&g2, 3, IGRAPH_UNDIRECTED, 1,2, 1,2, -1); + print_and_destroy(&g1, &g2); + + printf("Only second graph is multigraph: "); + igraph_small(&g1, 3, IGRAPH_UNDIRECTED, 0,1, -1); + igraph_small(&g2, 3, IGRAPH_UNDIRECTED, 1,2, 1,2, -1); + print_and_destroy(&g1, &g2); + + printf("Only first graph is multigraph: "); + igraph_small(&g1, 3, IGRAPH_UNDIRECTED, 0,1, 0,1, -1); + igraph_small(&g2, 3, IGRAPH_UNDIRECTED, 1,2, -1); + print_and_destroy(&g1, &g2); + + printf("Two multigraphs, first has loop: "); + igraph_small(&g1, 3, IGRAPH_UNDIRECTED, 0,0, 0,1, 0,1, -1); + igraph_small(&g2, 3, IGRAPH_UNDIRECTED, 1,2, 1,2, -1); + print_and_destroy(&g1, &g2); + + printf("Two multigraphs, both have loops: "); + igraph_small(&g1, 3, IGRAPH_UNDIRECTED, 0,0, 0,1, 0,1, -1); + igraph_small(&g2, 3, IGRAPH_UNDIRECTED, 1,1, 1,2, 1,2, -1); + print_and_destroy(&g1, &g2); + + printf("Two multigraphs, only loops: "); + igraph_small(&g1, 3, IGRAPH_UNDIRECTED, 0,0, 0,0, 1,1, -1); + igraph_small(&g2, 3, IGRAPH_UNDIRECTED, 1,1, 2,2, 2,2, -1); + print_and_destroy(&g1, &g2); + + printf("Check error for two multigraphs, different directedness\n"); + igraph_small(&g1, 3, IGRAPH_UNDIRECTED, 0,1, 0,1, -1); + igraph_small(&g2, 3, IGRAPH_DIRECTED, 1,2, 1,2, -1); + CHECK_ERROR(igraph_isomorphic(&g1, &g2, &iso), IGRAPH_EINVAL); + igraph_destroy(&g1); + igraph_destroy(&g2); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_isomorphic.out b/tests/unit/igraph_isomorphic.out new file mode 100644 index 0000000..d666c62 --- /dev/null +++ b/tests/unit/igraph_isomorphic.out @@ -0,0 +1,7 @@ +Two multigraphs: 1 +Only second graph is multigraph: 0 +Only first graph is multigraph: 0 +Two multigraphs, first has loop: 0 +Two multigraphs, both have loops: 1 +Two multigraphs, only loops: 1 +Check error for two multigraphs, different directedness diff --git a/tests/unit/igraph_isomorphic_bliss.c b/tests/unit/igraph_isomorphic_bliss.c new file mode 100644 index 0000000..f030bb8 --- /dev/null +++ b/tests/unit/igraph_isomorphic_bliss.c @@ -0,0 +1,150 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g1, g2; + igraph_t ring1, ring2; + igraph_vector_int_t color1, color2; + igraph_vector_int_t perm; + igraph_bool_t iso; + + igraph_bliss_sh_t sh_values[] = { + IGRAPH_BLISS_F, IGRAPH_BLISS_FL, + IGRAPH_BLISS_FS, IGRAPH_BLISS_FM, + IGRAPH_BLISS_FLM, IGRAPH_BLISS_FSM + }; + + const char *sh_names[] = { + "F", "FL", "FS", "FM", "FLM", "FSM" + }; + + size_t i; + + /* necessary because of igraph_vector_shuffle() below */ + igraph_rng_seed(igraph_rng_default(), 137); + + for (i=0; i < sizeof(sh_values) / sizeof(igraph_bliss_sh_t); ++i) { + + igraph_bliss_sh_t sh = sh_values[i]; + printf("Splitting heuristic: %s\n", sh_names[i]); + + igraph_ring(&ring1, 100, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/1); + igraph_vector_int_init_range(&perm, 0, igraph_vcount(&ring1)); + igraph_vector_int_shuffle(&perm); + igraph_permute_vertices(&ring1, &ring2, &perm); + + /* Without colors */ + printf("Without vertex colors: "); + iso = 0; + igraph_isomorphic_bliss(&ring1, &ring2, 0, 0, &iso, 0, 0, sh, 0, 0); + printf("%s\n", iso ? "YES" : "NO"); + + /* Everything has the same colors */ + printf("All vertices having the same color: "); + igraph_vector_int_init(&color1, igraph_vcount(&ring1)); + igraph_vector_int_init(&color2, igraph_vcount(&ring2)); + igraph_vector_int_fill(&color1, 1); + igraph_vector_int_fill(&color2, 1); + + iso = 0; + igraph_isomorphic_bliss(&ring1, &ring2, &color1, &color2, &iso, 0, 0, sh, 0, 0); + printf("%s\n", iso ? "YES" : "NO"); + + /* Try a negative result */ + printf("Non-matching colors 1: "); + igraph_vector_int_fill(&color1, 0); + igraph_vector_int_fill(&color2, 0); + VECTOR(color1)[0] = 1; + + iso = 1; + igraph_isomorphic_bliss(&ring1, &ring2, &color1, &color2, &iso, 0, 0, sh, 0, 0); + printf("%s\n", iso ? "YES" : "NO"); + + /* Another negative, same color distribution, different topology */ + printf("Non-matching colors 2: "); + igraph_vector_int_fill(&color1, 0); + igraph_vector_int_fill(&color2, 0); + VECTOR(color1)[0] = 1; + VECTOR(color1)[1] = 1; + VECTOR(color2)[0] = 1; + VECTOR(color2)[3] = 1; + + iso = 1; + igraph_isomorphic_bliss(&ring1, &ring2, &color1, &color2, &iso, 0, 0, sh, 0, 0); + printf("%s\n", iso ? "YES" : "NO"); + + igraph_vector_int_destroy(&color1); + igraph_vector_int_destroy(&color2); + + igraph_vector_int_destroy(&perm); + igraph_destroy(&ring2); + igraph_destroy(&ring1); + + /* More complicated test with colors */ + printf("Isomorphic colored graphs: "); + + igraph_small(&g1, 8, IGRAPH_DIRECTED, + 0, 4, 0, 5, 0, 6, 1, 4, 1, 5, 1, 7, 2, 4, 2, 6, 2, 7, 3, 5, 3, 6, 3, 7, -1 + ); + igraph_small(&g2, 8, IGRAPH_DIRECTED, + 0, 1, 0, 3, 0, 4, 2, 3, 2, 1, 2, 6, 5, 1, 5, 4, 5, 6, 7, 3, 7, 6, 7, 4, -1 + ); + + igraph_vector_int_init(&color1, 8); + igraph_vector_int_init(&color2, 8); + + VECTOR(color1)[1] = 1; + VECTOR(color1)[3] = 1; + VECTOR(color1)[5] = 1; + VECTOR(color1)[7] = 1; + + VECTOR(color2)[2] = 1; + VECTOR(color2)[3] = 1; + VECTOR(color2)[6] = 1; + VECTOR(color2)[7] = 1; + + iso = 0; + igraph_isomorphic_bliss(&g1, &g2, &color1, &color2, &iso, 0, 0, sh, 0, 0); + printf("%s\n", iso ? "YES" : "NO"); + + igraph_vector_int_destroy(&color1); + igraph_vector_int_destroy(&color2); + + igraph_destroy(&g2); + igraph_destroy(&g1); + + printf("\n"); + + VERIFY_FINALLY_STACK(); + + } + + return 0; +} diff --git a/tests/unit/igraph_isomorphic_bliss.out b/tests/unit/igraph_isomorphic_bliss.out new file mode 100644 index 0000000..1424d0f --- /dev/null +++ b/tests/unit/igraph_isomorphic_bliss.out @@ -0,0 +1,42 @@ +Splitting heuristic: F +Without vertex colors: YES +All vertices having the same color: YES +Non-matching colors 1: NO +Non-matching colors 2: NO +Isomorphic colored graphs: YES + +Splitting heuristic: FL +Without vertex colors: YES +All vertices having the same color: YES +Non-matching colors 1: NO +Non-matching colors 2: NO +Isomorphic colored graphs: YES + +Splitting heuristic: FS +Without vertex colors: YES +All vertices having the same color: YES +Non-matching colors 1: NO +Non-matching colors 2: NO +Isomorphic colored graphs: YES + +Splitting heuristic: FM +Without vertex colors: YES +All vertices having the same color: YES +Non-matching colors 1: NO +Non-matching colors 2: NO +Isomorphic colored graphs: YES + +Splitting heuristic: FLM +Without vertex colors: YES +All vertices having the same color: YES +Non-matching colors 1: NO +Non-matching colors 2: NO +Isomorphic colored graphs: YES + +Splitting heuristic: FSM +Without vertex colors: YES +All vertices having the same color: YES +Non-matching colors 1: NO +Non-matching colors 2: NO +Isomorphic colored graphs: YES + diff --git a/tests/unit/igraph_isomorphic_vf2.c b/tests/unit/igraph_isomorphic_vf2.c new file mode 100644 index 0000000..c91d0d2 --- /dev/null +++ b/tests/unit/igraph_isomorphic_vf2.c @@ -0,0 +1,226 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t ring1, ring2; + igraph_vector_int_t color1, color2; + igraph_vector_int_t perm; + igraph_bool_t iso; + igraph_integer_t count; + igraph_integer_t i; + + igraph_rng_seed(igraph_rng_default(), 12345); + + igraph_ring(&ring1, 100, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/1); + igraph_vector_int_init_range(&perm, 0, igraph_vcount(&ring1)); + igraph_vector_int_shuffle(&perm); + igraph_permute_vertices(&ring1, &ring2, &perm); + + /* Without colors */ + igraph_isomorphic(&ring1, &ring2, &iso); + if (!iso) { + fprintf(stderr, "Without color failed.\n"); + return 1; + } + + /* Without colors, number of isomorphisms */ + igraph_count_isomorphisms_vf2(&ring1, &ring2, 0, 0, 0, 0, &count, 0, 0, 0); + if (count != 200) { + fprintf(stderr, "Count without colors failed, expected 200, got %" IGRAPH_PRId ".\n", count); + return 2; + } + + /* Separate colors for each vertex */ + igraph_vector_int_init(&color1, igraph_vcount(&ring1)); + igraph_vector_int_init(&color2, igraph_vcount(&ring2)); + for (i = 0; i < igraph_vector_int_size(&color1); i++) { + VECTOR(color1)[i] = VECTOR(color2)[VECTOR(perm)[i]] = i; + } + igraph_count_isomorphisms_vf2(&ring1, &ring2, &color1, &color2, 0, 0, &count, 0, 0, 0); + if (count != 1) { + fprintf(stderr, "Count with separate colors failed, expected 1, got %" IGRAPH_PRId ".\n", count); + return 5; + } + + /* Try a negative result */ + igraph_vector_int_fill(&color1, 0); + igraph_vector_int_fill(&color2, 0); + VECTOR(color1)[0] = 1; + igraph_isomorphic_vf2(&ring1, &ring2, &color1, &color2, 0, 0, &iso, 0, 0, 0, 0, 0); + if (iso) { + fprintf(stderr, "Negative test failed.\n"); + return 6; + } + + /* Another negative, same color distribution, different topology */ + igraph_vector_int_fill(&color1, 0); + igraph_vector_int_fill(&color2, 0); + VECTOR(color1)[0] = 1; + VECTOR(color1)[1] = 1; + VECTOR(color2)[0] = 1; + VECTOR(color2)[(VECTOR(perm)[1] + 1) % igraph_vcount(&ring2)] = 1; + igraph_isomorphic_vf2(&ring1, &ring2, &color1, &color2, 0, 0, &iso, 0, 0, 0, 0, 0); + if (iso) { + fprintf(stderr, "Second negative test failed.\n"); + return 7; + } + + igraph_vector_int_destroy(&color1); + igraph_vector_int_destroy(&color2); + + igraph_vector_int_destroy(&perm); + igraph_destroy(&ring2); + igraph_destroy(&ring1); + + /* ---------------------------------------------------------------- */ + /* SUBGRAPH ISOMORPHISM */ + /* ---------------------------------------------------------------- */ + + igraph_ring(&ring1, 100, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/0); + igraph_ring(&ring2, 80, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/0); + + /* One color */ + igraph_vector_int_init(&color1, igraph_vcount(&ring1)); + igraph_vector_int_init(&color2, igraph_vcount(&ring2)); + igraph_count_subisomorphisms_vf2(&ring1, &ring2, &color1, &color2, 0, 0, + &count, 0, 0, 0); + if (count != 42) { + fprintf(stderr, "Count with one color failed, expected 42, got %" IGRAPH_PRId ".\n", count); + return 31; + } + + igraph_vector_int_destroy(&color1); + igraph_vector_int_destroy(&color2); + + igraph_destroy(&ring1); + igraph_destroy(&ring2); + + /* ---------------------------------------------------------------- */ + /* EDGE COLORING, GRAPH ISOMORPHISM */ + /* ---------------------------------------------------------------- */ + + igraph_ring(&ring1, 100, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/ 1); + igraph_vector_int_init_range(&perm, 0, igraph_ecount(&ring1)); + igraph_vector_int_shuffle(&perm); + igraph_permute_vertices(&ring1, &ring2, &perm); + igraph_vector_int_destroy(&perm); + + /* Everything has the same color */ + igraph_vector_int_init(&color1, igraph_ecount(&ring1)); + igraph_vector_int_init(&color2, igraph_ecount(&ring2)); + igraph_isomorphic_vf2(&ring1, &ring2, 0, 0, &color1, &color2, &iso, 0, 0, 0, 0, 0); + if (!iso) { + fprintf(stderr, "Single edge-color failed.\n"); + return 41; + } + + /* Two colors, just counting */ + for (i = 0; i < igraph_vector_int_size(&color1); i += 2) { + VECTOR(color1)[i] = VECTOR(color2)[i] = 0; + VECTOR(color1)[i + 1] = VECTOR(color2)[i] = 1; + } + igraph_count_isomorphisms_vf2(&ring1, &ring2, 0, 0, &color1, &color2, &count, 0, 0, 0); + if (count != 100) { + fprintf(stderr, "Count with two edge colors failed, expected 100, got %" IGRAPH_PRId ".\n", count); + return 42; + } + + /* Separate colors for each edge */ + for (i = 0; i < igraph_vector_int_size(&color1); i++) { + VECTOR(color1)[i] = VECTOR(color2)[i] = i; + } + igraph_count_isomorphisms_vf2(&ring1, &ring2, 0, 0, &color1, &color2, &count, 0, 0, 0); + if (count != 1) { + fprintf(stderr, "Count with separate edge colors failed, expected 1, got %" IGRAPH_PRId ".\n", count); + return 43; + } + + /* Try a negative result */ + igraph_vector_int_fill(&color1, 0); + igraph_vector_int_fill(&color2, 0); + VECTOR(color1)[0] = 1; + igraph_isomorphic_vf2(&ring1, &ring2, 0, 0, &color1, &color2, &iso, 0, 0, 0, 0, 0); + if (iso) { + fprintf(stderr, "Negative edge test failed.\n"); + return 44; + } + + /* Another negative, same color distribution, different topology */ + igraph_vector_int_fill(&color1, 0); + igraph_vector_int_fill(&color2, 0); + VECTOR(color1)[0] = 1; + VECTOR(color1)[1] = 1; + VECTOR(color2)[0] = 1; + VECTOR(color2)[2] = 1; + igraph_isomorphic_vf2(&ring1, &ring2, 0, 0, &color1, &color2, &iso, 0, 0, 0, 0, 0); + if (iso) { + fprintf(stderr, "Second negative edge test failed.\n"); + return 45; + } + + igraph_vector_int_destroy(&color1); + igraph_vector_int_destroy(&color2); + + igraph_destroy(&ring1); + igraph_destroy(&ring2); + + /* ---------------------------------------------------------------- */ + /* EDGE COLORED SUBGRAPH ISOMORPHISM */ + /* ---------------------------------------------------------------- */ + + igraph_ring(&ring1, 100, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/0); + igraph_ring(&ring2, 80, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/0); + + /* One color */ + igraph_vector_int_init(&color1, igraph_ecount(&ring1)); + igraph_vector_int_init(&color2, igraph_ecount(&ring2)); + igraph_count_subisomorphisms_vf2(&ring1, &ring2, 0, 0, &color1, &color2, + &count, 0, 0, 0); + if (count != 42) { + fprintf(stderr, "Count with one edge color failed, expected 42, got %" IGRAPH_PRId ".\n", count); + return 51; + } + + /* Two colors */ + for (i = 0; i < igraph_vector_int_size(&color1) - 1; i += 2) { + VECTOR(color1)[i] = 0; + VECTOR(color1)[i + 1] = 1; + } + for (i = 0; i < igraph_vector_int_size(&color2) - 1; i += 2) { + VECTOR(color2)[i] = 0; + VECTOR(color2)[i + 1] = 1; + } + igraph_count_subisomorphisms_vf2(&ring1, &ring2, 0, 0, &color1, &color2, + &count, 0, 0, 0); + if (count != 22) { + fprintf(stderr, "Count with two edge colors failed, expected 22, got %" IGRAPH_PRId ".\n", count); + return 52; + } + + igraph_vector_int_destroy(&color1); + igraph_vector_int_destroy(&color2); + + igraph_destroy(&ring1); + igraph_destroy(&ring2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_join.c b/tests/unit/igraph_join.c new file mode 100644 index 0000000..a246da2 --- /dev/null +++ b/tests/unit/igraph_join.c @@ -0,0 +1,78 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t left, right, joined; + + /* Standard join. */ + printf("Standard join.\n"); + igraph_small(&left, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, -1); + igraph_small(&right, 5, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,4, -1); + + igraph_join(&joined, &left, &right); + print_graph(&joined); + printf("\n"); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&joined); + + /* Standard directed join */ + printf("Standard directed join.\n"); + igraph_small(&left, 2, IGRAPH_DIRECTED, 0,1, -1); + igraph_small(&right, 3, IGRAPH_DIRECTED, 0,1, 2,1, -1); + + igraph_join(&joined, &left, &right); + print_graph(&joined); + printf("\n"); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&joined); + + /* Empty graphs; the result is the null graph. */ + igraph_small(&left, 0, IGRAPH_UNDIRECTED, -1); + igraph_small(&right, 0, IGRAPH_UNDIRECTED, -1); + igraph_join(&joined, &left, &right); + IGRAPH_ASSERT(igraph_ecount(&joined) != 0 || igraph_vcount(&joined) != 0); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&joined); + + /* Empty graph joined with non-empty; the result is the non-empty. */ + igraph_small(&left, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, -1); + igraph_small(&right, 0, IGRAPH_UNDIRECTED, -1); + igraph_join(&joined, &left, &right); + IGRAPH_ASSERT(igraph_ecount(&joined) != igraph_ecount(&left) + || igraph_vcount(&joined) != igraph_vcount(&left)); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&joined); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_join.out b/tests/unit/igraph_join.out new file mode 100644 index 0000000..8e83993 --- /dev/null +++ b/tests/unit/igraph_join.out @@ -0,0 +1,54 @@ +Standard join. +directed: false +vcount: 9 +edges: { +0 1 +0 4 +0 5 +0 6 +0 7 +0 8 +1 2 +1 4 +1 5 +1 6 +1 7 +1 8 +2 2 +2 4 +2 5 +2 6 +2 7 +2 8 +3 4 +3 5 +3 6 +3 7 +3 8 +4 5 +5 6 +6 6 +6 8 +} + +Standard directed join. +directed: true +vcount: 5 +edges: { +0 1 +0 2 +0 3 +0 4 +1 2 +1 3 +1 4 +2 0 +2 1 +2 3 +3 0 +3 1 +4 0 +4 1 +4 3 +} + diff --git a/tests/unit/igraph_joint_degree_distribution.c b/tests/unit/igraph_joint_degree_distribution.c new file mode 100644 index 0000000..0c9169f --- /dev/null +++ b/tests/unit/igraph_joint_degree_distribution.c @@ -0,0 +1,312 @@ +/* + IGraph library. + Copyright (C) 2023 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 + +#include "test_utilities.h" + +/* Check vector equality with tolerances. Consider NaN values equal. */ +igraph_bool_t vector_eq(const igraph_vector_t *a, const igraph_vector_t *b) { + igraph_integer_t na = igraph_vector_size(a); + igraph_integer_t nb = igraph_vector_size(b); + if (na != nb) { + return false; + } + for (igraph_integer_t i=0; i < na; i++) { + if (isnan(VECTOR(*a)[i]) && isnan(VECTOR(*b)[i])) { + continue; + } + if (! igraph_almost_equals(VECTOR(*a)[i], VECTOR(*b)[i], 1e-12)) { + return false; + } + } + return true; +} + +/* Compare to igraph_joint_degree_matrix() */ +void check_jdm(const igraph_t *g, const igraph_vector_t *weights) { + igraph_matrix_t jdm, p; + igraph_integer_t vcount = igraph_vcount(g); + igraph_integer_t nrow, ncol, n; + + igraph_matrix_init(&jdm, 0, 0); + igraph_matrix_init(&p, 0, 0); + + igraph_joint_degree_matrix(g, weights, &jdm, -1, -1); + igraph_joint_degree_distribution(g, weights, &p, IGRAPH_OUT, IGRAPH_IN, true, /*normalized*/ false, -1, -1); + + nrow = igraph_matrix_nrow(&p); + ncol = igraph_matrix_ncol(&p); + + if (vcount > 0) { + igraph_vector_t v; + + IGRAPH_ASSERT(nrow > 0); + IGRAPH_ASSERT(ncol > 0); + nrow--; + ncol--; + + IGRAPH_ASSERT(igraph_matrix_nrow(&jdm) == nrow); + IGRAPH_ASSERT(igraph_matrix_ncol(&jdm) == ncol); + + igraph_vector_init(&v, 0); + + igraph_matrix_get_col(&p, &v, 0); + IGRAPH_ASSERT(igraph_vector_isnull(&v)); + + igraph_matrix_get_row(&p, &v, 0); + IGRAPH_ASSERT(igraph_vector_isnull(&v)); + + igraph_vector_destroy(&v); + + igraph_matrix_remove_row(&p, 0); + igraph_matrix_remove_col(&p, 0); + } else { + // vcount == 0 + IGRAPH_ASSERT(nrow == 0); + IGRAPH_ASSERT(ncol == 0); + } + + n = nrow < ncol ? nrow : ncol; + + if (! igraph_is_directed(g)) { + for (igraph_integer_t i=0; i < n; i++) { + MATRIX(jdm, i, i) *= 2; + } + } + + igraph_real_t total; + if (weights) { + total = igraph_vector_sum(weights); + } else { + total = igraph_ecount(g); + } + if (! igraph_is_directed(g)) { + total *= 2; + } + + IGRAPH_ASSERT(igraph_matrix_sum(&p) == total); + + IGRAPH_ASSERT(igraph_matrix_all_e(&jdm, &p)); + + igraph_matrix_destroy(&p); + igraph_matrix_destroy(&jdm); +} + +void check_assort_i(const igraph_t *g, igraph_neimode_t from_mode, igraph_neimode_t to_mode) { + igraph_matrix_t p; + igraph_real_t r1, r2; + igraph_integer_t nrow, ncol; + igraph_vector_t dfrom, dto; + igraph_vector_t a, b; + igraph_bool_t directed = igraph_is_directed(g); + + igraph_vector_init(&dfrom, 0); + igraph_vector_init(&dto, 0); + igraph_vector_init(&a, 0); + igraph_vector_init(&b, 0); + + igraph_strength(g, &dfrom, igraph_vss_all(), from_mode, true, NULL); + igraph_strength(g, &dto, igraph_vss_all(), to_mode, true, NULL); + + igraph_matrix_init(&p, 0, 0); + igraph_joint_degree_distribution(g, NULL, &p, from_mode, to_mode, true, /*normalized*/ true, -1, -1); + + nrow = igraph_matrix_nrow(&p); + ncol = igraph_matrix_ncol(&p); + + igraph_assortativity(g, &dfrom, directed ? &dto : NULL, &r1, IGRAPH_DIRECTED, /*normalized*/ false); + + igraph_matrix_rowsum(&p, &a); + igraph_matrix_colsum(&p, &b); + + r2 = 0; + for (igraph_integer_t i=0; i < nrow; i++) { + for (igraph_integer_t j=0; j < ncol; j++) { + r2 += (MATRIX(p, i, j) - VECTOR(a)[i] * VECTOR(b)[j]) * (igraph_real_t) i * (igraph_real_t) j; + } + } + // printf("Assortativity: %g == %g\n", r1, r2); + IGRAPH_ASSERT(igraph_almost_equals(r1, r2, 1e-13)); + + igraph_matrix_destroy(&p); + + igraph_vector_destroy(&b); + igraph_vector_destroy(&a); + igraph_vector_destroy(&dto); + igraph_vector_destroy(&dfrom); +} + +/* Compare to igraph_assortativity() */ +void check_assort(const igraph_t *g) { + if (igraph_is_directed(g)) { + check_assort_i(g, IGRAPH_OUT, IGRAPH_IN); + check_assort_i(g, IGRAPH_IN, IGRAPH_OUT); + check_assort_i(g, IGRAPH_OUT, IGRAPH_OUT); + check_assort_i(g, IGRAPH_IN, IGRAPH_IN); + check_assort_i(g, IGRAPH_ALL, IGRAPH_IN); + check_assort_i(g, IGRAPH_ALL, IGRAPH_OUT); + check_assort_i(g, IGRAPH_OUT, IGRAPH_ALL); + check_assort_i(g, IGRAPH_IN, IGRAPH_ALL); + } else { + check_assort_i(g, IGRAPH_ALL, IGRAPH_ALL); + } +} + +void check_knnk_i(const igraph_t *g, const igraph_vector_t *weights, igraph_neimode_t from_mode, igraph_neimode_t to_mode) { + igraph_matrix_t p; + igraph_integer_t nrow, ncol; + igraph_vector_t knnk, knnk2; + igraph_vector_t q; + + igraph_vector_init(&knnk, 0); + igraph_vector_init(&q, 0); + + igraph_matrix_init(&p, 0, 0); + igraph_joint_degree_distribution(g, weights, &p, from_mode, to_mode, true, /*normalized*/ true, -1, -1); + + nrow = igraph_matrix_nrow(&p); + ncol = igraph_matrix_ncol(&p); + + igraph_degree_correlation_vector(g, weights, &knnk, from_mode, to_mode, /*directed_neighbors*/ true); + IGRAPH_ASSERT(igraph_vector_size(&knnk) == nrow); + + igraph_vector_init(&knnk2, nrow); + igraph_matrix_rowsum(&p, &q); + for (igraph_integer_t k=0; k < nrow; k++) { + for (igraph_integer_t j=0; j < ncol; j++) { + VECTOR(knnk2)[k] += j * MATRIX(p, k, j); + } + } + + igraph_vector_div(&knnk2, &q); + + /* + printf("%d - %d\n", from_mode, to_mode); + print_vector(&knnk); + print_vector(&knnk2); + */ + IGRAPH_ASSERT(vector_eq(&knnk, &knnk2)); + + igraph_vector_destroy(&knnk2); + + igraph_matrix_destroy(&p); + + igraph_vector_destroy(&q); + igraph_vector_destroy(&knnk); +} + +/* Compare to igraph_degree_correlation_vector() */ +void check_knnk(const igraph_t *g, const igraph_vector_t *weights) { + if (igraph_is_directed(g)) { + check_knnk_i(g, weights, IGRAPH_OUT, IGRAPH_IN); + check_knnk_i(g, weights, IGRAPH_IN, IGRAPH_OUT); + check_knnk_i(g, weights, IGRAPH_OUT, IGRAPH_OUT); + check_knnk_i(g, weights, IGRAPH_IN, IGRAPH_IN); + check_knnk_i(g, weights, IGRAPH_ALL, IGRAPH_IN); + check_knnk_i(g, weights, IGRAPH_ALL, IGRAPH_OUT); + check_knnk_i(g, weights, IGRAPH_OUT, IGRAPH_ALL); + check_knnk_i(g, weights, IGRAPH_IN, IGRAPH_ALL); + } else { + check_knnk_i(g, weights, IGRAPH_ALL, IGRAPH_ALL); + } +} + +int main(void) { + igraph_t dg, ug; + igraph_vector_t weights; + igraph_matrix_t p; + + igraph_rng_seed(igraph_rng_default(), 137); + + igraph_matrix_init(&p, 0, 0); + + /* Directed */ + + igraph_empty(&dg, 0, IGRAPH_DIRECTED); + check_jdm(&dg, NULL); + igraph_destroy(&dg); + + igraph_empty(&dg, 1, IGRAPH_DIRECTED); + check_jdm(&dg, NULL); + igraph_destroy(&dg); + + igraph_small(&dg, 2, IGRAPH_DIRECTED, 0,0, 0,1, 0,1, 1,0, -1); + check_jdm(&dg, NULL); + check_assort(&dg); + check_knnk(&dg, NULL); + igraph_destroy(&dg); + + igraph_erdos_renyi_game_gnm(&dg, 10, 30, IGRAPH_DIRECTED, /*loops*/ true); + check_jdm(&dg, NULL); + check_assort(&dg); + check_knnk(&dg, NULL); + + igraph_vector_init_range(&weights, 0, igraph_ecount(&dg)); + check_jdm(&dg, &weights); + check_knnk(&dg, &weights); + igraph_vector_destroy(&weights); + + igraph_joint_degree_distribution(&dg, NULL, &p, IGRAPH_ALL, IGRAPH_ALL, false, false, -1, -1); + print_matrix(&p); + + igraph_joint_degree_distribution(&dg, NULL, &p, IGRAPH_ALL, IGRAPH_ALL, false, false, 3, 10); + print_matrix(&p); + + igraph_destroy(&dg); + + /* Undirected */ + + igraph_empty(&ug, 0, IGRAPH_UNDIRECTED); + check_jdm(&ug, NULL); + igraph_destroy(&ug); + + igraph_empty(&ug, 1, IGRAPH_UNDIRECTED); + check_jdm(&ug, NULL); + igraph_destroy(&ug); + + igraph_small(&ug, 2, IGRAPH_UNDIRECTED, 0,1, 0,1, 1,1, -1); + check_jdm(&ug, NULL); + check_assort(&ug); + check_knnk(&ug, NULL); + igraph_destroy(&ug); + + igraph_erdos_renyi_game_gnm(&ug, 10, 30, IGRAPH_UNDIRECTED, /*loops*/ true); + check_jdm(&ug, NULL); + check_assort(&ug); + check_knnk(&ug, NULL); + + igraph_vector_init_range(&weights, 0, igraph_ecount(&ug)); + check_jdm(&ug, &weights); + check_knnk(&ug, &weights); + igraph_vector_destroy(&weights); + + igraph_joint_degree_distribution(&ug, NULL, &p, IGRAPH_ALL, IGRAPH_ALL, false, false, -1, -1); + print_matrix(&p); + + igraph_joint_degree_distribution(&ug, NULL, &p, IGRAPH_ALL, IGRAPH_ALL, false, false, 3, 10); + print_matrix(&p); + + igraph_destroy(&ug); + + igraph_matrix_destroy(&p); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_joint_degree_distribution.out b/tests/unit/igraph_joint_degree_distribution.out new file mode 100644 index 0000000..e180dff --- /dev/null +++ b/tests/unit/igraph_joint_degree_distribution.out @@ -0,0 +1,26 @@ +[ 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 0 0 0 0 0 0 0 0 + 0 0 0 0 4 0 2 3 4 3 + 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 2 0 2 2 4 2 + 0 0 0 0 3 0 2 0 1 1 + 0 0 0 0 4 0 4 1 4 3 + 0 0 0 0 3 0 2 1 3 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 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 0 0 0 0 0 + 0 0 0 0 0 0 0 6 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 5 + 0 0 0 0 0 0 0 0 + 0 0 0 6 0 5 0 38 ] +[ 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 0 + 0 0 0 0 3 0 0 3 0 0 0 ] diff --git a/tests/unit/igraph_joint_type_distribution.c b/tests/unit/igraph_joint_type_distribution.c new file mode 100644 index 0000000..1ab5cef --- /dev/null +++ b/tests/unit/igraph_joint_type_distribution.c @@ -0,0 +1,163 @@ +/* + IGraph library. + Copyright (C) 2023 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 + +#include "test_utilities.h" + +/* Compare with igraph_modularity() and igraph_assortativity_nominal() */ +void check_assort(const igraph_t *g, const igraph_vector_t *weights, const igraph_vector_int_t *types) { + igraph_vector_t a, b; + igraph_matrix_t p; + igraph_integer_t n; + igraph_real_t c1, c2, q1, q2; + + igraph_vector_init(&a, 0); + igraph_vector_init(&b, 0); + + igraph_matrix_init(&p, 0, 0); + + igraph_joint_type_distribution(g, weights, &p, types, NULL, /*directed*/ true, /*normalized*/ true); + + igraph_matrix_rowsum(&p, &a); + igraph_matrix_colsum(&p, &b); + + n = igraph_matrix_nrow(&p); + IGRAPH_ASSERT(igraph_matrix_ncol(&p) == n); + + c1 = c2 = 0; + for (igraph_integer_t i=0; i < n; i++) { + c1 += MATRIX(p, i, i); + c2 += VECTOR(a)[i] * VECTOR(b)[i]; + } + q2 = c1 - c2; + + igraph_modularity(g, types, weights, 1, true, &q1); + // printf("Modularity: %g == %g\n", q1, q2); + IGRAPH_ASSERT(igraph_almost_equals(q1, q2, 1e-14)); + + if (! weights) { + q1 /= 1 - c2; + igraph_assortativity_nominal(g, types, &q2, /*directed*/ true, /*normalized*/ true); + // printf("Normalized nominal assortativity: %g == %g\n", q1, q2); + IGRAPH_ASSERT(igraph_almost_equals(q1, q2, 1e-14)); + } + + igraph_matrix_destroy(&p); + + igraph_vector_destroy(&b); + igraph_vector_destroy(&a); +} + +int main(void) { + igraph_t graph; + igraph_vector_int_t t1, t2; + igraph_vector_t weights; + igraph_matrix_t p; + + igraph_matrix_init(&p, 0, 0); + + printf("Null graph\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_init(&t1, 0); + igraph_joint_type_distribution(&graph, NULL, &p, &t1, NULL, false, false); + print_matrix(&p); + igraph_vector_int_destroy(&t1); + igraph_destroy(&graph); + + printf("\nSingleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_vector_int_init_int(&t1, 1, 0); + igraph_joint_type_distribution(&graph, NULL, &p, &t1, NULL, false, false); + print_matrix(&p); + igraph_vector_int_destroy(&t1); + igraph_destroy(&graph); + + printf("\nUndirected singleton with loop\n"); + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, 0,0, -1); + igraph_vector_int_init_int(&t1, 1, 0); + igraph_joint_type_distribution(&graph, NULL, &p, &t1, NULL, false, false); + print_matrix(&p); + igraph_vector_int_destroy(&t1); + igraph_destroy(&graph); + + printf("\nDirected singleton with loop\n"); + igraph_small(&graph, 1, IGRAPH_DIRECTED, 0,0, -1); + igraph_vector_int_init_int(&t1, 1, 0); + igraph_joint_type_distribution(&graph, NULL, &p, &t1, NULL, false, false); + print_matrix(&p); + igraph_vector_int_destroy(&t1); + igraph_destroy(&graph); + + printf("\nSmall undirected graph\n"); + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, + 3, 0, 0, 3, 0, 2, 3, 1, 5, 5, 4, 2, 1, 1, 1, 1, 0, 1, 5, 1, + -1); + + igraph_vector_int_init_int(&t1, 6, + 0, 0, 1, 1, 2, 2); + + igraph_joint_type_distribution(&graph, NULL, &p, &t1, NULL, false, false); + print_matrix(&p); + + IGRAPH_ASSERT(igraph_matrix_sum(&p) == 2*igraph_ecount(&graph)); + check_assort(&graph, NULL, &t1); + + igraph_vector_init_range(&weights, 10, 10 + igraph_ecount(&graph)); + check_assort(&graph, &weights, &t1); + igraph_vector_destroy(&weights); + + igraph_vector_int_destroy(&t1); + igraph_destroy(&graph); + + printf("\nSmall directed graph\n"); + igraph_small(&graph, 6, IGRAPH_DIRECTED, + 1, 1, 2, 4, 0, 2, 2, 2, 3, 2, 3, 0, 2, 1, 2, 3, 4, 1, 2, 4, + -1); + + igraph_vector_int_init_int(&t1, 6, + 0, 0, 1, 1, 2, 2); + + igraph_joint_type_distribution(&graph, NULL, &p, &t1, NULL, true, false); + + printf("From and to types are the same:\n"); + print_matrix(&p); + IGRAPH_ASSERT(igraph_matrix_sum(&p) == igraph_ecount(&graph)); + + check_assort(&graph, NULL, &t1); + + igraph_vector_init_range(&weights, 10, 10 + igraph_ecount(&graph)); + check_assort(&graph, &weights, &t1); + igraph_vector_destroy(&weights); + + igraph_vector_int_init_int(&t2, 6, + 0, 1, 1, 1, 0, 1); + printf("From and to types are different:\n"); + igraph_joint_type_distribution(&graph, NULL, &p, &t1, &t2, true, false); + print_matrix(&p); + IGRAPH_ASSERT(igraph_matrix_sum(&p) == igraph_ecount(&graph)); + + igraph_vector_int_destroy(&t2); + igraph_vector_int_destroy(&t1); + igraph_destroy(&graph); + + igraph_matrix_destroy(&p); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_joint_type_distribution.out b/tests/unit/igraph_joint_type_distribution.out new file mode 100644 index 0000000..743f562 --- /dev/null +++ b/tests/unit/igraph_joint_type_distribution.out @@ -0,0 +1,26 @@ +Null graph +[ 0-by-0 ] + +Singleton graph +[ 0 ] + +Undirected singleton with loop +[ 2 ] + +Directed singleton with loop +[ 2 ] + +Small undirected graph +[ 6 4 1 + 4 0 1 + 1 1 2 ] + +Small directed graph +From and to types are the same: +[ 1 1 0 + 2 3 2 + 1 0 0 ] +From and to types are different: +[ 0 2 + 3 4 + 0 1 ] diff --git a/tests/unit/igraph_k_regular_game.c b/tests/unit/igraph_k_regular_game.c new file mode 100644 index 0000000..a5e6833 --- /dev/null +++ b/tests/unit/igraph_k_regular_game.c @@ -0,0 +1,200 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t deg; + igraph_bool_t is_simple; + + igraph_set_error_handler(&igraph_error_handler_ignore); + + igraph_vector_int_init(°, 0); + + /* k-regular undirected graph, even degrees, no multiple edges */ + igraph_k_regular_game(&g, 10, 4, 0, 0); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_ALL, 1); + igraph_vector_int_print(°); + igraph_is_simple(&g, &is_simple); + if (!is_simple) { + return 1; + } + if (igraph_is_directed(&g)) { + return 1; + } + igraph_destroy(&g); + + /* k-regular undirected graph, odd degrees, even number of vertices, no multiple edges */ + igraph_k_regular_game(&g, 10, 3, 0, 0); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_ALL, 1); + igraph_vector_int_print(°); + igraph_is_simple(&g, &is_simple); + if (!is_simple) { + return 2; + } + if (igraph_is_directed(&g)) { + return 2; + } + igraph_destroy(&g); + + /* k-regular undirected graph, odd degrees, odd number of vertices, no multiple edges */ + if (!igraph_k_regular_game(&g, 9, 3, 0, 0)) { + return 3; + } + + /* k-regular undirected graph, even degrees, multiple edges */ + igraph_k_regular_game(&g, 10, 4, 0, 1); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_ALL, 1); + igraph_vector_int_print(°); + if (igraph_is_directed(&g)) { + return 14; + } + igraph_destroy(&g); + + /* k-regular undirected graph, odd degrees, even number of vertices, multiple edges */ + igraph_k_regular_game(&g, 10, 3, 0, 1); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_ALL, 1); + igraph_vector_int_print(°); + if (igraph_is_directed(&g)) { + return 15; + } + igraph_destroy(&g); + + /* k-regular undirected graph, odd degrees, odd number of vertices, multiple edges */ + if (!igraph_k_regular_game(&g, 9, 3, 0, 1)) { + return 4; + } + + /* k-regular directed graph, even degrees, no multiple edges */ + igraph_k_regular_game(&g, 10, 4, 1, 0); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_IN, 1); + igraph_vector_int_print(°); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_OUT, 1); + igraph_vector_int_print(°); + igraph_is_simple(&g, &is_simple); + if (!is_simple) { + return 5; + } + if (!igraph_is_directed(&g)) { + return 5; + } + igraph_destroy(&g); + + /* k-regular directed graph, odd degrees, even number of vertices, no multiple edges */ + igraph_k_regular_game(&g, 10, 3, 1, 0); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_IN, 1); + igraph_vector_int_print(°); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_OUT, 1); + igraph_vector_int_print(°); + igraph_is_simple(&g, &is_simple); + if (!is_simple) { + return 6; + } + if (!igraph_is_directed(&g)) { + return 6; + } + igraph_destroy(&g); + + /* k-regular directed graph, odd degrees, odd number of vertices, no multiple edges */ + igraph_k_regular_game(&g, 9, 3, 1, 0); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_IN, 1); + igraph_vector_int_print(°); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_OUT, 1); + igraph_vector_int_print(°); + igraph_is_simple(&g, &is_simple); + if (!is_simple) { + return 7; + } + if (!igraph_is_directed(&g)) { + return 7; + } + igraph_destroy(&g); + + /* k-regular directed graph, even degrees, multiple edges */ + igraph_k_regular_game(&g, 10, 4, 1, 1); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_IN, 1); + igraph_vector_int_print(°); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_OUT, 1); + igraph_vector_int_print(°); + if (!igraph_is_directed(&g)) { + return 16; + } + igraph_destroy(&g); + + /* k-regular directed graph, odd degrees, even number of vertices, multiple edges */ + igraph_k_regular_game(&g, 10, 3, 1, 1); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_IN, 1); + igraph_vector_int_print(°); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_OUT, 1); + igraph_vector_int_print(°); + if (!igraph_is_directed(&g)) { + return 17; + } + igraph_destroy(&g); + + /* k-regular directed graph, odd degrees, odd number of vertices, multiple edges */ + igraph_k_regular_game(&g, 9, 3, 1, 1); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_IN, 1); + igraph_vector_int_print(°); + igraph_degree(&g, °, igraph_vss_all(), IGRAPH_OUT, 1); + igraph_vector_int_print(°); + if (!igraph_is_directed(&g)) { + return 18; + } + igraph_destroy(&g); + + /* k-regular undirected graph, too large degree, no multiple edges */ + if (!igraph_k_regular_game(&g, 10, 10, 0, 0)) { + return 8; + } + + /* k-regular directed graph, too large degree, no multiple edges */ + if (!igraph_k_regular_game(&g, 10, 10, 1, 0)) { + return 9; + } + + /* empty graph */ + if (igraph_k_regular_game(&g, 0, 0, 0, 0)) { + return 10; + } + if (igraph_vcount(&g) != 0 || igraph_ecount(&g) != 0 || igraph_is_directed(&g)) { + return 11; + } + igraph_destroy(&g); + if (igraph_k_regular_game(&g, 0, 0, 1, 0)) { + return 12; + } + if (igraph_vcount(&g) != 0 || igraph_ecount(&g) != 0 || !igraph_is_directed(&g)) { + return 13; + } + igraph_destroy(&g); + + igraph_vector_int_destroy(°); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_k_regular_game.out b/tests/unit/igraph_k_regular_game.out new file mode 100644 index 0000000..8b82ce0 --- /dev/null +++ b/tests/unit/igraph_k_regular_game.out @@ -0,0 +1,16 @@ +4 4 4 4 4 4 4 4 4 4 +3 3 3 3 3 3 3 3 3 3 +4 4 4 4 4 4 4 4 4 4 +3 3 3 3 3 3 3 3 3 3 +4 4 4 4 4 4 4 4 4 4 +4 4 4 4 4 4 4 4 4 4 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 +4 4 4 4 4 4 4 4 4 4 +4 4 4 4 4 4 4 4 4 4 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 diff --git a/tests/unit/igraph_k_shortest_paths.out b/tests/unit/igraph_k_shortest_paths.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/igraph_kautz.c b/tests/unit/igraph_kautz.c new file mode 100644 index 0000000..a0d8eca --- /dev/null +++ b/tests/unit/igraph_kautz.c @@ -0,0 +1,61 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g, g_test; + igraph_bool_t iso, same; + + /* BA, AB, CB, etc. */ + IGRAPH_ASSERT(igraph_kautz(&g, /* m */ 2, /* n */ 1) == IGRAPH_SUCCESS); + igraph_small(&g_test, 6, IGRAPH_DIRECTED, 0, 1, 0, 5, 1, 0, 1, 4, 2, 0, 2, 4, 3, 1, 3, 5, 4, 2, 4, 3, 5, 3, 5, 2, -1); + IGRAPH_ASSERT(igraph_isomorphic(&g, &g_test, &iso) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(iso); + igraph_destroy(&g); + igraph_destroy(&g_test); + + /* 1 symbol, string length 11, should be empty graph */ + IGRAPH_ASSERT(igraph_kautz(&g, /* m */ 0, /* n */ 10) == IGRAPH_SUCCESS); + igraph_small(&g_test, 0, IGRAPH_DIRECTED, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g); + igraph_destroy(&g_test); + + /* 1 symbol, string length 1 should be single vertex */ + IGRAPH_ASSERT(igraph_kautz(&g, /* m */ 0, /* n */ 0) == IGRAPH_SUCCESS); + igraph_small(&g_test, 1, IGRAPH_DIRECTED, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g); + igraph_destroy(&g_test); + + /* String length 1 should be full graph */ + IGRAPH_ASSERT(igraph_kautz(&g, /* m */ 5, /* n */ 0) == IGRAPH_SUCCESS); + igraph_full(&g_test, 6, IGRAPH_DIRECTED, /*loops*/ 0); + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g); + igraph_destroy(&g_test); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_lapack_dgeev.c b/tests/unit/igraph_lapack_dgeev.c new file mode 100644 index 0000000..5490e4c --- /dev/null +++ b/tests/unit/igraph_lapack_dgeev.c @@ -0,0 +1,224 @@ +/* + 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, see . +*/ + +#include +#include +#include "test_utilities.h" + +#define DIM 10 + +int real_cplx_mult(const igraph_matrix_t *A, + const igraph_vector_t *v_real, + const igraph_vector_t *v_imag, + igraph_vector_t *res_real, + igraph_vector_t *res_imag) { + + igraph_integer_t n = igraph_vector_size(v_real); + igraph_integer_t r, c; + + if (igraph_matrix_nrow(A) != n || + igraph_matrix_ncol(A) != n || + igraph_vector_size(v_imag) != n) { + printf("Wrong matrix or vector size"); + return 1; + } + + igraph_vector_resize(res_real, n); + igraph_vector_resize(res_imag, n); + + for (r = 0; r < n; r++) { + igraph_real_t s_real = 0.0; + igraph_real_t s_imag = 0.0; + for (c = 0; c < n; c++) { + s_real += MATRIX(*A, r, c) * VECTOR(*v_real)[c]; + s_imag += MATRIX(*A, r, c) * VECTOR(*v_imag)[c]; + } + VECTOR(*res_real)[r] = s_real; + VECTOR(*res_imag)[r] = s_imag; + } + + return 0; +} + +int sc_cplx_cplx_mult(igraph_real_t lambda_real, + igraph_real_t lambda_imag, + const igraph_vector_t *v_real, + const igraph_vector_t *v_imag, + igraph_vector_t *res_real, + igraph_vector_t *res_imag) { + + igraph_integer_t r; + igraph_integer_t n = igraph_vector_size(v_real); + + if (igraph_vector_size(v_imag) != n) { + printf("Wrong vector sizes"); + return 1; + } + + igraph_vector_resize(res_real, n); + igraph_vector_resize(res_imag, n); + + for (r = 0; r < n; r++) { + VECTOR(*res_real)[r] = (lambda_real * VECTOR(*v_real)[r] - + lambda_imag * VECTOR(*v_imag)[r]); + VECTOR(*res_imag)[r] = (lambda_imag * VECTOR(*v_real)[r] + + lambda_real * VECTOR(*v_imag)[r]); + } + + return 0; +} + +igraph_bool_t check_ev(const igraph_matrix_t *A, + const igraph_vector_t *values_real, + const igraph_vector_t *values_imag, + const igraph_matrix_t *vectors_left, + const igraph_matrix_t *vectors_right, + igraph_real_t tol) { + + int i, n = igraph_matrix_nrow(A); + igraph_vector_t v_real, v_imag; + igraph_vector_t AV_real, AV_imag, lv_real, lv_imag; + igraph_vector_t null; + + if (igraph_matrix_ncol(A) != n) { + return 1; + } + if (igraph_vector_size(values_real) != n) { + return 1; + } + if (igraph_vector_size(values_imag) != n) { + return 1; + } + if (igraph_matrix_nrow(vectors_left) != n) { + return 1; + } + if (igraph_matrix_ncol(vectors_left) != n) { + return 1; + } + if (igraph_matrix_nrow(vectors_right) != n) { + return 1; + } + if (igraph_matrix_ncol(vectors_right) != n) { + return 1; + } + + igraph_vector_init(&AV_real, n); + igraph_vector_init(&AV_imag, n); + igraph_vector_init(&lv_real, n); + igraph_vector_init(&lv_imag, n); + igraph_vector_init(&null, n); + igraph_vector_null(&null); + + for (i = 0; i < n; i++) { + if (VECTOR(*values_imag)[i] == 0.0) { + igraph_vector_view(&v_real, &MATRIX(*vectors_right, 0, i), n); + igraph_vector_view(&v_imag, VECTOR(null), n); + } else if (VECTOR(*values_imag)[i] > 0.0) { + igraph_vector_view(&v_real, &MATRIX(*vectors_right, 0, i), n); + igraph_vector_view(&v_imag, &MATRIX(*vectors_right, 0, i + 1), n); + } else if (VECTOR(*values_imag)[i] < 0.0) { + igraph_vector_view(&v_real, &MATRIX(*vectors_right, 0, i - 1), n); + igraph_vector_view(&v_imag, &MATRIX(*vectors_right, 0, i), n); + igraph_vector_scale(&v_imag, -1.0); + } + real_cplx_mult(A, &v_real, &v_imag, &AV_real, &AV_imag); + sc_cplx_cplx_mult(VECTOR(*values_real)[i], VECTOR(*values_imag)[i], + &v_real, &v_imag, &lv_real, &lv_imag); + + if (igraph_vector_maxdifference(&AV_real, &lv_real) > tol || + igraph_vector_maxdifference(&AV_imag, &lv_imag) > tol) { + printf("ERROR:\n"); + igraph_vector_print(&AV_real); + igraph_vector_print(&AV_imag); + igraph_vector_print(&lv_real); + igraph_vector_print(&lv_imag); + return 1; + } + } + + igraph_vector_destroy(&null); + igraph_vector_destroy(&AV_imag); + igraph_vector_destroy(&AV_real); + igraph_vector_destroy(&lv_imag); + igraph_vector_destroy(&lv_real); + + return 0; +} + +int main(void) { + + igraph_matrix_t A; + igraph_matrix_t vectors_left, vectors_right; + igraph_vector_t values_real, values_imag; + int i, j; + int info = 1; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&vectors_left, 0, 0); + igraph_matrix_init(&vectors_right, 0, 0); + igraph_vector_init(&values_real, 0); + igraph_vector_init(&values_imag, 0); + + for (i = 0; i < DIM; i++) { + for (j = 0; j < DIM; j++) { + MATRIX(A, i, j) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + igraph_lapack_dgeev(&A, &values_real, &values_imag, + &vectors_left, &vectors_right, &info); + + if (check_ev(&A, &values_real, &values_imag, + &vectors_left, &vectors_right, /*tol=*/ 1e-8)) { + return 1; + } + + igraph_matrix_resize(&A, 10, 10); + igraph_matrix_null(&A); + MATRIX(A, 0, 1) = MATRIX(A, 0, 2) = MATRIX(A, 0, 3) = 1 / 3.0; + MATRIX(A, 1, 0) = MATRIX(A, 1, 4) = MATRIX(A, 1, 5) = MATRIX(A, 1, 6) = 1 / 4.0; + MATRIX(A, 2, 0) = MATRIX(A, 2, 7) = MATRIX(A, 2, 8) = MATRIX(A, 2, 9) = 1 / 4.0; + MATRIX(A, 3, 0) = 1.0; + MATRIX(A, 4, 1) = 1.0; + MATRIX(A, 5, 1) = 1.0; + MATRIX(A, 6, 1) = 1.0; + MATRIX(A, 7, 2) = 1.0; + MATRIX(A, 8, 2) = 1.0; + MATRIX(A, 9, 2) = 1.0; + + info = 0; + igraph_lapack_dgeev(&A, &values_real, &values_imag, + &vectors_left, &vectors_right, &info); + + if (check_ev(&A, &values_real, &values_imag, + &vectors_left, &vectors_right, /*tol=*/ 1e-8)) { + return 3; + } + + igraph_vector_destroy(&values_imag); + igraph_vector_destroy(&values_real); + igraph_matrix_destroy(&vectors_right); + igraph_matrix_destroy(&vectors_left); + igraph_matrix_destroy(&A); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_lapack_dgeevx.c b/tests/unit/igraph_lapack_dgeevx.c new file mode 100644 index 0000000..e976e0b --- /dev/null +++ b/tests/unit/igraph_lapack_dgeevx.c @@ -0,0 +1,206 @@ +/* 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, see . +*/ + +#include +#include +#include "test_utilities.h" + +#define DIM 10 + +int real_cplx_mult(const igraph_matrix_t *A, + const igraph_vector_t *v_real, + const igraph_vector_t *v_imag, + igraph_vector_t *res_real, + igraph_vector_t *res_imag) { + + igraph_integer_t n = igraph_vector_size(v_real); + igraph_integer_t r, c; + + if (igraph_matrix_nrow(A) != n || + igraph_matrix_ncol(A) != n || + igraph_vector_size(v_imag) != n) { + printf("Wrong matrix or vector size"); + return 1; + } + + igraph_vector_resize(res_real, n); + igraph_vector_resize(res_imag, n); + + for (r = 0; r < n; r++) { + igraph_real_t s_real = 0.0; + igraph_real_t s_imag = 0.0; + for (c = 0; c < n; c++) { + s_real += MATRIX(*A, r, c) * VECTOR(*v_real)[c]; + s_imag += MATRIX(*A, r, c) * VECTOR(*v_imag)[c]; + } + VECTOR(*res_real)[r] = s_real; + VECTOR(*res_imag)[r] = s_imag; + } + + return 0; +} + +int sc_cplx_cplx_mult(igraph_real_t lambda_real, + igraph_real_t lambda_imag, + const igraph_vector_t *v_real, + const igraph_vector_t *v_imag, + igraph_vector_t *res_real, + igraph_vector_t *res_imag) { + + igraph_integer_t r; + igraph_integer_t n = igraph_vector_size(v_real); + + if (igraph_vector_size(v_imag) != n) { + printf("Wrong vector sizes"); + return 1; + } + + igraph_vector_resize(res_real, n); + igraph_vector_resize(res_imag, n); + + for (r = 0; r < n; r++) { + VECTOR(*res_real)[r] = (lambda_real * VECTOR(*v_real)[r] - + lambda_imag * VECTOR(*v_imag)[r]); + VECTOR(*res_imag)[r] = (lambda_imag * VECTOR(*v_real)[r] + + lambda_real * VECTOR(*v_imag)[r]); + } + + return 0; +} + +igraph_bool_t check_ev(const igraph_matrix_t *A, + const igraph_vector_t *values_real, + const igraph_vector_t *values_imag, + const igraph_matrix_t *vectors_left, + const igraph_matrix_t *vectors_right, + igraph_real_t tol) { + + igraph_integer_t n = igraph_matrix_nrow(A); + igraph_vector_t v_real, v_imag; + igraph_vector_t AV_real, AV_imag, lv_real, lv_imag; + igraph_vector_t null; + igraph_integer_t i; + + if (igraph_matrix_ncol(A) != n) { + return 1; + } + if (igraph_vector_size(values_real) != n) { + return 1; + } + if (igraph_vector_size(values_imag) != n) { + return 1; + } + if (igraph_matrix_nrow(vectors_left) != n) { + return 1; + } + if (igraph_matrix_ncol(vectors_left) != n) { + return 1; + } + if (igraph_matrix_nrow(vectors_right) != n) { + return 1; + } + if (igraph_matrix_ncol(vectors_right) != n) { + return 1; + } + + igraph_vector_init(&AV_real, n); + igraph_vector_init(&AV_imag, n); + igraph_vector_init(&lv_real, n); + igraph_vector_init(&lv_imag, n); + igraph_vector_init(&null, n); + igraph_vector_null(&null); + + for (i = 0; i < n; i++) { + if (VECTOR(*values_imag)[i] == 0.0) { + igraph_vector_view(&v_real, &MATRIX(*vectors_right, 0, i), n); + igraph_vector_view(&v_imag, VECTOR(null), n); + } else if (VECTOR(*values_imag)[i] > 0.0) { + igraph_vector_view(&v_real, &MATRIX(*vectors_right, 0, i), n); + igraph_vector_view(&v_imag, &MATRIX(*vectors_right, 0, i + 1), n); + } else if (VECTOR(*values_imag)[i] < 0.0) { + igraph_vector_view(&v_real, &MATRIX(*vectors_right, 0, i - 1), n); + igraph_vector_view(&v_imag, &MATRIX(*vectors_right, 0, i), n); + igraph_vector_scale(&v_imag, -1.0); + } + real_cplx_mult(A, &v_real, &v_imag, &AV_real, &AV_imag); + sc_cplx_cplx_mult(VECTOR(*values_real)[i], VECTOR(*values_imag)[i], + &v_real, &v_imag, &lv_real, &lv_imag); + + if (igraph_vector_maxdifference(&AV_real, &lv_real) > tol || + igraph_vector_maxdifference(&AV_imag, &lv_imag) > tol) { + igraph_vector_print(&AV_real); + igraph_vector_print(&AV_imag); + igraph_vector_print(&lv_real); + igraph_vector_print(&lv_imag); + return 1; + } + } + + igraph_vector_destroy(&null); + igraph_vector_destroy(&AV_imag); + igraph_vector_destroy(&AV_real); + igraph_vector_destroy(&lv_imag); + igraph_vector_destroy(&lv_real); + + return 0; +} + +int main(void) { + + igraph_matrix_t A; + igraph_matrix_t vectors_left, vectors_right; + igraph_vector_t values_real, values_imag; + igraph_integer_t i, j; + int info = 1; + int ilo, ihi; + igraph_real_t abnrm; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&vectors_left, 0, 0); + igraph_matrix_init(&vectors_right, 0, 0); + igraph_vector_init(&values_real, 0); + igraph_vector_init(&values_imag, 0); + + for (i = 0; i < DIM; i++) { + for (j = 0; j < DIM; j++) { + MATRIX(A, i, j) = igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + igraph_lapack_dgeevx(IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH, + &A, &values_real, &values_imag, + &vectors_left, &vectors_right, &ilo, &ihi, + /*scale=*/ 0, &abnrm, /*rconde=*/ 0, + /*rcondv=*/ 0, &info); + + if (check_ev(&A, &values_real, &values_imag, + &vectors_left, &vectors_right, /*tol=*/ 1e-8)) { + return 1; + } + + igraph_vector_destroy(&values_imag); + igraph_vector_destroy(&values_real); + igraph_matrix_destroy(&vectors_right); + igraph_matrix_destroy(&vectors_left); + igraph_matrix_destroy(&A); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_lapack_dgehrd.c b/tests/unit/igraph_lapack_dgehrd.c new file mode 100644 index 0000000..5355615 --- /dev/null +++ b/tests/unit/igraph_lapack_dgehrd.c @@ -0,0 +1,81 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + int nodes = 10; + igraph_t tree; + igraph_matrix_t sto; + igraph_matrix_t hess; + igraph_matrix_complex_t evec1, evec2; + igraph_vector_complex_t eval1, eval2; + igraph_eigen_which_t which; + int i; + + igraph_kary_tree(&tree, nodes, /* children= */ 3, IGRAPH_TREE_UNDIRECTED); + + igraph_matrix_init(&sto, nodes, nodes); + igraph_get_stochastic(&tree, &sto, /*column_wise=*/ 0, /* weights = */ NULL); + igraph_matrix_transpose(&sto); + + igraph_matrix_init(&hess, nodes, nodes); + igraph_lapack_dgehrd(&sto, 1, nodes, &hess); + + igraph_matrix_complex_init(&evec1, 0, 0); + igraph_vector_complex_init(&eval1, 0); + which.pos = IGRAPH_EIGEN_ALL; + igraph_eigen_matrix(&sto, 0, 0, nodes, 0, IGRAPH_EIGEN_LAPACK, &which, 0, 0, + &eval1, &evec1); + + igraph_matrix_complex_init(&evec2, 0, 0); + igraph_vector_complex_init(&eval2, 0); + igraph_eigen_matrix(&hess, 0, 0, nodes, 0, IGRAPH_EIGEN_LAPACK, &which, 0, + 0, &eval2, &evec2); + + for (i = 0; i < nodes; i++) { + igraph_real_t d = igraph_complex_abs(igraph_complex_sub(VECTOR(eval1)[i], + VECTOR(eval2)[i])); + if (d > 1e-14) { + printf("Difference: %g\n", d); + return 1; + } + } + + igraph_matrix_complex_destroy(&evec2); + igraph_vector_complex_destroy(&eval2); + + igraph_matrix_complex_destroy(&evec1); + igraph_vector_complex_destroy(&eval1); + + igraph_matrix_destroy(&hess); + igraph_matrix_destroy(&sto); + igraph_destroy(&tree); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_lapack_dgehrd.out b/tests/unit/igraph_lapack_dgehrd.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/igraph_lapack_dgetrf.c b/tests/unit/igraph_lapack_dgetrf.c new file mode 100644 index 0000000..2a742f3 --- /dev/null +++ b/tests/unit/igraph_lapack_dgetrf.c @@ -0,0 +1,74 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void check_and_destroy(igraph_matrix_t *matrix, igraph_bool_t pivot) { + igraph_vector_int_t pivot_indices; + int info; + igraph_vector_int_init(&pivot_indices, 0); + printf("Starting matrix:\n"); + igraph_matrix_print(matrix); + if (pivot) { + IGRAPH_ASSERT(igraph_lapack_dgetrf(matrix, &pivot_indices, &info) == IGRAPH_SUCCESS); + } else { + IGRAPH_ASSERT(igraph_lapack_dgetrf(matrix, NULL, &info) == IGRAPH_SUCCESS); + } + printf("Returned matrix:\n"); + igraph_matrix_print(matrix); + if (pivot) { + printf("Returned pivot indices:\n"); + igraph_vector_int_print(&pivot_indices); + } + printf("info: %d\n", info); + igraph_vector_int_destroy(&pivot_indices); + igraph_matrix_destroy(matrix); + printf("\n"); +} + +int main(void) { + igraph_matrix_t matrix; + + printf("Empty matrix:\n"); + igraph_matrix_init(&matrix, 0, 0); + check_and_destroy(&matrix, 1); + + int elements_1[9] = {7, 8, 9, 2, 2, 3, 1, 1, 1}; + matrix_init_int_row_major(&matrix, 3, 3, elements_1); + check_and_destroy(&matrix, 1); + + int elements_2[9] = {1, 1, 1, 2, 2, 3, 7, 8, 9}; + matrix_init_int_row_major(&matrix, 3, 3, elements_2); + check_and_destroy(&matrix, 1); + + int elements_3[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + matrix_init_int_row_major(&matrix, 3, 3, elements_3); + check_and_destroy(&matrix, 0); + + int elements_4[6] = {1, 2, 3, 4, 5, 6}; + matrix_init_int_row_major(&matrix, 2, 3, elements_4); + check_and_destroy(&matrix, 1); + + int elements_5[6] = {1, 2, 3, 4, 5, 6}; + matrix_init_int_row_major(&matrix, 3, 2, elements_5); + check_and_destroy(&matrix, 1); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_lapack_dgetrf.out b/tests/unit/igraph_lapack_dgetrf.out new file mode 100644 index 0000000..e8f319c --- /dev/null +++ b/tests/unit/igraph_lapack_dgetrf.out @@ -0,0 +1,63 @@ +Empty matrix: +Starting matrix: +Returned matrix: +Returned pivot indices: + +info: 0 + +Starting matrix: +7 8 9 +2 2 3 +1 1 1 +Returned matrix: + 7 8 9 +0.285714 -0.285714 0.428571 +0.142857 0.5 -0.5 +Returned pivot indices: +1 2 3 +info: 0 + +Starting matrix: +1 1 1 +2 2 3 +7 8 9 +Returned matrix: + 7 8 9 +0.285714 -0.285714 0.428571 +0.142857 0.5 -0.5 +Returned pivot indices: +3 2 3 +info: 0 + +Starting matrix: +0 1 2 +3 4 5 +6 7 8 +Returned matrix: + 6 7 8 + 0 1 2 +0.5 0.5 0 +info: 3 + +Starting matrix: +1 2 3 +4 5 6 +Returned matrix: + 4 5 6 +0.25 0.75 1.5 +Returned pivot indices: +2 2 +info: 0 + +Starting matrix: +1 2 +3 4 +5 6 +Returned matrix: + 5 6 +0.2 0.8 +0.6 0.5 +Returned pivot indices: +3 3 +info: 0 + diff --git a/tests/unit/igraph_lapack_dgetrs.c b/tests/unit/igraph_lapack_dgetrs.c new file mode 100644 index 0000000..5bc678b --- /dev/null +++ b/tests/unit/igraph_lapack_dgetrs.c @@ -0,0 +1,122 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void check_and_destroy(igraph_matrix_t *a, igraph_matrix_t *b, igraph_vector_int_t *ipiv, + igraph_bool_t transpose, igraph_error_t error) { + printf("LU matrix:\n"); + igraph_matrix_print(a); + printf("Pivot vector:\n"); + igraph_vector_int_print(ipiv); + printf("B matrix:\n"); + igraph_matrix_print(b); + IGRAPH_ASSERT(igraph_lapack_dgetrs(transpose, a, ipiv, b) == error); + if (error == IGRAPH_SUCCESS) { + printf("Returned matrix:\n"); + igraph_matrix_print(b); + } + igraph_vector_int_destroy(ipiv); + igraph_matrix_destroy(a); + igraph_matrix_destroy(b); + printf("\n"); +} + +int main(void) { + igraph_matrix_t a, b; + igraph_vector_int_t ipiv; + + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Checking empty matrices:\n"); + igraph_matrix_init(&a, 0, 0); + igraph_matrix_init(&b, 0, 0); + igraph_vector_int_init_int(&ipiv, 0); + check_and_destroy(&a, &b, &ipiv, 0, IGRAPH_SUCCESS); + + { + printf("Checking 3x3 matrix:\n"); + double a_elements[] = {7, 8, 9, 2./7., -2./7., 3./7., 1./7., 1./2., -1./2.}; + int b_elements[] = {1, 1, 1}; + matrix_init_real_row_major(&a, 3, 3, a_elements); + matrix_init_int_row_major(&b, 3, 1, b_elements); + igraph_vector_int_init_int(&ipiv, 3, 1, 2, 3); + check_and_destroy(&a, &b, &ipiv, 0, IGRAPH_SUCCESS); + } + { + printf("Checking transpose and pivot:\n"); + double a_elements[] = {9, 3, 1, 8./9., -2./3., 1./9., 7./9., 1./2., 1./6.}; + int b_elements[] = {1, 1, 1}; + matrix_init_real_row_major(&a, 3, 3, a_elements); + matrix_init_int_row_major(&b, 3, 1, b_elements); + igraph_vector_int_init_int(&ipiv, 3, 3, 2, 3); + check_and_destroy(&a, &b, &ipiv, 1, IGRAPH_SUCCESS); + } + { + printf("Checking 2x3 matrix, expected to fail:\n"); + double a_elements[] = {4, 5, 6, 1./4., 3./4., 3./2.}; + int b_elements[] = {1, 1}; + matrix_init_real_row_major(&a, 2, 3, a_elements); + matrix_init_int_row_major(&b, 2, 1, b_elements); + igraph_vector_int_init_int(&ipiv, 2, 2, 2); + check_and_destroy(&a, &b, &ipiv, 0, IGRAPH_NONSQUARE); + } + { + printf("Checking singular matrix, this gives random output, so we just check for memory problems.\n\n"); + double a_elements[] = {6, 8, 7, 0, 1, 2, 0.5, 0.5, 0}; + int b_elements[] = {1, 1, 1}; + matrix_init_real_row_major(&a, 3, 3, a_elements); + matrix_init_int_row_major(&b, 3, 1, b_elements); + igraph_vector_int_init_int(&ipiv, 3, 1, 2, 3); + IGRAPH_ASSERT(igraph_lapack_dgetrs(0, &a, &ipiv, &b) == IGRAPH_SUCCESS); + igraph_vector_int_destroy(&ipiv); + igraph_matrix_destroy(&a); + igraph_matrix_destroy(&b); + } + { + printf("Checking wrong size of B matrix, should fail:\n"); + double a_elements[] = {7, 8, 9, 2./7., -2./7., 3./7., 1./7., 1./2., -1./2.}; + int b_elements[] = {1, 1}; + matrix_init_real_row_major(&a, 3, 3, a_elements); + matrix_init_int_row_major(&b, 2, 1, b_elements); + igraph_vector_int_init_int(&ipiv, 3, 1, 2, 3); + check_and_destroy(&a, &b, &ipiv, 0, IGRAPH_EINVAL); + } + { + printf("Checking nonexisting pivots, should fail:\n"); + double a_elements[] = {7, 8, 9, 2./7., -2./7., 3./7., 1./7., 1./2., -1./2.}; + int b_elements[] = {1, 1, 1}; + matrix_init_real_row_major(&a, 3, 3, a_elements); + matrix_init_int_row_major(&b, 3, 1, b_elements); + igraph_vector_int_init_int(&ipiv, 3, 5, 6, 7); + check_and_destroy(&a, &b, &ipiv, 0, IGRAPH_EINVAL); + } + { + printf("Checking too few pivots, should fail:\n"); + double a_elements[] = {7, 8, 9, 2./7., -2./7., 3./7., 1./7., 1./2., -1./2.}; + int b_elements[] = {1, 1, 1}; + matrix_init_real_row_major(&a, 3, 3, a_elements); + matrix_init_int_row_major(&b, 3, 1, b_elements); + igraph_vector_int_init_int(&ipiv, 0); + check_and_destroy(&a, &b, &ipiv, 0, IGRAPH_EINVAL); + } + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_lapack_dgetrs.out b/tests/unit/igraph_lapack_dgetrs.out new file mode 100644 index 0000000..04f19a6 --- /dev/null +++ b/tests/unit/igraph_lapack_dgetrs.out @@ -0,0 +1,86 @@ +Checking empty matrices: +LU matrix: +Pivot vector: + +B matrix: +Returned matrix: + +Checking 3x3 matrix: +LU matrix: + 7 8 9 +0.285714 -0.285714 0.428571 +0.142857 0.5 -0.5 +Pivot vector: +1 2 3 +B matrix: +1 +1 +1 +Returned matrix: + 6 +-4 +-1 + +Checking transpose and pivot: +LU matrix: + 9 3 1 +0.888889 -0.666667 0.111111 +0.777778 0.5 0.166667 +Pivot vector: +3 2 3 +B matrix: +1 +1 +1 +Returned matrix: + 6 +-4 +-1 + +Checking 2x3 matrix, expected to fail: +LU matrix: + 4 5 6 +0.25 0.75 1.5 +Pivot vector: +2 2 +B matrix: +1 +1 + +Checking singular matrix, this gives random output, so we just check for memory problems. + +Checking wrong size of B matrix, should fail: +LU matrix: + 7 8 9 +0.285714 -0.285714 0.428571 +0.142857 0.5 -0.5 +Pivot vector: +1 2 3 +B matrix: +1 +1 + +Checking nonexisting pivots, should fail: +LU matrix: + 7 8 9 +0.285714 -0.285714 0.428571 +0.142857 0.5 -0.5 +Pivot vector: +5 6 7 +B matrix: +1 +1 +1 + +Checking too few pivots, should fail: +LU matrix: + 7 8 9 +0.285714 -0.285714 0.428571 +0.142857 0.5 -0.5 +Pivot vector: + +B matrix: +1 +1 +1 + diff --git a/tests/unit/igraph_lapack_dsyevr.c b/tests/unit/igraph_lapack_dsyevr.c new file mode 100644 index 0000000..4a8d2df --- /dev/null +++ b/tests/unit/igraph_lapack_dsyevr.c @@ -0,0 +1,216 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +#define DIM 10 + +igraph_bool_t check_ev(const igraph_matrix_t *A, + const igraph_vector_t *values, + const igraph_matrix_t *vectors, igraph_real_t tol) { + igraph_vector_t v, y; + int i, j; + igraph_integer_t m = igraph_matrix_ncol(vectors); + igraph_integer_t n = igraph_matrix_nrow(A); + + if (igraph_matrix_ncol(A) != n) { + return 1; + } + if (igraph_vector_size(values) != m) { + return 1; + } + if (igraph_matrix_nrow(vectors) != n) { + return 1; + } + + igraph_vector_init(&y, n); + + for (i = 0; i < m; i++) { + igraph_vector_view(&v, &MATRIX(*vectors, 0, i), n); + igraph_vector_update(&y, &v); + igraph_blas_dgemv(/*transpose=*/ 0, /*alpha=*/ 1.0, A, &v, + /*beta=*/ -VECTOR(*values)[i], &y); + for (j = 0; j < n; j++) { + if (fabs(VECTOR(y)[i]) > tol) { + printf("Matrix:\n"); + igraph_matrix_print(A); + printf("lambda= %g\n", VECTOR(*values)[i]); + printf("v= "); + igraph_vector_print(&v); + printf("residual: "); + igraph_vector_print(&y); + return 1; + } + } + } + + igraph_vector_destroy(&y); + return 0; +} + +int main(void) { + + igraph_matrix_t A; + igraph_matrix_t vectors, vectors2; + igraph_vector_t values, values2; + int i, j; + int il, iu; + igraph_real_t vl, vu; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_matrix_init(&A, DIM, DIM); + igraph_matrix_init(&vectors, 0, 0); + igraph_vector_init(&values, 0); + + /* All eigenvalues and eigenvectors */ + + for (i = 0; i < DIM; i++) { + for (j = i; j < DIM; j++) { + MATRIX(A, i, j) = MATRIX(A, j, i) = + igraph_rng_get_integer(igraph_rng_default(), 1, 10); + } + } + + igraph_lapack_dsyevr(&A, IGRAPH_LAPACK_DSYEV_ALL, /*vl=*/ 0, /*vu=*/ 0, + /*vestimate=*/ 0, /*il=*/ 0, /*iu=*/ 0, + /*abstol=*/ 1e-10, &values, &vectors, /*support=*/ 0); + + if (igraph_vector_size(&values) != DIM) { + return 1; + } + if (igraph_matrix_nrow(&vectors) != DIM || + igraph_matrix_ncol(&vectors) != DIM) { + return 2; + } + if (check_ev(&A, &values, &vectors, /*tol=*/ 1e-8)) { + return 3; + } + + /* Only a subset */ + + igraph_matrix_init(&vectors2, 0, 0); + igraph_vector_init(&values2, 0); + + il = 2; + iu = 5; + igraph_lapack_dsyevr(&A, IGRAPH_LAPACK_DSYEV_SELECT, /*vl=*/ 0, /*vu=*/ 0, + /*vestimate=*/ 0, /*il=*/ il, /*iu=*/ iu, + /*abstol=*/ 1e-10, &values2, &vectors2, + /*support=*/ 0); + + if (igraph_vector_size(&values2) != iu - il + 1) { + return 4; + } + if (igraph_matrix_nrow(&vectors2) != DIM || + igraph_matrix_ncol(&vectors2) != iu - il + 1) { + return 5; + } + for (i = 0; i < iu - il + 1; i++) { + igraph_real_t m1 = 1.0; + + if (fabs(VECTOR(values)[il + i - 1] - VECTOR(values2)[i]) > 1e-8) { + printf("Full: "); + igraph_vector_print(&values); + printf("Subset: "); + igraph_vector_print(&values2); + return 6; + } + + if (MATRIX(vectors, 0, il + i - 1) * MATRIX(vectors2, 0, i) < 0) { + m1 = -1.0; + } else { + m1 = 1.0; + } + + for (j = 0; j < DIM; j++) { + if (fabs(MATRIX(vectors, j, il + i - 1) - + m1 * MATRIX(vectors2, j, i)) > 1e-8) { + printf("Full:\n"); + igraph_matrix_print(&vectors); + printf("Subset:\n"); + igraph_matrix_print(&vectors2); + return 7; + } + } + } + + igraph_vector_destroy(&values2); + igraph_matrix_destroy(&vectors2); + + /* Subset based on an interval */ + + igraph_matrix_init(&vectors2, 0, 0); + igraph_vector_init(&values2, 0); + + il = 2; + iu = 5; + vl = (VECTOR(values)[il - 1] + VECTOR(values)[il - 2]) / 2.0; + vu = (VECTOR(values)[iu] + VECTOR(values)[iu - 1]) / 2.0; + + igraph_lapack_dsyevr(&A, IGRAPH_LAPACK_DSYEV_INTERVAL, vl, vu, + /*vestimate=*/ iu - il + 1, /*il=*/ 0, /*iu=*/ 0, + /*abstol=*/ 1e-10, &values2, &vectors2, + /*support=*/ 0); + + if (igraph_vector_size(&values2) != iu - il + 1) { + return 4; + } + if (igraph_matrix_nrow(&vectors2) != DIM || + igraph_matrix_ncol(&vectors2) != iu - il + 1) { + return 5; + } + for (i = 0; i < iu - il + 1; i++) { + igraph_real_t m1 = 1.0; + + if (fabs(VECTOR(values)[il + i - 1] - VECTOR(values2)[i]) > 1e-8) { + printf("Full: "); + igraph_vector_print(&values); + printf("Subset: "); + igraph_vector_print(&values2); + return 6; + } + + if (MATRIX(vectors, 0, il + i - 1) * MATRIX(vectors2, 0, i) < 0) { + m1 = -1.0; + } else { + m1 = 1.0; + } + + for (j = 0; j < DIM; j++) { + if (fabs(MATRIX(vectors, j, il + i - 1) - + m1 * MATRIX(vectors2, j, i)) > 1e-8) { + printf("Full:\n"); + igraph_matrix_print(&vectors); + printf("Subset:\n"); + igraph_matrix_print(&vectors2); + return 7; + } + } + } + + igraph_vector_destroy(&values2); + igraph_matrix_destroy(&vectors2); + + igraph_vector_destroy(&values); + igraph_matrix_destroy(&vectors); + igraph_matrix_destroy(&A); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_lastcit_game.c b/tests/unit/igraph_lastcit_game.c new file mode 100644 index 0000000..861f759 --- /dev/null +++ b/tests/unit/igraph_lastcit_game.c @@ -0,0 +1,86 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_t preference; + + igraph_rng_seed(igraph_rng_default(), 42); + + /*No nodes*/ + igraph_vector_init_int_end(&preference, -1, 1, 1, -1); + IGRAPH_ASSERT(igraph_lastcit_game(&g, /*nodes*/ 0, /*edges_per_node*/ 5, /*agebins*/ 1, /*preference*/ &preference, /*directed*/ 0) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 0); + igraph_destroy(&g); + igraph_vector_destroy(&preference); + + /*No edges*/ + igraph_vector_init_int_end(&preference, -1, 1, 1, -1); + IGRAPH_ASSERT(igraph_lastcit_game(&g, /*nodes*/ 9, /*edges_per_node*/ 0, /*agebins*/ 1, /*preference*/ &preference, /*directed*/ 0) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 9); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + igraph_destroy(&g); + igraph_vector_destroy(&preference); + + /*Only cite un-cited to make a line*/ + igraph_vector_init_int_end(&preference, -1, 0, 1, -1); + IGRAPH_ASSERT(igraph_lastcit_game(&g, /*nodes*/ 9, /*edges_per_node*/ 1, /*agebins*/ 1, /*preference*/ &preference, /*directed*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + igraph_vector_destroy(&preference); + + /*Hugely prefer cited to make a star*/ + igraph_vector_init_real(&preference, 2, 1e30, 1e-30); + IGRAPH_ASSERT(igraph_lastcit_game(&g, /*nodes*/ 9, /*edges_per_node*/ 1, /*agebins*/ 1, /*preference*/ &preference, /*directed*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + igraph_vector_destroy(&preference); + + VERIFY_FINALLY_STACK(); + + /*Negative number of nodes*/ + igraph_vector_init_int_end(&preference, -1, 1, 1, -1); + CHECK_ERROR(igraph_lastcit_game(&g, /*nodes*/ -9, /*edges_per_node*/ 1, /*agebins*/ 1, /*preference*/ &preference, /*directed*/ 0), IGRAPH_EINVAL); + igraph_vector_destroy(&preference); + + /*Too few agebins*/ + igraph_vector_init_int_end(&preference, -1, 1, -1); + CHECK_ERROR(igraph_lastcit_game(&g, /*nodes*/ 9, /*edges_per_node*/ 1, /*agebins*/ 0, /*preference*/ &preference, /*directed*/ 0), IGRAPH_EINVAL); + igraph_vector_destroy(&preference); + + /*Wrong vector size*/ + igraph_vector_init_int_end(&preference, -1, 1, -1); + CHECK_ERROR(igraph_lastcit_game(&g, /*nodes*/ 9, /*edges_per_node*/ 1, /*agebins*/ 1, /*preference*/ &preference, /*directed*/ 0), IGRAPH_EINVAL); + igraph_vector_destroy(&preference); + + /*No uncited preference*/ + igraph_vector_init_int_end(&preference, -1, 1, 0, -1); + CHECK_ERROR(igraph_lastcit_game(&g, /*nodes*/ 9, /*edges_per_node*/ 1, /*agebins*/ 1, /*preference*/ &preference, /*directed*/ 0), IGRAPH_EINVAL); + igraph_vector_destroy(&preference); + + /*Negative preference*/ + igraph_vector_init_int_end(&preference, -1, -2, 1, -1); + CHECK_ERROR(igraph_lastcit_game(&g, /*nodes*/ 9, /*edges_per_node*/ 1, /*agebins*/ 1, /*preference*/ &preference, /*directed*/ 0), IGRAPH_EINVAL); + igraph_vector_destroy(&preference); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_lastcit_game.out b/tests/unit/igraph_lastcit_game.out new file mode 100644 index 0000000..d0617e2 --- /dev/null +++ b/tests/unit/igraph_lastcit_game.out @@ -0,0 +1,24 @@ +directed: false +vcount: 9 +edges: { +0 1 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +} +directed: true +vcount: 9 +edges: { +1 0 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 0 +} diff --git a/tests/unit/igraph_layout_bipartite.c b/tests/unit/igraph_layout_bipartite.c new file mode 100644 index 0000000..f2b13da --- /dev/null +++ b/tests/unit/igraph_layout_bipartite.c @@ -0,0 +1,110 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_matrix_t result; + igraph_vector_bool_t types; + + printf("No vertices:\n"); + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_bool_init(&types, 0); + IGRAPH_ASSERT(igraph_layout_bipartite(&g, &types, &result, /*hgap*/ 1.0, /*vgap*/ 1.0, /*maxiter*/ 100) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_vector_bool_destroy(&types); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("1 vertex:\n"); + igraph_small(&g, 1, 0, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_bool_init_int(&types, 1, 0); + IGRAPH_ASSERT(igraph_layout_bipartite(&g, &types, &result, /*hgap*/ 1.0, /*vgap*/ 1.0, /*maxiter*/ 100) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_vector_bool_destroy(&types); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("4 vertices, disconnected, not actually bipartite, with loops and multiple edges:\n"); + igraph_small(&g, 4, 0, 0,0, 0,0, 0,0, 1,2, 1,2, 1,3, 1,3, 2,3, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_bool_init_int(&types, 4, 0, 1, 0, 1); + IGRAPH_ASSERT(igraph_layout_bipartite(&g, &types, &result, /*hgap*/ 1.0, /*vgap*/ 1.0, /*maxiter*/ 100) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_vector_bool_destroy(&types); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("10 vertices bipartite graph:\n"); + igraph_small(&g, 10, 0, 0,5, 0,7, 1,6, 1,7, 1,8, 2,5, 3,8, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_bool_init_int(&types, 10, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1); + IGRAPH_ASSERT(igraph_layout_bipartite(&g, &types, &result, /*hgap*/ 1.0, /*vgap*/ 1.0, /*maxiter*/100) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_vector_bool_destroy(&types); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("10 vertices bipartite graph, no iterations:\n"); + igraph_small(&g, 10, 0, 0,5, 0,7, 1,6, 1,7, 1,8, 2,5, 3,8, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_bool_init_int(&types, 10, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1); + IGRAPH_ASSERT(igraph_layout_bipartite(&g, &types, &result, /*hgap*/ 1.0, /*vgap*/ 1.0, /*maxiter*/0) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_vector_bool_destroy(&types); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("4 vertices with -10 true values for types:\n"); + igraph_small(&g, 4, 0, 0,1, 1,2, 2,3, 3,0, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_bool_init_int(&types, 4, 0, -10, 0, -10); + IGRAPH_ASSERT(igraph_layout_bipartite(&g, &types, &result, /*hgap*/ 1.0, /*vgap*/ 1.0, /*maxiter*/ 100) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_vector_bool_destroy(&types); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("4 vertices, negative vgaps:\n"); + igraph_small(&g, 4, 0, 0,1, 1,2, 2,3, 3,0, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_bool_init_int(&types, 4, 0, 1, 0, 1); + IGRAPH_ASSERT(igraph_layout_bipartite(&g, &types, &result, /*hgap*/ 1.0, /*vgap*/ -1.0, /*maxiter*/ 100) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_vector_bool_destroy(&types); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + printf("4 vertices, negative hgaps, emits error.\n"); + igraph_small(&g, 4, 0, 0,1, 1,2, 2,3, 3,0, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_bool_init_int(&types, 4, 0, 1, 0, 1); + CHECK_ERROR(igraph_layout_bipartite(&g, &types, &result, /*hgap*/ -1.0, /*vgap*/ 1.0, /*maxiter*/ 100), IGRAPH_EINVAL); + igraph_vector_bool_destroy(&types); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_bipartite.out b/tests/unit/igraph_layout_bipartite.out new file mode 100644 index 0000000..26642a9 --- /dev/null +++ b/tests/unit/igraph_layout_bipartite.out @@ -0,0 +1,42 @@ +No vertices: +[ 0-by-2 ] +1 vertex: +[ 0 1 ] +4 vertices, disconnected, not actually bipartite, with loops and multiple edges: +[ 0 1 + 1 0 + 1 1 + 2 0 ] +10 vertices bipartite graph: +[ 1 1 + 2 1 + 0 1 + 3 1 + 4 1 + 0 0 + 2 0 + 1 0 + 3 0 + 5 0 ] +10 vertices bipartite graph, no iterations: +[ -0.5 1 + 1 1 + 2 1 + 3 1 + 4 1 + 0 0 + 1 0 + 2 0 + 3 0 + 5 0 ] +4 vertices with -10 true values for types: +[ 0 1 + 0 0 + 1 1 + 1 0 ] +4 vertices, negative vgaps: +[ 0 -1 + 0 0 + 1 -1 + 1 0 ] +4 vertices, negative hgaps, emits error. diff --git a/tests/unit/igraph_layout_davidson_harel.c b/tests/unit/igraph_layout_davidson_harel.c new file mode 100644 index 0000000..13ae94b --- /dev/null +++ b/tests/unit/igraph_layout_davidson_harel.c @@ -0,0 +1,184 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph R package. + 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 +#include + +#include "layout/layout_internal.h" + +#include "test_utilities.h" + +int intersect(void) { + + float negative[][8] = { + { 1, 2, 2, 2, 1, 1, 2, 1 }, /* 1 */ + { 1, 2, 1, 1, 2, 2, 2, 1 }, /* 2 */ + { 1, 0, 0, 1, 2, 0, 3, 1 }, /* 3 */ + { 1, 0, 1, 1, 0, 2, 2, 2 }, /* 4 */ + { 1, 0, 1, 2, 3, 1, 3, 3 }, /* 5 */ + { 0, 0, 0, 2, 1, 1, 1, 2 }, /* 6 */ + { 0, 1, 1, 1, 2, 0, 2, 3 }, /* 7 */ + { 0, 0, 5, 0, 2, 1, 4, 3 }, /* 8 */ + { 0, 0, 5, 5, 3, 2, 3, 2 } /* 9 */ + }; + + float positive[][8] = { + { 0, 1, 2, 1, 1, 0, 1, 2 }, /* 10 */ + { 0, 2, 5, 2, 1, 1, 4, 3 }, /* 11 */ + { 0, 0, 0, 3, 0, 1, 5, 1 }, /* 12 */ + { 0, 4, 2, 6, 0, 4, 2, 2 } /* 13 */ + }; + /* { 1,1,1,1, 1,1,0,0 }, /\* 14 *\/ */ + /* { 0,0,1,1, 1,1,1,1 }, /\* 15 *\/ */ + /* { 0,0,2,2, 1,1,1,1 }}; /\* 16 *\/ */ + + int no_neg = sizeof(negative) / sizeof(float) / 8; + int no_pos = sizeof(positive) / sizeof(float) / 8; + int i; + + for (i = 0; i < no_neg; i++) { + float *co = negative[i]; + if (igraph_i_layout_segments_intersect(co[0], co[1], co[2], co[3], + co[4], co[5], co[6], co[7])) { + return i + 1; + } + } + + for (i = 0; i < no_pos; i++) { + float *co = positive[i]; + if (!igraph_i_layout_segments_intersect(co[0], co[1], co[2], co[3], + co[4], co[5], co[6], co[7])) { + return no_neg + i + 1; + } + } + + return 0; +} + +int distance(void) { + + igraph_real_t configs[][7] = { + { 1, 1, 2, 0, 2, 3, 1.0 }, /* 1 */ + { 1, 1, 1, 0, 1, 3, 0.0 }, /* 2 */ + { 1, 1, 0, 1, 1, 0, 0.5 }, /* 3 */ + { 1, 2, 0, 0, 0, 1, 2.0 }, /* 4 */ + { 1, 0, 0, 1, 0, 2, 2.0 }, /* 5 */ + { 0, 0, 1, 1, 1, 2, 2.0 }, /* 6 */ + { 0, 3, 1, 1, 1, 2, 2.0 } /* 7 */ + }; + + int no = sizeof(configs) / sizeof(igraph_real_t) / 8; + int i; + + for (i = 0; i < no; i++) { + igraph_real_t *co = configs[i]; + igraph_real_t res = igraph_i_layout_point_segment_dist2(co[0], co[1], co[2], co[3], co[4], co[5]); + if (fabs(res - co[6]) > 1e-12) { + printf("%g\n", (double) res); + return i + 1; + } + } + + return 0; +} + +void check_layout_davidson_harel(void) { + igraph_t g; + igraph_matrix_t res; + igraph_bool_t use_seed; + igraph_integer_t maxiter, fineiter; + igraph_real_t cool_fact, weight_node_dist, weight_border, weight_edge_lengths, weight_edge_crossings, weight_node_edge_dist; + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_matrix_init(&res, 0, 0); + + use_seed = 0; + maxiter = 5; + fineiter = 5; + cool_fact = 0.75; + weight_node_dist = 1.0; + weight_border = 0.1; + weight_edge_lengths = 0.5; + weight_edge_crossings = 0.8; + weight_node_edge_dist = 0.2; + + printf("Checking graph with no vertices\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, -1); + igraph_layout_davidson_harel(&g, &res, use_seed, maxiter, fineiter, cool_fact, + weight_node_dist, weight_border, + weight_edge_lengths, + weight_edge_crossings, + weight_node_edge_dist); + + IGRAPH_ASSERT(igraph_matrix_nrow(&res) == 0); + igraph_destroy(&g); + + printf("Checking graph with 10 vertices, no edges\n"); + igraph_small(&g, 10, IGRAPH_DIRECTED, -1); + igraph_layout_davidson_harel(&g, &res, use_seed, maxiter, fineiter, cool_fact, + weight_node_dist, weight_border, + weight_edge_lengths, + weight_edge_crossings, + weight_node_edge_dist); + IGRAPH_ASSERT(igraph_matrix_nrow(&res) == 10); + IGRAPH_ASSERT(igraph_matrix_ncol(&res) == 2); + IGRAPH_ASSERT(igraph_matrix_max(&res) < 20); + IGRAPH_ASSERT(igraph_matrix_min(&res) > -20); + igraph_destroy(&g); + + printf("Checking full graph with 10 vertices\n"); + igraph_full(&g, 10, IGRAPH_DIRECTED, 1); + igraph_layout_davidson_harel(&g, &res, use_seed, maxiter, fineiter, cool_fact, + weight_node_dist, weight_border, + weight_edge_lengths, + weight_edge_crossings, + weight_node_edge_dist); + IGRAPH_ASSERT(igraph_matrix_nrow(&res) == 10); + IGRAPH_ASSERT(igraph_matrix_ncol(&res) == 2); + IGRAPH_ASSERT(igraph_matrix_max(&res) < 20); + IGRAPH_ASSERT(igraph_matrix_min(&res) > -20); + igraph_destroy(&g); + igraph_matrix_destroy(&res); +} + +int main(void) { + int res1, res2; + + res1 = intersect(); + if (res1 != 0) { + printf("Unexpected result from intersect(), config %d.\n", res1); + return res1; + } + res2 = distance() ; + if (res2 != 0) { + printf("Unexpected result from distance(), config %d.\n", res2); + return res2; + } + + check_layout_davidson_harel(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_layout_davidson_harel.out b/tests/unit/igraph_layout_davidson_harel.out new file mode 100644 index 0000000..a918258 --- /dev/null +++ b/tests/unit/igraph_layout_davidson_harel.out @@ -0,0 +1,3 @@ +Checking graph with no vertices +Checking graph with 10 vertices, no edges +Checking full graph with 10 vertices diff --git a/tests/unit/igraph_layout_drl.c b/tests/unit/igraph_layout_drl.c new file mode 100644 index 0000000..cf43023 --- /dev/null +++ b/tests/unit/igraph_layout_drl.c @@ -0,0 +1,99 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void set_options_fast(igraph_layout_drl_options_t *options) { + options->edge_cut = 4.0/5.0; + + options->init_iterations = 10; + options->init_temperature = 2000; + options->init_attraction = 10; + options->init_damping_mult = 1.0; + + options->liquid_iterations = 10; + options->liquid_temperature = 2000; + options->liquid_attraction = 10; + options->liquid_damping_mult = 1.0; + + options->expansion_iterations = 10; + options->expansion_temperature = 2000; + options->expansion_attraction = 2; + options->expansion_damping_mult = 1.0; + + options->cooldown_iterations = 10; + options->cooldown_temperature = 2000; + options->cooldown_attraction = 1; + options->cooldown_damping_mult = .1; + + options->crunch_iterations = 10; + options->crunch_temperature = 250; + options->crunch_attraction = 1; + options->crunch_damping_mult = 0.25; + + options->simmer_iterations = 10; + options->simmer_temperature = 250; + options->simmer_attraction = .5; + options->simmer_damping_mult = 1; +} + +void check_and_destroy(igraph_matrix_t *result, igraph_real_t half_size) { + igraph_real_t min, max; + igraph_matrix_minmax(result, &min, &max); + IGRAPH_ASSERT(min >= -half_size); + IGRAPH_ASSERT(max <= half_size); + igraph_matrix_destroy(result); +} + +int main(void) { + igraph_t g; + igraph_matrix_t result; + igraph_layout_drl_options_t options; + int i; + igraph_real_t *damping_muls[6] = {&options.init_damping_mult, &options.liquid_damping_mult, &options.expansion_damping_mult, &options.cooldown_damping_mult, &options.crunch_damping_mult, &options.simmer_damping_mult}; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_layout_drl_options_init(&options, IGRAPH_LAYOUT_DRL_DEFAULT); + + printf("The Zachary karate club.\n"); + igraph_famous(&g, "zachary"); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_drl(&g, &result, /*use_seed*/ 0, &options, + /*weights*/ NULL) == IGRAPH_SUCCESS); + check_and_destroy(&result, 50); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + set_options_fast(&options); + printf("Negative damping.\n"); + igraph_matrix_init(&result, 0, 0); + for (i = 0; i < 6; i++) { + *damping_muls[i] *= -1.0; + IGRAPH_ASSERT(igraph_layout_drl(&g, &result, /*use_seed*/ 0, &options, + /*weights*/ NULL) == IGRAPH_EINVAL); + *damping_muls[i] *= -1.0; + } + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_drl_3d.c b/tests/unit/igraph_layout_drl_3d.c new file mode 100644 index 0000000..6cecce6 --- /dev/null +++ b/tests/unit/igraph_layout_drl_3d.c @@ -0,0 +1,64 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +void check_and_destroy(igraph_matrix_t *result, igraph_real_t half_size) { + igraph_real_t min, max; + igraph_matrix_minmax(result, &min, &max); + IGRAPH_ASSERT(min >= -half_size); + IGRAPH_ASSERT(max <= half_size); + igraph_matrix_destroy(result); +} + +int main(void) { + igraph_t g; + igraph_matrix_t result; + igraph_layout_drl_options_t options; + int i; + igraph_real_t *damping_muls[6] = {&options.init_damping_mult, &options.liquid_damping_mult, &options.expansion_damping_mult, &options.cooldown_damping_mult, &options.crunch_damping_mult, &options.simmer_damping_mult}; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_layout_drl_options_init(&options, IGRAPH_LAYOUT_DRL_REFINE); + + printf("The Zachary karate club.\n"); + igraph_famous(&g, "zachary"); + igraph_matrix_init(&result, 0, 0); + igraph_layout_drl_3d(&g, &result, /*use_seed*/ 0, &options, + /*weights*/ NULL); + check_and_destroy(&result, 50); + + VERIFY_FINALLY_STACK(); + + igraph_layout_drl_options_init(&options, IGRAPH_LAYOUT_DRL_FINAL); + printf("Negative damping.\n"); + igraph_matrix_init(&result, 0, 0); + for (i = 0; i < 6; i++) { + *damping_muls[i] = -1.0; + CHECK_ERROR(igraph_layout_drl_3d(&g, &result, /*use_seed*/ 0, &options, + /*weights*/ NULL), IGRAPH_EINVAL); + *damping_muls[i] = 1.0; + } + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_drl_3d.out b/tests/unit/igraph_layout_drl_3d.out new file mode 100644 index 0000000..8d40472 --- /dev/null +++ b/tests/unit/igraph_layout_drl_3d.out @@ -0,0 +1,2 @@ +The Zachary karate club. +Negative damping. diff --git a/tests/unit/igraph_layout_fruchterman_reingold.c b/tests/unit/igraph_layout_fruchterman_reingold.c new file mode 100644 index 0000000..c2779a5 --- /dev/null +++ b/tests/unit/igraph_layout_fruchterman_reingold.c @@ -0,0 +1,128 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void make_box(int vertices, float half_size, igraph_vector_t *bounds) { + for (int i = 0; i < 4; i++) { + igraph_vector_init(&bounds[i], vertices); + } + igraph_vector_fill(&bounds[0], -half_size); + igraph_vector_fill(&bounds[1], half_size); + igraph_vector_fill(&bounds[2], -half_size); + igraph_vector_fill(&bounds[3], half_size); +} + +void destroy_bounds(igraph_vector_t *bounds) { + for (int i = 0; i < 4; i++) { + igraph_vector_destroy(&bounds[i]); + } +} + +void check_and_destroy(igraph_matrix_t *result, igraph_real_t half_size) { + igraph_real_t min, max; + igraph_matrix_minmax(result, &min, &max); + IGRAPH_ASSERT(min >= -half_size); + IGRAPH_ASSERT(max <= half_size); + igraph_matrix_destroy(result); +} + +int main(void) { + igraph_t g; + igraph_matrix_t result; + igraph_vector_t bounds[4]; + igraph_vector_t weights; + igraph_real_t seed[20] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1.0}; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("Empty graph.\n"); + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_fruchterman_reingold(&g, &result, /*use_seed*/ 0, + /*niter*/ 100, /*start_temp*/ 1.0, IGRAPH_LAYOUT_NOGRID, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("Singleton graph in a box.\n"); + igraph_small(&g, 1, 0, -1); + igraph_matrix_init(&result, 0, 0); + make_box(1, 1.0, bounds); + IGRAPH_ASSERT(igraph_layout_fruchterman_reingold(&g, &result, /*use_seed*/ 0, + /*niter*/ 100, /*start_temp*/ 1.0, IGRAPH_LAYOUT_NOGRID, + /*weights*/ NULL, &bounds[0], &bounds[1], &bounds[2], &bounds[3]) == IGRAPH_SUCCESS); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + destroy_bounds(bounds); + + printf("A few tests with a disconnected graph of 10 vertices with loops in a box from -1 to 1.\n"); + igraph_small(&g, 10, 0, 0,1, 1,2, 2,0, 5,6, 6,7, 7,6, 7,7, 8,8, -1); + igraph_vector_init(&weights, 8); + igraph_vector_fill(&weights, 100); + make_box(10, 1.0, bounds); + printf("Without weights, grid or bounds.\n"); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_fruchterman_reingold(&g, &result, /*use_seed*/ 0, + /*niter*/ 100, /*start_temp*/ 10.0, IGRAPH_LAYOUT_NOGRID, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL) == IGRAPH_SUCCESS); + check_and_destroy(&result, 50.0); + + printf("With weights and no grid.\n"); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_fruchterman_reingold(&g, &result, /*use_seed*/ 0, + /*niter*/ 100, /*start_temp*/ 1.0, IGRAPH_LAYOUT_NOGRID, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL) == IGRAPH_SUCCESS); + check_and_destroy(&result, 50.0); + + printf("With weights and grid and high temperature.\n"); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_fruchterman_reingold(&g, &result, /*use_seed*/ 0, + /*niter*/ 10, /*start_temp*/ 1e10, IGRAPH_LAYOUT_GRID, + &weights, &bounds[0], &bounds[1], &bounds[2], &bounds[3]) == IGRAPH_SUCCESS); + check_and_destroy(&result, 1.0); + + printf("With weights and grid and high temperature and seed.\n"); + matrix_init_real_row_major(&result, 10, 2, seed); + IGRAPH_ASSERT(igraph_layout_fruchterman_reingold(&g, &result, /*use_seed*/ 1, + /*niter*/ 10, /*start_temp*/ 1e10, IGRAPH_LAYOUT_GRID, + &weights, &bounds[0], &bounds[1], &bounds[2], &bounds[3]) == IGRAPH_SUCCESS); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + + printf("Full graph of 5 vertices, seed and no iterations:\n"); + igraph_full(&g, 5, 0, 0); + matrix_init_real_row_major(&result, 5, 2, seed); + IGRAPH_ASSERT(igraph_layout_fruchterman_reingold(&g, &result, /*use_seed*/ 1, + /*niter*/ 0, /*start_temp*/ 100, IGRAPH_LAYOUT_GRID, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + destroy_bounds(bounds); + igraph_vector_destroy(&weights); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_fruchterman_reingold.out b/tests/unit/igraph_layout_fruchterman_reingold.out new file mode 100644 index 0000000..fcca374 --- /dev/null +++ b/tests/unit/igraph_layout_fruchterman_reingold.out @@ -0,0 +1,14 @@ +Empty graph. +[ 0-by-2 ] +Singleton graph in a box. +A few tests with a disconnected graph of 10 vertices with loops in a box from -1 to 1. +Without weights, grid or bounds. +With weights and no grid. +With weights and grid and high temperature. +With weights and grid and high temperature and seed. +Full graph of 5 vertices, seed and no iterations: +[ 0.1 0.2 + 0.3 0.4 + 0.5 0.6 + 0.7 0.8 + 0.9 1 ] diff --git a/tests/unit/igraph_layout_fruchterman_reingold_3d.c b/tests/unit/igraph_layout_fruchterman_reingold_3d.c new file mode 100644 index 0000000..5a72e63 --- /dev/null +++ b/tests/unit/igraph_layout_fruchterman_reingold_3d.c @@ -0,0 +1,130 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void make_box(int vertices, float half_size, igraph_vector_t *bounds) { + for (int i = 0; i < 6; i++) { + igraph_vector_init(&bounds[i], vertices); + } + igraph_vector_fill(&bounds[0], -half_size); + igraph_vector_fill(&bounds[1], half_size); + igraph_vector_fill(&bounds[2], -half_size); + igraph_vector_fill(&bounds[3], half_size); + igraph_vector_fill(&bounds[4], -half_size); + igraph_vector_fill(&bounds[5], half_size); +} + +void destroy_bounds(igraph_vector_t *bounds) { + for (int i = 0; i < 6; i++) { + igraph_vector_destroy(&bounds[i]); + } +} + +void check_and_destroy(igraph_matrix_t *result, igraph_real_t half_size) { + igraph_real_t min, max; + igraph_matrix_minmax(result, &min, &max); + IGRAPH_ASSERT(min >= -half_size); + IGRAPH_ASSERT(max <= half_size); + igraph_matrix_destroy(result); +} + +int main(void) { + igraph_t g; + igraph_matrix_t result; + igraph_vector_t bounds[6]; + igraph_vector_t weights; + igraph_real_t seed[30] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2, -1.3, -1.4, -1.5, -1.6, -1.7, -1.8, -1.9, -2.0}; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("Empty graph.\n"); + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&result, 0, 0); + igraph_layout_fruchterman_reingold_3d(&g, &result, /*use_seed*/ 0, + /*niter*/ 100, /*start_temp*/ 1.0, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL, /*minz*/ NULL, /*maxz*/ NULL); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("Singleton graph in a box.\n"); + igraph_small(&g, 1, 0, -1); + igraph_matrix_init(&result, 0, 0); + make_box(1, 1.0, bounds); + igraph_layout_fruchterman_reingold_3d(&g, &result, /*use_seed*/ 0, + /*niter*/ 100, /*start_temp*/ 1.0, + /*weights*/ NULL, &bounds[0], &bounds[1], &bounds[2], &bounds[3], &bounds[4], &bounds[5]); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + destroy_bounds(bounds); + + printf("A few tests with a disconnected graph of 10 vertices with loops in a box from -1 to 1.\n"); + igraph_small(&g, 10, 0, 0,1, 1,2, 2,0, 5,6, 6,7, 7,6, 7,7, 8,8, -1); + igraph_vector_init(&weights, 8); + igraph_vector_fill(&weights, 100); + make_box(10, 1.0, bounds); + printf("Without weights, grid or bounds.\n"); + igraph_matrix_init(&result, 0, 0); + igraph_layout_fruchterman_reingold_3d(&g, &result, /*use_seed*/ 0, + /*niter*/ 100, /*start_temp*/ 10.0, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL, /*minz*/ NULL, /*maxz*/ NULL); + check_and_destroy(&result, 50.0); + + printf("With weights.\n"); + igraph_matrix_init(&result, 0, 0); + igraph_layout_fruchterman_reingold_3d(&g, &result, /*use_seed*/ 0, + /*niter*/ 100, /*start_temp*/ 1.0, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL, /*minz*/ NULL, /*maxz*/ NULL); + check_and_destroy(&result, 50.0); + + printf("With weights and high temperature.\n"); + igraph_matrix_init(&result, 0, 0); + igraph_layout_fruchterman_reingold_3d(&g, &result, /*use_seed*/ 0, + /*niter*/ 10, /*start_temp*/ 1e10, + &weights, &bounds[0], &bounds[1], &bounds[2], &bounds[3], &bounds[4], &bounds[5]); + check_and_destroy(&result, 1.0); + + printf("With weights and high temperature and seed.\n"); + matrix_init_real_row_major(&result, 10, 3, seed); + igraph_layout_fruchterman_reingold_3d(&g, &result, /*use_seed*/ 1, + /*niter*/ 10, /*start_temp*/ 1e10, + &weights, &bounds[0], &bounds[1], &bounds[2], &bounds[3], &bounds[4], &bounds[5]); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + + printf("Full graph of 5 vertices, seed and no iterations:\n"); + igraph_full(&g, 5, 0, 0); + matrix_init_real_row_major(&result, 5, 3, seed); + igraph_layout_fruchterman_reingold_3d(&g, &result, /*use_seed*/ 1, + /*niter*/ 0, /*start_temp*/ 100, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL, /*minz*/ NULL, /*maxz*/ NULL); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + destroy_bounds(bounds); + igraph_vector_destroy(&weights); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_fruchterman_reingold_3d.out b/tests/unit/igraph_layout_fruchterman_reingold_3d.out new file mode 100644 index 0000000..dc6e7b9 --- /dev/null +++ b/tests/unit/igraph_layout_fruchterman_reingold_3d.out @@ -0,0 +1,14 @@ +Empty graph. +[ 0-by-3 ] +Singleton graph in a box. +A few tests with a disconnected graph of 10 vertices with loops in a box from -1 to 1. +Without weights, grid or bounds. +With weights. +With weights and high temperature. +With weights and high temperature and seed. +Full graph of 5 vertices, seed and no iterations: +[ 0.1 0.2 0.3 + 0.4 0.5 0.6 + 0.7 0.8 0.9 + 1 -0.1 -0.2 + -0.3 -0.4 -0.5 ] diff --git a/tests/unit/igraph_layout_gem.c b/tests/unit/igraph_layout_gem.c new file mode 100644 index 0000000..831eaf9 --- /dev/null +++ b/tests/unit/igraph_layout_gem.c @@ -0,0 +1,120 @@ +/* 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_matrix_t res; + igraph_bool_t use_seed; + igraph_bool_t do_not_use_seed; + igraph_integer_t maxiter; + igraph_real_t temp_max; + igraph_real_t temp_min; + igraph_real_t temp_init; + + igraph_rng_seed(igraph_rng_default(), 42); + use_seed = 1; + do_not_use_seed = 0; + maxiter = 10; + temp_max = 1; + temp_min = 0.1; + temp_init = 1; + + printf("Check if 0 vertex graph crashes.\n"); + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, -1); + igraph_matrix_init(&res, 0, 2); + + igraph_layout_gem(&graph, &res, use_seed, maxiter, temp_max, temp_min, temp_init); + igraph_matrix_print(&res); + igraph_destroy(&graph); + igraph_matrix_destroy(&res); + + printf("Check seeded 1-vertex graph, no iterations.\n"); + maxiter = 0; + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, -1); + { + int elem[] = {3,4}; + matrix_init_int_row_major(&res, 1, 2, elem); + } + + igraph_layout_gem(&graph, &res, use_seed, maxiter, temp_max, temp_min, temp_init); + igraph_matrix_print(&res); + igraph_destroy(&graph); + igraph_matrix_destroy(&res); + maxiter = 40; + + printf("Check if 0 vertex graph crashes without a seed.\n"); + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, -1); + igraph_matrix_init(&res, 0, 2); + + igraph_layout_gem(&graph, &res, do_not_use_seed, maxiter, temp_max, temp_min, temp_init); + igraph_matrix_print(&res); + igraph_destroy(&graph); + igraph_matrix_destroy(&res); + + printf("Check unseeded 1-vertex graph.\n"); + maxiter = 40; + igraph_small(&graph, 1, IGRAPH_UNDIRECTED, -1); + igraph_matrix_init(&res, 0, 2); + + igraph_layout_gem(&graph, &res, do_not_use_seed, maxiter, temp_max, temp_min, temp_init); + igraph_destroy(&graph); + igraph_matrix_destroy(&res); + + printf("Check 2-vertex graph.\n"); + maxiter = 100000; + igraph_small(&graph, 2, IGRAPH_UNDIRECTED, 0,1, -1); + { + int elem[] = {3, 4, 5, 6}; + matrix_init_int_row_major(&res, 2, 2, elem); + } + + igraph_layout_gem(&graph, &res, use_seed, maxiter, temp_max, temp_min, temp_init); + IGRAPH_ASSERT(igraph_matrix_min(&res) > -1000); + IGRAPH_ASSERT(igraph_matrix_max(&res) < 1000); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + maxiter = 3; + printf("Check negative maxiter.\n"); + CHECK_ERROR(igraph_layout_gem(&graph, &res, use_seed, -1, temp_max, temp_min, temp_init), IGRAPH_EINVAL); + printf("Check negative temp_max.\n"); + CHECK_ERROR(igraph_layout_gem(&graph, &res, use_seed, maxiter, -1, temp_min, temp_init), IGRAPH_EINVAL); + printf("Check negative temp_min.\n"); + CHECK_ERROR(igraph_layout_gem(&graph, &res, use_seed, maxiter, temp_max, -1, temp_init), IGRAPH_EINVAL); + printf("Check negative temp_init.\n"); + CHECK_ERROR(igraph_layout_gem(&graph, &res, use_seed, maxiter, temp_max, temp_min, -1), IGRAPH_EINVAL); + printf("Check temp_init not between min and max.\n"); + CHECK_ERROR(igraph_layout_gem(&graph, &res, use_seed, maxiter, 1, 2, 1.5), IGRAPH_EINVAL); + printf("Check too many rows in seed.\n"); + igraph_matrix_destroy(&res); + igraph_matrix_init(&res, 10, 2); + CHECK_ERROR(igraph_layout_gem(&graph, &res, use_seed, maxiter, temp_max, temp_min, temp_init), IGRAPH_EINVAL); + printf("Check too many columns in seed.\n"); + igraph_matrix_destroy(&res); + igraph_matrix_init(&res, 2, 5); + CHECK_ERROR(igraph_layout_gem(&graph, &res, use_seed, maxiter, temp_max, temp_min, temp_init), IGRAPH_EINVAL); + + igraph_matrix_destroy(&res); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_gem.out b/tests/unit/igraph_layout_gem.out new file mode 100644 index 0000000..e70c584 --- /dev/null +++ b/tests/unit/igraph_layout_gem.out @@ -0,0 +1,13 @@ +Check if 0 vertex graph crashes. +Check seeded 1-vertex graph, no iterations. +3 4 +Check if 0 vertex graph crashes without a seed. +Check unseeded 1-vertex graph. +Check 2-vertex graph. +Check negative maxiter. +Check negative temp_max. +Check negative temp_min. +Check negative temp_init. +Check temp_init not between min and max. +Check too many rows in seed. +Check too many columns in seed. diff --git a/tests/unit/igraph_layout_graphopt.c b/tests/unit/igraph_layout_graphopt.c new file mode 100644 index 0000000..32995d5 --- /dev/null +++ b/tests/unit/igraph_layout_graphopt.c @@ -0,0 +1,98 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void check_and_destroy(igraph_matrix_t *result, igraph_real_t half_size) { + igraph_real_t min, max; + igraph_matrix_minmax(result, &min, &max); + IGRAPH_ASSERT(min >= -half_size); + IGRAPH_ASSERT(max <= half_size); + igraph_matrix_destroy(result); +} + +int main(void) { + igraph_t g; + igraph_matrix_t result; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No vertices:\n"); + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_graphopt(&g, &result, /*niter*/ 500, /*node_charge*/ 0.001, /*node_mass*/ 30, /*spring_length*/ 0, /*spring constant*/ 1, /*max_sa_movement*/ 5, /*use seed*/ 0) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("One vertex.\n"); + igraph_small(&g, 1, 0, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_graphopt(&g, &result, /*niter*/ 500, /*node_charge*/ 0.001, /*node_mass*/ 30, /*spring_length*/ 0, /*spring constant*/ 1, /*max_sa_movement*/ 5, /*use seed*/ 0) == IGRAPH_SUCCESS); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + + printf("Full graph of 4 vertices, no loops.\n"); + igraph_full(&g, 4, 0, 0); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_graphopt(&g, &result, /*niter*/ 500, /*node_charge*/ 0.001, /*node_mass*/ 30, /*spring_length*/ 0, /*spring constant*/ 1, /*max_sa_movement*/ 5, /*use seed*/ 0) == IGRAPH_SUCCESS); + check_and_destroy(&result, 20.0); + igraph_destroy(&g); + + printf("4 vertices, disconnected, with loops and multi-edges.\n"); + igraph_small(&g, 4, 0, 0,0, 0,0, 0,0, 1,2, 1,2, 1,2, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_graphopt(&g, &result, /*niter*/ 500, /*node_charge*/ 0.001, /*node_mass*/ 30, /*spring_length*/ 0, /*spring constant*/ 1, /*max_sa_movement*/ 5, /*use seed*/ 0) == IGRAPH_SUCCESS); + check_and_destroy(&result, 100.0); + igraph_destroy(&g); + + printf("Full graph of 4 vertices, no loops with no repulsion.\n"); + igraph_full(&g, 4, 0, 0); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_graphopt(&g, &result, /*niter*/ 500, /*node_charge*/ 0.0, /*node_mass*/ 30, /*spring_length*/ 0, /*spring constant*/ 1, /*max_sa_movement*/ 5, /*use seed*/ 0) == IGRAPH_SUCCESS); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + + printf("4 vertices in a line, with no repulsion, spring length 1 and a seed:\n"); + igraph_small(&g, 4, 0, 0,1, 1,2, 2,3, -1); + igraph_real_t seed[] = {0.15, -0.15, 0.05, -0.05, -0.05, 0.05, -0.15, 0.15}; + matrix_init_real_row_major(&result, 4, 2, seed); + IGRAPH_ASSERT(igraph_layout_graphopt(&g, &result, /*niter*/ 500, /*node_charge*/ 0.0, /*node_mass*/ 30, /*spring_length*/ 1, /*spring constant*/ 10, /*max_sa_movement*/ 5, /*use seed*/ 1) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("4 vertices in a line, with no repulsion, spring length 1 and a seed, no sa_movement:\n"); + igraph_small(&g, 4, 0, 0,1, 1,2, 2,3, -1); + matrix_init_real_row_major(&result, 4, 2, seed); + IGRAPH_ASSERT(igraph_layout_graphopt(&g, &result, /*niter*/ 500, /*node_charge*/ 0.0, /*node_mass*/ 30, /*spring_length*/ 1, /*spring constant*/ 10, /*max_sa_movement*/ 0, /*use seed*/ 1) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("Wrong size seed.\n"); + igraph_small(&g, 4, 0, 0,1, 1,2, 2,3, -1); + matrix_init_real_row_major(&result, 3, 2, seed); + IGRAPH_ASSERT(igraph_layout_graphopt(&g, &result, /*niter*/ 500, /*node_charge*/ 0.0, /*node_mass*/ 30, /*spring_length*/ 1, /*spring constant*/ 10, /*max_sa_movement*/ 0, /*use seed*/ 1) == IGRAPH_SUCCESS); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_graphopt.out b/tests/unit/igraph_layout_graphopt.out new file mode 100644 index 0000000..8dfd438 --- /dev/null +++ b/tests/unit/igraph_layout_graphopt.out @@ -0,0 +1,17 @@ +No vertices: +[ 0-by-2 ] +One vertex. +Full graph of 4 vertices, no loops. +4 vertices, disconnected, with loops and multi-edges. +Full graph of 4 vertices, no loops with no repulsion. +4 vertices in a line, with no repulsion, spring length 1 and a seed: +[ 1.06066 -1.06066 + 0.353553 -0.353553 + -0.353553 0.353553 + -1.06066 1.06066 ] +4 vertices in a line, with no repulsion, spring length 1 and a seed, no sa_movement: +[ 0.15 -0.15 + 0.05 -0.05 + -0.05 0.05 + -0.15 0.15 ] +Wrong size seed. diff --git a/tests/unit/igraph_layout_grid.c b/tests/unit/igraph_layout_grid.c new file mode 100644 index 0000000..c82a132 --- /dev/null +++ b/tests/unit/igraph_layout_grid.c @@ -0,0 +1,72 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_matrix_t coords; + + igraph_empty(&g, 15, 0); + igraph_matrix_init(&coords, 0, 0); + + /* Predefined width, 2D */ + igraph_layout_grid(&g, &coords, 5); + igraph_matrix_print(&coords); + printf("===\n"); + + /* Automatic width, 2D */ + igraph_layout_grid(&g, &coords, -1); + igraph_matrix_print(&coords); + printf("===\n"); + + /* Predefined width and height, 3D */ + igraph_layout_grid_3d(&g, &coords, 4, 2); + igraph_matrix_print(&coords); + printf("=====\n"); + + /* Predefined width, 3D */ + igraph_layout_grid_3d(&g, &coords, 4, -1); + igraph_matrix_print(&coords); + printf("=====\n"); + + /* Predefined height, 3D */ + igraph_layout_grid_3d(&g, &coords, -1, 3); + igraph_matrix_print(&coords); + printf("=====\n"); + + /* Automatic width and height, 3D */ + igraph_layout_grid_3d(&g, &coords, -1, -1); + igraph_matrix_print(&coords); + + igraph_matrix_destroy(&coords); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_layout_grid.out b/tests/unit/igraph_layout_grid.out new file mode 100644 index 0000000..1b8a947 --- /dev/null +++ b/tests/unit/igraph_layout_grid.out @@ -0,0 +1,95 @@ +0 0 +1 0 +2 0 +3 0 +4 0 +0 1 +1 1 +2 1 +3 1 +4 1 +0 2 +1 2 +2 2 +3 2 +4 2 +=== +0 0 +1 0 +2 0 +3 0 +0 1 +1 1 +2 1 +3 1 +0 2 +1 2 +2 2 +3 2 +0 3 +1 3 +2 3 +=== +0 0 0 +1 0 0 +2 0 0 +3 0 0 +0 1 0 +1 1 0 +2 1 0 +3 1 0 +0 0 1 +1 0 1 +2 0 1 +3 0 1 +0 1 1 +1 1 1 +2 1 1 +===== +0 0 0 +1 0 0 +2 0 0 +3 0 0 +0 1 0 +1 1 0 +2 1 0 +3 1 0 +0 0 1 +1 0 1 +2 0 1 +3 0 1 +0 1 1 +1 1 1 +2 1 1 +===== +0 0 0 +1 0 0 +2 0 0 +0 1 0 +1 1 0 +2 1 0 +0 2 0 +1 2 0 +2 2 0 +0 0 1 +1 0 1 +2 0 1 +0 1 1 +1 1 1 +2 1 1 +===== +0 0 0 +1 0 0 +2 0 0 +0 1 0 +1 1 0 +2 1 0 +0 2 0 +1 2 0 +2 2 0 +0 0 1 +1 0 1 +2 0 1 +0 1 1 +1 1 1 +2 1 1 diff --git a/tests/unit/igraph_layout_kamada_kawai.c b/tests/unit/igraph_layout_kamada_kawai.c new file mode 100644 index 0000000..75e77ca --- /dev/null +++ b/tests/unit/igraph_layout_kamada_kawai.c @@ -0,0 +1,154 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +void make_box(int vertices, float half_size, igraph_vector_t *bounds) { + for (int i = 0; i < 4; i++) { + igraph_vector_init(&bounds[i], vertices); + } + igraph_vector_fill(&bounds[0], -half_size); + igraph_vector_fill(&bounds[1], half_size); + igraph_vector_fill(&bounds[2], -half_size); + igraph_vector_fill(&bounds[3], half_size); +} + +void destroy_bounds(igraph_vector_t *bounds) { + for (int i = 0; i < 4; i++) { + igraph_vector_destroy(&bounds[i]); + } +} + +void check_and_destroy(igraph_matrix_t *result, igraph_real_t half_size) { + igraph_real_t min, max; + igraph_matrix_minmax(result, &min, &max); + IGRAPH_ASSERT(min >= -half_size); + IGRAPH_ASSERT(max <= half_size); + igraph_matrix_destroy(result); +} + +int main(void) { + igraph_t g; + igraph_matrix_t result; + igraph_vector_t bounds[4]; + igraph_vector_t weights; + igraph_real_t seed[20] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1.0}; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("Empty graph.\n"); + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&result, 0, 0); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 0, /*maxiter*/ 100, + /*epsilon*/ 0.0001, /*kkconst */ 10, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + + printf("Singleton graph in a box.\n"); + igraph_small(&g, 1, 0, -1); + igraph_matrix_init(&result, 0, 0); + make_box(1, 1.0, bounds); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 0, /*maxiter*/ 100, + /*epsilon*/ 0.0001, /*kkconst */ 10, + /*weights*/ NULL, &bounds[0], &bounds[1], &bounds[2], &bounds[3]); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + destroy_bounds(bounds); + + printf("Two connected vertices.\n"); + igraph_small(&g, 2, 0, 0,1, -1); + igraph_matrix_init(&result, 0, 0); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 0, /*maxiter*/ 1000, + /*epsilon*/ 0, /*kkconst */ 2, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL); + check_and_destroy(&result, 1.0); + + printf("Two connected vertices in a box.\n"); + igraph_matrix_init(&result, 0, 0); + make_box(2, 1.0, bounds); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 0, /*maxiter*/ 1000, + /*epsilon*/ 0, /*kkconst */ 2, + /*weights*/ NULL, &bounds[0], &bounds[1], &bounds[2], &bounds[3]); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + destroy_bounds(bounds); + + printf("A few tests with a disconnected graph of 10 vertices with loops in a box from -1 to 1.\n"); + igraph_small(&g, 10, 0, 0,1, 1,2, 2,0, 5,6, 6,7, 7,6, 7,7, 8,8, -1); + igraph_vector_init(&weights, 8); + igraph_vector_fill(&weights, 100); + make_box(10, 1.0, bounds); + printf("Without weights or bounds.\n"); + igraph_matrix_init(&result, 0, 0); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 0, /*maxiter*/ 100, + /*epsilon*/ 0.0001, /*kkconst */ 10, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL); + check_and_destroy(&result, 50.0); + + printf("With weights.\n"); + igraph_matrix_init(&result, 0, 0); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 0, /*maxiter*/ 100, + /*epsilon*/ 0.0001, /*kkconst */ 10, + &weights, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL); + check_and_destroy(&result, 50.0); + + printf("With weights, bounds, and high kkconst.\n"); + igraph_matrix_init(&result, 0, 0); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 0, /*maxiter*/ 100, + /*epsilon*/ 0.0001, /*kkconst */ 1000, + &weights, &bounds[0], &bounds[1], &bounds[2], &bounds[3]); + check_and_destroy(&result, 1.0); + + printf("With weights, bounds, and low kkconst.\n"); + igraph_matrix_init(&result, 0, 0); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 0, /*maxiter*/ 100, + /*epsilon*/ 0.0001, /*kkconst */ 0.0001, + &weights, &bounds[0], &bounds[1], &bounds[2], &bounds[3]); + check_and_destroy(&result, 1.0); + + printf("With weights, bounds, and high kkconst and seed.\n"); + matrix_init_real_row_major(&result, 10, 2, seed); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 1, /*maxiter*/ 100, + /*epsilon*/ 0.0001, /*kkconst */ 1000, + &weights, &bounds[0], &bounds[1], &bounds[2], &bounds[3]); + check_and_destroy(&result, 1.0); + igraph_destroy(&g); + + printf("Full graph of 5 vertices, seed and no iterations:\n"); + igraph_full(&g, 5, 0, 0); + matrix_init_real_row_major(&result, 5, 2, seed); + igraph_layout_kamada_kawai(&g, &result, /*use_seed*/ 1, /*maxiter*/ 0, + /*epsilon*/ 0.0001, /*kkconst */ 10, + /*weight*/ NULL, /*minx*/ NULL, /*maxx*/ NULL, /*miny*/ NULL, + /*maxy*/ NULL); + print_matrix(&result); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + destroy_bounds(bounds); + igraph_vector_destroy(&weights); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_kamada_kawai.out b/tests/unit/igraph_layout_kamada_kawai.out new file mode 100644 index 0000000..e200d1a --- /dev/null +++ b/tests/unit/igraph_layout_kamada_kawai.out @@ -0,0 +1,17 @@ +Empty graph. +[ 0-by-2 ] +Singleton graph in a box. +Two connected vertices. +Two connected vertices in a box. +A few tests with a disconnected graph of 10 vertices with loops in a box from -1 to 1. +Without weights or bounds. +With weights. +With weights, bounds, and high kkconst. +With weights, bounds, and low kkconst. +With weights, bounds, and high kkconst and seed. +Full graph of 5 vertices, seed and no iterations: +[ 0.1 0.2 + 0.3 0.4 + 0.5 0.6 + 0.7 0.8 + 0.9 1 ] diff --git a/tests/unit/igraph_layout_lgl.c b/tests/unit/igraph_layout_lgl.c new file mode 100644 index 0000000..6692cd5 --- /dev/null +++ b/tests/unit/igraph_layout_lgl.c @@ -0,0 +1,109 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_matrix_t coords; + igraph_real_t vc; + + igraph_rng_seed(igraph_rng_default(), 33); + + printf("Testing graph with no vertices\n"); + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&coords, 0, 0); + vc = igraph_vcount(&g); + igraph_layout_lgl(&g, &coords, + /* maxiter */ 150, + /* maxdelta */ vc, + /* area */ vc * vc, + /* coolexp */ 1.5, + /* repulserad */ vc * vc * vc, + /* cellsize */ sqrt(sqrt(vc)), + /* root */ 0); + + igraph_matrix_destroy(&coords); + igraph_destroy(&g); + + printf("Testing k-ary tree\n"); + igraph_kary_tree(&g, 100, 3, IGRAPH_TREE_UNDIRECTED); + igraph_matrix_init(&coords, 0, 0); + vc = igraph_vcount(&g); + igraph_layout_lgl(&g, &coords, + /* maxiter */ 150, + /* maxdelta */ vc, + /* area */ vc * vc, + /* coolexp */ 1.5, + /* repulserad */ vc * vc * vc, + /* cellsize */ sqrt(sqrt(vc)), + /* root */ 0); + + igraph_matrix_destroy(&coords); + igraph_destroy(&g); + + printf("Testing k-ary tree (many more times to stress-test igraph_2dgrid_t)\n"); + igraph_kary_tree(&g, 100, 3, IGRAPH_TREE_UNDIRECTED); + for (igraph_integer_t i = 0; i < 100; i++) { + igraph_matrix_init(&coords, 0, 0); + vc = igraph_vcount(&g); + igraph_layout_lgl(&g, &coords, + /* maxiter */ 150, + /* maxdelta */ vc, + /* area */ vc * vc, + /* coolexp */ 1.5, + /* repulserad */ vc * vc * vc, + /* cellsize */ sqrt(sqrt(vc)), + /* root */ 0); + + igraph_matrix_destroy(&coords); + } + igraph_destroy(&g); + + /* Test that a warning is printed for disconnected graphs */ + printf("Testing disconnected graph\n"); + igraph_small(&g, 5, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, 3, 4, -1); + igraph_matrix_init(&coords, 0, 0); + vc = igraph_vcount(&g); + EXPECT_WARNING( + igraph_layout_lgl(&g, &coords, + /* maxiter */ 150, + /* maxdelta */ vc, + /* area */ vc * vc, + /* coolexp */ 1.5, + /* repulserad */ vc * vc * vc, + /* cellsize */ sqrt(sqrt(vc)), + /* root */ 0), + "LGL layout does not support disconnected graphs yet." + ); + igraph_matrix_destroy(&coords); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_layout_lgl.out b/tests/unit/igraph_layout_lgl.out new file mode 100644 index 0000000..418aaf0 --- /dev/null +++ b/tests/unit/igraph_layout_lgl.out @@ -0,0 +1,4 @@ +Testing graph with no vertices +Testing k-ary tree +Testing k-ary tree (many more times to stress-test igraph_2dgrid_t) +Testing disconnected graph diff --git a/tests/unit/igraph_layout_mds.c b/tests/unit/igraph_layout_mds.c new file mode 100644 index 0000000..25c480b --- /dev/null +++ b/tests/unit/igraph_layout_mds.c @@ -0,0 +1,101 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "test_utilities.h" + +#define sqr(x) ((x)*(x)) + +int main(void) { + igraph_t g; + igraph_matrix_t coords, dist_mat; + igraph_integer_t i, j; + + igraph_rng_seed(igraph_rng_default(), 42); /* make tests deterministic */ + + RNG_BEGIN(); + + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&coords, 0, 0); + igraph_layout_mds(&g, &coords, 0, 2); + print_matrix(&coords); + igraph_matrix_destroy(&coords); + igraph_destroy(&g); + + igraph_kary_tree(&g, 10, 2, IGRAPH_TREE_UNDIRECTED); + igraph_matrix_init(&coords, 0, 0); + igraph_layout_mds(&g, &coords, 0, 2); + if (MATRIX(coords, 0, 0) > 0) { + for (i = 0; i < igraph_matrix_nrow(&coords); i++) { + MATRIX(coords, i, 0) *= -1; + } + } + if (MATRIX(coords, 0, 1) < 0) { + for (i = 0; i < igraph_matrix_nrow(&coords); i++) { + MATRIX(coords, i, 1) *= -1; + } + } + print_matrix(&coords); + igraph_matrix_destroy(&coords); + igraph_destroy(&g); + + igraph_full(&g, 8, IGRAPH_UNDIRECTED, 0); + igraph_matrix_init(&coords, 8, 2); + igraph_matrix_init(&dist_mat, 8, 8); + for (i = 0; i < 8; i++) + for (j = 0; j < 2; j++) { + MATRIX(coords, i, j) = RNG_INTEGER(0, 1000); + } + for (i = 0; i < 8; i++) + for (j = i + 1; j < 8; j++) { + double dist_sq = 0.0; + dist_sq += sqr(MATRIX(coords, i, 0) - MATRIX(coords, j, 0)); + dist_sq += sqr(MATRIX(coords, i, 1) - MATRIX(coords, j, 1)); + MATRIX(dist_mat, i, j) = sqrt(dist_sq); + MATRIX(dist_mat, j, i) = sqrt(dist_sq); + } + igraph_layout_mds(&g, &coords, &dist_mat, 2); + for (i = 0; i < 8; i++) + for (j = i + 1; j < 8; j++) { + double dist_sq = 0.0; + dist_sq += sqr(MATRIX(coords, i, 0) - MATRIX(coords, j, 0)); + dist_sq += sqr(MATRIX(coords, i, 1) - MATRIX(coords, j, 1)); + if (fabs(sqrt(dist_sq) - MATRIX(dist_mat, i, j)) > 1e-2) { + printf("dist(%" IGRAPH_PRId ", %" IGRAPH_PRId ") should be %.4f, but it is %.4f\n", + i, j, MATRIX(dist_mat, i, j), sqrt(dist_sq)); + return 1; + } + } + igraph_matrix_destroy(&dist_mat); + igraph_matrix_destroy(&coords); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + RNG_END(); + + return 0; +} diff --git a/tests/unit/igraph_layout_mds.out b/tests/unit/igraph_layout_mds.out new file mode 100644 index 0000000..3595b61 --- /dev/null +++ b/tests/unit/igraph_layout_mds.out @@ -0,0 +1,11 @@ +[ 0-by-2 ] +[ -0.692039 0.0247583 + 0.399957 0.178289 + -1.78403 -0.128772 + 1.25186 -0.697105 + 0.891182 1.32979 + -2.6988 -0.242629 + -2.6988 -0.242629 + 1.97413 -1.3515 + 1.97413 -1.3515 + 1.38241 2.4813 ] diff --git a/tests/unit/igraph_layout_merge.c b/tests/unit/igraph_layout_merge.c new file mode 100644 index 0000000..87cd1b6 --- /dev/null +++ b/tests/unit/igraph_layout_merge.c @@ -0,0 +1,83 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "layout/layout_internal.h" + +#include "test_utilities.h" + +int main(void) { + + /*******************/ + /* Testing the DLA */ + /*******************/ + igraph_integer_t nodes = 10; + igraph_i_layout_mergegrid_t grid; + igraph_vector_t x, y, r; + igraph_integer_t i; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_init(&x, nodes); + igraph_vector_init(&y, nodes); + igraph_vector_init(&r, nodes); + igraph_i_layout_mergegrid_init(&grid, -5, 5, 100, -5, 5, 100); + + /* radius */ + for (i = 0; i < nodes; i++) { + VECTOR(r)[i] = rand() / (double)RAND_MAX; + } + igraph_vector_sort(&r); + + /* place */ + VECTOR(x)[0] = 0; + VECTOR(y)[0] = 0; + igraph_i_layout_merge_place_sphere(&grid, 0, 0, VECTOR(r)[nodes - 1], 0); + + for (i = 1; i < nodes; i++) { + /* fprintf(stderr, "%" IGRAPH_PRId " ", i); */ + igraph_i_layout_merge_dla(&grid, i, + igraph_vector_get_ptr(&x, i), + igraph_vector_get_ptr(&y, i), + VECTOR(r)[nodes - i - 1], 0, 0, 4, 7); + igraph_i_layout_merge_place_sphere(&grid, VECTOR(x)[i], VECTOR(y)[i], + VECTOR(r)[nodes - i - 1], i); + } + + /* for (i=0; i + 334 Harvard st, Cambridge MA, 02139 USA + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t small, big; + igraph_matrix_t small_coords, big_coords, merged_coords; + igraph_vector_ptr_t graph_ptr; + igraph_matrix_list_t coords_list; + igraph_arpack_options_t arpack_opts; + igraph_integer_t i, j, nrow, ncol; + + /* To make things reproducible */ + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_small(&big, 10, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 0, + -1); + + igraph_small(&small, 3, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 0, + -1); + + igraph_arpack_options_init(&arpack_opts); + + igraph_matrix_init(&big_coords, 0, 0); + igraph_layout_circle(&big, &big_coords, igraph_vss_all()); + + igraph_matrix_init(&small_coords, 0, 0); + igraph_layout_circle(&small, &small_coords, igraph_vss_all()); + + igraph_vector_ptr_init(&graph_ptr, 2); + igraph_matrix_list_init(&coords_list, 0); + igraph_matrix_init(&merged_coords, 0, 0); + VECTOR(graph_ptr)[0] = &big; + VECTOR(graph_ptr)[1] = &small; + igraph_matrix_list_push_back(&coords_list, &big_coords); + igraph_matrix_list_push_back(&coords_list, &small_coords); + /* ownership of big_coords and small_coords now belongs to coords_list */ + + igraph_layout_merge_dla(&graph_ptr, &coords_list, &merged_coords); + + nrow = igraph_matrix_nrow(&merged_coords); + ncol = igraph_matrix_ncol(&merged_coords); + for (i = 0; i < nrow; i++) { + for (j = 0; j < ncol; j++) { + if (fabs(MATRIX(merged_coords, i, j)) < 1e-8) { + MATRIX(merged_coords, i, j) = 0; + } + } + } + + igraph_matrix_printf(&merged_coords, "%.4f"); + + igraph_matrix_destroy(&merged_coords); + igraph_vector_ptr_destroy(&graph_ptr); + igraph_matrix_list_destroy(&coords_list); + igraph_destroy(&small); + igraph_destroy(&big); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_layout_merge2.out b/tests/unit/igraph_layout_merge2.out new file mode 100644 index 0000000..954b96b --- /dev/null +++ b/tests/unit/igraph_layout_merge2.out @@ -0,0 +1,13 @@ +4.0748 0.0000 +3.2966 2.3951 +1.2592 3.8754 +-1.2592 3.8754 +-3.2966 2.3951 +-4.0748 0.0000 +-3.2966 -2.3951 +-1.2592 -3.8754 +1.2592 -3.8754 +3.2966 -2.3951 +-6.5576 -0.7416 +-9.5422 0.9815 +-9.5422 -2.4648 diff --git a/tests/unit/igraph_layout_merge3.c b/tests/unit/igraph_layout_merge3.c new file mode 100644 index 0000000..9de5cc6 --- /dev/null +++ b/tests/unit/igraph_layout_merge3.c @@ -0,0 +1,46 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_matrix_t coords; + int i; + + igraph_matrix_init(&coords, 0, 0); + + for (i = 0; i < 10; i++) { + igraph_erdos_renyi_game_gnp(&graph, 100, 2.0 / 100, IGRAPH_UNDIRECTED, /*loops=*/ 0); + igraph_layout_mds(&graph, &coords, /*dist=*/ 0, /*dim=*/ 2); + igraph_destroy(&graph); + } + + igraph_matrix_destroy(&coords); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_layout_random_3d.c b/tests/unit/igraph_layout_random_3d.c new file mode 100644 index 0000000..47f884f --- /dev/null +++ b/tests/unit/igraph_layout_random_3d.c @@ -0,0 +1,54 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_matrix_t result; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No vertices:\n"); + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_random_3d(&g, &result) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_destroy(&g); + igraph_matrix_destroy(&result); + + printf("One vertex:\n"); + igraph_small(&g, 1, 0, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_random_3d(&g, &result) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_destroy(&g); + igraph_matrix_destroy(&result); + + printf("10 vertices:\n"); + igraph_small(&g, 10, 0, 0,1, 0,1, 0,1, 2,2, 2,2, 2,2, 3,4, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_random_3d(&g, &result) == IGRAPH_SUCCESS); + print_matrix(&result); + igraph_destroy(&g); + igraph_matrix_destroy(&result); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_random_3d.out b/tests/unit/igraph_layout_random_3d.out new file mode 100644 index 0000000..9a8e0ce --- /dev/null +++ b/tests/unit/igraph_layout_random_3d.out @@ -0,0 +1,15 @@ +No vertices: +[ 0-by-3 ] +One vertex: +[ -0.725433 0.680789 0.968361 ] +10 vertices: +[ 0.845075 -0.758881 0.792851 + 0.934177 -0.392748 -0.70708 + -0.665235 -0.955511 -0.955927 + -0.264025 0.925958 -0.359115 + 0.280513 0.298567 -0.155571 + -0.698699 -0.609517 -0.57866 + -0.652602 -0.182232 0.560315 + -0.331901 0.826676 -0.733697 + 0.389425 0.420928 0.351843 + -0.312585 -0.346148 0.77823 ] diff --git a/tests/unit/igraph_layout_reingold_tilford_circular.c b/tests/unit/igraph_layout_reingold_tilford_circular.c new file mode 100644 index 0000000..846445f --- /dev/null +++ b/tests/unit/igraph_layout_reingold_tilford_circular.c @@ -0,0 +1,116 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void chop_print_destroy(igraph_matrix_t *result) { + matrix_chop(result, 1e-10); + print_matrix(result); + igraph_matrix_destroy(result); +} + +int main(void) { + igraph_t g; + igraph_matrix_t result; + igraph_vector_int_t roots, rootlevel; + + printf("Empty graph check:\n"); + igraph_small(&g, 0, 0, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_ALL, /*roots*/ NULL, /*rootlevel*/ NULL) == IGRAPH_SUCCESS); + chop_print_destroy(&result); + igraph_destroy(&g); + + printf("Singleton graph check:\n"); + igraph_small(&g, 1, 0, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_ALL, /*roots*/ NULL, /*rootlevel*/ NULL) == IGRAPH_SUCCESS); + chop_print_destroy(&result); + igraph_destroy(&g); + + printf("Star graph check with given root:\n"); + igraph_small(&g, 5, 1, 0,1, 0,2, 0,3, 0,4, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_int_init_int(&roots, 1, 1); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_OUT, &roots, /*rootlevel*/ NULL) == IGRAPH_SUCCESS); + chop_print_destroy(&result); + igraph_destroy(&g); + igraph_vector_int_destroy(&roots); + + printf("Star graph check with root found by topological sort:\n"); + igraph_small(&g, 5, 1, 1,0, 2,0, 3,0, 4,0, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_IN, NULL, /*rootlevel*/ NULL) == IGRAPH_SUCCESS); + chop_print_destroy(&result); + igraph_destroy(&g); + + printf("Two minitrees without rootlevel:\n"); + igraph_small(&g, 6, 1, 0,1, 0,2, 3,4, 3,5, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_int_init_int(&roots, 2, 0, 3); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_OUT, &roots, NULL) == IGRAPH_SUCCESS); + chop_print_destroy(&result); + igraph_destroy(&g); + igraph_vector_int_destroy(&roots); + + printf("Two minitrees with rootlevel 10 and 20:\n"); + igraph_small(&g, 6, 1, 0,1, 0,2, 3,4, 3,5, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_int_init_int(&roots, 2, 0, 3); + igraph_vector_int_init_int(&rootlevel, 2, 10, 20); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_OUT, &roots, &rootlevel) == IGRAPH_SUCCESS); + chop_print_destroy(&result); + igraph_destroy(&g); + igraph_vector_int_destroy(&roots); + igraph_vector_int_destroy(&rootlevel); + + printf("Graph with just loops, triple edges and disconnected vertices:\n"); + igraph_small(&g, 5, 1, 0,0, 0,0, 0,0, 1,2, 1,2, 1,2, -1); + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_ALL, NULL, /*rootlevel*/ NULL) == IGRAPH_SUCCESS); + chop_print_destroy(&result); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Checking proper error handling:\n"); + printf("Giving negative root.\n"); + igraph_small(&g, 5, 1, 0,1, 0,2, 0,3, 0,4, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_int_init_int(&roots, 1, -1); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_OUT, &roots, /*rootlevel*/ NULL) == IGRAPH_EINVVID); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + igraph_vector_int_destroy(&roots); + + printf("Giving negative rootlevel.\n"); + igraph_small(&g, 6, 1, 0,1, 0,2, 3,4, 3,5, -1); + igraph_matrix_init(&result, 0, 0); + igraph_vector_int_init_int(&roots, 2, 0, 3); + igraph_vector_int_init_int(&rootlevel, 2, -10, -20); + IGRAPH_ASSERT(igraph_layout_reingold_tilford_circular(&g, &result, IGRAPH_OUT, &roots, &rootlevel) == IGRAPH_EINVAL); + igraph_matrix_destroy(&result); + igraph_destroy(&g); + igraph_vector_int_destroy(&roots); + igraph_vector_int_destroy(&rootlevel); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_reingold_tilford_circular.out b/tests/unit/igraph_layout_reingold_tilford_circular.out new file mode 100644 index 0000000..1c2957a --- /dev/null +++ b/tests/unit/igraph_layout_reingold_tilford_circular.out @@ -0,0 +1,39 @@ +Empty graph check: +[ 0-by-2 ] +Singleton graph check: +[ 0 0 ] +Star graph check with given root: +[ 1 0 + 0 0 + -0.104528 0.994522 + -0.978148 -0.207912 + 0.309017 -0.951057 ] +Star graph check with root found by topological sort: +[ 0 0 + 1 0 + -0.104528 0.994522 + -0.978148 -0.207912 + 0.309017 -0.951057 ] +Two minitrees without rootlevel: +[ 0.642788 0.766044 + 2 0 + -0.347296 1.96962 + -0.34202 -0.939693 + -1.87939 -0.68404 + 1 -1.73205 ] +Two minitrees with rootlevel 10 and 20: +[ 5.5 9.52628 + 12 0 + -6 10.3923 + -10.5 -18.1865 + -22 0 + 11 -19.0526 ] +Graph with just loops, triple edges and disconnected vertices: +[ 1 0 + -0.104528 0.994522 + -0.209057 1.98904 + -0.978148 -0.207912 + 0.309017 -0.951057 ] +Checking proper error handling: +Giving negative root. +Giving negative rootlevel. diff --git a/tests/unit/igraph_layout_reingold_tilford_extended.c b/tests/unit/igraph_layout_reingold_tilford_extended.c new file mode 100644 index 0000000..30cb5c1 --- /dev/null +++ b/tests/unit/igraph_layout_reingold_tilford_extended.c @@ -0,0 +1,56 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + FILE *f; + igraph_matrix_t coords; + /* igraph_integer_t i, n; */ + + f = fopen("igraph_layout_reingold_tilford_extended.in", "r"); + IGRAPH_ASSERT(f != NULL); + igraph_read_graph_edgelist(&g, f, 0, IGRAPH_DIRECTED); + igraph_matrix_init(&coords, 0, 0); + + igraph_layout_reingold_tilford(&g, &coords, IGRAPH_IN, 0, 0); + + /* + n=igraph_vcount(&g); + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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 +#include "test_utilities.h" + +void run_test(int n) { + igraph_t graph; + igraph_matrix_t layout; + + printf("\n%d-vertex graph:\n", n); + igraph_empty(&graph, n, IGRAPH_UNDIRECTED); + igraph_matrix_init(&layout, 0, 0); + igraph_layout_sphere(&graph, &layout); + IGRAPH_ASSERT(igraph_matrix_nrow(&layout) == igraph_vcount(&graph)); + IGRAPH_ASSERT(igraph_matrix_ncol(&layout) == 3); + igraph_matrix_zapsmall(&layout, 0 /* automatic */); + igraph_matrix_print(&layout); + igraph_matrix_destroy(&layout); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); +} + +int main(void) { + run_test(0); + run_test(1); + run_test(2); + run_test(11); + + return 0; +} diff --git a/tests/unit/igraph_layout_sphere.out b/tests/unit/igraph_layout_sphere.out new file mode 100644 index 0000000..8d59ed9 --- /dev/null +++ b/tests/unit/igraph_layout_sphere.out @@ -0,0 +1,22 @@ + +0-vertex graph: + +1-vertex graph: +0 0 -1 + +2-vertex graph: +0 0 -1 +0 0 1 + +11-vertex graph: + 0 0 -1 +-0.141614 0.583048 -0.8 +-0.799764 -0.0194193 -0.6 +-0.324757 -0.857049 -0.4 + 0.664718 -0.719826 -0.2 + 0.966323 0.257333 0 + 0.197259 0.959734 0.2 + -0.76198 0.5093 0.4 + -0.57566 -0.555532 0.6 + 0.506779 -0.321208 0.8 + 0 0 1 diff --git a/tests/unit/igraph_layout_star.c b/tests/unit/igraph_layout_star.c new file mode 100644 index 0000000..95a90f1 --- /dev/null +++ b/tests/unit/igraph_layout_star.c @@ -0,0 +1,70 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g, igraph_integer_t center, igraph_vector_int_t *order, igraph_error_t error) { + igraph_matrix_t result; + igraph_matrix_init(&result, 0, 0); + IGRAPH_ASSERT(igraph_layout_star(g, &result, center, order) == error); + if (error == IGRAPH_SUCCESS) { + matrix_chop(&result, 1e-13); + print_matrix(&result); + } + + igraph_matrix_destroy(&result); + igraph_destroy(g); + if(order) { + igraph_vector_int_destroy(order); + } + +} + +int main(void) { + igraph_t g; + igraph_vector_int_t order; + + printf("No vertices.\n"); + igraph_small(&g, 0, 0, -1); + print_and_destroy(&g, 0, NULL, IGRAPH_SUCCESS); + + printf("Star of 8 points and a center:\n"); + igraph_small(&g, 9, 0, -1); + print_and_destroy(&g, 0, NULL, IGRAPH_SUCCESS); + + printf("Star of 8 points and a center in reverse:\n"); + igraph_small(&g, 9, 0, -1); + igraph_vector_int_init_int(&order, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + print_and_destroy(&g, 0, &order, IGRAPH_SUCCESS); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Checking if negative center fails nicely.\n"); + igraph_small(&g, 9, 0, -1); + print_and_destroy(&g, -10, NULL, IGRAPH_EINVAL); + + printf("Checking if order out of range fails nicely.\n"); + igraph_small(&g, 9, 0, -1); + igraph_vector_int_init_int(&order, 9, -1, -1, -1, 10, 10, 10, 2, 1, 0); + print_and_destroy(&g, 0, &order, IGRAPH_EINVAL); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_layout_star.out b/tests/unit/igraph_layout_star.out new file mode 100644 index 0000000..ee75993 --- /dev/null +++ b/tests/unit/igraph_layout_star.out @@ -0,0 +1,24 @@ +No vertices. +[ 0-by-2 ] +Star of 8 points and a center: +[ 0 0 + 1 0 + 0.707107 0.707107 + 0 1 + -0.707107 0.707107 + -1 0 + -0.707107 -0.707107 + 0 -1 + 0.707107 -0.707107 ] +Star of 8 points and a center in reverse: +[ 0 0 + 0.707107 -0.707107 + 0 -1 + -0.707107 -0.707107 + -1 0 + -0.707107 0.707107 + 0 1 + 0.707107 0.707107 + 1 0 ] +Checking if negative center fails nicely. +Checking if order out of range fails nicely. diff --git a/tests/unit/igraph_layout_sugiyama.c b/tests/unit/igraph_layout_sugiyama.c new file mode 100644 index 0000000..89dc453 --- /dev/null +++ b/tests/unit/igraph_layout_sugiyama.c @@ -0,0 +1,101 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g, extd_g; + igraph_matrix_t coords; + igraph_vector_int_t edgelist, extd_edgelist, extd_to_orig_eids; + igraph_vector_int_t layers; + + igraph_matrix_init(&coords, 0, 0); + igraph_vector_int_init(&extd_to_orig_eids, 0); + + /* Layout on simple graph with predefined layers */ + igraph_vector_int_init_int_end(&layers, -1, 0, 1, 1, 2, 3, 3, 4, 4, 5, -1); + igraph_vector_int_init_int_end(&edgelist, -1, + 0, 1, 0, 2, 0, 3, 1, 2, 2, 2, 1, 4, 2, 5, 4, 6, 5, 7, 6, 8, 7, 8, + 3, 8, 8, 1, 8, 2, -1); + igraph_create(&g, &edgelist, 0, 1); + + igraph_layout_sugiyama(&g, &coords, 0, 0, &layers, + /* hgap = */ 1, + /* vgap = */ 1, + /* maxiter = */ 100, + /* weights = */ 0); + igraph_matrix_print(&coords); + printf("===\n"); + + /* Same, but this time also return the extended graph */ + igraph_layout_sugiyama(&g, &coords, &extd_g, &extd_to_orig_eids, &layers, + /* hgap = */ 1, + /* vgap = */ 1, + /* maxiter = */ 100, + /* weights = */ 0); + igraph_matrix_print(&coords); + printf("===\n"); + igraph_vector_int_init(&extd_edgelist, 0); + igraph_get_edgelist(&extd_g, &extd_edgelist, 0); + igraph_vector_int_print(&extd_edgelist); + igraph_vector_int_destroy(&extd_edgelist); + igraph_destroy(&extd_g); + printf("===\n"); + igraph_vector_int_print(&extd_to_orig_eids); + printf("===\n"); + + igraph_vector_int_destroy(&layers); + + /* Same, but with automatic layering */ + igraph_layout_sugiyama(&g, &coords, 0, 0, 0, + /* hgap = */ 1, + /* vgap = */ 1, + /* maxiter = */ 100, + /* weights = */ 0); + igraph_matrix_print(&coords); + printf("===\n"); + + /* Layering with gaps in it */ + igraph_vector_int_init_int_end(&layers, -1, 0, 2, 2, 4, 6, 6, 12, 12, 15, -1); + igraph_layout_sugiyama(&g, &coords, 0, 0, &layers, + /* hgap = */ 1, + /* vgap = */ 1, + /* maxiter = */ 100, + /* weights = */ 0); + igraph_matrix_print(&coords); + igraph_vector_int_destroy(&layers); + printf("===\n"); + + igraph_vector_int_destroy(&edgelist); + igraph_matrix_destroy(&coords); + igraph_vector_int_destroy(&extd_to_orig_eids); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_layout_sugiyama.out b/tests/unit/igraph_layout_sugiyama.out new file mode 100644 index 0000000..413279e --- /dev/null +++ b/tests/unit/igraph_layout_sugiyama.out @@ -0,0 +1,87 @@ +2.5 0 +0.5 1 +2.5 1 + 4 2 + 0 3 + 2 3 + 0 4 + 2 4 + 2 5 + 4 1 + 0 2 + 2 2 + 4 3 + 4 4 + 1 2 + 1 3 + 1 4 + 3 2 + 3 3 + 3 4 +=== +2.5 0 +0.5 1 +2.5 1 + 4 2 + 0 3 + 2 3 + 0 4 + 2 4 + 2 5 + 4 1 + 0 2 + 2 2 + 4 3 + 4 4 + 1 2 + 1 3 + 1 4 + 3 2 + 3 3 + 3 4 +=== +0 1 0 2 0 9 9 3 1 2 1 10 10 4 2 2 2 11 11 5 3 12 12 13 13 8 4 6 5 7 6 8 7 8 8 16 16 15 15 14 14 1 8 19 19 18 18 17 17 2 +=== +0 1 2 2 3 5 5 4 6 6 11 11 11 7 8 9 10 12 12 12 12 13 13 13 13 +=== +2.5 0 + 1 1 +2.5 2 + 4 1 + 0 2 + 2 3 + 0 3 + 2 4 + 2 5 +2.5 1 + 4 2 + 4 3 + 4 4 + 0 4 + 1 2 + 1 3 + 1 4 + 3 3 + 3 4 +=== +2.5 0 +0.5 2 +2.5 2 + 4 4 + 0 6 + 2 6 + 0 12 + 2 12 + 2 15 + 4 2 + 0 4 + 2 4 + 4 6 + 4 12 + 1 4 + 1 6 + 1 12 + 3 4 + 3 6 + 3 12 +=== diff --git a/tests/unit/igraph_layout_umap.c b/tests/unit/igraph_layout_umap.c new file mode 100644 index 0000000..d41183f --- /dev/null +++ b/tests/unit/igraph_layout_umap.c @@ -0,0 +1,332 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + + +/* Helper function, computes layout span of a subset of vertices */ +igraph_real_t get_layout_span(const igraph_matrix_t *layout, int i_start, int i_end) { + igraph_real_t dx, dy, dz, xmin, xmax, ymin, ymax, zmin, zmax, spanmax; + igraph_integer_t ndim = igraph_matrix_ncol(layout); + + if (i_start >= i_end) + return -1; + + xmin = xmax = MATRIX(*layout, i_start, 0); + ymin = ymax = MATRIX(*layout, i_start, 1); + if (ndim == 3) { + zmin = zmax = MATRIX(*layout, i_start, 2); + } + for (int i = i_start + 1; i < i_end; i++) { + xmin = fmin(xmin, MATRIX(*layout, i, 0)); + xmax = fmax(xmax, MATRIX(*layout, i, 0)); + ymin = fmin(ymin, MATRIX(*layout, i, 1)); + ymax = fmax(ymax, MATRIX(*layout, i, 1)); + if (ndim == 3) { + zmin = fmin(zmin, MATRIX(*layout, i, 2)); + zmax = fmax(zmax, MATRIX(*layout, i, 2)); + } + } + /* total span of the layout */ + dx = xmax - xmin; + dy = ymax - ymin; + spanmax = dx > dy ? dx : dy; + if (ndim == 3) { + dz = zmax - zmin; + spanmax = dz > spanmax ? dz : spanmax; + } + return spanmax; +} + + +void check_graph_largeunion( + const igraph_matrix_t *layout, + const igraph_vector_int_t *subgraph_sizes) { + + /* sizes of the full subgraphs are 50, 70, 90 */ + igraph_real_t distlim; + igraph_integer_t nrow = igraph_matrix_nrow(layout); + igraph_error_t err; + int nerr = 0; + igraph_integer_t nvertices = 0; + + /* each cluster should occupy no more than say 50% of the layout */ + distlim = 0.2 * get_layout_span(layout, 0, nrow); + for (int i = 0; i < igraph_vector_int_size(subgraph_sizes); i++) { + err = get_layout_span( + layout, nvertices, nvertices + VECTOR(*subgraph_sizes)[i]) > distlim; + nvertices += VECTOR(*subgraph_sizes)[i]; + if (err) { + nerr++; + printf("layout of subgraph #%d too wide\n", i); + } + } + if (nerr == 0) { + printf("UMAP layout of large graph seems fine.\n"); + } +} + + +void check_graph_twoclusters_weights( + const igraph_vector_t *weights, + const igraph_vector_t *distances) { + int nerr = 0; + igraph_integer_t i; + igraph_real_t weight; + igraph_integer_t nc = igraph_vector_size(weights); + igraph_integer_t nd; + + if (distances == NULL) { + for (i = 0; i < nc; i++) { + if (VECTOR(*weights)[i] != 1.0) { + printf("Connectivities with NULL distances should all be 1 or -1.\n"); + return; + } + } + + printf("Connectivities from NULL distances seem fine.\n"); + } else { + nd = igraph_vector_size(distances); + if (nd != nc) { + nerr++; + printf("Length of distances and weights must be equal.\n"); + return; + } + + for (i = 0; i < nc; i++) { + weight = VECTOR(*weights)[i]; + if (weight > 1.0) { + printf("Weights cannot be >1.0, found %f", weight); + return; + } else if (weight < 0.0) { + printf("Weights cannot be negative, found %f", weight); + return; + } + } + printf("Connectivities from distances seem fine.\n"); + } + return; +} + + +void check_graph_twoclusters(const igraph_matrix_t *layout) { + /* 4 vertices (0-3), 2 articulation points (4-5), 4 vertices (6-9) */ + igraph_real_t xm, ym, zm, dx, dy, dz, dist, distmax; + igraph_integer_t ndim = igraph_matrix_ncol(layout); + + int nerr = 0; + + distmax = get_layout_span(layout, 0, 12); + + for (int iclu = 0; iclu < 8; iclu+= 7) { + xm = 0; + ym = 0; + zm = 0; + for (int i = iclu; i < iclu + 4; i++) { + xm += MATRIX(*layout, i, 0); + ym += MATRIX(*layout, i, 1); + if (ndim == 3) { + zm += MATRIX(*layout, i, 2); + } + } + xm /= 4; + ym /= 4; + zm /= 4; + for (int i = iclu; i < iclu + 4; i++) { + dx = MATRIX(*layout, i, 0) - xm; + dy = MATRIX(*layout, i, 1) - ym; + if (ndim == 3) { + dz = MATRIX(*layout, i, 2) - zm; + } + dist = (dx * dx) + (dy * dy); + if (ndim == 3) { + dist += (dz * dz); + } + dist = sqrt(dist); + + if (dist > 0.2 * distmax) { + printf("ERROR: UMAP cluster not compact!\n"); + printf("Cluster %d of 2, vertex %d, dx: %g, dy: %g, dist: %g, distmax: %g, d/dmax: %e\n", 1 + iclu / 7, i, dx, dy, dist, distmax, dist / distmax); + nerr++; + } + } + } + + if (nerr == 0) { + printf("UMAP layout seems fine.\n"); + } else { + printf("UMAP layout inconsistent:\nx\ty\n"); + igraph_matrix_print(layout); + } +} + + +void check_graph_singleton(const igraph_matrix_t *layout) { + igraph_integer_t nrows = igraph_matrix_nrow(layout); + igraph_integer_t ncols = igraph_matrix_ncol(layout); + igraph_integer_t nerr = 0; + + if (nrows != 1) { + printf("Singleton graph layout has %d rows instead of 1.\n", (int)nrows); + nerr++; + } + if (ncols != 2) { + printf("Singleton graph layout has %d cols instead of 2.\n", (int)ncols); + nerr++; + } + if ((fabs(MATRIX(*layout, 0, 0)) > 0.001) || (fabs(MATRIX(*layout, 0, 1)) > 0.001)) { + printf("Singleton graph layout is not (0,0): (%g,%g)\n", MATRIX(*layout, 0, 0), MATRIX(*layout, 0, 1)); + nerr++; + } + if (nerr == 0) { + printf("UMAP layout seems fine.\n"); + } +} + + +int main(void) { + igraph_t graph, empty_graph, singleton_graph; + igraph_vector_t distances, weights; + igraph_matrix_t layout; + igraph_t graph1, graph2, graph3; + igraph_vector_ptr_t graph_ptr; + igraph_vector_int_t subgraph_sizes; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_matrix_init(&layout, 0, 0); + + /* Check simple graphs (empty, singleton) */ + igraph_small(&empty_graph, 0, IGRAPH_UNDIRECTED, -1); + igraph_small(&singleton_graph, 1, IGRAPH_UNDIRECTED, -1); + + printf("Check error for negative min_dist.\n"); + CHECK_ERROR(igraph_layout_umap(&empty_graph, &layout, 0, NULL, -0.01, 500, 0), IGRAPH_EINVAL); + + printf("Check error for negative epochs.\n"); + CHECK_ERROR(igraph_layout_umap(&empty_graph, &layout, 0, NULL, 0.01, -1, 0), IGRAPH_EINVAL); + + printf("Check error for seed layout with wrong dimensions.\n"); + CHECK_ERROR(igraph_layout_umap(&empty_graph, &layout, 1, NULL, 0.01, 500, 0), IGRAPH_EINVAL); + + printf("Empty graph:\n"); + IGRAPH_ASSERT(igraph_layout_umap(&empty_graph, &layout, 0, NULL, 0.01, 500, 0) == IGRAPH_SUCCESS); + igraph_matrix_print(&layout); + + printf("Singleton graph:\n"); + IGRAPH_ASSERT(igraph_layout_umap(&singleton_graph, &layout, 0, NULL, 0.01, 500, 0) == IGRAPH_SUCCESS); + check_graph_singleton(&layout); + + printf("Singleton graph, use seed:\n"); + IGRAPH_ASSERT(igraph_layout_umap(&singleton_graph, &layout, 1, NULL, 0.01, 500, 0) == IGRAPH_SUCCESS); + check_graph_singleton(&layout); + + igraph_destroy(&singleton_graph); + igraph_destroy(&empty_graph); + + /* Check a small graph with two main groups of vertices */ + igraph_small(&graph, 12, IGRAPH_UNDIRECTED, + 0,1, 0,2, 0,3, 1,2, 1,3, 2,3, + 3,4, 4,5, 5,6, + 6,7, 7,8, 6,8, 7,9, 6,9, 8,9, 7,10, 8,10, 9,10, 10,11, 9,11, 8,11, 7,11, + -1); + igraph_vector_init_real(&distances, + igraph_ecount(&graph), + 0.1, 0.09, 0.12, 0.09, 0.1, 0.1, + 0.9, 0.9, 0.9, + 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.08, 0.05, 0.1, 0.08, 0.12, 0.09, 0.11 + ); + + printf("layout of two clusters of vertices with 2 articulation points:\n"); + IGRAPH_ASSERT(igraph_layout_umap(&graph, &layout, 0, &distances, 0.01, 500, 0) == IGRAPH_SUCCESS); + check_graph_twoclusters(&layout); + + printf("same graph, different epochs:\n"); + IGRAPH_ASSERT(igraph_layout_umap(&graph, &layout, 0, &distances, 0.0, 5000, 0) == IGRAPH_SUCCESS); + check_graph_twoclusters(&layout); + + printf("Same graph, no distances:\n"); + IGRAPH_ASSERT(igraph_layout_umap(&graph, &layout, 0, NULL, 0.0, 500, 0) == IGRAPH_SUCCESS); + check_graph_twoclusters(&layout); + igraph_matrix_resize(&layout, 0, 0); + + printf("Same graph, 3D layout:\n"); + IGRAPH_ASSERT(igraph_layout_umap_3d(&graph, &layout, 0, NULL, 0.0, 500, 0) == IGRAPH_SUCCESS); + check_graph_twoclusters(&layout); + + printf("Same graph, custom initial layout:\n"); + igraph_layout_random(&graph, &layout); + IGRAPH_ASSERT(igraph_layout_umap(&graph, &layout, 1, NULL, 0.01, 500, 0) == IGRAPH_SUCCESS); + check_graph_twoclusters(&layout); + + printf("Same graph, just compute weights from NULL distances:\n"); + igraph_vector_init(&weights, 0); + igraph_layout_umap_compute_weights(&graph, NULL, &weights); + check_graph_twoclusters_weights(&weights, NULL); + + printf("Same graph, just compute weights from distances:\n"); + igraph_layout_umap_compute_weights(&graph, &distances, &weights); + check_graph_twoclusters_weights(&weights, &distances); + + printf("Same graph, precomputed weights:\n"); + IGRAPH_ASSERT(igraph_layout_umap(&graph, &layout, 0, &weights, 0.01, 500, 1) == IGRAPH_SUCCESS); + check_graph_twoclusters(&layout); + + igraph_matrix_destroy(&layout); + igraph_vector_destroy(&distances); + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + /* Check a larger graph with around 150 vertices, split in 3 main clusters */ + printf("Large disjoint union of 3 full graphs:\n"); + igraph_matrix_init(&layout, 0, 0); + igraph_vector_int_init(&subgraph_sizes, 3); + VECTOR(subgraph_sizes)[0] = 10; + VECTOR(subgraph_sizes)[1] = 50; + VECTOR(subgraph_sizes)[2] = 30; + // Make 3 full graphs + igraph_vector_ptr_init(&graph_ptr, 3); + igraph_full(&graph1, VECTOR(subgraph_sizes)[0], 0, 0); + VECTOR(graph_ptr)[0] = &graph1; + igraph_full(&graph2, VECTOR(subgraph_sizes)[1], 0, 0); + VECTOR(graph_ptr)[1] = &graph2; + igraph_full(&graph3, VECTOR(subgraph_sizes)[2], 0, 0); + VECTOR(graph_ptr)[2] = &graph3; + // Get the disjoint union + igraph_disjoint_union_many(&graph, &graph_ptr); + // Call UMAP + IGRAPH_ASSERT(igraph_layout_umap( + &graph, &layout, 0, NULL, 0.0, 50, 0) == IGRAPH_SUCCESS); + // Check the layout, it should have three balls + check_graph_largeunion(&layout, &subgraph_sizes); + + // Destroy data structures + igraph_matrix_destroy(&layout); + igraph_destroy(&graph); + igraph_destroy(&graph3); + igraph_destroy(&graph2); + igraph_destroy(&graph1); + igraph_vector_int_destroy(&subgraph_sizes); + igraph_vector_ptr_destroy(&graph_ptr); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_layout_umap.out b/tests/unit/igraph_layout_umap.out new file mode 100644 index 0000000..9ed2821 --- /dev/null +++ b/tests/unit/igraph_layout_umap.out @@ -0,0 +1,26 @@ +Check error for negative min_dist. +Check error for negative epochs. +Check error for seed layout with wrong dimensions. +Empty graph: +Singleton graph: +UMAP layout seems fine. +Singleton graph, use seed: +UMAP layout seems fine. +layout of two clusters of vertices with 2 articulation points: +UMAP layout seems fine. +same graph, different epochs: +UMAP layout seems fine. +Same graph, no distances: +UMAP layout seems fine. +Same graph, 3D layout: +UMAP layout seems fine. +Same graph, custom initial layout: +UMAP layout seems fine. +Same graph, just compute weights from NULL distances: +Connectivities from NULL distances seem fine. +Same graph, just compute weights from distances: +Connectivities from distances seem fine. +Same graph, precomputed weights: +UMAP layout seems fine. +Large disjoint union of 3 full graphs: +UMAP layout of large graph seems fine. diff --git a/tests/unit/igraph_le_community_to_membership.c b/tests/unit/igraph_le_community_to_membership.c new file mode 100644 index 0000000..641d216 --- /dev/null +++ b/tests/unit/igraph_le_community_to_membership.c @@ -0,0 +1,111 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_vector_int_t *membership, igraph_vector_int_t *csize, igraph_matrix_int_t *mat) { + printf("Membership: "); + igraph_vector_int_print(membership); + printf("Csize: "); + igraph_vector_int_print(csize); + printf("\n"); + igraph_vector_int_destroy(membership); + igraph_matrix_int_destroy(mat); +} + +int main(void) { + igraph_matrix_int_t merges; + igraph_vector_int_t membership; + igraph_vector_int_t csize; + + igraph_vector_int_init_int(&csize, 0); + + printf("One member:\n"); + igraph_vector_int_init_int(&membership, 1, 0); + igraph_matrix_int_init(&merges, 0, 2); + IGRAPH_ASSERT(igraph_le_community_to_membership(&merges, /*steps*/ 0, &membership, &csize) == IGRAPH_SUCCESS); + print_and_destroy(&membership, &csize, &merges); + + printf("Five singleton clusters, one merge:\n"); + igraph_vector_int_init_int(&membership, 5, 0, 1, 2, 3, 4); + { + int elem[] = {1, 3}; + matrix_int_init_int_row_major(&merges, 1, 2, elem); + } + IGRAPH_ASSERT(igraph_le_community_to_membership(&merges, /*steps*/ 1, &membership, &csize) == IGRAPH_SUCCESS); + print_and_destroy(&membership, &csize, &merges); + + printf("Six clusters, two merges:\n"); + igraph_vector_int_init_int(&membership, 12, 0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5); + { + int elem[] = {0,3, 2,5}; + matrix_int_init_int_row_major(&merges, 2, 2, elem); + } + IGRAPH_ASSERT(igraph_le_community_to_membership(&merges, /*steps*/ 2, &membership, &csize) == IGRAPH_SUCCESS); + print_and_destroy(&membership, &csize, &merges); + + + VERIFY_FINALLY_STACK(); + printf("These should fail nicely:\n"); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Five singleton clusters, two merges, but second merge refers to singleton which is already merged.\n"); + igraph_vector_int_init_int(&membership, 5, 0, 1, 2, 3, 4); + { + int elem[] = {1,3, 1,4,}; + matrix_int_init_int_row_major(&merges, 2, 2, elem); + } + IGRAPH_ASSERT(igraph_le_community_to_membership(&merges, /*steps*/ 2, &membership, &csize) == IGRAPH_EINVAL); + igraph_vector_int_destroy(&membership); + igraph_matrix_int_destroy(&merges); + + printf("Negative cluster index.\n"); + igraph_vector_int_init_int(&membership, 5, -1, 0, 1, 2, 3); + { + int elem[] = {1,2, 3,4,}; + matrix_int_init_int_row_major(&merges, 2, 2, elem); + } + IGRAPH_ASSERT(igraph_le_community_to_membership(&merges, /*steps*/ 2, &membership, &csize) == IGRAPH_EINVAL); + igraph_vector_int_destroy(&membership); + igraph_matrix_int_destroy(&merges); + + printf("Skip a cluster index.\n"); + igraph_vector_int_init_int(&membership, 5, 0, 0, 2, 3, 4); + { + int elem[] = {1,2, 3,4,}; + matrix_int_init_int_row_major(&merges, 2, 2, elem); + } + IGRAPH_ASSERT(igraph_le_community_to_membership(&merges, /*steps*/ 2, &membership, &csize) == IGRAPH_EINVAL); + igraph_vector_int_destroy(&membership); + igraph_matrix_int_destroy(&merges); + + printf("Too many steps.\n"); + igraph_vector_int_init_int(&membership, 5, 0, 1, 2, 3, 4); + { + int elem[] = {1,2, 3,4,}; + matrix_int_init_int_row_major(&merges, 2, 2, elem); + } + IGRAPH_ASSERT(igraph_le_community_to_membership(&merges, /*steps*/ 20, &membership, &csize) == IGRAPH_EINVAL); + igraph_vector_int_destroy(&membership); + igraph_matrix_int_destroy(&merges); + + igraph_vector_int_destroy(&csize); + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_le_community_to_membership.out b/tests/unit/igraph_le_community_to_membership.out new file mode 100644 index 0000000..90f6849 --- /dev/null +++ b/tests/unit/igraph_le_community_to_membership.out @@ -0,0 +1,17 @@ +One member: +Membership: 0 +Csize: 1 + +Five singleton clusters, one merge: +Membership: 1 0 2 0 3 +Csize: 2 1 1 1 + +Six clusters, two merges: +Membership: 1 1 2 0 0 1 1 3 3 0 0 0 +Csize: 5 4 1 2 + +These should fail nicely: +Five singleton clusters, two merges, but second merge refers to singleton which is already merged. +Negative cluster index. +Skip a cluster index. +Too many steps. diff --git a/tests/unit/igraph_linegraph.c b/tests/unit/igraph_linegraph.c new file mode 100644 index 0000000..30f90dc --- /dev/null +++ b/tests/unit/igraph_linegraph.c @@ -0,0 +1,81 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g_start, g_line, g_test; + igraph_bool_t same; + + /* Undirected */ + igraph_small(&g_start, 7, IGRAPH_UNDIRECTED, + 0, 1, // 0 + 1, 2, // 1 + 1, 3, // 2 + 1, 3, // 3 + 2, 2, // 4 + 2, 4, // 5 + 3, 4, // 6 + 4, 5, // 7 + -1); + IGRAPH_ASSERT(igraph_linegraph(&g_start, &g_line) == IGRAPH_SUCCESS); + igraph_small(&g_test, 8, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 1, 4, 1, 4, 1, 5, 2, 3, 2, 3, + 2, 6, 3, 6, 4, 4, 4, 5, 4, 5, 5, 6, 5, 7, 6, 7, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&g_line, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g_start); + igraph_destroy(&g_line); + igraph_destroy(&g_test); + + /* Directed */ + igraph_small(&g_start, 7, IGRAPH_DIRECTED, + 0, 1, // 0 + 1, 2, // 1 + 1, 3, // 2 + 3, 1, // 3 + 2, 2, // 4 + 2, 4, // 5 + 3, 4, // 6 + 4, 5, // 7 + -1); + IGRAPH_ASSERT(igraph_linegraph(&g_start, &g_line) == IGRAPH_SUCCESS); + igraph_small(&g_test, 8, IGRAPH_DIRECTED, + 0, 1, 0, 2, 1, 4, 1, 5, 2, 3, 2, 6, 3, 1, 3, 2, 4, 4, 4, 5, + 5, 7, 6, 7, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&g_line, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g_start); + igraph_destroy(&g_line); + igraph_destroy(&g_test); + + /* No edges */ + igraph_small(&g_start, 7, IGRAPH_DIRECTED, -1); + IGRAPH_ASSERT(igraph_linegraph(&g_start, &g_line) == IGRAPH_SUCCESS); + igraph_small(&g_test, 0, IGRAPH_DIRECTED, -1); + IGRAPH_ASSERT(igraph_is_same_graph(&g_line, &g_test, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + igraph_destroy(&g_start); + igraph_destroy(&g_line); + igraph_destroy(&g_test); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_list_triangles.c b/tests/unit/igraph_list_triangles.c new file mode 100644 index 0000000..adc8d08 --- /dev/null +++ b/tests/unit/igraph_list_triangles.c @@ -0,0 +1,61 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph) { + igraph_vector_int_t result; + igraph_vector_int_init(&result, 0); + IGRAPH_ASSERT(igraph_list_triangles(graph, &result) == IGRAPH_SUCCESS); + print_vector_int(&result); + IGRAPH_ASSERT(igraph_vector_int_size(&result) % 3 == 0); + igraph_vector_int_destroy(&result); + printf("\n"); +} + + +int main(void) { + igraph_t g_0, g_1, g_5_full, g_lm; + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_full(&g_5_full, 5, 0, IGRAPH_NO_LOOPS); + igraph_small(&g_lm, 6, 1, 0,1, 0,1, 0,2, 0,2, 1,1, 1,2, 1,2, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + + + printf("No vertices:\n"); + call_and_print(&g_0); + + printf("One vertex:\n"); + call_and_print(&g_1); + + printf("Full graph of 5 vertices:\n"); + call_and_print(&g_5_full); + + printf("Graph with loops and multiple edges:\n"); + call_and_print(&g_lm); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_5_full); + igraph_destroy(&g_lm); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_list_triangles.out b/tests/unit/igraph_list_triangles.out new file mode 100644 index 0000000..359c7ab --- /dev/null +++ b/tests/unit/igraph_list_triangles.out @@ -0,0 +1,12 @@ +No vertices: +( ) + +One vertex: +( ) + +Full graph of 5 vertices: +( 0 1 4 0 1 2 0 1 3 0 2 4 0 2 3 0 3 4 1 2 4 1 2 3 1 3 4 2 3 4 ) + +Graph with loops and multiple edges: +( 1 2 0 1 2 3 ) + diff --git a/tests/unit/igraph_local_scan_k_ecount.c b/tests/unit/igraph_local_scan_k_ecount.c new file mode 100644 index 0000000..2ca2a9a --- /dev/null +++ b/tests/unit/igraph_local_scan_k_ecount.c @@ -0,0 +1,107 @@ +/* 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, int k, igraph_vector_t *weights, igraph_neimode_t mode) { + igraph_vector_t result; + igraph_vector_init(&result, 0); + IGRAPH_ASSERT(igraph_local_scan_k_ecount(graph, k, &result, weights, mode) == IGRAPH_SUCCESS); + print_vector(&result); + igraph_vector_destroy(&result); + printf("\n"); +} + + +int main(void) { + igraph_t g_0, g_1, g_lmu, g_lm, g_lm_nl; + igraph_vector_t weights, result; + + igraph_vector_init_real(&weights, 8, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6); + igraph_vector_init(&result, 0); + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_lmu, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); //undirected + igraph_small(&g_lm_nl, 6, 1, 0,1, 0,2, 1,3, 2,0, 2,3, 3,4, 3,4, -1); // no loop + + printf("No vertices:\n"); + call_and_print(&g_0, 2, NULL, IGRAPH_ALL); + + printf("One vertex:\n"); + call_and_print(&g_1, 2, NULL, IGRAPH_ALL); + + printf("Directed disconnected graph with loops and multiple edges, no weights, k = 0, IGRAPH_IN:\n"); + call_and_print(&g_lm, 0, NULL, IGRAPH_IN); + + printf("Same graph, k=1:\n"); + call_and_print(&g_lm, 1, NULL, IGRAPH_IN); + + printf("Same graph, k=1, IGRAPH_ALL:\n"); + call_and_print(&g_lm, 1, NULL, IGRAPH_ALL); + + printf("Same graph, without loops, k=1:\n"); + call_and_print(&g_lm_nl, 1, NULL, IGRAPH_IN); + + printf("Same graph with loop, k=1, undirected:\n"); + call_and_print(&g_lmu, 1, NULL, IGRAPH_IN); + + printf("Same graph, weighted:\n"); + call_and_print(&g_lmu, 1, &weights, IGRAPH_IN); + + printf("Checking if calling igraph_local_scan_1_ecount properly redirects:\n"); + igraph_vector_clear(&result); + printf("Directed, IN: "); + IGRAPH_ASSERT(igraph_local_scan_1_ecount(&g_lm, &result, NULL, IGRAPH_IN) == IGRAPH_SUCCESS); + print_vector(&result); + printf("Directed, ALL: "); + IGRAPH_ASSERT(igraph_local_scan_1_ecount(&g_lm, &result, NULL, IGRAPH_ALL) == IGRAPH_SUCCESS); + print_vector(&result); + printf("Undirected, with weights: "); + IGRAPH_ASSERT(igraph_local_scan_1_ecount(&g_lmu, &result, &weights, IGRAPH_IN) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("Same graph, directed, k=2:\n"); + call_and_print(&g_lm, 2, NULL, IGRAPH_IN); + + printf("Same graph, undirected, k=2:\n"); + call_and_print(&g_lmu, 2, NULL, IGRAPH_IN); + + printf("Same graph, weighted:\n"); + call_and_print(&g_lmu, 2, &weights, IGRAPH_IN); + + VERIFY_FINALLY_STACK(); + + printf("Wrong size weights.\n"); + igraph_vector_clear(&weights); + CHECK_ERROR(igraph_local_scan_k_ecount(&g_lmu, 3, &result, &weights, IGRAPH_ALL), IGRAPH_EINVAL); + + printf("Negative k.\n"); + CHECK_ERROR(igraph_local_scan_k_ecount(&g_lmu, -3, &result, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_lmu); + igraph_destroy(&g_lm); + igraph_destroy(&g_lm_nl); + igraph_vector_destroy(&weights); + igraph_vector_destroy(&result); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_local_scan_k_ecount.out b/tests/unit/igraph_local_scan_k_ecount.out new file mode 100644 index 0000000..5cc8fce --- /dev/null +++ b/tests/unit/igraph_local_scan_k_ecount.out @@ -0,0 +1,40 @@ +No vertices: +( ) + +One vertex: +( 0 ) + +Directed disconnected graph with loops and multiple edges, no weights, k = 0, IGRAPH_IN: +( 1 2 1 2 2 0 ) + +Same graph, k=1: +( 2 2 2 3 2 0 ) + +Same graph, k=1, IGRAPH_ALL: +( 4 3 3 5 2 0 ) + +Same graph, without loops, k=1: +( 2 1 2 2 2 0 ) + +Same graph with loop, k=1, undirected: +( 4 3 3 5 2 0 ) + +Same graph, weighted: +( 0.3 0.2 0.7 1.8 1.1 0 ) + +Checking if calling igraph_local_scan_1_ecount properly redirects: +Directed, IN: ( 2 2 2 3 2 0 ) +Directed, ALL: ( 4 3 3 5 2 0 ) +Undirected, with weights: ( 0.3 0.2 0.7 1.8 1.1 0 ) + +Same graph, directed, k=2: +( 2 4 2 6 5 0 ) + +Same graph, undirected, k=2: +( 6 8 8 8 5 0 ) + +Same graph, weighted: +( 0.9 2 2 2 1.8 0 ) + +Wrong size weights. +Negative k. diff --git a/tests/unit/igraph_local_scan_k_ecount_them.c b/tests/unit/igraph_local_scan_k_ecount_them.c new file mode 100644 index 0000000..2d2b83a --- /dev/null +++ b/tests/unit/igraph_local_scan_k_ecount_them.c @@ -0,0 +1,135 @@ +/* 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *us, igraph_t *them, int k, igraph_vector_t *weights, igraph_neimode_t mode) { + igraph_vector_t result; + igraph_vector_init(&result, 0); + IGRAPH_ASSERT(igraph_local_scan_k_ecount_them(us, them, k, &result, weights, mode) == IGRAPH_SUCCESS); + print_vector(&result); + igraph_vector_destroy(&result); + printf("\n"); +} + + +int main(void) { + igraph_t g_0, g_1, g_lmu, g_lm, g_lm_nl, g_6, g_6_1, g_6_full; + igraph_vector_t weights, result; + + igraph_vector_init_real(&weights, 8, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6); + igraph_vector_init(&result, 0); + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_6, 6, 1, -1); + igraph_small(&g_6_1, 6, 1, 0,1, -1); + igraph_full(&g_6_full, 6, 1, 0); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_lmu, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); //undirected + igraph_small(&g_lm_nl, 6, 1, 0,1, 0,2, 1,3, 2,0, 2,3, 3,4, 3,4, -1); // no loop + + printf("First some tests where us and them are equal:\n"); + printf("No vertices:\n"); + call_and_print(&g_0, &g_0, 2, NULL, IGRAPH_ALL); + + printf("One vertex:\n"); + call_and_print(&g_1, &g_1, 2, NULL, IGRAPH_ALL); + + printf("Directed disconnected graph with loops and multiple edges, no weights, k = 0, IGRAPH_IN:\n"); + call_and_print(&g_lm, &g_lm, 0, NULL, IGRAPH_IN); + + printf("Same graph, with weights:\n"); + call_and_print(&g_lm, &g_lm, 0, &weights, IGRAPH_IN); + + printf("Same graph, k=1:\n"); + call_and_print(&g_lm, &g_lm, 1, NULL, IGRAPH_IN); + + printf("Same graph, k=1, IGRAPH_ALL:\n"); + call_and_print(&g_lm, &g_lm, 1, NULL, IGRAPH_ALL); + + printf("Same graph, without loops, k=1:\n"); + call_and_print(&g_lm_nl, &g_lm_nl, 1, NULL, IGRAPH_IN); + + printf("Same graph with loop, k=1, undirected:\n"); + call_and_print(&g_lmu, &g_lmu, 1, NULL, IGRAPH_IN); + + printf("Same graph, directed, k=2:\n"); + call_and_print(&g_lm, &g_lm, 2, NULL, IGRAPH_IN); + + printf("Same graph, undirected, k=2:\n"); + call_and_print(&g_lmu, &g_lmu, 2, NULL, IGRAPH_IN); + + printf("Same graph, weighted:\n"); + call_and_print(&g_lmu, &g_lmu, 2, &weights, IGRAPH_IN); + + printf("Now some tests where us and them are not equal:\n"); + printf("Us = same graph, them = edgless, directed, k=1, " + "should show 0, because there are no edges:\n"); + call_and_print(&g_lm, &g_6, 1, NULL, IGRAPH_IN); + + printf("Switched us and them, " + "should show only 1 at the loop:\n"); + call_and_print(&g_6, &g_lm, 1, NULL, IGRAPH_IN); + + printf("Us = same graph, them = only edge from 0 to 1, " + "directed, k=1, IGRAPH_IN, " + "should show edge from 0 to 1:\n"); + call_and_print(&g_lm, &g_6_1, 1, NULL, IGRAPH_IN); + + printf("Switched us and them, " + "should show edge from 0 to 1 and loop:\n"); + call_and_print(&g_6_1, &g_lm, 1, NULL, IGRAPH_IN); + + printf("Us = same graph, them = full graph, " + "directed, k=3, IGRAPH_ALL, " + "should show 4*5=20 edges for the connected part:\n"); + call_and_print(&g_lm, &g_6_full, 3, NULL, IGRAPH_ALL); + + printf("Switched us and them, " + "should show 8 edges for the whole graph:\n"); + call_and_print(&g_6_full, &g_lm, 3, NULL, IGRAPH_ALL); + VERIFY_FINALLY_STACK(); + + printf("Check error handling:\n"); + printf("Wrong size weights.\n"); + igraph_vector_clear(&weights); + CHECK_ERROR(igraph_local_scan_k_ecount_them(&g_lmu, &g_lmu, 3, &result, &weights, IGRAPH_ALL), IGRAPH_EINVAL); + + printf("Negative k.\n"); + CHECK_ERROR(igraph_local_scan_k_ecount_them(&g_lmu, &g_lmu, -3, &result, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + + printf("Number of vertices in us and them not equal.\n"); + CHECK_ERROR(igraph_local_scan_k_ecount_them(&g_lmu, &g_1, 3, &result, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + + printf("Directedness in us and them not equal.\n"); + CHECK_ERROR(igraph_local_scan_k_ecount_them(&g_lmu, &g_lm, 3, &result, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_lmu); + igraph_destroy(&g_lm); + igraph_destroy(&g_lm_nl); + igraph_destroy(&g_6); + igraph_destroy(&g_6_1); + igraph_destroy(&g_6_full); + igraph_vector_destroy(&weights); + igraph_vector_destroy(&result); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_local_scan_k_ecount_them.out b/tests/unit/igraph_local_scan_k_ecount_them.out new file mode 100644 index 0000000..3a9af81 --- /dev/null +++ b/tests/unit/igraph_local_scan_k_ecount_them.out @@ -0,0 +1,58 @@ +First some tests where us and them are equal: +No vertices: +( ) + +One vertex: +( 0 ) + +Directed disconnected graph with loops and multiple edges, no weights, k = 0, IGRAPH_IN: +( 1 2 1 2 2 0 ) + +Same graph, with weights: +( 0.3 0 0 0.6 1.1 0 ) + +Same graph, k=1: +( 2 2 2 3 2 0 ) + +Same graph, k=1, IGRAPH_ALL: +( 4 3 3 5 2 0 ) + +Same graph, without loops, k=1: +( 2 1 2 2 2 0 ) + +Same graph with loop, k=1, undirected: +( 4 3 3 5 2 0 ) + +Same graph, directed, k=2: +( 2 4 2 6 5 0 ) + +Same graph, undirected, k=2: +( 6 8 8 8 5 0 ) + +Same graph, weighted: +( 0.9 2 2 2 1.8 0 ) + +Now some tests where us and them are not equal: +Us = same graph, them = edgless, directed, k=1, should show 0, because there are no edges: +( 0 0 0 0 0 0 ) + +Switched us and them, should show only 1 at the loop: +( 0 1 0 0 0 0 ) + +Us = same graph, them = only edge from 0 to 1, directed, k=1, IGRAPH_IN, should show edge from 0 to 1: +( 0 1 0 0 0 0 ) + +Switched us and them, should show edge from 0 to 1 and loop: +( 0 2 0 0 0 0 ) + +Us = same graph, them = full graph, directed, k=3, IGRAPH_ALL, should show 4*5=20 edges for the connected part: +( 20 20 20 20 20 0 ) + +Switched us and them, should show 8 edges for the whole graph: +( 8 8 8 8 8 8 ) + +Check error handling: +Wrong size weights. +Negative k. +Number of vertices in us and them not equal. +Directedness in us and them not equal. diff --git a/tests/unit/igraph_local_scan_subset_ecount.c b/tests/unit/igraph_local_scan_subset_ecount.c new file mode 100644 index 0000000..a326356 --- /dev/null +++ b/tests/unit/igraph_local_scan_subset_ecount.c @@ -0,0 +1,101 @@ +/* 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, igraph_vector_t *weights, igraph_vector_int_list_t *subsets) { + igraph_vector_t result; + igraph_vector_init(&result, 0); + IGRAPH_ASSERT(igraph_local_scan_subset_ecount(graph, &result, weights, subsets) == IGRAPH_SUCCESS); + print_vector(&result); + igraph_vector_destroy(&result); + printf("\n"); +} + + +int main(void) { + igraph_t g_0, g_1, g_lmu, g_lm; + igraph_vector_t weights, result; + igraph_vector_int_list_t subsets; + igraph_vector_int_t n1; + + igraph_vector_init_real(&weights, 8, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6); + igraph_vector_init(&result, 0); + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_lmu, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); //undirected + + igraph_vector_int_list_init(&subsets, 0); + + + printf("No vertices:\n"); + call_and_print(&g_0, NULL, &subsets); + + printf("one vertex, empty subset:\n"); + igraph_vector_int_init(&n1, 0); + igraph_vector_int_list_push_back(&subsets, &n1); + call_and_print(&g_1, NULL, &subsets); + + printf("Graph with unconnected vertices, loops and multiple edges, empty subsets:\n"); + for (int i = 0; i < 5; i ++) { + igraph_vector_int_init_int(&n1, 0); + igraph_vector_int_list_push_back(&subsets, &n1); + } + call_and_print(&g_lm, NULL, &subsets); + igraph_vector_int_list_clear(&subsets); + + printf("Same graph, every vertex as a subset:\n"); + for (int i = 0; i < 6; i ++) { + igraph_vector_int_init_int(&n1, 1, i); + igraph_vector_int_list_push_back(&subsets, &n1); + } + call_and_print(&g_lm, NULL, &subsets); + igraph_vector_int_list_clear(&subsets); + + printf("Same graph, every vertex and next one as a subset:\n"); + for (int i = 0; i < 6; i ++) { + igraph_vector_int_init_int(&n1, 2, i, (i + 1) % 6); + igraph_vector_int_list_push_back(&subsets, &n1); + } + call_and_print(&g_lm, NULL, &subsets); + + printf("Same situation, but with weights:\n"); + call_and_print(&g_lm, &weights, &subsets); + + printf("Same situation, no weights, but undirected graph:\n"); + call_and_print(&g_lmu, NULL, &subsets); + igraph_vector_int_list_clear(&subsets); + + printf("Same graph, whole graph as subset:\n"); + igraph_vector_int_init_int(&n1, 6, 0, 1, 2, 3, 4, 5); + igraph_vector_int_list_push_back(&subsets, &n1); + call_and_print(&g_lmu, NULL, &subsets); + igraph_vector_int_list_destroy(&subsets); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_lm); + igraph_destroy(&g_lmu); + igraph_vector_destroy(&weights); + igraph_vector_destroy(&result); + igraph_vector_int_list_destroy(&subsets); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_local_scan_subset_ecount.out b/tests/unit/igraph_local_scan_subset_ecount.out new file mode 100644 index 0000000..6d3efef --- /dev/null +++ b/tests/unit/igraph_local_scan_subset_ecount.out @@ -0,0 +1,24 @@ +No vertices: +( ) + +one vertex, empty subset: +( 0 ) + +Graph with unconnected vertices, loops and multiple edges, empty subsets: +( 0 0 0 0 0 0 ) + +Same graph, every vertex as a subset: +( 0 1 0 0 0 0 ) + +Same graph, every vertex and next one as a subset: +( 2 1 1 2 0 0 ) + +Same situation, but with weights: +( 0 0.1 0.4 1.1 0 0 ) + +Same situation, no weights, but undirected graph: +( 2 1 1 2 0 0 ) + +Same graph, whole graph as subset: +( 8 ) + diff --git a/tests/unit/igraph_local_transitivity.c b/tests/unit/igraph_local_transitivity.c new file mode 100644 index 0000000..fd554e1 --- /dev/null +++ b/tests/unit/igraph_local_transitivity.c @@ -0,0 +1,246 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +/* Compare the elements of two vectors for equality, handling NaN values. */ +igraph_bool_t vector_equal(const igraph_vector_t *v1, const igraph_vector_t *v2) { + igraph_integer_t n1 = igraph_vector_size(v1), n2 = igraph_vector_size(v2); + igraph_integer_t i; + + if (n1 != n2) { + return 0; + } + + for (i=0; i < n1; ++i) { + /* Since NaN == NaN compares false, we must handle NaN values early. */ + if (isnan(VECTOR(*v1)[i]) && isnan(VECTOR(*v2)[i])) { + continue; + } + if (VECTOR(*v1)[i] != VECTOR(*v2)[i]) { + return 0; + } + } + + return 1; +} + +/* Compute the average of a vector, ignoring NaN values. */ +igraph_real_t vector_avg(const igraph_vector_t *v) { + igraph_integer_t n = igraph_vector_size(v); + igraph_integer_t i; + igraph_real_t sum = 0.0, count; + + count = 0; + for (i=0; i < n; ++i) { + if (isnan(VECTOR(*v)[i])) { + continue; + } + sum += VECTOR(*v)[i]; + count += 1; + } + return sum / count; +} + +int main(void) { + + igraph_t g; + igraph_vector_t result1, result2, result3; + igraph_vs_t vertices; + igraph_real_t avg_local; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_init(&result1, 0); + igraph_vector_init(&result2, 0); + + /* igraph_transitivity_local_undirected() uses different code paths for: + * - all vertices + * - some vertices of graphs with >= 100 vertices + * - some vertices of graphs with < 100 vertices + * + * We test that these are consistent. + */ + + /* 100 vertices */ + + igraph_erdos_renyi_game_gnp(&g, 100, 0.1, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_vs_range(&vertices, 0, igraph_vcount(&g)); + + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), + IGRAPH_TRANSITIVITY_NAN); + igraph_transitivity_local_undirected(&g, &result2, vertices, + IGRAPH_TRANSITIVITY_NAN); + + IGRAPH_ASSERT(vector_equal(&result1, &result2)); + + igraph_vs_destroy(&vertices); + igraph_destroy(&g); + + /* 50 vertices */ + + igraph_erdos_renyi_game_gnp(&g, 50, 0.3, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_vs_range(&vertices, 0, igraph_vcount(&g)); + + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), + IGRAPH_TRANSITIVITY_NAN); + igraph_transitivity_local_undirected(&g, &result2, vertices, + IGRAPH_TRANSITIVITY_NAN); + + IGRAPH_ASSERT(vector_equal(&result1, &result2)); + + igraph_vs_destroy(&vertices); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + /* Zachary karate club */ + + printf("Zachary karate club network:\n"); + igraph_famous(&g, "Zachary"); + + printf("IGRAPH_TRANSITIVITY_ZERO:\n"); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_ZERO); + print_vector(&result1); + + printf("IGRAPH_TRANSITIVITY_NAN:\n"); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + + igraph_destroy(&g); + + /* Small graphs */ + + printf("\nNull graph:\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + igraph_transitivity_avglocal_undirected(&g, &avg_local, IGRAPH_TRANSITIVITY_NAN); + IGRAPH_ASSERT(isnan(avg_local)); + igraph_transitivity_avglocal_undirected(&g, &avg_local, IGRAPH_TRANSITIVITY_ZERO); + IGRAPH_ASSERT(avg_local == 0); + igraph_destroy(&g); + + printf("\nSingleton graph:\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + igraph_transitivity_avglocal_undirected(&g, &avg_local, IGRAPH_TRANSITIVITY_NAN); + IGRAPH_ASSERT(isnan(avg_local)); + igraph_transitivity_avglocal_undirected(&g, &avg_local, IGRAPH_TRANSITIVITY_ZERO); + IGRAPH_ASSERT(avg_local == 0); + igraph_destroy(&g); + + printf("\nTwo connected vertices:\n"); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + igraph_destroy(&g); + + printf("\nTriangle:\n"); + igraph_full(&g, 3, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + igraph_destroy(&g); + + printf("\nTwo-star:\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,2, 0,1, -1); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + igraph_destroy(&g); + + /* Multigraph */ + + printf("\nDirected and multigraphs:\n"); + + igraph_small(&g, 20, IGRAPH_DIRECTED, + 15, 12, 12, 10, 15, 0, 11, 10, 2, 8, 8, 6, 13, 17, 10, 10, 17, 2, 14, + 0, 16, 13, 14, 14, 0, 5, 6, 4, 0, 9, 0, 6, 10, 9, 16, 4, 14, 5, 17, + 15, 14, 9, 17, 17, 1, 4, 10, 16, 7, 0, 11, 12, 6, 13, 2, 17, 4, 0, 0, + 14, 4, 0, 6, 16, 16, 14, 13, 13, 12, 11, 3, 11, 11, 3, 6, 7, 4, 14, + 10, 8, 13, 7, 14, 2, 5, 2, 0, 14, 3, 15, 5, 5, 7, 2, 14, 15, 5, 10, + 10, 16, 7, 9, 14, 0, 15, 7, 13, 1, 15, 1, 4, 5, 4, 6, 16, 13, 6, 17, + 8, 6, 9, 3, 8, 6, 6, 14, 11, 14, 6, 10, 10, 5, 1, 0, 16, 17, 9, 1, 5, + 0, 5, 15, 8, 0, 0, 8, 5, 3, 9, 4, 13, 12, 11, 0, 11, 0, 10, 6, 4, 13, + 8, 9, 11, 11, 3, 16, 1, 2, 16, 0, 9, 8, 3, 8, 8, 7, 12, 10, 9, 3, 13, + 5, 3, 9, 6, 2, 11, 10, 1, 16, 0, 2, 10, 17, 16, 8, 11, 5, 13, 0, 19, 19, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1); + + igraph_vs_range(&vertices, 0, igraph_vcount(&g)); + + printf("\nDirected multi:\n"); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + + igraph_vector_init_copy(&result3, &result1); + + igraph_transitivity_local_undirected(&g, &result2, vertices, IGRAPH_TRANSITIVITY_NAN); + print_vector(&result2); + IGRAPH_ASSERT(vector_equal(&result2, &result3)); + + igraph_transitivity_avglocal_undirected(&g, &avg_local, IGRAPH_TRANSITIVITY_NAN); + printf("Average: %.10g == %.10g == %.10g\n", avg_local, vector_avg(&result1), vector_avg(&result2)); + IGRAPH_ASSERT(fabs(avg_local - vector_avg(&result1)) < 1e-14); + + printf("\nUndirected multi:\n"); + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_COLLAPSE, NULL); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + IGRAPH_ASSERT(vector_equal(&result1, &result3)); + igraph_transitivity_local_undirected(&g, &result2, vertices, IGRAPH_TRANSITIVITY_NAN); + print_vector(&result2); + IGRAPH_ASSERT(vector_equal(&result2, &result3)); + + igraph_transitivity_avglocal_undirected(&g, &avg_local, IGRAPH_TRANSITIVITY_NAN); + printf("Average: %.10g == %.10g == %.10g\n", avg_local, vector_avg(&result1), vector_avg(&result2)); + IGRAPH_ASSERT(fabs(avg_local - vector_avg(&result1)) < 1e-14); + + printf("\nUndirected simple:\n"); + igraph_simplify(&g, true, true, NULL); + igraph_transitivity_local_undirected(&g, &result1, igraph_vss_all(), IGRAPH_TRANSITIVITY_NAN); + print_vector(&result1); + IGRAPH_ASSERT(vector_equal(&result1, &result3)); + igraph_transitivity_local_undirected(&g, &result2, vertices, IGRAPH_TRANSITIVITY_NAN); + print_vector(&result2); + IGRAPH_ASSERT(vector_equal(&result2, &result3)); + + igraph_transitivity_avglocal_undirected(&g, &avg_local, IGRAPH_TRANSITIVITY_NAN); + printf("Average: %.10g == %.10g == %.10g\n", avg_local, vector_avg(&result1), vector_avg(&result2)); + IGRAPH_ASSERT(fabs(avg_local - vector_avg(&result1)) < 1e-14); + + igraph_vector_destroy(&result3); + igraph_vector_destroy(&result2); + igraph_vector_destroy(&result1); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_local_transitivity.out b/tests/unit/igraph_local_transitivity.out new file mode 100644 index 0000000..b484061 --- /dev/null +++ b/tests/unit/igraph_local_transitivity.out @@ -0,0 +1,37 @@ +Zachary karate club network: +IGRAPH_TRANSITIVITY_ZERO: +( 0.15 0.333333 0.244444 0.666667 0.666667 0.5 0.5 1 0.5 0 0.666667 0 1 0.6 1 1 1 1 1 0.333333 1 1 1 0.4 0.333333 0.333333 1 0.166667 0.333333 0.666667 0.5 0.2 0.19697 0.110294 ) +IGRAPH_TRANSITIVITY_NAN: +( 0.15 0.333333 0.244444 0.666667 0.666667 0.5 0.5 1 0.5 0 0.666667 NaN 1 0.6 1 1 1 1 1 0.333333 1 1 1 0.4 0.333333 0.333333 1 0.166667 0.333333 0.666667 0.5 0.2 0.19697 0.110294 ) + +Null graph: +( ) + +Singleton graph: +( NaN ) + +Two connected vertices: +( NaN NaN ) + +Triangle: +( 1 1 1 ) + +Two-star: +( 0 NaN NaN ) + +Directed and multigraphs: + +Directed multi: +( 0.474359 0.47619 0.428571 0.266667 0.642857 0.388889 0.533333 0.52381 0.535714 0.357143 0.285714 0.4 0.166667 0.416667 0.472222 0.214286 0.444444 0.4 NaN NaN ) +( 0.474359 0.47619 0.428571 0.266667 0.642857 0.388889 0.533333 0.52381 0.535714 0.357143 0.285714 0.4 0.166667 0.416667 0.472222 0.214286 0.444444 0.4 NaN NaN ) +Average: 0.4126407543 == 0.4126407543 == 0.4126407543 + +Undirected multi: +( 0.474359 0.47619 0.428571 0.266667 0.642857 0.388889 0.533333 0.52381 0.535714 0.357143 0.285714 0.4 0.166667 0.416667 0.472222 0.214286 0.444444 0.4 NaN NaN ) +( 0.474359 0.47619 0.428571 0.266667 0.642857 0.388889 0.533333 0.52381 0.535714 0.357143 0.285714 0.4 0.166667 0.416667 0.472222 0.214286 0.444444 0.4 NaN NaN ) +Average: 0.4126407543 == 0.4126407543 == 0.4126407543 + +Undirected simple: +( 0.474359 0.47619 0.428571 0.266667 0.642857 0.388889 0.533333 0.52381 0.535714 0.357143 0.285714 0.4 0.166667 0.416667 0.472222 0.214286 0.444444 0.4 NaN NaN ) +( 0.474359 0.47619 0.428571 0.266667 0.642857 0.388889 0.533333 0.52381 0.535714 0.357143 0.285714 0.4 0.166667 0.416667 0.472222 0.214286 0.444444 0.4 NaN NaN ) +Average: 0.4126407543 == 0.4126407543 == 0.4126407543 diff --git a/tests/unit/igraph_maxflow.c b/tests/unit/igraph_maxflow.c new file mode 100644 index 0000000..719dd9e --- /dev/null +++ b/tests/unit/igraph_maxflow.c @@ -0,0 +1,240 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int check_flow(int errorinc, + const igraph_t *graph, igraph_real_t flow_value, + const igraph_vector_t *flow, const igraph_vector_int_t *cut, + const igraph_vector_int_t *partition, + const igraph_vector_int_t *partition2, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity, + igraph_bool_t print) { + + igraph_integer_t i, n = igraph_vcount(graph), m = igraph_ecount(graph); + igraph_integer_t nc = igraph_vector_int_size(cut); + igraph_vector_int_t inedges, outedges; + igraph_bool_t directed = igraph_is_directed(graph); + igraph_real_t cutsize; + igraph_t graph_copy; + igraph_matrix_t sp; + igraph_vector_int_t cut_int; + + igraph_vector_int_init(&cut_int, 0); + + if (print) { + printf("flow value: %g\n", (double) flow_value); + printf("flow: "); + igraph_vector_print(flow); + printf("first partition: "); + igraph_vector_int_print(partition); + printf("second partition: "); + igraph_vector_int_print(partition2); + printf("edges in the cut: "); + for (i = 0; i < nc; i++) { + igraph_integer_t edge = VECTOR(*cut)[i]; + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO (graph, edge); + if (!directed && from > to) { + igraph_integer_t tmp = from; + from = to; + to = tmp; + } + printf("%" IGRAPH_PRId "-%" IGRAPH_PRId " (%g), ", from, to, VECTOR(*capacity)[edge]); + } + printf("\n"); + } + fflush(stdout); + + /* Always less than the capacity */ + for (i = 0; i < m; i++) { + if (VECTOR(*flow)[i] > VECTOR(*capacity)[i]) { + return errorinc + 3; + } + } + + /* What comes in goes out, but only in directed graphs, + there is no in and out in undirected ones... + */ + if (igraph_is_directed(graph)) { + igraph_vector_int_init(&inedges, 0); + igraph_vector_int_init(&outedges, 0); + + for (i = 0; i < n; i++) { + igraph_integer_t n1, n2, j; + igraph_real_t in_flow = 0.0, out_flow = 0.0; + igraph_incident(graph, &inedges, i, IGRAPH_IN); + igraph_incident(graph, &outedges, i, IGRAPH_OUT); + n1 = igraph_vector_int_size(&inedges); + n2 = igraph_vector_int_size(&outedges); + for (j = 0; j < n1; j++) { + igraph_integer_t e = VECTOR(inedges)[j]; + in_flow += VECTOR(*flow)[e]; + } + for (j = 0; j < n2; j++) { + igraph_integer_t e = VECTOR(outedges)[j]; + out_flow += VECTOR(*flow)[e]; + } + if (i == source) { + if (in_flow > 0) { + return errorinc + 4; + } + if (out_flow != flow_value) { + return errorinc + 5; + } + } else if (i == target) { + if (out_flow > 0) { + return errorinc + 6; + } + if (in_flow != flow_value) { + return errorinc + 7; + } + + } else { + if (in_flow != out_flow) { + return errorinc + 8; + } + } + } + + igraph_vector_int_destroy(&inedges); + igraph_vector_int_destroy(&outedges); + } + + /* Check the minimum cut size*/ + for (i = 0, cutsize = 0.0; i < nc; i++) { + igraph_integer_t edge = VECTOR(*cut)[i]; + cutsize += VECTOR(*capacity)[edge]; + } + if (fabs(cutsize - flow_value) > 1e-14) { + return errorinc + 9; + } + + /* Check that the cut indeed cuts */ + igraph_copy(&graph_copy, graph); + + n = igraph_vector_int_size(cut); + igraph_vector_int_resize(&cut_int, n); + for (i = 0; i < n; i++) { + VECTOR(cut_int)[i] = VECTOR(*cut)[i]; + } + igraph_delete_edges(&graph_copy, igraph_ess_vector(&cut_int)); + igraph_matrix_init(&sp, 1, 1); + igraph_distances(&graph_copy, &sp, /*from=*/ igraph_vss_1(source), + /*to=*/ igraph_vss_1(target), IGRAPH_OUT); + if (MATRIX(sp, 0, 0) != IGRAPH_INFINITY) { + return errorinc + 10; + } + igraph_matrix_destroy(&sp); + igraph_destroy(&graph_copy); + + igraph_vector_int_destroy(&cut_int); + + return 0; +} + +int main(void) { + + igraph_t g; + igraph_real_t flow_value; + igraph_vector_int_t cut; + igraph_vector_t capacity; + igraph_vector_int_t partition, partition2; + igraph_vector_t flow; + igraph_integer_t i, n; + igraph_integer_t source, target; + FILE *infile; + igraph_real_t flow_value2 = 0.0; + int check; + igraph_maxflow_stats_t stats; + + igraph_vector_init(&capacity, 0); + + /***************/ + infile = fopen("ak-4102.max", "r"); + igraph_read_graph_dimacs_flow( + &g, infile, 0, 0, &source, &target, &capacity, IGRAPH_DIRECTED + ); + fclose(infile); + + igraph_vector_int_init(&cut, 0); + igraph_vector_int_init(&partition, 0); + igraph_vector_int_init(&partition2, 0); + igraph_vector_init(&flow, 0); + + igraph_maxflow(&g, &flow_value, &flow, &cut, &partition, + &partition2, source, target, &capacity, &stats); + + if (flow_value != 8207) { + return 1; + } + + n = igraph_vector_int_size(&cut); + for (i = 0; i < n; i++) { + igraph_integer_t e = VECTOR(cut)[i]; + flow_value2 += VECTOR(capacity)[e]; + } + if (flow_value != flow_value2) { + return 2; + } + + /* Check the flow */ + if ( (check = check_flow(0, &g, flow_value, &flow, &cut, &partition, + &partition2, source, target, &capacity, + /*print=*/ 0))) { + return check; + } + + igraph_destroy(&g); + igraph_vector_destroy(&capacity); + igraph_vector_int_destroy(&cut); + igraph_vector_int_destroy(&partition); + igraph_vector_int_destroy(&partition2); + igraph_vector_destroy(&flow); + + /* ------------------------------------- */ + + igraph_small(&g, 4, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 3, -1); + igraph_vector_init_int_end(&capacity, -1, 4, 2, 10, 2, 2, -1); + igraph_vector_int_init(&cut, 0); + igraph_vector_int_init(&partition, 0); + igraph_vector_int_init(&partition2, 0); + igraph_vector_init(&flow, 0); + + igraph_maxflow(&g, &flow_value, &flow, &cut, &partition, &partition2, + /*source=*/ 0, /*target=*/ 3, &capacity, &stats); + + if ( (check = check_flow(20, &g, flow_value, &flow, &cut, &partition, + &partition2, 0, 3, &capacity, + /*print=*/ 1))) { + return check; + } + + igraph_vector_int_destroy(&cut); + igraph_vector_int_destroy(&partition2); + igraph_vector_int_destroy(&partition); + igraph_vector_destroy(&capacity); + igraph_vector_destroy(&flow); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_maxflow.out b/tests/unit/igraph_maxflow.out new file mode 100644 index 0000000..06da92d --- /dev/null +++ b/tests/unit/igraph_maxflow.out @@ -0,0 +1,5 @@ +flow value: 4 +flow: 4 0 2 2 2 +first partition: 0 1 2 +second partition: 3 +edges in the cut: 1-3 (2), 2-3 (2), diff --git a/tests/unit/igraph_maximal_cliques.c b/tests/unit/igraph_maximal_cliques.c new file mode 100644 index 0000000..ea38ea4 --- /dev/null +++ b/tests/unit/igraph_maximal_cliques.c @@ -0,0 +1,151 @@ +/* + IGraph library. + Copyright (C) 2023 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 +#include "test_utilities.h" + +#define NODES 1000 +#define CLIQUE_SIZE 10 +#define NO_CLIQUES 10 +#define INT(a) (igraph_rng_get_integer(igraph_rng_default(), 0, (a))) + +void permutation(igraph_vector_int_t *vec) { + igraph_integer_t i, r, tmp; + for (i = 0; i < CLIQUE_SIZE; i++) { + r = INT(NODES - 1); + tmp = VECTOR(*vec)[i]; + VECTOR(*vec)[i] = VECTOR(*vec)[r]; + VECTOR(*vec)[r] = tmp; + } +} + +int sort_cmp(const igraph_vector_int_t *a, const igraph_vector_int_t *b) { + igraph_integer_t i, alen = igraph_vector_int_size(a), blen = igraph_vector_int_size(b); + if (alen != blen) { + return (alen < blen) - (alen > blen); + } + for (i = 0; i < alen; i++) { + igraph_integer_t ea = VECTOR(*a)[i], eb = VECTOR(*b)[i]; + if (ea != eb) { + return (ea > eb) - (ea < eb); + } + } + return 0; +} + +void sort_cliques(igraph_vector_int_list_t *cliques) { + igraph_integer_t i, n = igraph_vector_int_list_size(cliques); + for (i = 0; i < n; i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); + igraph_vector_int_sort(v); + } + igraph_vector_int_list_sort(cliques, sort_cmp); +} + +void print_cliques(igraph_vector_int_list_t *cliques) { + igraph_integer_t i; + sort_cliques(cliques); + for (i = 0; i < igraph_vector_int_list_size(cliques); i++) { + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); + igraph_vector_int_print(v); + } +} + +int main(void) { + + igraph_t g, g2, cli; + igraph_vector_int_t perm; + igraph_vector_int_list_t cliques; + igraph_integer_t no; + igraph_integer_t i; + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Create a graph that has a random component, plus a number of + relatively small cliques */ + + igraph_vector_int_init_range(&perm, 0, NODES); + igraph_erdos_renyi_game_gnm(&g, NODES, NODES, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_full(&cli, CLIQUE_SIZE, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + for (i = 0; i < NO_CLIQUES; i++) { + /* Permute vertices of g */ + permutation(&perm); + igraph_permute_vertices(&g, &g2, &perm); + igraph_destroy(&g); + g = g2; + + /* Add a clique */ + igraph_union(&g2, &g, &cli, /*edge_map1=*/ NULL, /*edge_map2=*/ NULL); + igraph_destroy(&g); + g = g2; + } + igraph_simplify(&g, /*remove_multiple=*/ true, /*remove_loop=*/ false, /*edge_comb=*/ NULL); + + igraph_vector_int_destroy(&perm); + igraph_destroy(&cli); + + /* Find the maximal cliques */ + + igraph_vector_int_list_init(&cliques, 0); + igraph_maximal_cliques(&g, &cliques, /*min_size=*/ 3, + /*max_size=*/ 0 /*no limit*/); + igraph_maximal_cliques_count(&g, &no, /*min_size=*/ 3, + /*max_size=*/ 0 /*no limit*/); + + if (no != igraph_vector_int_list_size(&cliques)) { + return 1; + } + + /* Print them */ + + print_cliques(&cliques); + + /* Clean up */ + + igraph_vector_int_list_destroy(&cliques); + igraph_destroy(&g); + + /* Build a triangle with a loop (thanks to Emmanuel Navarro) */ + + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, 0, 0, -1); + + /* Find the maximal cliques */ + + igraph_vector_int_list_init(&cliques, 0); + igraph_maximal_cliques(&g, &cliques, /*min_size=*/ 3, + /*max_size=*/ 0 /*no limit*/); + igraph_maximal_cliques_count(&g, &no, /*min_size=*/ 3, + /*max_size=*/ 0 /*no limit*/); + + if (no != igraph_vector_int_list_size(&cliques)) { + return 2; + } + + /* Print them */ + + print_cliques(&cliques); + + /* Clean up */ + + igraph_vector_int_list_destroy(&cliques); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_maximal_cliques.out b/tests/unit/igraph_maximal_cliques.out new file mode 100644 index 0000000..53d6187 --- /dev/null +++ b/tests/unit/igraph_maximal_cliques.out @@ -0,0 +1,15 @@ +0 1 2 3 4 5 6 7 8 9 +0 1 2 5 6 7 9 64 209 582 +8 19 76 89 253 488 560 830 841 857 +12 65 127 209 250 398 485 587 815 901 +19 76 89 253 488 560 830 841 857 892 +33 152 181 296 343 801 896 903 967 988 +33 152 296 343 387 477 801 896 903 967 +62 65 146 469 585 630 849 901 979 995 +178 488 523 560 768 771 821 882 890 953 +488 560 841 890 +229 586 948 +279 547 863 +291 408 580 +466 552 925 +0 1 2 diff --git a/tests/unit/igraph_maximal_cliques2.c b/tests/unit/igraph_maximal_cliques2.c new file mode 100644 index 0000000..2a28984 --- /dev/null +++ b/tests/unit/igraph_maximal_cliques2.c @@ -0,0 +1,81 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +void sort_cliques(igraph_vector_int_list_t *cliques) { + igraph_integer_t i, n = igraph_vector_int_list_size(cliques); + for (i = 0; i < n; i++) { + igraph_vector_int_sort(igraph_vector_int_list_get_ptr(cliques, i)); + } + igraph_vector_int_list_sort(cliques, igraph_vector_int_lex_cmp); +} + +void print_and_destroy(igraph_vector_int_list_t *cliques) { + sort_cliques(cliques); + print_vector_int_list(cliques); + igraph_vector_int_list_destroy(cliques); +} + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t cliques; + igraph_integer_t no; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_ring(&graph, /*n=*/ 10, /*directed=*/ 0, + /*mutual=*/ 0, /*circular=*/ 1); + igraph_vector_int_list_init(&cliques, 0); + + igraph_maximal_cliques(&graph, &cliques, /*min_size=*/ 0, + /*max_size=*/ 0); + igraph_maximal_cliques_count(&graph, &no, /*min_size=*/ 0, + /*max_size=*/ 0 /*no limit*/); + IGRAPH_ASSERT(no == igraph_vector_int_list_size(&cliques)); + + print_and_destroy(&cliques); + igraph_destroy(&graph); + + printf("---\n"); + /* ----------------------------------------------------------- */ + + igraph_erdos_renyi_game_gnp(&graph, 50, 0.5, /*directed=*/ 0, /*loops=*/ 0); + + igraph_vector_int_list_init(&cliques, 0); + + igraph_maximal_cliques(&graph, &cliques, /*min_size=*/ 8, + /*max_size=*/ 0); + igraph_maximal_cliques_count(&graph, &no, /*min_size=*/ 8, + /*max_size=*/ 0 /*no limit*/); + IGRAPH_ASSERT(no == igraph_vector_int_list_size(&cliques)); + + print_and_destroy(&cliques); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_maximal_cliques2.out b/tests/unit/igraph_maximal_cliques2.out new file mode 100644 index 0000000..8be32cd --- /dev/null +++ b/tests/unit/igraph_maximal_cliques2.out @@ -0,0 +1,20 @@ +{ + 0: ( 0 1 ) + 1: ( 0 9 ) + 2: ( 1 2 ) + 3: ( 2 3 ) + 4: ( 3 4 ) + 5: ( 4 5 ) + 6: ( 5 6 ) + 7: ( 6 7 ) + 8: ( 7 8 ) + 9: ( 8 9 ) +} +--- +{ + 0: ( 6 15 22 23 27 33 40 44 ) + 1: ( 6 22 23 27 33 40 44 48 ) + 2: ( 13 15 22 24 27 33 40 44 ) + 3: ( 13 15 22 24 33 36 38 42 ) + 4: ( 15 22 24 29 33 36 38 42 ) +} diff --git a/tests/unit/igraph_maximal_cliques3.c b/tests/unit/igraph_maximal_cliques3.c new file mode 100644 index 0000000..5574b34 --- /dev/null +++ b/tests/unit/igraph_maximal_cliques3.c @@ -0,0 +1,60 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +void sort_cliques(igraph_vector_int_list_t *cliques) { + igraph_integer_t i, n = igraph_vector_int_list_size(cliques); + for (i = 0; i < n; i++) { + igraph_vector_int_sort(igraph_vector_int_list_get_ptr(cliques, i)); + } + igraph_vector_int_list_sort(cliques, igraph_vector_int_lex_cmp); +} + +void print_and_destroy(igraph_vector_int_list_t *cliques) { + sort_cliques(cliques); + print_vector_int_list(cliques); + igraph_vector_int_list_destroy(cliques); +} + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t cliques; + + igraph_rng_seed(igraph_rng_default(), 41); + igraph_erdos_renyi_game_gnp(&graph, 100, 0.7, /*directed=*/ 0, /*loops=*/ 0); + + igraph_vector_int_list_init(&cliques, 0); + + igraph_maximal_cliques(&graph, &cliques, /*min_size=*/ 15, + /*max_size=*/ 0); + + print_and_destroy(&cliques); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_maximal_cliques3.out b/tests/unit/igraph_maximal_cliques3.out new file mode 100644 index 0000000..d4fd9b0 --- /dev/null +++ b/tests/unit/igraph_maximal_cliques3.out @@ -0,0 +1,6 @@ +{ + 0: ( 1 6 28 29 35 45 51 58 65 69 78 79 81 92 98 ) + 1: ( 1 6 28 29 35 49 51 58 65 69 78 79 81 92 98 ) + 2: ( 1 9 19 27 30 38 39 41 61 68 83 86 94 96 98 ) + 3: ( 7 12 15 19 23 47 64 75 80 85 87 92 94 95 98 ) +} diff --git a/tests/unit/igraph_maximal_cliques4.c b/tests/unit/igraph_maximal_cliques4.c new file mode 100644 index 0000000..317fd18 --- /dev/null +++ b/tests/unit/igraph_maximal_cliques4.c @@ -0,0 +1,90 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2013 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +void sort_cliques(igraph_vector_int_list_t *cliques) { + igraph_integer_t i, n = igraph_vector_int_list_size(cliques); + for (i = 0; i < n; i++) { + igraph_vector_int_sort(igraph_vector_int_list_get_ptr(cliques, i)); + } + igraph_vector_int_list_sort(cliques, igraph_vector_int_lex_cmp); +} + +void print_and_destroy(igraph_vector_int_list_t *cliques) { + sort_cliques(cliques); + print_vector_int_list(cliques); + igraph_vector_int_list_destroy(cliques); +} + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t cliques, cl1, cl2; + igraph_vector_int_t v1, v2; + igraph_integer_t n, n1, n2; + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_erdos_renyi_game_gnp(&graph, /*n=*/ 100, /*p=*/ 0.5, /*directed=*/ 0, /*loops=*/ 0); + + igraph_vector_int_list_init(&cliques, 0); + igraph_vector_int_list_init(&cl1, 0); + igraph_vector_int_list_init(&cl2, 0); + + igraph_maximal_cliques_subset(&graph, /*subset=*/ 0, + &cliques, &n, /*outfile=*/ 0, + /*min_size=*/ 9, /*max_size=*/ 0); + + igraph_vector_int_init_range(&v1, 0, 13); + igraph_vector_int_init_range(&v2, 13, 100); + igraph_maximal_cliques_subset(&graph, &v1, &cl1, &n1, /*outfile=*/ 0, + /*min_size=*/ 9, /*max_size=*/ 0); + igraph_maximal_cliques_subset(&graph, &v2, &cl2, &n2, /*outfile=*/ 0, + /*min_size=*/ 9, /*max_size=*/ 0); + + igraph_vector_int_destroy(&v1); + igraph_vector_int_destroy(&v2); + + if (n1 + n2 != n) { + return 1; + } + if (n1 != igraph_vector_int_list_size(&cl1)) { + return 2; + } + if (n2 != igraph_vector_int_list_size(&cl2)) { + return 3; + } + + print_and_destroy(&cliques); + printf("---\n"); + print_and_destroy(&cl1); + printf("+\n"); + print_and_destroy(&cl2); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_maximal_cliques4.out b/tests/unit/igraph_maximal_cliques4.out new file mode 100644 index 0000000..9780dd9 --- /dev/null +++ b/tests/unit/igraph_maximal_cliques4.out @@ -0,0 +1,108 @@ +{ + 0: ( 1 22 27 29 51 57 58 59 95 ) + 1: ( 1 22 29 51 52 57 58 59 95 ) + 2: ( 6 11 14 15 27 49 67 87 88 ) + 3: ( 6 11 14 15 49 67 87 88 98 ) + 4: ( 6 11 14 27 48 49 73 87 88 ) + 5: ( 6 11 14 27 49 67 73 87 88 ) + 6: ( 6 14 22 23 27 44 48 51 80 ) + 7: ( 6 14 22 23 27 48 51 80 95 ) + 8: ( 6 14 22 23 44 48 51 80 84 ) + 9: ( 6 14 22 23 48 51 80 84 95 ) + 10: ( 6 14 23 27 48 49 51 80 95 ) + 11: ( 11 14 27 48 49 73 82 87 88 ) + 12: ( 11 14 27 49 67 73 82 87 88 ) + 13: ( 11 25 27 48 49 73 82 87 88 ) + 14: ( 11 25 27 49 67 73 82 87 88 ) + 15: ( 14 27 48 49 51 57 80 92 95 ) + 16: ( 14 27 48 49 57 62 80 92 95 ) + 17: ( 18 22 24 27 29 51 58 77 80 ) + 18: ( 18 22 24 27 51 58 77 80 99 ) + 19: ( 18 22 24 29 51 58 63 77 80 ) + 20: ( 18 22 24 45 51 56 63 77 80 ) + 21: ( 18 22 24 45 51 56 77 80 99 ) + 22: ( 18 22 24 45 51 58 63 77 80 ) + 23: ( 18 22 24 45 51 58 77 80 99 ) + 24: ( 18 22 24 45 56 77 80 85 99 ) + 25: ( 20 31 52 56 57 59 62 79 80 ) + 26: ( 22 23 33 40 43 44 48 62 84 ) + 27: ( 22 24 27 29 44 51 58 77 80 ) + 28: ( 22 24 27 29 51 58 59 80 95 ) + 29: ( 22 24 27 29 51 58 77 80 95 ) + 30: ( 22 24 27 44 51 58 77 80 99 ) + 31: ( 22 24 29 44 51 58 63 77 80 ) + 32: ( 22 24 29 51 52 53 58 59 80 95 ) + 33: ( 22 24 29 51 52 58 63 80 95 ) + 34: ( 22 24 29 51 58 63 77 80 95 ) + 35: ( 22 24 29 52 53 59 80 95 96 ) + 36: ( 22 24 44 51 56 63 68 77 80 ) + 37: ( 22 24 45 51 56 63 68 77 80 ) + 38: ( 22 24 45 51 58 63 77 80 95 ) + 39: ( 22 24 45 51 63 68 77 80 95 ) + 40: ( 22 27 29 51 57 58 59 80 95 ) + 41: ( 22 27 29 57 59 62 80 95 96 ) + 42: ( 22 27 29 57 62 76 80 95 96 ) + 43: ( 22 29 51 52 57 58 59 80 95 ) + 44: ( 22 29 52 57 59 62 80 95 96 ) + 45: ( 24 29 52 53 59 79 80 95 96 ) + 46: ( 25 27 48 49 61 73 82 88 92 ) + 47: ( 25 27 49 61 73 76 82 89 92 ) + 48: ( 25 27 49 67 73 76 82 89 92 ) + 49: ( 29 52 57 59 62 79 80 95 96 ) +} +--- +{ + 0: ( 6 11 14 15 27 49 67 87 88 ) + 1: ( 6 11 14 15 49 67 87 88 98 ) + 2: ( 6 11 14 27 48 49 73 87 88 ) + 3: ( 6 11 14 27 49 67 73 87 88 ) + 4: ( 6 14 22 23 27 44 48 51 80 ) + 5: ( 6 14 22 23 27 48 51 80 95 ) + 6: ( 6 14 22 23 44 48 51 80 84 ) + 7: ( 6 14 22 23 48 51 80 84 95 ) + 8: ( 6 14 23 27 48 49 51 80 95 ) + 9: ( 11 14 27 48 49 73 82 87 88 ) + 10: ( 11 14 27 49 67 73 82 87 88 ) + 11: ( 11 25 27 48 49 73 82 87 88 ) + 12: ( 11 25 27 49 67 73 82 87 88 ) + 13: ( 14 27 48 49 51 57 80 92 95 ) + 14: ( 14 27 48 49 57 62 80 92 95 ) +} ++ +{ + 0: ( 1 22 27 29 51 57 58 59 95 ) + 1: ( 1 22 29 51 52 57 58 59 95 ) + 2: ( 18 22 24 27 29 51 58 77 80 ) + 3: ( 18 22 24 27 51 58 77 80 99 ) + 4: ( 18 22 24 29 51 58 63 77 80 ) + 5: ( 18 22 24 45 51 56 63 77 80 ) + 6: ( 18 22 24 45 51 56 77 80 99 ) + 7: ( 18 22 24 45 51 58 63 77 80 ) + 8: ( 18 22 24 45 51 58 77 80 99 ) + 9: ( 18 22 24 45 56 77 80 85 99 ) + 10: ( 20 31 52 56 57 59 62 79 80 ) + 11: ( 22 23 33 40 43 44 48 62 84 ) + 12: ( 22 24 27 29 44 51 58 77 80 ) + 13: ( 22 24 27 29 51 58 59 80 95 ) + 14: ( 22 24 27 29 51 58 77 80 95 ) + 15: ( 22 24 27 44 51 58 77 80 99 ) + 16: ( 22 24 29 44 51 58 63 77 80 ) + 17: ( 22 24 29 51 52 53 58 59 80 95 ) + 18: ( 22 24 29 51 52 58 63 80 95 ) + 19: ( 22 24 29 51 58 63 77 80 95 ) + 20: ( 22 24 29 52 53 59 80 95 96 ) + 21: ( 22 24 44 51 56 63 68 77 80 ) + 22: ( 22 24 45 51 56 63 68 77 80 ) + 23: ( 22 24 45 51 58 63 77 80 95 ) + 24: ( 22 24 45 51 63 68 77 80 95 ) + 25: ( 22 27 29 51 57 58 59 80 95 ) + 26: ( 22 27 29 57 59 62 80 95 96 ) + 27: ( 22 27 29 57 62 76 80 95 96 ) + 28: ( 22 29 51 52 57 58 59 80 95 ) + 29: ( 22 29 52 57 59 62 80 95 96 ) + 30: ( 24 29 52 53 59 79 80 95 96 ) + 31: ( 25 27 48 49 61 73 82 88 92 ) + 32: ( 25 27 49 61 73 76 82 89 92 ) + 33: ( 25 27 49 67 73 76 82 89 92 ) + 34: ( 29 52 57 59 62 79 80 95 96 ) +} diff --git a/tests/unit/igraph_maximal_cliques_file.c b/tests/unit/igraph_maximal_cliques_file.c new file mode 100644 index 0000000..d22b080 --- /dev/null +++ b/tests/unit/igraph_maximal_cliques_file.c @@ -0,0 +1,45 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g_empty, g_lm; + + igraph_small(&g_empty, 0, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,2, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_maximal_cliques_file(&g_empty, stdout, /*min*/ 0, /*max*/ 0) == IGRAPH_SUCCESS); + + printf("\nGraph with loops and multiple edges:\n"); + IGRAPH_ASSERT(igraph_maximal_cliques_file(&g_lm, stdout, /*min*/ 0, /*max*/ 0) == IGRAPH_SUCCESS); + + printf("\nGraph with loops and multiple edges, minimum clique size 10:\n"); + IGRAPH_ASSERT(igraph_maximal_cliques_file(&g_lm, stdout, /*min*/ 10, /*max*/ 0) == IGRAPH_SUCCESS); + + printf("\nGraph with loops and multiple edges, maximum clique size 2:\n"); + IGRAPH_ASSERT(igraph_maximal_cliques_file(&g_lm, stdout, /*min*/ 0, /*max*/ 2) == IGRAPH_SUCCESS); + + igraph_destroy(&g_empty); + igraph_destroy(&g_lm); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_maximal_cliques_file.out b/tests/unit/igraph_maximal_cliques_file.out new file mode 100644 index 0000000..2d216fa --- /dev/null +++ b/tests/unit/igraph_maximal_cliques_file.out @@ -0,0 +1,13 @@ +No vertices: + +Graph with loops and multiple edges: +5 +3 4 +3 1 2 +0 1 2 + +Graph with loops and multiple edges, minimum clique size 10: + +Graph with loops and multiple edges, maximum clique size 2: +5 +3 4 diff --git a/tests/unit/igraph_maximum_bipartite_matching.c b/tests/unit/igraph_maximum_bipartite_matching.c new file mode 100644 index 0000000..49037cc --- /dev/null +++ b/tests/unit/igraph_maximum_bipartite_matching.c @@ -0,0 +1,180 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int test_weighted_graph_from_mit_notes(void) { + /* Test graph from the following lecture notes: + * http://math.mit.edu/~goemans/18433S07/matching-notes.pdf + */ + igraph_t graph; + igraph_vector_bool_t types; + igraph_vector_int_t matching; + igraph_vector_t weights; + igraph_integer_t matching_size; + igraph_real_t matching_weight; + igraph_bool_t is_matching; + igraph_real_t weight_array[] = { 2, 7, 2, 3, + 1, 3, 9, 3, 3, + 1, 3, 3, 1, 2, + 4, 1, 2, + 3 + }; + int i; + + igraph_small(&graph, 0, 0, + 0, 6, 0, 7, 0, 8, 0, 9, + 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, + 2, 5, 2, 6, 2, 7, 2, 8, 2, 9, + 3, 5, 3, 7, 3, 9, + 4, 7, -1); + igraph_vector_bool_init(&types, 10); + for (i = 0; i < 10; i++) { + VECTOR(types)[i] = (i >= 5); + } + igraph_vector_int_init(&matching, 0); + igraph_vector_init_array(&weights, weight_array, + sizeof(weight_array) / sizeof(weight_array[0])); + + igraph_maximum_bipartite_matching(&graph, &types, &matching_size, + &matching_weight, &matching, &weights, 0); + if (matching_size != 4) { + printf("matching_size is %" IGRAPH_PRId ", expected: 4\n", matching_size); + return 1; + } + if (matching_weight != 19) { + printf("matching_weight is %" IGRAPH_PRId ", expected: 19\n", (igraph_integer_t) matching_weight); + return 2; + } + igraph_is_maximal_matching(&graph, &types, &matching, &is_matching); + if (!is_matching) { + printf("not a matching: "); + igraph_vector_int_print(&matching); + return 3; + } + + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&matching); + igraph_vector_bool_destroy(&types); + igraph_destroy(&graph); + + return 0; +} + +int test_weighted_graph_generated(void) { + /* Several randomly generated small test graphs */ + igraph_t graph; + igraph_vector_bool_t types; + igraph_vector_int_t matching; + igraph_vector_t weights; + igraph_integer_t matching_size; + igraph_real_t matching_weight; + igraph_real_t weight_array_1[] = { 8, 5, 9, 18, 20, 13 }; + igraph_real_t weight_array_2[] = { 20, 4, 20, 3, 13, 1 }; + int i; + + igraph_vector_bool_init(&types, 10); + for (i = 0; i < 10; i++) { + VECTOR(types)[i] = (i >= 5); + } + igraph_vector_int_init(&matching, 0); + + /* Case 1 */ + + igraph_small(&graph, 0, 0, 0, 8, 2, 7, 3, 7, 3, 8, 4, 5, 4, 9, -1); + igraph_vector_init_array(&weights, weight_array_1, + sizeof(weight_array_1) / sizeof(weight_array_1[0])); + igraph_maximum_bipartite_matching(&graph, &types, &matching_size, + &matching_weight, &matching, &weights, 0); + if (matching_weight != 43) { + printf("matching_weight is %" IGRAPH_PRId ", expected: 43\n", (igraph_integer_t)matching_weight); + return 2; + } + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + /* Case 2 */ + + igraph_small(&graph, 0, 0, 0, 5, 0, 6, 1, 7, 2, 5, 3, 5, 3, 9, -1); + igraph_vector_init_array(&weights, weight_array_2, + sizeof(weight_array_2) / sizeof(weight_array_2[0])); + igraph_maximum_bipartite_matching(&graph, &types, &matching_size, + &matching_weight, &matching, &weights, 0); + if (matching_weight != 41) { + printf("matching_weight is %" IGRAPH_PRId ", expected: 41\n", (igraph_integer_t)matching_weight); + return 2; + } + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + igraph_vector_int_destroy(&matching); + igraph_vector_bool_destroy(&types); + + return 0; +} + +int main(void) { + igraph_t g; + igraph_vector_bool_t types; + igraph_vector_t weights; + + igraph_integer_t matching_size; + igraph_real_t weighted_size; + + igraph_vector_int_t matching; + + igraph_small(&g, 4, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, + -1); + + igraph_vector_bool_init(&types, 4); + VECTOR(types)[0] = 0; + VECTOR(types)[1] = 1; + VECTOR(types)[2] = 0; + VECTOR(types)[3] = 1; + + igraph_vector_int_init(&matching, 0); + + igraph_vector_init(&weights, igraph_vcount(&g)); + igraph_vector_fill(&weights, 1.0); + + // Test incorrect types + CHECK_ERROR(igraph_maximum_bipartite_matching(&g, &types, &matching_size, NULL, &matching, NULL, 0), IGRAPH_EINVAL); + + // Test incorrect types for weighted graph + VECTOR(types)[2] = 0; + CHECK_ERROR(igraph_maximum_bipartite_matching(&g, &types, &matching_size, &weighted_size, &matching, &weights, 0), IGRAPH_EINVAL); + + igraph_vector_destroy(&weights); + igraph_vector_int_destroy(&matching); + + igraph_vector_bool_destroy(&types); + igraph_destroy(&g); + + if (test_weighted_graph_generated()) { + return 1; + } + + if (test_weighted_graph_from_mit_notes()) { + return 2; + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_mean_degree.c b/tests/unit/igraph_mean_degree.c new file mode 100644 index 0000000..703a419 --- /dev/null +++ b/tests/unit/igraph_mean_degree.c @@ -0,0 +1,59 @@ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_real_t k; + + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(isnan(k)); + igraph_destroy(&graph); + + igraph_empty(&graph, 10, IGRAPH_UNDIRECTED); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(k == 0); + igraph_destroy(&graph); + + igraph_ring(&graph, 5, IGRAPH_DIRECTED, false, true); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(k == 1); + igraph_destroy(&graph); + + igraph_ring(&graph, 5, IGRAPH_UNDIRECTED, false, true); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(k == 2); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,1, 1,1, -1); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(k == 2); + igraph_mean_degree(&graph, &k, false); + IGRAPH_ASSERT(k == 1); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_minimum_size_separators.c b/tests/unit/igraph_minimum_size_separators.c new file mode 100644 index 0000000..41a8a57 --- /dev/null +++ b/tests/unit/igraph_minimum_size_separators.c @@ -0,0 +1,169 @@ +/* + IGraph library. + Copyright (C) 2023 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 +#include "test_utilities.h" + +int print_and_destroy(igraph_vector_int_list_t *list) { + igraph_integer_t i, n = igraph_vector_int_list_size(list); + + for (i = 0; i < n; i++) { + igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(list, i); + igraph_vector_int_print(v); + } + + igraph_vector_int_list_destroy(list); + return 0; +} + +int main(void) { + igraph_t g, g2; + igraph_vector_int_list_t sep; + igraph_vs_t vs; + + igraph_small(&g, 7, IGRAPH_UNDIRECTED, + 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, + -1); + igraph_vector_int_list_init(&sep, 0); + igraph_minimum_size_separators(&g, &sep); + print_and_destroy(&sep); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 5, IGRAPH_UNDIRECTED, + 0, 3, 1, 3, 2, 3, + 0, 4, 1, 4, 2, 4, + -1); + igraph_vector_int_list_init(&sep, 0); + igraph_minimum_size_separators(&g, &sep); + print_and_destroy(&sep); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 5, IGRAPH_UNDIRECTED, + 2, 0, 3, 0, 4, 0, + 2, 1, 3, 1, 4, 1, + -1); + igraph_vector_int_list_init(&sep, 0); + igraph_minimum_size_separators(&g, &sep); + print_and_destroy(&sep); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 10, IGRAPH_UNDIRECTED, + 0, 2, 0, 3, 1, 2, 1, 3, 5, 2, 5, 3, 6, 2, 6, 3, + 7, 2, 7, 3, 8, 2, 8, 3, 9, 2, 9, 3, + 2, 4, 4, 3, + -1); + igraph_vector_int_list_init(&sep, 0); + igraph_minimum_size_separators(&g, &sep); + print_and_destroy(&sep); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_full(&g, 4, IGRAPH_UNDIRECTED, /*loops=*/ 0); + igraph_vector_int_list_init(&sep, 0); + igraph_minimum_size_separators(&g, &sep); + print_and_destroy(&sep); + igraph_destroy(&g); + + /* ----------------------------------------------------------- */ + + igraph_small(&g, 23, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, + 1, 2, 1, 3, 1, 4, 1, 6, + 2, 3, 2, 5, 2, 6, + 3, 4, 3, 5, 3, 6, + 4, 5, 4, 6, 4, 20, + 5, 6, + 6, 7, 6, 10, 6, 13, 6, 18, + 7, 8, 7, 10, 7, 13, + 8, 9, + 9, 11, 9, 12, + 10, 11, 10, 13, + 11, 15, + 12, 15, + 13, 14, + 14, 15, + 16, 17, 16, 18, 16, 19, + 17, 19, 17, 20, + 18, 19, 18, 21, 18, 22, + 19, 20, + 20, 21, 20, 22, + 21, 22, + -1); + + igraph_vector_int_list_init(&sep, 0); + igraph_minimum_size_separators(&g, &sep); + printf("Orig:\n"); + print_and_destroy(&sep); + + igraph_vector_int_list_init(&sep, 0); + igraph_vs_vector_small(&vs, 0, 1, 2, 3, 4, 5, 6, 16, 17, 18, 19, 20, 21, 22, -1); + igraph_induced_subgraph(&g, &g2, vs, IGRAPH_SUBGRAPH_AUTO); + igraph_minimum_size_separators(&g2, &sep); + printf("1-7,17-23:\n"); + print_and_destroy(&sep); + igraph_vs_destroy(&vs); + igraph_destroy(&g2); + + igraph_vector_int_list_init(&sep, 0); + igraph_vs_vector_small(&vs, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1); + igraph_induced_subgraph(&g, &g2, vs, IGRAPH_SUBGRAPH_AUTO); + igraph_minimum_size_separators(&g2, &sep); + printf("7-16:\n"); + print_and_destroy(&sep); + igraph_vs_destroy(&vs); + igraph_destroy(&g2); + + igraph_vector_int_list_init(&sep, 0); + igraph_vs_vector_small(&vs, 16, 17, 18, 19, 20, 21, 22, -1); + igraph_induced_subgraph(&g, &g2, vs, IGRAPH_SUBGRAPH_AUTO); + igraph_minimum_size_separators(&g2, &sep); + printf("17-23:\n"); + print_and_destroy(&sep); + igraph_vs_destroy(&vs); + igraph_destroy(&g2); + + igraph_vector_int_list_init(&sep, 0); + igraph_vs_vector_small(&vs, 6, 7, 10, 13, -1); + igraph_induced_subgraph(&g, &g2, vs, IGRAPH_SUBGRAPH_AUTO); + igraph_minimum_size_separators(&g2, &sep); + printf("7,8,11,14:\n"); + print_and_destroy(&sep); + igraph_vs_destroy(&vs); + igraph_destroy(&g2); + + igraph_vector_int_list_init(&sep, 0); + igraph_vs_vector_small(&vs, 0, 1, 2, 3, 4, 5, 6, -1); + igraph_induced_subgraph(&g, &g2, vs, IGRAPH_SUBGRAPH_AUTO); + igraph_minimum_size_separators(&g2, &sep); + printf("1-7:\n"); + print_and_destroy(&sep); + igraph_vs_destroy(&vs); + igraph_destroy(&g2); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_minimum_size_separators.out b/tests/unit/igraph_minimum_size_separators.out new file mode 100644 index 0000000..1d22142 --- /dev/null +++ b/tests/unit/igraph_minimum_size_separators.out @@ -0,0 +1,30 @@ +0 +3 4 +0 1 +2 3 +1 2 3 +0 2 3 +0 1 3 +0 1 2 +Orig: +6 +1-7,17-23: +9 11 +4 9 +6 11 +4 6 +7-16: +3 9 +7 9 +1 3 +17-23: +2 4 +7,8,11,14: +1 2 3 +0 2 3 +0 1 3 +0 1 2 +1-7: +1 2 3 4 5 +0 2 3 4 6 +0 1 3 5 6 diff --git a/tests/unit/igraph_modularity.c b/tests/unit/igraph_modularity.c new file mode 100644 index 0000000..6eb0bd4 --- /dev/null +++ b/tests/unit/igraph_modularity.c @@ -0,0 +1,117 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_t weights; + igraph_vector_int_t membership; + igraph_real_t modularity, resolution; + igraph_attribute_combination_t comb; + + /* Turn on attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_attribute_combination(&comb, + "weight", IGRAPH_ATTRIBUTE_COMBINE_SUM, + IGRAPH_NO_MORE_ATTRIBUTES); + + /* Set default seed to get reproducible results */ + igraph_rng_seed(igraph_rng_default(), 0); + + /* Null graph */ + igraph_vector_int_init(&membership, 0); + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, -1); + igraph_modularity(&graph, &membership, 0, /* resolution */ 1, IGRAPH_UNDIRECTED, &modularity); + IGRAPH_ASSERT(isnan(modularity)); + + igraph_destroy(&graph); + igraph_small(&graph, 0, IGRAPH_DIRECTED, -1); + igraph_modularity(&graph, &membership, 0, /* resolution */ 1, IGRAPH_UNDIRECTED, &modularity); + IGRAPH_ASSERT(isnan(modularity)); + + /* Should not crash if we omit 'modularity' */ + igraph_modularity(&graph, &membership, 0, /* resolution */ 1, IGRAPH_UNDIRECTED, /* modularity = */ NULL); + igraph_destroy(&graph); + igraph_vector_int_destroy(&membership); + + /* Simple unweighted graph */ + igraph_small(&graph, 10, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 5, 6, 5, 7, 5, 8, 5, 9, 6, 7, 6, 8, 6, 9, 7, 8, 7, 9, 8, 9, + 0, 5, -1); + + /* Set weights */ + igraph_vector_init(&weights, igraph_ecount(&graph)); + igraph_vector_fill(&weights, 1.0); + SETEANV(&graph, "weight", &weights); + + /* Set membership */ + igraph_vector_int_init_int(&membership, igraph_vcount(&graph), + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1); + + /* Calculate modularity */ + for (resolution = 0.5; resolution <= 1.5; resolution += 0.5) { + igraph_modularity(&graph, &membership, &weights, + /* resolution */ resolution, + IGRAPH_DIRECTED, &modularity); + printf("Modularity (resolution %.2f) is %g.\n", resolution, modularity); + } + + igraph_to_directed(&graph, IGRAPH_TO_DIRECTED_MUTUAL); + igraph_vector_resize(&weights, igraph_ecount(&graph)); + igraph_vector_fill(&weights, 1.0); + for (resolution = 0.5; resolution <= 1.5; resolution += 0.5) { + igraph_modularity(&graph, &membership, &weights, + /* resolution */ resolution, + IGRAPH_DIRECTED, &modularity); + printf("Modularity (resolution %.2f) is %g on directed graph.\n", resolution, modularity); + } + + /* Recalculate modularity on contracted graph */ + igraph_contract_vertices(&graph, &membership, NULL); + igraph_vector_int_destroy(&membership); + + igraph_simplify(&graph, /* remove_multiple */ true, /* remove_loops */ false, &comb); + + igraph_vector_int_init_range(&membership, 0, igraph_vcount(&graph)); + EANV(&graph, "weight", &weights); + for (resolution = 0.5; resolution <= 1.5; resolution += 0.5) { + igraph_modularity(&graph, &membership, &weights, + /* resolution */ resolution, + IGRAPH_DIRECTED, &modularity); + printf("Modularity (resolution %.2f) is %g after aggregation.\n", resolution, modularity); + } + + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + igraph_attribute_combination_destroy(&comb); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_modularity.out b/tests/unit/igraph_modularity.out new file mode 100644 index 0000000..5e062b0 --- /dev/null +++ b/tests/unit/igraph_modularity.out @@ -0,0 +1,9 @@ +Modularity (resolution 0.50) is 0.702381. +Modularity (resolution 1.00) is 0.452381. +Modularity (resolution 1.50) is 0.202381. +Modularity (resolution 0.50) is 0.702381 on directed graph. +Modularity (resolution 1.00) is 0.452381 on directed graph. +Modularity (resolution 1.50) is 0.202381 on directed graph. +Modularity (resolution 0.50) is 0.702381 after aggregation. +Modularity (resolution 1.00) is 0.452381 after aggregation. +Modularity (resolution 1.50) is 0.202381 after aggregation. diff --git a/tests/unit/igraph_modularity_matrix.c b/tests/unit/igraph_modularity_matrix.c new file mode 100644 index 0000000..280c7a1 --- /dev/null +++ b/tests/unit/igraph_modularity_matrix.c @@ -0,0 +1,129 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void test_print_destroy(igraph_t *g, igraph_vector_t *weights, float resolution, igraph_matrix_t *modmat, igraph_bool_t directed) { + igraph_integer_t i, j; + IGRAPH_ASSERT(igraph_modularity_matrix(g, weights, resolution, modmat, directed) == IGRAPH_SUCCESS); + for (i = 0; i < igraph_matrix_nrow(modmat); i++) { + for (j = 0; j < igraph_matrix_ncol(modmat); j++) { + if (fabs(MATRIX(*modmat, i, j)) < 1e-10) { + MATRIX(*modmat, i, j) = 0; + } + } + } + print_matrix(modmat); + igraph_destroy(g); + igraph_matrix_destroy(modmat); + if (weights) { + igraph_vector_destroy(weights); + } +} + +int main(void) { + igraph_t g; + igraph_vector_t weights; + igraph_vector_int_t membership; + igraph_matrix_t modmat; + igraph_real_t modularity, test_modularity; + igraph_integer_t i, j; + + printf("No vertices:\n"); + igraph_small(&g, 0, /*directed*/0, -1); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, NULL, 1.0, &modmat, 0); + + printf("No edges:\n"); + igraph_small(&g, 3, /*directed*/0, -1); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, NULL, 1.0, &modmat, 0); + + printf("Triangle with no resolution should give the adjacency matrix:\n"); + igraph_small(&g, 3, /*directed*/0, 0,1, 0,2, 1,2, -1); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, NULL, 0.0, &modmat, 0); + + printf("Triangle and point with self-loop, undirected :\n"); + igraph_small(&g, 4, /*directed*/0, 0,1, 0,2, 1,2, 3,3, -1); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, NULL, 1.0, &modmat, 0); + + printf("Triangle and point with self-loop, directed, but direction ignored:\n"); + igraph_small(&g, 4, /*directed*/1, 0,1, 0,2, 1,2, 3,3, -1); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, NULL, 1.0, &modmat, 0); + + printf("Triangle and point with self-loop, directed:\n"); + igraph_small(&g, 4, /*directed*/1, 0,1, 0,2, 1,2, 3,3, -1); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, NULL, 1.0, &modmat, 1); + + printf("Triangle with weights 0, 1, 2:\n"); + igraph_small(&g, 3, /*directed*/0, 0,1, 0,2, 1,2, -1); + igraph_vector_init_int(&weights, 3, 0, 1, 2); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, &weights, 1.0, &modmat, 0); + + printf("Triangle with weights 0, -1, -2:\n"); + igraph_small(&g, 3, /*directed*/0, 0,1, 0,2, 1,2, -1); + igraph_vector_init_int(&weights, 3, 0, -1, -2); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, &weights, 1.0, &modmat, 0); + + printf("Directed triangle with weights 0, 1, 2:\n"); + igraph_small(&g, 3, /*directed*/1, 0,1, 0,2, 1,2, -1); + igraph_vector_init_int(&weights, 3, 0, 1, 2); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, &weights, 1.0, &modmat, 1); + + printf("Triangle with weights -1, 0, 1 will cause divisions by zero:\n"); + igraph_small(&g, 3, /*directed*/0, 0,1, 0,2, 1,2, -1); + igraph_vector_init_int(&weights, 3, -1, 0, 1); + igraph_matrix_init(&modmat, 0, 0); + test_print_destroy(&g, &weights, 1.0, &modmat, 0); + + printf("Comparison with modularity:\n"); + igraph_small(&g, 5, /*directed*/1, 0,1, 0,2, 1,2, 3,4, 4,0, -1); + igraph_vector_init_int(&weights, 5, 1, 2, 3, 4, 5); + igraph_vector_int_init_int(&membership, 5, 0, 0, 0, 1, 1); + igraph_matrix_init(&modmat, 0, 0); + IGRAPH_ASSERT(igraph_modularity_matrix(&g, &weights, 0.7, &modmat, 1) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_modularity(&g, &membership, &weights, 0.7, 1, &modularity) == IGRAPH_SUCCESS); + print_matrix(&modmat); + test_modularity = 0; + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + test_modularity += MATRIX(modmat, i, j); + } + } + for (i = 3; i < 5; i++) { + for (j = 3; j < 5; j++) { + test_modularity += MATRIX(modmat, i, j); + } + } + printf("Modularity: %g, modularity via matrix: %g\n", modularity, test_modularity / igraph_vector_sum(&weights)); + igraph_destroy(&g); + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&weights); + igraph_matrix_destroy(&modmat); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_modularity_matrix.out b/tests/unit/igraph_modularity_matrix.out new file mode 100644 index 0000000..16f92b6 --- /dev/null +++ b/tests/unit/igraph_modularity_matrix.out @@ -0,0 +1,48 @@ +No vertices: +[ 0-by-0 ] +No edges: +[ NaN NaN NaN + NaN NaN NaN + NaN NaN NaN ] +Triangle with no resolution should give the adjacency matrix: +[ 0 1 1 + 1 0 1 + 1 1 0 ] +Triangle and point with self-loop, undirected : +[ -0.5 0.5 0.5 -0.5 + 0.5 -0.5 0.5 -0.5 + 0.5 0.5 -0.5 -0.5 + -0.5 -0.5 -0.5 1.5 ] +Triangle and point with self-loop, directed, but direction ignored: +[ -0.5 0.5 0.5 -0.5 + 0.5 -0.5 0.5 -0.5 + 0.5 0.5 -0.5 -0.5 + -0.5 -0.5 -0.5 1.5 ] +Triangle and point with self-loop, directed: +[ 0 0.5 0 -0.5 + 0 -0.25 0.5 -0.25 + 0 0 0 0 + 0 -0.25 -0.5 0.75 ] +Triangle with weights 0, 1, 2: +[ -0.166667 -0.333333 0.5 + -0.333333 -0.666667 1 + 0.5 1 -1.5 ] +Triangle with weights 0, -1, -2: +[ 0.166667 0.333333 -0.5 + 0.333333 0.666667 -1 + -0.5 -1 1.5 ] +Directed triangle with weights 0, 1, 2: +[ 0 0 0 + 0 0 0 + 0 0 0 ] +Triangle with weights -1, 0, 1 will cause divisions by zero: +[ -Inf NaN Inf + NaN NaN NaN + Inf NaN -Inf ] +Comparison with modularity: +[ -0.7 0.86 1.3 0 -0.56 + -0.7 -0.14 2.3 0 -0.56 + 0 0 0 0 0 + -0.933333 -0.186667 -0.933333 0 3.25333 + 3.83333 -0.233333 -1.16667 0 -0.933333 ] +Modularity: 0.349333, modularity via matrix: 0.349333 diff --git a/tests/unit/igraph_moran_process.c b/tests/unit/igraph_moran_process.c new file mode 100644 index 0000000..9725335 --- /dev/null +++ b/tests/unit/igraph_moran_process.c @@ -0,0 +1,224 @@ +/* -*- mode: C -*- */ +/* + Test suite for the Moran process in a network setting. + 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 +#include + +#include "test_utilities.h" + +/* test parameters structure */ +typedef struct { + igraph_t *graph; + igraph_vector_t *weights; + igraph_vector_t *quantities; + igraph_vector_int_t *strategies; + igraph_neimode_t mode; + igraph_error_t retval; +} strategy_test_t; + +/* Error tests, i.e. we expect errors to be raised for each test. + */ +int error_tests(void) { + igraph_t g, gzero, h; + igraph_vector_t quant, quantnvert, quantzero; + igraph_vector_int_t strat, stratnvert, stratzero; + igraph_vector_t wgt, wgtnedge, wgtzero; + int i, n, nvert, ret; + strategy_test_t *test; + + igraph_empty(&h, 0, 0); /* empty graph */ + /* nonempty graph */ + igraph_small(&g, /* n= */ 0, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + nvert = igraph_vcount(&g); + /* weights vectors */ + igraph_vector_init(&wgt, 0); + igraph_vector_init(&wgtnedge, igraph_ecount(&g)); + /* quantities vectors */ + igraph_vector_init(&quant, 1); + igraph_vector_init_real(&quantnvert, nvert, 0.1, 0.2, 0.3); + /* strategies vectors */ + igraph_vector_int_init(&strat, 2); + igraph_vector_int_init_int(&stratnvert, nvert, 0, 1, 2); + + igraph_small(&gzero, /* n= */ 0, IGRAPH_UNDIRECTED, + 0, 3, 0, 4, 1, 2, 1, 4, 1, 5, 2, 3, 2, 4, 3, 4, -1); + nvert = igraph_vcount(&gzero); + igraph_vector_init(&quantzero, nvert); /* vector of zeros */ + igraph_vector_int_init(&stratzero, nvert); /* vector of zeros */ + igraph_vector_init(&wgtzero, igraph_ecount(&gzero)); /* vector of zeros */ + /* igraph_vector_init_real(&stratzero, nvert, 1.0, 0.0, 1.0, 2.0, 0.0, 3.0); */ + + /* test parameters */ + /*------graph--weights--quantities--strategies--mode--retval------*/ + /* null pointer for graph */ + strategy_test_t null_graph = {NULL, NULL, NULL, NULL, IGRAPH_ALL, IGRAPH_EINVAL}; + /* null pointer for weights vector */ + strategy_test_t null_wgt = {&g, NULL, &quantnvert, &stratnvert, IGRAPH_ALL, IGRAPH_EINVAL}; + /* null pointer for quantities vector */ + strategy_test_t null_quant = {&g, &wgt, NULL, NULL, IGRAPH_ALL, IGRAPH_EINVAL}; + /* null pointer for strategies vector */ + strategy_test_t null_strat = {&g, &wgt, &quant, NULL, IGRAPH_ALL, IGRAPH_EINVAL}; + /* empty graph */ + strategy_test_t empty_graph = {&h, &wgt, &quant, &strat, IGRAPH_ALL, IGRAPH_EINVAL}; + /* length of quantities vector different from number of vertices */ + strategy_test_t qdiff_length = {&g, &wgtnedge, &quant, &strat, IGRAPH_ALL, IGRAPH_EINVAL}; + /* length of strategies vector different from number of vertices */ + strategy_test_t sdiff_length = {&g, &wgtnedge, &quantnvert, &strat, IGRAPH_ALL, IGRAPH_EINVAL}; + /* length of weights vector different from number of edges */ + strategy_test_t wdiff_length = {&g, &wgt, &quantnvert, &stratnvert, IGRAPH_ALL, IGRAPH_EINVAL}; + /* weights vector contains all zeros */ + strategy_test_t zero_wgt = {&g, &wgtnedge, &quantnvert, &stratnvert, IGRAPH_ALL, IGRAPH_EINVAL}; + /* quantities vector contains all zeros */ + strategy_test_t zero_quant = {&gzero, &wgtzero, &quantzero, &stratzero, IGRAPH_ALL, IGRAPH_EINVAL}; + strategy_test_t *all_checks[] = {/* 1 */ &null_graph, + /* 2 */ &null_quant, + /* 3 */ &null_strat, + /* 4 */ &null_wgt, + /* 5 */ &empty_graph, + /* 6 */ &qdiff_length, + /* 7 */ &sdiff_length, + /* 8 */ &wdiff_length, + /* 9 */ &zero_quant, + /* 10 */ &zero_wgt + }; + + /* Run the error tests. We expect error to be raised for each test. */ + igraph_set_error_handler(igraph_error_handler_ignore); + n = 10; + i = 0; + while (i < n) { + test = all_checks[i]; + ret = igraph_moran_process(test->graph, test->weights, test->quantities, + test->strategies, test->mode); + if (ret != test->retval) { + printf("Error test no. %d failed.\n", (int)(i + 1)); + return IGRAPH_FAILURE; + } + i++; + } + /* clean up */ + igraph_destroy(&g); + igraph_destroy(&gzero); + igraph_destroy(&h); + igraph_vector_destroy(&quant); + igraph_vector_destroy(&quantnvert); + igraph_vector_destroy(&quantzero); + igraph_vector_int_destroy(&strat); + igraph_vector_int_destroy(&stratnvert); + igraph_vector_int_destroy(&stratzero); + igraph_vector_destroy(&wgt); + igraph_vector_destroy(&wgtnedge); + igraph_vector_destroy(&wgtzero); + + return IGRAPH_SUCCESS; +} + +/* One iteration of the Moran process on a simple digraph. + */ +int moran_one_test(void) { + igraph_t g; + igraph_integer_t u = -1; /* vertex chosen for reproduction */ + igraph_integer_t v = -1; /* clone of u */ + igraph_integer_t nedge, nvert; + igraph_real_t q = 0.0; + igraph_vector_t quant, quantcp; + igraph_vector_int_t strat, stratcp; + igraph_vector_t wgt; + igraph_integer_t i; + + /* graph representing the game network; quantities and strategies vectors */ + igraph_small(&g, /*nvert*/ 0, IGRAPH_DIRECTED, + 0, 1, 0, 4, 1, 2, 1, 4, 2, 1, 3, 2, 4, 0, 4, 3, -1); + nvert = igraph_vcount(&g); + nedge = igraph_ecount(&g); + igraph_vector_init_real(&quant, nvert, 0.77, 0.83, 0.64, 0.81, 0.05); + igraph_vector_int_init_real(&strat, nvert, 2.0, 0.0, 0.0, 1.0, 2.0); + /* Set the edge weights. Here we assume the following correspondence */ + /* between edge IDs and directed edges: */ + /* edge 0: 0 -> 1 */ + /* edge 1: 0 -> 4 */ + /* edge 2: 1 -> 2 */ + /* edge 3: 1 -> 4 */ + /* edge 4: 2 -> 1 */ + /* edge 5: 3 -> 2 */ + /* edge 6: 4 -> 0 */ + /* edge 7: 4 -> 3 */ + igraph_vector_init_real(&wgt, nedge, 1.9, 0.8, 6.2, 2.4, 1.1, 5.2, 7.3, 8.8); + + /* play game */ + igraph_vector_init_copy(&quantcp, &quant); + igraph_vector_int_init_copy(&stratcp, &strat); + igraph_moran_process(&g, &wgt, &quantcp, &stratcp, IGRAPH_OUT); + + /* Determine which vertex was chosen for death. The original quantities */ + /* vector contain unique values, i.e. no duplicates. Thus we compare the */ + /* updated quantities with the original one. */ + for (i = 0; i < igraph_vector_size(&quant); i++) { + if (VECTOR(quant)[i] != VECTOR(quantcp)[i]) { + /* found the new clone vertex */ + v = i; + q = VECTOR(quantcp)[i]; + break; + } + } + IGRAPH_ASSERT(v >= 0); + IGRAPH_ASSERT(q != 0.0); + + /* Now we know that v is a clone of some vertex. Determine the vertex that */ + /* v is a clone of. */ + for (i = 0; i < igraph_vector_size(&quant); i++) { + if (VECTOR(quant)[i] == q) { + /* found the vertex chosen for reproduction */ + u = i; + break; + } + } + IGRAPH_ASSERT(u >= 0); + + /* check that v is indeed a clone of u */ + if (VECTOR(quant)[u] != VECTOR(quantcp)[v]) { + return IGRAPH_FAILURE; + } + if (VECTOR(strat)[u] != VECTOR(stratcp)[v]) { + return IGRAPH_FAILURE; + } + + igraph_destroy(&g); + igraph_vector_destroy(&quant); + igraph_vector_destroy(&quantcp); + igraph_vector_int_destroy(&strat); + igraph_vector_int_destroy(&stratcp); + igraph_vector_destroy(&wgt); + + return IGRAPH_SUCCESS; +} + +int main(void) { + + igraph_rng_seed(igraph_rng_default(), 1298); + + IGRAPH_ASSERT(error_tests() == IGRAPH_SUCCESS); + IGRAPH_ASSERT(moran_one_test() == IGRAPH_SUCCESS); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_motifs_randesu.c b/tests/unit/igraph_motifs_randesu.c new file mode 100644 index 0000000..386a695 --- /dev/null +++ b/tests/unit/igraph_motifs_randesu.c @@ -0,0 +1,69 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +igraph_error_t print_motif(const igraph_t *graph, igraph_vector_int_t *vids, + igraph_integer_t isoclass, void* extra) { + printf("Class %" IGRAPH_PRId ": ", isoclass); + print_vector_int(vids); + return IGRAPH_SUCCESS; +} + +int main(void) { + + igraph_t g; + igraph_vector_t hist; + igraph_real_t zeros[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + igraph_vector_t cut_prob; + igraph_integer_t size; + + igraph_ring(&g, 1000, IGRAPH_DIRECTED, 1, 1); + igraph_vector_init(&hist, 0); + igraph_motifs_randesu(&g, &hist, 3, igraph_vector_view(&cut_prob, zeros, 3)); + print_vector(&hist); + igraph_destroy(&g); + igraph_vector_destroy(&hist); + + igraph_famous(&g, "Octahedral"); + size = 3; + printf("Motif size: %" IGRAPH_PRId "\n", size); + igraph_motifs_randesu_callback(&g, size, igraph_vector_view(&cut_prob, zeros, size), &print_motif, NULL); + size = 4; + printf("Motif size: %" IGRAPH_PRId "\n", size); + igraph_motifs_randesu_callback(&g, size, igraph_vector_view(&cut_prob, zeros, size), &print_motif, NULL); + size = 5; + printf("Motif size: %" IGRAPH_PRId "\n", size); + igraph_motifs_randesu_callback(&g, size, igraph_vector_view(&cut_prob, zeros, size), &print_motif, NULL); + size = 6; + printf("Motif size: %" IGRAPH_PRId "\n", size); + igraph_motifs_randesu_callback(&g, size, igraph_vector_view(&cut_prob, zeros, size), &print_motif, NULL); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_motifs_randesu.out b/tests/unit/igraph_motifs_randesu.out new file mode 100644 index 0000000..fbe0095 --- /dev/null +++ b/tests/unit/igraph_motifs_randesu.out @@ -0,0 +1,47 @@ +( NaN NaN 0 NaN 0 0 0 0 0 0 1000 0 0 0 0 0 ) +Motif size: 3 +Class 2: ( 0 5 1 ) +Class 3: ( 0 5 2 ) +Class 3: ( 0 5 3 ) +Class 2: ( 0 5 4 ) +Class 3: ( 0 3 1 ) +Class 2: ( 0 3 2 ) +Class 2: ( 0 3 4 ) +Class 3: ( 0 2 1 ) +Class 2: ( 0 2 4 ) +Class 2: ( 0 1 4 ) +Class 3: ( 1 4 2 ) +Class 3: ( 1 4 3 ) +Class 2: ( 1 4 5 ) +Class 2: ( 1 3 2 ) +Class 2: ( 1 3 5 ) +Class 2: ( 1 2 5 ) +Class 3: ( 2 5 4 ) +Class 2: ( 2 5 3 ) +Class 2: ( 2 4 3 ) +Class 3: ( 3 5 4 ) +Motif size: 4 +Class 8: ( 0 5 4 1 ) +Class 9: ( 0 5 4 2 ) +Class 9: ( 0 5 4 3 ) +Class 9: ( 0 5 3 1 ) +Class 9: ( 0 5 3 2 ) +Class 9: ( 0 5 2 1 ) +Class 9: ( 0 3 4 1 ) +Class 8: ( 0 3 4 2 ) +Class 9: ( 0 3 2 1 ) +Class 9: ( 0 2 4 1 ) +Class 9: ( 1 4 5 2 ) +Class 9: ( 1 4 5 3 ) +Class 9: ( 1 4 3 2 ) +Class 8: ( 1 3 5 2 ) +Class 9: ( 2 5 3 4 ) +Motif size: 5 +Class 31: ( 0 5 4 3 1 ) +Class 31: ( 0 5 4 3 2 ) +Class 31: ( 0 5 4 2 1 ) +Class 31: ( 0 5 3 2 1 ) +Class 31: ( 0 3 4 2 1 ) +Class 31: ( 1 4 5 3 2 ) +Motif size: 6 +Class 152: ( 0 5 4 3 2 1 ) diff --git a/tests/unit/igraph_motifs_randesu_estimate.c b/tests/unit/igraph_motifs_randesu_estimate.c new file mode 100644 index 0000000..74f894e --- /dev/null +++ b/tests/unit/igraph_motifs_randesu_estimate.c @@ -0,0 +1,97 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, int size, igraph_vector_t *cut_prob, + igraph_integer_t sample_size, igraph_vector_int_t *parsample) { + igraph_integer_t estimate; + IGRAPH_ASSERT(igraph_motifs_randesu_estimate(graph, &estimate, size, cut_prob, sample_size, parsample) == IGRAPH_SUCCESS); + printf("Estimate: %" IGRAPH_PRId "\n\n", estimate); +} + + +int main(void) { + igraph_t g_0, g_1, g_50_full, g_4_3_1; + igraph_vector_t cut_prob_0_3; + igraph_vector_t cut_prob_0_4; + igraph_vector_t cut_prob_01; + igraph_vector_int_t parsample; + igraph_integer_t estimate; + + igraph_vector_init_real(&cut_prob_0_3, 3, 0.0, 0.0, 0.0); + igraph_vector_init_real(&cut_prob_0_4, 4, 0.0, 0.0, 0.0, 0.0); + igraph_vector_init_real(&cut_prob_01, 3, 0.1, 0.1, 0.1); + igraph_vector_int_init_range(&parsample, 0, 41); + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_full(&g_50_full, 50, 0, IGRAPH_NO_LOOPS); + igraph_small(&g_4_3_1, 4, 0, 0,1, 1,2, 2,0, -1); + + printf("No vertices:\n"); + call_and_print(&g_0, /*size*/ 3, &cut_prob_0_3, /*sample_size*/ 1, /*parsample*/ NULL); + + printf("One vertex:\n"); + call_and_print(&g_1, /*size*/ 3, &cut_prob_0_3, /*sample_size*/ 1, /*parsample*/ NULL); + + printf("Full graph of 50 vertices, motif size 3, sample all, (50 choose 3 = 19600):\n"); + call_and_print(&g_50_full, /*size*/ 3, &cut_prob_0_3, /*sample_size*/ 50, /*parsample*/ NULL); + + printf("Full graph of 50 vertices, motif size 3, sample all, cut_prob 0.1 at each level:\n"); + call_and_print(&g_50_full, /*size*/ 3, &cut_prob_01, /*sample_size*/ 50, /*parsample*/ NULL); + + printf("Full graph of 50 vertices, motif size 3, sample 20:\n"); + call_and_print(&g_50_full, /*size*/ 3, &cut_prob_0_3, /*sample_size*/ 20, /*parsample*/ NULL); + + printf("Full graph of 50 vertices, motif size 3, sample first 40:\n"); + call_and_print(&g_50_full, /*size*/ 3, &cut_prob_0_3, /*sample_size*/ 0, &parsample); + + printf("Full graph of 50 vertices, motif size 4, sample 20 (50 choose 4 = 230300):\n"); + call_and_print(&g_50_full, /*size*/ 4, &cut_prob_0_4, /*sample_size*/ 20, /*parsample*/ NULL); + + printf("Triangle and a vertex, motif size 4, sample all:\n"); + call_and_print(&g_4_3_1, /*size*/ 4, &cut_prob_0_4, /*sample_size*/ 4, /*parsample*/ NULL); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Cut prob too short.\n"); + IGRAPH_ASSERT(igraph_motifs_randesu_estimate(&g_4_3_1, &estimate, /*size*/ 14, &cut_prob_0_3, /*sample_size*/ 4, /*parsample*/ NULL) == IGRAPH_EINVAL); + + printf("Too many samples.\n"); + IGRAPH_ASSERT(igraph_motifs_randesu_estimate(&g_4_3_1, &estimate, /*size*/ 4, &cut_prob_0_4, /*sample_size*/ 40, /*parsample*/ NULL) == IGRAPH_EINVAL); + + printf("Too many parsamples.\n"); + IGRAPH_ASSERT(igraph_motifs_randesu_estimate(&g_4_3_1, &estimate, /*size*/ 4, &cut_prob_0_4, /*sample_size*/ 4, /*parsample*/ &parsample) == IGRAPH_EINVVID); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_50_full); + igraph_destroy(&g_4_3_1); + igraph_vector_destroy(&cut_prob_0_3); + igraph_vector_destroy(&cut_prob_0_4); + igraph_vector_destroy(&cut_prob_01); + igraph_vector_int_destroy(&parsample); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_motifs_randesu_estimate.out b/tests/unit/igraph_motifs_randesu_estimate.out new file mode 100644 index 0000000..ff6db6f --- /dev/null +++ b/tests/unit/igraph_motifs_randesu_estimate.out @@ -0,0 +1,27 @@ +No vertices: +Estimate: 0 + +One vertex: +Estimate: 0 + +Full graph of 50 vertices, motif size 3, sample all, (50 choose 3 = 19600): +Estimate: 19600 + +Full graph of 50 vertices, motif size 3, sample all, cut_prob 0.1 at each level: +Estimate: 15990 + +Full graph of 50 vertices, motif size 3, sample 20: +Estimate: 16040 + +Full graph of 50 vertices, motif size 3, sample first 40: +Estimate: 23800 + +Full graph of 50 vertices, motif size 4, sample 20 (50 choose 4 = 230300): +Estimate: 153070 + +Triangle and a vertex, motif size 4, sample all: +Estimate: 0 + +Cut prob too short. +Too many samples. +Too many parsamples. diff --git a/tests/unit/igraph_motifs_randesu_no.c b/tests/unit/igraph_motifs_randesu_no.c new file mode 100644 index 0000000..0ed3fc4 --- /dev/null +++ b/tests/unit/igraph_motifs_randesu_no.c @@ -0,0 +1,79 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, int size, igraph_vector_t *cut_prob) { + igraph_integer_t result; + IGRAPH_ASSERT(igraph_motifs_randesu_no(graph, &result, size, cut_prob) == IGRAPH_SUCCESS); + printf("Result: %" IGRAPH_PRId "\n\n", result); +} + +int main(void) { + igraph_t g_0, g_1, g_50_full, g_4_3_1; + igraph_vector_t cut_prob_0_3; + igraph_vector_t cut_prob_0_4; + igraph_vector_t cut_prob_01; + igraph_integer_t result; + + igraph_vector_init_real(&cut_prob_0_3, 3, 0.0, 0.0, 0.0); + igraph_vector_init_real(&cut_prob_0_4, 4, 0.0, 0.0, 0.0, 0.0); + igraph_vector_init_real(&cut_prob_01, 3, 0.1, 0.1, 0.1); + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_full(&g_50_full, 50, 0, IGRAPH_NO_LOOPS); + igraph_small(&g_4_3_1, 4, 0, 0,1, 1,2, 2,0, -1); + + printf("No vertices:\n"); + call_and_print(&g_0, /*size*/ 3, &cut_prob_0_3); + + printf("One vertex:\n"); + call_and_print(&g_1, /*size*/ 3, &cut_prob_0_3); + + printf("Full graph of 50 vertices, motif size 3 (50 choose 3 = 19600):\n"); + call_and_print(&g_50_full, /*size*/ 3, &cut_prob_0_3); + + printf("Full graph of 50 vertices, motif size 3, cut_prob 0.1 at each level:\n"); + call_and_print(&g_50_full, /*size*/ 3, &cut_prob_01); + + printf("Full graph of 50 vertices, motif size 4 (50 choose 4 = 230300:\n"); + call_and_print(&g_50_full, /*size*/ 4, &cut_prob_0_4); + + printf("Triangle and a vertex, motif size 4:\n"); + call_and_print(&g_4_3_1, /*size*/ 4, &cut_prob_0_4); + + VERIFY_FINALLY_STACK(); + + printf("Cut prob too short.\n"); + CHECK_ERROR(igraph_motifs_randesu_no(&g_4_3_1, &result, /*size*/ 14, &cut_prob_0_3), IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_50_full); + igraph_destroy(&g_4_3_1); + igraph_vector_destroy(&cut_prob_0_3); + igraph_vector_destroy(&cut_prob_0_4); + igraph_vector_destroy(&cut_prob_01); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_motifs_randesu_no.out b/tests/unit/igraph_motifs_randesu_no.out new file mode 100644 index 0000000..45eaa93 --- /dev/null +++ b/tests/unit/igraph_motifs_randesu_no.out @@ -0,0 +1,19 @@ +No vertices: +Result: 0 + +One vertex: +Result: 0 + +Full graph of 50 vertices, motif size 3 (50 choose 3 = 19600): +Result: 19600 + +Full graph of 50 vertices, motif size 3, cut_prob 0.1 at each level: +Result: 15990 + +Full graph of 50 vertices, motif size 4 (50 choose 4 = 230300: +Result: 230300 + +Triangle and a vertex, motif size 4: +Result: 0 + +Cut prob too short. diff --git a/tests/unit/igraph_neighborhood.c b/tests/unit/igraph_neighborhood.c new file mode 100644 index 0000000..f30d560 --- /dev/null +++ b/tests/unit/igraph_neighborhood.c @@ -0,0 +1,85 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g_empty, g_lm; + igraph_vector_int_list_t result; + igraph_vs_t vids; + + igraph_vector_int_list_init(&result, 0); + igraph_vs_all(&vids); + + igraph_small(&g_empty, 0, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_empty, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int_list(&result); + + printf("Directed graph with loops and multi-edges, order 0:\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_lm, &result, vids, /*order*/ 0, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int_list(&result); + + printf("Directed graph with loops and multi-edges, order 1, ignoring direction:\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_lm, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int_list(&result); + + printf("Directed graph with loops and multi-edges, order 1, only checking IGRAPH_IN:\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_lm, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_IN, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int_list(&result); + + printf("Directed graph with loops and multi-edges, order 10, ignoring direction:\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_lm, &result, vids, /*order*/ 10, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int_list(&result); + + printf("Directed graph with loops and multi-edges, order 2, mindist 2, IGRAPH_OUT:\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_lm, &result, vids, /*order*/ 2, + /*mode*/ IGRAPH_OUT, /*mindist*/ 2) == IGRAPH_SUCCESS); + print_vector_int_list(&result); + + printf("Directed graph with loops and multi-edges, order 4, mindist 4, IGRAPH_ALL:\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_lm, &result, vids, /*order*/ 4, + /*mode*/ IGRAPH_ALL, /*mindist*/ 4) == IGRAPH_SUCCESS); + print_vector_int_list(&result); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Negative order.\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_lm, &result, vids, /*order*/ -4, + /*mode*/ IGRAPH_ALL, /*mindist*/ 4) == IGRAPH_EINVAL); + + printf("Negative mindist.\n"); + IGRAPH_ASSERT(igraph_neighborhood(&g_lm, &result, vids, /*order*/ 4, + /*mode*/ IGRAPH_ALL, /*mindist*/ -4) == IGRAPH_EINVAL); + + igraph_vector_int_list_destroy(&result); + igraph_destroy(&g_empty); + igraph_destroy(&g_lm); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_neighborhood.out b/tests/unit/igraph_neighborhood.out new file mode 100644 index 0000000..db26443 --- /dev/null +++ b/tests/unit/igraph_neighborhood.out @@ -0,0 +1,59 @@ +No vertices: +{ +} +Directed graph with loops and multi-edges, order 0: +{ + 0: ( 0 ) + 1: ( 1 ) + 2: ( 2 ) + 3: ( 3 ) + 4: ( 4 ) + 5: ( 5 ) +} +Directed graph with loops and multi-edges, order 1, ignoring direction: +{ + 0: ( 0 1 2 ) + 1: ( 1 0 3 ) + 2: ( 2 0 3 ) + 3: ( 3 1 2 4 ) + 4: ( 4 3 ) + 5: ( 5 ) +} +Directed graph with loops and multi-edges, order 1, only checking IGRAPH_IN: +{ + 0: ( 0 2 ) + 1: ( 1 0 ) + 2: ( 2 0 ) + 3: ( 3 1 2 ) + 4: ( 4 3 ) + 5: ( 5 ) +} +Directed graph with loops and multi-edges, order 10, ignoring direction: +{ + 0: ( 0 1 2 3 4 ) + 1: ( 1 0 3 2 4 ) + 2: ( 2 0 3 1 4 ) + 3: ( 3 1 2 4 0 ) + 4: ( 4 3 1 2 0 ) + 5: ( 5 ) +} +Directed graph with loops and multi-edges, order 2, mindist 2, IGRAPH_OUT: +{ + 0: ( 3 ) + 1: ( 4 ) + 2: ( 1 4 ) + 3: ( ) + 4: ( ) + 5: ( ) +} +Directed graph with loops and multi-edges, order 4, mindist 4, IGRAPH_ALL: +{ + 0: ( ) + 1: ( ) + 2: ( ) + 3: ( ) + 4: ( ) + 5: ( ) +} +Negative order. +Negative mindist. diff --git a/tests/unit/igraph_neighborhood_graphs.c b/tests/unit/igraph_neighborhood_graphs.c new file mode 100644 index 0000000..776eaf8 --- /dev/null +++ b/tests/unit/igraph_neighborhood_graphs.c @@ -0,0 +1,102 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_and_clear(igraph_graph_list_t *result) { + igraph_integer_t i; + igraph_t *g; + for (i = 0; i < igraph_graph_list_size(result); i++) { + g = igraph_graph_list_get_ptr(result, i); + if (igraph_vcount(g) == 0) { + printf("null graph\n"); + } else if (igraph_ecount(g) == 0 && igraph_vcount(g) == 1) { + printf("One vertex, no edges\n"); + } else { + print_graph_canon(g); + } + } + printf("\n"); + igraph_graph_list_clear(result); +} + +int main(void) { + igraph_t g_empty, g_lm; + igraph_graph_list_t result; + igraph_vs_t vids; + + igraph_graph_list_init(&result, 0); + igraph_vs_all(&vids); + + igraph_small(&g_empty, 0, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_empty, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_and_clear(&result); + + printf("Directed graph with loops and multi-edges, order 0:\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_lm, &result, vids, /*order*/ 0, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_and_clear(&result); + + printf("Directed graph with loops and multi-edges, order 1, ignoring direction:\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_lm, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_and_clear(&result); + + printf("Directed graph with loops and multi-edges, order 1, only checking IGRAPH_IN:\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_lm, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_IN, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_and_clear(&result); + + printf("Directed graph with loops and multi-edges, order 10, ignoring direction:\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_lm, &result, vids, /*order*/ 10, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_and_clear(&result); + + printf("Directed graph with loops and multi-edges, order 2, mindist 2, IGRAPH_OUT:\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_lm, &result, vids, /*order*/ 2, + /*mode*/ IGRAPH_OUT, /*mindist*/ 2) == IGRAPH_SUCCESS); + print_and_clear(&result); + + printf("Directed graph with loops and multi-edges, order 4, mindist 4, IGRAPH_ALL:\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_lm, &result, vids, /*order*/ 4, + /*mode*/ IGRAPH_ALL, /*mindist*/ 4) == IGRAPH_SUCCESS); + print_and_clear(&result); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Negative order.\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_lm, &result, vids, /*order*/ -4, + /*mode*/ IGRAPH_ALL, /*mindist*/ 4) == IGRAPH_EINVAL); + + printf("Negative mindist.\n"); + IGRAPH_ASSERT(igraph_neighborhood_graphs(&g_lm, &result, vids, /*order*/ 4, + /*mode*/ IGRAPH_ALL, /*mindist*/ -4) == IGRAPH_EINVAL); + + igraph_graph_list_destroy(&result); + igraph_destroy(&g_empty); + igraph_destroy(&g_lm); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_neighborhood_graphs.out b/tests/unit/igraph_neighborhood_graphs.out new file mode 100644 index 0000000..fac5f6b --- /dev/null +++ b/tests/unit/igraph_neighborhood_graphs.out @@ -0,0 +1,173 @@ +No vertices: + +Directed graph with loops and multi-edges, order 0: +One vertex, no edges +directed: true +vcount: 1 +edges: { +0 0 +} +One vertex, no edges +One vertex, no edges +One vertex, no edges +One vertex, no edges + +Directed graph with loops and multi-edges, order 1, ignoring direction: +directed: true +vcount: 3 +edges: { +0 1 +0 2 +1 1 +2 0 +} +directed: true +vcount: 3 +edges: { +0 1 +1 1 +1 2 +} +directed: true +vcount: 3 +edges: { +0 1 +1 0 +1 2 +} +directed: true +vcount: 4 +edges: { +0 0 +0 2 +1 2 +2 3 +2 3 +} +directed: true +vcount: 2 +edges: { +0 1 +0 1 +} +One vertex, no edges + +Directed graph with loops and multi-edges, order 1, only checking IGRAPH_IN: +directed: true +vcount: 2 +edges: { +0 1 +1 0 +} +directed: true +vcount: 2 +edges: { +0 1 +1 1 +} +directed: true +vcount: 2 +edges: { +0 1 +1 0 +} +directed: true +vcount: 3 +edges: { +0 0 +0 2 +1 2 +} +directed: true +vcount: 2 +edges: { +0 1 +0 1 +} +One vertex, no edges + +Directed graph with loops and multi-edges, order 10, ignoring direction: +directed: true +vcount: 5 +edges: { +0 1 +0 2 +1 1 +1 3 +2 0 +2 3 +3 4 +3 4 +} +directed: true +vcount: 5 +edges: { +0 1 +0 2 +1 1 +1 3 +2 0 +2 3 +3 4 +3 4 +} +directed: true +vcount: 5 +edges: { +0 1 +0 2 +1 1 +1 3 +2 0 +2 3 +3 4 +3 4 +} +directed: true +vcount: 5 +edges: { +0 1 +0 2 +1 1 +1 3 +2 0 +2 3 +3 4 +3 4 +} +directed: true +vcount: 5 +edges: { +0 1 +0 2 +1 1 +1 3 +2 0 +2 3 +3 4 +3 4 +} +One vertex, no edges + +Directed graph with loops and multi-edges, order 2, mindist 2, IGRAPH_OUT: +One vertex, no edges +One vertex, no edges +directed: true +vcount: 2 +edges: { +0 0 +} +null graph +null graph +null graph + +Directed graph with loops and multi-edges, order 4, mindist 4, IGRAPH_ALL: +null graph +null graph +null graph +null graph +null graph +null graph + +Negative order. +Negative mindist. diff --git a/tests/unit/igraph_neighborhood_size.c b/tests/unit/igraph_neighborhood_size.c new file mode 100644 index 0000000..c108875 --- /dev/null +++ b/tests/unit/igraph_neighborhood_size.c @@ -0,0 +1,86 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g_empty, g_lm; + igraph_vector_int_t result; + igraph_vs_t vids; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_int_init(&result, 0); + igraph_vs_all(&vids); + + igraph_small(&g_empty, 0, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_neighborhood_size(&g_empty, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int(&result); + + printf("Directed graph with loops and multi-edges, order 0:\n"); + IGRAPH_ASSERT(igraph_neighborhood_size(&g_lm, &result, vids, /*order*/ 0, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int(&result); + + printf("Directed graph with loops and multi-edges, order 1, ignoring direction:\n"); + IGRAPH_ASSERT(igraph_neighborhood_size(&g_lm, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int(&result); + + printf("Directed graph with loops and multi-edges, order 1, only checking IGRAPH_IN:\n"); + IGRAPH_ASSERT(igraph_neighborhood_size(&g_lm, &result, vids, /*order*/ 1, + /*mode*/ IGRAPH_IN, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int(&result); + + printf("Directed graph with loops and multi-edges, order 10, ignoring direction:\n"); + IGRAPH_ASSERT(igraph_neighborhood_size(&g_lm, &result, vids, /*order*/ 10, + /*mode*/ IGRAPH_ALL, /*mindist*/ 0) == IGRAPH_SUCCESS); + print_vector_int(&result); + + printf("Directed graph with loops and multi-edges, order 2, mindist 2, IGRAPH_OUT:\n"); + IGRAPH_ASSERT(igraph_neighborhood_size(&g_lm, &result, vids, /*order*/ 2, + /*mode*/ IGRAPH_OUT, /*mindist*/ 2) == IGRAPH_SUCCESS); + print_vector_int(&result); + + printf("Directed graph with loops and multi-edges, order 4, mindist 4, IGRAPH_ALL:\n"); + IGRAPH_ASSERT(igraph_neighborhood_size(&g_lm, &result, vids, /*order*/ 4, + /*mode*/ IGRAPH_ALL, /*mindist*/ 4) == IGRAPH_SUCCESS); + print_vector_int(&result); + + VERIFY_FINALLY_STACK(); + + printf("Negative order.\n"); + CHECK_ERROR(igraph_neighborhood_size(&g_lm, &result, vids, /*order*/ -4, + /*mode*/ IGRAPH_ALL, /*mindist*/ 4), IGRAPH_EINVAL); + + printf("Negative mindist.\n"); + CHECK_ERROR(igraph_neighborhood_size(&g_lm, &result, vids, /*order*/ 4, + /*mode*/ IGRAPH_ALL, /*mindist*/ -4), IGRAPH_EINVAL); + + igraph_vector_int_destroy(&result); + igraph_destroy(&g_empty); + igraph_destroy(&g_lm); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_neighborhood_size.out b/tests/unit/igraph_neighborhood_size.out new file mode 100644 index 0000000..3560f8f --- /dev/null +++ b/tests/unit/igraph_neighborhood_size.out @@ -0,0 +1,16 @@ +No vertices: +( ) +Directed graph with loops and multi-edges, order 0: +( 1 1 1 1 1 1 ) +Directed graph with loops and multi-edges, order 1, ignoring direction: +( 3 3 3 4 2 1 ) +Directed graph with loops and multi-edges, order 1, only checking IGRAPH_IN: +( 2 2 2 3 2 1 ) +Directed graph with loops and multi-edges, order 10, ignoring direction: +( 5 5 5 5 5 1 ) +Directed graph with loops and multi-edges, order 2, mindist 2, IGRAPH_OUT: +( 1 1 2 0 0 0 ) +Directed graph with loops and multi-edges, order 4, mindist 4, IGRAPH_ALL: +( 0 0 0 0 0 0 ) +Negative order. +Negative mindist. diff --git a/tests/unit/igraph_neighbors.c b/tests/unit/igraph_neighbors.c new file mode 100644 index 0000000..dcd12dc --- /dev/null +++ b/tests/unit/igraph_neighbors.c @@ -0,0 +1,39 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t v; + + igraph_vector_int_init(&v, 0); + igraph_small(&g, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, 2,2, -1); + /* Wrong mode */ + CHECK_ERROR(igraph_neighbors(&g, &v, 2, (igraph_neimode_t)0), IGRAPH_EINVMODE); /* conv for c++ */ + + /* Vertex does not exist */ + CHECK_ERROR(igraph_neighbors(&g, &v, 4, IGRAPH_ALL), IGRAPH_EINVVID); + + igraph_vector_int_destroy(&v); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_pagerank.c b/tests/unit/igraph_pagerank.c new file mode 100644 index 0000000..ca70371 --- /dev/null +++ b/tests/unit/igraph_pagerank.c @@ -0,0 +1,401 @@ +/* -*- 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, see . +*/ + +#include +#include +#include + +#include "test_utilities.h" + +int is_almost_one(igraph_real_t x) { + /* 2^5 = 32 is 5 binary digits of tolerance */ + if (fabs(x - 1) > 32*DBL_EPSILON) { + printf("Expected value to be 1, but actually got %15g.", x); + return 0; + } + return 1; +} + +int main(void) { + + igraph_t g; + igraph_vector_t res, reset, weights; + igraph_arpack_options_t arpack_options; + igraph_real_t value; + igraph_error_t err; + + /* The ARPACK method uses a random perturbation to the in-degrees + to set the starting vector for ARPACK. */ + igraph_rng_seed(igraph_rng_default(), 137); + + igraph_arpack_options_init(&arpack_options); + + /* Test graphs taken from http://www.iprcom.com/papers/pagerank/ */ + + printf("\nTest graph 1\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0,1, 1,2, 2,0, 3,2, 0,2, + -1); + + igraph_vector_init(&res, 0); + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_vector_destroy(&res); + + igraph_destroy(&g); + + printf("\nTest graph 2\n"); + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0,1, 0,2, 0,3, 1,0, 2,0, 3,0, + 3,4, 3,5, 3,6, 3,7, 4,0, 5,0, + 6,0, 7,0, + -1); + + igraph_vector_init(&res, 0); + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_vector_destroy(&res); + igraph_destroy(&g); + + /* Undirected star graph */ + + printf("\nUndirected star\n"); + + igraph_star(&g, 11, IGRAPH_STAR_UNDIRECTED, 0); + igraph_vector_init(&res, 0); + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + /* Check twice more for consistency, this time without explicitly + * supplied ARPACK options */ + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, 0); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, 0); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 0, 0.85, 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + /* Check personalized PageRank */ + + printf("\nPersonalized PageRank\n"); + + igraph_personalized_pagerank_vs(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 0, 0.5, + igraph_vss_1(1), 0, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_personalized_pagerank_vs(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 0, 0.5, + igraph_vss_1(1), 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + + /* Errors */ + + igraph_set_error_handler(igraph_error_handler_ignore); + igraph_vector_init(&reset, 2); + err = igraph_personalized_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, 0, + igraph_vss_all(), 0, 0.85, &reset, 0, + &arpack_options); + IGRAPH_ASSERT(err == IGRAPH_EINVAL); + + err = igraph_personalized_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, 0, + igraph_vss_all(), 0, 0.85, &reset, 0, 0); + IGRAPH_ASSERT(err == IGRAPH_EINVAL); + + igraph_vector_resize(&reset, 10); + igraph_vector_fill(&reset, 0); + err = igraph_personalized_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, + &res, 0, igraph_vss_all(), 0, 0.85, + &reset, 0, &arpack_options); + IGRAPH_ASSERT(err == IGRAPH_EINVAL); + + err = igraph_personalized_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, + &res, 0, igraph_vss_all(), 0, 0.85, + &reset, 0, 0); + IGRAPH_ASSERT(err == IGRAPH_EINVAL); + + igraph_vector_destroy(&reset); + igraph_destroy(&g); + igraph_set_error_handler(igraph_error_handler_abort); + + + /* Special cases: check for empty graph */ + /* The ARPACK method has a special code path for this case */ + + printf("\nEdgeless graph\n"); + + igraph_empty(&g, 10, IGRAPH_UNDIRECTED); + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 1, 0.85, 0, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 1, 0.85, 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_destroy(&g); + + /* Special cases: check for empty graph, personalized */ + /* The ARPACK method has a special code path for this case */ + + printf("\nEdgeless graph, personalized PageRank\n"); + + igraph_empty(&g, 4, IGRAPH_UNDIRECTED); + igraph_vector_init_range(&reset, 1, 5); + + igraph_personalized_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &reset, 0, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_personalized_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &reset, 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + /* We also test the personalized case on a non-empty disconnected case, + * which does not use the special code path for the ARPACK version. + * The result must be the same for ARPACK and PRPACK. */ + + printf("\nOne edge, two isolated vertices, personalized\n"); + igraph_add_edge(&g, 0, 1); + + igraph_personalized_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &reset, 0, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_personalized_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &reset, 0, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_vector_destroy(&reset); + igraph_destroy(&g); + + /* Special cases: check for full graph, zero weights */ + /* The ARPACK method has a special code path for this case */ + + printf("\nComplete graph, zero weights\n"); + + igraph_full(&g, 10, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_vector_init(&weights, 45); + igraph_vector_fill(&weights, 0); + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &weights, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &weights, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* Another test case for PageRank (bug #792352) */ + + printf("\nTest graph 3\n"); + + igraph_small(&g, 9, IGRAPH_DIRECTED, 0, 5, 1, 5, 2, 0, 3, 1, 5, 4, 5, 7, 6, 0, 8, 0, 8, 1, -1); + igraph_vector_init(&weights, 9); + VECTOR(weights)[0] = 4; + VECTOR(weights)[1] = 5; + VECTOR(weights)[2] = 5; + VECTOR(weights)[3] = 4; + VECTOR(weights)[4] = 4; + VECTOR(weights)[5] = 4; + VECTOR(weights)[6] = 3; + VECTOR(weights)[7] = 4; + VECTOR(weights)[8] = 4; + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &weights, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &weights, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + /* Multigraph */ + + printf("\nSmall undirected multigraph (unweighted)\n"); + + igraph_small(&g, 0, IGRAPH_UNDIRECTED, + 0,1, 1,2, 1,2, + -1); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 1, 0.85, NULL, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 1, 0.85, NULL, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + printf("\nSmall undirected multigraph (unit weights)\n"); + + igraph_vector_init(&weights, 3); + igraph_vector_fill(&weights, 1.0); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &weights, &arpack_options); + printf("ARPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res, &value, + igraph_vss_all(), 1, 0.85, &weights, 0); + printf("PRPACK: "); print_vector(&res); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + igraph_vector_destroy(&res); + + /* Graph with more than 127 vertices. PRPACK uses a different method above this size. */ + + { + igraph_vector_int_t edges_to_delete; + igraph_vector_t res_arpack, res_prpack; + igraph_vector_t weights; + igraph_integer_t i, n; + + printf("\nLarge test graph, unweighted\n"); + + /* 243 vertices, 729 edges */ + igraph_de_bruijn(&g, 3, 5); + + /* We delete some edges to break the symmetry of the graph. + * Otherwise all vertices would have the same PageRank. */ + igraph_vector_int_init_range(&edges_to_delete, 0, 38); + igraph_delete_edges(&g, igraph_ess_vector(&edges_to_delete)); + igraph_vector_int_destroy(&edges_to_delete); + + /* Note: This test graph is not connected and has self-loops. */ + + igraph_vector_init(&res_arpack, 0); + igraph_vector_init(&res_prpack, 0); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res_arpack, &value, + igraph_vss_all(), 1, 0.85, NULL, &arpack_options); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res_prpack, &value, + igraph_vss_all(), 1, 0.85, NULL, NULL); + IGRAPH_ASSERT(is_almost_one(value)); + + n = igraph_vector_size(&res_arpack); + for (i=0; i < n; ++i) { + igraph_real_t ar = VECTOR(res_arpack)[i]; + igraph_real_t pr = VECTOR(res_prpack)[i]; + if (fabs(ar - pr) > 1e-12) { + printf("Unexpected difference between ARPACK and PRPACK results for vertex %" IGRAPH_PRId ":\n" + "ARPACK: %g\n" + "PRPACK: %g\n" + "Difference: %g\n", + i, ar, pr, fabs(ar - pr)); + } + } + + printf("\nLarge test graph, weighted\n"); + + igraph_vector_init_range(&weights, igraph_ecount(&g) + 1, 2*igraph_ecount(&g) + 1); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &res_arpack, &value, + igraph_vss_all(), 1, 0.85, &weights, &arpack_options); + IGRAPH_ASSERT(is_almost_one(value)); + + igraph_pagerank(&g, IGRAPH_PAGERANK_ALGO_PRPACK, &res_prpack, &value, + igraph_vss_all(), 1, 0.85, &weights, NULL); + IGRAPH_ASSERT(is_almost_one(value)); + + n = igraph_vector_size(&res_arpack); + for (i=0; i < n; ++i) { + igraph_real_t ar = VECTOR(res_arpack)[i]; + igraph_real_t pr = VECTOR(res_prpack)[i]; + if (fabs(ar - pr) > 1e-12) { + printf("Unexpected difference between ARPACK and PRPACK results for vertex %" IGRAPH_PRId ":\n" + "ARPACK: %g\n" + "PRPACK: %g\n" + "Difference: %g\n", + i, ar, pr, fabs(ar - pr)); + } + } + + igraph_vector_destroy(&weights); + + igraph_vector_destroy(&res_arpack); + igraph_vector_destroy(&res_prpack); + + igraph_destroy(&g); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_pagerank.out b/tests/unit/igraph_pagerank.out new file mode 100644 index 0000000..38ae365 --- /dev/null +++ b/tests/unit/igraph_pagerank.out @@ -0,0 +1,52 @@ + +Test graph 1 +ARPACK: ( 0.288717 0.201989 0.389109 0.120186 ) +PRPACK: ( 0.288717 0.201989 0.389109 0.120186 ) + +Test graph 2 +ARPACK: ( 0.336204 0.0759047 0.0759047 0.205964 0.0765056 0.0765056 0.0765056 0.0765056 ) +PRPACK: ( 0.336204 0.0759047 0.0759047 0.205964 0.0765056 0.0765056 0.0765056 0.0765056 ) + +Undirected star +ARPACK: ( 0.46683 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 ) +PRPACK: ( 0.46683 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 ) +ARPACK: ( 0.46683 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 ) +PRPACK: ( 0.46683 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 ) +ARPACK: ( 0.46683 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 ) +PRPACK: ( 0.46683 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 0.053317 ) + +Personalized PageRank +ARPACK: ( 0.333333 0.516667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 ) +PRPACK: ( 0.333333 0.516667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 0.0166667 ) + +Edgeless graph +ARPACK: ( 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 ) +PRPACK: ( 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 ) + +Edgeless graph, personalized PageRank +ARPACK: ( 0.1 0.2 0.3 0.4 ) +PRPACK: ( 0.1 0.2 0.3 0.4 ) + +One edge, two isolated vertices, personalized +ARPACK: ( 0.36036 0.38038 0.111111 0.148148 ) +PRPACK: ( 0.36036 0.38038 0.111111 0.148148 ) + +Complete graph, zero weights +ARPACK: ( 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 ) +PRPACK: ( 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 ) + +Test graph 3 +ARPACK: ( 0.143734 0.104639 0.045995 0.045995 0.155268 0.257112 0.045995 0.155268 0.045995 ) +PRPACK: ( 0.143734 0.104639 0.045995 0.045995 0.155268 0.257112 0.045995 0.155268 0.045995 ) + +Small undirected multigraph (unweighted) +ARPACK: ( 0.187838 0.486486 0.325676 ) +PRPACK: ( 0.187838 0.486486 0.325676 ) + +Small undirected multigraph (unit weights) +ARPACK: ( 0.187838 0.486486 0.325676 ) +PRPACK: ( 0.187838 0.486486 0.325676 ) + +Large test graph, unweighted + +Large test graph, weighted diff --git a/tests/unit/igraph_path_length_hist.c b/tests/unit/igraph_path_length_hist.c new file mode 100644 index 0000000..adde842 --- /dev/null +++ b/tests/unit/igraph_path_length_hist.c @@ -0,0 +1,68 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g, igraph_vector_t *res, igraph_bool_t directed) { + igraph_real_t unconnected; + igraph_path_length_hist(g, res, &unconnected, directed); + + printf("result: "); + igraph_vector_print(res); + printf("unconnected: %g\n\n", unconnected); + + igraph_destroy(g); + +} + +int main(void) { + igraph_t g; + igraph_vector_t res; + + igraph_vector_init(&res, 0); + + printf("Graph with no vertices:\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, -1); + print_and_destroy(&g, &res, 1); + + printf("Graph with one vertex:\n"); + igraph_small(&g, 1, IGRAPH_UNDIRECTED, -1); + print_and_destroy(&g, &res, 1); + + printf("Graph with two vertices:\n"); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, -1); + print_and_destroy(&g, &res, 1); + + printf("Graph with two connected vertices:\n"); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + print_and_destroy(&g, &res, 1); + + printf("Graph with loops and multiple edges, undirected:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,2, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, &res, 0); + + printf("Graph with loops and multiple edges, directed:\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,2, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, &res, 1); + + igraph_vector_destroy(&res); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_path_length_hist.out b/tests/unit/igraph_path_length_hist.out new file mode 100644 index 0000000..f636d3a --- /dev/null +++ b/tests/unit/igraph_path_length_hist.out @@ -0,0 +1,24 @@ +Graph with no vertices: +result: +unconnected: 0 + +Graph with one vertex: +result: +unconnected: 0 + +Graph with two vertices: +result: +unconnected: 1 + +Graph with two connected vertices: +result: 1 +unconnected: 0 + +Graph with loops and multiple edges, undirected: +result: 6 3 1 +unconnected: 5 + +Graph with loops and multiple edges, directed: +result: 7 5 1 +unconnected: 17 + diff --git a/tests/unit/igraph_perfect.c b/tests/unit/igraph_perfect.c new file mode 100644 index 0000000..5400493 --- /dev/null +++ b/tests/unit/igraph_perfect.c @@ -0,0 +1,135 @@ +/* -*- 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t graph, comp_graph; + igraph_bool_t is_perfect; + igraph_error_handler_t *ehandler; + + igraph_rng_seed(igraph_rng_default(), 42); + + // Bipartite + //========================================================== + igraph_bipartite_game_gnm(&graph, NULL, 10, 10, 20, IGRAPH_UNDIRECTED, IGRAPH_ALL); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + + // Complement to star graph size 10 - chordal + //========================================================== + igraph_star(&graph, 10, IGRAPH_STAR_UNDIRECTED, 0); + igraph_complementer(&comp_graph, &graph, 0); + igraph_is_perfect(&comp_graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + igraph_destroy(&comp_graph); + + // A cycle of size 5 + //========================================================== + igraph_ring(&graph, 5, IGRAPH_UNDIRECTED, 0, 1); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(!is_perfect); + igraph_destroy(&graph); + + // A complement cycle of size 7 + //========================================================== + igraph_ring(&graph, 7, IGRAPH_UNDIRECTED, 0, 1); + igraph_complementer(&comp_graph, &graph, 0); + igraph_is_perfect(&comp_graph, &is_perfect); + IGRAPH_ASSERT(!is_perfect); + igraph_destroy(&graph); + igraph_destroy(&comp_graph); + + // A random tree + //========================================================== + igraph_tree_game(&graph, 10, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + + // 4X4 grid + //========================================================== + igraph_vector_int_t dims; + igraph_vector_int_init(&dims, 2); + VECTOR(dims)[0] = 4; + VECTOR(dims)[1] = 4; + igraph_square_lattice(&graph, &dims, 1, IGRAPH_UNDIRECTED, /* mutual */ 0, /* periodic */ 0); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + igraph_vector_int_destroy(&dims); + + // Paley graph of order 9 + //========================================================== + igraph_small(&graph, 9, IGRAPH_UNDIRECTED, + 0, 1, 0, 3, 0, 6, 0, 2, 1, 2, 1, 4, 1, 7, 2, 5, 2, 8, + 3, 4, 3, 5, 3, 6, 4, 5, 4, 7, 5, 8, 6, 7, 7, 8, 6, 8, -1); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + + // famous graph - chvatal + //========================================================== + igraph_famous(&graph, "Chvatal"); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(!is_perfect); + igraph_destroy(&graph); + + // famous graph - house + //========================================================== + igraph_famous(&graph, "House"); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + + // Null graph + //========================================================== + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + + // Singleton graph + //========================================================== + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + + // Empty graph + //========================================================== + igraph_empty(&graph, 2, IGRAPH_UNDIRECTED); + igraph_is_perfect(&graph, &is_perfect); + IGRAPH_ASSERT(is_perfect); + igraph_destroy(&graph); + + // Test directed paths + igraph_bipartite_game_gnm(&graph, NULL, 10, 10, 20, IGRAPH_DIRECTED, IGRAPH_ALL); + ehandler = igraph_set_error_handler(igraph_error_handler_printignore); + IGRAPH_ASSERT(igraph_is_perfect(&graph, &is_perfect) == IGRAPH_EINVAL); + igraph_set_error_handler(ehandler); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_permute_vertices.c b/tests/unit/igraph_permute_vertices.c new file mode 100644 index 0000000..2770930 --- /dev/null +++ b/tests/unit/igraph_permute_vertices.c @@ -0,0 +1,66 @@ +/* + 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, see . +*/ + +#include "test_utilities.h" + +int main(void) { + igraph_t graph, pgraph; + igraph_vector_int_t perm; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_small(&graph, 6, IGRAPH_DIRECTED, + 0,1, 1,0, 2,1, 2,2, 5,4, 5,4, + -1); + + printf("Graph before permuting vertices:\n"); + print_graph(&graph); + + igraph_vector_int_init_int(&perm, 6, + 0, 1, 5, 3, 4, 2); + + igraph_permute_vertices(&graph, &pgraph, &perm); + + printf("\nGraph after permuting vertices:\n"); + print_graph(&pgraph); + + igraph_destroy(&pgraph); + + /* Now we test invalid input. */ + + /* Out-of-range value in permutation */ + VECTOR(perm)[0] = 7; + CHECK_ERROR(igraph_permute_vertices(&graph, &pgraph, &perm), IGRAPH_EINVAL); + + /* Duplicate value in permutation */ + VECTOR(perm)[0] = 1; + CHECK_ERROR(igraph_permute_vertices(&graph, &pgraph, &perm), IGRAPH_EINVAL); + + /* Invalid permutation vector length */ + VECTOR(perm)[0] = 0; + VECTOR(perm)[2] = 2; + igraph_vector_int_resize(&perm, 5); + CHECK_ERROR(igraph_permute_vertices(&graph, &pgraph, &perm), IGRAPH_EINVAL); + + igraph_vector_int_destroy(&perm); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_permute_vertices.out b/tests/unit/igraph_permute_vertices.out new file mode 100644 index 0000000..7aa06aa --- /dev/null +++ b/tests/unit/igraph_permute_vertices.out @@ -0,0 +1,23 @@ +Graph before permuting vertices: +directed: true +vcount: 6 +edges: { +0 1 +1 0 +2 1 +2 2 +5 4 +5 4 +} + +Graph after permuting vertices: +directed: true +vcount: 6 +edges: { +0 1 +1 0 +5 1 +5 5 +2 4 +2 4 +} diff --git a/tests/unit/igraph_power_law_fit.c b/tests/unit/igraph_power_law_fit.c new file mode 100644 index 0000000..c2cb761 --- /dev/null +++ b/tests/unit/igraph_power_law_fit.c @@ -0,0 +1,331 @@ +/* -*- 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 +#include "test_utilities.h" + +void print_result(const igraph_plfit_result_t* result) { + printf("continuous = %s\n", result->continuous ? "true" : "false"); + printf("alpha = %.5f\n", result->alpha); + printf("xmin = %.5f\n", result->xmin); + printf("L = %.5f\n", result->L); + printf("D = %.5f\n", result->D); + printf("====================\n"); +} + +int test_continuous(void) { + igraph_plfit_result_t result; + igraph_vector_t vector; + double data[] = { 1.52219974, 6.80675663, 1.02798042, 1.31180733, 3.97473174, + 1.17209342, 1.64889191, 2.47764721, 1.32939375, 3.03762554, 1.62638327, + 6.08405495, 1.70890382, 1.05294973, 1.17408407, 4.48945532, 1.16777371, + 2.52502391, 1.09755984, 1.63838051, 1.03811206, 1.47224168, 1.57161431, + 1.60163451, 2.08280263, 1.04678340, 1.33317526, 1.58588741, 1.26484666, + 1.02367503, 1.57045702, 3.42374138, 1.23190611, 1.09378228, 1.04959505, + 1.05818408, 1.43879491, 2.22750459, 1.41027204, 1.81964745, 2.80239939, + 1.25399323, 1.07479219, 3.94616077, 1.26367914, 1.87367507, 1.35741026, + 1.14867526, 7.33024762, 1.87957274, 2.79258534, 1.21682159, 1.61194300, + 2.81885973, 1.21514746, 1.12850917, 51.85245035, 1.21883209, 1.04861029, + 1.69215609, 2.18429429, 1.59752172, 1.41909984, 3.14393355, 1.18298455, + 1.67063821, 1.88568524, 1.07445906, 1.45007973, 1.12568920, 1.56806310, + 1.36996101, 1.19440982, 6.57296980, 1.35860725, 1.06552137, 1.16950701, + 1.34750790, 1.66977492, 1.22658722, 1.62247444, 1.23458784, 8.55843760, + 1.70020162, 4.76368831, 1.04846170, 1.13689661, 1.94449567, 1.10584812, + 1.32525767, 1.26640912, 1.91372972, 1.56185373, 2.37829675, 1.04616674, + 2.43549177, 1.14961092, 1.82106455, 1.25818298, 1.64763037, 1.43019402, + 1.50439978, 1.90281251, 1.34827040, 1.57935671, 1.77260751, 1.06976614, + 1.12236012, 2.19770254, 1.51825533, 1.19027804, 1.08307524, 1.57912902, + 3.33313888, 2.14005088, 1.38341873, 1.20088138, 1.25870539, 1.03811620, + 1.86622820, 2.99310953, 1.55615055, 2.12364873, 4.49081000, 1.01274439, + 1.22373389, 3.79059729, 3.10099275, 2.70218546, 1.03609624, 2.20776919, + 1.00651347, 1.87344592, 1.04903307, 1.24899747, 1.20377911, 1.12706494, + 1.01706713, 7.01069306, 1.05363146, 2.50105512, 1.11168552, 1.71133998, + 1.17714528, 1.37986755, 2.20981534, 1.18179277, 2.07982010, 4.04967099, + 1.00680257, 1.62850069, 2.58816230, 1.35079027, 1.03382890, 4.54326500, + 1.62489905, 1.36102570, 1.52349738, 1.06606346, 7.80558026, 1.02602538, + 1.43330925, 1.36040920, 9.29692547, 15.27015690, 1.75966437, 1.02635409, + 1.40421505, 2.87296958, 1.46232202, 1.87065204, 3.37278803, 1.82589564, + 1.06488044, 1.72568108, 1.21062115, 4.39311214, 1.12636227, 2.20820528, + 1.09826903, 2.58989998, 1.34944949, 1.08654244, 2.38021951, 3.96308780, + 1.37494639, 1.18245279, 3.72506217, 3.79775023, 1.19018356, 2.86924476, + 3.40015888, 1.92317855, 1.55203754, 1.34985008, 1.31480190, 1.65899877, + 4.77446435, 1.41073246, 1.35555456, 2.40543613, 2.72162935, 1.34475982, + 1.41342115, 5.15278473, 1.69654436, 3.21081899, 1.18822397, 1.40394863, + 1.06793574, 1.67085563, 1.08125975, 1.11765459, 1.17245045, 1.15711479, + 1.18656910, 1.61296203, 1.71427634, 1.24017302, 2.05291524, 2.52658791, + 2.04645295, 34.07541626, 1.32670899, 1.03893757, 1.08957199, 5.55332328, + 1.17276097, 1.60389480, 2.02098430, 2.92934928, 1.00558653, 1.05830070, + 1.81440889, 3.85044779, 1.12317456, 1.39547640, 2.93105179, 1.95048788, + 1.05602445, 1.96855429, 1.60432293, 3.28820202, 1.50117325, 1.19775674, + 1.28280841, 1.08318646, 1.02098264, 1.24861938, 1.06511473, 1.07549717, + 3.57739126, 1.07265409, 1.06312441, 1.16296512, 3.83654484, 2.02366951, + 1.73168875, 1.60443228, 2.30779766, 1.50531775, 1.31925607, 1.87926179, + 1.86249354, 2.14768716, 2.31583955, 2.15651148, 1.29677318, 1.10110071, + 1.03383916, 1.50665009, 1.16502917, 1.40055008, 2.80847193, 1.29824634, + 2.76239920, 1.73123621, 1.15286577, 1.89493526, 1.63112634, 1.17828846, + 1.01293513, 1.84834048, 4.19026736, 1.82684815, 3.51812301, 1.33499862, + 2.03087497, 1.32419883, 1.34126954, 1.98250684, 1.00025697, 1.59416883, + 6.38249787, 2.79055559, 1.57750678, 1.36953983, 1.37513919, 3.63573178, + 1.15637432, 9.28386344, 1.16947695, 1.54995742, 1.44018755, 1.29332881, + 1.81274872, 1.14900153, 1.07117403, 1.17035915, 1.39229249, 1.96645872, + 1.09147706, 1.25211993, 1.07092474, 1.85394206, 1.29807741, 3.41499510, + 1.22444449, 1.00913782, 3.87431854, 1.01072376, 1.01186727, 3.00175639, + 2.52183377, 1.23992099, 1.69819010, 1.36850400, 1.14577814, 1.06035078, + 1.08414298, 1.55920217, 5.07059630, 1.15434572, 1.41873305, 1.24712256, + 1.10478618, 1.30707247, 1.85719110, 1.89873207, 1.72629431, 1.65171651, + 7.10864875, 2.31945709, 1.06722361, 1.26696259, 2.23845503, 1.38674196, + 1.91015397, 1.29590323, 1.10448028, 4.52757499, 2.00258408, 1.38299092, + 1.01431427, 1.54039270, 1.34880396, 1.08784083, 1.35553378, 1.37307373, + 1.32320467, 1.50261683, 6.91050685, 1.06083157, 1.20841351, 2.92719840, + 2.82178183, 2.05765813, 1.84621661, 1.04677388, 2.13801850, 1.39654855, + 1.13037727, 1.37887598, 1.03221650, 1.15981176, 1.09896163, 1.88624084, + 1.43459062, 1.54587662, 1.48604380, 2.06197392, 1.97079675, 4.31388672, + 2.94376994, 3.48708489, 1.09674551, 2.46926816, 1.23705940, 1.57512843, + 1.15595205, 1.18432818, 1.54298936, 1.60600489, 1.07361787, 1.38666771, + 1.45533003, 1.78940830, 1.33799752, 1.12955889, 4.59400278, 1.15170228, + 1.39346636, 1.61408789, 2.21293753, 5.33166143, 1.18147947, 1.54426891, + 1.32496426, 1.25037632, 3.31244261, 1.36211171, 1.82239599, 1.75235087, + 1.67044831, 1.24802350, 1.34776327, 1.34740665, 1.30664120, 1.06852680, + 1.22513631, 1.25310923, 1.36394926, 1.07796356, 3.10823551, 1.46770227, + 1.40264883, 1.08787681, 1.26460358, 1.10348946, 2.03168839, 1.09435135, + 1.66991715, 1.19738540, 1.28922229, 2.85704149, 1.33952521, 1.73497688, + 2.90052876, 5.34596348, 1.36399078, 3.38399264, 1.06089658, 1.09370142, + 1.37523679, 3.01964907, 1.40684792, 1.11312672, 2.44666372, 1.73953904, + 1.65569280, 1.05813000, 2.02893022, 1.72877601, 1.55758690, 1.83904301, + 1.14316984, 1.17792251, 1.44106281, 9.67126482, 1.93207441, 1.08242887, + 2.87271135, 2.19095115, 2.13195479, 1.02355472, 1.18218470, 1.30907724, + 1.13291587, 2.85659336, 12.62726889, 1.18818589, 1.02852443, 1.12838670, + 1.36349361, 1.34817100, 1.30535737, 3.22225028, 1.28680350, 1.83979657, + 1.11088952, 1.43866586, 8.52587567, 3.73988696, 2.65816056, 1.17373111, + 2.61567111, 3.24024082, 2.96798864, 1.05335616, 1.31159271, 1.36485918, + 1.24988767, 7.80609746, 1.54892174, 1.10682809, 1.21728827, 1.20429971, + 1.72719055, 1.78534831, 1.04414979, 1.25646988, 1.19788383, 1.08854812, + 1.04859628, 1.04676064, 5.07295341, 3.83595341, 1.61079632, 1.10528426, + 1.15050241, 2.78129736, 1.25494119, 1.28692155, 1.06812292, 3.29393761, + 1.37542463, 1.67241953, 1.21698665, 10.57727604, 8.63598976, 1.18886984, + 1.30609583, 9.47777457, 1.69612900, 2.23002585, 1.58461615, 1.04110023, + 3.08140806, 1.39599251, 1.06575789, 1.29741002, 1.75253864, 1.82594258, + 1.15111702, 1.17370053, 1.15254396, 1.94401179, 5.36344596, 4.66322185, + 1.15073993, 3.21478159, 1.39843306, 1.03961906, 5.72845289, 1.72454161, + 1.04610704, 1.38975310, 1.77732797, 1.10139931, 2.23656355, 1.89952669, + 1.72136921, 1.15798212, 1.59545971, 1.08789161, 1.93272206, 2.57480708, + 1.04977784, 2.00874078, 3.40065861, 1.00978603, 3.97804652, 1.54762586, + 1.01015493, 1.15148220, 1.15246483, 19.67426012, 1.33290993, 2.33137522, + 1.12841749, 1.73407057, 2.00469493, 1.27418995, 1.49814918, 1.10398785, + 1.20063760, 1.05536150, 1.87616599, 1.49305736, 1.60241346, 1.16666060, + 1.05013736, 1.77929210, 1.00206028, 3.41096863, 1.47499925, 1.14071240, + 1.65361002, 1.76466424, 8.49298111, 1.41069285, 2.11681605, 4.90260842, + 1.13029658, 1.20802818, 1.42525579, 1.00310774, 1.08082363, 9.95194247, + 2.82773946, 2.77420002, 1.82543685, 1.28557906, 1.97711769, 1.19001264, + 1.95712650, 1.54230291, 1.31625757, 2.36364128, 1.11523099, 1.00343756, + 1.71299382, 1.44667100, 2.38154868, 1.41174217, 1.80660493, 1.51020853, + 1.16761479, 1.25898190, 1.18150781, 1.58465451, 2.03560597, 3.48531184, + 1.21187672, 1.35111036, 1.02954922, 1.90892663, 3.99078548, 5.67385199, + 4.38055264, 1.17446048, 13.41617858, 1.60241740, 1.14811206, 4.68120263, + 3.83763710, 2.66095263, 1.83338503, 4.75973082, 1.08982301, 4.04104276, + 1.34220189, 1.06135891, 2.71185882, 1.46085873, 1.09915614, 10.35178646, + 2.54402271, 2.65696704, 1.31388649, 1.02942408, 1.57780748, 1.01552697, + 2.24860361, 2.22011778, 1.13595134, 1.11492512, 2.11966788, 1.20420149, + 1.11112428, 3.09324603, 2.87240762, 1.50486558, 1.92227231, 4.12480449, + 1.58244751, 1.69922308, 6.28134904, 2.91944178, 1.85386792, 1.41799519, + 1.64636127, 2.05837832, 1.07153521, 2.05376943, 2.60053549, 1.09773382, + 1.54671309, 1.68007415, 3.43941489, 1.41601033, 2.00237256, 1.20830978, + 1.25582363, 1.10830461, 1.24850906, 1.88035202, 1.70557719, 1.04191110, + 1.33501003, 1.33554804, 1.36935735, 4.79153510, 1.06566392, 1.14495966, + 1.90020028, 1.08266994, 1.20588153, 1.40730214, 4.34320304, 1.71762330, + 1.06620797, 1.39695239, 1.03024563, 3.94971225, 5.02945862, 1.06145571, + 1.42511911, 2.13889169, 1.04986044, 1.91400616, 5.50708156, 1.52870464, + 1.11303137, 1.05282759, 1.83793940, 3.05244089, 2.64499634, 1.51688076, + 2.63350152, 1.31014486, 1.69462474, 1.67792130, 1.34236945, 1.02358460, + 1.04593509, 1.04007620, 1.87990081, 1.28585413, 1.01636283, 3.55338495, + 1.19542700, 1.23630628, 1.32321942, 4.03762786, 1.25379147, 1.12330233, + 1.24966418, 1.26323243, 1.14779989, 1.20378343, 1.01531796, 1.44500318, + 1.72723672, 15.68799957, 1.37641063, 7.00788166, 3.89674130, 1.68303382, + 1.10089816, 1.72831362, 2.70479861, 1.75821836, 2.32404215, 2.64165162, + 1.42441301, 1.83256456, 1.12548819, 4.81273800, 2.52840227, 2.68430190, + 1.00928919, 1.02438446, 1.33909276, 2.32261242, 1.01299124, 1.07614975, + 1.66823898, 1.97172786, 1.01707292, 1.68325092, 1.76834032, 1.08952069, + 1.02265517, 1.96843176, 1.83351706, 1.92704772, 18.44811035, 1.00178046, + 2.70555953, 1.35839004, 1.04834633, 1.26649072, 2.87152600, 4.12536409, + 1.25200853, 1.71199647, 1.61175739, 1.26313274, 1.75224120, 2.70412800, + 1.33998630, 1.61271556, 2.65784769, 10.38771107, 1.33121364, 1.01207979, + 2.00238212, 2.50195600, 1.96917548, 1.71618169, 1.37050585, 10.11861690, + 1.18339112, 1.80083386, 2.88582103, 1.21935761, 2.37900131, 1.49449487, + 4.75106319, 2.33977804, 2.87963540, 1.01807103, 3.74847411, 1.71981276, + 1.50726964, 1.20723219, 1.37904840, 1.04565533, 1.59877004, 1.11481349, + 2.17320556, 2.07108468, 1.23274077, 1.75180110, 1.27558910, 1.63240839, + 1.58760550, 1.01266256, 1.30395323, 1.14618521, 1.02385023, 2.24198100, + 1.26765471, 1.15855534, 1.83936251, 1.32970987, 1.25844192, 1.31133485, + 4.74300303, 6.19325623, 1.31832913, 3.97645560, 1.00545340, 1.24431862, + 1.25855820, 1.15514241, 1.35986865, 1.72446070, 1.13069572, 2.45890932, + 1.00394684, 1.03533631, 1.87698184, 2.34576160, 1.03997887, 1.02694456, + 2.52227100, 2.66278467, 1.17002905, 3.42239624, 2.46753038, 1.17103623, + 1.07832850, 1.42782632, 1.29110546, 1.03435772, 1.33512109, 1.14337058, + 1.34103634, 1.15155161, 2.59805360, 2.09650343, 1.53399143, 1.02319185, + 1.32210667, 1.05720671, 1.20882651, 2.34881662, 1.05163662, 3.26219380, + 10.58124156, 1.07283644, 1.02105339, 1.23268679, 1.81469813, 1.49393533, + 1.29760853, 5.37676625, 1.02529938, 1.86815537, 1.57961476, 3.77408176, + 2.79405589, 3.25246617, 1.63913824, 3.12133428, 1.03787574, 4.17232960, + 1.33406468, 1.57119541, 1.13675102, 3.42874720, 1.13066210, 1.33896458, + 1.23883935, 1.35272696, 1.15172654, 2.18633755, 1.23251881, 1.59742606, + 1.08718410, 1.06168544, 1.19926517, 1.00214807, 1.29121086, 3.44575916, + 1.26524744, 1.16718301, 4.11789988, 1.25375574, 1.35753968, 1.69247751, + 1.28473150, 2.20669768, 1.53213883, 2.30598771, 1.68420243, 1.37320685, + 2.08619411, 1.26990265, 1.82215898, 1.10656122, 1.40229835, 1.11896817, + 1.00127366, 2.88218857, 2.79105702, 1.28699225, 1.15929737, 1.07928363, + 10.54130128, 8.79261793, 1.15699405, 1.69050500, 2.76586152, 1.22802809, + 1.38014655, 2.19208585, 1.64409370, 1.46918371, 2.99582898, 1.37759923, + 1.29776632, 1.82884215, 2.67317357, 1.37063041, 1.26884340, 1.07874723, + 1.48172681, 1.01771849, 2.40642202, 1.37115433, 1.05954574, 2.12998246, + 2.34178079, 1.54515623, 1.00179963, 2.12228030, 1.46007334, 1.20664530, + 1.31417158, 1.03322353, 1.95420119, 1.30541569, 1.15016102, 2.17036908, + 2.81707947, 1.16173181, 2.01742565, 1.02478594, 1.57428560, 1.21209176, + 2.20735202, 1.12935761, 2.08850147, 1.05353378, 1.02324910, 1.49636415, + 1.48061026, 2.25651770, 3.04296168, 1.24380806, 1.07707360, 2.00284318, + 10.02810932, 3.38695326, 6.82841534, 2.13556915, 1.19152238 + }; + + igraph_vector_view(&vector, data, sizeof(data) / sizeof(data[0])); + + /* determining xmin and alpha */ + if (igraph_power_law_fit(&vector, &result, -1, 0)) { + return 1; + } + print_result(&result); + + /* determining alpha only */ + if (igraph_power_law_fit(&vector, &result, 2, 0)) { + return 2; + } + print_result(&result); + + return 0; +} + +int test_discrete(void) { + igraph_plfit_result_t result; + igraph_vector_t vector; + igraph_real_t p; + + double data[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 4, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 5, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, + 1, 1, 1, 1, 1, 1, 6, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 4, 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, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 2, 1, 4, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 4, 1, 1, 14, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 4, 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, 2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 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, 5, 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, 2, 1, 1, 1, 4, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 4, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 5, 1, 1, 5, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 3, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, + 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, + 1, 1, 2, 4, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, + 1, 1, 1, 1, 2, 11, 1, 33, 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, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, + 2, 1, 1, 1, 2, 2, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 16, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 2, 1, 1, 12, 1, 1, 1, 1, 1, 1, 1, 3, 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, 2, 2, 1, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 2, 1, 4, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 1, 1, 1, 4, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 3, 2, 1, 1, 1, 2 + }; + + igraph_vector_view(&vector, data, sizeof(data) / sizeof(data[0])); + + /* determining xmin and alpha */ + if (igraph_power_law_fit(&vector, &result, -1, 0)) { + return 3; + } + IGRAPH_ASSERT(result.data == &vector); + print_result(&result); + + /* determining alpha only */ + if (igraph_power_law_fit(&vector, &result, 2, 0)) { + return 4; + } + IGRAPH_ASSERT(result.data == &vector); + print_result(&result); + + /* forcing continuous fitting */ + if (igraph_power_law_fit(&vector, &result, -1, 1)) { + return 5; + } + IGRAPH_ASSERT(result.data == &vector); + print_result(&result); + + /* forcing continuous fitting, xmin given */ + if (igraph_power_law_fit(&vector, &result, 2, 1)) { + return 6; + } + IGRAPH_ASSERT(result.data == &vector); + print_result(&result); + + /* Calculating p-value for the fit */ + if (igraph_plfit_result_calculate_p_value(&result, &p, 0.1)) { + return 7; + } + printf("p = %.2f\n", p); + + return 0; +} + +int main(void) { + int retval; + + /* Seed random number generator to ensure reproducibility. */ + igraph_rng_seed(igraph_rng_default(), 42); + + retval = test_continuous(); + if (retval) { + return retval; + } + + retval = test_discrete(); + if (retval) { + return retval; + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_power_law_fit.out b/tests/unit/igraph_power_law_fit.out new file mode 100644 index 0000000..c1ac4e6 --- /dev/null +++ b/tests/unit/igraph_power_law_fit.out @@ -0,0 +1,37 @@ +continuous = true +alpha = 2.81976 +xmin = 1.00979 +L = -946.14703 +D = 0.01454 +==================== +continuous = true +alpha = 2.81157 +xmin = 2.00000 +L = -463.92064 +D = 0.05091 +==================== +continuous = false +alpha = 3.11405 +xmin = 1.00000 +L = -622.60933 +D = 0.00941 +==================== +continuous = false +alpha = 3.27157 +xmin = 2.00000 +L = -185.83215 +D = 0.04504 +==================== +continuous = true +alpha = 3.77550 +xmin = 11.00000 +L = -13.68681 +D = 0.15260 +==================== +continuous = true +alpha = 5.26868 +xmin = 2.00000 +L = -75.22503 +D = 0.70253 +==================== +p = 0.00 diff --git a/tests/unit/igraph_preference_game.c b/tests/unit/igraph_preference_game.c new file mode 100644 index 0000000..341d8b0 --- /dev/null +++ b/tests/unit/igraph_preference_game.c @@ -0,0 +1,251 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_t type_dist; + igraph_matrix_t pref_mat, type_dist_mat; + igraph_vector_int_t types, out_types, in_types; + igraph_bool_t connected, has_loop, has_multi; + igraph_integer_t i, j, count; + + igraph_vector_int_init(&types, 0); + + /* Symmetric preference game */ + igraph_vector_init_real(&type_dist, 3, 1.0, 1.0, 1.0); + + igraph_matrix_init(&pref_mat, 3, 3); + for (i = 0; i < 3; i++) { + MATRIX(pref_mat, i, i) = 0.2; + } + + /* undirected, no loops */ + igraph_preference_game(&g, 1000, 3, &type_dist, /*fixed_sizes=*/ 0, + &pref_mat, &types, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 1000); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + + igraph_is_connected(&g, &connected, IGRAPH_STRONG); + IGRAPH_ASSERT(! connected); + + igraph_has_loop(&g, &has_loop); + IGRAPH_ASSERT(! has_loop); + + igraph_has_multiple(&g, &has_multi); + IGRAPH_ASSERT(! has_multi); + + IGRAPH_ASSERT(igraph_vector_int_size(&types) == igraph_vcount(&g)); + IGRAPH_ASSERT(igraph_vector_int_min(&types) == 0); + IGRAPH_ASSERT(igraph_vector_int_max(&types) == 2); + + igraph_destroy(&g); + + /* Note: preference matrix must be symmetric in the undirected case. */ + for (i = 0; i < 2; i++) { + MATRIX(pref_mat, i, i + 1) = 0.1; + MATRIX(pref_mat, i + 1, i) = 0.1; + } + + /* directed, no loops */ + igraph_preference_game(&g, 1000, 3, &type_dist, /*fixed_sizes=*/0, + &pref_mat, &types, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 1000); + IGRAPH_ASSERT(igraph_is_directed(&g)); + + igraph_has_loop(&g, &has_loop); + IGRAPH_ASSERT(! has_loop); + + igraph_has_multiple(&g, &has_multi); + IGRAPH_ASSERT(! has_multi); + + IGRAPH_ASSERT(igraph_vector_int_size(&types) == igraph_vcount(&g)); + IGRAPH_ASSERT(igraph_vector_int_min(&types) == 0); + IGRAPH_ASSERT(igraph_vector_int_max(&types) == 2); + + igraph_destroy(&g); + + /* undirected, loops */ + for (i = 0; i < 3; i++) { + MATRIX(pref_mat, i, i) = 1.0; + } + + igraph_preference_game(&g, 100, 3, &type_dist, /*fixed_sizes=*/ 0, + &pref_mat, &types, IGRAPH_UNDIRECTED, IGRAPH_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 100); + IGRAPH_ASSERT(igraph_ecount(&g) >= 1395); + IGRAPH_ASSERT(!igraph_is_directed(&g)); + + igraph_has_loop(&g, &has_loop); + IGRAPH_ASSERT(has_loop); + + igraph_has_multiple(&g, &has_multi); + IGRAPH_ASSERT(! has_multi); + + IGRAPH_ASSERT(igraph_vector_int_size(&types) == igraph_vcount(&g)); + IGRAPH_ASSERT(igraph_vector_int_min(&types) == 0); + IGRAPH_ASSERT(igraph_vector_int_max(&types) == 2); + + igraph_destroy(&g); + + /* directed, loops */ + igraph_preference_game(&g, 100, 3, &type_dist, /*fixed_sizes=*/ 0, + &pref_mat, NULL, IGRAPH_DIRECTED, IGRAPH_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 100); + IGRAPH_ASSERT(igraph_ecount(&g) >= 2700); + IGRAPH_ASSERT(igraph_is_directed(&g)); + + igraph_has_loop(&g, &has_loop); + IGRAPH_ASSERT(has_loop); + + igraph_has_multiple(&g, &has_multi); + IGRAPH_ASSERT(! has_multi); + + igraph_destroy(&g); + + /* fixed sizes, divide evenly */ + igraph_matrix_resize(&pref_mat, 9, 9); + for (i = 0; i < 9; i++) { + for (j = 0; j < 9; j++) { + MATRIX(pref_mat, i, j) = (j == i + 1 || j == i - 1) ? 0.1 : 0; + } + } + igraph_preference_game(&g, 50, 9, /*type_dist=*/ 0, /*fixed_sizes=*/ 1, + &pref_mat, &types, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 50); + IGRAPH_ASSERT(!igraph_is_directed(&g)); + + igraph_has_loop(&g, &has_loop); + IGRAPH_ASSERT(! has_loop); + + igraph_has_multiple(&g, &has_multi); + IGRAPH_ASSERT(! has_multi); + + for (i = 0; i < 9; i++) { + count = 0; + for (j = 0; j < 50; j++) { + if (VECTOR(types)[j] == i) { + count++; + } + } + IGRAPH_ASSERT(count == 5 || count == 6); + } + + igraph_destroy(&g); + + igraph_vector_int_destroy(&types); + + /* Asymmetric preference game */ + + igraph_vector_int_init(&out_types, 0); + igraph_vector_int_init(&in_types, 0); + + /* directed, no loops */ + igraph_matrix_resize(&pref_mat, 2, 3); + MATRIX(pref_mat, 0, 0) = 1; + MATRIX(pref_mat, 0, 1) = 1; + MATRIX(pref_mat, 0, 2) = 1; + MATRIX(pref_mat, 1, 0) = 1; + MATRIX(pref_mat, 1, 1) = 1; + MATRIX(pref_mat, 1, 2) = 1; + + igraph_asymmetric_preference_game(&g, 100, 2, 3, NULL, &pref_mat, &out_types, &in_types, IGRAPH_NO_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 100); + IGRAPH_ASSERT(igraph_ecount(&g) == 9900); + IGRAPH_ASSERT(igraph_is_directed(&g)); + + igraph_has_loop(&g, &has_loop); + IGRAPH_ASSERT(! has_loop); + + igraph_has_multiple(&g, &has_multi); + IGRAPH_ASSERT(! has_multi); + + igraph_destroy(&g); + + /* directed, loops */ + igraph_matrix_resize(&pref_mat, 2, 2); + MATRIX(pref_mat, 0, 0) = 1; + MATRIX(pref_mat, 0, 1) = 1; + MATRIX(pref_mat, 1, 0) = 1; + MATRIX(pref_mat, 1, 1) = 1; + + igraph_asymmetric_preference_game(&g, 100, 2, 2, NULL, &pref_mat, NULL, NULL, IGRAPH_LOOPS); + + IGRAPH_ASSERT(igraph_vcount(&g) == 100); + IGRAPH_ASSERT(igraph_ecount(&g) == 10000); + IGRAPH_ASSERT(igraph_is_directed(&g)); + + igraph_count_loops(&g, &count); + IGRAPH_ASSERT(count == 100); + + igraph_has_multiple(&g, &has_multi); + IGRAPH_ASSERT(! has_multi); + + igraph_destroy(&g); + + /* check that vertex out-/in-types are generated correctly; here pref_mat does not matter */ + + igraph_matrix_resize(&pref_mat, 3, 2); + igraph_matrix_null(&pref_mat); + + igraph_matrix_init(&type_dist_mat, 3, 2); + igraph_matrix_null(&type_dist_mat); + MATRIX(type_dist_mat, 2, 0) = 1; /* all out-types are 2, all in-types are 0 */ + + igraph_asymmetric_preference_game(&g, 10, 3, 2, &type_dist_mat, &pref_mat, &out_types, &in_types, IGRAPH_LOOPS); + { + /* Check that all out-types are 2 and all in-types are 0 */ + igraph_vector_int_t v; + igraph_vector_int_init(&v, igraph_vcount(&g)); + + igraph_vector_int_fill(&v, 2); + IGRAPH_ASSERT(igraph_vector_int_all_e(&out_types, &v)); + + igraph_vector_int_fill(&v, 0); + IGRAPH_ASSERT(igraph_vector_int_all_e(&in_types, &v)); + + igraph_vector_int_destroy(&v); + } + + igraph_destroy(&g); + + igraph_matrix_destroy(&type_dist_mat); + igraph_vector_destroy(&type_dist); + igraph_matrix_destroy(&pref_mat); + + igraph_vector_int_destroy(&out_types); + igraph_vector_int_destroy(&in_types); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_progress_handler_stderr.c b/tests/unit/igraph_progress_handler_stderr.c new file mode 100644 index 0000000..dbec2c4 --- /dev/null +++ b/tests/unit/igraph_progress_handler_stderr.c @@ -0,0 +1,27 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_progress_handler_stderr("This is a message ", 10, NULL); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_pseudo_diameter.c b/tests/unit/igraph_pseudo_diameter.c new file mode 100644 index 0000000..26ec5ef --- /dev/null +++ b/tests/unit/igraph_pseudo_diameter.c @@ -0,0 +1,131 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_result(igraph_t *g, igraph_integer_t start_vid, igraph_bool_t directed, igraph_bool_t unconn) { + igraph_real_t result; + igraph_integer_t from, to; + IGRAPH_ASSERT(igraph_pseudo_diameter(g, &result, start_vid, &from, &to, directed, unconn) == IGRAPH_SUCCESS); + printf("result: "); + print_real(stdout, result, "%g"); + printf(", from %" IGRAPH_PRId " to %" IGRAPH_PRId "\n\n", from, to); +} + +int main(void) { + igraph_t g; + igraph_real_t result; + igraph_error_handler_t *ehandler; + igraph_integer_t i; + + igraph_rng_seed(igraph_rng_default(), 42); + + + printf("No vertices, no allowed starting vertex ID.\n\n"); + igraph_small(&g, 0, 0, -1); + ehandler = igraph_set_error_handler(igraph_error_handler_ignore); + IGRAPH_ASSERT(igraph_pseudo_diameter(&g, &result, 0, NULL, NULL, 1, 1) == IGRAPH_EINVAL); + igraph_set_error_handler(ehandler); + + printf("Null graph without explicit start vertex:\n"); + print_result(&g, -1, 1, 1); + igraph_destroy(&g); + + printf("1 vertex:\n"); + igraph_small(&g, 1, 0, -1); + print_result(&g, 0, 1, 1); + igraph_destroy(&g); + + printf("2 vertices unconn = true:\n"); + igraph_small(&g, 2, 0, -1); + print_result(&g, 0, 1, 1); + igraph_destroy(&g); + + printf("2 vertices unconn = false:\n"); + igraph_small(&g, 2, 0, -1); + print_result(&g, 0, 1, 0); + igraph_destroy(&g); + + printf("2 vertices, directed, unconn = true:\n"); + igraph_small(&g, 2, 1, -1); + print_result(&g, 0, 1, 1); + igraph_destroy(&g); + + printf("2 vertices, directed, unconn = false:\n"); + igraph_small(&g, 2, 1, -1); + print_result(&g, 0, 1, 0); + igraph_destroy(&g); + + printf("Undirected disconnected graph with loops and multiple edges.\n"); + igraph_small(&g, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + for (i = 0; i < 6; i ++) { + print_result(&g, i, 1, 1); + } + igraph_destroy(&g); + + printf("Directed disconnected graph with loops and multiple edges, direction ignored.\n"); + igraph_small(&g, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + for (i = 0; i < 6; i ++) { + print_result(&g, i, 0, 1); + } + igraph_destroy(&g); + + printf("Directed disconnected graph with loops and multiple edges.\n"); + igraph_small(&g, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + for (i = 0; i < 6; i ++) { + print_result(&g, i, 1, 1); + } + igraph_destroy(&g); + + printf("Directed graph, pointing outward from 2.\n"); + igraph_small(&g, 5, 1, 1,0, 2,1, 2,3, 3,4, -1); + for (i = 0; i < 5; i ++) { + print_result(&g, i, 1, 1); + } + igraph_destroy(&g); + + printf("Same graph, direction ignored.\n"); + igraph_small(&g, 5, 1, 1,0, 2,1, 2,3, 3,4, -1); + for (i = 0; i < 5; i ++) { + print_result(&g, i, 0, 1); + } + igraph_destroy(&g); + + printf("Same graph, unconn = false.\n"); + igraph_small(&g, 5, 1, 1,0, 2,1, 2,3, 3,4, -1); + print_result(&g, 0, 1, 0); + igraph_destroy(&g); + + printf("Same graph, undirected.\n"); + igraph_small(&g, 5, 0, 1,0, 2,1, 2,3, 3,4, -1); + for (i = 0; i < 5; i ++) { + print_result(&g, i, 1, 1); + } + igraph_destroy(&g); + + printf("Petersen graph with random starts.\n"); + igraph_famous(&g, "petersen"); + for (i = 0; i < 6; i ++) { + print_result(&g, -1, 1, 1); + } + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_pseudo_diameter.out b/tests/unit/igraph_pseudo_diameter.out new file mode 100644 index 0000000..93b7650 --- /dev/null +++ b/tests/unit/igraph_pseudo_diameter.out @@ -0,0 +1,108 @@ +No vertices, no allowed starting vertex ID. + +Null graph without explicit start vertex: +result: NaN, from -1 to -1 + +1 vertex: +result: 0, from 0 to 0 + +2 vertices unconn = true: +result: 0, from 0 to 0 + +2 vertices unconn = false: +result: Inf, from -1 to -1 + +2 vertices, directed, unconn = true: +result: 0, from 0 to 0 + +2 vertices, directed, unconn = false: +result: Inf, from -1 to -1 + +Undirected disconnected graph with loops and multiple edges. +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 3, from 4 to 0 + +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 0, from 5 to 5 + +Directed disconnected graph with loops and multiple edges, direction ignored. +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 3, from 4 to 0 + +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 0, from 5 to 5 + +Directed disconnected graph with loops and multiple edges. +result: 3, from 0 to 4 + +result: 2, from 2 to 1 + +result: 3, from 0 to 4 + +result: 3, from 0 to 4 + +result: 3, from 0 to 4 + +result: 0, from 5 to 5 + +Directed graph, pointing outward from 2. +result: 2, from 2 to 0 + +result: 2, from 2 to 0 + +result: 2, from 2 to 0 + +result: 2, from 2 to 0 + +result: 2, from 2 to 4 + +Same graph, direction ignored. +result: 4, from 0 to 4 + +result: 4, from 4 to 0 + +result: 4, from 0 to 4 + +result: 4, from 0 to 4 + +result: 4, from 4 to 0 + +Same graph, unconn = false. +result: Inf, from -1 to -1 + +Same graph, undirected. +result: 4, from 0 to 4 + +result: 4, from 4 to 0 + +result: 4, from 0 to 4 + +result: 4, from 0 to 4 + +result: 4, from 4 to 0 + +Petersen graph with random starts. +result: 2, from 1 to 4 + +result: 2, from 5 to 1 + +result: 2, from 8 to 2 + +result: 2, from 9 to 0 + +result: 2, from 9 to 0 + +result: 2, from 7 to 1 + diff --git a/tests/unit/igraph_pseudo_diameter_dijkstra.c b/tests/unit/igraph_pseudo_diameter_dijkstra.c new file mode 100644 index 0000000..057af58 --- /dev/null +++ b/tests/unit/igraph_pseudo_diameter_dijkstra.c @@ -0,0 +1,153 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_result(igraph_t *g, igraph_vector_t *weights, igraph_integer_t start_vid, igraph_bool_t directed, igraph_bool_t unconn) { + igraph_real_t result; + igraph_integer_t from, to; + IGRAPH_ASSERT(igraph_pseudo_diameter_dijkstra(g, weights, &result, start_vid, &from, &to, directed, unconn) == IGRAPH_SUCCESS); + printf("result: "); + print_real(stdout, result, "%g"); + printf(", from %" IGRAPH_PRId " to %" IGRAPH_PRId "\n\n", from, to); +} + +int main(void) { + igraph_t g; + igraph_real_t result; + int i; + igraph_vector_t weights; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("Null graph without explicit start vertex:\n"); + igraph_vector_init_int(&weights, 0); + igraph_small(&g, 0, 0, -1); + print_result(&g, &weights, -1, 1, 1); + igraph_destroy(&g); + + printf("1 vertex:\n"); + igraph_small(&g, 1, 0, -1); + print_result(&g, &weights, 0, 1, 1); + igraph_destroy(&g); + + printf("2 vertices unconn = true:\n"); + igraph_small(&g, 2, 0, -1); + print_result(&g, &weights, 0, 1, 1); + igraph_destroy(&g); + + printf("2 vertices unconn = false:\n"); + igraph_small(&g, 2, 0, -1); + print_result(&g, &weights, 0, 1, 0); + igraph_destroy(&g); + + printf("2 vertices, directed, unconn = true:\n"); + igraph_small(&g, 2, 1, -1); + print_result(&g, &weights, 0, 1, 1); + igraph_destroy(&g); + + printf("2 vertices, directed, unconn = false:\n"); + igraph_small(&g, 2, 1, -1); + print_result(&g, &weights, 0, 1, 0); + igraph_destroy(&g); + igraph_vector_destroy(&weights); + + printf("Undirected disconnected graph with loops and multiple edges, all weights equal.\n"); + igraph_vector_init_int(&weights, 8, 1, 1, 1, 1, 1, 1, 1, 1); + igraph_small(&g, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + for (i = 0; i < 6; i ++) { + print_result(&g, &weights, i, 1, 1); + } + + printf("Undirected disconnected graph with loops and multiple edges, NULL weights.\n"); + for (i = 0; i < 6; i ++) { + print_result(&g, NULL, i, 1, 1); + } + igraph_destroy(&g); + + printf("Same graph, directed, direction ignored.\n"); + igraph_small(&g, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + for (i = 0; i < 6; i ++) { + print_result(&g, &weights, i, 0, 1); + } + + printf("Same graph, direction not ignored.\n"); + for (i = 0; i < 6; i ++) { + print_result(&g, &weights, i, 1, 1); + } + igraph_vector_destroy(&weights); + + printf("Same graph, weighted.\n"); + igraph_vector_init_int(&weights, 8, 0, 1, 2, 3, 4, 5, 6, 7); + for (i = 0; i < 6; i ++) { + print_result(&g, &weights, i, 1, 1); + } + + printf("Same graph, weighted, directions ignored.\n"); + for (i = 0; i < 6; i ++) { + print_result(&g, &weights, i, 0, 1); + } + igraph_destroy(&g); + igraph_vector_destroy(&weights); + + printf("Directed graph, weighted, pointing outward from 2.\n"); + igraph_vector_init_int(&weights, 4, 0, 1, 2, 3); + igraph_small(&g, 5, 1, 1,0, 2,1, 2,3, 3,4, -1); + for (i = 0; i < 5; i ++) { + print_result(&g, &weights, i, 1, 1); + } + + printf("Same graph, random starting vertex.\n"); + print_result(&g, &weights, -1, 1, 1); + + printf("Same graph, unconn = false.\n"); + print_result(&g, &weights, 0, 1, 0); + + printf("Same graph, direction ignored.\n"); + for (i = 0; i < 5; i ++) { + print_result(&g, &weights, i, 0, 1); + } + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Weight vector size too small.\n"); + igraph_vector_init_int(&weights, 3, 0, 1, 2); + igraph_small(&g, 5, 1, 1,0, 2,1, 2,3, 3,4, -1); + IGRAPH_ASSERT(igraph_pseudo_diameter_dijkstra(&g, &weights, &result, 0, NULL, NULL, 0, 1) == IGRAPH_EINVAL); + + printf("Starting vertex ID too large.\n"); + IGRAPH_ASSERT(igraph_pseudo_diameter_dijkstra(&g, NULL, &result, 10, NULL, NULL, 0, 1) == IGRAPH_EINVAL); + igraph_vector_destroy(&weights); + + printf("Negative weight.\n"); + igraph_vector_init_int(&weights, 4, 0, 1, 2, -1); + IGRAPH_ASSERT(igraph_pseudo_diameter_dijkstra(&g, &weights, &result, 0, NULL, NULL, 0, 1) == IGRAPH_EINVAL); + igraph_vector_destroy(&weights); + + printf("NaN weight.\n"); + igraph_vector_init_real(&weights, 4, 0., 1., 2., IGRAPH_NAN); + IGRAPH_ASSERT(igraph_pseudo_diameter_dijkstra(&g, &weights, &result, 0, NULL, NULL, 0, 1) == IGRAPH_EINVAL); + igraph_vector_destroy(&weights); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_pseudo_diameter_dijkstra.out b/tests/unit/igraph_pseudo_diameter_dijkstra.out new file mode 100644 index 0000000..529c17a --- /dev/null +++ b/tests/unit/igraph_pseudo_diameter_dijkstra.out @@ -0,0 +1,128 @@ +Null graph without explicit start vertex: +result: NaN, from -1 to -1 + +1 vertex: +result: 0, from 0 to 0 + +2 vertices unconn = true: +result: 0, from 0 to 0 + +2 vertices unconn = false: +result: Inf, from -1 to -1 + +2 vertices, directed, unconn = true: +result: 0, from 0 to 0 + +2 vertices, directed, unconn = false: +result: Inf, from -1 to -1 + +Undirected disconnected graph with loops and multiple edges, all weights equal. +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 2, from 2 to 1 + +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 0, from 5 to 5 + +Undirected disconnected graph with loops and multiple edges, NULL weights. +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 3, from 4 to 0 + +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 0, from 5 to 5 + +Same graph, directed, direction ignored. +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 2, from 2 to 1 + +result: 3, from 0 to 4 + +result: 3, from 4 to 0 + +result: 0, from 5 to 5 + +Same graph, direction not ignored. +result: 3, from 0 to 4 + +result: 2, from 2 to 1 + +result: 3, from 0 to 4 + +result: 3, from 0 to 4 + +result: 3, from 0 to 4 + +result: 0, from 5 to 5 + +Same graph, weighted. +result: 11, from 2 to 4 + +result: 11, from 2 to 4 + +result: 11, from 2 to 4 + +result: 11, from 2 to 4 + +result: 11, from 2 to 4 + +result: 0, from 5 to 5 + +Same graph, weighted, directions ignored. +result: 10, from 4 to 2 + +result: 10, from 4 to 2 + +result: 10, from 2 to 4 + +result: 10, from 4 to 2 + +result: 10, from 4 to 2 + +result: 0, from 5 to 5 + +Directed graph, weighted, pointing outward from 2. +result: 5, from 2 to 4 + +result: 5, from 2 to 4 + +result: 5, from 2 to 4 + +result: 5, from 2 to 4 + +result: 5, from 2 to 4 + +Same graph, random starting vertex. +result: 5, from 2 to 4 + +Same graph, unconn = false. +result: Inf, from -1 to -1 + +Same graph, direction ignored. +result: 6, from 0 to 4 + +result: 6, from 1 to 4 + +result: 6, from 4 to 0 + +result: 6, from 0 to 4 + +result: 6, from 4 to 0 + +Weight vector size too small. +Starting vertex ID too large. +Negative weight. +NaN weight. diff --git a/tests/unit/igraph_psumtree.c b/tests/unit/igraph_psumtree.c new file mode 100644 index 0000000..ebecf5c --- /dev/null +++ b/tests/unit/igraph_psumtree.c @@ -0,0 +1,218 @@ + +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_psumtree_t tree; + igraph_vector_t vec; + igraph_integer_t i, idx; + igraph_real_t sum; + + igraph_rng_seed(igraph_rng_default(), 42); + + RNG_BEGIN(); + + /* Uniform random numbers */ + igraph_vector_init(&vec, 16); + igraph_psumtree_init(&tree, 16); + sum = igraph_psumtree_sum(&tree); + if (sum != 0) { + printf("Sum: %f instead of 0.\n", sum); + return 1; + } + + for (i = 0; i < 16; i++) { + igraph_psumtree_update(&tree, i, 1); + } + if ((sum = igraph_psumtree_sum(&tree)) != 16) { + printf("Sum: %f instead of 16.\n", sum); + return 2; + } + + for (i = 0; i < 16000; i++) { + igraph_psumtree_search(&tree, &idx, RNG_UNIF(0, sum)); + VECTOR(vec)[idx] += 1; + } + for (i = 0; i < 16; i++) { + if (VECTOR(vec)[i] < 800 || VECTOR(vec)[i] > 1200) { + return 3; + } + } + + /* Nonuniform, even indices have twice as much chance */ + for (i = 0; i < 16; i += 2) { + igraph_psumtree_update(&tree, i, 2); + } + if ((sum = igraph_psumtree_sum(&tree)) != 24) { + printf("Sum: %f instead of 24.\n", sum); + return 4; + } + + igraph_vector_null(&vec); + for (i = 0; i < 24000; i++) { + igraph_psumtree_search(&tree, &idx, RNG_UNIF(0, sum)); + VECTOR(vec)[idx] += 1; + } + for (i = 0; i < 16; i++) { + if (i % 2 == 0 && (VECTOR(vec)[i] < 1800 || VECTOR(vec)[i] > 2200)) { + return 5; + } + if (i % 2 != 0 && (VECTOR(vec)[i] < 800 || VECTOR(vec)[i] > 1200)) { + return 6; + } + } + + /* Test zero probabilities */ + igraph_psumtree_update(&tree, 0, 0); + igraph_psumtree_update(&tree, 5, 0); + igraph_psumtree_update(&tree, 15, 0); + sum = igraph_psumtree_sum(&tree); + + igraph_vector_null(&vec); + for (i = 0; i < 20000; i++) { + igraph_psumtree_search(&tree, &idx, RNG_UNIF(0, sum)); + VECTOR(vec)[idx] += 1; + } + if (VECTOR(vec)[0] != 0 || VECTOR(vec)[5] != 0 || VECTOR(vec)[15] != 0) { + return 7; + } + + igraph_vector_destroy(&vec); + igraph_psumtree_destroy(&tree); + + /* Check that search considers intervals closed on the left, + * see https://github.com/igraph/igraph/issues/2080 */ + + igraph_psumtree_init(&tree, 6); + igraph_psumtree_update(&tree, 2, 1.0); + igraph_psumtree_update(&tree, 4, 1.0); + igraph_psumtree_search(&tree, &idx, 0.0); + IGRAPH_ASSERT(idx == 2); + igraph_psumtree_search(&tree, &idx, 0.5); + IGRAPH_ASSERT(idx == 2); + igraph_psumtree_search(&tree, &idx, 1.0); + IGRAPH_ASSERT(idx == 4); + igraph_psumtree_search(&tree, &idx, 1.2); + IGRAPH_ASSERT(idx == 4); + igraph_psumtree_destroy(&tree); + + /****************************************************/ + /* Non power-of-two vector size */ + /****************************************************/ + + igraph_vector_init(&vec, 9); + igraph_psumtree_init(&tree, 9); + + for (i = 0; i < 9; i++) { + igraph_psumtree_update(&tree, i, 1); + } + sum = igraph_psumtree_sum(&tree); + + for (i = 0; i < 9000; i++) { + igraph_psumtree_search(&tree, &idx, RNG_UNIF(0, sum)); + VECTOR(vec)[idx] += 1; + } + for (i = 0; i < 9; i++) { + if (VECTOR(vec)[i] < 800 || VECTOR(vec)[i] > 1200) { + return 8; + } + } + + /* Nonuniform, even indices have twice as much chance */ + for (i = 0; i < 9; i += 2) { + igraph_psumtree_update(&tree, i, 2); + } + sum = igraph_psumtree_sum(&tree); + + igraph_vector_null(&vec); + for (i = 0; i < 14000; i++) { + igraph_psumtree_search(&tree, &idx, RNG_UNIF(0, sum)); + VECTOR(vec)[idx] += 1; + } + for (i = 0; i < 9; i++) { + if (i % 2 == 0 && (VECTOR(vec)[i] < 1800 || VECTOR(vec)[i] > 2200)) { + return 9; + } + if (i % 2 != 0 && (VECTOR(vec)[i] < 800 || VECTOR(vec)[i] > 1200)) { + return 10; + } + } + + /* Test query */ + for (i = 0; i < igraph_psumtree_size(&tree); i++) { + if (i % 2 == 0 && igraph_psumtree_get(&tree, i) != 2) { + return 11; + } + if (i % 2 != 0 && igraph_psumtree_get(&tree, i) != 1) { + return 12; + } + } + + /* Test zero probabilities */ + igraph_psumtree_update(&tree, 0, 0); + igraph_psumtree_update(&tree, 5, 0); + igraph_psumtree_update(&tree, 8, 0); + sum = igraph_psumtree_sum(&tree); + + igraph_vector_null(&vec); + for (i = 0; i < 9000; i++) {; + igraph_psumtree_search(&tree, &idx, RNG_UNIF(0, sum)); + VECTOR(vec)[idx] += 1; + } + if (VECTOR(vec)[0] != 0 || VECTOR(vec)[5] != 0 || VECTOR(vec)[8] != 0) { + return 11; + } + + igraph_vector_destroy(&vec); + igraph_psumtree_destroy(&tree); + + /****************************************************/ + /* Error handling */ + /****************************************************/ + + igraph_psumtree_init(&tree, 9); + + igraph_error_handler_t *oldhandler = igraph_set_error_handler(&igraph_error_handler_ignore); + if (igraph_psumtree_update(&tree, 2, -2) == IGRAPH_SUCCESS) { + return 12; + } + if (igraph_psumtree_update(&tree, 2, -INFINITY) == IGRAPH_SUCCESS) { + return 13; + } + if (igraph_psumtree_update(&tree, 2, IGRAPH_NAN) == IGRAPH_SUCCESS) { + return 14; + } + igraph_set_error_handler(oldhandler); + + igraph_psumtree_destroy(&tree); + + RNG_END(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_qsort.c b/tests/unit/igraph_qsort.c new file mode 100644 index 0000000..13a30a7 --- /dev/null +++ b/tests/unit/igraph_qsort.c @@ -0,0 +1,62 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int comp(const void *a, const void *b) { + igraph_real_t *aa = (igraph_real_t *) a; + igraph_real_t *bb = (igraph_real_t *) b; + + if (*aa < *bb) { + return -1; + } else if (*aa > *bb) { + return 1; + } + + return 0; +} + +int main(void) { + const int len = 100; + igraph_vector_t v; + int i; + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_vector_init(&v, len); + for (i = 0; i < len; i++) { + VECTOR(v)[i] = i; + } + igraph_vector_shuffle(&v); + + igraph_qsort(VECTOR(v), igraph_vector_size(&v), sizeof(VECTOR(v)[0]), comp); + + igraph_vector_print(&v); + + igraph_vector_destroy(&v); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_qsort.out b/tests/unit/igraph_qsort.out new file mode 100644 index 0000000..580e05a --- /dev/null +++ b/tests/unit/igraph_qsort.out @@ -0,0 +1 @@ +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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 diff --git a/tests/unit/igraph_qsort_r.c b/tests/unit/igraph_qsort_r.c new file mode 100644 index 0000000..3321ca2 --- /dev/null +++ b/tests/unit/igraph_qsort_r.c @@ -0,0 +1,72 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int comp(void *extra, const void *a, const void *b) { + igraph_vector_t *v = (igraph_vector_t*) extra; + igraph_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; + igraph_real_t aaa = VECTOR(*v)[*aa]; + igraph_real_t bbb = VECTOR(*v)[*bb]; + + if (aaa < bbb) { + return -1; + } else if (aaa > bbb) { + return 1; + } + + return 0; +} + +int main(void) { + const int len = 100; + igraph_vector_t v; + igraph_vector_int_t idx; + int i; + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_vector_init(&v, len); + igraph_vector_int_init(&idx, len); + for (i = 0; i < len; i++) { + VECTOR(v)[i] = i; + VECTOR(idx)[i] = i; + } + igraph_vector_shuffle(&v); + + igraph_qsort_r(VECTOR(idx), len, sizeof(VECTOR(idx)[0]), (void*) &v, comp); + + for (i = 0; i < len; i++) { + printf("%g ", VECTOR(v)[ VECTOR(idx)[i] ]); + } + printf("\n"); + + igraph_vector_int_destroy(&idx); + igraph_vector_destroy(&v); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_qsort_r.out b/tests/unit/igraph_qsort_r.out new file mode 100644 index 0000000..246042d --- /dev/null +++ b/tests/unit/igraph_qsort_r.out @@ -0,0 +1 @@ +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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 diff --git a/tests/unit/igraph_random_sample.c b/tests/unit/igraph_random_sample.c new file mode 100644 index 0000000..25e5fa7 --- /dev/null +++ b/tests/unit/igraph_random_sample.c @@ -0,0 +1,65 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void check(igraph_integer_t l, igraph_integer_t h, igraph_integer_t s) { + igraph_vector_int_t v; + + igraph_vector_int_init(&v, 0); + igraph_random_sample(&v, l, h, s); + + IGRAPH_ASSERT(igraph_vector_int_size(&v) == s); + IGRAPH_ASSERT(VECTOR(v)[0] >= l); + IGRAPH_ASSERT(VECTOR(v)[igraph_vector_int_size(&v) - 1] <= h); + + for (igraph_integer_t i = 0; i < s - 1; i++) { + IGRAPH_ASSERT(VECTOR(v)[i] < VECTOR(v)[i + 1]); + } + + igraph_vector_int_destroy(&v); +} + +int main(void) { + igraph_vector_int_t v; + + igraph_rng_seed(igraph_rng_default(), 42); + + check(-100, 100, 10); + check(-10000, 10000, 100); + check(0, 0, 1); + check(100, 110, 11); + + igraph_vector_int_init(&v, 0); + + igraph_random_sample(&v, 100, 1000, 0); + IGRAPH_ASSERT(igraph_vector_int_size(&v) == 0); + + /* test parameters */ + /*----------low----high----length*/ + /* lower limit is greater than upper limit */ + CHECK_ERROR(igraph_random_sample(&v, 300, 200, 10), IGRAPH_EINVAL); + /* sample size is greater than size of candidate pool */ + CHECK_ERROR(igraph_random_sample(&v, 200, 300, 500), IGRAPH_EINVAL); + + igraph_vector_int_destroy(&v); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_random_walk.c b/tests/unit/igraph_random_walk.c new file mode 100644 index 0000000..70e7e92 --- /dev/null +++ b/tests/unit/igraph_random_walk.c @@ -0,0 +1,235 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void random_vertex_walk(igraph_t *graph, igraph_vector_t *weights, + igraph_integer_t start, igraph_neimode_t mode, + igraph_integer_t steps, igraph_random_walk_stuck_t stuck) { + igraph_vector_int_t vertices; + + igraph_vector_int_init(&vertices, 0); + igraph_random_walk(graph, weights, &vertices, NULL, start, mode, steps, stuck); + print_vector_int(&vertices); + igraph_vector_int_destroy(&vertices); +} + +void random_walk(igraph_t *graph, igraph_vector_t *weights, + igraph_integer_t start, igraph_neimode_t mode, + igraph_integer_t steps, igraph_random_walk_stuck_t stuck) { + igraph_vector_int_t vertices; + igraph_vector_int_t edges; + + igraph_vector_int_init(&vertices, 0); + igraph_vector_int_init(&edges, 0); + igraph_random_walk(graph, weights, &vertices, &edges, start, mode, steps, stuck); + print_vector_int(&vertices); + igraph_vector_int_destroy(&vertices); + igraph_vector_int_destroy(&edges); +} + +int main(void) { + igraph_t g_1, g_line, g_full, g_loop, g_de_bruijn; + igraph_vector_int_t vertices, edges; + igraph_vector_t g_line_weights, g_de_bruijn_weights, error_weights; + igraph_integer_t ec, i; + + igraph_vector_int_init(&vertices, 0); + igraph_vector_int_init(&edges, 0); + igraph_vector_init(&g_line_weights, 0); + igraph_vector_init(&g_de_bruijn_weights, 0); + igraph_vector_init(&error_weights, 0); + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_small(&g_1, 1, IGRAPH_UNDIRECTED, -1); + igraph_small(&g_line, 5, IGRAPH_DIRECTED, 0,1, 1,2, 2,3, 3,4, -1); + igraph_full(&g_full, 20, IGRAPH_UNDIRECTED, 0); + igraph_small(&g_loop, 1, IGRAPH_DIRECTED, 0,0, -1); + + /* This directed graph has loop edges. + It also has multi-edges when considered as undirected. */ + igraph_de_bruijn(&g_de_bruijn, 3, 2); + + /* Initialize de bruijn graph weights */ + igraph_rng_seed(igraph_rng_default(), 42); + ec = igraph_ecount(&g_de_bruijn); + igraph_vector_resize(&g_de_bruijn_weights, ec); + for (i = 0; i < ec; ++i) { + VECTOR(g_de_bruijn_weights)[i] = igraph_rng_get_unif01(igraph_rng_default()); + } + + /* Initialize g_line graph weights */ + igraph_rng_seed(igraph_rng_default(), 42); + ec = igraph_ecount(&g_line); + igraph_vector_resize(&g_line_weights, ec); + for (i = 0; i < ec; ++i) { + VECTOR(g_line_weights)[i] = igraph_rng_get_unif01(igraph_rng_default()); + } + + + /* 1. only vertices required, edges = NULL + unweighted -> igraph_i_random_walk_adjlist + weighted -> igraph_i_random_walk_inclist */ + printf("Only vertices required, edges = NULL:\n"); + + printf("Singleton graph with zero steps:\n"); + random_vertex_walk(&g_1, NULL, 0, IGRAPH_OUT, 0, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Singleton graph with one step:\n"); + random_vertex_walk(&g_line, NULL, 0, IGRAPH_OUT, 1, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Line graph:\n"); + random_vertex_walk(&g_line, NULL, 0, IGRAPH_OUT, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Line graph, 10 steps, returns on stuck:\n"); + random_vertex_walk(&g_line, NULL, 0, IGRAPH_OUT, 10, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Line graph backward:\n"); + random_vertex_walk(&g_line, NULL, 4, IGRAPH_IN, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Loop at vertex 0:\n"); + random_vertex_walk(&g_loop, NULL, 0, IGRAPH_OUT, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + igraph_rng_seed(igraph_rng_default(), 42); + printf("Checking an actual random walk with seed 42:\n"); + random_vertex_walk(&g_full, NULL, 4, IGRAPH_IN, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + /* weighted, directed */ + igraph_random_walk(&g_de_bruijn, &g_de_bruijn_weights, &vertices, NULL, 0, IGRAPH_OUT, 1000, IGRAPH_RANDOM_WALK_STUCK_RETURN); + IGRAPH_ASSERT(igraph_vector_int_size(&vertices) == 1001); + + /* weighted, undirecetd */ + igraph_random_walk(&g_de_bruijn, &g_de_bruijn_weights, &vertices, NULL, 0, IGRAPH_ALL, 1000, IGRAPH_RANDOM_WALK_STUCK_RETURN); + IGRAPH_ASSERT(igraph_vector_int_size(&vertices) == 1001); + + /* weighted, directed line graph, 4 edges, 10 steps, returns on stuck */ + igraph_random_walk(&g_line, &g_line_weights, &vertices, NULL, 4, IGRAPH_IN, 10, IGRAPH_RANDOM_WALK_STUCK_RETURN); + IGRAPH_ASSERT(igraph_vector_int_size(&vertices) == 5); + + + /* 2. both vertices and edges required -> igraph_i_random_walk_inclist */ + printf("\nBoth vertices and edges required:\n"); + + printf("Singleton graph with zero steps:\n"); + random_walk(&g_1, NULL, 0, IGRAPH_OUT, 0, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Singleton graph with one step:\n"); + random_walk(&g_line, NULL, 0, IGRAPH_OUT, 1, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Line graph:\n"); + random_walk(&g_line, NULL, 0, IGRAPH_OUT, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Line graph, 10 steps, returns on stuck:\n"); + random_walk(&g_line, NULL, 0, IGRAPH_OUT, 10, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Line graph backward:\n"); + random_walk(&g_line, NULL, 4, IGRAPH_IN, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + printf("Loop at vertex 0:\n"); + random_walk(&g_loop, NULL, 0, IGRAPH_OUT, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + igraph_rng_seed(igraph_rng_default(), 42); + printf("Checking an actual random walk with seed 42:\n"); + random_walk(&g_full, NULL, 4, IGRAPH_IN, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN); + + /* weighted, directed */ + igraph_random_walk(&g_de_bruijn, &g_de_bruijn_weights, &vertices, &edges, 0, IGRAPH_OUT, 1000, IGRAPH_RANDOM_WALK_STUCK_RETURN); + IGRAPH_ASSERT(igraph_vector_int_size(&vertices) == 1001); + IGRAPH_ASSERT(igraph_vector_int_size(&edges) == 1000); + + /* weighted, undirecetd */ + igraph_random_walk(&g_de_bruijn, &g_de_bruijn_weights, &vertices, &edges, 0, IGRAPH_ALL, 1000, IGRAPH_RANDOM_WALK_STUCK_RETURN); + IGRAPH_ASSERT(igraph_vector_int_size(&vertices) == 1001); + IGRAPH_ASSERT(igraph_vector_int_size(&edges) == 1000); + + /* weighted, directed line graph, 4 edges, 10 steps, returns on stuck */ + igraph_random_walk(&g_line, &g_line_weights, &vertices, &edges, 4, IGRAPH_IN, 10, IGRAPH_RANDOM_WALK_STUCK_RETURN); + IGRAPH_ASSERT(igraph_vector_int_size(&vertices) == 5); + IGRAPH_ASSERT(igraph_vector_int_size(&edges) == 4); + + + /* 3. only edges required, vertices = NULL -> igraph_i_random_walk_inclist */ + printf("\nOnly edges required, vertices = NULL:\n"); + + /* weighted, directed */ + igraph_random_walk(&g_de_bruijn, &g_de_bruijn_weights, NULL, &edges, 0, IGRAPH_OUT, 1000, IGRAPH_RANDOM_WALK_STUCK_RETURN); + IGRAPH_ASSERT(igraph_vector_int_size(&edges) == 1000); + + /* weighted, undirecetd */ + igraph_random_walk(&g_de_bruijn, &g_de_bruijn_weights, NULL, &edges, 0, IGRAPH_ALL, 1000, IGRAPH_RANDOM_WALK_STUCK_RETURN); + IGRAPH_ASSERT(igraph_vector_int_size(&edges) == 1000); + + + igraph_set_error_handler(igraph_error_handler_ignore); + printf("Checking error handling:\n"); + + /* 1. only vertices required, edges = NULL + unweighted -> igraph_i_random_walk_adjlist + weighted -> igraph_i_random_walk_inclist */ + printf("\nOnly vertices required, edges = NULL:\n"); + + printf("unweighted directed line graph, 10 steps, errors on stuck.\n"); + IGRAPH_ASSERT(igraph_random_walk(&g_line, NULL, &vertices, NULL, 0, IGRAPH_OUT, 10, IGRAPH_RANDOM_WALK_STUCK_ERROR) == IGRAPH_ERWSTUCK); + + printf("Vertex out of range.\n"); + IGRAPH_ASSERT(igraph_random_walk(&g_1, NULL, &vertices, NULL, 10, IGRAPH_OUT, 0, IGRAPH_RANDOM_WALK_STUCK_RETURN) == IGRAPH_EINVAL); + + printf("Negative number of steps.\n"); + IGRAPH_ASSERT(igraph_random_walk(&g_1, NULL, &vertices, NULL, 0, IGRAPH_OUT, -10, IGRAPH_RANDOM_WALK_STUCK_RETURN) == IGRAPH_EINVAL); + + /* weighted, directed line graph, 4 edges, 10 steps, errors on stuck */ + IGRAPH_ASSERT(igraph_random_walk(&g_line, &g_line_weights, &vertices, NULL, 4, IGRAPH_IN, 10, IGRAPH_RANDOM_WALK_STUCK_ERROR) == IGRAPH_ERWSTUCK); + + /* weighted, directed line graph, negative weight value for edge-0 */ + ec = igraph_ecount(&g_line); + igraph_vector_resize(&error_weights, ec); + VECTOR(error_weights)[0] = -10; + IGRAPH_ASSERT(igraph_random_walk(&g_line, &error_weights, &vertices, NULL, 0, IGRAPH_OUT, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN) == IGRAPH_EINVAL); + + /* weighted, directed line graph, invalid weight vector length (!= ec) */ + igraph_vector_resize(&error_weights, 2 * ec); + IGRAPH_ASSERT(igraph_random_walk(&g_line, &error_weights, &vertices, NULL, 0, IGRAPH_OUT, 4, IGRAPH_RANDOM_WALK_STUCK_RETURN) == IGRAPH_EINVAL); + + + /* 2. both vertices and edges required -> igraph_i_random_walk_inclist */ + printf("\nBoth vertices and edges required:\n"); + + printf("unweighted directed line graph, 10 steps, errors on stuck.\n"); + IGRAPH_ASSERT(igraph_random_walk(&g_line, NULL, &vertices, &edges, 0, IGRAPH_OUT, 10, IGRAPH_RANDOM_WALK_STUCK_ERROR) == IGRAPH_ERWSTUCK); + + /* weighted, directed line graph, 4 edges, 10 steps, errors on stuck */ + IGRAPH_ASSERT(igraph_random_walk(&g_line, &g_line_weights, &vertices, &edges, 4, IGRAPH_IN, 10, IGRAPH_RANDOM_WALK_STUCK_ERROR) == IGRAPH_ERWSTUCK); + + + igraph_destroy(&g_1); + igraph_destroy(&g_line); + igraph_destroy(&g_loop); + igraph_destroy(&g_full); + igraph_destroy(&g_de_bruijn); + + igraph_vector_destroy(&g_de_bruijn_weights); + igraph_vector_destroy(&g_line_weights); + igraph_vector_destroy(&error_weights); + igraph_vector_int_destroy(&vertices); + igraph_vector_int_destroy(&edges); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_random_walk.out b/tests/unit/igraph_random_walk.out new file mode 100644 index 0000000..6a9b2f8 --- /dev/null +++ b/tests/unit/igraph_random_walk.out @@ -0,0 +1,42 @@ +Only vertices required, edges = NULL: +Singleton graph with zero steps: +( 0 ) +Singleton graph with one step: +( 0 1 ) +Line graph: +( 0 1 2 3 4 ) +Line graph, 10 steps, returns on stuck: +( 0 1 2 3 4 ) +Line graph backward: +( 4 3 2 1 0 ) +Loop at vertex 0: +( 0 0 0 0 0 ) +Checking an actual random walk with seed 42: +( 4 2 11 16 19 ) + +Both vertices and edges required: +Singleton graph with zero steps: +( 0 ) +Singleton graph with one step: +( 0 1 ) +Line graph: +( 0 1 2 3 4 ) +Line graph, 10 steps, returns on stuck: +( 0 1 2 3 4 ) +Line graph backward: +( 4 3 2 1 0 ) +Loop at vertex 0: +( 0 0 0 0 0 ) +Checking an actual random walk with seed 42: +( 4 2 11 16 19 ) + +Only edges required, vertices = NULL: +Checking error handling: + +Only vertices required, edges = NULL: +unweighted directed line graph, 10 steps, errors on stuck. +Vertex out of range. +Negative number of steps. + +Both vertices and edges required: +unweighted directed line graph, 10 steps, errors on stuck. diff --git a/tests/unit/igraph_read_graph_graphdb.c b/tests/unit/igraph_read_graph_graphdb.c new file mode 100644 index 0000000..866cb58 --- /dev/null +++ b/tests/unit/igraph_read_graph_graphdb.c @@ -0,0 +1,58 @@ +/* -*- 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 + +*/ +#include +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + FILE *ifile; + + ifile = fopen("si2_b06m_s20.A98", "rb"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_graphdb(&g, ifile, IGRAPH_DIRECTED); + fclose(ifile); + IGRAPH_ASSERT(igraph_is_directed(&g)); + print_graph(&g); + igraph_destroy(&g); + + igraph_error_handler_t *handler = igraph_set_error_handler(&igraph_error_handler_printignore); + + /* Node count too low, extra bytes at end. */ + ifile = fopen("si2_b06m_s20-bad1.A98", "rb"); + IGRAPH_ASSERT(ifile != NULL); + IGRAPH_ASSERT(igraph_read_graph_graphdb(&g, ifile, IGRAPH_DIRECTED) == IGRAPH_PARSEERROR); + fclose(ifile); + + /* Truncated file. */ + ifile = fopen("si2_b06m_s20-bad2.A98", "rb"); + IGRAPH_ASSERT(ifile != NULL); + IGRAPH_ASSERT(igraph_read_graph_graphdb(&g, ifile, IGRAPH_DIRECTED) == IGRAPH_PARSEERROR); + fclose(ifile); + + igraph_set_error_handler(handler); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_read_graph_graphdb.out b/tests/unit/igraph_read_graph_graphdb.out new file mode 100644 index 0000000..3f8c91b --- /dev/null +++ b/tests/unit/igraph_read_graph_graphdb.out @@ -0,0 +1,8 @@ +directed: true +vcount: 4 +edges: { +0 2 +1 0 +3 0 +3 2 +} diff --git a/tests/unit/igraph_read_graph_graphml.c b/tests/unit/igraph_read_graph_graphml.c new file mode 100644 index 0000000..6072350 --- /dev/null +++ b/tests/unit/igraph_read_graph_graphml.c @@ -0,0 +1,213 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + +*/ +#include +#include +#include /* unlink */ + +#include "test_utilities.h" + +void custom_warning_handler (const char *reason, const char *file, + int line) { + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + printf("Warning: %s\n", reason); +} + +void dump_graph(const char* header, const igraph_t* g) { + fputs(header, stdout); + printf("Vertices: %" IGRAPH_PRId "\n", igraph_vcount(g)); + printf("Edges: %" IGRAPH_PRId "\n", igraph_ecount(g)); + printf("Directed: %i\n", igraph_is_directed(g) ? 1 : 0); + igraph_write_graph_edgelist(g, stdout); +} + +void dump_vertex_attribute_bool(const char* name, const igraph_t* g) { + igraph_integer_t i, n = igraph_vcount(g); + + printf("Vertex attribute '%s':", name); + for (i = 0; i < n; i++) { + printf(" %s", VAB(g, name, i) ? "true" : "false"); + } + printf("\n"); +} + +void dump_vertex_attribute_numeric(const char* name, const igraph_t* g) { + igraph_integer_t i, n = igraph_vcount(g); + + printf("Vertex attribute '%s':", name); + for (i = 0; i < n; i++) { + printf(" "); + igraph_real_printf_precise(VAN(g, name, i)); + } + printf("\n"); +} + +void dump_vertex_attribute_string(const char* name, const igraph_t* g) { + igraph_integer_t i, n = igraph_vcount(g); + + printf("Vertex attribute '%s':", name); + for (i = 0; i < n; i++) { + printf(" '%s'", VAS(g, name, i)); + } + printf("\n"); +} + +int main(void) { + igraph_t g; + igraph_error_handler_t* oldhandler; + igraph_warning_handler_t* oldwarnhandler; + igraph_error_t result; + FILE *ifile, *ofile; + + igraph_set_attribute_table(&igraph_cattribute_table); + + /* GraphML */ + ifile = fopen("test.graphml", "r"); + IGRAPH_ASSERT(ifile != NULL); + + oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); + oldwarnhandler = igraph_set_warning_handler(custom_warning_handler); + if ((result = igraph_read_graph_graphml(&g, ifile, 0))) { + /* maybe it is simply disabled at compile-time */ + if (result == IGRAPH_UNIMPLEMENTED) { + return 77; + } + return 1; + } + igraph_set_error_handler(oldhandler); + + fclose(ifile); + + /* Write it back */ + ofile = fopen("test2.graphml", "w"); + /* If we can't create the test file, just skip the test */ + if (ofile) { + if ((result = igraph_write_graph_graphml(&g, ofile, /*prefixattr=*/ true))) { + printf("Received unexpected return code: %d\n", result); + return 1; + } + fclose(ofile); + unlink("test2.graphml"); + } + dump_graph("Directed graph:\n", &g); + dump_vertex_attribute_bool("gender", &g); + dump_vertex_attribute_string("color", &g); + igraph_destroy(&g); + + /* The same with undirected graph */ + ifile = fopen("test.graphml", "r"); + IGRAPH_ASSERT(ifile != NULL); + if ((result = igraph_read_graph_graphml(&g, ifile, 0))) { + printf("Received unexpected return code: %d\n", result); + return 1; + } + fclose(ifile); + dump_graph("Undirected graph:\n", &g); + dump_vertex_attribute_bool("gender", &g); + dump_vertex_attribute_string("color", &g); + igraph_destroy(&g); + + /* Test a GraphML file with default attributes */ + ifile = fopen("graphml-default-attrs.xml", "r"); + IGRAPH_ASSERT(ifile != NULL); + if ((result = igraph_read_graph_graphml(&g, ifile, 0))) { + printf("Received unexpected return code: %d\n", result); + return 1; + } + fclose(ifile); + dump_graph("Graph with default attributes:\n", &g); + dump_vertex_attribute_bool("type", &g); + dump_vertex_attribute_string("gender", &g); + dump_vertex_attribute_numeric("age", &g); + dump_vertex_attribute_bool("retired", &g); + igraph_destroy(&g); + + /* Test a GraphML file with namespaces */ + ifile = fopen("graphml-namespace.xml", "r"); + IGRAPH_ASSERT(ifile != NULL); + if ((result = igraph_read_graph_graphml(&g, ifile, 0))) { + printf("Received unexpected return code: %d\n", result); + return 1; + } + fclose(ifile); + dump_graph("Graph with namespace:\n", &g); + igraph_destroy(&g); + + /* Test a not-really-valid GraphML file as it has no namespace information */ + ifile = fopen("graphml-lenient.xml", "r"); + IGRAPH_ASSERT(ifile != NULL); + if ((result = igraph_read_graph_graphml(&g, ifile, 0))) { + printf("Received unexpected return code: %d\n", result); + return 1; + } + fclose(ifile); + dump_graph("Graph without namespace information:\n", &g); + igraph_destroy(&g); + + /* Test a GraphML file with excess whitespace around attribute values + * (which we attempt to handle gracefully) */ + ifile = fopen("graphml-whitespace.xml", "r"); + IGRAPH_ASSERT(ifile != NULL); + if ((result = igraph_read_graph_graphml(&g, ifile, 0))) { + printf("Received unexpected return code: %d\n", result); + return 1; + } + fclose(ifile); + dump_graph("Graph with whitespace in attributes:\n", &g); + dump_vertex_attribute_bool("type", &g); + dump_vertex_attribute_string("name", &g); + dump_vertex_attribute_numeric("weight", &g); + igraph_destroy(&g); + + /* Test a GraphML file from yEd -- we should be able to parse the nodes and + * edges */ + igraph_set_warning_handler(igraph_warning_handler_ignore); + ifile = fopen("graphml-yed.xml", "r"); + IGRAPH_ASSERT(ifile != NULL); + if ((result = igraph_read_graph_graphml(&g, ifile, 0))) { + printf("Received unexpected return code: %d\n", result); + return 1; + } + fclose(ifile); + dump_graph("Graph from yEd:\n", &g); + igraph_destroy(&g); + + /* Restore the old error handler */ + igraph_set_error_handler(igraph_error_handler_abort); + + /* Restore the old warning handler */ + igraph_set_warning_handler(oldwarnhandler); + + /* There were sometimes problems with this file */ + /* Only if called from R though, and only on random occasions, once in every + ten reads. Do testing here doesn't make much sense, but if we have the file + then let's do it anyway. */ + ifile = fopen("graphml-hsa05010.xml", "r"); + IGRAPH_ASSERT(ifile != NULL); + igraph_read_graph_graphml(&g, ifile, 0); + fclose(ifile); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_read_graph_graphml.out b/tests/unit/igraph_read_graph_graphml.out new file mode 100644 index 0000000..a38b9af --- /dev/null +++ b/tests/unit/igraph_read_graph_graphml.out @@ -0,0 +1,67 @@ +Warning: Unknown attribute key 'd3' in a tag, ignoring attribute. +Directed graph: +Vertices: 6 +Edges: 7 +Directed: 0 +0 1 +0 2 +1 3 +2 3 +2 4 +3 5 +4 5 +Vertex attribute 'gender': true true false true false false +Vertex attribute 'color': 'green' 'yellow' 'blue' 'red "with entities"' 'yellow' 'turquoise' +Warning: Unknown attribute key 'd3' in a tag, ignoring attribute. +Undirected graph: +Vertices: 6 +Edges: 7 +Directed: 0 +0 1 +0 2 +1 3 +2 3 +2 4 +3 5 +4 5 +Vertex attribute 'gender': true true false true false false +Vertex attribute 'color': 'green' 'yellow' 'blue' 'red "with entities"' 'yellow' 'turquoise' +Graph with default attributes: +Vertices: 3 +Edges: 2 +Directed: 1 +0 1 +0 2 +Vertex attribute 'type': false true true +Vertex attribute 'gender': 'male' 'female' 'male' +Vertex attribute 'age': 30 20 20 +Vertex attribute 'retired': false false false +Graph with namespace: +Vertices: 3 +Edges: 2 +Directed: 0 +0 1 +1 2 +Graph without namespace information: +Vertices: 3 +Edges: 2 +Directed: 0 +0 1 +1 2 +Graph with whitespace in attributes: +Vertices: 5 +Edges: 4 +Directed: 0 +0 1 +1 2 +1 3 +2 3 +Vertex attribute 'type': true false true true true +Vertex attribute 'name': 'spam' ' ' ' ham' 'bacon ' ' eggs ' +Vertex attribute 'weight': 1 NaN 3 4 5 +Graph from yEd: +Vertices: 3 +Edges: 2 +Directed: 1 +1 0 +2 1 diff --git a/tests/unit/igraph_realize_bipartite_degree_sequence.c b/tests/unit/igraph_realize_bipartite_degree_sequence.c new file mode 100644 index 0000000..e43edf2 --- /dev/null +++ b/tests/unit/igraph_realize_bipartite_degree_sequence.c @@ -0,0 +1,323 @@ +/* + IGraph library. + Constructing realizations of degree sequences and bi-degree sequences. + Copyright (C) 2024 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 +#include + +#include "test_utilities.h" + +void check_degrees( + const igraph_t *graph, + const igraph_vector_int_t *ds1, const igraph_vector_int_t *ds2 +) { + const igraph_integer_t vcount = igraph_vcount(graph); + const igraph_integer_t n1 = igraph_vector_int_size(ds1); + const igraph_integer_t n2 = igraph_vector_int_size(ds2); + igraph_vector_int_t degrees, expected_degrees; + + if (vcount != n1+n2) { + printf("Bad graph size %" IGRAPH_PRId + " for bidegree sequence size (%" IGRAPH_PRId + ", %" IGRAPH_PRId ")", + vcount, n1, n2); + return; + } + + igraph_vector_int_init(°rees, vcount); + igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + + igraph_vector_int_init_copy(&expected_degrees, ds1); + igraph_vector_int_append(&expected_degrees, ds2); + if (! igraph_vector_int_all_e(°rees, &expected_degrees)) { + printf("Degrees not as expected!"); + printf("Expected: "); print_vector_int(&expected_degrees); + printf("Actual: "); print_vector_int(°rees); + } + + igraph_vector_int_destroy(&expected_degrees); + igraph_vector_int_destroy(°rees); +} + +int main(void) { + igraph_vector_int_t ds1, ds2; + igraph_t g; + igraph_bool_t is_connected; + igraph_bool_t is_bipartite; + igraph_bool_t is_simple; + + igraph_realize_degseq_t methods[] = { + IGRAPH_REALIZE_DEGSEQ_SMALLEST, + IGRAPH_REALIZE_DEGSEQ_LARGEST, + IGRAPH_REALIZE_DEGSEQ_INDEX + }; + char *method_names[] = { + "smallest", "largest", "index" + }; + + // Edge cases + printf("===EDGE CASES===\n"); + + // Tests that should fail with IGRAPH_EINVAL + printf("--ds1 empty--\n"); + igraph_vector_int_init(&ds1, 0); + igraph_vector_int_init_int(&ds2, 2, + 1, 1); + CHECK_ERROR( + igraph_realize_bipartite_degree_sequence(&g, &ds1, &ds2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST), + IGRAPH_EINVAL + ); + + printf("--ds2 empty--\n"); + CHECK_ERROR( + igraph_realize_bipartite_degree_sequence(&g, &ds2, &ds1, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST), + IGRAPH_EINVAL + ); + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + + printf("\n===Empty degree sequences===\n"); + + igraph_vector_int_init(&ds1, 0); + igraph_vector_int_init(&ds2, 0); + for (size_t i=0; i < sizeof(methods) / sizeof(methods[0]); i++) { + printf("Method: %s\n", method_names[i]); + igraph_realize_bipartite_degree_sequence( + &g, &ds1, &ds2, IGRAPH_SIMPLE_SW, methods[i]); + print_graph(&g); + printf("\n"); + igraph_destroy(&g); + } + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + + printf("\n===All 0 degree sequences===\n"); + + igraph_vector_int_init(&ds1, 3); + igraph_vector_int_init(&ds2, 4); + for (size_t i=0; i < sizeof(methods) / sizeof(methods[0]); i++) { + printf("Method: %s\n", method_names[i]); + igraph_realize_bipartite_degree_sequence( + &g, &ds1, &ds2, IGRAPH_SIMPLE_SW, methods[i]); + print_graph(&g); + printf("\n"); + igraph_destroy(&g); + } + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + + printf("\n===TESTING SMALLEST METHOD===\n"); + // Simple undirected bipartite graph. + printf("\n===Simple, connected graph===\n"); + igraph_integer_t deg1[] = {2, 3, 2, 1}; + igraph_integer_t deg2[] = {3, 1, 2, 1, 1}; + + igraph_vector_int_init_array(&ds1, deg1, 4); + igraph_vector_int_init_array(&ds2, deg2, 5); + + igraph_realize_bipartite_degree_sequence(&g, &ds1, &ds2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + + igraph_is_simple(&g, &is_simple); + igraph_is_connected(&g, &is_connected, IGRAPH_STRONG); + igraph_is_bipartite(&g, &is_bipartite, NULL); + + print_graph(&g); + check_degrees(&g, &ds1, &ds2); + + printf("Simple: %d\n", is_simple); + printf("Connected: %d\n", is_connected); + printf("Bipartite: %d\n", is_bipartite); + + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + igraph_destroy(&g); + + // undirected bipartite multigraph. + igraph_bool_t has_multi; + printf("\n===Connected multigraph===\n"); + + igraph_integer_t deg1m[] = {2, 3, 1}; + igraph_integer_t deg2m[] = {4, 2}; + + igraph_vector_int_init_array(&ds1, deg1m, 3); + igraph_vector_int_init_array(&ds2, deg2m, 2); + + igraph_realize_bipartite_degree_sequence(&g, &ds1, &ds2, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + + igraph_has_multiple(&g, &has_multi); + igraph_is_connected(&g, &is_connected, IGRAPH_STRONG); + igraph_is_bipartite(&g, &is_bipartite, NULL); + + print_graph(&g); + check_degrees(&g, &ds1, &ds2); + + printf("vcount: %" IGRAPH_PRId "\n", igraph_vcount(&g)); + printf("Has multiedges: %d\n", has_multi); + printf("Connected: %d\n", is_connected); + printf("Bipartite: %d\n", is_bipartite); + + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + igraph_destroy(&g); + + printf("\n===TESTING LARGEST METHOD===\n"); + // Simple undirected bipartite graph. + printf("\n===Simple graph===\n"); + igraph_integer_t deg1l[] = {2, 3, 2, 1}; + igraph_integer_t deg2l[] = {3, 1, 2, 1, 1}; + + igraph_vector_int_init_array(&ds1, deg1l, 4); + igraph_vector_int_init_array(&ds2, deg2l, 5); + + igraph_realize_bipartite_degree_sequence(&g, &ds1, &ds2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + + igraph_is_simple(&g, &is_simple); + // For this method, it does not have to be connected + igraph_is_connected(&g, &is_connected, IGRAPH_WEAK); + igraph_is_bipartite(&g, &is_bipartite, NULL); + + print_graph(&g); + check_degrees(&g, &ds1, &ds2); + + printf("Simple: %d\n", is_simple); + printf("Connected: %d\n", is_connected); + printf("Bipartite: %d\n", is_bipartite); + + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + igraph_destroy(&g); + + // undirected bipartite multigraph. + printf("\n===Multigraph===\n"); + + igraph_integer_t deg1l_m[] = {2, 3, 1}; + igraph_integer_t deg2l_m[]= {4, 2}; + + igraph_vector_int_init_array(&ds1, deg1l_m, 3); + igraph_vector_int_init_array(&ds2, deg2l_m, 2); + + igraph_realize_bipartite_degree_sequence(&g, &ds1, &ds2, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + + igraph_has_multiple(&g, &has_multi); + // For this method, it does not need to be connected + igraph_is_connected(&g, &is_connected, IGRAPH_WEAK); + igraph_is_bipartite(&g, &is_bipartite, NULL); + + print_graph(&g); + check_degrees(&g, &ds1, &ds2); + + printf("Has multiedges: %d\n", has_multi); + printf("Connected: %d\n", is_connected); + printf("Bipartite: %d\n", is_bipartite); + + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + igraph_destroy(&g); + + printf("\n===TESTING INDEX METHOD===\n"); + // Simple, undirected bipartite. + printf("\n===Simple graph===\n"); + igraph_integer_t deg1i[] = {1, 4, 3, 2}; + igraph_integer_t deg2i[] = {2, 1, 1, 4, 2}; + + igraph_vector_int_init_array(&ds1, deg1i, 4); + igraph_vector_int_init_array(&ds2, deg2i, 5); + + igraph_realize_bipartite_degree_sequence(&g, &ds1, &ds2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + + igraph_is_simple(&g, &is_simple); + // For this method, it does not have to be connected + igraph_is_connected(&g, &is_connected, IGRAPH_STRONG); + igraph_is_bipartite(&g, &is_bipartite, NULL); + + print_graph(&g); + check_degrees(&g, &ds1, &ds2); + + printf("Simple: %d\n", is_simple); + printf("Connected: %d\n", is_connected); + printf("Bipartite: %d\n", is_bipartite); + + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + igraph_destroy(&g); + + // undirected bipartite multigraph. + printf("\n===Multigraph===\n"); + + igraph_integer_t deg1i_m[] = {2, 3, 1}; + igraph_integer_t deg2i_m[]= {4, 2}; + + igraph_vector_int_init_array(&ds1, deg1i_m, 3); + igraph_vector_int_init_array(&ds2, deg2i_m, 2); + + igraph_realize_bipartite_degree_sequence(&g, &ds1, &ds2, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + + print_graph(&g); + check_degrees(&g, &ds1, &ds2); + + igraph_has_multiple(&g, &has_multi); + // For this method, it does not need to be connected + igraph_is_connected(&g, &is_connected, IGRAPH_STRONG); + igraph_is_bipartite(&g, &is_bipartite, NULL); + + printf("Has multiedges: %d\n", has_multi); + printf("Connected: %d\n", is_connected); + printf("Bipartite: %d\n", is_bipartite); + + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + igraph_destroy(&g); + + // A bidegree sequence that is not potentially connected + igraph_vector_int_init_int(&ds1, 3, + 1, 1, 1); + for (size_t i=0; i < sizeof(methods) / sizeof(methods[0]); i++) { + igraph_realize_bipartite_degree_sequence( + &g, &ds1, &ds1, IGRAPH_SIMPLE_SW, methods[i]); + igraph_is_bipartite(&g, &is_bipartite, NULL); + IGRAPH_ASSERT(is_bipartite); + igraph_destroy(&g); + } + igraph_vector_int_destroy(&ds1); + + // A bidegree sequence that can only be realized as a multigraph, + // but not a simple graph + igraph_vector_int_init_int(&ds1, 3, + 1, 3, 1); + igraph_vector_int_init_int(&ds2, 2, + 2, 3); + for (size_t i=0; i < sizeof(methods) / sizeof(methods[0]); i++) { + CHECK_ERROR( + igraph_realize_bipartite_degree_sequence( + &g, &ds1, &ds2, IGRAPH_SIMPLE_SW, methods[i]), + IGRAPH_EINVAL + ); + + igraph_realize_bipartite_degree_sequence( + &g, &ds1, &ds2, IGRAPH_MULTI_SW, methods[i]); + + igraph_is_bipartite(&g, &is_bipartite, NULL); + IGRAPH_ASSERT(is_bipartite); + igraph_destroy(&g); + } + igraph_vector_int_destroy(&ds1); + igraph_vector_int_destroy(&ds2); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_realize_bipartite_degree_sequence.out b/tests/unit/igraph_realize_bipartite_degree_sequence.out new file mode 100644 index 0000000..638007d --- /dev/null +++ b/tests/unit/igraph_realize_bipartite_degree_sequence.out @@ -0,0 +1,148 @@ +===EDGE CASES=== +--ds1 empty-- +--ds2 empty-- + +===Empty degree sequences=== +Method: smallest +directed: false +vcount: 0 +edges: { +} + +Method: largest +directed: false +vcount: 0 +edges: { +} + +Method: index +directed: false +vcount: 0 +edges: { +} + + +===All 0 degree sequences=== +Method: smallest +directed: false +vcount: 7 +edges: { +} + +Method: largest +directed: false +vcount: 7 +edges: { +} + +Method: index +directed: false +vcount: 7 +edges: { +} + + +===TESTING SMALLEST METHOD=== + +===Simple, connected graph=== +directed: false +vcount: 9 +edges: { +4 3 +8 1 +7 1 +4 1 +5 0 +6 0 +4 2 +6 2 +} +Simple: 1 +Connected: 1 +Bipartite: 1 + +===Connected multigraph=== +directed: false +vcount: 5 +edges: { +3 2 +3 0 +3 0 +3 1 +4 1 +4 1 +} +vcount: 5 +Has multiedges: 1 +Connected: 1 +Bipartite: 1 + +===TESTING LARGEST METHOD=== + +===Simple graph=== +directed: false +vcount: 9 +edges: { +4 1 +6 1 +5 1 +4 0 +6 0 +4 2 +7 2 +8 3 +} +Simple: 1 +Connected: 0 +Bipartite: 1 + +===Multigraph=== +directed: false +vcount: 5 +edges: { +3 1 +3 1 +3 0 +4 0 +4 1 +3 2 +} +Has multiedges: 1 +Connected: 1 +Bipartite: 1 + +===TESTING INDEX METHOD=== + +===Simple graph=== +directed: false +vcount: 9 +edges: { +7 0 +7 1 +4 1 +8 1 +5 1 +7 2 +4 2 +8 2 +7 3 +6 3 +} +Simple: 1 +Connected: 1 +Bipartite: 1 + +===Multigraph=== +directed: false +vcount: 5 +edges: { +3 0 +3 0 +3 1 +4 1 +4 1 +3 2 +} +Has multiedges: 1 +Connected: 1 +Bipartite: 1 diff --git a/tests/unit/igraph_realize_degree_sequence.c b/tests/unit/igraph_realize_degree_sequence.c new file mode 100644 index 0000000..0d4efcc --- /dev/null +++ b/tests/unit/igraph_realize_degree_sequence.c @@ -0,0 +1,138 @@ + +#include +#include + +#include "test_utilities.h" + +void realize1(igraph_vector_int_t *ods, igraph_vector_int_t *ids, igraph_edge_type_sw_t et, igraph_realize_degseq_t method) { + igraph_t graph; + int err; + err = igraph_realize_degree_sequence(&graph, ods, ids, et, method); + if (err == IGRAPH_SUCCESS) { + printf("\n"); + print_graph(&graph); + igraph_destroy(&graph); + } else if (err == IGRAPH_UNIMPLEMENTED) { + printf(" not implemented\n"); + } else { + printf(" not graphical\n"); + } +} + +void realize2(igraph_vector_int_t *ods, igraph_vector_int_t *ids, igraph_edge_type_sw_t et) { + printf("Largest:"); + realize1(ods, ids, et, IGRAPH_REALIZE_DEGSEQ_LARGEST); + printf("Smallest:"); + realize1(ods, ids, et, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + printf("Index:"); + realize1(ods, ids, et, IGRAPH_REALIZE_DEGSEQ_INDEX); +} + +void undirected_print_destroy(igraph_vector_int_t *ds) { + print_vector_int(ds); + printf("\nSIMPLE GRAPH:\n"); + realize2(ds, NULL, IGRAPH_SIMPLE_SW); + printf("\nLOOPLESS MULTIGRAPH:\n"); + realize2(ds, NULL, IGRAPH_MULTI_SW); + printf("\nLOOPY MULTIGRAPH:\n"); + realize2(ds, NULL, IGRAPH_MULTI_SW | IGRAPH_LOOPS_SW); + printf("\n\n"); + igraph_vector_int_destroy(ds); +} + +void directed_print_destroy(igraph_vector_int_t *ods, igraph_vector_int_t *ids) { + print_vector_int(ods); + print_vector_int(ids); + printf("\nSIMPLE GRAPH:\n"); + realize2(ods, ids, IGRAPH_SIMPLE_SW); + printf("\nLOOPLESS MULTIGRAPH:\n"); + realize2(ods, ids, IGRAPH_MULTI_SW); + printf("\nLOOPY MULTIGRAPH:\n"); + realize2(ods, ids, IGRAPH_MULTI_SW | IGRAPH_LOOPS_SW); + printf("\n\n"); + igraph_vector_int_destroy(ods); + igraph_vector_int_destroy(ids); +} + + +int main(void) { + igraph_vector_int_t ds, ods, ids; + + igraph_set_error_handler(&igraph_error_handler_ignore); + + /* Undirected */ + + igraph_vector_int_init(&ds, 0); + undirected_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 1, 2, 2, 3, -1); + undirected_print_destroy(&ds); + + /* contains negative degree */ + igraph_vector_int_init_int_end(&ds, -1, 1, 2, 2, -3, -1); + undirected_print_destroy(&ds); + + /* odd sum */ + igraph_vector_int_init_int_end(&ds, -1, 1, 1, 2, 3, -1); + undirected_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 1, 2, 3, -1); + undirected_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 4, 4, 4, -1); + undirected_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 3, 5, -1); + undirected_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 5, 3, -1); + undirected_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 1, 3, 3, 4, 1, 2, 1, 1, 1, 3, -1); + undirected_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 2, 0, 3, 2, 2, 2, 2, 3, -1); + undirected_print_destroy(&ds); + + /* Directed */ + + igraph_vector_int_init(&ods, 0); + igraph_vector_int_init(&ids, 0); + directed_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ods, -1, 3, 0, 1, 1, 1, 1, 0, 1, -1); + igraph_vector_int_init_int_end(&ids, -1, 2, 1, 0, 2, 2, 1, 0, 0, -1); + directed_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ods, -1, 3, 1, 2, 3, 1, 2, 2, -1); + igraph_vector_int_init_int_end(&ids, -1, 2, 2, 1, 2, 3, 2, 2, -1); + directed_print_destroy(&ods, &ids); + + /* single loops: graphical, but multi-eges only: non-graphical */ + igraph_vector_int_init_int_end(&ids, -1, 1, 0, 2, -1); + igraph_vector_int_init_int_end(&ods, -1, 0, 1, 2, -1); + directed_print_destroy(&ods, &ids); + + /* same as before, different ordering, to test the "index" method */ + igraph_vector_int_init_int_end(&ids, -1, 2, 0, 1, -1); + igraph_vector_int_init_int_end(&ods, -1, 2, 1, 0, -1); + directed_print_destroy(&ods, &ids); + + /* same as before, different ordering, to test the "index" method */ + igraph_vector_int_init_int_end(&ids, -1, 0, 2, 1, -1); + igraph_vector_int_init_int_end(&ods, -1, 1, 2, 0, -1); + directed_print_destroy(&ods, &ids); + + igraph_vector_int_init_int_end(&ids, -1, 2, 0, -1); + igraph_vector_int_init_int_end(&ods, -1, 0, 2, -1); + directed_print_destroy(&ods, &ids); + + /* simple complete graph on 4 vertices */ + igraph_vector_int_init_int_end(&ids, -1, 3, 3, 3, 3, -1); + igraph_vector_int_init_int_end(&ods, -1, 3, 3, 3, 3, -1); + directed_print_destroy(&ods, &ids); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_realize_degree_sequence.out b/tests/unit/igraph_realize_degree_sequence.out new file mode 100644 index 0000000..5e6386f --- /dev/null +++ b/tests/unit/igraph_realize_degree_sequence.out @@ -0,0 +1,974 @@ +( ) + +SIMPLE GRAPH: +Largest: +directed: false +vcount: 0 +edges: { +} +Smallest: +directed: false +vcount: 0 +edges: { +} +Index: +directed: false +vcount: 0 +edges: { +} + +LOOPLESS MULTIGRAPH: +Largest: +directed: false +vcount: 0 +edges: { +} +Smallest: +directed: false +vcount: 0 +edges: { +} +Index: +directed: false +vcount: 0 +edges: { +} + +LOOPY MULTIGRAPH: +Largest: +directed: false +vcount: 0 +edges: { +} +Smallest: +directed: false +vcount: 0 +edges: { +} +Index: +directed: false +vcount: 0 +edges: { +} + + +( 1 2 2 3 ) + +SIMPLE GRAPH: +Largest: +directed: false +vcount: 4 +edges: { +3 2 +3 1 +3 0 +2 1 +} +Smallest: +directed: false +vcount: 4 +edges: { +3 0 +3 2 +2 1 +3 1 +} +Index: +directed: false +vcount: 4 +edges: { +3 0 +3 1 +2 1 +3 2 +} + +LOOPLESS MULTIGRAPH: +Largest: +directed: false +vcount: 4 +edges: { +3 1 +3 2 +1 0 +3 2 +} +Smallest: +directed: false +vcount: 4 +edges: { +3 0 +3 1 +2 1 +3 2 +} +Index: +directed: false +vcount: 4 +edges: { +3 0 +3 1 +2 1 +3 2 +} + +LOOPY MULTIGRAPH: +Largest: +directed: false +vcount: 4 +edges: { +3 1 +3 2 +1 0 +3 2 +} +Smallest: +directed: false +vcount: 4 +edges: { +3 0 +3 1 +2 1 +3 2 +} +Index: +directed: false +vcount: 4 +edges: { +3 0 +3 1 +2 1 +3 2 +} + + +( 1 2 2 -3 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPY MULTIGRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + + +( 1 1 2 3 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPY MULTIGRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + + +( 1 2 3 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: +directed: false +vcount: 3 +edges: { +2 1 +2 0 +2 1 +} +Smallest: +directed: false +vcount: 3 +edges: { +2 0 +2 1 +2 1 +} +Index: +directed: false +vcount: 3 +edges: { +2 0 +2 1 +2 1 +} + +LOOPY MULTIGRAPH: +Largest: +directed: false +vcount: 3 +edges: { +2 1 +2 0 +2 1 +} +Smallest: +directed: false +vcount: 3 +edges: { +2 0 +2 1 +2 1 +} +Index: +directed: false +vcount: 3 +edges: { +2 0 +2 1 +2 1 +} + + +( 4 4 4 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: +directed: false +vcount: 3 +edges: { +1 0 +2 1 +2 0 +2 1 +2 0 +1 0 +} +Smallest: +directed: false +vcount: 3 +edges: { +2 0 +1 0 +2 0 +1 0 +2 1 +2 1 +} +Index: +directed: false +vcount: 3 +edges: { +1 0 +2 0 +2 0 +1 0 +2 1 +2 1 +} + +LOOPY MULTIGRAPH: +Largest: +directed: false +vcount: 3 +edges: { +1 0 +2 1 +2 0 +2 1 +2 0 +1 0 +} +Smallest: +directed: false +vcount: 3 +edges: { +2 0 +1 0 +2 0 +1 0 +2 1 +2 1 +} +Index: +directed: false +vcount: 3 +edges: { +1 0 +2 0 +2 0 +1 0 +2 1 +2 1 +} + + +( 3 5 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPY MULTIGRAPH: +Largest: +directed: false +vcount: 2 +edges: { +1 0 +1 0 +1 0 +1 1 +} +Smallest: +directed: false +vcount: 2 +edges: { +1 0 +1 0 +1 0 +1 1 +} +Index: +directed: false +vcount: 2 +edges: { +1 0 +1 0 +1 0 +1 1 +} + + +( 5 3 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPY MULTIGRAPH: +Largest: +directed: false +vcount: 2 +edges: { +1 0 +1 0 +1 0 +0 0 +} +Smallest: +directed: false +vcount: 2 +edges: { +1 0 +1 0 +1 0 +0 0 +} +Index: +directed: false +vcount: 2 +edges: { +1 0 +1 0 +1 0 +0 0 +} + + +( 1 3 3 4 1 2 1 1 1 3 ) + +SIMPLE GRAPH: +Largest: +directed: false +vcount: 10 +edges: { +9 3 +3 2 +3 1 +5 3 +9 2 +9 1 +2 1 +8 5 +7 6 +4 0 +} +Smallest: +directed: false +vcount: 10 +edges: { +8 3 +7 3 +6 1 +4 2 +9 0 +9 5 +5 2 +2 1 +9 3 +3 1 +} +Index: +directed: false +vcount: 10 +edges: { +3 0 +3 1 +2 1 +9 1 +3 2 +9 2 +5 3 +5 4 +9 6 +8 7 +} + +LOOPLESS MULTIGRAPH: +Largest: +directed: false +vcount: 10 +edges: { +3 1 +9 2 +5 3 +9 1 +3 2 +4 0 +7 6 +8 5 +9 1 +3 2 +} +Smallest: +directed: false +vcount: 10 +edges: { +8 3 +7 1 +6 2 +9 4 +3 0 +5 3 +5 1 +2 1 +9 2 +9 3 +} +Index: +directed: false +vcount: 10 +edges: { +3 0 +3 1 +2 1 +9 1 +9 2 +3 2 +5 3 +5 4 +9 6 +8 7 +} + +LOOPY MULTIGRAPH: +Largest: +directed: false +vcount: 10 +edges: { +3 1 +9 2 +5 3 +9 1 +3 2 +4 0 +7 6 +8 5 +9 1 +3 2 +} +Smallest: +directed: false +vcount: 10 +edges: { +8 3 +7 1 +6 2 +9 4 +3 0 +5 3 +5 1 +2 1 +9 2 +9 3 +} +Index: +directed: false +vcount: 10 +edges: { +3 0 +3 1 +2 1 +9 1 +9 2 +3 2 +5 3 +5 4 +9 6 +8 7 +} + + +( 2 0 3 2 2 2 2 3 ) + +SIMPLE GRAPH: +Largest: +directed: false +vcount: 8 +edges: { +7 2 +7 6 +7 5 +4 2 +3 2 +4 0 +3 0 +6 5 +} +Smallest: +directed: false +vcount: 8 +edges: { +6 2 +7 6 +5 2 +7 5 +7 0 +3 2 +4 0 +4 3 +} +Index: +directed: false +vcount: 8 +edges: { +2 0 +7 0 +7 2 +3 2 +4 3 +5 4 +6 5 +7 6 +} + +LOOPLESS MULTIGRAPH: +Largest: +directed: false +vcount: 8 +edges: { +7 2 +3 0 +5 4 +7 6 +3 2 +5 0 +7 4 +6 2 +} +Smallest: +directed: false +vcount: 8 +edges: { +6 2 +7 6 +7 0 +3 0 +4 3 +5 4 +5 2 +7 2 +} +Index: +directed: false +vcount: 8 +edges: { +2 0 +7 0 +7 2 +3 2 +4 3 +5 4 +6 5 +7 6 +} + +LOOPY MULTIGRAPH: +Largest: +directed: false +vcount: 8 +edges: { +7 2 +3 0 +5 4 +7 6 +3 2 +5 0 +7 4 +6 2 +} +Smallest: +directed: false +vcount: 8 +edges: { +6 2 +7 6 +7 0 +3 0 +4 3 +5 4 +5 2 +7 2 +} +Index: +directed: false +vcount: 8 +edges: { +2 0 +7 0 +7 2 +3 2 +4 3 +5 4 +6 5 +7 6 +} + + +( ) +( ) + +SIMPLE GRAPH: +Largest: +directed: true +vcount: 0 +edges: { +} +Smallest: +directed: true +vcount: 0 +edges: { +} +Index: +directed: true +vcount: 0 +edges: { +} + +LOOPLESS MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + +LOOPY MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + + +( 3 0 1 1 1 1 0 1 ) +( 2 1 0 2 2 1 0 0 ) + +SIMPLE GRAPH: +Largest: +directed: true +vcount: 8 +edges: { +0 3 +0 4 +0 5 +3 0 +4 0 +5 4 +2 3 +7 1 +} +Smallest: +directed: true +vcount: 8 +edges: { +7 0 +2 3 +5 4 +3 0 +0 4 +0 3 +0 5 +4 1 +} +Index: +directed: true +vcount: 8 +edges: { +0 3 +0 4 +0 5 +2 0 +3 4 +4 3 +5 0 +7 1 +} + +LOOPLESS MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + +LOOPY MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + + +( 3 1 2 3 1 2 2 ) +( 2 2 1 2 3 2 2 ) + +SIMPLE GRAPH: +Largest: +directed: true +vcount: 7 +edges: { +4 0 +3 4 +3 5 +3 6 +1 4 +0 1 +0 3 +0 5 +6 2 +6 1 +2 6 +2 3 +5 0 +5 4 +} +Smallest: +directed: true +vcount: 7 +edges: { +2 4 +2 0 +0 3 +0 5 +0 6 +6 4 +6 1 +1 3 +3 5 +3 4 +3 1 +4 6 +5 0 +5 2 +} +Index: +directed: true +vcount: 7 +edges: { +0 4 +0 3 +0 5 +1 6 +2 4 +2 1 +3 0 +3 6 +3 5 +4 0 +5 4 +5 3 +6 1 +6 2 +} + +LOOPLESS MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + +LOOPY MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + + +( 0 1 2 ) +( 1 0 2 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + +LOOPY MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + + +( 2 1 0 ) +( 2 0 1 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + +LOOPY MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + + +( 1 2 0 ) +( 0 2 1 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + +LOOPY MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + + +( 0 2 ) +( 2 0 ) + +SIMPLE GRAPH: +Largest: not graphical +Smallest: not graphical +Index: not graphical + +LOOPLESS MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + +LOOPY MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + + +( 3 3 3 3 ) +( 3 3 3 3 ) + +SIMPLE GRAPH: +Largest: +directed: true +vcount: 4 +edges: { +0 1 +0 2 +0 3 +1 0 +1 2 +1 3 +2 0 +2 1 +2 3 +3 0 +3 1 +3 2 +} +Smallest: +directed: true +vcount: 4 +edges: { +3 0 +3 1 +3 2 +2 3 +2 0 +2 1 +1 3 +1 2 +1 0 +0 3 +0 2 +0 1 +} +Index: +directed: true +vcount: 4 +edges: { +0 1 +0 2 +0 3 +1 0 +1 2 +1 3 +2 0 +2 1 +2 3 +3 0 +3 1 +3 2 +} + +LOOPLESS MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + +LOOPY MULTIGRAPH: +Largest: not implemented +Smallest: not implemented +Index: not implemented + + diff --git a/tests/unit/igraph_recent_degree_aging_game.c b/tests/unit/igraph_recent_degree_aging_game.c new file mode 100644 index 0000000..384731f --- /dev/null +++ b/tests/unit/igraph_recent_degree_aging_game.c @@ -0,0 +1,93 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_bool_t tree; + igraph_vector_int_t outseq; + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/0, /*edges per step(m)*/ 1, /*outseq*/ NULL, + /*outpref?*/ 0, /*pa_exp*/ 1, /*aging_exp*/ 1, /*aging_bins*/ 1, /*time_window*/ 1, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("No edges:\n"); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/5, /*edges per step(m)*/ 0, /*outseq*/ NULL, + /*outpref?*/ 0, /*pa_exp*/ 1, /*aging_exp*/ 1, /*aging_bins*/ 6, /*time_window*/ 1, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Prefer more edges to make a star of double edges:\n"); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/5, /*edges per step(m)*/ 2, /*outseq*/ NULL, + /*outpref?*/ 0, /*pa_exp*/ 20, /*aging_exp*/ 0, /*aging_bins*/ 1, /*time_window*/ 100, /*zero appeal*/ 0.001, + /*directed?*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Prefer older edges to make a star:\n"); + igraph_vector_int_init_int(&outseq, 7, 1, 2, 1, 2, 1, 2, 1); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/7, /*edges per step(m)*/ 0, &outseq, + /*outpref?*/ 0, /*pa_exp*/ 0, /*aging_exp*/ 20, /*aging_bins*/ 8, /*time_window*/ 100, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Checking if adding one edge per step makes a tree.\n"); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/10, /*edges per step(m)*/ 1, /*outseq*/ NULL, + /*outpref?*/ 1, /*pa_exp*/ 2, /*aging_exp*/ 2, /*aging_bins*/ 5, /*time_window*/ 4, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_SUCCESS); + igraph_is_tree(&g, &tree, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(tree); + igraph_destroy(&g); + igraph_vector_int_destroy(&outseq); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Checking if these errors are properly handled:\n"); + printf("Negative number of vertices.\n"); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/-20, /*edges per step(m)*/ 1, /*outseq*/ NULL, + /*outpref?*/ 0, /*pa_exp*/ 2, /*aging_exp*/ 2, /*aging_bins*/ 3, /*time_window*/ 4, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_EINVAL); + + printf("Negative number of edges.\n"); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/20, /*edges per step(m)*/ -1, /*outseq*/ NULL, + /*outpref?*/ 0, /*pa_exp*/ 2, /*aging_exp*/ 2, /*aging_bins*/ 10, /*time_window*/ 4, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_EINVAL); + + printf("Negative aging bin.\n"); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/20, /*edges per step(m)*/ 1, /*outseq*/ NULL, + /*outpref?*/ 0, /*pa_exp*/ 2, /*aging_exp*/ 2, /*aging_bins*/ -3, /*time_window*/ 4, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_EINVAL); + + printf("Negative zero appeal.\n"); + IGRAPH_ASSERT(igraph_recent_degree_aging_game(&g, /*nodes*/20, /*edges per step(m)*/ 1, /*outseq*/ NULL, + /*outpref?*/ 0, /*pa_exp*/ 2, /*aging_exp*/ 2, /*aging_bins*/ 10, /*time_window*/ 4, /*zero appeal*/ -1, + /*directed?*/ 0) == IGRAPH_EINVAL); + + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_recent_degree_aging_game.out b/tests/unit/igraph_recent_degree_aging_game.out new file mode 100644 index 0000000..6aeec62 --- /dev/null +++ b/tests/unit/igraph_recent_degree_aging_game.out @@ -0,0 +1,43 @@ +No vertices: +directed: false +vcount: 0 +edges: { +} +No edges: +directed: false +vcount: 5 +edges: { +} +Prefer more edges to make a star of double edges: +directed: false +vcount: 5 +edges: { +0 1 +0 1 +0 2 +0 2 +0 3 +0 3 +0 4 +0 4 +} +Prefer older edges to make a star: +directed: false +vcount: 7 +edges: { +0 1 +0 1 +0 2 +0 3 +0 3 +0 4 +0 5 +0 5 +0 6 +} +Checking if adding one edge per step makes a tree. +Checking if these errors are properly handled: +Negative number of vertices. +Negative number of edges. +Negative aging bin. +Negative zero appeal. diff --git a/tests/unit/igraph_recent_degree_game.c b/tests/unit/igraph_recent_degree_game.c new file mode 100644 index 0000000..8005555 --- /dev/null +++ b/tests/unit/igraph_recent_degree_game.c @@ -0,0 +1,73 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_recent_degree_game(&g, /*number of vertices (n)*/0, /*power*/ 0.0, + /*window*/ 1, /*edges per step(m)*/ 1, /*outseq*/ NULL, /*outpref?*/ 0, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("No edges:\n"); + IGRAPH_ASSERT(igraph_recent_degree_game(&g, /*number of vertices (n)*/ 5, /*power*/ 0.0, + /*window*/ 1, /*edges per step(m)*/ 0, /*outseq*/ NULL, /*outpref?*/ 1, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("A star with double edges.\n"); + IGRAPH_ASSERT(igraph_recent_degree_game(&g, /*number of vertices (n)*/ 10, /*power*/ 30.0, + /*window*/ 100, /*edges per step(m)*/ 2, /*outseq*/ NULL, /*outpref?*/ 0, /*zero appeal*/ 0.001, + /*directed?*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + /*Negative number of vertices*/ + IGRAPH_ASSERT(igraph_recent_degree_game(&g, /*number of vertices (n)*/ -1, /*power*/ 10.0, + /*window*/ 100, /*edges per step(m)*/ 1, /*outseq*/ NULL, /*outpref?*/ 0, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_EINVAL); + + /*Negative number of edges*/ + IGRAPH_ASSERT(igraph_recent_degree_game(&g, /*number of vertices (n)*/ 1, /*power*/ 10.0, + /*window*/ 100, /*edges per step(m)*/ -1, /*outseq*/ NULL, /*outpref?*/ 0, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_EINVAL); + + /*Negative window*/ + IGRAPH_ASSERT(igraph_recent_degree_game(&g, /*number of vertices (n)*/ 1, /*power*/ 10.0, + /*window*/ -100, /*edges per step(m)*/ 1, /*outseq*/ NULL, /*outpref?*/ 0, /*zero appeal*/ 1, + /*directed?*/ 0) == IGRAPH_EINVAL); + + /*Negative zero appeal*/ + IGRAPH_ASSERT(igraph_recent_degree_game(&g, /*number of vertices (n)*/ 1, /*power*/ 10.0, + /*window*/ 100, /*edges per step(m)*/ 1, /*outseq*/ NULL, /*outpref?*/ 0, /*zero appeal*/ -1, + /*directed?*/ 0) == IGRAPH_EINVAL); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_recent_degree_game.out b/tests/unit/igraph_recent_degree_game.out new file mode 100644 index 0000000..f8000d8 --- /dev/null +++ b/tests/unit/igraph_recent_degree_game.out @@ -0,0 +1,33 @@ +No vertices: +directed: false +vcount: 0 +edges: { +} +No edges: +directed: false +vcount: 5 +edges: { +} +A star with double edges. +directed: true +vcount: 10 +edges: { +1 0 +1 0 +2 0 +2 0 +3 0 +3 0 +4 0 +4 0 +5 0 +5 0 +6 0 +6 0 +7 0 +7 0 +8 0 +8 0 +9 0 +9 0 +} diff --git a/tests/unit/igraph_residual_graph.c b/tests/unit/igraph_residual_graph.c new file mode 100644 index 0000000..5986625 --- /dev/null +++ b/tests/unit/igraph_residual_graph.c @@ -0,0 +1,62 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g, residual, expected_residual; + igraph_vector_t capacity, residual_capacity, flow, expected_residual_capacity; + igraph_bool_t iso; + + igraph_vector_init(&residual_capacity, 0); + + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 3, 4, 3, 5, 4, 5, -1); + igraph_small(&expected_residual, 6, IGRAPH_DIRECTED, + 0, 1, 1, 2, 1, 3, 2, 4, 3, 5, 4, 5, -1); + igraph_vector_init_int_end(&capacity, -1, 4, 2, 2, 3, 4, 1, 2, 5, -1); + igraph_vector_init_int_end(&flow, -1, 3, 2, 1, 2, 3, 1, 1, 4, -1); + igraph_vector_init_int_end(&expected_residual_capacity, -1, 1, 1, 1, 1, 1, 1, -1); + + igraph_residual_graph(&g, &capacity, &residual, &residual_capacity, &flow); + + /* tests */ + + IGRAPH_ASSERT(!igraph_isomorphic(&residual, &expected_residual, &iso)); + + IGRAPH_ASSERT(iso); + + IGRAPH_ASSERT(igraph_vector_all_e(&expected_residual_capacity, &residual_capacity)); + + /* cleanup */ + + igraph_vector_destroy(&capacity); + igraph_vector_destroy(&residual_capacity); + igraph_vector_destroy(&flow); + igraph_vector_destroy(&expected_residual_capacity); + + igraph_destroy(&g); + igraph_destroy(&residual); + igraph_destroy(&expected_residual); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_reverse_edges.c b/tests/unit/igraph_reverse_edges.c new file mode 100644 index 0000000..942288e --- /dev/null +++ b/tests/unit/igraph_reverse_edges.c @@ -0,0 +1,46 @@ +/* + 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 +*/ + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + + igraph_small(&graph, 0, IGRAPH_DIRECTED, + 0,1, 1,2, 2,3, 3,1, 1,4, + -1); + + printf("Original graph:\n"); + print_graph(&graph); + + printf("Reverse one edge:\n"); + igraph_reverse_edges(&graph, igraph_ess_1(2)); + print_graph(&graph); + + printf("Reverse all edges:\n"); + igraph_reverse_edges(&graph, igraph_ess_all(IGRAPH_EDGEORDER_ID)); + print_graph(&graph); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_rewire.c b/tests/unit/igraph_rewire.c new file mode 100644 index 0000000..1679c9e --- /dev/null +++ b/tests/unit/igraph_rewire.c @@ -0,0 +1,84 @@ +/* -*- 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, see . +*/ + +#include + +#include "operators/rewire_internal.h" + +#include "test_utilities.h" + +static void check_rewiring(igraph_tree_mode_t tree_mode, igraph_bool_t use_adjlist, igraph_bool_t allow_loops, const char* description) { + + igraph_t g; + igraph_vector_int_t indegree_before, outdegree_before, indegree_after, outdegree_after; + + igraph_kary_tree(&g, 10, 3, tree_mode); + + igraph_vector_int_init(&indegree_before, 0); + igraph_vector_int_init(&outdegree_before, 0); + igraph_degree(&g, &indegree_before, igraph_vss_all(), IGRAPH_IN, 1); + igraph_degree(&g, &outdegree_before, igraph_vss_all(), IGRAPH_OUT, 1); + + igraph_i_rewire(&g, 1000, allow_loops ? IGRAPH_REWIRING_SIMPLE_LOOPS : IGRAPH_REWIRING_SIMPLE, use_adjlist); + + igraph_vector_int_init(&indegree_after, 0); + igraph_vector_int_init(&outdegree_after, 0); + igraph_degree(&g, &indegree_after, igraph_vss_all(), IGRAPH_IN, 1); + igraph_degree(&g, &outdegree_after, igraph_vss_all(), IGRAPH_OUT, 1); + + if ((!igraph_vector_int_all_e(&indegree_before, &indegree_after)) || + (!igraph_vector_int_all_e(&outdegree_before, &outdegree_after))) { + + printf("%s: graph degrees changed. Rewired graph is below.\n", description); + print_graph(&g); + + abort(); + } + + igraph_destroy(&g); + igraph_vector_int_destroy(&indegree_before); + igraph_vector_int_destroy(&outdegree_before); + igraph_vector_int_destroy(&indegree_after); + igraph_vector_int_destroy(&outdegree_after); + +} + +int main(void) { + igraph_rng_seed(igraph_rng_default(), 3925); + + /* Short test for the top-level igraph_rewire() functions (instead of igraph_i_rewire()). */ + { + igraph_t graph; + igraph_ring(&graph, 12, IGRAPH_UNDIRECTED, /* mutual= */ 0, /* circular= */ 1); + igraph_rewire(&graph, 50, IGRAPH_REWIRING_SIMPLE); + igraph_destroy(&graph); + } + + check_rewiring(IGRAPH_TREE_OUT, 0, 0, "Directed, no loops, standard-method"); + check_rewiring(IGRAPH_TREE_OUT, 1, 0, "Directed, no loops, adjlist-method"); + check_rewiring(IGRAPH_TREE_OUT, 0, 1, "Directed, loops, standard-method"); + check_rewiring(IGRAPH_TREE_OUT, 1, 1, "Directed, loops, adjlist-method"); + check_rewiring(IGRAPH_TREE_UNDIRECTED, 0, 0, "Undirected, no loops, standard-method"); + check_rewiring(IGRAPH_TREE_UNDIRECTED, 1, 0, "Undirected, no loops, adjlist-method"); + check_rewiring(IGRAPH_TREE_UNDIRECTED, 0, 1, "Undirected, loops, standard-method"); + check_rewiring(IGRAPH_TREE_UNDIRECTED, 1, 1, "Undirected, loops, adjlist-method"); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_rewire_directed_edges.c b/tests/unit/igraph_rewire_directed_edges.c new file mode 100644 index 0000000..defdb48 --- /dev/null +++ b/tests/unit/igraph_rewire_directed_edges.c @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g, g_copy; + igraph_bool_t same; + igraph_vector_int_t degrees; + igraph_vs_t vertices; + igraph_rng_seed(igraph_rng_default(), 42); + + /*No edges, should just return the same graph*/ + igraph_small(&g, 5, IGRAPH_DIRECTED, -1); + IGRAPH_ASSERT(igraph_rewire_directed_edges(&g, /*probability*/ 0.1, + /*loops*/ false, /*mode*/ IGRAPH_ALL) + == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_ecount(&g) == 0); + IGRAPH_ASSERT(igraph_vcount(&g) == 5); + igraph_destroy(&g); + + /*No rewire*/ + igraph_small(&g, 10, IGRAPH_DIRECTED, 0,1, 0,3, 5,4, 4,8, 9,2, 9,3, 9,7, 7,7, 7,8, -1); + igraph_copy(&g_copy, &g); + IGRAPH_ASSERT(igraph_rewire_directed_edges(&g, /*probability*/ 0.0, + /*loops*/ false, /*mode*/ IGRAPH_ALL) + == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_copy, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(same); + + /*Rewire*/ + IGRAPH_ASSERT(igraph_rewire_directed_edges(&g, /*probability*/ 0.5, + /*loops*/ true, /*mode*/ IGRAPH_ALL) + == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_is_same_graph(&g, &g_copy, &same) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(!same); /*guaranteed for this seed*/ + IGRAPH_ASSERT(igraph_ecount(&g) == 9); + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + igraph_destroy(&g); + igraph_destroy(&g_copy); + + /*Out-star remains out-star if outs are moved*/ + igraph_small(&g, 10, IGRAPH_DIRECTED, 0,1, 0,2, 0,3, 0,4, 0,5, 0,6, 0,7, 0,8, 0,9, -1); + IGRAPH_ASSERT(igraph_rewire_directed_edges(&g, /*probability*/ 1.0, + /*loops*/ false, /*mode*/ IGRAPH_OUT) + == IGRAPH_SUCCESS); + igraph_vector_int_init(°rees, 0); + igraph_vs_1(&vertices, 0); + igraph_degree(&g, °rees, vertices, IGRAPH_ALL, 0); + IGRAPH_ASSERT(VECTOR(degrees)[0] == 9); + igraph_vector_int_destroy(°rees); + igraph_vs_destroy(&vertices); + igraph_destroy(&g); + + /*Check if multiple edges are created when using mode == IGRAPH_ALL*/ + igraph_small(&g, 5, IGRAPH_DIRECTED, 0,1, 0,2, 0,3, 0,4, 1,2, 1,3, 1,4, 2,3, 2,4, 3,4, -1); + IGRAPH_ASSERT(igraph_rewire_directed_edges(&g, /*probability*/ 1.0, + /*loops*/ false, /*mode*/ IGRAPH_ALL) + == IGRAPH_SUCCESS); + print_graph_canon(&g); + + /*A few erroneous calls*/ + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + IGRAPH_ASSERT(igraph_rewire_directed_edges(&g, /*probability*/ -0.1, /*loops*/ false, /*mode*/ IGRAPH_ALL) == IGRAPH_EINVAL); + IGRAPH_ASSERT(igraph_rewire_directed_edges(&g, /*probability*/ 1.1, /*loops*/ false, /*mode*/ IGRAPH_ALL) == IGRAPH_EINVAL); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_rewire_directed_edges.out b/tests/unit/igraph_rewire_directed_edges.out new file mode 100644 index 0000000..4802f6b --- /dev/null +++ b/tests/unit/igraph_rewire_directed_edges.out @@ -0,0 +1,14 @@ +directed: true +vcount: 5 +edges: { +0 4 +0 4 +1 0 +1 2 +1 2 +1 2 +2 0 +2 1 +3 1 +3 1 +} diff --git a/tests/unit/igraph_rng_get_integer.c b/tests/unit/igraph_rng_get_integer.c new file mode 100644 index 0000000..a555bbb --- /dev/null +++ b/tests/unit/igraph_rng_get_integer.c @@ -0,0 +1,210 @@ +/* -*- 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +void simple_tests(void) { + int i; + + /* Seed the RNG, generate 10 random integers */ + igraph_rng_seed(igraph_rng_default(), 42); + for (i = 0; i < 10; i++) { + printf("%" IGRAPH_PRId "\n", igraph_rng_get_integer(igraph_rng_default(), 10, 100)); + } + + printf("========\n"); + + /* Seed the RNG again with the same seed, verify that we get the same + * numbers */ + igraph_rng_seed(igraph_rng_default(), 42); + for (i = 0; i < 10; i++) { + printf("%" IGRAPH_PRId "\n", igraph_rng_get_integer(igraph_rng_default(), 10, 100)); + } + + printf("========\n"); + + /* Seed the RNG again with a different seed, verify that we get different + * numbers */ + igraph_rng_seed(igraph_rng_default(), 84); + for (i = 0; i < 10; i++) { + printf("%" IGRAPH_PRId "\n", igraph_rng_get_integer(igraph_rng_default(), 10, 100)); + } + + printf("========\n"); +} + +void generate_random_vector( + igraph_rng_t* rng, igraph_vector_int_t* numbers, igraph_integer_t lo, igraph_integer_t hi +) { + igraph_integer_t i, n = igraph_vector_int_size(numbers); + + for (i = 0; i < n; i++) { + VECTOR(*numbers)[i] = igraph_rng_get_integer(rng, lo, hi); + } + IGRAPH_ASSERT(igraph_vector_int_min(numbers) >= lo); + IGRAPH_ASSERT(igraph_vector_int_max(numbers) <= hi); +} + +/* Checks whether a given vector of numbers contains all numbers between `lo' + * and `hi' */ +void check_occurrences(const igraph_vector_int_t* numbers, igraph_integer_t lo, igraph_integer_t hi) { + igraph_integer_t i; + + for (i = lo; i <= hi; i++) { + IGRAPH_ASSERT(igraph_vector_int_contains(numbers, i)); + } +} + +/* Checks whether each X consecutive bits of the given numbers contain all + * possible combinations of (0, 1) as a quick proxy for randomness, for X = 1..4 */ +void check_consecutive_bits(const igraph_vector_int_t* numbers, uint8_t num_bits) { + igraph_uint_t i, j, k, masked, n, mask, still_needed; + igraph_vector_bool_t seen; + + n = igraph_vector_int_size(numbers); + + for (j = 0; j < 4; j++) { + mask = ((igraph_integer_t) 1 << (j + 1)) - 1; + igraph_vector_bool_init(&seen, mask + 1); + for (i = 0; i < num_bits - j; i++, mask <<= 1) { + still_needed = (1 << (j + 1)); + igraph_vector_bool_fill(&seen, 0); + for (k = 0; k < n; k++) { + masked = (VECTOR(*numbers)[k] & mask) >> i; + if (!VECTOR(seen)[masked]) { + VECTOR(seen)[masked] = 1; + still_needed--; + if (!still_needed) { + break; + } + } + } + + if (still_needed) { + if (j > 0) { + printf( + "Expected %" IGRAPH_PRIu " consecutive bit(s) of random integers with " + "%d bits to contain all bit combinations.\n", + (j + 1), num_bits + ); + printf( + "Missing %" IGRAPH_PRIu " combination(s) in bits %" IGRAPH_PRIu "-%" IGRAPH_PRIu "\n", + still_needed, (i + j), i + ); + } else { + printf( + "Expected every bit of random integers with " + "%d bits to contain at least one 0 and at least one 1.\n", + num_bits + ); + printf( + "This does not hold for bit %" IGRAPH_PRIu " in this vector:\n", + i + ); + } + printf("\n"); + print_vector_int(numbers); + IGRAPH_ASSERT(!still_needed); + } + } + igraph_vector_bool_destroy(&seen); + } +} + +void stress_tests(void) { + igraph_rng_t rng; + const igraph_rng_type_t* rng_types[] = { + &igraph_rngtype_mt19937, + &igraph_rngtype_glibc2, + &igraph_rngtype_pcg32, + &igraph_rngtype_pcg64, + }; + igraph_integer_t i; + igraph_vector_int_t numbers; + const igraph_integer_t N = 1000; + + igraph_vector_int_init(&numbers, N); + + for (i = 0; i < sizeof(rng_types) / sizeof(rng_types[0]); i++) { + igraph_error_handler_t *oldhandler = igraph_set_error_handler(&igraph_error_handler_printignore); + igraph_error_t err = igraph_rng_init(&rng, rng_types[i]); + switch (err) { + case IGRAPH_SUCCESS: + break; + case IGRAPH_UNIMPLEMENTED: + continue; + default: + IGRAPH_FATAL("Error while initializing RNG."); + } + igraph_set_error_handler(oldhandler); + + igraph_rng_seed(&rng, 42); + + /* We are going to test multiple ranges. In each range, we generate + * 1000 random numbers and test whether all the values are in the + * specified range. For the small ranges, we also test whether each + * value occurred at least once as the chances of this not happening is + * astronomically small */ + + /* Test integer generation in a small 0-based range */ + generate_random_vector(&rng, &numbers, 0, 5); + check_occurrences(&numbers, 0, 5); + + /* Test integer generation in a small non-0-based range */ + generate_random_vector(&rng, &numbers, 8, 13); + check_occurrences(&numbers, 8, 13); + + /* Test integer generation in a larger non-0-based range */ + generate_random_vector(&rng, &numbers, -2048, 2047); + check_consecutive_bits(&numbers, 12); + + /* Test integer generation in [0; IGRAPH_INTEGER_MAX] */ + generate_random_vector(&rng, &numbers, 0, IGRAPH_INTEGER_MAX); + check_consecutive_bits(&numbers, sizeof(igraph_integer_t) * 8 - 1); + + /* Test integer generation in [-5; IGRAPH_INTEGER_MAX-5] */ + generate_random_vector(&rng, &numbers, -5, IGRAPH_INTEGER_MAX-5); + + /* Test integer generation in [-5; IGRAPH_INTEGER_MAX] */ + generate_random_vector(&rng, &numbers, -5, IGRAPH_INTEGER_MAX); + + /* Test integer generation in [IGRAPH_INTEGER_MIN; -5] */ + generate_random_vector(&rng, &numbers, IGRAPH_INTEGER_MIN, -5); + + /* Test integer generation in [IGRAPH_INTEGER_MIN; IGRAPH_INTEGER_MAX] */ + generate_random_vector(&rng, &numbers, IGRAPH_INTEGER_MIN, IGRAPH_INTEGER_MAX); + check_consecutive_bits(&numbers, sizeof(igraph_integer_t) * 8); + + igraph_rng_destroy(&rng); + } + + igraph_vector_int_destroy(&numbers); +} + +int main(void) { + simple_tests(); + stress_tests(); + + return 0; +} diff --git a/tests/unit/igraph_rng_get_integer.out b/tests/unit/igraph_rng_get_integer.out new file mode 100644 index 0000000..1568516 --- /dev/null +++ b/tests/unit/igraph_rng_get_integer.out @@ -0,0 +1,33 @@ +22 +59 +86 +99 +99 +77 +93 +12 +20 +62 +======== +22 +59 +86 +99 +99 +77 +93 +12 +20 +62 +======== +33 +48 +87 +63 +80 +14 +15 +95 +79 +43 +======== diff --git a/tests/unit/igraph_roulette_wheel_imitation.c b/tests/unit/igraph_roulette_wheel_imitation.c new file mode 100644 index 0000000..362b17f --- /dev/null +++ b/tests/unit/igraph_roulette_wheel_imitation.c @@ -0,0 +1,66 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g, gzero, h; + igraph_vector_t quant, quantzero; + igraph_vector_int_t strat, stratzero; + igraph_integer_t nvert; + + /* nonempty graph */ + igraph_small(&g, /*nvert=*/ 0, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + igraph_empty(&h, 0, 0); /* empty graph */ + igraph_vector_init(&quant, 1); /* quantities vector */ + igraph_vector_int_init(&strat, 2); /* strategies vector */ + igraph_small(&gzero, /*nvert=*/ 0, IGRAPH_UNDIRECTED, + 0, 3, 0, 4, 1, 2, 1, 4, 1, 5, 2, 3, 2, 4, 3, 4, -1); + nvert = igraph_vcount(&gzero); + igraph_vector_int_init_int(&stratzero, nvert, 1, 0, 1, 2, 0, 3); + igraph_vector_init(&quantzero, nvert); /* vector of zeros */ + + /* test parameters */ + /*graph--vert--islocal--quantities--strategies--mode--retval*/ + /* null pointer for graph */ + CHECK_ERROR(igraph_roulette_wheel_imitation(NULL, 0, 1, NULL, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + /* null pointer for quantities vector */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, 1, NULL, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + /* null pointer for strategies vector */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, 1, &quant, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + /* empty graph */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&h, 0, 1, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL); + /* length of quantities vector different from number of vertices */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, 1, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL); + /* length of strategies vector different from number of vertices */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, 1, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL); + /* quantities vector contains all zeros */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&gzero, 4, 1, &quantzero, &stratzero, IGRAPH_ALL), IGRAPH_EINVAL); + + igraph_destroy(&g); + igraph_destroy(&gzero); + igraph_destroy(&h); + igraph_vector_destroy(&quant); + igraph_vector_destroy(&quantzero); + igraph_vector_int_destroy(&strat); + igraph_vector_int_destroy(&stratzero); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_running_mean.c b/tests/unit/igraph_running_mean.c new file mode 100644 index 0000000..cfff960 --- /dev/null +++ b/tests/unit/igraph_running_mean.c @@ -0,0 +1,60 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_vector_t data, result; + + igraph_set_error_handler(igraph_error_handler_ignore); + igraph_vector_init_int(&result, 0); + + printf("No values, binwidth 0 should fail.\n"); + igraph_vector_init_int(&data, 0); + IGRAPH_ASSERT(igraph_running_mean(&data, &result, /*binwidth*/ 0) == IGRAPH_EINVAL); + igraph_vector_destroy(&data); + + printf("No values, binwidth 1 should fail.\n"); + igraph_vector_init_int(&data, 0); + IGRAPH_ASSERT(igraph_running_mean(&data, &result, /*binwidth*/ 1) == IGRAPH_EINVAL); + igraph_vector_destroy(&data); + + printf("One value, binwidth 1:\n"); + igraph_vector_init_int(&data, 1, 1); + IGRAPH_ASSERT(igraph_running_mean(&data, &result, /*binwidth*/ 1) == IGRAPH_SUCCESS); + print_vector(&result); + igraph_vector_destroy(&data); + + printf("1, 2, 3, 4, 5, binwidth 1:\n"); + igraph_vector_init_int(&data, 5, 1, 2, 3, 4, 5); + IGRAPH_ASSERT(igraph_running_mean(&data, &result, /*binwidth*/ 1) == IGRAPH_SUCCESS); + print_vector(&result); + igraph_vector_destroy(&data); + + printf("1, 2, 3, 4, 5, binwidth 2:\n"); + igraph_vector_init_int(&data, 5, 1, 2, 3, 4, 5); + IGRAPH_ASSERT(igraph_running_mean(&data, &result, /*binwidth*/ 2) == IGRAPH_SUCCESS); + print_vector(&result); + igraph_vector_destroy(&data); + + igraph_vector_destroy(&result); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_running_mean.out b/tests/unit/igraph_running_mean.out new file mode 100644 index 0000000..8ce5528 --- /dev/null +++ b/tests/unit/igraph_running_mean.out @@ -0,0 +1,8 @@ +No values, binwidth 0 should fail. +No values, binwidth 1 should fail. +One value, binwidth 1: +( 1 ) +1, 2, 3, 4, 5, binwidth 1: +( 1 2 3 4 5 ) +1, 2, 3, 4, 5, binwidth 2: +( 1.5 2.5 3.5 4.5 ) diff --git a/tests/unit/igraph_sample_dirichlet.c b/tests/unit/igraph_sample_dirichlet.c new file mode 100644 index 0000000..6257105 --- /dev/null +++ b/tests/unit/igraph_sample_dirichlet.c @@ -0,0 +1,83 @@ +/* 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 . +*/ + +#include +#include "test_utilities.h" + +void check_result(igraph_matrix_t *res, igraph_integer_t n) +{ + igraph_vector_t colsum; + igraph_vector_init(&colsum, 0); + + IGRAPH_ASSERT(igraph_matrix_min(res) >= 0); + IGRAPH_ASSERT(igraph_matrix_max(res) <= 1.0); + igraph_matrix_colsum(res, &colsum); + IGRAPH_ASSERT(igraph_vector_size(&colsum) == n); + for (igraph_integer_t i = 0; i < igraph_vector_size(&colsum); i++) { + IGRAPH_ASSERT(igraph_almost_equals(VECTOR(colsum)[i], 1, 0.0000001)); + } + igraph_vector_destroy(&colsum); +} + + +int main(void) { + igraph_vector_t alpha; + igraph_matrix_t res; + + igraph_rng_seed(igraph_rng_default(), 42); + igraph_matrix_init(&res, 0, 0); + + printf("Zero vectors to sample should return empty matrix:\n"); + igraph_vector_init_int(&alpha, 2, 1, 1); + igraph_sample_dirichlet(0, &alpha, &res); + igraph_matrix_print(&res); + igraph_vector_destroy(&alpha); + + printf("Check if result vectors add up to one.\n"); + igraph_vector_init_int(&alpha, 5, 1, 2, 3, 4, 5); + igraph_sample_dirichlet(100, &alpha, &res); + check_result(&res, 100); + igraph_vector_destroy(&alpha); + + printf("Distribution localized at 0.5, 0.5:\n"); + igraph_vector_init_real(&alpha, 2, 1e30, 1e30); + igraph_sample_dirichlet(2, &alpha, &res); + igraph_matrix_print(&res); + igraph_vector_destroy(&alpha); + + VERIFY_FINALLY_STACK(); + + printf("Check if too short parameter vector is handled correctly.\n"); + igraph_vector_init(&alpha, 0); + CHECK_ERROR(igraph_sample_dirichlet(0, &alpha, &res), IGRAPH_EINVAL); + igraph_vector_destroy(&alpha); + + printf("Check if negative number of samples is handled correctly.\n"); + igraph_vector_init_int(&alpha, 2, 1, 1); + CHECK_ERROR(igraph_sample_dirichlet(-1, &alpha, &res), IGRAPH_EINVAL); + igraph_vector_destroy(&alpha); + + printf("Check if negative alpha is handled correctly.\n"); + igraph_vector_init_int(&alpha, 2, -1, 1); + CHECK_ERROR(igraph_sample_dirichlet(0, &alpha, &res), IGRAPH_EINVAL); + igraph_vector_destroy(&alpha); + + igraph_matrix_destroy(&res); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sample_dirichlet.out b/tests/unit/igraph_sample_dirichlet.out new file mode 100644 index 0000000..7a22df1 --- /dev/null +++ b/tests/unit/igraph_sample_dirichlet.out @@ -0,0 +1,10 @@ +Zero vectors to sample should return empty matrix: + + +Check if result vectors add up to one. +Distribution localized at 0.5, 0.5: +0.5 0.5 +0.5 0.5 +Check if too short parameter vector is handled correctly. +Check if negative number of samples is handled correctly. +Check if negative alpha is handled correctly. diff --git a/tests/unit/igraph_sample_sphere.c b/tests/unit/igraph_sample_sphere.c new file mode 100644 index 0000000..97fa507 --- /dev/null +++ b/tests/unit/igraph_sample_sphere.c @@ -0,0 +1,66 @@ +/* + 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 . + */ + +#include +#include "test_utilities.h" + +void check(igraph_bool_t volume, igraph_integer_t dim, igraph_integer_t n, igraph_real_t radius, igraph_bool_t positive) { + igraph_matrix_t samples; + + igraph_matrix_init(&samples, 0, 0); + if (volume) { + igraph_sample_sphere_volume(dim, n, radius, positive, &samples); + } else { + igraph_sample_sphere_surface(dim, n, radius, positive, &samples); + } + IGRAPH_ASSERT(igraph_matrix_ncol(&samples) == n); + IGRAPH_ASSERT(igraph_matrix_nrow(&samples) == dim); + for (igraph_integer_t col = 0; col < n; col++) { + igraph_real_t sum = 0; + for (igraph_integer_t row = 0; row < dim; row++) { + if (positive) { + IGRAPH_ASSERT(MATRIX(samples, row, col) >= 0); + } + sum += MATRIX(samples, row, col) * MATRIX(samples, row, col); + } + if (volume) { + IGRAPH_ASSERT(sum <= radius * radius); + } else { + IGRAPH_ASSERT(igraph_almost_equals(sum, radius * radius, 0.00001)); + } + } + igraph_matrix_destroy(&samples); +} + +int main(void) { + + igraph_rng_seed(igraph_rng_default(), 42); + + //No samples + check(0, 2, 0, 1, 0); + check(1, 2, 0, 1, 0); + //Five samples, four-dimensions, radius 2 + check(0, 4, 5, 2, 0); + check(1, 4, 5, 2, 0); + //Same, positive orthant + check(0, 4, 5, 2, 1); + check(1, 4, 5, 2, 1); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sbm_game.c b/tests/unit/igraph_sbm_game.c new file mode 100644 index 0000000..599eef0 --- /dev/null +++ b/tests/unit/igraph_sbm_game.c @@ -0,0 +1,119 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_integer_t n, igraph_matrix_t *pref_matrix, igraph_vector_int_t *block_sizes, igraph_bool_t directed, igraph_bool_t loops) { + igraph_t result; + IGRAPH_ASSERT(igraph_sbm_game(&result, n, pref_matrix, block_sizes, directed, loops) == IGRAPH_SUCCESS); + print_graph_canon(&result); + printf("\n"); + igraph_destroy(&result); +} + + +int main(void) { + igraph_t result; + igraph_matrix_t pref_matrix_0, pref_matrix_1, pref_matrix_3, pref_matrix_3u, pref_matrix_nonsq, pref_matrix_oor, pref_matrix_nsym; + igraph_vector_int_t block_sizes_0, block_sizes_1, block_sizes_3, block_sizes_neg; + + igraph_matrix_init(&pref_matrix_0, 0, 0); + + igraph_matrix_init(&pref_matrix_1, 1, 1); + MATRIX(pref_matrix_1, 0, 0) = 1; + + igraph_matrix_init(&pref_matrix_3, 3, 3); + igraph_matrix_null(&pref_matrix_3); + MATRIX(pref_matrix_3, 0, 1) = 1; + MATRIX(pref_matrix_3, 2, 2) = 1; + + igraph_matrix_init(&pref_matrix_3u, 3, 3); + igraph_matrix_null(&pref_matrix_3u); + MATRIX(pref_matrix_3u, 0, 1) = 1; + MATRIX(pref_matrix_3u, 1, 0) = 1; + MATRIX(pref_matrix_3u, 2, 2) = 1; + + igraph_matrix_init(&pref_matrix_nonsq, 3, 2); + + igraph_matrix_init(&pref_matrix_oor, 3, 3); + igraph_matrix_null(&pref_matrix_oor); + MATRIX(pref_matrix_oor, 0, 1) = 10; + + igraph_matrix_init(&pref_matrix_nsym, 3, 3); + igraph_matrix_null(&pref_matrix_nsym); + MATRIX(pref_matrix_nsym, 0, 1) = 1; + + igraph_vector_int_init_int(&block_sizes_0, 0); + igraph_vector_int_init_int(&block_sizes_1, 1, 1); + igraph_vector_int_init_int(&block_sizes_3, 3, 2, 2, 2); + igraph_vector_int_init_int(&block_sizes_neg, 3, 2, 2, -2); + + printf("No vertices.\n"); + call_and_print(0, &pref_matrix_0, &block_sizes_0, 0, 0); + + printf("One vertex, directed, with loops.\n"); + call_and_print(1, &pref_matrix_1, &block_sizes_1, 1, 1); + + printf("Six vertices, directed, only edges from block 0 to 1 and 2 to 2.\n"); + call_and_print(6, &pref_matrix_3, &block_sizes_3, 1, 1); + + printf("Six vertices, directed, only edges from block 0 to 1 and 2 to 2, no loops.\n"); + call_and_print(6, &pref_matrix_3, &block_sizes_3, 1, 0); + + printf("Six vertices, undirected, only edges between block 0 and 1, and inside block 2.\n"); + call_and_print(6, &pref_matrix_3u, &block_sizes_3, 0, 1); + + printf("Six vertices, undirected, only edges between block 0 and 1, and inside block 2, no loops.\n"); + call_and_print(6, &pref_matrix_3u, &block_sizes_3, 0, 0); + + VERIFY_FINALLY_STACK(); + + printf("Check for nonsquare matrix error handling.\n"); + CHECK_ERROR(igraph_sbm_game(&result, 6, &pref_matrix_nonsq, &block_sizes_3, 0, 0), IGRAPH_NONSQUARE); + + printf("Check for preference matrix probability out of range error handling.\n"); + CHECK_ERROR(igraph_sbm_game(&result, 6, &pref_matrix_oor, &block_sizes_3, 0, 0), IGRAPH_EINVAL); + + printf("Check for nonsymmetric preference matrix for undirected graph error handling.\n"); + CHECK_ERROR(igraph_sbm_game(&result, 6, &pref_matrix_nsym, &block_sizes_3, 0, 0), IGRAPH_EINVAL); + + printf("Check for incorrect block size vector error handling.\n"); + CHECK_ERROR(igraph_sbm_game(&result, 6, &pref_matrix_3, &block_sizes_1, 1, 0), IGRAPH_EINVAL); + + printf("Check for negative block size error handling.\n"); + CHECK_ERROR(igraph_sbm_game(&result, 6, &pref_matrix_3, &block_sizes_neg, 1, 0), IGRAPH_EINVAL); + + printf("Check for sum of block sizes not equal to number of vertices error handling.\n"); + CHECK_ERROR(igraph_sbm_game(&result, 3, &pref_matrix_3, &block_sizes_3, 1, 0), IGRAPH_EINVAL); + + igraph_matrix_destroy(&pref_matrix_0); + igraph_matrix_destroy(&pref_matrix_1); + igraph_matrix_destroy(&pref_matrix_3); + igraph_matrix_destroy(&pref_matrix_3u); + igraph_matrix_destroy(&pref_matrix_oor); + igraph_matrix_destroy(&pref_matrix_nsym); + igraph_matrix_destroy(&pref_matrix_nonsq); + igraph_vector_int_destroy(&block_sizes_0); + igraph_vector_int_destroy(&block_sizes_1); + igraph_vector_int_destroy(&block_sizes_3); + igraph_vector_int_destroy(&block_sizes_neg); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sbm_game.out b/tests/unit/igraph_sbm_game.out new file mode 100644 index 0000000..5cb74db --- /dev/null +++ b/tests/unit/igraph_sbm_game.out @@ -0,0 +1,69 @@ +No vertices. +directed: false +vcount: 0 +edges: { +} + +One vertex, directed, with loops. +directed: true +vcount: 1 +edges: { +0 0 +} + +Six vertices, directed, only edges from block 0 to 1 and 2 to 2. +directed: true +vcount: 6 +edges: { +0 2 +0 3 +1 2 +1 3 +4 4 +4 5 +5 4 +5 5 +} + +Six vertices, directed, only edges from block 0 to 1 and 2 to 2, no loops. +directed: true +vcount: 6 +edges: { +0 2 +0 3 +1 2 +1 3 +4 5 +5 4 +} + +Six vertices, undirected, only edges between block 0 and 1, and inside block 2. +directed: false +vcount: 6 +edges: { +0 2 +0 3 +1 2 +1 3 +4 4 +4 5 +5 5 +} + +Six vertices, undirected, only edges between block 0 and 1, and inside block 2, no loops. +directed: false +vcount: 6 +edges: { +0 2 +0 3 +1 2 +1 3 +4 5 +} + +Check for nonsquare matrix error handling. +Check for preference matrix probability out of range error handling. +Check for nonsymmetric preference matrix for undirected graph error handling. +Check for incorrect block size vector error handling. +Check for negative block size error handling. +Check for sum of block sizes not equal to number of vertices error handling. diff --git a/tests/unit/igraph_set_progress_handler.c b/tests/unit/igraph_set_progress_handler.c new file mode 100644 index 0000000..19c99af --- /dev/null +++ b/tests/unit/igraph_set_progress_handler.c @@ -0,0 +1,46 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +igraph_error_t handler(const char* message, igraph_real_t percent, void*data) { + printf("handler, %s, %f, %d\n", message, percent, *(int*)data); + return IGRAPH_SUCCESS; +} + +int main(void) { + igraph_set_progress_handler(handler); + int data = 10; + + printf("progress with set progress handler:\n"); + IGRAPH_PROGRESS("message", 100.0, &data); + + igraph_progress_handler_t *previous = igraph_set_progress_handler(NULL); + + printf("\nprogress with no handler:\n"); + IGRAPH_PROGRESS("message", 100.0, &data); + + igraph_set_progress_handler(previous); + + printf("\nprogress with previous handler:\n"); + IGRAPH_PROGRESS("message", 100.0, &data); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_set_progress_handler.out b/tests/unit/igraph_set_progress_handler.out new file mode 100644 index 0000000..fa289a4 --- /dev/null +++ b/tests/unit/igraph_set_progress_handler.out @@ -0,0 +1,7 @@ +progress with set progress handler: +handler, message, 100.000000, 10 + +progress with no handler: + +progress with previous handler: +handler, message, 100.000000, 10 diff --git a/tests/unit/igraph_similarity.c b/tests/unit/igraph_similarity.c new file mode 100644 index 0000000..090d284 --- /dev/null +++ b/tests/unit/igraph_similarity.c @@ -0,0 +1,205 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int check_jaccard_all(const igraph_t* g, igraph_matrix_t* m, + igraph_neimode_t mode, igraph_bool_t loops) { + igraph_vector_int_t pairs; + igraph_vector_t res; + igraph_integer_t i, j, k, n; + igraph_eit_t eit; + + igraph_vector_init(&res, 0); + + /* First, query the similarities for all the vertices to a matrix */ + igraph_similarity_jaccard(g, m, igraph_vss_all(), mode, loops); + + /* Second, query the similarities for all pairs using a pair vector */ + n = igraph_vcount(g); + igraph_vector_int_init(&pairs, 0); + for (i = 0; i < n; i++) { + for (j = n - 1; j >= 0; j--) { + igraph_vector_int_push_back(&pairs, i); + igraph_vector_int_push_back(&pairs, j); + } + } + igraph_similarity_jaccard_pairs(g, &res, &pairs, mode, loops); + for (i = 0, k = 0; i < n; i++) { + for (j = n - 1; j >= 0; j--, k++) { + if (fabs(VECTOR(res)[k] - MATRIX(*m, i, j)) > 1e-6) { + fprintf(stderr, "Jaccard similarity calculation for vertex pair %" IGRAPH_PRId "-%" IGRAPH_PRId " " + "does not match the value in the full matrix (%.6f vs %.6f)\n", + i, j, VECTOR(res)[k], MATRIX(*m, i, j)); + return 1; + } + } + } + igraph_vector_int_destroy(&pairs); + + /* Third, query the similarities for all edges */ + igraph_similarity_jaccard_es(g, &res, igraph_ess_all(IGRAPH_EDGEORDER_FROM), mode, loops); + igraph_eit_create(g, igraph_ess_all(IGRAPH_EDGEORDER_FROM), &eit); + k = 0; + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t eid = IGRAPH_EIT_GET(eit); + i = IGRAPH_FROM(g, eid); + j = IGRAPH_TO(g, eid); + if (fabs(VECTOR(res)[k] - MATRIX(*m, i, j)) > 1e-6) { + fprintf(stderr, "Jaccard similarity calculation for edge %" IGRAPH_PRId "-%" IGRAPH_PRId " (ID=%" IGRAPH_PRId ") " + "does not match the value in the full matrix (%.6f vs %.6f)\n", + i, j, eid, VECTOR(res)[k], MATRIX(*m, i, j)); + return 1; + } + IGRAPH_EIT_NEXT(eit); + k++; + } + + igraph_eit_destroy(&eit); + + igraph_vector_destroy(&res); + + return 0; +} + +int check_dice_all(const igraph_t* g, igraph_matrix_t* m, + igraph_neimode_t mode, igraph_bool_t loops) { + igraph_vector_int_t pairs; + igraph_vector_t res; + igraph_integer_t i, j, k, n; + igraph_eit_t eit; + + igraph_vector_init(&res, 0); + + /* First, query the similarities for all the vertices to a matrix */ + igraph_similarity_dice(g, m, igraph_vss_all(), mode, loops); + + /* Second, query the similarities for all pairs using a pair vector */ + n = igraph_vcount(g); + igraph_vector_int_init(&pairs, 0); + for (i = 0; i < n; i++) { + for (j = n - 1; j >= 0; j--) { + igraph_vector_int_push_back(&pairs, i); + igraph_vector_int_push_back(&pairs, j); + } + } + igraph_similarity_dice_pairs(g, &res, &pairs, mode, loops); + for (i = 0, k = 0; i < n; i++) { + for (j = n - 1; j >= 0; j--, k++) { + if (fabs(VECTOR(res)[k] - MATRIX(*m, i, j)) > 1e-6) { + fprintf(stderr, "Dice similarity calculation for vertex pair %" IGRAPH_PRId "-%" IGRAPH_PRId " " + "does not match the value in the full matrix (%.6f vs %.6f)\n", + i, j, VECTOR(res)[k], MATRIX(*m, i, j)); + return 1; + } + } + } + igraph_vector_int_destroy(&pairs); + + /* Third, query the similarities for all edges */ + igraph_similarity_dice_es(g, &res, igraph_ess_all(IGRAPH_EDGEORDER_FROM), mode, loops); + igraph_eit_create(g, igraph_ess_all(IGRAPH_EDGEORDER_FROM), &eit); + k = 0; + while (!IGRAPH_EIT_END(eit)) { + igraph_integer_t eid = IGRAPH_EIT_GET(eit); + i = IGRAPH_FROM(g, eid); + j = IGRAPH_TO(g, eid); + if (fabs(VECTOR(res)[k] - MATRIX(*m, i, j)) > 1e-6) { + fprintf(stderr, "Dice similarity calculation for edge %" IGRAPH_PRId "-%" IGRAPH_PRId " (ID=%" IGRAPH_PRId ") " + "does not match the value in the full matrix (%.6f vs %.6f)\n", + i, j, eid, VECTOR(res)[k], MATRIX(*m, i, j)); + return 1; + } + IGRAPH_EIT_NEXT(eit); + k++; + } + + igraph_eit_destroy(&eit); + + igraph_vector_destroy(&res); + + return 0; +} + +int main(void) { + + igraph_t g; + igraph_matrix_t m; + int ret; + + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0, 1, 2, 1, 2, 0, 3, 0, + -1); + + igraph_matrix_init(&m, 0, 0); + + ret = check_jaccard_all(&g, &m, IGRAPH_ALL, 1); + print_matrix(&m); + if (ret) { + return 1; + } + + igraph_similarity_jaccard(&g, &m, igraph_vss_range(1, 3), IGRAPH_ALL, 0); + print_matrix(&m); + + ret = check_jaccard_all(&g, &m, IGRAPH_OUT, 1); + print_matrix(&m); + if (ret) { + return 3; + } + + ret = check_jaccard_all(&g, &m, IGRAPH_IN, 0); + print_matrix(&m); + if (ret) { + return 4; + } + + ret = check_dice_all(&g, &m, IGRAPH_ALL, 1); + print_matrix(&m); + if (ret) { + return 5; + } + + ret = check_dice_all(&g, &m, IGRAPH_OUT, 1); + print_matrix(&m); + if (ret) { + return 6; + } + + ret = check_dice_all(&g, &m, IGRAPH_IN, 0); + print_matrix(&m); + if (ret) { + return 7; + } + + igraph_similarity_inverse_log_weighted(&g, &m, igraph_vss_all(), IGRAPH_ALL); + print_matrix(&m); + + igraph_similarity_inverse_log_weighted(&g, &m, igraph_vss_all(), IGRAPH_OUT); + print_matrix(&m); + + igraph_similarity_inverse_log_weighted(&g, &m, igraph_vss_all(), IGRAPH_IN); + print_matrix(&m); + + igraph_matrix_destroy(&m); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_similarity.out b/tests/unit/igraph_similarity.out new file mode 100644 index 0000000..908da62 --- /dev/null +++ b/tests/unit/igraph_similarity.out @@ -0,0 +1,38 @@ +[ 1 0.75 0.75 0.5 + 0.75 1 1 0.25 + 0.75 1 1 0.25 + 0.5 0.25 0.25 1 ] +[ 1 0.333333 + 0.333333 1 ] +[ 1 0.5 0.666667 0.333333 + 0.5 1 0.333333 0 + 0.666667 0.333333 1 0.25 + 0.333333 0 0.25 1 ] +[ 1 0.333333 0 0 + 0.333333 1 0 0 + 0 0 1 0 + 0 0 0 1 ] +[ 1 0.857143 0.857143 0.666667 + 0.857143 1 1 0.4 + 0.857143 1 1 0.4 + 0.666667 0.4 0.4 1 ] +[ 1 0.666667 0.8 0.5 + 0.666667 1 0.5 0 + 0.8 0.5 1 0.4 + 0.5 0 0.4 1 ] +[ 1 0.5 0 0 + 0.5 1 0 0 + 0 0 1 0 + 0 0 0 1 ] +[ 0 1.4427 1.4427 0 + 1.4427 0 0.910239 0.910239 + 1.4427 0.910239 0 0.910239 + 0 0.910239 0.910239 0 ] +[ 0 0 1.4427 0 + 0 0 0 0 + 1.4427 0 0 1.4427 + 0 0 1.4427 0 ] +[ 0 1.4427 0 0 + 1.4427 0 0 0 + 0 0 0 0 + 0 0 0 0 ] diff --git a/tests/unit/igraph_simple_interconnected_islands_game.c b/tests/unit/igraph_simple_interconnected_islands_game.c new file mode 100644 index 0000000..c8f8491 --- /dev/null +++ b/tests/unit/igraph_simple_interconnected_islands_game.c @@ -0,0 +1,93 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No islands:\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/0, /*size of islands*/ 1, + /*islands_pin*/ 1, /*number of edges between two islands*/ 1) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("One island, no edges:\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/1, /*size of islands*/ 4, + /*islands_pin*/ 0, /*number of edges between two islands*/ 2) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("One island, full graph:\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/1, /*size of islands*/ 4, + /*islands_pin*/ 1, /*number of edges between two islands*/ 2) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Two islands, full graphs, no connections between islands:\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/2, /*size of islands*/ 4, + /*islands_pin*/ 1, /*number of edges between two islands*/ 0) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Three islands, full graphs, 16 connections between islands.\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/3, /*size of islands*/ 4, + /*islands_pin*/ 1, /*number of edges between two islands*/ 16) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_ecount(&g) == 18 + 48); + igraph_destroy(&g); + + printf("Three islands, random graphs, 3 connections between islands.\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/3, /*size of islands*/ 4, + /*islands_pin*/ 0.5, /*number of edges between two islands*/ 3) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Negative number of islands.\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/-2, /*size of islands*/ 4, + /*islands_pin*/ 1, /*number of edges between two islands*/ 0) == IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("Negative island size.\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/2, /*size of islands*/ -4, + /*islands_pin*/ 1, /*number of edges between two islands*/ 0) == IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("Probability out of range.\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/2, /*size of islands*/ 4, + /*islands_pin*/ 2, /*number of edges between two islands*/ 0) == IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("Negative number of edges between islands.\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/2, /*size of islands*/ 4, + /*islands_pin*/ 1, /*number of edges between two islands*/ -3) == IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("Too many edges between islands.\n"); + IGRAPH_ASSERT(igraph_simple_interconnected_islands_game(&g, /*number of islands*/3, /*size of islands*/ 4, + /*islands_pin*/ 1, /*number of edges between two islands*/ 20) == IGRAPH_EINVAL); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_simple_interconnected_islands_game.out b/tests/unit/igraph_simple_interconnected_islands_game.out new file mode 100644 index 0000000..53a61b3 --- /dev/null +++ b/tests/unit/igraph_simple_interconnected_islands_game.out @@ -0,0 +1,63 @@ +No islands: +directed: false +vcount: 0 +edges: { +} +One island, no edges: +directed: false +vcount: 4 +edges: { +} +One island, full graph: +directed: false +vcount: 4 +edges: { +0 1 +0 2 +0 3 +1 2 +1 3 +2 3 +} +Two islands, full graphs, no connections between islands: +directed: false +vcount: 8 +edges: { +0 1 +0 2 +0 3 +1 2 +1 3 +2 3 +4 5 +4 6 +4 7 +5 6 +5 7 +6 7 +} +Three islands, full graphs, 16 connections between islands. +Three islands, random graphs, 3 connections between islands. +directed: false +vcount: 12 +edges: { +0 4 +0 11 +1 10 +2 4 +3 4 +3 10 +4 5 +4 6 +5 7 +5 9 +6 8 +7 11 +9 10 +10 11 +} +Negative number of islands. +Negative island size. +Probability out of range. +Negative number of edges between islands. +Too many edges between islands. diff --git a/tests/unit/igraph_sir.c b/tests/unit/igraph_sir.c new file mode 100644 index 0000000..4e0633a --- /dev/null +++ b/tests/unit/igraph_sir.c @@ -0,0 +1,96 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_sir(igraph_sir_t *sir) { + igraph_integer_t i, n; + + n = igraph_vector_size(&sir->times); + IGRAPH_ASSERT(n >= 2); + + IGRAPH_ASSERT(VECTOR(sir->times)[0] == 0); + for (i = 1; i < n; i++) { + IGRAPH_ASSERT(VECTOR(sir->times)[i] > VECTOR(sir->times)[i-1]); + } + + printf("susceptibles: "); + print_vector_int(&sir->no_s); + printf("infected: "); + print_vector_int(&sir->no_i); + printf("recovered: "); + print_vector_int(&sir->no_r); +} + + +void print_result(igraph_t *g, igraph_real_t beta, igraph_real_t gamma, igraph_integer_t no_sim) { + igraph_vector_ptr_t result; + igraph_vector_ptr_init(&result, 0); + IGRAPH_ASSERT(igraph_sir(g, beta, gamma, no_sim, &result) == IGRAPH_SUCCESS); + for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&result); i++) { + print_sir(VECTOR(result)[i]); + igraph_sir_destroy(VECTOR(result)[i]); + } + igraph_vector_ptr_destroy_all(&result); + printf("\n"); +} + +int main(void) { + igraph_t g_empty, g_lm, g_line, g_1, g_2, g_full; + + igraph_rng_seed(igraph_rng_default(), 43); + + igraph_small(&g_empty, 0, 0, -1); + igraph_small(&g_lm, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_2, 2, 0, -1); + igraph_small(&g_line, 5, 0, 0,1, 1,2, 2,3, 3,4, -1); + igraph_full(&g_full, 5, 0, IGRAPH_NO_LOOPS); + + printf("Only one person, low recovery rate:\n"); + print_result(&g_1, 0.1, 0.0001, 2); + + printf("Two people, not connected, only one infection expected:\n"); + print_result(&g_2, 1, 1, 2); + + printf("Line:\n"); + print_result(&g_line, 1, 1, 2); + + printf("Line, low infection rate, few infections expected:\n"); + print_result(&g_line, 0.0001, 1, 2); + + printf("Full graph, more infections expected than line with same rates:\n"); + print_result(&g_full, 1, 1, 2); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + IGRAPH_ASSERT(igraph_sir(&g_lm, 1, 1, 1, NULL) == IGRAPH_EINVAL); + IGRAPH_ASSERT(igraph_sir(&g_empty, 1, 1, 1, NULL) == IGRAPH_EINVAL); + + igraph_destroy(&g_empty); + igraph_destroy(&g_lm); + igraph_destroy(&g_line); + igraph_destroy(&g_1); + igraph_destroy(&g_2); + igraph_destroy(&g_full); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sir.out b/tests/unit/igraph_sir.out new file mode 100644 index 0000000..4f48449 --- /dev/null +++ b/tests/unit/igraph_sir.out @@ -0,0 +1,40 @@ +Only one person, low recovery rate: +susceptibles: ( 0 0 ) +infected: ( 1 0 ) +recovered: ( 0 1 ) +susceptibles: ( 0 0 ) +infected: ( 1 0 ) +recovered: ( 0 1 ) + +Two people, not connected, only one infection expected: +susceptibles: ( 1 1 ) +infected: ( 1 0 ) +recovered: ( 0 1 ) +susceptibles: ( 1 1 ) +infected: ( 1 0 ) +recovered: ( 0 1 ) + +Line: +susceptibles: ( 4 3 3 3 ) +infected: ( 1 2 1 0 ) +recovered: ( 0 0 1 2 ) +susceptibles: ( 4 3 2 2 2 2 ) +infected: ( 1 2 3 2 1 0 ) +recovered: ( 0 0 0 1 2 3 ) + +Line, low infection rate, few infections expected: +susceptibles: ( 4 4 ) +infected: ( 1 0 ) +recovered: ( 0 1 ) +susceptibles: ( 4 4 ) +infected: ( 1 0 ) +recovered: ( 0 1 ) + +Full graph, more infections expected than line with same rates: +susceptibles: ( 4 3 2 1 0 0 0 0 0 0 ) +infected: ( 1 2 3 4 5 4 3 2 1 0 ) +recovered: ( 0 0 0 0 0 1 2 3 4 5 ) +susceptibles: ( 4 3 3 2 1 0 0 0 0 0 ) +infected: ( 1 2 1 2 3 4 3 2 1 0 ) +recovered: ( 0 0 1 1 1 1 2 3 4 5 ) + diff --git a/tests/unit/igraph_solve_lsap.c b/tests/unit/igraph_solve_lsap.c new file mode 100644 index 0000000..b49aae0 --- /dev/null +++ b/tests/unit/igraph_solve_lsap.c @@ -0,0 +1,67 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_vector_int_t result; + igraph_matrix_t m_pdc, m_0, m_m34, m_m43; + int pdc[] = {9, 2, 7, 8, + 6, 4, 3, 7, + 5, 8, 1, 8, + 7, 6, 9, 4}; + int m34[] = {3, 3, 2, 3, + 2, 3, 3, 3, + 3, 2, 3, 3}; + int m43[] = {3, 3, 2, + 2, 3, 3, + 3, 2, 3, + 2, 3, 3}; + igraph_vector_int_init(&result, 0); + matrix_init_int_row_major(&m_pdc, 4, 4, pdc); + matrix_init_int_row_major(&m_m34, 3, 4, m34); + matrix_init_int_row_major(&m_m43, 4, 3, m43); + igraph_matrix_init(&m_0, 0, 0); + + printf("4 tasks, 4 agents:\n"); + igraph_solve_lsap(&m_pdc, 4, &result); + print_vector_int(&result); + + printf("\n0 tasks, 0 agents:\n"); + igraph_solve_lsap(&m_0, 0, &result); + print_vector_int(&result); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("\n4 tasks, 3 agents, n = 4.\n"); + IGRAPH_ASSERT(igraph_solve_lsap(&m_m34, 4, &result) == IGRAPH_EINVAL); + + printf("\n3 tasks, 4 agents, n = 4.\n"); + IGRAPH_ASSERT(igraph_solve_lsap(&m_m43, 4, &result) == IGRAPH_EINVAL); + + igraph_vector_int_destroy(&result); + igraph_matrix_destroy(&m_pdc); + igraph_matrix_destroy(&m_0); + igraph_matrix_destroy(&m_m34); + igraph_matrix_destroy(&m_m43); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_solve_lsap.out b/tests/unit/igraph_solve_lsap.out new file mode 100644 index 0000000..d4e1a48 --- /dev/null +++ b/tests/unit/igraph_solve_lsap.out @@ -0,0 +1,9 @@ +4 tasks, 4 agents: +( 1 0 2 3 ) + +0 tasks, 0 agents: +( ) + +4 tasks, 3 agents, n = 4. + +3 tasks, 4 agents, n = 4. diff --git a/tests/unit/igraph_spanner.c b/tests/unit/igraph_spanner.c new file mode 100644 index 0000000..cc62599 --- /dev/null +++ b/tests/unit/igraph_spanner.c @@ -0,0 +1,203 @@ +/* + 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 . +*/ + + +#include +#include "test_utilities.h" +#include + +void test_spanner(igraph_t *graph, igraph_vector_int_t *spanner, double stretch, igraph_vector_t *weights) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_t spanner_graph; + igraph_vector_t spanner_weights; + igraph_matrix_t res_spanner, res_graph; + + /* create the spanner graph with igraph_subgraph_from_edges() as recommended in the docs, + then compare it with the original graph and validate the stretch factor */ + if (weights) { + igraph_cattribute_EAN_setv(graph, "weight", weights); + } + igraph_subgraph_from_edges(graph, &spanner_graph, igraph_ess_vector(spanner), 0); + igraph_vector_init(&spanner_weights, igraph_vector_int_size(spanner)); + if (weights){ + igraph_cattribute_EANV(&spanner_graph, "weight", igraph_ess_all(IGRAPH_EDGEORDER_ID), &spanner_weights); + } else { + igraph_vector_fill(&spanner_weights, 1); + } + + // compare number of nodes + IGRAPH_ASSERT(igraph_vcount(&spanner_graph) == no_of_nodes); + + // compare number of edges. We expect the number of edges to decrease in + // all cases but the trivial ones + if (stretch > 1 && no_of_edges > 1) { + IGRAPH_ASSERT(igraph_ecount(&spanner_graph) < no_of_edges); + } + + // print the number of nodes, the number of original edges and the new edge + // count. This is not validated (there is no expected output) but it helps + // to gauge whether the algorithm is not simply keeping most of the edges + printf( + "%" IGRAPH_PRId ", %" IGRAPH_PRId " --> %" IGRAPH_PRId "\n", + no_of_nodes, no_of_edges, igraph_ecount(&spanner_graph) + ); + + // Validate the stretch factor + igraph_matrix_init(&res_spanner, 0, 0); + igraph_matrix_init(&res_graph, 0, 0); + igraph_distances_dijkstra(graph, &res_graph, igraph_vss_all(), igraph_vss_all(), weights, IGRAPH_ALL); + igraph_distances_dijkstra(&spanner_graph, &res_spanner, igraph_vss_all(), igraph_vss_all(), &spanner_weights, IGRAPH_ALL); + for (igraph_integer_t x = 0; x < no_of_nodes; x++) { + for (igraph_integer_t y = 0; y < no_of_nodes; y++) { + if (x == y) { + continue; + } + IGRAPH_ASSERT(MATRIX(res_spanner, x, y) <= MATRIX(res_graph, x, y) * stretch); + } + } + igraph_matrix_destroy(&res_graph); + igraph_matrix_destroy(&res_spanner); + + // Clean up + igraph_vector_destroy(&spanner_weights); + igraph_destroy(&spanner_graph); +} + +int main(void) { + igraph_t graph; + igraph_vector_t weights; + igraph_vector_int_t spanner; + igraph_integer_t no_of_edges; + + /* Initialize attribute handler; we will use edge attributes in test_spanner() */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* Seed the RNG to make the test output predictable */ + igraph_rng_seed(igraph_rng_default(), 42); + + /* Create the output vector -- this will be re-used several times */ + igraph_vector_int_init(&spanner, 0); + + /* Trivial spanner with stretch of one */ + printf("Complete graph with stretch of one\n"); + igraph_full(&graph, 20, IGRAPH_UNDIRECTED, 0); + igraph_spanner(&graph, &spanner, 1, NULL); + test_spanner(&graph, &spanner, 1, NULL); + igraph_destroy(&graph); + + /* Test spanner for random weighted complete graph */ + printf("Weighted complete graph with random weights and stretch = 10\n"); + igraph_full(&graph, 20, IGRAPH_UNDIRECTED, 0); + no_of_edges = igraph_ecount(&graph); + igraph_vector_init(&weights, no_of_edges); + for (int i = 0; i < no_of_edges; i++) { + double generated_number = igraph_rng_get_unif(igraph_rng_default(), 1, 100); + VECTOR(weights)[i] = generated_number; + } + igraph_spanner(&graph, &spanner, 10, &weights); + test_spanner(&graph, &spanner, 10, &weights); + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + /* Test spanner for unweighted complete graph */ + printf("Unweighted complete graph with stretch = 5\n"); + igraph_full(&graph, 20, IGRAPH_UNDIRECTED, 0); + igraph_spanner(&graph, &spanner, 5, NULL); + test_spanner(&graph, &spanner, 5, NULL); + igraph_destroy(&graph); + + /* Random Erdos-Renyi graph */ + printf("Random Erdos-Renyi graph\n"); + igraph_erdos_renyi_game_gnp(&graph, 200, 0.25, IGRAPH_UNDIRECTED, 0); + no_of_edges = igraph_ecount(&graph); + igraph_vector_init(&weights, no_of_edges); + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + double generated_number = igraph_rng_get_unif(igraph_rng_default(), 1, 100); + VECTOR(weights)[i] = generated_number; + } + igraph_spanner(&graph, &spanner, 7, &weights); + test_spanner(&graph, &spanner, 7, &weights); + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + /* Geometric random graph */ + printf("Geometric random graph, unweighted\n"); + igraph_grg_game(&graph, 100, 0.2, /* torus = */ 0, 0, 0); + igraph_spanner(&graph, &spanner, 7, 0); + test_spanner(&graph, &spanner, 7, 0); + igraph_destroy(&graph); + + /* Singleton graph */ + printf("Singleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_spanner(&graph, &spanner, 2, 0); + test_spanner(&graph, &spanner, 2, 0); + igraph_destroy(&graph); + + /* Null graph */ + printf("Null graph\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_spanner(&graph, &spanner, 2, 0); + test_spanner(&graph, &spanner, 2, 0); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + /* Error conditions */ + igraph_set_error_handler(igraph_error_handler_ignore); + + igraph_erdos_renyi_game_gnp(&graph, 200, 0.9, IGRAPH_UNDIRECTED, 0); + no_of_edges = igraph_ecount(&graph); + igraph_vector_init(&weights, no_of_edges); + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + double generated_number = igraph_rng_get_unif(igraph_rng_default(), 1, 100); + VECTOR(weights)[i] = generated_number; + } + + printf("Negative weight\n"); + VECTOR(weights)[10] = -42; + IGRAPH_ASSERT(igraph_spanner(&graph, &spanner, 7, &weights) == IGRAPH_EINVAL); + VECTOR(weights)[10] = 42; + + printf("NaN weight\n"); + VECTOR(weights)[10] = IGRAPH_NAN; + IGRAPH_ASSERT(igraph_spanner(&graph, &spanner, 7, &weights) == IGRAPH_EINVAL); + VECTOR(weights)[10] = 42; + + printf("Invalid spanning factor\n"); + IGRAPH_ASSERT(igraph_spanner(&graph, &spanner, 0.5, &weights) == IGRAPH_EINVAL); + + printf("Invalid weight vector length\n"); + igraph_vector_resize(&weights, no_of_edges - 1); + IGRAPH_ASSERT(igraph_spanner(&graph, &spanner, 7, &weights) == IGRAPH_EINVAL); + + igraph_vector_destroy(&weights); + igraph_destroy(&graph); + + /* Regression test for https://github.com/igraph/igraph/issues/2487 + * Edge directions should be ignored in directed graphs. */ + igraph_rng_seed(igraph_rng_default(), 42); + igraph_small(&graph, 2, IGRAPH_DIRECTED, 0, 1, -1); + igraph_spanner(&graph, &spanner, 1.72, NULL); + igraph_destroy(&graph); + + igraph_vector_int_destroy(&spanner); + + return IGRAPH_SUCCESS; +} diff --git a/tests/unit/igraph_sparsemat2.c b/tests/unit/igraph_sparsemat2.c new file mode 100644 index 0000000..bb3160a --- /dev/null +++ b/tests/unit/igraph_sparsemat2.c @@ -0,0 +1,152 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 + 02110-1301 USA + +*/ + +#define NCOMPLEX /* to make it compile with MSVC on Windows */ + +#include + +#include "test_utilities.h" + +igraph_error_t my_gaxpy(const igraph_matrix_t *m, + const igraph_vector_t *v, + igraph_vector_t *res) { + return igraph_blas_dgemv(false, 1.0, m, v, 0.0, res); +} + +igraph_bool_t check_same(const igraph_sparsemat_t *A, + const igraph_matrix_t *M) { + igraph_matrix_t A_dense; + igraph_bool_t result; + + igraph_matrix_init(&A_dense, 1, 1); + igraph_sparsemat_as_matrix(&A_dense, A); + result = igraph_matrix_all_e(&A_dense, M); + igraph_matrix_destroy(&A_dense); + + return result; +} + +int main(void) { + + igraph_sparsemat_t A, B, C, D; + igraph_vector_t v, w, x, y; + igraph_matrix_t M, N, O; + igraph_integer_t i; + + RNG_BEGIN(); + + /* Matrix-vector product */ +#define NROW 10 +#define NCOL 5 +#define EDGES NROW*NCOL/3 + igraph_matrix_init(&M, NROW, NCOL); + igraph_sparsemat_init(&A, NROW, NCOL, EDGES); + for (i = 0; i < EDGES; i++) { + igraph_integer_t r = RNG_INTEGER(0, NROW - 1); + igraph_integer_t c = RNG_INTEGER(0, NCOL - 1); + igraph_real_t value = RNG_INTEGER(1, 5); + MATRIX(M, r, c) = MATRIX(M, r, c) + value; + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_vector_init(&v, NCOL); + igraph_vector_init(&w, NCOL); + for (i = 0; i < NCOL; i++) { + VECTOR(v)[i] = VECTOR(w)[i] = RNG_INTEGER(1, 5); + } + + igraph_vector_init(&x, NROW); + igraph_vector_init(&y, NROW); + my_gaxpy(&M, &v, &x); + igraph_vector_null(&y); + igraph_sparsemat_gaxpy(&B, &w, &y); + + if (!igraph_vector_all_e(&x, &y)) { + return 1; + } + + igraph_vector_destroy(&x); + igraph_vector_destroy(&y); + igraph_vector_destroy(&v); + igraph_vector_destroy(&w); + igraph_sparsemat_destroy(&B); + igraph_matrix_destroy(&M); + +#undef NROW +#undef NCOL +#undef EDGES + + /* Matrix-matrix product */ +#define NROW_A 10 +#define NCOL_A 7 +#define EDGES_A NROW_A*NCOL_A/3 +#define NROW_B 7 +#define NCOL_B 9 +#define EDGES_B NROW_B*NCOL_B/3 + igraph_matrix_init(&M, NROW_A, NCOL_A); + igraph_sparsemat_init(&A, NROW_A, NCOL_A, EDGES_A); + for (i = 0; i < EDGES_A; i++) { + igraph_integer_t r = RNG_INTEGER(0, NROW_A - 1); + igraph_integer_t c = RNG_INTEGER(0, NCOL_A - 1); + igraph_real_t value = RNG_INTEGER(1, 5); + MATRIX(M, r, c) = MATRIX(M, r, c) + value; + igraph_sparsemat_entry(&A, r, c, value); + } + igraph_sparsemat_compress(&A, &C); + igraph_sparsemat_destroy(&A); + + igraph_matrix_init(&N, NROW_B, NCOL_B); + igraph_sparsemat_init(&B, NROW_B, NCOL_B, EDGES_B); + for (i = 0; i < EDGES_B; i++) { + igraph_integer_t r = RNG_INTEGER(0, NROW_B - 1); + igraph_integer_t c = RNG_INTEGER(0, NCOL_B - 1); + igraph_real_t value = RNG_INTEGER(1, 5); + MATRIX(N, r, c) = MATRIX(N, r, c) + value; + igraph_sparsemat_entry(&B, r, c, value); + } + igraph_sparsemat_compress(&B, &D); + igraph_sparsemat_destroy(&B); + + igraph_matrix_init(&O, 0, 0); + igraph_blas_dgemm(false, false, 1.0, &M, &N, 0.0, &O); + igraph_sparsemat_multiply(&C, &D, &A); + + if (! check_same(&A, &O)) { + return 2; + } + + igraph_sparsemat_destroy(&C); + igraph_sparsemat_destroy(&D); + igraph_sparsemat_destroy(&A); + igraph_matrix_destroy(&M); + igraph_matrix_destroy(&N); + igraph_matrix_destroy(&O); + + VERIFY_FINALLY_STACK(); + + RNG_END(); + + return 0; +} diff --git a/tests/unit/igraph_sparsemat2.out b/tests/unit/igraph_sparsemat2.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/igraph_sparsemat5.c b/tests/unit/igraph_sparsemat5.c new file mode 100644 index 0000000..c06dc52 --- /dev/null +++ b/tests/unit/igraph_sparsemat5.c @@ -0,0 +1,401 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +#define EPS 1e-13 + + +/* Generic test for 1x1 matrices */ +void test_1x1(igraph_real_t value) { + igraph_sparsemat_t A, B; + igraph_matrix_t values, vectors; + igraph_vector_t values2; + igraph_arpack_options_t options; + + igraph_arpack_options_init(&options); + + igraph_sparsemat_init(&A, 1, 1, 1); + igraph_sparsemat_entry(&A, 0, 0, value); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_matrix_init(&values, 0, 0); + igraph_matrix_init(&vectors, 0, 0); + options.mode = 1; + igraph_sparsemat_arpack_rnsolve(&B, /*options=*/ 0, /*storage=*/ 0, + &values, &vectors); + printf("rnsolve:\n - eigenvalues:\n"); + print_matrix(&values); + printf(" - eigenvectors:\n"); + print_matrix(&vectors); + igraph_matrix_destroy(&values); + igraph_matrix_destroy(&vectors); + + igraph_vector_init(&values2, 0); + igraph_matrix_init(&vectors, 0, 0); + options.mode = 1; + igraph_sparsemat_arpack_rssolve(&B, /*options=*/ 0, /*storage=*/ 0, + &values2, &vectors, IGRAPH_SPARSEMAT_SOLVE_LU); + printf("rssolve:\n - eigenvalues:\n"); + print_vector(&values2); + printf(" - eigenvectors:\n"); + print_matrix(&vectors); + igraph_vector_destroy(&values2); + igraph_matrix_destroy(&vectors); + + igraph_sparsemat_destroy(&B); +} + +/* Generic test for 2x2 matrices */ +void test_2x2(igraph_real_t a, igraph_real_t b, igraph_real_t c, igraph_real_t d) { + igraph_sparsemat_t A, B; + igraph_matrix_t values, vectors; + igraph_vector_t values2; + igraph_arpack_options_t options; + + igraph_arpack_options_init(&options); + options.mode = 1; + options.nev = 2; + + igraph_sparsemat_init(&A, 2, 2, 4); + igraph_sparsemat_entry(&A, 0, 0, a); + igraph_sparsemat_entry(&A, 0, 1, b); + igraph_sparsemat_entry(&A, 1, 0, c); + igraph_sparsemat_entry(&A, 1, 1, d); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_matrix_init(&values, 0, 0); + igraph_matrix_init(&vectors, 0, 0); + igraph_sparsemat_arpack_rnsolve(&B, &options, /*storage=*/ 0, + &values, &vectors); + printf("rnsolve:\n - eigenvalues:\n"); + print_matrix(&values); + printf(" - eigenvectors:\n"); + print_matrix(&vectors); + igraph_matrix_destroy(&values); + igraph_matrix_destroy(&vectors); + + if (b == c) { + igraph_vector_init(&values2, 0); + igraph_matrix_init(&vectors, 0, 0); + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values2, &vectors, IGRAPH_SPARSEMAT_SOLVE_QR); + printf("rssolve:\n - eigenvalues:\n"); + print_vector(&values2); + printf(" - eigenvectors:\n"); + print_matrix(&vectors); + igraph_vector_destroy(&values2); + igraph_matrix_destroy(&vectors); + } + + igraph_sparsemat_destroy(&B); +} + +int main(void) { + + igraph_sparsemat_t A, B; + igraph_matrix_t vectors, values2; + igraph_vector_t values; + igraph_integer_t i; + igraph_arpack_options_t options; + igraph_real_t min, max; + igraph_t g1, g2, g3; + + /* igraph_arpack_rssolve()/rnsolve() use the RNG to generate + * a random starting vector for ARPACK. */ + igraph_rng_seed(igraph_rng_default(), 123); + + /***********************************************************************/ + + /* Identity matrix */ + printf("== Identity matrix ==\n"); +#define DIM 10 + igraph_sparsemat_init(&A, DIM, DIM, DIM); + for (i = 0; i < DIM; i++) { + igraph_sparsemat_entry(&A, i, i, 1.0); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_vector_init(&values, 0); + igraph_arpack_options_init(&options); + + options.mode = 1; + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, /*vectors=*/ 0, /*solvemethod=*/0); + IGRAPH_ASSERT(VECTOR(values)[0] == 1.0); + + options.mode = 3; + options.sigma = 2; + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, /*vectors=*/ 0, + IGRAPH_SPARSEMAT_SOLVE_LU); + IGRAPH_ASSERT(VECTOR(values)[0] == 1.0); + + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, /*vectors=*/ 0, + IGRAPH_SPARSEMAT_SOLVE_QR); + IGRAPH_ASSERT(VECTOR(values)[0] == 1.0); + + igraph_vector_destroy(&values); + igraph_sparsemat_destroy(&B); + +#undef DIM + + /***********************************************************************/ + + /* Diagonal matrix */ + printf("\n== Diagonal matrix ==\n"); +#define DIM 10 + igraph_sparsemat_init(&A, DIM, DIM, DIM); + for (i = 0; i < DIM; i++) { + igraph_sparsemat_entry(&A, i, i, i + 1.0); + } + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_vector_init(&values, 0); + igraph_matrix_init(&vectors, 0, 0); + + /* Regular mode */ + options.mode = 1; + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, /*vectors=*/ &vectors, + /*solvemethod=*/ 0); + if ( fabs(VECTOR(values)[0] - DIM) > EPS ) { + printf("Regular: VECTOR(values)[0] numerical precision is only %g, should be %g", + fabs((double)VECTOR(values)[0] - DIM), EPS); + abort(); + } + + IGRAPH_ASSERT( fabs(fabs(MATRIX(vectors, DIM - 1, 0)) - 1.0) < EPS); + + MATRIX(vectors, DIM - 1, 0) = 0.0; + igraph_matrix_minmax(&vectors, &min, &max); + IGRAPH_ASSERT(fabs(min) < EPS); + IGRAPH_ASSERT(fabs(max) < EPS); + + /* Shift and invert mode */ + options.mode = 3; + options.sigma = 11; + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, /*vectors=*/ &vectors, + IGRAPH_SPARSEMAT_SOLVE_LU); + if ( fabs(VECTOR(values)[0] - DIM) > EPS ) { + printf("Shift and invert, LU: VECTOR(values)[0] numerical precision is only %g, should be %g", + fabs((double)VECTOR(values)[0] - DIM), EPS); + abort(); + } + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, /*vectors=*/ &vectors, + IGRAPH_SPARSEMAT_SOLVE_QR); + if ( fabs(VECTOR(values)[0] - DIM) > EPS ) { + printf("Shift and invert, QR: VECTOR(values)[0] numerical precision is only %g, should be %g", + fabs((double)VECTOR(values)[0] - DIM), EPS); + abort(); + } + + IGRAPH_ASSERT( fabs(fabs(MATRIX(vectors, DIM - 1, 0)) - 1.0) < EPS); + + MATRIX(vectors, DIM - 1, 0) = 0.0; + igraph_matrix_minmax(&vectors, &min, &max); + IGRAPH_ASSERT(fabs(min) < EPS); + IGRAPH_ASSERT(fabs(max) < EPS); + + igraph_vector_destroy(&values); + igraph_matrix_destroy(&vectors); + igraph_sparsemat_destroy(&B); +#undef DIM + + /***********************************************************************/ + + /* A tree, plus a ring */ + printf("\n== A tree, plus a ring ==\n"); +#define DIM 10 + igraph_kary_tree(&g1, DIM, /*children=*/ 2, IGRAPH_TREE_UNDIRECTED); + igraph_ring(&g2, DIM, IGRAPH_UNDIRECTED, /*mutual=*/ 0, /*circular=*/ 1); + igraph_union(&g3, &g1, &g2, /*edge_map1=*/ 0, /*edge_map1=*/ 0); + igraph_destroy(&g1); + igraph_destroy(&g2); + + igraph_sparsemat_init(&A, 1, 1, 0); + igraph_get_adjacency_sparse(&g3, &A, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_destroy(&g3); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_vector_init(&values, 0); + igraph_matrix_init(&vectors, 0, 0); + + /* Regular mode */ + options.mode = 1; + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, &vectors, /*solvemethod=*/ 0); + + if (MATRIX(vectors, 0, 0) < 0.0) { + igraph_matrix_scale(&vectors, -1.0); + } + + printf("\nRegular:\n"); + printf("Eigenvalues:\n"); + print_vector(&values); + printf("Eigenvectors:\n"); + print_matrix(&vectors); + + /* Shift and invert mode */ + options.mode = 3; + options.sigma = VECTOR(values)[0] * 1.1; + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, &vectors, + IGRAPH_SPARSEMAT_SOLVE_LU); + + if (MATRIX(vectors, 0, 0) < 0.0) { + igraph_matrix_scale(&vectors, -1.0); + } + printf("\nShift and invert, LU:\n"); + printf("Eigenvalues:\n"); + print_vector(&values); + printf("Eigenvectors:\n"); + print_matrix(&vectors); + + igraph_sparsemat_arpack_rssolve(&B, &options, /*storage=*/ 0, + &values, &vectors, + IGRAPH_SPARSEMAT_SOLVE_QR); + if (MATRIX(vectors, 0, 0) < 0.0) { + igraph_matrix_scale(&vectors, -1.0); + } + printf("\nShift and invert, QR:\n"); + printf("Eigenvalues:\n"); + print_vector(&values); + printf("Eigenvectors:\n"); + print_matrix(&vectors); + + igraph_vector_destroy(&values); + igraph_matrix_destroy(&vectors); + igraph_sparsemat_destroy(&B); +#undef DIM + + + /***********************************************************************/ + + /* A directed tree and a directed, mutual ring, no ARPACK options */ + printf("\n== A directed tree and a directed, mutual ring ==\n"); +#define DIM 10 + igraph_kary_tree(&g1, DIM, /*children=*/ 2, IGRAPH_TREE_OUT); + igraph_ring(&g2, DIM, IGRAPH_DIRECTED, /*mutual=*/ 1, /*circular=*/ 1); + igraph_union(&g3, &g1, &g2, /*edge_map1=*/ 0, /*edge_map2=*/ 0); + igraph_destroy(&g1); + igraph_destroy(&g2); + + igraph_sparsemat_init(&A, 1, 1, 0); + igraph_get_adjacency_sparse(&g3, &A, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_destroy(&g3); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_matrix_init(&values2, 0, 0); + igraph_matrix_init(&vectors, 0, 0); + + /* Regular mode */ + options.mode = 1; + igraph_sparsemat_arpack_rnsolve(&B, /*options=*/ 0, /*storage=*/ 0, + &values2, &vectors); + + if (MATRIX(vectors, 0, 0) < 0.0) { + igraph_matrix_scale(&vectors, -1.0); + } + + printf("\nRegular:\n"); + printf("Eigenvalues:\n"); + print_matrix(&values2); + printf("Eigenvectors:\n"); + print_matrix(&vectors); + + igraph_matrix_destroy(&values2); + igraph_matrix_destroy(&vectors); + igraph_sparsemat_destroy(&B); +#undef DIM + + /***********************************************************************/ + + /* A small test graph */ + printf("\n== A small test graph ==\n"); + + igraph_small(&g1, 11, IGRAPH_DIRECTED, + 0, 1, 1, 3, 1, 8, 2, 10, 3, 6, 3, 10, 4, 2, 5, 4, + 6, 1, 6, 4, 7, 9, 8, 5, 8, 7, 9, 8, 10, 0, + -1); + + igraph_sparsemat_init(&A, 1, 1, 0); + igraph_get_adjacency_sparse(&g1, &A, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); + igraph_destroy(&g1); + igraph_sparsemat_compress(&A, &B); + igraph_sparsemat_destroy(&A); + + igraph_matrix_init(&values2, 0, 0); + igraph_matrix_init(&vectors, 0, 0); + + /* Regular mode */ + options.mode = 1; + igraph_sparsemat_arpack_rnsolve(&B, &options, /*storage=*/ 0, + &values2, &vectors); + + if (MATRIX(vectors, 0, 0) < 0.0) { + igraph_matrix_scale(&vectors, -1.0); + } + + printf("\nRegular:\n"); + printf("Eigenvalues:\n"); + print_matrix(&values2); + printf("Eigenvectors:\n"); + print_matrix(&vectors); + + igraph_matrix_destroy(&values2); + igraph_matrix_destroy(&vectors); + igraph_sparsemat_destroy(&B); + + /***********************************************************************/ + + /* Testing the special case solver for 1x1 matrices */ + printf("\n== Testing the special case solver for 1x1 matrices ==\n"); + test_1x1(2); + test_1x1(0); + test_1x1(-3); + + /***********************************************************************/ + + /* Testing the special case solver for 2x2 matrices */ + printf("\n== Testing the special case solver for 2x2 matrices ==\n"); + test_2x2(1, 2, 2, 4); /* symmetric */ + test_2x2(1, 2, 3, 4); /* non-symmetric, real eigenvalues */ + test_2x2(1, -5, 10, 4); /* non-symmetric, complex eigenvalues */ + test_2x2(0, 0, 0, 0); /* symmetric, pathological */ + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_sparsemat5.out b/tests/unit/igraph_sparsemat5.out new file mode 100644 index 0000000..725b4b6 --- /dev/null +++ b/tests/unit/igraph_sparsemat5.out @@ -0,0 +1,159 @@ +== Identity matrix == + +== Diagonal matrix == + +== A tree, plus a ring == + +Regular: +Eigenvalues: +( 3.79369 ) +Eigenvectors: +[ 0.272513 + 0.387127 + 0.421856 + 0.429479 + 0.344793 + 0.266584 + 0.24469 + 0.239837 + 0.235697 + 0.224848 ] + +Shift and invert, LU: +Eigenvalues: +( 3.79369 ) +Eigenvectors: +[ 0.272513 + 0.387127 + 0.421856 + 0.429479 + 0.344793 + 0.266584 + 0.24469 + 0.239837 + 0.235697 + 0.224848 ] + +Shift and invert, QR: +Eigenvalues: +( 3.79369 ) +Eigenvectors: +[ 0.272513 + 0.387127 + 0.421856 + 0.429479 + 0.344793 + 0.266584 + 0.24469 + 0.239837 + 0.235697 + 0.224848 ] + +== A directed tree and a directed, mutual ring == + +Regular: +Eigenvalues: +[ 2.61264 0 ] +Eigenvectors: +[ 0.467245 + 0.571573 + 0.427081 + 0.335532 + 0.263456 + 0.130696 + 0.0780048 + 0.0731022 + 0.112985 + 0.222086 ] + +== A small test graph == + +Regular: +Eigenvalues: +[ 1.35971 0 ] +Eigenvectors: +[ 0.352334 + 0.479071 + 0.190574 + 0.525509 + 0.140158 + 0.10308 + 0.455414 + 0.0680917 + 0.125888 + 0.0925848 + 0.259125 ] + +== Testing the special case solver for 1x1 matrices == +rnsolve: + - eigenvalues: +[ 2 0 ] + - eigenvectors: +[ 1 ] +rssolve: + - eigenvalues: +( 2 ) + - eigenvectors: +[ 1 ] +rnsolve: + - eigenvalues: +[ 0 0 ] + - eigenvectors: +[ 1 ] +rssolve: + - eigenvalues: +( 0 ) + - eigenvectors: +[ 1 ] +rnsolve: + - eigenvalues: +[ -3 0 ] + - eigenvectors: +[ 1 ] +rssolve: + - eigenvalues: +( -3 ) + - eigenvectors: +[ 1 ] + +== Testing the special case solver for 2x2 matrices == +rnsolve: + - eigenvalues: +[ 5 0 + 0 0 ] + - eigenvectors: +[ 1 -4 + 2 2 ] +rssolve: + - eigenvalues: +( 5 0 ) + - eigenvectors: +[ 1 -4 + 2 2 ] +rnsolve: + - eigenvalues: +[ 5.37228 0 + -0.372281 0 ] + - eigenvectors: +[ 1.37228 -4.37228 + 3 3 ] +rnsolve: + - eigenvalues: +[ 2.5 6.91014 + 2.5 -6.91014 ] + - eigenvectors: +[ -1.5 6.91014 + 10 0 ] +rnsolve: + - eigenvalues: +[ 0 0 + 0 0 ] + - eigenvectors: +[ 1 0 + 0 1 ] +rssolve: + - eigenvalues: +( 0 0 ) + - eigenvectors: +[ 1 0 + 0 1 ] diff --git a/tests/unit/igraph_sparsemat9.c b/tests/unit/igraph_sparsemat9.c new file mode 100644 index 0000000..d3370c5 --- /dev/null +++ b/tests/unit/igraph_sparsemat9.c @@ -0,0 +1,88 @@ +/* -*- 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 + +*/ + +#include + +#include "test_utilities.h" + +#define DIM1 10 +#define DIM2 5 +#define DIM3 6 + +#define INT(a) (igraph_rng_get_integer(igraph_rng_default(), 0, (a))) +#define REAL() (igraph_rng_get_normal(igraph_rng_default(), 0, 1)) + +int main(void) { + igraph_sparsemat_t sA, sB, sC; + igraph_matrix_t A1, A2, A3, B, C; + int i; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_sparsemat_init(&sA, DIM1, DIM2, 20); + for (i = 0; i < 10; i++) { + igraph_sparsemat_entry(&sA, INT(DIM1 - 1), INT(DIM2 - 1), REAL()); + } + igraph_sparsemat_compress(&sA, &sB); + igraph_sparsemat_destroy(&sA); + + igraph_sparsemat_init(&sA, DIM2, DIM3, 20); + for (i = 0; i < 10; i++) { + igraph_sparsemat_entry(&sA, INT(DIM2 - 1), INT(DIM3 - 1), REAL()); + } + igraph_sparsemat_compress(&sA, &sC); + igraph_sparsemat_destroy(&sA); + + igraph_matrix_init(&B, 0, 0); + igraph_sparsemat_as_matrix(&B, &sB); + igraph_matrix_init(&C, 0, 0); + igraph_sparsemat_as_matrix(&C, &sC); + + /* All possible products */ + igraph_sparsemat_multiply(&sB, &sC, &sA); + igraph_matrix_init(&A1, 0, 0); + igraph_sparsemat_as_matrix(&A1, &sA); + igraph_matrix_init(&A2, 0, 0); + igraph_sparsemat_dense_multiply(&B, &sC, &A2); + igraph_matrix_init(&A3, 0, 0); + igraph_sparsemat_multiply_by_dense(&sB, &C, &A3); + + if (igraph_matrix_maxdifference(&A1, &A2) > 1e-10 || + igraph_matrix_maxdifference(&A2, &A3) > 1e-10) { + return 1; + } + + igraph_sparsemat_destroy(&sA); + igraph_sparsemat_destroy(&sB); + igraph_sparsemat_destroy(&sC); + + igraph_matrix_destroy(&A1); + igraph_matrix_destroy(&A2); + igraph_matrix_destroy(&A3); + igraph_matrix_destroy(&B); + igraph_matrix_destroy(&C); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_sparsemat_droptol.c b/tests/unit/igraph_sparsemat_droptol.c new file mode 100644 index 0000000..f0f3b45 --- /dev/null +++ b/tests/unit/igraph_sparsemat_droptol.c @@ -0,0 +1,63 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_sparsemat_t spmat; + igraph_sparsemat_t spmat_comp; + + printf("0x0 matrix.\n"); + igraph_sparsemat_init(&spmat, 0, 0, /*nzmax*/0); + igraph_sparsemat_compress(&spmat, &spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_droptol(&spmat_comp, 5) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat_comp); + + printf("3x3 matrix.\n"); + igraph_sparsemat_init(&spmat, 3, 3, /*nzmax*/7); + igraph_sparsemat_entry(&spmat, 0, 0, 5); + igraph_sparsemat_entry(&spmat, 1, 1, 6); + igraph_sparsemat_entry(&spmat, 2, 2, 7); + igraph_sparsemat_entry(&spmat, 3, 0, 1); + igraph_sparsemat_entry(&spmat, 0, 3, 2); + igraph_sparsemat_entry(&spmat, 2, 1, 3); + igraph_sparsemat_entry(&spmat, 1, 2, -14); + igraph_sparsemat_compress(&spmat, &spmat_comp); + printf("Remove values within distance 5 from zero:\n"); + IGRAPH_ASSERT(igraph_sparsemat_droptol(&spmat_comp, 5) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + printf("Remove values within distance 20 from zero:\n"); + IGRAPH_ASSERT(igraph_sparsemat_droptol(&spmat_comp, 20) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat_comp); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("uncompressed matrix.\n"); + igraph_sparsemat_init(&spmat, 0, 0, /*nzmax*/0); + IGRAPH_ASSERT(igraph_sparsemat_droptol(&spmat, 10) == IGRAPH_EINVAL); + igraph_sparsemat_destroy(&spmat); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sparsemat_droptol.out b/tests/unit/igraph_sparsemat_droptol.out new file mode 100644 index 0000000..40fae2f --- /dev/null +++ b/tests/unit/igraph_sparsemat_droptol.out @@ -0,0 +1,16 @@ +0x0 matrix. +3x3 matrix. +Remove values within distance 5 from zero: +col 0: locations 0 to -1 +col 1: locations 0 to 0 +1 : 6 +col 2: locations 1 to 2 +2 : 7 +1 : -14 +col 3: locations 3 to 2 +Remove values within distance 20 from zero: +col 0: locations 0 to -1 +col 1: locations 0 to -1 +col 2: locations 0 to -1 +col 3: locations 0 to -1 +uncompressed matrix. diff --git a/tests/unit/igraph_sparsemat_fkeep.c b/tests/unit/igraph_sparsemat_fkeep.c new file mode 100644 index 0000000..320941c --- /dev/null +++ b/tests/unit/igraph_sparsemat_fkeep.c @@ -0,0 +1,80 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +igraph_integer_t fkeep_none(igraph_integer_t row, igraph_integer_t col, igraph_real_t value, void *other) { + IGRAPH_UNUSED(row); + IGRAPH_UNUSED(col); + IGRAPH_UNUSED(value); + IGRAPH_UNUSED(other); + return 0; +} + +igraph_integer_t fkeep(igraph_integer_t row, igraph_integer_t col, igraph_real_t value, void *other) { + if (row == 0 || col == 1 || value > *(int*)other) { + return 0; + } + return 1; +} + +int main(void) { + igraph_sparsemat_t spmat; + igraph_sparsemat_t spmat_comp; + int a = 0; + + printf("0x0 matrix.\n"); + igraph_sparsemat_init(&spmat, 0, 0, /*nzmax*/0); + igraph_sparsemat_compress(&spmat, &spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_fkeep(&spmat_comp, &fkeep, &a) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat_comp); + + printf("3x3 matrix.\n"); + igraph_sparsemat_init(&spmat, 3, 3, /*nzmax*/7); + igraph_sparsemat_entry(&spmat, 0, 0, 5); + igraph_sparsemat_entry(&spmat, 1, 1, 6); + igraph_sparsemat_entry(&spmat, 2, 2, 7); + igraph_sparsemat_entry(&spmat, 3, 0, 1); + igraph_sparsemat_entry(&spmat, 0, 3, 2); + igraph_sparsemat_entry(&spmat, 2, 1, 3); + igraph_sparsemat_entry(&spmat, 1, 2, 4); + igraph_sparsemat_compress(&spmat, &spmat_comp); + a = 6; + printf("Remove row 0, column 1, and values above 6:\n"); + IGRAPH_ASSERT(igraph_sparsemat_fkeep(&spmat_comp, &fkeep, &a) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + printf("Remove everything:\n"); + IGRAPH_ASSERT(igraph_sparsemat_fkeep(&spmat_comp, &fkeep_none, &a) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat_comp); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("uncompressed matrix.\n"); + igraph_sparsemat_init(&spmat, 0, 0, /*nzmax*/0); + IGRAPH_ASSERT(igraph_sparsemat_fkeep(&spmat, &fkeep, &a) == IGRAPH_EINVAL); + igraph_sparsemat_destroy(&spmat); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sparsemat_fkeep.out b/tests/unit/igraph_sparsemat_fkeep.out new file mode 100644 index 0000000..c055d2a --- /dev/null +++ b/tests/unit/igraph_sparsemat_fkeep.out @@ -0,0 +1,15 @@ +0x0 matrix. +3x3 matrix. +Remove row 0, column 1, and values above 6: +col 0: locations 0 to 0 +3 : 1 +col 1: locations 1 to 0 +col 2: locations 1 to 1 +1 : 4 +col 3: locations 2 to 1 +Remove everything: +col 0: locations 0 to -1 +col 1: locations 0 to -1 +col 2: locations 0 to -1 +col 3: locations 0 to -1 +uncompressed matrix. diff --git a/tests/unit/igraph_sparsemat_getelements_sorted.c b/tests/unit/igraph_sparsemat_getelements_sorted.c new file mode 100644 index 0000000..bcddc4b --- /dev/null +++ b/tests/unit/igraph_sparsemat_getelements_sorted.c @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void print_res_i_j(igraph_vector_t *result, igraph_vector_int_t *i, igraph_vector_int_t *j) { + print_vector(result); + print_vector_int(i); + print_vector_int(j); +} + +void destroy_all(igraph_vector_t *result, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_sparsemat_t *spmat, igraph_sparsemat_t *spmat_comp) { + igraph_vector_destroy(result); + igraph_vector_int_destroy(i); + igraph_vector_int_destroy(j); + igraph_sparsemat_destroy(spmat); + igraph_sparsemat_destroy(spmat_comp); +} + +void init_all(igraph_vector_t *result, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_sparsemat_t *spmat) { + igraph_vector_init(result, 0); + igraph_vector_int_init(i, 0); + igraph_vector_int_init(j, 0); + igraph_sparsemat_init(spmat, 0, 0, 0); +} + +int main(void) { + igraph_sparsemat_t spmat; + igraph_sparsemat_t spmat_comp; + igraph_vector_t result; + igraph_vector_int_t i, j; + int k, l; + int size = 3; + + printf("0x0 matrix\n"); + init_all(&result, &i, &j, &spmat); + igraph_sparsemat_compress(&spmat, &spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_getelements_sorted(&spmat, &i, &j, &result) == IGRAPH_SUCCESS); + printf("triplet:\n"); + print_res_i_j(&result, &i, &j); + IGRAPH_ASSERT(igraph_sparsemat_getelements_sorted(&spmat_comp, &i, &j, &result) == IGRAPH_SUCCESS); + printf("compressed:\n"); + print_res_i_j(&result, &i, &j); + destroy_all(&result, &i, &j, &spmat, &spmat_comp); + + printf("\n3x3 matrix\n"); + init_all(&result, &i, &j, &spmat); + /* make sure to fill spmat in an order that is not _already_ sorted by + * row indices first */ + for (k = 0; k < size; k += 2) { + for (l = 0; l < size; l ++) { + igraph_sparsemat_entry(&spmat, k, l, 100); + } + } + for (k = 0; k < size; k += 2) { + for (l = 0; l < size; l ++) { + igraph_sparsemat_entry(&spmat, k, l, k * size + l); + } + } + igraph_sparsemat_compress(&spmat, &spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_getelements_sorted(&spmat, &i, &j, &result) == IGRAPH_SUCCESS); + printf("triplet:\n"); + print_res_i_j(&result, &i, &j); + IGRAPH_ASSERT(igraph_sparsemat_getelements_sorted(&spmat_comp, &i, &j, &result) == IGRAPH_SUCCESS); + printf("compressed:\n"); + print_res_i_j(&result, &i, &j); + + destroy_all(&result, &i, &j, &spmat, &spmat_comp); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sparsemat_getelements_sorted.out b/tests/unit/igraph_sparsemat_getelements_sorted.out new file mode 100644 index 0000000..ce3cacc --- /dev/null +++ b/tests/unit/igraph_sparsemat_getelements_sorted.out @@ -0,0 +1,19 @@ +0x0 matrix +triplet: +( ) +( ) +( ) +compressed: +( ) +( ) +( 0 ) + +3x3 matrix +triplet: +( 100 0 100 6 100 1 100 7 100 2 100 8 ) +( 0 0 2 2 0 0 2 2 0 0 2 2 ) +( 0 0 0 0 1 1 1 1 2 2 2 2 ) +compressed: +( 100 0 100 6 100 1 100 7 100 2 100 8 ) +( 0 0 2 2 0 0 2 2 0 0 2 2 ) +( 0 4 8 12 ) diff --git a/tests/unit/igraph_sparsemat_is_symmetric.c b/tests/unit/igraph_sparsemat_is_symmetric.c new file mode 100644 index 0000000..408fcfa --- /dev/null +++ b/tests/unit/igraph_sparsemat_is_symmetric.c @@ -0,0 +1,72 @@ +/* -*- 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +#define DIM 10 + +#define INT(a) (igraph_rng_get_integer(igraph_rng_default(), 0, (a))) + +int main(void) { + int runs = 100; + const int noelements = 20; + igraph_sparsemat_t A; + igraph_bool_t result; + int i; + + igraph_rng_seed(igraph_rng_default(), 42); + + for (; runs > 0; runs--) { + + igraph_sparsemat_init(&A, DIM, DIM, noelements * 2); + for (i = 0; i < noelements; i++) { + int row = INT(DIM - 1); + int col = INT(DIM - 1); + int val = INT(100); + igraph_sparsemat_entry(&A, row, col, val); + igraph_sparsemat_entry(&A, col, row, val); + } + igraph_sparsemat_is_symmetric(&A, &result); + if (!result) { + return 1; + } + igraph_sparsemat_destroy(&A); + + igraph_sparsemat_init(&A, DIM, DIM, noelements); + for (i = 0; i < noelements; i++) { + igraph_sparsemat_entry(&A, INT(DIM - 1), INT(DIM - 1), INT(100)); + } + igraph_sparsemat_is_symmetric(&A, &result); + if (result) { + return 2; + } + igraph_sparsemat_destroy(&A); + + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_sparsemat_iterator_idx.c b/tests/unit/igraph_sparsemat_iterator_idx.c new file mode 100644 index 0000000..54f3120 --- /dev/null +++ b/tests/unit/igraph_sparsemat_iterator_idx.c @@ -0,0 +1,56 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_sparsemat_t spmat; + igraph_sparsemat_t spmat_comp; + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_t it_comp; + + printf("0x0 matrix.\n"); + igraph_sparsemat_init(&spmat, 0, 0, /*nzmax*/0); + igraph_sparsemat_compress(&spmat, &spmat_comp); + igraph_sparsemat_iterator_init(&it, &spmat); + igraph_sparsemat_iterator_init(&it_comp, &spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_iterator_idx(&it) == 0); + IGRAPH_ASSERT(igraph_sparsemat_iterator_idx(&it_comp) == 0); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat_comp); + + printf("3x3 matrix.\n"); + igraph_sparsemat_init(&spmat, 3, 3, /*nzmax*/0); + igraph_sparsemat_entry(&spmat, 0, 0, 5); + igraph_sparsemat_entry(&spmat, 3, 3, 6); + igraph_sparsemat_entry(&spmat, 3, 3, 6); + igraph_sparsemat_compress(&spmat, &spmat_comp); + igraph_sparsemat_iterator_init(&it, &spmat); + igraph_sparsemat_iterator_init(&it_comp, &spmat_comp); + igraph_sparsemat_iterator_next(&it); + igraph_sparsemat_iterator_next(&it); + igraph_sparsemat_iterator_next(&it_comp); + IGRAPH_ASSERT(igraph_sparsemat_iterator_idx(&it) == 2); + IGRAPH_ASSERT(igraph_sparsemat_iterator_idx(&it_comp) == 1); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat_comp); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sparsemat_minmax.c b/tests/unit/igraph_sparsemat_minmax.c new file mode 100644 index 0000000..a43b4aa --- /dev/null +++ b/tests/unit/igraph_sparsemat_minmax.c @@ -0,0 +1,248 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +#define N 10 +#define M 20 +#define NZ 50 + +#define MIN 0 +#define MAX 10 + +typedef igraph_error_t fun(igraph_sparsemat_t *A, igraph_vector_t *res); + +int doit(int which) { + + igraph_integer_t i; + igraph_sparsemat_t A, A2; + igraph_vector_t vec; + fun *colfun, *rowfun; + + if (which == MIN) { + colfun = igraph_sparsemat_colmins; + rowfun = igraph_sparsemat_rowmins; + } else { + colfun = igraph_sparsemat_colmaxs; + rowfun = igraph_sparsemat_rowmaxs; + } + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Triplet diagonal matrix */ + + printf("Triplet diagonal matrix\n"); + igraph_vector_init(&vec, N); + for (i = 0; i < N; i++) { + VECTOR(vec)[i] = i; + } + igraph_sparsemat_init_diag(&A, /*nzmax=*/ N, /*values=*/ &vec, /*compress=*/ 0); + + igraph_vector_null(&vec); + rowfun(&A, &vec); + for (i = 0; i < N; i++) { + if (VECTOR(vec)[i] != i) { + return which + 1; + } + } + + igraph_vector_null(&vec); + colfun(&A, &vec); + for (i = 0; i < N; i++) { + if (VECTOR(vec)[i] != i) { + return which + 2; + } + } + + igraph_vector_destroy(&vec); + igraph_sparsemat_destroy(&A); + + /* Compressed diagonal matrix */ + + printf("Compressed diagonal matrix\n"); + igraph_vector_init(&vec, N); + for (i = 0; i < N; i++) { + VECTOR(vec)[i] = i; + } + igraph_sparsemat_init_diag(&A, /*nzmax=*/ N, /*values=*/ &vec, /*compress=*/ 1); + + igraph_vector_null(&vec); + rowfun(&A, &vec); + for (i = 0; i < N; i++) { + if (VECTOR(vec)[i] != i) { + return which + 3; + } + } + + igraph_vector_null(&vec); + colfun(&A, &vec); + for (i = 0; i < N; i++) { + if (VECTOR(vec)[i] != i) { + return which + 4; + } + } + + igraph_vector_destroy(&vec); + igraph_sparsemat_destroy(&A); + + + /* Random triplet matrix */ + + printf("Random triplet matrix\n"); + igraph_sparsemat_init(&A, /*rows=*/ N, /*cols=*/ M, /*nzmax=*/ NZ + 5); + for (i = 0; i < NZ; i++) { + int r = igraph_rng_get_integer(igraph_rng_default(), 0, N - 1); + int c = igraph_rng_get_integer(igraph_rng_default(), 0, M - 1); + igraph_real_t x = igraph_rng_get_integer(igraph_rng_default(), + -10, 10); + IGRAPH_ASSERT(x >= -10 && x <= 10); + igraph_sparsemat_entry(&A, r, c, x); + } + if (which == MAX) { + igraph_sparsemat_scale(&A, -1.0); + } + + igraph_vector_init(&vec, 0); + colfun(&A, &vec); + igraph_vector_print(&vec); + + igraph_vector_null(&vec); + rowfun(&A, &vec); + igraph_vector_print(&vec); + + /* Random compresssed matrix */ + + printf("Random compressed matrix\n"); + igraph_sparsemat_compress(&A, &A2); + + igraph_vector_null(&vec); + colfun(&A2, &vec); + igraph_vector_print(&vec); + + igraph_vector_null(&vec); + rowfun(&A2, &vec); + igraph_vector_print(&vec); + + igraph_vector_destroy(&vec); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&A2); + + /* Matrix with zero rows, triplet */ + + printf("Matrix with zero rows, triplet\n"); + igraph_sparsemat_init(&A, /*rows=*/ 0, /*cols=*/ M, /*nzmax=*/ NZ); + if (which == MAX) { + igraph_sparsemat_scale(&A, -1.0); + } + + igraph_vector_init(&vec, 5); + rowfun(&A, &vec); + if (igraph_vector_size(&vec) != 0) { + return which + 5; + } + + igraph_vector_null(&vec); + colfun(&A, &vec); + igraph_vector_print(&vec); + + /* Matrix with zero rows, compressed */ + + printf("Matrix with zero rows, compressed\n"); + igraph_sparsemat_compress(&A, &A2); + + igraph_vector_null(&vec); + rowfun(&A, &vec); + if (igraph_vector_size(&vec) != 0) { + return which + 6; + } + + igraph_vector_null(&vec); + colfun(&A, &vec); + igraph_vector_print(&vec); + + igraph_vector_destroy(&vec); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&A2); + + /* Matrix with zero columns, triplet */ + + printf("Matrix with zero columns, triplet\n"); + igraph_sparsemat_init(&A, /*rows=*/ N, /*cols=*/ 0, /*nzmax=*/ NZ); + if (which == MAX) { + igraph_sparsemat_scale(&A, -1.0); + } + + igraph_vector_init(&vec, 5); + colfun(&A, &vec); + if (igraph_vector_size(&vec) != 0) { + return which + 7; + } + + igraph_vector_null(&vec); + rowfun(&A, &vec); + igraph_vector_print(&vec); + + /* Matrix with zero columns, compressed */ + + printf("Matrix with zero columns, compressed\n"); + igraph_sparsemat_compress(&A, &A2); + + igraph_vector_null(&vec); + colfun(&A, &vec); + if (igraph_vector_size(&vec) != 0) { + return which + 8; + } + + igraph_vector_null(&vec); + rowfun(&A, &vec); + igraph_vector_print(&vec); + + igraph_vector_destroy(&vec); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&A2); + + return 0; +} + +int main(void) { + int res; + + res = doit(/*which=*/ MIN); + if (res) { + return res; + } + + VERIFY_FINALLY_STACK(); + + res = doit(/*which=*/ MAX); + if (res) { + return res; + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_sparsemat_minmax.out b/tests/unit/igraph_sparsemat_minmax.out new file mode 100644 index 0000000..7942811 --- /dev/null +++ b/tests/unit/igraph_sparsemat_minmax.out @@ -0,0 +1,32 @@ +Triplet diagonal matrix +Compressed diagonal matrix +Random triplet matrix +-8 -5 3 -5 -3 7 -10 -4 -2 1 -10 -6 -10 -5 -4 -5 -6 -9 10 -8 +10 -10 -6 -4 -5 9 -10 -10 -9 -8 +Random compressed matrix +-8 -5 3 -5 -3 7 -10 -4 -1 1 -7 -6 -10 -5 -4 -5 -6 -11 10 -8 +10 -10 -6 -4 -5 9 -7 -10 -11 -8 +Matrix with zero rows, triplet +Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf +Matrix with zero rows, compressed +Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf +Matrix with zero columns, triplet +Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf +Matrix with zero columns, compressed +Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf +Triplet diagonal matrix +Compressed diagonal matrix +Random triplet matrix +8 5 -3 5 3 -7 10 4 2 -1 10 6 10 5 4 5 6 9 -10 8 +-10 10 6 4 5 -9 10 10 9 8 +Random compressed matrix +8 5 -3 5 3 -7 10 4 1 -1 7 6 10 5 4 5 6 11 -10 8 +-10 10 6 4 5 -9 7 10 11 8 +Matrix with zero rows, triplet +-Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf +Matrix with zero rows, compressed +-Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf +Matrix with zero columns, triplet +-Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf +Matrix with zero columns, compressed +-Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf diff --git a/tests/unit/igraph_sparsemat_nonzero_storage.c b/tests/unit/igraph_sparsemat_nonzero_storage.c new file mode 100644 index 0000000..9cbafe0 --- /dev/null +++ b/tests/unit/igraph_sparsemat_nonzero_storage.c @@ -0,0 +1,69 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + + +int main(void) { + igraph_sparsemat_t spmat; + igraph_sparsemat_t spmat_comp; + int i, j; + int size = 3; + + printf("0x0 matrix\n"); + igraph_sparsemat_init(&spmat, 0, 0, 0); + igraph_sparsemat_compress(&spmat, &spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_nonzero_storage(&spmat) == 0); + IGRAPH_ASSERT(igraph_sparsemat_nonzero_storage(&spmat_comp) == 0); + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat_comp); + + printf("3x3 compressed matrix with duplicate values that add up to zero.\n"); + igraph_sparsemat_init(&spmat, size, size, 7); + for (i = 0; i < size; i++) { + for (j = 0; j < size; j++) { + igraph_sparsemat_entry(&spmat, i, j, 5); + igraph_sparsemat_entry(&spmat, i, j, -5); + + /* This checks if there's two entries for every loop. */ + IGRAPH_ASSERT(igraph_sparsemat_nonzero_storage(&spmat) == (i * size + j + 1) * 2); + igraph_sparsemat_compress(&spmat, &spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_nonzero_storage(&spmat_comp) == (i * size + j + 1) * 2); + + igraph_sparsemat_destroy(&spmat_comp); + } + } + printf("Adding one entry to work around some broken error handling.\n"); + igraph_sparsemat_entry(&spmat, 0, 0, 5); + igraph_sparsemat_compress(&spmat, &spmat_comp); + + printf("Removing duplicates should leave us with one entry in each position.\n"); + igraph_sparsemat_dupl(&spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_nonzero_storage(&spmat_comp) == (size * size)); + + printf("Removing all zeros should leave us with only one entry.\n"); + igraph_sparsemat_dropzeros(&spmat_comp); + IGRAPH_ASSERT(igraph_sparsemat_nonzero_storage(&spmat_comp) == 1); + + igraph_sparsemat_destroy(&spmat); + igraph_sparsemat_destroy(&spmat_comp); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sparsemat_normalize.c b/tests/unit/igraph_sparsemat_normalize.c new file mode 100644 index 0000000..92cd698 --- /dev/null +++ b/tests/unit/igraph_sparsemat_normalize.c @@ -0,0 +1,106 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void create_test_matrix( + igraph_sparsemat_t* spmat, + igraph_sparsemat_t* spmat_comp, + igraph_bool_t use_zero_col_row +) { + igraph_sparsemat_init(spmat, 3, 3, /*nzmax*/7); + igraph_sparsemat_entry(spmat, 0, 0, 6); + igraph_sparsemat_entry(spmat, 2, 2, 7); + + if (!use_zero_col_row) { + igraph_sparsemat_entry(spmat, 0, 1, 2); + igraph_sparsemat_entry(spmat, 1, 0, 4); + igraph_sparsemat_entry(spmat, 1, 1, 6); + igraph_sparsemat_entry(spmat, 2, 1, 2); + igraph_sparsemat_entry(spmat, 1, 2, -14); + } + + igraph_sparsemat_compress(spmat, spmat_comp); +} + +int main(void) { + igraph_sparsemat_t spmat; + igraph_sparsemat_t spmat_comp; + + printf("0x0 matrix.\n"); + igraph_sparsemat_init(&spmat, 0, 0, /*nzmax*/0); + igraph_sparsemat_compress(&spmat, &spmat_comp); + + IGRAPH_ASSERT(igraph_sparsemat_normalize_rows(&spmat_comp, /* allow_zero = */ 1) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_sparsemat_normalize_cols(&spmat_comp, /* allow_zero = */ 1) == IGRAPH_SUCCESS); + + /* Normalization should succeed for an empty matrix even if allow_zero = 0 + * because technically there is no 0/0 division anywhere */ + IGRAPH_ASSERT(igraph_sparsemat_normalize_rows(&spmat_comp, /* allow_zero = */ 0) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_sparsemat_normalize_cols(&spmat_comp, /* allow_zero = */ 0) == IGRAPH_SUCCESS); + + igraph_sparsemat_destroy(&spmat_comp); + igraph_sparsemat_destroy(&spmat); + + printf("\n"); + printf("3x3 matrix without zero rows and columns, row-wise normalization.\n"); + create_test_matrix(&spmat, &spmat_comp, /* use_zero_col_row = */ 0); + IGRAPH_ASSERT(igraph_sparsemat_normalize_rows(&spmat_comp, /* allow_zero = */ 0) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + igraph_sparsemat_destroy(&spmat_comp); + igraph_sparsemat_destroy(&spmat); + + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("\n"); + printf("3x3 matrix without zero rows and columns, column-wise normalization.\n"); + create_test_matrix(&spmat, &spmat_comp, /* use_zero_col_row = */ 0); + IGRAPH_ASSERT(igraph_sparsemat_normalize_cols(&spmat_comp, /* allow_zero = */ 0) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + igraph_sparsemat_destroy(&spmat_comp); + igraph_sparsemat_destroy(&spmat); + + printf("\n"); + printf("3x3 matrix with zero rows and columns, row-wise normalization.\n"); + create_test_matrix(&spmat, &spmat_comp, /* use_zero_col_row = */ 1); + IGRAPH_ASSERT(igraph_sparsemat_normalize_rows(&spmat_comp, /* allow_zero = */ 0) == IGRAPH_EINVAL); + IGRAPH_ASSERT(igraph_sparsemat_normalize_rows(&spmat_comp, /* allow_zero = */ 1) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + igraph_sparsemat_destroy(&spmat_comp); + igraph_sparsemat_destroy(&spmat); + + printf("\n"); + printf("3x3 matrix with zero rows and columns, column-wise normalization.\n"); + create_test_matrix(&spmat, &spmat_comp, /* use_zero_col_row = */ 1); + IGRAPH_ASSERT(igraph_sparsemat_normalize_cols(&spmat_comp, /* allow_zero = */ 0) == IGRAPH_EINVAL); + IGRAPH_ASSERT(igraph_sparsemat_normalize_cols(&spmat_comp, /* allow_zero = */ 1) == IGRAPH_SUCCESS); + igraph_sparsemat_print(&spmat_comp, stdout); + igraph_sparsemat_destroy(&spmat_comp); + igraph_sparsemat_destroy(&spmat); + + printf("\n"); + printf("uncompressed matrix.\n"); + igraph_sparsemat_init(&spmat, 0, 0, /*nzmax*/0); + IGRAPH_ASSERT(igraph_sparsemat_droptol(&spmat, 10) == IGRAPH_EINVAL); + igraph_sparsemat_destroy(&spmat); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_sparsemat_normalize.out b/tests/unit/igraph_sparsemat_normalize.out new file mode 100644 index 0000000..05b3195 --- /dev/null +++ b/tests/unit/igraph_sparsemat_normalize.out @@ -0,0 +1,41 @@ +0x0 matrix. + +3x3 matrix without zero rows and columns, row-wise normalization. +col 0: locations 0 to 1 +0 : 0.75 +1 : -1 +col 1: locations 2 to 4 +0 : 0.25 +1 : -1.5 +2 : 0.222222 +col 2: locations 5 to 6 +2 : 0.777778 +1 : 3.5 + +3x3 matrix without zero rows and columns, column-wise normalization. +col 0: locations 0 to 1 +0 : 0.6 +1 : 0.4 +col 1: locations 2 to 4 +0 : 0.2 +1 : 0.6 +2 : 0.2 +col 2: locations 5 to 6 +2 : -1 +1 : 2 + +3x3 matrix with zero rows and columns, row-wise normalization. +col 0: locations 0 to 0 +0 : 1 +col 1: locations 1 to 0 +col 2: locations 1 to 1 +2 : 1 + +3x3 matrix with zero rows and columns, column-wise normalization. +col 0: locations 0 to 0 +0 : 1 +col 1: locations 1 to 0 +col 2: locations 1 to 1 +2 : 1 + +uncompressed matrix. diff --git a/tests/unit/igraph_sparsemat_view.c b/tests/unit/igraph_sparsemat_view.c new file mode 100644 index 0000000..24d21aa --- /dev/null +++ b/tests/unit/igraph_sparsemat_view.c @@ -0,0 +1,47 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_sparsemat_t spmat; + igraph_matrix_t mat; + igraph_integer_t p[] = {0, 1, 3}; + igraph_integer_t i[] = {1, 0, 2}; + igraph_real_t x[] = {1, 5, 2}; + + printf("Empty sparsemat.\n"); + igraph_matrix_init(&mat, 0, 0); + igraph_sparsemat_view(&spmat, /*nzmax*/ 0, /*m*/ 0, /*n*/ 0, /*p*/ NULL, /*i*/ NULL, /*x*/ NULL, /*nz*/ 0); + igraph_sparsemat_as_matrix(&mat, &spmat); + print_matrix(&mat); + igraph_free(spmat.cs); + igraph_matrix_destroy(&mat); + + printf("3x2 sparsemat:\n"); + igraph_matrix_init(&mat, 0, 0); + igraph_sparsemat_view(&spmat, /*nzmax*/ 3, /*m*/ 3, /*n*/ 2, p, i, x, /*nz*/ -1); + igraph_sparsemat_as_matrix(&mat, &spmat); + print_matrix(&mat); + igraph_free(spmat.cs); + igraph_matrix_destroy(&mat); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_sparsemat_view.out b/tests/unit/igraph_sparsemat_view.out new file mode 100644 index 0000000..edff804 --- /dev/null +++ b/tests/unit/igraph_sparsemat_view.out @@ -0,0 +1,6 @@ +Empty sparsemat. +[ 0-by-0 ] +3x2 sparsemat: +[ 0 5 + 1 0 + 0 2 ] diff --git a/tests/unit/igraph_sparsemat_which_minmax.c b/tests/unit/igraph_sparsemat_which_minmax.c new file mode 100644 index 0000000..d757034 --- /dev/null +++ b/tests/unit/igraph_sparsemat_which_minmax.c @@ -0,0 +1,283 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +#define N 10 +#define M 20 +#define NZ 50 + +#define MIN 0 +#define MAX 10 + +typedef igraph_error_t fun(igraph_sparsemat_t *A, igraph_vector_t *res, + igraph_vector_int_t *pos); + +int doit(int which) { + + int i; + igraph_sparsemat_t A, A2; + igraph_vector_t vec; + igraph_vector_int_t pos; + fun *colfun, *rowfun; + + if (which == MIN) { + colfun = igraph_sparsemat_which_min_cols; + rowfun = igraph_sparsemat_which_min_rows; + } else { + /* colfun = */ /* TODO */ + /* rowfun = */ /* TODO */ + } + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Triplet diagonal matrix */ + + printf("Triplet diagonal matrix\n"); + igraph_vector_init(&vec, N); + igraph_vector_int_init(&pos, N); + for (i = 0; i < N; i++) { + VECTOR(vec)[i] = i; + } + igraph_sparsemat_init_diag(&A, /*nzmax=*/ N, /*values=*/ &vec, /*compress=*/ 0); + + igraph_vector_null(&vec); + igraph_vector_int_null(&pos); + rowfun(&A, &vec, &pos); + for (i = 0; i < N; i++) { + if (VECTOR(vec)[i] != i) { + return which + 1; + } + } + for (i = 0; i < N; i++) { + if (VECTOR(pos)[i] != i) { + return which + 2; + } + } + + igraph_vector_null(&vec); + colfun(&A, &vec, &pos); + for (i = 0; i < N; i++) { + if (VECTOR(vec)[i] != i) { + return which + 3; + } + } + for (i = 0; i < N; i++) { + if (VECTOR(pos)[i] != i) { + return which + 4; + } + } + + igraph_vector_destroy(&vec); + igraph_vector_int_destroy(&pos); + igraph_sparsemat_destroy(&A); + + /* Compressed diagonal matrix */ + + igraph_vector_init(&vec, N); + igraph_vector_int_init(&pos, N); + for (i = 0; i < N; i++) { + VECTOR(vec)[i] = i; + } + igraph_sparsemat_init_diag(&A, /*nzmax=*/ N, /*values=*/ &vec, /*compress=*/ 1); + + igraph_vector_null(&vec); + rowfun(&A, &vec, &pos); + for (i = 0; i < N; i++) { + if (VECTOR(vec)[i] != i) { + return which + 5; + } + } + for (i = 0; i < N; i++) { + if (VECTOR(pos)[i] != i) { + return which + 6; + } + } + + igraph_vector_null(&vec); + colfun(&A, &vec, &pos); + for (i = 0; i < N; i++) { + if (VECTOR(vec)[i] != i) { + return which + 7; + } + } + for (i = 0; i < N; i++) { + if (VECTOR(pos)[i] != i) { + return which + 8; + } + } + + igraph_vector_destroy(&vec); + igraph_vector_int_destroy(&pos); + igraph_sparsemat_destroy(&A); + + + /* Random triplet matrix */ + + printf("Random triplet matrix\n"); + igraph_sparsemat_init(&A, /*rows=*/ N, /*cols=*/ M, /*nzmax=*/ NZ + 5); + for (i = 0; i < NZ; i++) { + igraph_integer_t r = RNG_INTEGER(0, N-1); + igraph_integer_t c = RNG_INTEGER(0, M-1); + igraph_real_t x = RNG_INTEGER(-10, 10); + IGRAPH_ASSERT(x >= -10 && x <= 10); + igraph_sparsemat_entry(&A, r, c, x); + } + if (which == MAX) { + igraph_sparsemat_scale(&A, -1.0); + } + + igraph_vector_init(&vec, 0); + igraph_vector_int_init(&pos, 0); + colfun(&A, &vec, &pos); + igraph_vector_print(&vec); + igraph_vector_int_print(&pos); + + igraph_vector_null(&vec); + rowfun(&A, &vec, &pos); + igraph_vector_print(&vec); + igraph_vector_int_print(&pos); + + /* Random compresssed matrix */ + + printf("Random compressed matrix\n"); + igraph_sparsemat_compress(&A, &A2); + + igraph_vector_null(&vec); + colfun(&A2, &vec, &pos); + igraph_vector_print(&vec); + igraph_vector_int_print(&pos); + + igraph_vector_null(&vec); + rowfun(&A2, &vec, &pos); + igraph_vector_print(&vec); + igraph_vector_int_print(&pos); + + igraph_vector_destroy(&vec); + igraph_vector_int_destroy(&pos); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&A2); + + /* Matrix with zero rows, triplet */ + + printf("Matrix with zero rows, triplet\n"); + igraph_sparsemat_init(&A, /*rows=*/ 0, /*cols=*/ M, /*nzmax=*/ NZ); + if (which == MAX) { + igraph_sparsemat_scale(&A, -1.0); + } + + igraph_vector_init(&vec, 5); + igraph_vector_int_init(&pos, 5); + rowfun(&A, &vec, &pos); + if (igraph_vector_size(&vec) != 0) { + return which + 5; + } + + igraph_vector_null(&vec); + colfun(&A, &vec, &pos); + igraph_vector_print(&vec); + igraph_vector_int_print(&pos); + + /* Matrix with zero rows, compressed */ + + printf("Matrix with zero rows, compressed\n"); + igraph_sparsemat_compress(&A, &A2); + + igraph_vector_null(&vec); + rowfun(&A, &vec, &pos); + if (igraph_vector_size(&vec) != 0) { + return which + 6; + } + + igraph_vector_null(&vec); + colfun(&A, &vec, &pos); + igraph_vector_print(&vec); + igraph_vector_int_print(&pos); + + igraph_vector_destroy(&vec); + igraph_vector_int_destroy(&pos); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&A2); + + /* Matrix with zero columns, triplet */ + + printf("Matrix with zero columns, triplet\n"); + igraph_sparsemat_init(&A, /*rows=*/ N, /*cols=*/ 0, /*nzmax=*/ NZ); + if (which == MAX) { + igraph_sparsemat_scale(&A, -1.0); + } + + igraph_vector_init(&vec, 5); + igraph_vector_int_init(&pos, 5); + colfun(&A, &vec, &pos); + if (igraph_vector_size(&vec) != 0) { + return which + 7; + } + + igraph_vector_null(&vec); + rowfun(&A, &vec, &pos); + igraph_vector_print(&vec); + igraph_vector_int_print(&pos); + + /* Matrix with zero columns, compressed */ + + printf("Matrix with zero columns, compressed\n"); + igraph_sparsemat_compress(&A, &A2); + + igraph_vector_null(&vec); + colfun(&A, &vec, &pos); + if (igraph_vector_size(&vec) != 0) { + return which + 8; + } + + igraph_vector_null(&vec); + rowfun(&A, &vec, &pos); + igraph_vector_print(&vec); + igraph_vector_int_print(&pos); + + igraph_vector_destroy(&vec); + igraph_vector_int_destroy(&pos); + igraph_sparsemat_destroy(&A); + igraph_sparsemat_destroy(&A2); + + return 0; +} + +int main(void) { + int res; + + res = doit(/*which=*/ MIN); + if (res) { + return res; + } + + /* res = doit(/\*which=*\/ MAX); */ + /* if (res) { return res; } */ + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_sparsemat_which_minmax.out b/tests/unit/igraph_sparsemat_which_minmax.out new file mode 100644 index 0000000..e5567be --- /dev/null +++ b/tests/unit/igraph_sparsemat_which_minmax.out @@ -0,0 +1,23 @@ +Triplet diagonal matrix +Random triplet matrix +-8 -5 3 -5 -3 7 -10 -4 -2 1 -10 -6 -10 -5 -4 -5 -6 -9 10 -8 +9 2 8 9 6 8 7 6 1 3 6 2 1 4 3 6 8 8 3 8 +10 -10 -6 -4 -5 9 -10 -10 -9 -8 +14 12 11 14 13 17 10 6 17 0 +Random compressed matrix +-8 -5 3 -5 -3 7 -10 -4 -1 1 -7 -6 -10 -5 -4 -5 -6 -11 10 -8 +9 2 8 9 6 8 7 6 1 3 6 2 1 4 3 6 8 8 3 8 +10 -10 -6 -4 -5 9 -7 -10 -11 -8 +14 12 11 14 13 17 10 6 17 0 +Matrix with zero rows, triplet +Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Matrix with zero rows, compressed +Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Matrix with zero columns, triplet +Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf +0 0 0 0 0 0 0 0 0 0 +Matrix with zero columns, compressed +Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf +0 0 0 0 0 0 0 0 0 0 diff --git a/tests/unit/igraph_split_join_distance.c b/tests/unit/igraph_split_join_distance.c new file mode 100644 index 0000000..3c5d16c --- /dev/null +++ b/tests/unit/igraph_split_join_distance.c @@ -0,0 +1,79 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_vector_int_t comm1, comm2; + igraph_integer_t distance12, distance21; + + printf("No vertices:\n"); + igraph_vector_int_init_int(&comm1, 0); + igraph_vector_int_init_int(&comm2, 0); + IGRAPH_ASSERT(igraph_split_join_distance(&comm1, &comm2, &distance12, &distance21) == IGRAPH_SUCCESS); + printf("%" IGRAPH_PRId ", %" IGRAPH_PRId "\n", distance12, distance21); + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + printf("Comparing 5 separate vertices and one 5-element cluster:\n"); + igraph_vector_int_init_int(&comm1, 5, 0, 1, 2, 3, 4); + igraph_vector_int_init_int(&comm2, 5, 0, 0, 0, 0, 0); + IGRAPH_ASSERT(igraph_split_join_distance(&comm1, &comm2, &distance12, &distance21) == IGRAPH_SUCCESS); + printf("%" IGRAPH_PRId ", %" IGRAPH_PRId "\n", distance12, distance21); + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + printf("Comparing ((6, 1), (2,4), (3,5,0)) with ((2), (6,0,3), (4,5), (1)):\n"); + igraph_vector_int_init_int(&comm1, 7, 2, 0, 1, 2, 1, 2, 0); + igraph_vector_int_init_int(&comm2, 7, 1, 3, 0, 1, 2, 2, 1); + IGRAPH_ASSERT(igraph_split_join_distance(&comm1, &comm2, &distance12, &distance21) == IGRAPH_SUCCESS); + printf("%" IGRAPH_PRId ", %" IGRAPH_PRId "\n", distance12, distance21); + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + printf("Comparing ((0,1), (), 2) with ((0), (), (1,2))\n"); + igraph_vector_int_init_int(&comm1, 3, 0, 0, 2); + igraph_vector_int_init_int(&comm2, 3, 0, 2, 2); + IGRAPH_ASSERT(igraph_split_join_distance(&comm1, &comm2, &distance12, &distance21) == IGRAPH_SUCCESS); + printf("%" IGRAPH_PRId ", %" IGRAPH_PRId "\n", distance12, distance21); + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("\nExpected to fail nicely:\n\n"); + + printf("Differently sized clusterings\n"); + igraph_vector_int_init_int(&comm1, 3, 0, 1, 2); + igraph_vector_int_init_int(&comm2, 5, 0, 0, 0, 0, 0); + IGRAPH_ASSERT(igraph_split_join_distance(&comm1, &comm2, &distance12, &distance21) == IGRAPH_EINVAL); + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + printf("Member index too high\n"); + igraph_vector_int_init_int(&comm1, 3, 0, 1, 2); + igraph_vector_int_init_int(&comm2, 3, 9, 0, 0); + IGRAPH_ASSERT(igraph_split_join_distance(&comm1, &comm2, &distance12, &distance21) == IGRAPH_EINVAL); + igraph_vector_int_destroy(&comm1); + igraph_vector_int_destroy(&comm2); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_split_join_distance.out b/tests/unit/igraph_split_join_distance.out new file mode 100644 index 0000000..8fd8c65 --- /dev/null +++ b/tests/unit/igraph_split_join_distance.out @@ -0,0 +1,13 @@ +No vertices: +0, 0 +Comparing 5 separate vertices and one 5-element cluster: +0, 4 +Comparing ((6, 1), (2,4), (3,5,0)) with ((2), (6,0,3), (4,5), (1)): +3, 2 +Comparing ((0,1), (), 2) with ((0), (), (1,2)) +1, 1 + +Expected to fail nicely: + +Differently sized clusterings +Member index too high diff --git a/tests/unit/igraph_square_lattice.c b/tests/unit/igraph_square_lattice.c new file mode 100644 index 0000000..26f5cac --- /dev/null +++ b/tests/unit/igraph_square_lattice.c @@ -0,0 +1,232 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +typedef struct { + int dim; + int m; + int nei; + igraph_bool_t directed, mutual, periodic; + igraph_integer_t *dimedges; +} lat_test_t; + +#define LAT_TEST(id, d, m, ne, di, mu, ci, ...) \ + igraph_integer_t lat_ ## id ## _edges[] = { __VA_ARGS__ } ; \ + lat_test_t lat_ ## id = { d, m, ne, di, mu, ci, lat_ ## id ## _edges } + +/*----------------d--m--ne-di-mu-ci-dimedges------------------------*/ +LAT_TEST(u_0, 0, 0, 1, 0, 0, 0, -1 ); +LAT_TEST(u_01, 1, 0, 1, 0, 0, 0, 0 ); +LAT_TEST(u_02, 2, 0, 1, 0, 0, 0, 0, 1 ); +LAT_TEST(u_03, 2, 0, 1, 0, 0, 0, 1, 0 ); + +LAT_TEST(d_0, 0, 0, 1, 1, 0, 0, -1 ); +LAT_TEST(d_01, 1, 0, 1, 1, 0, 0, 0 ); +LAT_TEST(d_02, 2, 0, 1, 1, 0, 0, 0, 1 ); +LAT_TEST(d_03, 2, 0, 1, 1, 0, 0, 1, 0 ); + +LAT_TEST(u_1, 1, 0, 1, 0, 0, 0, 1 ); +LAT_TEST(u_1x1, 2, 0, 1, 0, 0, 0, 1, 1 ); +LAT_TEST(u_2, 1, 1, 1, 0, 0, 0, 2, 0, 1 ); +LAT_TEST(u_2x1, 2, 1, 1, 0, 0, 0, 2, 1, 0, 1 ); +LAT_TEST(u_2x2, 2, 4, 1, 0, 0, 0, 2, 2, 0, 1, 0, 2, 1, 3, 2, 3 ); + +LAT_TEST(uc_1, 1, 0, 1, 0, 0, 1, 1 ); +LAT_TEST(uc_1x1, 2, 0, 1, 0, 0, 1, 1, 1 ); +LAT_TEST(uc_2, 1, 1, 1, 0, 0, 1, 2, 0, 1 ); +LAT_TEST(uc_2x1, 2, 1, 1, 0, 0, 1, 2, 1, 0, 1 ); +LAT_TEST(uc_2x2, 2, 4, 1, 0, 0, 1, 2, 2, 0, 1, 0, 2, 1, 3, 2, 3 ); + +LAT_TEST(dc_1, 1, 0, 1, 1, 0, 1, 1 ); +LAT_TEST(dc_1x1, 2, 0, 1, 1, 0, 1, 1, 1 ); +LAT_TEST(dc_2, 1, 2, 1, 1, 0, 1, 2, 0, 1, 1, 0 ); +LAT_TEST(dc_2x1, 2, 2, 1, 1, 0, 1, 2, 1, 0, 1, 1, 0 ); +LAT_TEST(dc_2x2, 2, 8, 1, 1, 0, 1, 2, 2, 0, 1, 0, 2, 1, 3, 2, 3, + 1, 0, 2, 0, 3, 1, 3, 2, ); + +LAT_TEST(d_1, 1, 0, 1, 1, 0, 0, 1 ); +LAT_TEST(d_1x1, 2, 0, 1, 1, 0, 0, 1, 1 ); +LAT_TEST(d_2, 1, 1, 1, 1, 0, 0, 2, 0, 1 ); +LAT_TEST(d_2x1, 2, 1, 1, 1, 0, 0, 2, 1, 0, 1 ); +LAT_TEST(d_2x2, 2, 4, 1, 1, 0, 0, 2, 2, 0, 1, 0, 2, 1, 3, 2, 3 ); + +LAT_TEST(dmc_1, 1, 0, 1, 1, 0, 1, 1 ); +LAT_TEST(dmc_1x1, 2, 0, 1, 1, 0, 1, 1, 1 ); +LAT_TEST(dmc_2, 1, 2, 1, 1, 0, 1, 2, 0, 1, 1, 0 ); +LAT_TEST(dmc_2x1, 2, 2, 1, 1, 0, 1, 2, 1, 0, 1, 1, 0 ); +LAT_TEST(dmc_2x2, 2, 4, 1, 1, 0, 1, 2, 2, 0, 1, 0, 2, 1, 3, 2, 3, + 1, 0, 3, 2, ); +/*----------------d--m--ne-di-mu-ci-dimedges------------------------*/ + +/* TODO: add more */ + +lat_test_t *all_checks[] = { /* 1 */ &lat_u_0, /* 2 */ &lat_u_01, + /* 3 */ &lat_u_02, /* 4 */ &lat_u_03, + /* 5 */ &lat_d_0, /* 6 */ &lat_d_01, + /* 7 */ &lat_d_02, /* 8 */ &lat_d_03, + /* 9 */ &lat_u_1, /* 10 */ &lat_u_1x1, + /* 11 */ &lat_u_2, /* 12 */ &lat_u_2x1, + /* 13 */ &lat_u_2x2, /* 14 */ &lat_u_1, + /* 15 */ &lat_u_1x1, /* 16 */ &lat_u_2, + /* 17 */ &lat_u_2x1, /* 18 */ &lat_uc_2x2, + /* 19 */ &lat_dc_1, /* 20 */ &lat_dc_1x1, + /* 21 */ &lat_dc_2, /* 22 */ &lat_dc_2x1, + /* 23 */ &lat_dc_2x2,/* 24 */ &lat_d_1, + /* 25 */ &lat_d_1x1, /* 26 */ &lat_d_2, + /* 27 */ &lat_d_2x1, /* 28 */ &lat_d_2x2, + /* 29 */ &lat_dc_2x2,/* 30 */ &lat_d_1, + /* 31 */ &lat_d_1x1, /* 32 */ &lat_d_2, + /* 33 */ &lat_d_2x1, /* 34 */ &lat_d_2x2, + 0 + }; + +int check_lattice_properties(const igraph_t *lattice) { + igraph_bool_t res; + + /* Connected */ + igraph_is_connected(lattice, &res, IGRAPH_WEAK); + if (!res && igraph_vcount(lattice) > 0) { + printf("Not connected\n"); + return 1; + } + + /* Simple */ + igraph_is_simple(lattice, &res); + if (!res) { + printf("Not simple\n"); + return 2; + } + + return 0; +} + +int check_lattice(const lat_test_t *test) { + igraph_t graph, othergraph; + igraph_vector_int_t otheredges; + igraph_vector_int_t dimvector; + igraph_vector_bool_t periodic; + igraph_bool_t iso; + int ret; + + /* Create lattice */ + igraph_vector_int_view(&dimvector, test->dimedges, test->dim); + igraph_vector_bool_init(&periodic, test->dim); + igraph_vector_bool_fill(&periodic, test->periodic); + igraph_square_lattice( + &graph, &dimvector, test->nei, test->directed, test->mutual, &periodic + ); + igraph_vector_bool_destroy(&periodic); + + /* Check its properties */ + if ((ret = check_lattice_properties(&graph))) { + igraph_destroy(&graph); + printf("Lattice properties are not satisfied\n"); + return ret; + } + + /* Check that it is isomorphic to the stored graph */ + igraph_vector_int_view(&otheredges, test->dimedges + test->dim, test->m * 2); + igraph_create(&othergraph, &otheredges, igraph_vector_int_prod(&dimvector), + test->directed); + igraph_isomorphic(&graph, &othergraph, &iso); + if (!iso) { + printf("--\n"); + print_graph(&graph); + printf("--\n"); + print_graph(&othergraph); + igraph_destroy(&graph); + igraph_destroy(&othergraph); + return 50; + } + + igraph_destroy(&graph); + igraph_destroy(&othergraph); + return 0; +} + +int main(void) { + int i, ret; + + i = 0; + while (all_checks[i]) { + if ((ret = check_lattice(all_checks[i]))) { + printf("Check no #%d failed.\n", (int) (i + 1)); + return ret; + } + i++; + } + + VERIFY_FINALLY_STACK(); + + /* Compare to the hypercube graph. */ + { + igraph_t g1, g2; + igraph_vector_int_t dims; + igraph_bool_t iso; + + /* Q_1 is the singleton graph */ + + igraph_hypercube(&g1, 0, IGRAPH_UNDIRECTED); + IGRAPH_ASSERT(igraph_vcount(&g1) == 1); + IGRAPH_ASSERT(igraph_ecount(&g1) == 0); + IGRAPH_ASSERT(!igraph_is_directed(&g1)); + igraph_destroy(&g1); + + /* Q_4 undirected */ + + igraph_vector_int_init(&dims, 4); + igraph_vector_int_fill(&dims, 2); + + igraph_square_lattice(&g2, &dims, 1, IGRAPH_UNDIRECTED, false, NULL); + igraph_hypercube(&g1, 4, IGRAPH_UNDIRECTED); + igraph_isomorphic(&g1, &g2, &iso); + IGRAPH_ASSERT(iso); + igraph_destroy(&g1); + igraph_destroy(&g2); + + igraph_vector_int_destroy(&dims); + + /* Q_5 directed */ + + igraph_vector_int_init(&dims, 5); + igraph_vector_int_fill(&dims, 2); + + igraph_square_lattice(&g2, &dims, 1, IGRAPH_DIRECTED, false, NULL); + igraph_hypercube(&g1, 5, IGRAPH_DIRECTED); + igraph_isomorphic(&g1, &g2, &iso); + IGRAPH_ASSERT(iso); + igraph_destroy(&g1); + igraph_destroy(&g2); + + igraph_vector_int_destroy(&dims); + + CHECK_ERROR(igraph_hypercube(&g1, 128, false), IGRAPH_EINVAL); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_st_edge_connectivity.c b/tests/unit/igraph_st_edge_connectivity.c new file mode 100644 index 0000000..20a82de --- /dev/null +++ b/tests/unit/igraph_st_edge_connectivity.c @@ -0,0 +1,39 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_integer_t value; + + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 3, 4, 3, 5, 4, 5, -1); + + igraph_st_edge_connectivity(&g, &value, 0, 5); + + igraph_destroy(&g); + + IGRAPH_ASSERT(value == 2); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_st_mincut.c b/tests/unit/igraph_st_mincut.c new file mode 100644 index 0000000..fda5fe0 --- /dev/null +++ b/tests/unit/igraph_st_mincut.c @@ -0,0 +1,70 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void sort_and_print(igraph_vector_int_t *vec) { + igraph_vector_int_sort(vec); + print_vector_int(vec); +} + +int main(void) { + + igraph_t g; + igraph_vector_int_t cut, partition, partition2; + igraph_vector_t capacity; + igraph_real_t value; + int source = 0; + int target = 4; + + igraph_vector_int_init(&partition, 0); + igraph_vector_int_init(&partition2, 0); + igraph_vector_int_init(&cut, 0); + + igraph_small(&g, 5, IGRAPH_DIRECTED, 0, 1, 1, 2, 1, 3, 2, 4, 3, 4, -1); + igraph_vector_init_int_end(&capacity, -1, 8, 2, 3, 3, 2, -1); + + /* test without capacity */ + + igraph_st_mincut(&g, &value, &cut, &partition, &partition2, source, target, /*capacity*/ NULL); + + /* cut and partition should have only one element */ + print_vector_int(&cut); + print_vector_int(&partition); + sort_and_print(&partition2); + + /* test with capacity */ + + igraph_st_mincut(&g, &value, &cut, &partition, &partition2, source, target, &capacity); + + sort_and_print(&cut); + sort_and_print(&partition); + sort_and_print(&partition2); + + /* cleanup */ + + igraph_vector_int_destroy(&cut); + igraph_vector_int_destroy(&partition); + igraph_vector_int_destroy(&partition2); + igraph_vector_destroy(&capacity); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_st_mincut.out b/tests/unit/igraph_st_mincut.out new file mode 100644 index 0000000..5fbe789 --- /dev/null +++ b/tests/unit/igraph_st_mincut.out @@ -0,0 +1,6 @@ +( 0 ) +( 0 ) +( 1 2 3 4 ) +( 1 4 ) +( 0 1 3 ) +( 2 4 ) diff --git a/tests/unit/igraph_st_mincut_value.c b/tests/unit/igraph_st_mincut_value.c new file mode 100644 index 0000000..8129c75 --- /dev/null +++ b/tests/unit/igraph_st_mincut_value.c @@ -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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_vector_t capacity; + igraph_real_t value; + + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 3, 4, 3, 5, 4, 5, -1); + igraph_vector_init_int_end(&capacity, -1, 5, 2, 2, 3, 4, 1, 2, 5, -1); + + igraph_st_mincut_value(&g, &value, 0, 5, &capacity); + + igraph_vector_destroy(&capacity); + igraph_destroy(&g); + + IGRAPH_ASSERT(value == 7); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_st_vertex_connectivity.c b/tests/unit/igraph_st_vertex_connectivity.c new file mode 100644 index 0000000..6d8b40a --- /dev/null +++ b/tests/unit/igraph_st_vertex_connectivity.c @@ -0,0 +1,86 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g, igraph_integer_t source, igraph_integer_t target, igraph_vconn_nei_t neighbors) { + igraph_integer_t res; + igraph_st_vertex_connectivity(g, &res, source, target, neighbors); + printf("%" IGRAPH_PRId "\n", res); + igraph_destroy(g); +} + +int main(void) { + igraph_t g; + igraph_integer_t res; + + printf("graph with two unconnected vertices: "); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, -1); + print_and_destroy(&g, 0, 1, IGRAPH_VCONN_NEI_ERROR); + + printf("graph with two connected vertices, IGRAPH_VCONN_NEI_NEGATIVE: "); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + print_and_destroy(&g, 0, 1, IGRAPH_VCONN_NEI_NEGATIVE); + + printf("graph with two connected vertices, IGRAPH_VCONN_NEI_NUMBER_OF_NODES: "); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + print_and_destroy(&g, 0, 1, IGRAPH_VCONN_NEI_NUMBER_OF_NODES); + + printf("graph with two connected vertices, IGRAPH_VCONN_NEI_IGNORE: "); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, 0,1, 0,1, -1); + print_and_destroy(&g, 0, 1, IGRAPH_VCONN_NEI_IGNORE); + + printf("directed graph with two connected vertices, IGRAPH_VCONN_NEI_IGNORE: "); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, 0,1, 0,1, 1,0, 1,0, -1); + print_and_destroy(&g, 0, 1, IGRAPH_VCONN_NEI_IGNORE); + + printf("line graph with 6 vertices: "); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,3, 3,4, 4,5, -1); + print_and_destroy(&g, 0, 5, IGRAPH_VCONN_NEI_ERROR); + + printf("full graph with 6 vertices IGRAPH_VCONN_NEI_IGNORE: "); + igraph_full(&g, 6, IGRAPH_UNDIRECTED, 0); + print_and_destroy(&g, 0, 1, IGRAPH_VCONN_NEI_IGNORE); + + printf("full graph with 6 vertices IGRAPH_VCONN_NEI_IGNORE, directed: "); + igraph_full(&g, 6, IGRAPH_DIRECTED, 0); + print_and_destroy(&g, 0, 1, IGRAPH_VCONN_NEI_IGNORE); + + printf("line graph with 3 vertices, 6 edges: "); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, 0,1, 0,1, 1,2, 1,2, 1,2, 1,2, -1); + print_and_destroy(&g, 0, 2, IGRAPH_VCONN_NEI_ERROR); + + VERIFY_FINALLY_STACK(); + + printf("check error on graph with two connected vertices.\n"); + igraph_small(&g, 2, IGRAPH_UNDIRECTED, 0,1, -1); + CHECK_ERROR(igraph_st_vertex_connectivity(&g, &res, 0, 0, IGRAPH_VCONN_NEI_ERROR), IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("check error on graph with no vertices.\n"); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, -1); + CHECK_ERROR(igraph_st_vertex_connectivity(&g, &res, 0, 0, IGRAPH_VCONN_NEI_ERROR), IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("check error on graph with one vertex.\n"); + igraph_small(&g, 1, IGRAPH_UNDIRECTED, -1); + CHECK_ERROR(igraph_st_vertex_connectivity(&g, &res, 0, 0, IGRAPH_VCONN_NEI_ERROR), IGRAPH_EINVAL); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); +} diff --git a/tests/unit/igraph_st_vertex_connectivity.out b/tests/unit/igraph_st_vertex_connectivity.out new file mode 100644 index 0000000..d2b1ae3 --- /dev/null +++ b/tests/unit/igraph_st_vertex_connectivity.out @@ -0,0 +1,12 @@ +graph with two unconnected vertices: 0 +graph with two connected vertices, IGRAPH_VCONN_NEI_NEGATIVE: -1 +graph with two connected vertices, IGRAPH_VCONN_NEI_NUMBER_OF_NODES: 2 +graph with two connected vertices, IGRAPH_VCONN_NEI_IGNORE: 0 +directed graph with two connected vertices, IGRAPH_VCONN_NEI_IGNORE: 0 +line graph with 6 vertices: 1 +full graph with 6 vertices IGRAPH_VCONN_NEI_IGNORE: 4 +full graph with 6 vertices IGRAPH_VCONN_NEI_IGNORE, directed: 4 +line graph with 3 vertices, 6 edges: 1 +check error on graph with two connected vertices. +check error on graph with no vertices. +check error on graph with one vertex. diff --git a/tests/unit/igraph_static_power_law_game.c b/tests/unit/igraph_static_power_law_game.c new file mode 100644 index 0000000..758ddd2 --- /dev/null +++ b/tests/unit/igraph_static_power_law_game.c @@ -0,0 +1,113 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 42); + + printf("No vertices:\n"); + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 0, /*number of edges*/ 0, + /*exponent_out*/ 2.0, /*exponent in*/ 2.0, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("No edges, undirected:\n"); + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 10, /*number of edges*/ 0, + /*exponent_out*/ 2.0, /*exponent in*/ -2.0, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_SUCCESS); + print_graph_canon(&g); + igraph_destroy(&g); + + printf("Checking some basic outputs.\n"); + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 100, /*number of edges*/ 30, + /*exponent_out*/ 2.0, /*exponent in*/ -2.0, IGRAPH_LOOPS, IGRAPH_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100); + IGRAPH_ASSERT(igraph_ecount(&g) == 30); + igraph_destroy(&g); + + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 90, /*number of edges*/ 40, + /*exponent_out*/ 2.0, /*exponent in*/ -2.0, IGRAPH_LOOPS, IGRAPH_NO_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 90); + IGRAPH_ASSERT(igraph_ecount(&g) == 40); + igraph_destroy(&g); + + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 110, /*number of edges*/ 50, + /*exponent_out*/ 2.0, /*exponent in*/ -2.0, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 110); + IGRAPH_ASSERT(igraph_ecount(&g) == 50); + igraph_destroy(&g); + + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 100, /*number of edges*/ 30, + /*exponent_out*/ 2.0, /*exponent in*/ 2.0, IGRAPH_LOOPS, IGRAPH_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 100); + IGRAPH_ASSERT(igraph_ecount(&g) == 30); + igraph_destroy(&g); + + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 90, /*number of edges*/ 40, + /*exponent_out*/ 2.0, /*exponent in*/ 2.2, IGRAPH_LOOPS, IGRAPH_NO_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 90); + IGRAPH_ASSERT(igraph_ecount(&g) == 40); + igraph_destroy(&g); + + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 110, /*number of edges*/ 50, + /*exponent_out*/ 2.0, /*exponent in*/ 2.5, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&g) == 110); + IGRAPH_ASSERT(igraph_ecount(&g) == 50); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Negative number of vertices.\n"); + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ -100, /*number of edges*/ 30, + /*exponent_out*/ 2.0, /*exponent in*/ -2.0, IGRAPH_LOOPS, IGRAPH_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("Negative number of edges.\n"); + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 100, /*number of edges*/ -30, + /*exponent_out*/ 2.0, /*exponent in*/ -2.0, IGRAPH_LOOPS, IGRAPH_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("Exponent out too low.\n"); + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 100, /*number of edges*/ 30, + /*exponent_out*/ 1.0, /*exponent in*/ -2.0, IGRAPH_LOOPS, IGRAPH_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_EINVAL); + igraph_destroy(&g); + + printf("Exponent in too low but not negative.\n"); + IGRAPH_ASSERT(igraph_static_power_law_game(&g, /*number of vertices*/ 100, /*number of edges*/ 30, + /*exponent_out*/ 2.0, /*exponent in*/ 0.5, IGRAPH_LOOPS, IGRAPH_MULTIPLE, + /*finite_size_correction*/ true) == IGRAPH_EINVAL); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_static_power_law_game.out b/tests/unit/igraph_static_power_law_game.out new file mode 100644 index 0000000..db040f4 --- /dev/null +++ b/tests/unit/igraph_static_power_law_game.out @@ -0,0 +1,15 @@ +No vertices: +directed: true +vcount: 0 +edges: { +} +No edges, undirected: +directed: false +vcount: 10 +edges: { +} +Checking some basic outputs. +Negative number of vertices. +Negative number of edges. +Exponent out too low. +Exponent in too low but not negative. diff --git a/tests/unit/igraph_stochastic_imitation.c b/tests/unit/igraph_stochastic_imitation.c new file mode 100644 index 0000000..3b537ce --- /dev/null +++ b/tests/unit/igraph_stochastic_imitation.c @@ -0,0 +1,57 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g, h; + igraph_vector_t quant; + igraph_vector_int_t strat; + + /* nonempty graph */ + igraph_small(&g, /*n vertices*/ 0, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + igraph_empty(&h, 0, 0); /* empty graph */ + igraph_vector_init(&quant, 1); /* quantities vector */ + igraph_vector_int_init(&strat, 2); /* strategies vector */ + + /* test parameters */ + /*graph--vertex--algo--quantities--strategies--mode--retval*/ + /* null pointer for graph */ + CHECK_ERROR(igraph_roulette_wheel_imitation(NULL, 0, IGRAPH_IMITATE_BLIND, NULL, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + /* null pointer for quantities vector */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, IGRAPH_IMITATE_BLIND, NULL, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + /* null pointer for strategies vector */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, IGRAPH_IMITATE_BLIND, &quant, NULL, IGRAPH_ALL), IGRAPH_EINVAL); + /* empty graph */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&h, 0, IGRAPH_IMITATE_BLIND, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL); + /* length of quantities vector different from number of vertices */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, IGRAPH_IMITATE_BLIND, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL); + /* length of strategies vector different from number of vertices */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, IGRAPH_IMITATE_BLIND, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL); + /* unknown algorithm */ + CHECK_ERROR(igraph_roulette_wheel_imitation(&g, 0, (igraph_imitate_algorithm_t) -1, &quant, &strat, IGRAPH_ALL), IGRAPH_EINVAL); + + igraph_destroy(&g); + igraph_destroy(&h); + igraph_vector_destroy(&quant); + igraph_vector_int_destroy(&strat); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_strvector.c b/tests/unit/igraph_strvector.c new file mode 100644 index 0000000..3ed860d --- /dev/null +++ b/tests/unit/igraph_strvector.c @@ -0,0 +1,183 @@ +/* -*- 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, see . +*/ + +#include + +#include "test_utilities.h" + +void strvector_print(const igraph_strvector_t *sv) { + igraph_integer_t i, s = igraph_strvector_size(sv); + for (i = 0; i < s; i++) { + printf("\"%s\"\n", igraph_strvector_get(sv, i)); + } + printf("\n"); +} + +int main(void) { + + igraph_strvector_t sv1, sv2, sv3, sv4; + + printf("igraph_strvector_init, igraph_strvector_destroy\n"); + igraph_strvector_init(&sv1, 10); + igraph_strvector_destroy(&sv1); + igraph_strvector_init(&sv1, 0); + igraph_strvector_destroy(&sv1); + + printf("igraph_strvector_get, igraph_strvector_set\n"); + igraph_strvector_init(&sv1, 5); + strvector_print(&sv1); + igraph_strvector_set(&sv1, 0, "zero"); + igraph_strvector_set(&sv1, 1, "one"); + igraph_strvector_set(&sv1, 2, "two"); + igraph_strvector_set(&sv1, 3, "three"); + igraph_strvector_set(&sv1, 4, "four"); + strvector_print(&sv1); + + printf("igraph_strvector_remove_section\n"); + igraph_strvector_remove_section(&sv1, 0, 5); + if (igraph_strvector_size(&sv1) != 0) { + return 1; + } + printf("resize to 10 and then back to 5\n"); + igraph_strvector_resize(&sv1, 10); + igraph_strvector_set(&sv1, 0, "zero"); + igraph_strvector_set(&sv1, 1, "one"); + igraph_strvector_set(&sv1, 2, "two"); + igraph_strvector_set(&sv1, 3, "three"); + igraph_strvector_set(&sv1, 4, "four"); + igraph_strvector_resize(&sv1, 5); + strvector_print(&sv1); + printf("resize to 0\n"); + igraph_strvector_resize(&sv1, 0); + if (igraph_strvector_size(&sv1) != 0) { + return 1; + } + printf("resize to 10 and then back to 5 again\n"); + igraph_strvector_resize(&sv1, 10); + igraph_strvector_set(&sv1, 0, "zero"); + igraph_strvector_set(&sv1, 1, "one"); + igraph_strvector_set(&sv1, 2, "two"); + igraph_strvector_set(&sv1, 3, "three"); + igraph_strvector_set(&sv1, 4, "four"); + igraph_strvector_resize(&sv1, 5); + strvector_print(&sv1); + + printf("igraph_strvector_copy\n"); + igraph_strvector_init_copy(&sv2, &sv1); + strvector_print(&sv1); + + igraph_strvector_resize(&sv1, 0); + igraph_strvector_destroy(&sv2); + igraph_strvector_init_copy(&sv2, &sv1); + if (igraph_strvector_size(&sv2) != 0) { + return 2; + } + igraph_strvector_destroy(&sv2); + + printf("igraph_strvector_push_back\n"); + igraph_strvector_push_back(&sv1, "zeroth"); + igraph_strvector_push_back(&sv1, "first"); + igraph_strvector_push_back(&sv1, "second"); + igraph_strvector_push_back(&sv1, "third"); + igraph_strvector_push_back(&sv1, "fourth"); + strvector_print(&sv1); + + printf("igraph_strvector_push_back_len\n"); + igraph_strvector_push_back_len(&sv1, "extra zeroth", 5); + igraph_strvector_push_back_len(&sv1, "extra first", 100); + igraph_strvector_push_back_len(&sv1, "extra second", 0); + strvector_print(&sv1); + igraph_strvector_destroy(&sv1); + + printf("igraph_strvector_append\n"); + printf("===\n"); + igraph_strvector_init(&sv1, 0); + igraph_strvector_init(&sv2, 0); + igraph_strvector_append(&sv1, &sv2); + strvector_print(&sv1); + printf("===\n"); + + igraph_strvector_resize(&sv1, 3); + igraph_strvector_append(&sv1, &sv2); + strvector_print(&sv1); + printf("===\n"); + + igraph_strvector_append(&sv2, &sv1); + strvector_print(&sv2); + printf("===\n"); + + igraph_strvector_set(&sv1, 0, "0"); + igraph_strvector_set(&sv1, 1, "1"); + igraph_strvector_set(&sv1, 2, "2"); + igraph_strvector_set(&sv2, 0, "3"); + igraph_strvector_set(&sv2, 1, "4"); + igraph_strvector_set(&sv2, 2, "5"); + igraph_strvector_append(&sv1, &sv2); + strvector_print(&sv1); + + igraph_strvector_destroy(&sv1); + igraph_strvector_destroy(&sv2); + + printf("igraph_strvector_merge\n"); + igraph_strvector_init(&sv1, 3); + igraph_strvector_set(&sv1, 0, "zero"); + igraph_strvector_set(&sv1, 1, "one"); + igraph_strvector_set(&sv1, 2, "two"); + + igraph_strvector_init(&sv2, 2); + igraph_strvector_set(&sv2, 0, "a"); + igraph_strvector_set(&sv2, 1, "b"); + + igraph_strvector_init_copy(&sv3, &sv1); + igraph_strvector_init_copy(&sv4, &sv2); + + igraph_strvector_merge(&sv1, &sv2); + IGRAPH_ASSERT(igraph_strvector_size(&sv2) == 0); + + igraph_strvector_append(&sv3, &sv4); + IGRAPH_ASSERT(igraph_strvector_size(&sv1) == igraph_strvector_size(&sv3)); + + for (igraph_integer_t i=0; i < igraph_strvector_size(&sv1); ++i) { + IGRAPH_ASSERT(strcmp(igraph_strvector_get(&sv1, i), igraph_strvector_get(&sv3, i)) == 0); + } + + igraph_strvector_destroy(&sv1); + igraph_strvector_destroy(&sv2); + igraph_strvector_destroy(&sv3); + igraph_strvector_destroy(&sv4); + + printf("clear\n"); + igraph_strvector_init(&sv1, 3); + igraph_strvector_set(&sv1, 0, "0"); + igraph_strvector_set(&sv1, 1, "1"); + igraph_strvector_set(&sv1, 2, "2"); + igraph_strvector_clear(&sv1); + if (igraph_strvector_size(&sv1) != 0) { + return 3; + } + igraph_strvector_resize(&sv1, 4); + strvector_print(&sv1); + igraph_strvector_set(&sv1, 0, "one"); + igraph_strvector_set(&sv1, 2, "two"); + strvector_print(&sv1); + igraph_strvector_destroy(&sv1); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_strvector.out b/tests/unit/igraph_strvector.out new file mode 100644 index 0000000..94f8e66 --- /dev/null +++ b/tests/unit/igraph_strvector.out @@ -0,0 +1,87 @@ +igraph_strvector_init, igraph_strvector_destroy +igraph_strvector_get, igraph_strvector_set +"" +"" +"" +"" +"" + +"zero" +"one" +"two" +"three" +"four" + +igraph_strvector_remove_section +resize to 10 and then back to 5 +"zero" +"one" +"two" +"three" +"four" + +resize to 0 +resize to 10 and then back to 5 again +"zero" +"one" +"two" +"three" +"four" + +igraph_strvector_copy +"zero" +"one" +"two" +"three" +"four" + +igraph_strvector_push_back +"zeroth" +"first" +"second" +"third" +"fourth" + +igraph_strvector_push_back_len +"zeroth" +"first" +"second" +"third" +"fourth" +"extra" +"extra first" +"" + +igraph_strvector_append +=== + +=== +"" +"" +"" + +=== +"" +"" +"" + +=== +"0" +"1" +"2" +"3" +"4" +"5" + +igraph_strvector_merge +clear +"" +"" +"" +"" + +"one" +"" +"two" +"" + diff --git a/tests/unit/igraph_subcomponent.c b/tests/unit/igraph_subcomponent.c new file mode 100644 index 0000000..5ee68c3 --- /dev/null +++ b/tests/unit/igraph_subcomponent.c @@ -0,0 +1,81 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, igraph_integer_t vertex, igraph_neimode_t mode) { + igraph_vector_int_t result; + igraph_vector_int_init(&result, 0); + IGRAPH_ASSERT(igraph_subcomponent(graph, &result, vertex, mode) == IGRAPH_SUCCESS); + igraph_vector_int_sort(&result); + igraph_vector_int_print(&result); + igraph_vector_int_destroy(&result); + printf("\n"); +} + + +int main(void) { + igraph_t g_0, g_1, g_lm, g_lmu; + igraph_vector_int_t result; + igraph_vector_int_init(&result, 0); + igraph_integer_t i; + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_lm, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + igraph_small(&g_lmu, 6, 0, 0,1, 0,2, 1,1, 1,3, 2,0, 2,0, 2,3, 3,4, 3,4, -1); + + printf("No vertices, should give error for impossible starting vertex.\n"); + CHECK_ERROR(igraph_subcomponent(&g_0, &result, 0, IGRAPH_ALL), IGRAPH_EINVVID); + + printf("One vertex.\n"); + call_and_print(&g_1, 0, IGRAPH_ALL); + + printf("All vertices of a graph, IGRAPH_OUT:\n"); + for (i = 0; i < 6; i++) { + call_and_print(&g_lm, i, IGRAPH_OUT); + } + + printf("All vertices of a graph, IGRAPH_IN:\n"); + for (i = 0; i < 6; i++) { + call_and_print(&g_lm, i, IGRAPH_IN); + } + + printf("All vertices of a graph, IGRAPH_ALL:\n"); + for (i = 0; i < 6; i++) { + call_and_print(&g_lm, i, IGRAPH_ALL); + } + + printf("All vertices of a graph, undirected:\n"); + for (i = 0; i < 6; i++) { + call_and_print(&g_lmu, i, IGRAPH_OUT); + } + + printf("Check for invalid mode error handling.\n"); + CHECK_ERROR(igraph_subcomponent(&g_1, &result, 0, (igraph_neimode_t) 100), IGRAPH_EINVMODE); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_lm); + igraph_destroy(&g_lmu); + igraph_vector_int_destroy(&result); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_subcomponent.out b/tests/unit/igraph_subcomponent.out new file mode 100644 index 0000000..a97a075 --- /dev/null +++ b/tests/unit/igraph_subcomponent.out @@ -0,0 +1,57 @@ +No vertices, should give error for impossible starting vertex. +One vertex. +0 + +All vertices of a graph, IGRAPH_OUT: +0 1 2 3 4 + +1 3 4 + +0 1 2 3 4 + +3 4 + +4 + +5 + +All vertices of a graph, IGRAPH_IN: +0 2 + +0 1 2 + +0 2 + +0 1 2 3 + +0 1 2 3 4 + +5 + +All vertices of a graph, IGRAPH_ALL: +0 1 2 3 4 + +0 1 2 3 4 + +0 1 2 3 4 + +0 1 2 3 4 + +0 1 2 3 4 + +5 + +All vertices of a graph, undirected: +0 1 2 3 4 + +0 1 2 3 4 + +0 1 2 3 4 + +0 1 2 3 4 + +0 1 2 3 4 + +5 + +Check for invalid mode error handling. diff --git a/tests/unit/igraph_subisomorphic.c b/tests/unit/igraph_subisomorphic.c new file mode 100644 index 0000000..8365072 --- /dev/null +++ b/tests/unit/igraph_subisomorphic.c @@ -0,0 +1,64 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + /*igraph_subisomorphic now calls igraph_subisomorphic_vf2, most of + the testing is done through calling that directly*/ + igraph_t g1, g2; + igraph_bool_t result; + + printf("No vertices.\n"); + igraph_small(&g1, 0, 0, -1); + igraph_small(&g2, 0, 0, -1); + IGRAPH_ASSERT(igraph_subisomorphic(&g1, &g2, &result) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(result); + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("Basic positive example, undirected.\n"); + igraph_small(&g1, 4, 0, 0,1, 1,2, 3,2, 3,1, -1); + igraph_small(&g2, 3, 0, 0,1, 1,2, 2,0, -1); + IGRAPH_ASSERT(igraph_subisomorphic(&g1, &g2, &result) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(result); + igraph_destroy(&g1); + igraph_destroy(&g2); + + printf("Basic negative example, directed.\n"); + igraph_small(&g1, 4, 1, 0,1, 1,2, 3,2, 3,1, -1); + igraph_small(&g2, 3, 1, 0,1, 1,2, 2,0, -1); + IGRAPH_ASSERT(igraph_subisomorphic(&g1, &g2, &result) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(!result); + igraph_destroy(&g1); + igraph_destroy(&g2); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Mismatching directedness.\n"); + igraph_small(&g1, 4, 1, 0,1, 1,2, 3,2, 3,1, -1); + igraph_small(&g2, 3, 0, 0,1, 1,2, 2,0, -1); + IGRAPH_ASSERT(igraph_subisomorphic(&g1, &g2, &result) == IGRAPH_EINVAL); + igraph_destroy(&g1); + igraph_destroy(&g2); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_subisomorphic_lad.c b/tests/unit/igraph_subisomorphic_lad.c new file mode 100644 index 0000000..1db8cbf --- /dev/null +++ b/tests/unit/igraph_subisomorphic_lad.c @@ -0,0 +1,196 @@ +/* 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, see . +*/ + +#include +#include "test_utilities.h" + +/* This test counts motifs using LAD and compares the results with + * the RANDESU motif finder */ +void test_k_motifs(const igraph_t *graph, const int k, const int class_count, igraph_bool_t directed) { + igraph_vector_t randesu_counts, lad_counts; + igraph_vector_t cut_prob; + igraph_bool_t equal; + igraph_integer_t i, n; + igraph_integer_t vcount; + igraph_real_t expected_count; + + vcount = igraph_vcount(graph); + + n = class_count; + + igraph_vector_init(&lad_counts, n); + + for (i = 0; i < n; i++) { + igraph_t pattern; + igraph_vector_int_list_t maps; + igraph_integer_t nAutomorphisms; + + igraph_isoclass_create(&pattern, k, i, directed); + igraph_vector_int_list_init(&maps, 0); + + igraph_subisomorphic_lad(&pattern, graph, NULL, NULL, NULL, &maps, /* induced = */ true, 0); + + igraph_count_subisomorphisms_vf2(&pattern, &pattern, NULL, NULL, NULL, NULL, &nAutomorphisms, NULL, NULL, NULL); + + VECTOR(lad_counts)[i] = igraph_vector_int_list_size(&maps) / nAutomorphisms; + + igraph_vector_int_list_destroy(&maps); + + igraph_destroy(&pattern); + } + + igraph_vector_init(&cut_prob, k); + igraph_vector_init(&randesu_counts, 0); + igraph_motifs_randesu(graph, &randesu_counts, k, &cut_prob); + + equal = 1 /* true */; + for (i = 0; i < n; i++) { + if (isnan(VECTOR(randesu_counts)[i])) { + continue; + } + if (VECTOR(randesu_counts)[i] != VECTOR(lad_counts)[i]) { + equal = 0; + break; + } + } + + if (! equal) { + printf("LAD %s %d-motif count does not agree with RANDESU.\n", directed ? "directed" : "undirected", k); + } + + expected_count = 1; + for (i = 0; i < k; i++) { + expected_count *= (vcount - i); + } + for (i = 0; i < k; i++) { + expected_count /= (i + 1); + } + if (igraph_vector_sum(&lad_counts) != expected_count) { + printf("Total %d-vertex %s subgraph count is incorrect.\n", k, directed ? "directed" : "undirected"); + } + + igraph_vector_destroy(&randesu_counts); + igraph_vector_destroy(&lad_counts); + igraph_vector_destroy(&cut_prob); +} + +void test_motifs(void) { + igraph_t graph; + igraph_integer_t count; + + igraph_rng_seed(igraph_rng_default(), 42); + + /* The graph is chosen to have approximately 50% density + * so that most motifs have a high chance of appearing. */ + igraph_erdos_renyi_game_gnm(&graph, 30, 400, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + + igraph_graph_count(3, IGRAPH_DIRECTED, &count); + test_k_motifs(&graph, 3, count, IGRAPH_DIRECTED); + + igraph_graph_count(4, IGRAPH_DIRECTED, &count); + test_k_motifs(&graph, 4, count, IGRAPH_DIRECTED); + + igraph_destroy(&graph); +} + +void test_motifs_undirected(void) { + igraph_t graph; + igraph_integer_t count; + + igraph_rng_seed(igraph_rng_default(), 137); + + /* The graph is chosen to have slightly higher than 50% density + * so that most connected motifs have a high chance of appearing. */ + igraph_erdos_renyi_game_gnm(&graph, 18, 80, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_graph_count(3, IGRAPH_UNDIRECTED, &count); + test_k_motifs(&graph, 3, count, IGRAPH_UNDIRECTED); + + igraph_graph_count(4, IGRAPH_UNDIRECTED, &count); + test_k_motifs(&graph, 4, count, IGRAPH_UNDIRECTED); + + igraph_destroy(&graph); + + /* Use a smaller graph so that the test would not take too long. + * The graph is chosen to have slightly higher than 50% density + * so that most connected motifs have a high chance of appearing. */ + igraph_erdos_renyi_game_gnm(&graph, 12, 36, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_graph_count(5, IGRAPH_UNDIRECTED, &count); + test_k_motifs(&graph, 5, count, IGRAPH_UNDIRECTED); + + igraph_graph_count(6, IGRAPH_UNDIRECTED, &count); + test_k_motifs(&graph, 6, count, IGRAPH_UNDIRECTED); + + igraph_destroy(&graph); +} + + +int main(void) { + igraph_t pattern, target; + igraph_bool_t iso; + igraph_vector_int_t map; + igraph_vector_int_list_t maps; + + igraph_vector_int_init(&map, 0); + igraph_vector_int_list_init(&maps, 0); + + igraph_small(&target, 9, IGRAPH_UNDIRECTED, + 0, 1, 0, 4, 0, 6, + 1, 4, 1, 2, + 2, 3, + 3, 4, 3, 5, 3, 7, 3, 8, + 4, 5, 4, 6, + 5, 6, 5, 8, + 7, 8, + -1); + + igraph_small(&pattern, 0, IGRAPH_UNDIRECTED, -1); + igraph_subisomorphic_lad(&pattern, &target, /*domains=*/ NULL, &iso, &map, &maps, + /*induced=*/ false, /*time_limit=*/ 0); + + IGRAPH_ASSERT(iso); + IGRAPH_ASSERT(igraph_vector_int_size(&map) == 0); + IGRAPH_ASSERT(igraph_vector_int_list_size(&maps) == 1); + IGRAPH_ASSERT(igraph_vector_int_size(igraph_vector_int_list_get_ptr(&maps, 0)) == 0); + + igraph_destroy(&pattern); + igraph_destroy(&target); + + igraph_vector_int_destroy(&map); + igraph_vector_int_list_destroy(&maps); + + + /* Check error: pattern and target differ in directedness */ + igraph_vector_int_init(&map, 0); + igraph_vector_int_list_init(&maps, 0); + igraph_small(&pattern, 0, IGRAPH_DIRECTED, -1); + CHECK_ERROR(igraph_subisomorphic_lad(&pattern, &target, /*domains=*/ 0, + &iso, &map, &maps, /*induced=*/ 0, + /*time_limit=*/ 0), IGRAPH_EINVAL); + igraph_vector_int_destroy(&map); + igraph_vector_int_list_destroy(&maps); + igraph_destroy(&pattern); + igraph_destroy(&target); + + test_motifs(); + test_motifs_undirected(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_to_directed.c b/tests/unit/igraph_to_directed.c new file mode 100644 index 0000000..04f227f --- /dev/null +++ b/tests/unit/igraph_to_directed.c @@ -0,0 +1,43 @@ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t ug; + igraph_t dg; + + igraph_small(&ug, 6, /* directed= */ 0, + 2,0, 1,4, 3,2, 2,3, 0,4, 5,0, 2,3, 5,3, 2,5, 0,1, + -1); + + igraph_copy(&dg, &ug); + printf("\nARBITRARY:\n"); + igraph_to_directed(&dg, IGRAPH_TO_DIRECTED_ARBITRARY); + print_graph(&dg); + igraph_destroy(&dg); + + igraph_copy(&dg, &ug); + printf("\nMUTUAL:\n"); + igraph_to_directed(&dg, IGRAPH_TO_DIRECTED_MUTUAL); + print_graph(&dg); + igraph_destroy(&dg); + + igraph_copy(&dg, &ug); + printf("\nACYCLIC:\n"); + igraph_to_directed(&dg, IGRAPH_TO_DIRECTED_ACYCLIC); + print_graph(&dg); + igraph_destroy(&dg); + + igraph_copy(&dg, &ug); + printf("\nRANDOM (edge count only):\n"); + igraph_to_directed(&dg, IGRAPH_TO_DIRECTED_RANDOM); + printf("%" IGRAPH_PRId "\n", igraph_ecount(&dg)); + igraph_destroy(&dg); + + igraph_destroy(&ug); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_to_directed.out b/tests/unit/igraph_to_directed.out new file mode 100644 index 0000000..6c0d17e --- /dev/null +++ b/tests/unit/igraph_to_directed.out @@ -0,0 +1,61 @@ + +ARBITRARY: +directed: true +vcount: 6 +edges: { +0 2 +1 4 +2 3 +2 3 +0 4 +0 5 +2 3 +3 5 +2 5 +0 1 +} + +MUTUAL: +directed: true +vcount: 6 +edges: { +0 2 +1 4 +2 3 +2 3 +0 4 +0 5 +2 3 +3 5 +2 5 +0 1 +2 0 +4 1 +3 2 +3 2 +4 0 +5 0 +3 2 +5 3 +5 2 +1 0 +} + +ACYCLIC: +directed: true +vcount: 6 +edges: { +0 2 +1 4 +2 3 +2 3 +0 4 +0 5 +2 3 +3 5 +2 5 +0 1 +} + +RANDOM (edge count only): +10 diff --git a/tests/unit/igraph_to_prufer.c b/tests/unit/igraph_to_prufer.c new file mode 100644 index 0000000..cfc6b77 --- /dev/null +++ b/tests/unit/igraph_to_prufer.c @@ -0,0 +1,172 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +igraph_bool_t test_from_prufer_back_to_prufer(void) { + igraph_t graph; + igraph_integer_t prufer[] = {2, 3, 2, 3}; + + igraph_vector_int_t expected_prufer, output_prufer; + + igraph_bool_t success = 0; + + igraph_vector_int_view(&expected_prufer, prufer, 4); + igraph_from_prufer(&graph, &expected_prufer); + + igraph_vector_int_init(&output_prufer, 4); + igraph_to_prufer(&graph, &output_prufer); + + success = igraph_vector_int_all_e(&expected_prufer, &output_prufer); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&output_prufer); + + return success; +} + +igraph_bool_t test_from_prufer_back_to_prufer_with_resize(void) { + igraph_t graph; + igraph_integer_t prufer[] = {0, 2, 4, 1, 1, 0}; + + igraph_vector_int_t expected_prufer, output_prufer; + + igraph_bool_t success; + + igraph_vector_int_view(&expected_prufer, prufer, 6); + igraph_from_prufer(&graph, &expected_prufer); + + igraph_vector_int_init(&output_prufer, 0); + igraph_to_prufer(&graph, &output_prufer); + + success = igraph_vector_int_all_e(&expected_prufer, &output_prufer); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&output_prufer); + + return success; +} + +igraph_bool_t test_from_prufer_back_to_prufer_with_resize2(void) { + igraph_t graph; + igraph_integer_t prufer[] = {2, 4, 5, 1, 3}; + + igraph_vector_int_t expected_prufer, output_prufer; + + igraph_bool_t success; + + igraph_vector_int_view(&expected_prufer, prufer, 5); + igraph_from_prufer(&graph, &expected_prufer); + + igraph_vector_int_init(&output_prufer, 0); + igraph_to_prufer(&graph, &output_prufer); + + + success = igraph_vector_int_all_e(&output_prufer, &expected_prufer); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&output_prufer); + + return success; +} + +igraph_error_t random_tree(igraph_integer_t size, igraph_t* tree, igraph_vector_int_t* prufer) { + igraph_integer_t i, j; + igraph_integer_t prufer_length; + + if (size < 0) { + IGRAPH_ERROR("Invalid size.", IGRAPH_EINVAL); + } + + if (size < 2) { + return igraph_empty(tree, size, IGRAPH_UNDIRECTED); + } + + prufer_length = size - 2; + IGRAPH_CHECK(igraph_vector_int_resize(prufer, prufer_length)); + + for (i = 0; i < prufer_length; ++i) { + j = RNG_INTEGER(0, size - 1); + VECTOR(*prufer)[i] = j; + } + + IGRAPH_CHECK(igraph_from_prufer(tree, prufer)); + + return IGRAPH_SUCCESS; +} + +igraph_bool_t test_from_random_prufer_back_to_prufer(int tree_size) { + igraph_t graph; + igraph_vector_int_t expected_prufer, output_prufer; + + igraph_bool_t success = 0; + igraph_integer_t random_seed = 4096; + + igraph_vector_int_init(&output_prufer, 0); + igraph_vector_int_init(&expected_prufer, 0); + + igraph_rng_seed(igraph_rng_default(), random_seed); + + random_tree(tree_size, &graph, &expected_prufer); + + igraph_to_prufer(&graph, &output_prufer); + + success = igraph_vector_int_all_e(&output_prufer, &expected_prufer); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&expected_prufer); + igraph_vector_int_destroy(&output_prufer); + + return success; +} + +#undef RUN_TEST /* from test_utilities.h */ + +int test_num = 0; +#define RUN_TEST(TEST) \ + test_num++; \ + if (!(TEST)) { \ + return test_num; \ + } + +int main(void) { + + RNG_BEGIN(); + + RUN_TEST(test_from_prufer_back_to_prufer()); + RUN_TEST(test_from_prufer_back_to_prufer_with_resize()); + RUN_TEST(test_from_prufer_back_to_prufer_with_resize2()); + RUN_TEST(test_from_random_prufer_back_to_prufer(10)); + RUN_TEST(test_from_random_prufer_back_to_prufer(100)); + RUN_TEST(test_from_random_prufer_back_to_prufer(1000)); + RUN_TEST(test_from_random_prufer_back_to_prufer(10000)); + + RNG_END(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_transitive_closure.c b/tests/unit/igraph_transitive_closure.c new file mode 100644 index 0000000..45a0011 --- /dev/null +++ b/tests/unit/igraph_transitive_closure.c @@ -0,0 +1,88 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +void print_closure(const igraph_t *graph) { + igraph_t closure; + igraph_bool_t simple; + + igraph_transitive_closure(graph, &closure); + print_graph_canon(&closure); + IGRAPH_ASSERT(igraph_vcount(graph) == igraph_vcount(&closure)); + IGRAPH_ASSERT(igraph_is_directed(graph) == igraph_is_directed(&closure)); + igraph_is_simple(&closure, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&closure); +} + +int main(void) { + igraph_t graph; + + printf("Directed null graph\n"); + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nUndirected null graph\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nDirected singleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_DIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nUndirected singleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nEdgeless graph\n"); + igraph_empty(&graph, 3, IGRAPH_DIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nSmall DAG\n"); + igraph_small(&graph, 9, IGRAPH_DIRECTED, + 8, 7, 7, 6, 6, 3, 6, 0, 3, 2, 3, 1, 5, 0, 4, 1, + -1); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nSmall directed multigraph with cycles \n"); + igraph_small(&graph, 10, IGRAPH_DIRECTED, + 0,1, 1,2, 2,0, 2,0, 0,3, 3,4, 4,3, 3,3, 0,5, 5,6, 6,6, 8,7, + -1); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nSmall undirected graph\n"); + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 3,4, + -1); + print_closure(&graph); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_transitive_closure.out b/tests/unit/igraph_transitive_closure.out new file mode 100644 index 0000000..087b94a --- /dev/null +++ b/tests/unit/igraph_transitive_closure.out @@ -0,0 +1,92 @@ +Directed null graph +directed: true +vcount: 0 +edges: { +} + +Undirected null graph +directed: false +vcount: 0 +edges: { +} + +Directed singleton graph +directed: true +vcount: 1 +edges: { +} + +Undirected singleton graph +directed: false +vcount: 1 +edges: { +} + +Edgeless graph +directed: true +vcount: 3 +edges: { +} + +Small DAG +directed: true +vcount: 9 +edges: { +3 1 +3 2 +4 1 +5 0 +6 0 +6 1 +6 2 +6 3 +7 0 +7 1 +7 2 +7 3 +7 6 +8 0 +8 1 +8 2 +8 3 +8 6 +8 7 +} + +Small directed multigraph with cycles +directed: true +vcount: 10 +edges: { +0 1 +0 2 +0 3 +0 4 +0 5 +0 6 +1 0 +1 2 +1 3 +1 4 +1 5 +1 6 +2 0 +2 1 +2 3 +2 4 +2 5 +2 6 +3 4 +4 3 +5 6 +8 7 +} + +Small undirected graph +directed: false +vcount: 6 +edges: { +0 1 +0 2 +1 2 +3 4 +} diff --git a/tests/unit/igraph_transitivity_avglocal_undirected.c b/tests/unit/igraph_transitivity_avglocal_undirected.c new file mode 100644 index 0000000..5d382e9 --- /dev/null +++ b/tests/unit/igraph_transitivity_avglocal_undirected.c @@ -0,0 +1,77 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g_0, g_1, g_simple, g_ml; + igraph_real_t result; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_simple, 6, 1, 0,1, 0,2, 1,2, 1,3, 2,3, 3,4, -1); + igraph_small(&g_ml, 6, 0, 0,1, 0,2, 1,1, 1,2, 1,3, 2,1, 2,3, 3,4, 3,4, -1); + + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("No vertices, transitivity zero:\n"); + IGRAPH_ASSERT(igraph_transitivity_avglocal_undirected(&g_0, &result, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_real(stdout, result, "%g"); + printf("\n"); + + printf("No vertices, transitivity NaN:\n"); + IGRAPH_ASSERT(igraph_transitivity_avglocal_undirected(&g_0, &result, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_real(stdout, result, "%g"); + printf("\n"); + + printf("One vertex, transitivity zero:\n"); + IGRAPH_ASSERT(igraph_transitivity_avglocal_undirected(&g_1, &result, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_real(stdout, result, "%g"); + printf("\n"); + + printf("One vertex, transitivity NaN:\n"); + IGRAPH_ASSERT(igraph_transitivity_avglocal_undirected(&g_1, &result, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_real(stdout, result, "%g"); + printf("\n"); + + printf("Simple graph:\n"); + IGRAPH_ASSERT(igraph_transitivity_avglocal_undirected(&g_simple, &result, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_real(stdout, result, "%g"); + printf("\n"); + + printf("Multigraph:\n"); + IGRAPH_ASSERT(igraph_transitivity_avglocal_undirected(&g_ml, &result, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_real(stdout, result, "%g"); + printf("\n"); + + printf("Multigraph, TRANSITIVITY_NAN:\n"); + IGRAPH_ASSERT(igraph_transitivity_avglocal_undirected(&g_ml, &result, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_real(stdout, result, "%g"); + printf("\n"); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_simple); + igraph_destroy(&g_ml); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_transitivity_avglocal_undirected.out b/tests/unit/igraph_transitivity_avglocal_undirected.out new file mode 100644 index 0000000..bbf4123 --- /dev/null +++ b/tests/unit/igraph_transitivity_avglocal_undirected.out @@ -0,0 +1,14 @@ +No vertices, transitivity zero: +0 +No vertices, transitivity NaN: +NaN +One vertex, transitivity zero: +0 +One vertex, transitivity NaN: +NaN +Simple graph: +0.444444 +Multigraph: +0.444444 +Multigraph, TRANSITIVITY_NAN: +0.666667 diff --git a/tests/unit/igraph_transitivity_barrat.c b/tests/unit/igraph_transitivity_barrat.c new file mode 100644 index 0000000..5f618aa --- /dev/null +++ b/tests/unit/igraph_transitivity_barrat.c @@ -0,0 +1,132 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void warning_handler_print_stdout(const char *reason, const char *file, + int line) { + IGRAPH_UNUSED(file); + IGRAPH_UNUSED(line); + fprintf(stdout, "Warning: %s\n", reason); +} + +int main(void) { + igraph_t g_0, g_1, g_simple; + igraph_vector_t result, weights_none, weights_simple; + + igraph_small(&g_0, 0, 0, -1); + igraph_small(&g_1, 1, 0, -1); + igraph_small(&g_simple, 6, 0, 0,1, 0,2, 1,2, 1,3, 2,3, 3,4, -1); + + igraph_vector_init(&result, 0); + igraph_vector_init(&weights_none, 0); + igraph_vector_init_int(&weights_simple, 6, -1, 0, 1, 2, 3, 4); + + igraph_set_warning_handler(warning_handler_print_stdout); + + printf("No vertices, transitivity zero, NULL weights:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_0, &result, igraph_vss_all(), /*weights*/ NULL, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("No vertices, transitivity zero, NULL weights, no vs:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_0, &result, igraph_vss_none(), /*weights*/ NULL, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("No vertices, transitivity zero, no weights:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_0, &result, igraph_vss_all(), &weights_none, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("No vertices, transitivity zero, no weights, no vs:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_0, &result, igraph_vss_none(), &weights_none, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("No vertices, transitivity NAN:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_0, &result, igraph_vss_all(), NULL, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("No vertices, transitivity NAN, no vs:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_0, &result, igraph_vss_none(), NULL, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("One vertex, transitivity zero:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_1, &result, igraph_vss_all(), NULL, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("One vertex, transitivity NaN:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_1, &result, igraph_vss_all(), NULL, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("One vertex, transitivity zero, vs one:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_1, &result, igraph_vss_1(0), NULL, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("One vertex, transitivity NaN, vs one:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_1, &result, igraph_vss_1(0), NULL, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("Simple graph, NULL weights, transitivity NAN:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_simple, &result, igraph_vss_all(), NULL, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("Simple graph, with weights, transitivity NAN:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_simple, &result, igraph_vss_all(), &weights_simple, IGRAPH_TRANSITIVITY_NAN) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("Simple graph, with weights, transitivity zero:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_simple, &result, igraph_vss_all(), &weights_simple, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + printf("Simple graph, with weights, transitivity zero, vss none:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_simple, &result, igraph_vss_none(), &weights_simple, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_SUCCESS); + print_vector(&result); + printf("\n"); + + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Wrong weight length, vss all:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_simple, &result, igraph_vss_all(), &weights_none, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_EINVAL); + + printf("Wrong weight length, vss none:\n"); + IGRAPH_ASSERT(igraph_transitivity_barrat(&g_simple, &result, igraph_vss_none(), &weights_none, IGRAPH_TRANSITIVITY_ZERO) == IGRAPH_EINVAL); + + igraph_destroy(&g_0); + igraph_destroy(&g_1); + igraph_destroy(&g_simple); + + igraph_vector_destroy(&result); + igraph_vector_destroy(&weights_none); + igraph_vector_destroy(&weights_simple); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_transitivity_barrat.out b/tests/unit/igraph_transitivity_barrat.out new file mode 100644 index 0000000..08ecba8 --- /dev/null +++ b/tests/unit/igraph_transitivity_barrat.out @@ -0,0 +1,45 @@ +No vertices, transitivity zero, NULL weights: +( ) + +No vertices, transitivity zero, NULL weights, no vs: +( ) + +No vertices, transitivity zero, no weights: +( ) + +No vertices, transitivity zero, no weights, no vs: +( ) + +No vertices, transitivity NAN: +( ) + +No vertices, transitivity NAN, no vs: +( ) + +One vertex, transitivity zero: +( 0 ) + +One vertex, transitivity NaN: +( NaN ) + +One vertex, transitivity zero, vs one: +( 0 ) + +One vertex, transitivity NaN, vs one: +( NaN ) + +Simple graph, NULL weights, transitivity NAN: +Warning: No weights given for Barrat's transitivity, unweighted version is used. +( 1 0.666667 0.666667 0.333333 NaN NaN ) + +Simple graph, with weights, transitivity NAN: +( 1 0.75 0.625 0.277778 NaN NaN ) + +Simple graph, with weights, transitivity zero: +( 1 0.75 0.625 0.277778 0 0 ) + +Simple graph, with weights, transitivity zero, vss none: +( ) + +Wrong weight length, vss all: +Wrong weight length, vss none: diff --git a/tests/unit/igraph_tree_from_parent_vector.c b/tests/unit/igraph_tree_from_parent_vector.c new file mode 100644 index 0000000..d481e21 --- /dev/null +++ b/tests/unit/igraph_tree_from_parent_vector.c @@ -0,0 +1,77 @@ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_int_t parents; + igraph_bool_t is_tree, is_forest; + + igraph_vector_int_init_int(&parents, 5, + 4, 4, 1, -2, 3); + + printf("Out-tree:\n"); + igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_OUT); + print_graph(&graph); + igraph_is_tree(&graph, &is_tree, NULL, IGRAPH_OUT); + IGRAPH_ASSERT(is_tree); + igraph_destroy(&graph); + + printf("\nIn-tree:\n"); + igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_IN); + print_graph(&graph); + igraph_is_tree(&graph, &is_tree, NULL, IGRAPH_IN); + IGRAPH_ASSERT(is_tree); + igraph_destroy(&graph); + + printf("\nUndirected tree:\n"); + igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_UNDIRECTED); + print_graph(&graph); + igraph_is_tree(&graph, &is_tree, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(is_tree); + igraph_destroy(&graph); + + printf("\nForest:\n"); + VECTOR(parents)[0] = -1; + igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_OUT); + print_graph(&graph); + igraph_is_forest(&graph, &is_forest, NULL, IGRAPH_OUT); + IGRAPH_ASSERT(is_forest); + igraph_destroy(&graph); + + /* Invalid tree mode */ + CHECK_ERROR(igraph_tree_from_parent_vector(&graph, &parents, -1), IGRAPH_EINVAL); + + /* Invalid vertex */ + VECTOR(parents)[0] = 5; + CHECK_ERROR(igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_OUT), IGRAPH_EINVVID); + + /* Self-loop */ + VECTOR(parents)[0] = 0; + CHECK_ERROR(igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_OUT), IGRAPH_EINVAL); + + /* Longer cycle */ + VECTOR(parents)[0] = 4; + VECTOR(parents)[3] = 0; + CHECK_ERROR(igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_OUT), IGRAPH_EINVAL); + + printf("\nEdgeless graph:\n"); + igraph_vector_int_fill(&parents, -1); + igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_OUT); + print_graph(&graph); + IGRAPH_ASSERT(igraph_ecount(&graph) == 0); + igraph_destroy(&graph); + + printf("\nNull graph:\n"); + igraph_vector_int_clear(&parents); + igraph_tree_from_parent_vector(&graph, &parents, IGRAPH_TREE_OUT); + print_graph(&graph); + IGRAPH_ASSERT(igraph_vcount(&graph) == 0); + igraph_destroy(&graph); + + igraph_vector_int_destroy(&parents); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_tree_from_parent_vector.out b/tests/unit/igraph_tree_from_parent_vector.out new file mode 100644 index 0000000..fad0396 --- /dev/null +++ b/tests/unit/igraph_tree_from_parent_vector.out @@ -0,0 +1,50 @@ +Out-tree: +directed: true +vcount: 5 +edges: { +4 0 +3 4 +4 1 +1 2 +} + +In-tree: +directed: true +vcount: 5 +edges: { +0 4 +4 3 +1 4 +2 1 +} + +Undirected tree: +directed: false +vcount: 5 +edges: { +4 0 +4 3 +4 1 +2 1 +} + +Forest: +directed: true +vcount: 5 +edges: { +4 1 +3 4 +1 2 +} + +Edgeless graph: +directed: true +vcount: 5 +edges: { +} + +Null graph: +directed: true +vcount: 0 +edges: { +} diff --git a/tests/unit/igraph_triangular_lattice.c b/tests/unit/igraph_triangular_lattice.c new file mode 100644 index 0000000..96ccbf3 --- /dev/null +++ b/tests/unit/igraph_triangular_lattice.c @@ -0,0 +1,102 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_int_t dimvector; + + /* empty graph */ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 2)); + VECTOR(dimvector)[0] = 3; + + IGRAPH_CHECK(igraph_triangular_lattice(&graph, &dimvector, true, false)); + printf("Empty graph:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + /* triangular triangular lattice with a single vertex and no edges*/ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 1)); + VECTOR(dimvector)[0] = 1; + + IGRAPH_CHECK(igraph_triangular_lattice(&graph, &dimvector, true, false)); + printf("Triangular triangular lattice, single vertex:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + /* triangular triangular lattice */ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 1)); + VECTOR(dimvector)[0] = 5; + + IGRAPH_CHECK(igraph_triangular_lattice(&graph, &dimvector, true, false)); + printf("Triangular triangular lattice:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + /* rectangular triangular lattice */ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 2)); + VECTOR(dimvector)[0] = 4; + VECTOR(dimvector)[1] = 5; + + IGRAPH_CHECK(igraph_triangular_lattice(&graph, &dimvector, true, true)); + printf("Rectangular triangular lattice:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + /* hexagonal triangular lattice */ + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 3)); + VECTOR(dimvector)[0] = 3; + VECTOR(dimvector)[1] = 4; + VECTOR(dimvector)[2] = 5; + + IGRAPH_CHECK(igraph_triangular_lattice(&graph, &dimvector, false, true)); + printf("Hexagonal triangular lattice:\n"); + print_graph_canon(&graph); + + igraph_destroy(&graph); + + /* Erroneous calls */ + VECTOR(dimvector)[0] = -3; + CHECK_ERROR(igraph_triangular_lattice(&graph, &dimvector, true, true), IGRAPH_EINVAL); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + IGRAPH_CHECK(igraph_vector_int_init(&dimvector, 4)); + VECTOR(dimvector)[0] = 3; + VECTOR(dimvector)[1] = 4; + VECTOR(dimvector)[2] = 5; + VECTOR(dimvector)[3] = 5; + CHECK_ERROR(igraph_triangular_lattice(&graph, &dimvector, true, true), IGRAPH_EINVAL); + + igraph_destroy(&graph); + igraph_vector_int_destroy(&dimvector); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_triangular_lattice.out b/tests/unit/igraph_triangular_lattice.out new file mode 100644 index 0000000..4141012 --- /dev/null +++ b/tests/unit/igraph_triangular_lattice.out @@ -0,0 +1,228 @@ +Empty graph: +directed: true +vcount: 0 +edges: { +} +Triangular triangular lattice, single vertex: +directed: true +vcount: 1 +edges: { +} +Triangular triangular lattice: +directed: true +vcount: 15 +edges: { +0 1 +0 5 +1 2 +1 5 +1 6 +2 3 +2 6 +2 7 +3 4 +3 7 +3 8 +4 8 +5 6 +5 9 +6 7 +6 9 +6 10 +7 8 +7 10 +7 11 +8 11 +9 10 +9 12 +10 11 +10 12 +10 13 +11 13 +12 13 +12 14 +13 14 +} +Rectangular triangular lattice: +directed: true +vcount: 20 +edges: { +0 1 +0 5 +0 6 +1 0 +1 2 +1 6 +1 7 +2 1 +2 3 +2 7 +2 8 +3 2 +3 4 +3 8 +3 9 +4 3 +4 9 +5 0 +5 6 +5 10 +6 0 +6 1 +6 5 +6 7 +6 10 +6 11 +7 1 +7 2 +7 6 +7 8 +7 11 +7 12 +8 2 +8 3 +8 7 +8 9 +8 12 +8 13 +9 3 +9 4 +9 8 +9 13 +9 14 +10 5 +10 6 +10 11 +10 15 +10 16 +11 6 +11 7 +11 10 +11 12 +11 16 +11 17 +12 7 +12 8 +12 11 +12 13 +12 17 +12 18 +13 8 +13 9 +13 12 +13 14 +13 18 +13 19 +14 9 +14 13 +14 19 +15 10 +15 16 +16 10 +16 11 +16 15 +16 17 +17 11 +17 12 +17 16 +17 18 +18 12 +18 13 +18 17 +18 19 +19 13 +19 14 +19 18 +} +Hexagonal triangular lattice: +directed: false +vcount: 36 +edges: { +0 1 +0 3 +0 4 +1 2 +1 4 +1 5 +2 5 +2 6 +3 4 +3 7 +3 8 +4 5 +4 8 +4 9 +5 6 +5 9 +5 10 +6 10 +6 11 +7 8 +7 12 +7 13 +8 9 +8 13 +8 14 +9 10 +9 14 +9 15 +10 11 +10 15 +10 16 +11 16 +11 17 +12 13 +12 18 +13 14 +13 18 +13 19 +14 15 +14 19 +14 20 +15 16 +15 20 +15 21 +16 17 +16 21 +16 22 +17 22 +17 23 +18 19 +18 24 +19 20 +19 24 +19 25 +20 21 +20 25 +20 26 +21 22 +21 26 +21 27 +22 23 +22 27 +22 28 +23 28 +24 25 +24 29 +25 26 +25 29 +25 30 +26 27 +26 30 +26 31 +27 28 +27 31 +27 32 +28 32 +29 30 +29 33 +30 31 +30 33 +30 34 +31 32 +31 34 +31 35 +32 35 +33 34 +34 35 +} diff --git a/tests/unit/igraph_trussness.c b/tests/unit/igraph_trussness.c new file mode 100644 index 0000000..22a7160 --- /dev/null +++ b/tests/unit/igraph_trussness.c @@ -0,0 +1,114 @@ +/* + + Copyright 2017 The Johns Hopkins University Applied Physics Laboratory LLC. All Rights Reserved. + Copyright 2021 The igraph team + + Truss algorithm for cohesive subgroups. + + Author: Alex Perrone + Date: 2017-08-03 + Minor edits: The igraph team, 2021 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + 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 "test_utilities.h" + +void print_and_destroy(igraph_t *graph, igraph_vector_int_t *trussness) { + igraph_integer_t i, n = igraph_vector_int_size(trussness); + + printf("fromNode, toNode, trussness\n"); + for (i=0; i < n; i++) { + igraph_integer_t from, to; + igraph_edge(graph, i, &from, &to); + printf("%" IGRAPH_PRId ", %" IGRAPH_PRId ", %" IGRAPH_PRId "\n", from, to, VECTOR(*trussness)[i]); + } + + igraph_vector_int_destroy(trussness); + igraph_destroy(graph); +} + +int main(void) { + + igraph_t graph; + igraph_vector_int_t v; + igraph_vector_int_t trussness; + + /* Create actual graph */ + igraph_integer_t edges[] = { 0,1, 0,2, 0,3, 0,4, + 1,2, 1,3, 1,4, 2,3, 2,4, 3,4, 3,6, 3,11, + 4,5, 4,6, 5,6, 5,7, 5,8, 5,9, 6,7, 6,10, 6,11, + 7,8, 7,9, 8,9, 8,10 }; + igraph_integer_t n = sizeof(edges) / sizeof(edges[0]); + + igraph_vector_int_view(&v, edges, n); + igraph_create(&graph, &v, 0, IGRAPH_UNDIRECTED); + + /* Compute the trussness of the edges. */ + printf("Simple graph:\n"); + igraph_vector_int_init(&trussness, 0); + igraph_trussness(&graph, &trussness); + print_and_destroy(&graph, &trussness); + + /* Add some loop edges -- they should have trussness = 2 */ + printf("\nGraph with loops:\n"); + igraph_create(&graph, &v, 0, IGRAPH_UNDIRECTED); + igraph_add_edge(&graph, 0, 0); + igraph_add_edge(&graph, 7, 7); + igraph_add_edge(&graph, 5, 5); + igraph_vector_int_init(&trussness, 0); + igraph_trussness(&graph, &trussness); + print_and_destroy(&graph, &trussness); + + /* Null graph trivial case */ + printf("\nNull graph:\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_init(&trussness, 0); + igraph_trussness(&graph, &trussness); + print_and_destroy(&graph, &trussness); + + /* Singleton graph trivial case */ + printf("\nSingleton graph:\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + igraph_vector_int_init(&trussness, 0); + igraph_trussness(&graph, &trussness); + print_and_destroy(&graph, &trussness); + + /* Graph with no edges trivial case */ + printf("\nGraph with no edges:\n"); + igraph_empty(&graph, 10, IGRAPH_UNDIRECTED); + igraph_vector_int_init(&trussness, 0); + igraph_trussness(&graph, &trussness); + print_and_destroy(&graph, &trussness); + + VERIFY_FINALLY_STACK(); + + /* Multigraph */ + printf("\nTrying multigraph:\n"); + igraph_create(&graph, &v, 0, IGRAPH_UNDIRECTED); + igraph_to_directed(&graph, IGRAPH_TO_DIRECTED_MUTUAL); + igraph_to_undirected(&graph, IGRAPH_TO_UNDIRECTED_EACH, 0); + igraph_vector_int_init(&trussness, 0); + CHECK_ERROR(igraph_trussness(&graph, &trussness), IGRAPH_UNIMPLEMENTED); + igraph_vector_int_destroy(&trussness); + igraph_destroy(&graph); + + return 0; +} diff --git a/tests/unit/igraph_trussness.out b/tests/unit/igraph_trussness.out new file mode 100644 index 0000000..d18d431 --- /dev/null +++ b/tests/unit/igraph_trussness.out @@ -0,0 +1,69 @@ +Simple graph: +fromNode, toNode, trussness +0, 1, 5 +0, 2, 5 +0, 3, 5 +0, 4, 5 +1, 2, 5 +1, 3, 5 +1, 4, 5 +2, 3, 5 +2, 4, 5 +3, 4, 5 +3, 6, 3 +3, 11, 3 +4, 5, 3 +4, 6, 3 +5, 6, 3 +5, 7, 4 +5, 8, 4 +5, 9, 4 +6, 7, 3 +6, 10, 2 +6, 11, 3 +7, 8, 4 +7, 9, 4 +8, 9, 4 +8, 10, 2 + +Graph with loops: +fromNode, toNode, trussness +0, 1, 5 +0, 2, 5 +0, 3, 5 +0, 4, 5 +1, 2, 5 +1, 3, 5 +1, 4, 5 +2, 3, 5 +2, 4, 5 +3, 4, 5 +3, 6, 3 +3, 11, 3 +4, 5, 3 +4, 6, 3 +5, 6, 3 +5, 7, 4 +5, 8, 4 +5, 9, 4 +6, 7, 3 +6, 10, 2 +6, 11, 3 +7, 8, 4 +7, 9, 4 +8, 9, 4 +8, 10, 2 +0, 0, 2 +7, 7, 2 +5, 5, 2 + +Null graph: +fromNode, toNode, trussness + +Singleton graph: +fromNode, toNode, trussness + +Graph with no edges: +fromNode, toNode, trussness + +Trying multigraph: diff --git a/tests/unit/igraph_turan.c b/tests/unit/igraph_turan.c new file mode 100644 index 0000000..4b82e5d --- /dev/null +++ b/tests/unit/igraph_turan.c @@ -0,0 +1,168 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g_turan; + igraph_t g_multipartite; + igraph_vector_int_t partitions; + igraph_vector_int_t types_turan; + igraph_vector_int_t types_multipartite; + igraph_bool_t res; + + printf("Empty graph with zero vertices:"); + igraph_vector_int_init(&types_turan, 0); + igraph_turan(&g_turan, &types_turan, 0, 10); + + print_graph_canon(&g_turan); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types_turan); + + igraph_vector_int_destroy(&types_turan); + igraph_destroy(&g_turan); + + + printf("\nTuran graph with 10 vertices and 1 partition:"); + igraph_vector_int_init(&types_turan, 0); + igraph_turan(&g_turan, &types_turan, 10, 1); + + print_graph_canon(&g_turan); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types_turan); + + igraph_vector_int_destroy(&types_turan); + igraph_destroy(&g_turan); + + + printf("\nTuran graph with 4 vertices and 6 partitions\n"); + printf("gives 4 vertices and 1 partition:"); + igraph_vector_int_init(&types_turan, 0); + igraph_turan(&g_turan, &types_turan, 4, 6); + + print_graph_canon(&g_turan); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types_turan); + + igraph_vector_int_destroy(&types_turan); + igraph_destroy(&g_turan); + + igraph_vector_int_init(&partitions, 4); + igraph_vector_int_init(&types_multipartite, 0); + igraph_vector_int_init(&types_turan, 0); + + VECTOR(partitions)[0] = 3; + VECTOR(partitions)[1] = 3; + VECTOR(partitions)[2] = 3; + VECTOR(partitions)[3] = 4; + + igraph_full_multipartite(&g_multipartite, &types_multipartite, &partitions, + IGRAPH_UNDIRECTED, IGRAPH_ALL); + igraph_turan(&g_turan, &types_turan, 13, 4); + + printf("\nTuran graph with 13 vertices and 4 partitions:"); + print_graph_canon(&g_turan); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types_turan); + + igraph_isomorphic(&g_multipartite, &g_turan, &res); + if (res) { + printf("\nTuran(13,4) is isomorphic to full multipartite(4,3,3,3)\n"); + } + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types_multipartite); + igraph_vector_int_destroy(&types_turan); + igraph_destroy(&g_turan); + igraph_destroy(&g_multipartite); + + + igraph_vector_int_init(&partitions, 3); + igraph_vector_int_init(&types_multipartite, 0); + igraph_vector_int_init(&types_turan, 0); + + VECTOR(partitions)[0] = 3; + VECTOR(partitions)[1] = 3; + VECTOR(partitions)[2] = 2; + + igraph_full_multipartite(&g_multipartite, &types_multipartite, &partitions, + IGRAPH_UNDIRECTED, IGRAPH_ALL); + igraph_turan(&g_turan, &types_turan, 8, 3); + + printf("\nTuran graph with 8 vertices and 3 partitions:"); + print_graph_canon(&g_turan); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types_turan); + + igraph_isomorphic(&g_multipartite, &g_turan, &res); + if (res) { + printf("\nTuran(8,3) is isomorphic to full multipartite(3,3,2)\n"); + } + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types_multipartite); + igraph_vector_int_destroy(&types_turan); + igraph_destroy(&g_turan); + igraph_destroy(&g_multipartite); + + + igraph_vector_int_init(&partitions, 3); + igraph_vector_int_init(&types_multipartite, 0); + igraph_vector_int_init(&types_turan, 0); + + VECTOR(partitions)[0] = 2; + VECTOR(partitions)[1] = 2; + VECTOR(partitions)[2] = 2; + + igraph_full_multipartite(&g_multipartite, &types_multipartite, &partitions, + IGRAPH_UNDIRECTED, IGRAPH_ALL); + igraph_turan(&g_turan, &types_turan, 6, 3); + + printf("\nTuran graph with 6 vertices and 3 partitions:"); + print_graph_canon(&g_turan); + + printf("\nPartition type:\n"); + igraph_vector_int_print(&types_turan); + + igraph_isomorphic(&g_multipartite, &g_turan, &res); + if (res) { + printf("\nTuran(6,3) is isomorphic to full multipartite(2,2,2)\n"); + } + + igraph_vector_int_destroy(&partitions); + igraph_vector_int_destroy(&types_multipartite); + igraph_vector_int_destroy(&types_turan); + igraph_destroy(&g_turan); + igraph_destroy(&g_multipartite); + + VERIFY_FINALLY_STACK(); + + return IGRAPH_SUCCESS; +} diff --git a/tests/unit/igraph_turan.out b/tests/unit/igraph_turan.out new file mode 100644 index 0000000..06d8c1f --- /dev/null +++ b/tests/unit/igraph_turan.out @@ -0,0 +1,156 @@ +Empty graph with zero vertices:directed: false +vcount: 0 +edges: { +} + +Partition type: + + +Turan graph with 10 vertices and 1 partition:directed: false +vcount: 10 +edges: { +} + +Partition type: +0 0 0 0 0 0 0 0 0 0 + +Turan graph with 4 vertices and 6 partitions +gives 4 vertices and 1 partition:directed: false +vcount: 4 +edges: { +0 1 +0 2 +0 3 +1 2 +1 3 +2 3 +} + +Partition type: +0 1 2 3 + +Turan graph with 13 vertices and 4 partitions:directed: false +vcount: 13 +edges: { +0 4 +0 5 +0 6 +0 7 +0 8 +0 9 +0 10 +0 11 +0 12 +1 4 +1 5 +1 6 +1 7 +1 8 +1 9 +1 10 +1 11 +1 12 +2 4 +2 5 +2 6 +2 7 +2 8 +2 9 +2 10 +2 11 +2 12 +3 4 +3 5 +3 6 +3 7 +3 8 +3 9 +3 10 +3 11 +3 12 +4 7 +4 8 +4 9 +4 10 +4 11 +4 12 +5 7 +5 8 +5 9 +5 10 +5 11 +5 12 +6 7 +6 8 +6 9 +6 10 +6 11 +6 12 +7 10 +7 11 +7 12 +8 10 +8 11 +8 12 +9 10 +9 11 +9 12 +} + +Partition type: +0 0 0 0 1 1 1 2 2 2 3 3 3 + +Turan(13,4) is isomorphic to full multipartite(4,3,3,3) + +Turan graph with 8 vertices and 3 partitions:directed: false +vcount: 8 +edges: { +0 3 +0 4 +0 5 +0 6 +0 7 +1 3 +1 4 +1 5 +1 6 +1 7 +2 3 +2 4 +2 5 +2 6 +2 7 +3 6 +3 7 +4 6 +4 7 +5 6 +5 7 +} + +Partition type: +0 0 0 1 1 1 2 2 + +Turan(8,3) is isomorphic to full multipartite(3,3,2) + +Turan graph with 6 vertices and 3 partitions:directed: false +vcount: 6 +edges: { +0 2 +0 3 +0 4 +0 5 +1 2 +1 3 +1 4 +1 5 +2 4 +2 5 +3 4 +3 5 +} + +Partition type: +0 0 1 1 2 2 + +Turan(6,3) is isomorphic to full multipartite(2,2,2) diff --git a/tests/unit/igraph_unfold_tree.c b/tests/unit/igraph_unfold_tree.c new file mode 100644 index 0000000..f099452 --- /dev/null +++ b/tests/unit/igraph_unfold_tree.c @@ -0,0 +1,82 @@ +/* + 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, see . + */ + +#include +#include "test_utilities.h" + +void print_and_destroy(igraph_t *g, igraph_vector_int_t *roots) { + igraph_t tree; + igraph_vector_int_t vertex_index; + + igraph_vector_int_init(&vertex_index, 0); + + igraph_unfold_tree(g, &tree, IGRAPH_OUT, roots, &vertex_index); + + printf("Tree:\n"); + print_graph_canon(&tree); + printf("Vertex indices:\n"); + igraph_vector_int_print(&vertex_index); + printf("\n"); + + igraph_destroy(&tree); + igraph_destroy(g); + igraph_vector_int_destroy(roots); + igraph_vector_int_destroy(&vertex_index); +} + +int main(void) { + igraph_t g, tree; + igraph_vector_int_t roots; + + printf("Graph with no vertices\n"); + igraph_vector_int_init(&roots, 0); + igraph_small(&g, 0, IGRAPH_UNDIRECTED, -1); + print_and_destroy(&g, &roots); + + printf("Graph with loops, multiple edges and isolated vertex:\n"); + igraph_vector_int_init_int(&roots, 1, 0); + igraph_small(&g, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, &roots); + + printf("Same graph, undirected:\n"); + igraph_vector_int_init_int(&roots, 1, 0); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, -1); + print_and_destroy(&g, &roots); + + printf("Almost same graph, two roots:\n"); + igraph_vector_int_init_int(&roots, 2, 0, 5); + igraph_small(&g, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, 5,5, 5,5, -1); + print_and_destroy(&g, &roots); + + printf("Same graph, multiple roots in same tree:\n"); + igraph_vector_int_init_int(&roots, 5, 0, 0, 1, 2, 3); + igraph_small(&g, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, 5,5, 5,5, -1); + print_and_destroy(&g, &roots); + + VERIFY_FINALLY_STACK(); + + printf("Check error for root out of bounds.\n"); + igraph_vector_int_init_int(&roots, 5, -1, 0, 1, 2, 3); + igraph_small(&g, 6, 1, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, 3,4, 5,5, 5,5, -1); + CHECK_ERROR(igraph_unfold_tree(&g, &tree, IGRAPH_OUT, &roots, NULL), IGRAPH_EINVVID); + igraph_destroy(&g); + igraph_vector_int_destroy(&roots); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_unfold_tree.out b/tests/unit/igraph_unfold_tree.out new file mode 100644 index 0000000..b32a1cc --- /dev/null +++ b/tests/unit/igraph_unfold_tree.out @@ -0,0 +1,80 @@ +Graph with no vertices +Tree: +directed: false +vcount: 0 +edges: { +} +Vertex indices: + + +Graph with loops, multiple edges and isolated vertex: +Tree: +directed: true +vcount: 10 +edges: { +0 1 +0 2 +1 3 +2 7 +2 8 +3 4 +3 9 +6 1 +} +Vertex indices: +0 1 2 3 4 5 1 0 3 4 + +Same graph, undirected: +Tree: +directed: false +vcount: 10 +edges: { +0 1 +0 2 +0 6 +1 3 +1 7 +2 8 +3 4 +3 9 +} +Vertex indices: +0 1 2 3 4 5 2 1 3 4 + +Almost same graph, two roots: +Tree: +directed: true +vcount: 12 +edges: { +0 1 +0 2 +1 3 +2 7 +2 8 +3 4 +3 9 +6 1 +10 5 +11 5 +} +Vertex indices: +0 1 2 3 4 5 1 0 3 4 5 5 + +Same graph, multiple roots in same tree: +Tree: +directed: true +vcount: 10 +edges: { +0 1 +0 2 +1 3 +2 7 +2 8 +3 4 +3 9 +6 1 +} +Vertex indices: +0 1 2 3 4 5 1 0 3 4 + +Check error for root out of bounds. diff --git a/tests/unit/igraph_union.c b/tests/unit/igraph_union.c new file mode 100644 index 0000000..9b5c0a1 --- /dev/null +++ b/tests/unit/igraph_union.c @@ -0,0 +1,156 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2023 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 + +#include + +#include "test_utilities.h" + +void print_and_destroy_graph_and_maps(igraph_t *uni, igraph_vector_int_list_t *list) { + printf("Union graph:\n"); + print_graph(uni); + igraph_destroy(uni); + + printf("Edge maps:\n"); + print_vector_int_list(list); + igraph_vector_int_list_clear(list); +} + +int main(void) { + igraph_t left, right, uni; + igraph_vector_int_t edges; + igraph_vector_ptr_t glist; + igraph_vector_int_t edge_map1, edge_map2; + igraph_vector_int_list_t edgemaps; + + printf("BINARY VERSION\n"); + + igraph_vector_int_init(&edge_map1, 0); + igraph_vector_int_init(&edge_map2, 0); + + igraph_small(&left, 0, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 2, 2, 3, + -1); + + igraph_small(&right, 0, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 2, 2, 4, + -1); + + igraph_union(&uni, &left, &right, &edge_map1, &edge_map2); + printf("Union graph:\n"); + print_graph(&uni); + printf("Edge maps:\n"); + print_vector_int(&edge_map1); + print_vector_int(&edge_map2); + + igraph_destroy(&uni); + igraph_destroy(&left); + igraph_destroy(&right); + + igraph_vector_int_destroy(&edge_map1); + igraph_vector_int_destroy(&edge_map2); + + printf("\n\nN-ARY VERSION\n"); + + /* Empty graph list */ + + printf("\nEmpty graph list:\n"); + igraph_vector_ptr_init(&glist, 0); + igraph_vector_int_list_init(&edgemaps, 0); + igraph_union_many(&uni, &glist, &edgemaps); + print_and_destroy_graph_and_maps(&uni, &edgemaps); + igraph_vector_ptr_destroy(&glist); + + /* Non-empty graph list */ + + printf("\nNon-empty directed graph list 1:\n"); + igraph_vector_ptr_init(&glist, 10); + for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { + VECTOR(glist)[i] = IGRAPH_CALLOC(1, igraph_t); + igraph_small(VECTOR(glist)[i], 0, IGRAPH_DIRECTED, + 0, 1, 1, 0, -1); + } + + igraph_union_many(&uni, &glist, &edgemaps); + + for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { + igraph_destroy(VECTOR(glist)[i]); + IGRAPH_FREE(VECTOR(glist)[i]); + } + + print_and_destroy_graph_and_maps(&uni, &edgemaps); + igraph_vector_ptr_destroy(&glist); + + /* Another non-empty graph list */ + + printf("\nNon-empty directed graph list 2:\n"); + + igraph_vector_ptr_init(&glist, 10); + for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { + VECTOR(glist)[i] = IGRAPH_CALLOC(1, igraph_t); + igraph_vector_int_init(&edges, 4); + VECTOR(edges)[0] = i; VECTOR(edges)[1] = i+1; + VECTOR(edges)[2] = 1; VECTOR(edges)[3] = 0; + igraph_create(VECTOR(glist)[i], &edges, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&edges); + } + + igraph_union_many(&uni, &glist, &edgemaps); + igraph_write_graph_edgelist(&uni, stdout); + + for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { + igraph_destroy(VECTOR(glist)[i]); + IGRAPH_FREE(VECTOR(glist)[i]); + } + + print_and_destroy_graph_and_maps(&uni, &edgemaps); + igraph_vector_ptr_destroy(&glist); + + /* Undirected graph list*/ + + printf("\nUndirected graph list:\n"); + + igraph_vector_ptr_init(&glist, 10); + for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { + VECTOR(glist)[i] = IGRAPH_CALLOC(1, igraph_t); + igraph_vector_int_init(&edges, 4); + VECTOR(edges)[0] = i; VECTOR(edges)[1] = i+1; + VECTOR(edges)[2] = 1; VECTOR(edges)[3] = 0; + igraph_create(VECTOR(glist)[i], &edges, 0, IGRAPH_UNDIRECTED); + igraph_vector_int_destroy(&edges); + } + + igraph_union_many(&uni, &glist, &edgemaps); + igraph_write_graph_edgelist(&uni, stdout); + + for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { + igraph_destroy(VECTOR(glist)[i]); + IGRAPH_FREE(VECTOR(glist)[i]); + } + + print_and_destroy_graph_and_maps(&uni, &edgemaps); + igraph_vector_ptr_destroy(&glist); + + igraph_vector_int_list_destroy(&edgemaps); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_union.out b/tests/unit/igraph_union.out new file mode 100644 index 0000000..e97d17c --- /dev/null +++ b/tests/unit/igraph_union.out @@ -0,0 +1,133 @@ +BINARY VERSION +Union graph: +directed: true +vcount: 5 +edges: { +0 1 +1 2 +2 2 +2 3 +2 4 +} +Edge maps: +( 0 1 2 3 ) +( 0 1 2 4 ) + + +N-ARY VERSION + +Empty graph list: +Union graph: +directed: true +vcount: 0 +edges: { +} +Edge maps: +{ +} + +Non-empty directed graph list 1: +Union graph: +directed: true +vcount: 2 +edges: { +1 0 +0 1 +} +Edge maps: +{ + 0: ( 1 0 ) + 1: ( 1 0 ) + 2: ( 1 0 ) + 3: ( 1 0 ) + 4: ( 1 0 ) + 5: ( 1 0 ) + 6: ( 1 0 ) + 7: ( 1 0 ) + 8: ( 1 0 ) + 9: ( 1 0 ) +} + +Non-empty directed graph list 2: +0 1 +1 0 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +Union graph: +directed: true +vcount: 11 +edges: { +9 10 +8 9 +7 8 +6 7 +5 6 +4 5 +3 4 +2 3 +1 2 +1 0 +0 1 +} +Edge maps: +{ + 0: ( 10 9 ) + 1: ( 8 9 ) + 2: ( 7 9 ) + 3: ( 6 9 ) + 4: ( 5 9 ) + 5: ( 4 9 ) + 6: ( 3 9 ) + 7: ( 2 9 ) + 8: ( 1 9 ) + 9: ( 0 9 ) +} + +Undirected graph list: +0 1 +0 1 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +Union graph: +directed: false +vcount: 11 +edges: { +10 9 +9 8 +8 7 +7 6 +6 5 +5 4 +4 3 +3 2 +2 1 +1 0 +1 0 +} +Edge maps: +{ + 0: ( 10 9 ) + 1: ( 8 9 ) + 2: ( 7 9 ) + 3: ( 6 9 ) + 4: ( 5 9 ) + 5: ( 4 9 ) + 6: ( 3 9 ) + 7: ( 2 9 ) + 8: ( 1 9 ) + 9: ( 0 9 ) +} diff --git a/tests/unit/igraph_vector_floor.c b/tests/unit/igraph_vector_floor.c new file mode 100644 index 0000000..6f3d60b --- /dev/null +++ b/tests/unit/igraph_vector_floor.c @@ -0,0 +1,41 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + + +int main(void) { + igraph_vector_t from; + igraph_vector_int_t to; + + igraph_vector_init_real(&from, 9, -0.6, -0.5, -0.4, -0.0, 0.0, 0.4, 0.5, 0.6, 1.1); + igraph_vector_int_init(&to, 0); + + printf("From:\n"); + igraph_vector_print(&from); + IGRAPH_ASSERT(igraph_vector_floor(&from, &to) == IGRAPH_SUCCESS); + + printf("To:\n"); + igraph_vector_int_print(&to); + + igraph_vector_int_destroy(&to); + igraph_vector_destroy(&from); + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_vector_floor.out b/tests/unit/igraph_vector_floor.out new file mode 100644 index 0000000..88c44aa --- /dev/null +++ b/tests/unit/igraph_vector_floor.out @@ -0,0 +1,4 @@ +From: +-0.6 -0.5 -0.4 -0 0 0.4 0.5 0.6 1.1 +To: +-1 -1 -1 0 0 0 0 0 1 diff --git a/tests/unit/igraph_vector_lex_cmp.c b/tests/unit/igraph_vector_lex_cmp.c new file mode 100644 index 0000000..55a8897 --- /dev/null +++ b/tests/unit/igraph_vector_lex_cmp.c @@ -0,0 +1,54 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_vector_t v1, v2, v3, v4, v5, v6, v7, v8; + + igraph_vector_init_real(&v1, 3, 1e30, 2e30, 9e30); + igraph_vector_init_real(&v2, 3, 1e30, 2e30, 3e30); + igraph_vector_init_real(&v3, 2, 1e30, 2e30); + igraph_vector_init_real(&v4, 0); + igraph_vector_init_real(&v5, 3, 1e30, 2e30, 9e30); + igraph_vector_init_real(&v6, 0); + igraph_vector_init_real(&v7, 3, 9e30, 2e30, 1e30); + igraph_vector_init_real(&v8, 2, 3e30, 3e30); + + igraph_vector_t *vectors[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8}; + size_t n = sizeof(vectors) / sizeof(igraph_vector_t *); + + printf("Lexicographical ordering:\n"); + igraph_qsort(vectors, n, sizeof(igraph_vector_t *), igraph_vector_lex_cmp_untyped); + + for (size_t i = 0; i < n; i++) { + print_vector(vectors[i]); + } + + printf("\nColexicographical ordering:\n"); + igraph_qsort(vectors, n, sizeof(igraph_vector_t *), igraph_vector_colex_cmp_untyped); + + for (size_t i = 0; i < n; i++) { + print_vector(vectors[i]); + igraph_vector_destroy(vectors[i]); + } + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_vector_lex_cmp.out b/tests/unit/igraph_vector_lex_cmp.out new file mode 100644 index 0000000..b65d3a8 --- /dev/null +++ b/tests/unit/igraph_vector_lex_cmp.out @@ -0,0 +1,19 @@ +Lexicographical ordering: +( ) +( ) +( 1e+30 2e+30 ) +( 1e+30 2e+30 3e+30 ) +( 1e+30 2e+30 9e+30 ) +( 1e+30 2e+30 9e+30 ) +( 3e+30 3e+30 ) +( 9e+30 2e+30 1e+30 ) + +Colexicographical ordering: +( ) +( ) +( 9e+30 2e+30 1e+30 ) +( 1e+30 2e+30 ) +( 1e+30 2e+30 3e+30 ) +( 3e+30 3e+30 ) +( 1e+30 2e+30 9e+30 ) +( 1e+30 2e+30 9e+30 ) diff --git a/tests/unit/igraph_vertex_disjoint_paths.c b/tests/unit/igraph_vertex_disjoint_paths.c new file mode 100644 index 0000000..fb99bb9 --- /dev/null +++ b/tests/unit/igraph_vertex_disjoint_paths.c @@ -0,0 +1,53 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_integer_t value; + + igraph_small(&g, 7, IGRAPH_DIRECTED, + 0,1, 0,2, 1,2, 1,3, 2,4, 3,4, 3,5, 4,5, 0,5, 3,3, 5,2, 1,3, 3,1, + -1); + + igraph_vertex_disjoint_paths(&g, &value, 0, 5); + IGRAPH_ASSERT(value == 3); + + igraph_vertex_disjoint_paths(&g, &value, 1, 3); + IGRAPH_ASSERT(value == 2); + + igraph_vertex_disjoint_paths(&g, &value, 4, 0); + IGRAPH_ASSERT(value == 0); + + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_EACH, NULL); + + igraph_vertex_disjoint_paths(&g, &value, 4, 0); + IGRAPH_ASSERT(value == 3); + + igraph_vertex_disjoint_paths(&g, &value, 1, 3); + IGRAPH_ASSERT(value == 5); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_voronoi.c b/tests/unit/igraph_voronoi.c new file mode 100644 index 0000000..fa4921d --- /dev/null +++ b/tests/unit/igraph_voronoi.c @@ -0,0 +1,134 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t generators, membership; + igraph_vector_t distances, weights; + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Init result vars */ + + igraph_vector_int_init(&membership, 0); + igraph_vector_init(&distances, 0); + + /* Edge cases */ + + printf("Singleton graph.\n"); + + igraph_empty(&g, 1, IGRAPH_DIRECTED); + igraph_vector_int_init_int(&generators, 1, + 0); + + igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_ALL, IGRAPH_VORONOI_RANDOM); + print_vector_int(&membership); + print_vector(&distances); + + igraph_vector_int_destroy(&generators); + igraph_destroy(&g); + + /* Disconnected directed multigraph */ + + printf("\n\nDisconnected directed multigraph.\n"); + + igraph_small(&g, 0, IGRAPH_DIRECTED, + 0,2, 1,2, 2,3, 3,4, 5,4, 6,4, 2,3, 1,1, + -1); + igraph_vector_int_init_int(&generators, 2, + 0, 1); + + printf("\nTiebreaking: 'first'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_OUT, IGRAPH_VORONOI_FIRST); + print_vector_int(&membership); + print_vector(&distances); + + printf("\nTiebreaking: 'last'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_OUT, IGRAPH_VORONOI_LAST); + print_vector_int(&membership); + print_vector(&distances); + + printf("\nTiebreaking: 'random'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_OUT, IGRAPH_VORONOI_RANDOM); + print_vector_int(&membership); + print_vector(&distances); + + igraph_vector_int_destroy(&generators); + igraph_destroy(&g); + + /* Karate club network */ + + printf("\n\nKarate club, unweighted.\n"); + + igraph_famous(&g, "Zachary"); + + igraph_vector_int_init_int(&generators, 3, + 0, 32, 24); + + printf("\nTiebreaking: 'first'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_ALL, IGRAPH_VORONOI_FIRST); + print_vector_int(&membership); + print_vector(&distances); + + printf("\nTiebreaking: 'last'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_ALL, IGRAPH_VORONOI_LAST); + print_vector_int(&membership); + print_vector(&distances); + + printf("\nTiebreaking: 'random'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, NULL, IGRAPH_ALL, IGRAPH_VORONOI_RANDOM); + print_vector_int(&membership); + print_vector(&distances); + + printf("\n\nKarate club, betweenness weighted.\n"); + + igraph_vector_init(&weights, 0); + igraph_edge_betweenness(&g, &weights, IGRAPH_UNDIRECTED, NULL); + + printf("\nTiebreaking: 'first'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, &weights, IGRAPH_ALL, IGRAPH_VORONOI_FIRST); + print_vector_int(&membership); + print_vector(&distances); + + printf("\nTiebreaking: 'last'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, &weights, IGRAPH_ALL, IGRAPH_VORONOI_LAST); + print_vector_int(&membership); + print_vector(&distances); + + printf("\nTiebreaking: 'random'\n"); + igraph_voronoi(&g, &membership, &distances, &generators, &weights, IGRAPH_ALL, IGRAPH_VORONOI_RANDOM); + print_vector_int(&membership); + print_vector(&distances); + + igraph_vector_destroy(&weights); + + igraph_vector_int_destroy(&generators); + igraph_destroy(&g); + + /* Destroy result vars */ + + igraph_vector_destroy(&distances); + igraph_vector_int_destroy(&membership); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_voronoi.out b/tests/unit/igraph_voronoi.out new file mode 100644 index 0000000..fdbf0e9 --- /dev/null +++ b/tests/unit/igraph_voronoi.out @@ -0,0 +1,48 @@ +Singleton graph. +( 0 ) +( 0 ) + + +Disconnected directed multigraph. + +Tiebreaking: 'first' +( 0 1 0 0 0 -1 -1 ) +( 0 0 1 2 3 Inf Inf ) + +Tiebreaking: 'last' +( 0 1 1 1 1 -1 -1 ) +( 0 0 1 2 3 Inf Inf ) + +Tiebreaking: 'random' +( 0 1 1 0 0 -1 -1 ) +( 0 0 1 2 3 Inf Inf ) + + +Karate club, unweighted. + +Tiebreaking: 'first' +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 1 1 2 2 1 2 0 1 1 0 1 1 ) +( 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 0 1 2 1 2 1 1 1 0 1 ) + +Tiebreaking: 'last' +( 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 2 2 1 2 2 1 1 2 1 1 ) +( 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 0 1 2 1 2 1 1 1 0 1 ) + +Tiebreaking: 'random' +( 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 1 0 1 0 1 1 2 2 1 2 1 1 1 2 1 1 ) +( 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 0 1 2 1 2 1 1 1 0 1 ) + + +Karate club, betweenness weighted. + +Tiebreaking: 'first' +( 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 2 2 1 2 1 1 1 2 1 1 ) +( 0 14.1667 20.2143 11.5 29.3333 32 32 12.8024 15.0667 21.2286 29.3333 33 18.4 19.8714 13.5111 13.5111 48.5 22.5095 13.5111 22.3762 13.5111 22.5095 13.5111 12.5333 0 2.36667 15.6302 10.4667 18.3952 13.0873 9.56667 22.5 0 4.61429 ) + +Tiebreaking: 'last' +( 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 2 2 1 2 1 1 1 2 1 1 ) +( 0 14.1667 20.2143 11.5 29.3333 32 32 12.8024 15.0667 21.2286 29.3333 33 18.4 19.8714 13.5111 13.5111 48.5 22.5095 13.5111 22.3762 13.5111 22.5095 13.5111 12.5333 0 2.36667 15.6302 10.4667 18.3952 13.0873 9.56667 22.5 0 4.61429 ) + +Tiebreaking: 'random' +( 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 2 2 1 2 1 1 1 2 1 1 ) +( 0 14.1667 20.2143 11.5 29.3333 32 32 12.8024 15.0667 21.2286 29.3333 33 18.4 19.8714 13.5111 13.5111 48.5 22.5095 13.5111 22.3762 13.5111 22.5095 13.5111 12.5333 0 2.36667 15.6302 10.4667 18.3952 13.0873 9.56667 22.5 0 4.61429 ) diff --git a/tests/unit/igraph_weighted_adjacency.c b/tests/unit/igraph_weighted_adjacency.c new file mode 100644 index 0000000..25b15e8 --- /dev/null +++ b/tests/unit/igraph_weighted_adjacency.c @@ -0,0 +1,340 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + +*/ + +#include +#include + +#include "test_utilities.h" + +//TODO make public? +igraph_error_t igraph_is_same_graph_weighted(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *res, igraph_vector_t *weights1, igraph_vector_t *weights2) { + igraph_integer_t nv1 = igraph_vcount(graph1); + igraph_integer_t nv2 = igraph_vcount(graph2); + igraph_integer_t ne1 = igraph_ecount(graph1); + igraph_integer_t ne2 = igraph_ecount(graph2); + igraph_integer_t 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; + } + + for (i = 0; i < ne1; i++) { + eid1 = VECTOR(graph1->ii)[i]; + eid2 = 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; + } + + /* Check they have the same weight */ + if (VECTOR(*weights1)[eid1] != VECTOR(*weights2)[eid2]) { + return IGRAPH_SUCCESS; + } + } + + *res = 1; /* No difference was found, graphs are the same */ + return IGRAPH_SUCCESS; +} + +void check_sparsemat(igraph_t *g, igraph_adjacency_t mode, igraph_loops_t loops, igraph_matrix_t *adjmatrix, igraph_vector_t *other_weights) { + igraph_t g_sparse; + igraph_sparsemat_t sparse_adjmatrix, sparse_adjmatrix_comp; + igraph_bool_t same; + igraph_vector_t weights; + + igraph_vector_init(&weights, 0); + + igraph_matrix_as_sparsemat(&sparse_adjmatrix, adjmatrix, 0.0001); + igraph_sparsemat_compress(&sparse_adjmatrix, &sparse_adjmatrix_comp); + igraph_sparse_weighted_adjacency(&g_sparse, &sparse_adjmatrix_comp, mode, &weights, loops); + igraph_is_same_graph_weighted(g, &g_sparse, &same, other_weights, &weights); + if (!same) { + printf("Sparse graph differs from non-sparse:\n"); + print_weighted_graph(&g_sparse, &weights); + exit(1); + } + igraph_sparsemat_destroy(&sparse_adjmatrix); + igraph_sparsemat_destroy(&sparse_adjmatrix_comp); + igraph_destroy(&g_sparse); + igraph_vector_destroy(&weights); +} + +void test_single_matrix(igraph_matrix_t* mat, igraph_adjacency_t mode) { + igraph_t g; + igraph_vector_t weights; + + igraph_vector_init(&weights, 0); + + printf("No loops\n--------\n\n"); + igraph_weighted_adjacency(&g, mat, mode, &weights, IGRAPH_NO_LOOPS); + + print_weighted_graph(&g, &weights); + check_sparsemat(&g, mode, IGRAPH_NO_LOOPS, mat, &weights); + + igraph_destroy(&g); + printf("\n"); + + printf("Loops once\n----------\n\n"); + igraph_weighted_adjacency(&g, mat, mode, &weights, IGRAPH_LOOPS_ONCE); + print_weighted_graph(&g, &weights); + check_sparsemat(&g, mode, IGRAPH_LOOPS_ONCE, mat, &weights); + igraph_destroy(&g); + printf("\n"); + + printf("Loops twice\n-----------\n\n"); + igraph_weighted_adjacency(&g, mat, mode, &weights, IGRAPH_LOOPS_TWICE); + print_weighted_graph(&g, &weights); + check_sparsemat(&g, mode, IGRAPH_LOOPS_TWICE, mat, &weights); + igraph_destroy(&g); + printf("\n"); + + igraph_vector_destroy(&weights); + + VERIFY_FINALLY_STACK(); +} + +void check_error(igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, igraph_loops_t loops, igraph_error_t error) { + igraph_t g; + igraph_sparsemat_t sparse_adjmatrix, sparse_adjmatrix_comp; + igraph_vector_t weights; + igraph_vector_init(&weights, 0); + + igraph_matrix_as_sparsemat(&sparse_adjmatrix, adjmatrix, 0.0001); + igraph_sparsemat_compress(&sparse_adjmatrix, &sparse_adjmatrix_comp); + + CHECK_ERROR(igraph_weighted_adjacency(&g, adjmatrix, mode, &weights, loops), error); + CHECK_ERROR(igraph_sparse_adjacency(&g, &sparse_adjmatrix_comp, mode, loops), error); + + igraph_sparsemat_destroy(&sparse_adjmatrix); + igraph_sparsemat_destroy(&sparse_adjmatrix_comp); + igraph_vector_destroy(&weights); +} + +int main(void) { + igraph_matrix_t mat; + igraph_matrix_t mat_sym; + int m[4][4] = { { 0, 1, 2, 0 }, { 2, 0, 0, 1 }, { 0, 0, 4, 0 }, { 0, 1, 0, 0 } }; + int m_sym[4][4] = { { 0, 2, 2, 0 }, { 2, 0, 0, 1 }, { 2, 0, 4, 0 }, { 0, 1, 0, 0 } }; + igraph_integer_t i, j; + + printf("0x0 matrix\n"); + printf("==========\n\n"); + igraph_matrix_init(&mat, 0, 0); + test_single_matrix(&mat, IGRAPH_ADJ_DIRECTED); + igraph_matrix_destroy(&mat); + + igraph_matrix_init(&mat, 4, 4); + for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) { + MATRIX(mat, i, j) = m[i][j]; + } + igraph_matrix_init(&mat_sym, 4, 4); + for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) { + MATRIX(mat_sym, i, j) = m_sym[i][j]; + } + + /* [ 0 1 2 0 ] + [ 2 0 0 1 ] + [ 0 0 4 0 ] + [ 0 1 0 0 ] */ + printf("IGRAPH_ADJ_DIRECTED\n"); + printf("===================\n\n"); + test_single_matrix(&mat, IGRAPH_ADJ_DIRECTED); + + /* [ 0 1 2 0 ] + [ - 0 0 1 ] + [ - - 4 0 ] + [ - - - 0 ] */ + printf("IGRAPH_ADJ_UPPER\n"); + printf("================\n\n"); + test_single_matrix(&mat, IGRAPH_ADJ_UPPER); + + /* [ 0 - - - ] + [ 2 0 - - ] + [ 0 0 4 - ] + [ 0 1 0 0 ] */ + printf("IGRAPH_ADJ_LOWER\n"); + printf("================\n\n"); + test_single_matrix(&mat, IGRAPH_ADJ_LOWER); + + /* [ 0 1 0 0 ] + [ 1 0 0 1 ] + [ 0 0 4 0 ] + [ 0 1 0 0 ] */ + printf("IGRAPH_ADJ_MIN\n"); + printf("==============\n\n"); + test_single_matrix(&mat, IGRAPH_ADJ_MIN); + + /* [ 0 2 2 0 ] + [ 2 0 0 1 ] + [ 2 0 4 0 ] + [ 0 1 0 0 ] */ + printf("IGRAPH_ADJ_MAX\n"); + printf("==============\n\n"); + test_single_matrix(&mat, IGRAPH_ADJ_MAX); + + /* [ 0 2 2 0 ] + [ 2 0 0 1 ] + [ 2 0 4 0 ] + [ 0 1 0 0 ] */ + printf("IGRAPH_ADJ_UNDIRECTED\n"); + printf("==============\n\n"); + test_single_matrix(&mat_sym, IGRAPH_ADJ_UNDIRECTED); + + /* [ 0 3 2 0 ] + [ 3 0 0 2 ] + [ 2 0 4 0 ] + [ 0 2 0 0 ] */ + printf("IGRAPH_ADJ_PLUS\n"); + printf("===============\n\n"); + test_single_matrix(&mat, IGRAPH_ADJ_PLUS); + + igraph_matrix_destroy(&mat); + igraph_matrix_destroy(&mat_sym); + + VERIFY_FINALLY_STACK(); + + { + igraph_real_t elem[] = { 0, 1.5, IGRAPH_INFINITY, + IGRAPH_NAN, -IGRAPH_INFINITY, -5.2, + IGRAPH_NAN, 0, IGRAPH_NAN }; + + igraph_real_t elem_sym[] = { 0, IGRAPH_NAN, IGRAPH_INFINITY, + IGRAPH_NAN, -IGRAPH_INFINITY, 0, + IGRAPH_INFINITY, 0, IGRAPH_NAN }; + + igraph_matrix_t am; + igraph_vector_t weights; + igraph_t graph; + + printf("\nTesting NaN and Inf passthrough\n"); + + igraph_vector_init(&weights, 0); + + matrix_init_real_row_major(&am, 3, 3, elem); + + printf("\nIGRAPH_ADJ_DIRECTED\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_DIRECTED, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_MAX\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_MAX, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_MIN\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_MIN, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_LOWER\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_LOWER, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_UPPER\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_UPPER, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_PLUS\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_PLUS, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + /* Must detect that the matrix is not symmetric and throw an error. */ + CHECK_ERROR( + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_UNDIRECTED, &weights, IGRAPH_LOOPS_TWICE), + IGRAPH_EINVAL); + + igraph_matrix_destroy(&am); + + matrix_init_real_row_major(&am, 3, 3, elem_sym); + + /* Must detect that the matrix is symmetric (desite the NaNs) and succeed. */ + printf("\nIGRAPH_ADJ_UNDIRECTED (symmetric)\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_UNDIRECTED, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + igraph_matrix_destroy(&am); + + igraph_vector_destroy(&weights); + + printf("\n"); + } + + VERIFY_FINALLY_STACK(); + + { + printf("Check handling of non-square matrix error.\n"); + igraph_real_t e[] = {1, 2, 0}; + igraph_matrix_view(&mat, e, 1, 3); + check_error(&mat, IGRAPH_ADJ_DIRECTED, IGRAPH_NO_LOOPS, IGRAPH_NONSQUARE); + } + + { + printf("Check handling of invalid adjacency mode.\n"); + igraph_real_t e[] = {0, 2, 0, 3, 0, 4, 0, 5, 6}; + igraph_matrix_view(&mat, e, 3, 3); + check_error(&mat, (igraph_adjacency_t) 42, IGRAPH_LOOPS_TWICE, IGRAPH_EINVAL); + } + + { + printf("Check error for 0x1 matrix.\n"); + igraph_matrix_init(&mat, 0, 1); + check_error(&mat, IGRAPH_ADJ_DIRECTED, IGRAPH_LOOPS_TWICE, IGRAPH_NONSQUARE); + igraph_matrix_destroy(&mat); + } + + { + printf("Check error for non-symmetric matrix and IGRAPH_ADJ_UNDIRECTED.\n"); + igraph_real_t e[] = {0, 2, 0, 3, 0, 4, 0, 5, 6}; + igraph_matrix_view(&mat, e, 3, 3); + check_error(&mat, IGRAPH_ADJ_UNDIRECTED, IGRAPH_LOOPS_TWICE, IGRAPH_EINVAL); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_weighted_adjacency.out b/tests/unit/igraph_weighted_adjacency.out new file mode 100644 index 0000000..394427e --- /dev/null +++ b/tests/unit/igraph_weighted_adjacency.out @@ -0,0 +1,383 @@ +0x0 matrix +========== + +No loops +-------- + +directed: true +vcount: 0 +edges: { +} + +Loops once +---------- + +directed: true +vcount: 0 +edges: { +} + +Loops twice +----------- + +directed: true +vcount: 0 +edges: { +} + +IGRAPH_ADJ_DIRECTED +=================== + +No loops +-------- + +directed: true +vcount: 4 +edges: { +0 1: 1 +0 2: 2 +1 0: 2 +1 3: 1 +3 1: 1 +} + +Loops once +---------- + +directed: true +vcount: 4 +edges: { +0 1: 1 +0 2: 2 +1 0: 2 +1 3: 1 +2 2: 4 +3 1: 1 +} + +Loops twice +----------- + +directed: true +vcount: 4 +edges: { +0 1: 1 +0 2: 2 +1 0: 2 +1 3: 1 +2 2: 2 +3 1: 1 +} + +IGRAPH_ADJ_UPPER +================ + +No loops +-------- + +directed: false +vcount: 4 +edges: { +1 0: 1 +2 0: 2 +3 1: 1 +} + +Loops once +---------- + +directed: false +vcount: 4 +edges: { +1 0: 1 +2 0: 2 +3 1: 1 +2 2: 4 +} + +Loops twice +----------- + +directed: false +vcount: 4 +edges: { +1 0: 1 +2 0: 2 +3 1: 1 +2 2: 2 +} + +IGRAPH_ADJ_LOWER +================ + +No loops +-------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +3 1: 1 +} + +Loops once +---------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +2 2: 4 +3 1: 1 +} + +Loops twice +----------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +2 2: 2 +3 1: 1 +} + +IGRAPH_ADJ_MIN +============== + +No loops +-------- + +directed: false +vcount: 4 +edges: { +1 0: 1 +3 1: 1 +} + +Loops once +---------- + +directed: false +vcount: 4 +edges: { +1 0: 1 +3 1: 1 +2 2: 4 +} + +Loops twice +----------- + +directed: false +vcount: 4 +edges: { +1 0: 1 +3 1: 1 +2 2: 2 +} + +IGRAPH_ADJ_MAX +============== + +No loops +-------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +2 0: 2 +3 1: 1 +} + +Loops once +---------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +2 0: 2 +3 1: 1 +2 2: 4 +} + +Loops twice +----------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +2 0: 2 +3 1: 1 +2 2: 2 +} + +IGRAPH_ADJ_UNDIRECTED +============== + +No loops +-------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +2 0: 2 +3 1: 1 +} + +Loops once +---------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +2 0: 2 +3 1: 1 +2 2: 4 +} + +Loops twice +----------- + +directed: false +vcount: 4 +edges: { +1 0: 2 +2 0: 2 +3 1: 1 +2 2: 2 +} + +IGRAPH_ADJ_PLUS +=============== + +No loops +-------- + +directed: false +vcount: 4 +edges: { +1 0: 3 +2 0: 2 +3 1: 2 +} + +Loops once +---------- + +directed: false +vcount: 4 +edges: { +1 0: 3 +2 0: 2 +3 1: 2 +2 2: 4 +} + +Loops twice +----------- + +directed: false +vcount: 4 +edges: { +1 0: 3 +2 0: 2 +3 1: 2 +2 2: 2 +} + + +Testing NaN and Inf passthrough + +IGRAPH_ADJ_DIRECTED +directed: true +vcount: 3 +edges: { +0 1 +0 2 +1 0 +1 1 +1 2 +2 0 +2 2 +} +( 1.5 Inf NaN -Inf -5.2 NaN NaN ) + +IGRAPH_ADJ_MAX +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 2 +} +( NaN NaN -Inf NaN ) + +IGRAPH_ADJ_MIN +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 1 +2 2 +} +( NaN NaN -Inf -5.2 NaN ) + +IGRAPH_ADJ_LOWER +directed: false +vcount: 3 +edges: { +1 0 +1 1 +2 0 +2 2 +} +( NaN -Inf NaN NaN ) + +IGRAPH_ADJ_UPPER +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 1 +2 2 +} +( 1.5 Inf -Inf -5.2 NaN ) + +IGRAPH_ADJ_PLUS +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 1 +2 2 +} +( NaN NaN -Inf -5.2 NaN ) + +IGRAPH_ADJ_UNDIRECTED (symmetric) +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 2 +} +( NaN Inf -Inf NaN ) + +Check handling of non-square matrix error. +Check handling of invalid adjacency mode. +Check error for 0x1 matrix. +Check error for non-symmetric matrix and IGRAPH_ADJ_UNDIRECTED. diff --git a/tests/unit/igraph_weighted_cliques.c b/tests/unit/igraph_weighted_cliques.c new file mode 100644 index 0000000..3dcb98a --- /dev/null +++ b/tests/unit/igraph_weighted_cliques.c @@ -0,0 +1,249 @@ + +#include +#include + +#include "test_utilities.h" + +int compare_vectors(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2) { + igraph_integer_t s1 = igraph_vector_int_size(v1); + igraph_integer_t s2 = igraph_vector_int_size(v2); + if (s1 < s2) { + return -1; + } + if (s1 > s2) { + return 1; + } + for (igraph_integer_t i = 0; i < s1; ++i) { + if (VECTOR(*v1)[i] < VECTOR(*v2)[i]) { + return -1; + } + if (VECTOR(*v1)[i] > VECTOR(*v2)[i]) { + return 1; + } + } + return 0; +} + +/* Takes a pointer vector of vectors. Sorts each vector, then sorts the pointer vector */ +void canonicalize_list(igraph_vector_int_list_t *list) { + igraph_integer_t len = igraph_vector_int_list_size(list); + for (igraph_integer_t i = 0; i < len; ++i) { + igraph_vector_int_sort(igraph_vector_int_list_get_ptr(list, i)); + } + igraph_vector_int_list_sort(list, &compare_vectors); +} + +/* Prints a clique vector along with its weight */ +void print_weighted_clique(const igraph_vector_int_t *clique, const igraph_vector_t *vertex_weights) { + igraph_integer_t i, n = igraph_vector_int_size(clique); + igraph_real_t clique_weight = 0.0; + for (i = 0; i < n; i++) { + int v = VECTOR(*clique)[i]; + clique_weight += vertex_weights ? igraph_vector_get(vertex_weights, v) : 1; + printf(" %d", v); + } + printf(" w=%g\n", clique_weight); +} + +/* Prints a clique list and clears it */ +void print_and_clear_weighted_clique_list(igraph_vector_int_list_t *cliques, const igraph_vector_t *vertex_weights) { + igraph_integer_t i, count; + + canonicalize_list(cliques); + + count = igraph_vector_int_list_size(cliques); + for (i = 0; i < count; i++) { + igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(cliques, i); + print_weighted_clique(v, vertex_weights); + } + + igraph_vector_int_list_clear(cliques); +} + +int main(void) { + igraph_t graph; + + const igraph_integer_t n = 10; /* number of vertices in test graph */ + + /* edges of the test graph */ + igraph_vector_int_t edges; + igraph_integer_t edge_data[] = {0, 1, 0, 6, 0, 7, 0, 8, 0, 9, 1, 2, 1, 3, 1, 4, 1, + 6, 1, 7, 1, 8, 1, 9, 2, 3, 2, 5, 2, 6, 2, 7, 2, 9, + 3, 5, 3, 6, 3, 7, 3, 9, 4, 5, 4, 6, 4, 7, 4, 9, 5, + 8, 6, 7, 6, 8, 7, 8, 8, 9 + }; + + /* vertex weights in test graph, + note that current implementation only supports integer weights */ + igraph_vector_t vertex_weights; + igraph_real_t vertex_weight_data[] = {3., 2., 3., 5., 2., 3., 1., 3., 5., 5.}; + + igraph_vector_int_list_t result; /* result clique list */ + igraph_integer_t count; /* number of cliques found */ + + igraph_real_t weighted_clique_no; + + + /* create graph */ + igraph_vector_int_init_array(&edges, edge_data, (sizeof edge_data) / sizeof(edge_data[0])); + igraph_create(&graph, &edges, n, IGRAPH_UNDIRECTED); + + /* set up vertex weight vector */ + igraph_vector_init_array(&vertex_weights, vertex_weight_data, (sizeof vertex_weight_data) / sizeof(vertex_weight_data[0])); + + /* initialize result vector_ptr */ + igraph_vector_int_list_init(&result, 0); + + + /* all weighted cliques above weight 6 */ + igraph_weighted_cliques(&graph, &vertex_weights, &result, 6, 0, /* maximal= */ false); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " weighted cliques found above weight 6\n", count); + print_and_clear_weighted_clique_list(&result, &vertex_weights); + + + /* all weighted cliques between weights 5 and 10 */ + igraph_weighted_cliques(&graph, &vertex_weights, &result, 5, 10, /* maximal= */ false); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " weighted cliques found between weights 5 and 10\n", count); + print_and_clear_weighted_clique_list(&result, &vertex_weights); + + + /* maximal weighted cliques above weight 7 */ + igraph_weighted_cliques(&graph, &vertex_weights, &result, 7, 0, /* maximal= */ true); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " maximal weighted cliques found above weight 7\n", count); + print_and_clear_weighted_clique_list(&result, &vertex_weights); + + + /* maximal weighed cliques beteen weights 5 and 10 */ + igraph_weighted_cliques(&graph, &vertex_weights, &result, 5, 10, /* maximal= */ true); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " maximal weighted cliques found between weights 5 and 10\n", count); + print_and_clear_weighted_clique_list(&result, &vertex_weights); + + + /* largest weight cliques */ + igraph_largest_weighted_cliques(&graph, &vertex_weights, &result); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " largest weight cliques found\n", count); + print_and_clear_weighted_clique_list(&result, &vertex_weights); + + igraph_weighted_clique_number(&graph, &vertex_weights, &weighted_clique_no); + printf("weighted clique number: %g\n", weighted_clique_no); + + + /* Test unweighted fallback: */ + printf("\nUnweighted case:\n"); + + /* test fallback to unweighted variants: all cliques */ + igraph_weighted_cliques(&graph, NULL, &result, 4, 5, /* maximal= */ false); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " unweighted cliques found between sizes 4 and 5\n", count); + print_and_clear_weighted_clique_list(&result, 0); + + /* test fallback to unweighted variants: maximal cliques */ + igraph_weighted_cliques(&graph, NULL, &result, 4, 5, /* maximal= */ true); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " unweighted maximal cliques found between sizes 4 and 5\n", count); + print_and_clear_weighted_clique_list(&result, 0); + + /* test fallback to unweighted variants: largest cliques */ + igraph_largest_weighted_cliques(&graph, NULL, &result); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " largest unweighted cliques found\n", count); + print_and_clear_weighted_clique_list(&result, 0); + + /* test fallback to unweighted variants: clique number */ + igraph_weighted_clique_number(&graph, NULL, &weighted_clique_no); + printf("unweighted clique number: %g\n", weighted_clique_no); + + /* Here we test unit weights, which should give identical results to the unweighted case: */ + printf("\nUnit weights:\n"); + + igraph_vector_fill(&vertex_weights, 1); + + /* test unit weights: all cliques */ + igraph_weighted_cliques(&graph, &vertex_weights, &result, 4, 5, /* maximal= */ false); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " cliques with unit weights found between sizes 4 and 5\n", count); + print_and_clear_weighted_clique_list(&result, 0); + + /* test unit weights: maximal cliques */ + igraph_weighted_cliques(&graph, &vertex_weights, &result, 4, 5, /* maximal= */ true); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " maximal cliques with unit weights between sizes 4 and 5\n", count); + print_and_clear_weighted_clique_list(&result, 0); + + /* test unit weights: largest cliques */ + igraph_largest_weighted_cliques(&graph, &vertex_weights, &result); + + count = igraph_vector_int_list_size(&result); + printf("%" IGRAPH_PRId " largest cliques with unit weights found\n", count); + print_and_clear_weighted_clique_list(&result, 0); + + /* test unit weights: clique number */ + igraph_weighted_clique_number(&graph, NULL, &weighted_clique_no); + printf("clique number with unit weights: %g\n", weighted_clique_no); + + + /* free data structures */ + igraph_vector_int_list_destroy(&result); + igraph_vector_destroy(&vertex_weights); + igraph_destroy(&graph); + igraph_vector_int_destroy(&edges); + + /* additional small examples */ + + printf("\nP_2 graph with weights (5, 5):\n"); + igraph_small(&graph, 2, IGRAPH_UNDIRECTED, + 0,1, + -1); + + igraph_vector_init_int(&vertex_weights, 2, + 5, 5); + + igraph_weighted_clique_number(&graph, &vertex_weights, &weighted_clique_no); + printf("weighted clique number: %g\n", weighted_clique_no); + + printf("\nP_2 graph with weights (5, 4):\n"); + VECTOR(vertex_weights)[1] = 4; + + igraph_weighted_clique_number(&graph, &vertex_weights, &weighted_clique_no); + printf("weighted clique number: %g\n", weighted_clique_no); + + igraph_vector_destroy(&vertex_weights); + igraph_destroy(&graph); + + printf("\nK_3 graph with weights (3, 3, 3):\n"); + igraph_full(&graph, 3, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_vector_init_int(&vertex_weights, 3, + 3, 3, 3); + + igraph_weighted_clique_number(&graph, &vertex_weights, &weighted_clique_no); + printf("weighted clique number: %g\n", weighted_clique_no); + + printf("\nK_3 graph with weights (3, 4, 3):\n"); + VECTOR(vertex_weights)[1] = 4; + + igraph_weighted_clique_number(&graph, &vertex_weights, &weighted_clique_no); + printf("weighted clique number: %g\n", weighted_clique_no); + + igraph_vector_destroy(&vertex_weights); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_weighted_cliques.out b/tests/unit/igraph_weighted_cliques.out new file mode 100644 index 0000000..5219e23 --- /dev/null +++ b/tests/unit/igraph_weighted_cliques.out @@ -0,0 +1,204 @@ +63 weighted cliques found above weight 6 + 0 7 w=6 + 0 8 w=8 + 0 9 w=8 + 1 3 w=7 + 1 8 w=7 + 1 9 w=7 + 2 3 w=8 + 2 5 w=6 + 2 7 w=6 + 2 9 w=8 + 3 5 w=8 + 3 6 w=6 + 3 7 w=8 + 3 9 w=10 + 4 9 w=7 + 5 8 w=8 + 6 8 w=6 + 7 8 w=8 + 8 9 w=10 + 0 1 6 w=6 + 0 1 7 w=8 + 0 1 8 w=10 + 0 1 9 w=10 + 0 6 7 w=7 + 0 6 8 w=9 + 0 7 8 w=11 + 0 8 9 w=13 + 1 2 3 w=10 + 1 2 6 w=6 + 1 2 7 w=8 + 1 2 9 w=10 + 1 3 6 w=8 + 1 3 7 w=10 + 1 3 9 w=12 + 1 4 7 w=7 + 1 4 9 w=9 + 1 6 7 w=6 + 1 6 8 w=8 + 1 7 8 w=10 + 1 8 9 w=12 + 2 3 5 w=11 + 2 3 6 w=9 + 2 3 7 w=11 + 2 3 9 w=13 + 2 6 7 w=7 + 3 6 7 w=9 + 4 6 7 w=6 + 6 7 8 w=9 + 0 1 6 7 w=9 + 0 1 6 8 w=11 + 0 1 7 8 w=13 + 0 1 8 9 w=15 + 0 6 7 8 w=12 + 1 2 3 6 w=11 + 1 2 3 7 w=13 + 1 2 3 9 w=15 + 1 2 6 7 w=9 + 1 3 6 7 w=11 + 1 4 6 7 w=8 + 1 6 7 8 w=11 + 2 3 6 7 w=12 + 0 1 6 7 8 w=14 + 1 2 3 6 7 w=14 +53 weighted cliques found between weights 5 and 10 + 3 w=5 + 8 w=5 + 9 w=5 + 0 1 w=5 + 0 7 w=6 + 0 8 w=8 + 0 9 w=8 + 1 2 w=5 + 1 3 w=7 + 1 7 w=5 + 1 8 w=7 + 1 9 w=7 + 2 3 w=8 + 2 5 w=6 + 2 7 w=6 + 2 9 w=8 + 3 5 w=8 + 3 6 w=6 + 3 7 w=8 + 3 9 w=10 + 4 5 w=5 + 4 7 w=5 + 4 9 w=7 + 5 8 w=8 + 6 8 w=6 + 7 8 w=8 + 8 9 w=10 + 0 1 6 w=6 + 0 1 7 w=8 + 0 1 8 w=10 + 0 1 9 w=10 + 0 6 7 w=7 + 0 6 8 w=9 + 1 2 3 w=10 + 1 2 6 w=6 + 1 2 7 w=8 + 1 2 9 w=10 + 1 3 6 w=8 + 1 3 7 w=10 + 1 4 6 w=5 + 1 4 7 w=7 + 1 4 9 w=9 + 1 6 7 w=6 + 1 6 8 w=8 + 1 7 8 w=10 + 2 3 6 w=9 + 2 6 7 w=7 + 3 6 7 w=9 + 4 6 7 w=6 + 6 7 8 w=9 + 0 1 6 7 w=9 + 1 2 6 7 w=9 + 1 4 6 7 w=8 +8 maximal weighted cliques found above weight 7 + 5 8 w=8 + 1 4 9 w=9 + 2 3 5 w=11 + 0 1 8 9 w=15 + 1 2 3 9 w=15 + 1 4 6 7 w=8 + 0 1 6 7 8 w=14 + 1 2 3 6 7 w=14 +4 maximal weighted cliques found between weights 5 and 10 + 4 5 w=5 + 5 8 w=8 + 1 4 9 w=9 + 1 4 6 7 w=8 +2 largest weight cliques found + 0 1 8 9 w=15 + 1 2 3 9 w=15 +weighted clique number: 15 + +Unweighted case: +15 unweighted cliques found between sizes 4 and 5 + 0 1 6 7 w=4 + 0 1 6 8 w=4 + 0 1 7 8 w=4 + 0 1 8 9 w=4 + 0 6 7 8 w=4 + 1 2 3 6 w=4 + 1 2 3 7 w=4 + 1 2 3 9 w=4 + 1 2 6 7 w=4 + 1 3 6 7 w=4 + 1 4 6 7 w=4 + 1 6 7 8 w=4 + 2 3 6 7 w=4 + 0 1 6 7 8 w=5 + 1 2 3 6 7 w=5 +5 unweighted maximal cliques found between sizes 4 and 5 + 0 1 8 9 w=4 + 1 2 3 9 w=4 + 1 4 6 7 w=4 + 0 1 6 7 8 w=5 + 1 2 3 6 7 w=5 +2 largest unweighted cliques found + 0 1 6 7 8 w=5 + 1 2 3 6 7 w=5 +unweighted clique number: 5 + +Unit weights: +15 cliques with unit weights found between sizes 4 and 5 + 0 1 6 7 w=4 + 0 1 6 8 w=4 + 0 1 7 8 w=4 + 0 1 8 9 w=4 + 0 6 7 8 w=4 + 1 2 3 6 w=4 + 1 2 3 7 w=4 + 1 2 3 9 w=4 + 1 2 6 7 w=4 + 1 3 6 7 w=4 + 1 4 6 7 w=4 + 1 6 7 8 w=4 + 2 3 6 7 w=4 + 0 1 6 7 8 w=5 + 1 2 3 6 7 w=5 +5 maximal cliques with unit weights between sizes 4 and 5 + 0 1 8 9 w=4 + 1 2 3 9 w=4 + 1 4 6 7 w=4 + 0 1 6 7 8 w=5 + 1 2 3 6 7 w=5 +2 largest cliques with unit weights found + 0 1 6 7 8 w=5 + 1 2 3 6 7 w=5 +clique number with unit weights: 5 + +P_2 graph with weights (5, 5): +weighted clique number: 10 + +P_2 graph with weights (5, 4): +weighted clique number: 9 + +K_3 graph with weights (3, 3, 3): +weighted clique number: 9 + +K_3 graph with weights (3, 4, 3): +weighted clique number: 10 diff --git a/tests/unit/igraph_wheel.c b/tests/unit/igraph_wheel.c new file mode 100644 index 0000000..bd5385d --- /dev/null +++ b/tests/unit/igraph_wheel.c @@ -0,0 +1,56 @@ +/* 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 . +*/ + +#include +#include "test_utilities.h" + +void call_and_print(igraph_t *graph, igraph_integer_t n, igraph_wheel_mode_t mode, + igraph_integer_t center) { + + IGRAPH_ASSERT(igraph_wheel(graph, n, mode, center) == IGRAPH_SUCCESS); + print_graph_canon(graph); + igraph_destroy(graph); + printf("\n"); +} + +int main(void) { + igraph_t graph; + + printf("-- Test graph with 1 vertex --\n"); + call_and_print(&graph, 1, IGRAPH_WHEEL_UNDIRECTED, 0); + printf("-- Test graph with 2 vertices --\n"); + call_and_print(&graph, 2, IGRAPH_WHEEL_UNDIRECTED, 0); + printf("-- Test graph with OUT mode --\n"); + call_and_print(&graph, 4, IGRAPH_WHEEL_OUT, 0); + printf("-- Test graph with IN mode --\n"); + call_and_print(&graph, 4, IGRAPH_WHEEL_IN, 0); + printf("-- Test graph with MUTUAL mode --\n"); + call_and_print(&graph, 4, IGRAPH_WHEEL_MUTUAL, 0); + printf("-- Test graph with UNDIRECTED mode \n"); + call_and_print(&graph, 4, IGRAPH_WHEEL_UNDIRECTED, 0); + printf("-- Test graph with center equal to n/2 --\n"); + call_and_print(&graph, 4, IGRAPH_WHEEL_OUT, 2); + printf("-- Test graph with center equal to n - 1: --\n"); + call_and_print(&graph, 4, IGRAPH_WHEEL_OUT, 3); + printf("-- Test graph with center equal to 1: --\n"); + call_and_print(&graph, 4, IGRAPH_WHEEL_OUT, 1); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_wheel.out b/tests/unit/igraph_wheel.out new file mode 100644 index 0000000..ef75dda --- /dev/null +++ b/tests/unit/igraph_wheel.out @@ -0,0 +1,104 @@ +-- Test graph with 1 vertex -- +directed: false +vcount: 0 +edges: { +} + +-- Test graph with 2 vertices -- +directed: false +vcount: 2 +edges: { +0 1 +1 1 +} + +-- Test graph with OUT mode -- +directed: true +vcount: 4 +edges: { +0 1 +0 2 +0 3 +1 2 +2 3 +3 1 +} + +-- Test graph with IN mode -- +directed: true +vcount: 4 +edges: { +1 0 +1 2 +2 0 +2 3 +3 0 +3 1 +} + +-- Test graph with MUTUAL mode -- +directed: true +vcount: 4 +edges: { +0 1 +0 2 +0 3 +1 0 +1 2 +1 3 +2 0 +2 1 +2 3 +3 0 +3 1 +3 2 +} + +-- Test graph with UNDIRECTED mode +directed: false +vcount: 4 +edges: { +0 1 +0 2 +0 3 +1 2 +1 3 +2 3 +} + +-- Test graph with center equal to n/2 -- +directed: true +vcount: 4 +edges: { +0 1 +1 3 +2 0 +2 1 +2 3 +3 0 +} + +-- Test graph with center equal to n - 1: -- +directed: true +vcount: 4 +edges: { +0 1 +1 2 +2 0 +3 0 +3 1 +3 2 +} + +-- Test graph with center equal to 1: -- +directed: true +vcount: 4 +edges: { +0 2 +1 0 +1 2 +1 3 +2 3 +3 0 +} + diff --git a/tests/unit/igraph_widest_paths.c b/tests/unit/igraph_widest_paths.c new file mode 100644 index 0000000..39364e7 --- /dev/null +++ b/tests/unit/igraph_widest_paths.c @@ -0,0 +1,547 @@ +/* + 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 . +*/ + + +#include + +#include "test_utilities.h" + +/* initialise structures used in "get_widest_path(s)" algorithms */ +void init_vertices_and_edges(igraph_integer_t n, + igraph_vector_int_list_t *vertices, igraph_vector_int_list_t *edges, + igraph_vector_int_t *parents, igraph_vector_int_t *inbound_edges, + igraph_vector_int_t *vertices2, igraph_vector_int_t *edges2) { + igraph_vector_int_list_init(vertices, n); + igraph_vector_int_list_init(edges, n); + + igraph_vector_int_init(parents, 0); + igraph_vector_int_init(inbound_edges, 0); + + igraph_vector_int_init(vertices2, 0); + igraph_vector_int_init(edges2, 0); +} + +/* destroy structures used in "get_widest_path(s)" algorithms, ignores if NULL*/ +void destroy_vertices_and_edges(igraph_vector_int_list_t *vertices, igraph_vector_int_list_t *edges, + igraph_vector_int_t *parents, igraph_vector_int_t *inbound_edges, + igraph_vector_int_t *vertices2, igraph_vector_int_t *edges2) { + if (edges2) igraph_vector_int_destroy(edges2); + if (vertices2) igraph_vector_int_destroy(vertices2); + + if (inbound_edges) igraph_vector_int_destroy(inbound_edges); + if (parents) igraph_vector_int_destroy(parents); + + if (edges) igraph_vector_int_list_destroy(edges); + if (vertices) igraph_vector_int_list_destroy(vertices); +} + +/* destroy all structures, ignores if NULL */ +void destroy_all(igraph_t *g, igraph_vector_t *w, igraph_matrix_t *res1, igraph_matrix_t *res2, + igraph_vs_t *from, igraph_vs_t *to, + igraph_vector_int_list_t *vertices, igraph_vector_int_list_t *edges, + igraph_vector_int_t *parents, igraph_vector_int_t *inbound_edges, + igraph_vector_int_t *vertices2, igraph_vector_int_t *edges2) { + + destroy_vertices_and_edges(vertices, edges, parents, inbound_edges, vertices2, edges2); + + if (to) igraph_vs_destroy(to); + if (from) igraph_vs_destroy(from); + if (res2) igraph_matrix_destroy(res2); + if (res1) igraph_matrix_destroy(res1); + + if (w) igraph_vector_destroy(w); + if (g) igraph_destroy(g); +} + +/* confirm that all widest paths algorithms fail */ +void check_invalid_input(igraph_t *g, igraph_vector_t *w, igraph_matrix_t *res, + igraph_integer_t source, igraph_integer_t destination, + igraph_vs_t *from, igraph_vs_t *to, + igraph_vector_int_list_t *vertices, igraph_vector_int_list_t *edges, + igraph_vector_int_t *parents, igraph_vector_int_t *inbound_edges, + igraph_vector_int_t *vertices2, igraph_vector_int_t *edges2) { + CHECK_ERROR(igraph_widest_path_widths_dijkstra(g, res, *from, *to, w, IGRAPH_OUT), IGRAPH_EINVAL); + CHECK_ERROR(igraph_widest_path_widths_floyd_warshall(g, res, *from, *to, w, IGRAPH_OUT), IGRAPH_EINVAL); + CHECK_ERROR(igraph_get_widest_paths(g, vertices, edges, source, *to, w, IGRAPH_OUT, + parents, inbound_edges), IGRAPH_EINVAL); + CHECK_ERROR(igraph_get_widest_path(g, vertices2, edges2, source, destination, w, IGRAPH_OUT), IGRAPH_EINVAL); +} + +/* runs the width finding algorithms and assert they succeed */ +void run_widest_paths(igraph_t *g, igraph_vector_t *w, igraph_matrix_t *res1, igraph_matrix_t *res2, + igraph_vs_t *from, igraph_vs_t *to, igraph_neimode_t mode) { + IGRAPH_ASSERT(igraph_widest_path_widths_dijkstra(g, res1, *from, *to, w, mode) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_widest_path_widths_floyd_warshall(g, res2, *from, *to, w, mode) == IGRAPH_SUCCESS); +} + +/* runs the path finding algorithms and asserts they succeed */ +void run_get_widest_paths(igraph_t *g, igraph_vector_t *w, + igraph_integer_t source, igraph_integer_t destination, + igraph_vs_t *to, igraph_neimode_t mode, + igraph_vector_int_list_t *vertices, igraph_vector_int_list_t *edges, + igraph_vector_int_t *parents, igraph_vector_int_t *inbound_edges, + igraph_vector_int_t *vertices2, igraph_vector_int_t *edges2) { + IGRAPH_ASSERT(igraph_get_widest_paths(g, vertices, edges, source, *to, w, mode, + parents, inbound_edges) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_get_widest_path(g, vertices2, edges2, source, destination, + w, mode) == IGRAPH_SUCCESS); +} + +/* print results of just the matrices */ +void check_and_print_matrices(igraph_matrix_t *res1, igraph_matrix_t *res2) { + IGRAPH_ASSERT(igraph_matrix_all_e(res1, res2)); + print_matrix_format(res1, stdout, "%f"); +} + +/* prints results of all widest path algorithms */ +void print_results(igraph_integer_t n, igraph_matrix_t *res1, igraph_matrix_t *res2, + igraph_vector_int_list_t *vertices, igraph_vector_int_list_t *edges, + igraph_vector_int_t *parents, igraph_vector_int_t *inbound_edges, + igraph_vector_int_t *vertices2, igraph_vector_int_t *edges2) { + igraph_integer_t i; + + check_and_print_matrices(res1, res2); + printf("\n"); + + for (i = 0; i < n; i++) { + printf("path to node %" IGRAPH_PRId ":\n", i); + igraph_vector_int_t *vertex_path = igraph_vector_int_list_get_ptr(vertices, i); + igraph_vector_int_t *edge_path = igraph_vector_int_list_get_ptr(edges, i); + printf(" vertices: "); + print_vector_int(vertex_path); + printf(" edges: "); + print_vector_int(edge_path); + } + printf("\n"); + + printf("parents: "); + print_vector_int(parents); + printf("inbound_edges: "); + print_vector_int(inbound_edges); + printf("\n"); + + printf("vertex path: "); + print_vector_int(vertices2); + printf("edge path: "); + print_vector_int(edges2); + printf("\n"); +} + + +int main(void) { + + igraph_integer_t n, m; + igraph_t g; + igraph_matrix_t res1, res2; + igraph_vs_t from, to; + igraph_vector_t w; + + igraph_vector_int_list_t vertices; + igraph_vector_int_list_t edges; + igraph_vector_int_t parents; + igraph_vector_int_t inbound_edges; + + igraph_vector_int_t vertices2; + igraph_vector_int_t edges2; + + + /* ==================================================================== */ + /* 1. null weight vector */ + + n = 3; + m = 3; + igraph_small(&g, n, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + + igraph_matrix_init(&res1, n, n); + igraph_vs_range(&from, 0, n); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + check_invalid_input(&g, NULL, &res1, 0, 2, &from, &to, &vertices, &edges, + &parents, &inbound_edges, &vertices2, &edges2); + + destroy_vertices_and_edges(&vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, NULL, &res1, NULL, &from, &to, NULL, NULL, NULL, NULL, NULL, NULL); + + /* ==================================================================== */ + /* 2. Number of weights don't match number of edges */ + + n = 3; + m = 3; + igraph_small(&g, n, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + igraph_vector_init_real(&w, m+3, -1.0, 2.0, 3.0, 2.0, 5.0, 1.0); + + igraph_matrix_init(&res1, n, n); + igraph_vs_range(&from, 0, n); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + check_invalid_input(&g, &w, &res1, 0, 2, &from, &to, &vertices, &edges, + &parents, &inbound_edges, &vertices2, &edges2); + + destroy_vertices_and_edges(&vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, NULL, &from, &to, NULL, NULL, NULL, NULL, NULL, NULL); + + /* ==================================================================== */ + /* 3. NaN values in weights */ + + n = 3; + m = 3; + igraph_small(&g, n, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 2, 0, -1); + igraph_vector_init_real(&w, m, -1.0, IGRAPH_NAN, 3.0); + + igraph_matrix_init(&res1, n, n); + igraph_vs_range(&from, 0, n); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + check_invalid_input(&g, &w, &res1, 0, 2, &from, &to, &vertices, &edges, + &parents, &inbound_edges, &vertices2, &edges2); + + destroy_vertices_and_edges(&vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, NULL, &from, &to, NULL, NULL, NULL, NULL, NULL, NULL); + + /* ==================================================================== */ + /* 4. Empty graph */ + + n = 0; + m = 0; + igraph_small(&g, n, IGRAPH_UNDIRECTED, -1); + igraph_vector_init(&w, m); + + igraph_matrix_init(&res1, n, n); + igraph_matrix_init(&res2, n, n); + igraph_vs_none(&from); + igraph_vs_none(&to); + + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + /* Should successfully run with nothing occuring */ + + destroy_all(&g, &w, &res1, &res2, &from, &to, NULL, NULL, NULL, NULL, NULL, NULL); + + /* ==================================================================== */ + /* 5. 1 node graph */ + printf("=== 5. Testing 1 Node Graph ===\n"); + + n = 1; + m = 0; + igraph_small(&g, n, IGRAPH_UNDIRECTED, -1); + igraph_vector_init(&w, m); + + igraph_matrix_init(&res1, n, n); + igraph_matrix_init(&res2, n, n); + igraph_vs_all(&from); + igraph_vs_all(&to); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 0, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + + /* ==================================================================== */ + /* 6. Unreachable Nodes */ + printf("\n=== 6. Testing Unreachable Nodes ===\n"); + + n = 4; + m = 4; + igraph_small(&g, n, IGRAPH_DIRECTED, 0, 2, 0, 1, 1, 2, 3, 2, -1); + igraph_vector_init_real(&w, m, 1.0, 2.0, 3.0, 5.0); + + igraph_matrix_init(&res1, 1, n); + igraph_matrix_init(&res2, 1, n); + igraph_vs_1(&from, 0); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 3, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + + /* ==================================================================== */ + /* 7. Self Loops */ + printf("\n=== 7. Testing Self Loops ===\n"); + + n = 3; + m = 6; + igraph_small(&g, n, IGRAPH_UNDIRECTED, 0, 2, 0, 1, 1, 2, 0, 0, 1, 1, 2, 2, -1); + igraph_vector_init_real(&w, m, 1.0, 2.0, 3.0, 5.0, 5.0, 1.0); + + igraph_matrix_init(&res1, n, n); + igraph_matrix_init(&res2, n, n); + igraph_vs_all(&from); + igraph_vs_all(&to); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 2, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + /* Self loops should be effectively ignored, since the widest path to + yourself is always infinity. */ + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + + /* ==================================================================== */ + /* 8. Multiple Edges */ + printf("\n=== 8. Testing Multiple Edges ===\n"); + + n = 2; + m = 8; + igraph_small(&g, n, IGRAPH_UNDIRECTED, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, -1); + igraph_vector_init_real(&w, m, 2.0, 2.0, 2.0, 10.0, 2.0, 2.0, 2.0, 2.0); + + igraph_matrix_init(&res1, 1, n); + igraph_matrix_init(&res2, 1, n); + igraph_vs_1(&from, 0); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 1, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + + /* ==================================================================== */ + /* 9. Directed Graphs */ + printf("\n=== 9. Testing Directed Graphs ===\n"); + + n = 2; + m = 6; + igraph_small(&g, n, IGRAPH_DIRECTED, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1); + igraph_vector_init_real(&w, m, 100.0, 200.0, 100.0, 200.0, 1.0, 200.0 ); + + igraph_matrix_init(&res1, 1, n); + igraph_matrix_init(&res2, 1, n); + igraph_vs_1(&from, 0); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 1, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + + /* ==================================================================== */ + /* 10. Mode */ + printf("\n=== 10. Testing Mode ===\n"); + /* This is same as test 9, just with the mode reversed. */ + + n = 2; + m = 6; + igraph_small(&g, n, IGRAPH_DIRECTED, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, -1); + igraph_vector_init_real(&w, m, 100.0, 200.0, 100.0, 200.0, 1.0, 200.0 ); + + igraph_matrix_init(&res1, 1, n); + igraph_matrix_init(&res2, 1, n); + igraph_vs_1(&from, 0); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_IN); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 1, &to, IGRAPH_IN, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + + /* ==================================================================== */ + /* 11. Multiple Widest Paths */ + printf("\n=== 11. Testing Multiple Widest Paths ===\n"); + + n = 8; + m = 9; + igraph_small(&g, n, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 7, 0, 3, 3, 4, 4, 7, + 0, 5, 5, 6, 6, 7, -1); + igraph_vector_init_real(&w, m, 100.0, 10.0, 200.0, 10.0, 15.0, 35.0, 3300.0, 10.0, 10.0 ); + + igraph_matrix_init(&res1, n, n); + igraph_matrix_init(&res2, n, n); + igraph_vs_1(&from, 0); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 7, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + check_and_print_matrices(&res1, &res2); + + printf("\npath to node 7:\n"); + igraph_vector_int_t *vertex_path = igraph_vector_int_list_get_ptr(&vertices, 7); + igraph_vector_int_t *edge_path = igraph_vector_int_list_get_ptr(&edges, 7); + + printf(" vertices: "); + print_vector_int(vertex_path); + printf(" edges: "); + print_vector_int(edge_path); + printf("parents: "); + print_vector_int(&parents); + printf("inbound_edges: "); + print_vector_int(&inbound_edges); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + /* ==================================================================== */ + /* 12. 5 Node Simple Graph */ + printf("\n=== 12. Testing 5 Node Simple Graph ===\n"); + + n = 5; + m = 5; + igraph_small(&g, n, IGRAPH_UNDIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 0, 3, + -1); + igraph_vector_init_real(&w, m, 8.0, 6.0, 10.0, 7.0, 5.0); + + igraph_matrix_init(&res1, n, n); + igraph_matrix_init(&res2, n, n); + igraph_vs_all(&from); + igraph_vs_all(&to); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 4, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + /* ==================================================================== */ + /* 13. 7 Node Wikipedia Graph */ + printf("\n=== 13. Testing 7 Node Wikipedia Graph ===\n"); + + n = 7; + m = 11; + igraph_small(&g, n, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 2, 5, + 3, 4, 3, 6, 4, 5, 4, 6, 5, 6, + -1); + igraph_vector_init_real(&w, m, 15.0, 53.0, 40.0, 46.0, 31.0, + 17.0, 3.0, 11.0, 29.0, 8.0, 40.0); + + igraph_matrix_init(&res1, 1, n); + igraph_matrix_init(&res2, 1, n); + igraph_vs_1(&from, 3); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 3, /* destination */ 6, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + + /* ==================================================================== */ + /* 14. Negative Widths */ + printf("\n=== 14. Testing Negative Weights ===\n"); + + n = 4; + m = 4; + igraph_small(&g, n, IGRAPH_DIRECTED, 0, 1, 1, 2, 0, 3, 3, 2, -1); + igraph_vector_init_real(&w, m, 2.0, -3.0, -5.0, 6.0); + + igraph_matrix_init(&res1, 1, n); + igraph_matrix_init(&res2, 1, n); + igraph_vs_1(&from, 0); + igraph_vs_range(&to, 0, n); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 2, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + + /* ==================================================================== */ + /* 15. Disconnected Graph */ + printf("\n=== 15. Testing Disconnected Graphs ===\n"); + + n = 5; + m = 4; + igraph_small(&g, n, IGRAPH_UNDIRECTED, 0, 1, 1, 2, 0, 2, 3, 4, -1); + igraph_vector_init_real(&w, m, 2.0, 5.0, 5.0, 7.0); + + igraph_matrix_init(&res1, n, n); + igraph_matrix_init(&res2, n, n); + igraph_vs_all(&from); + igraph_vs_all(&to); + + init_vertices_and_edges(n, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + run_widest_paths(&g, &w, &res1, &res2, &from, &to, IGRAPH_OUT); + run_get_widest_paths(&g, &w, /* source */ 0, /* destination */ 4, &to, IGRAPH_OUT, + &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + print_results(n, &res1, &res2, &vertices, &edges, &parents, &inbound_edges, &vertices2, &edges2); + + destroy_all(&g, &w, &res1, &res2, &from, &to, &vertices, &edges, &parents, &inbound_edges, + &vertices2, &edges2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_widest_paths.out b/tests/unit/igraph_widest_paths.out new file mode 100644 index 0000000..c481541 --- /dev/null +++ b/tests/unit/igraph_widest_paths.out @@ -0,0 +1,233 @@ +=== 5. Testing 1 Node Graph === +[ Inf ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) + +parents: ( -1 ) +inbound_edges: ( -1 ) + +vertex path: ( 0 ) +edge path: ( ) + + +=== 6. Testing Unreachable Nodes === +[ Inf 2.000000 2.000000 -Inf ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) +path to node 1: + vertices: ( 0 1 ) + edges: ( 1 ) +path to node 2: + vertices: ( 0 1 2 ) + edges: ( 1 2 ) +path to node 3: + vertices: ( ) + edges: ( ) + +parents: ( -1 0 1 -2 ) +inbound_edges: ( -1 1 2 -1 ) + +vertex path: ( ) +edge path: ( ) + + +=== 7. Testing Self Loops === +[ Inf 2.000000 2.000000 + 2.000000 Inf 3.000000 + 2.000000 3.000000 Inf ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) +path to node 1: + vertices: ( 0 1 ) + edges: ( 1 ) +path to node 2: + vertices: ( 0 1 2 ) + edges: ( 1 2 ) + +parents: ( -1 0 1 ) +inbound_edges: ( -1 1 2 ) + +vertex path: ( 0 1 2 ) +edge path: ( 1 2 ) + + +=== 8. Testing Multiple Edges === +[ Inf 10.000000 ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) +path to node 1: + vertices: ( 0 1 ) + edges: ( 3 ) + +parents: ( -1 0 ) +inbound_edges: ( -1 3 ) + +vertex path: ( 0 1 ) +edge path: ( 3 ) + + +=== 9. Testing Directed Graphs === +[ Inf 1.000000 ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) +path to node 1: + vertices: ( 0 1 ) + edges: ( 4 ) + +parents: ( -1 0 ) +inbound_edges: ( -1 4 ) + +vertex path: ( 0 1 ) +edge path: ( 4 ) + + +=== 10. Testing Mode === +[ Inf 200.000000 ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) +path to node 1: + vertices: ( 0 1 ) + edges: ( 5 ) + +parents: ( -1 0 ) +inbound_edges: ( -1 5 ) + +vertex path: ( 0 1 ) +edge path: ( 5 ) + + +=== 11. Testing Multiple Widest Paths === +[ Inf 100.000000 10.000000 10.000000 10.000000 3300.000000 10.000000 10.000000 ] + +path to node 7: + vertices: ( 0 1 2 7 ) + edges: ( 0 1 2 ) +parents: ( -1 0 1 0 3 0 5 2 ) +inbound_edges: ( -1 0 1 3 4 6 7 2 ) + +=== 12. Testing 5 Node Simple Graph === +[ Inf 8.000000 6.000000 6.000000 6.000000 + 8.000000 Inf 6.000000 6.000000 6.000000 + 6.000000 6.000000 Inf 10.000000 7.000000 + 6.000000 6.000000 10.000000 Inf 7.000000 + 6.000000 6.000000 7.000000 7.000000 Inf ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) +path to node 1: + vertices: ( 0 1 ) + edges: ( 0 ) +path to node 2: + vertices: ( 0 1 2 ) + edges: ( 0 1 ) +path to node 3: + vertices: ( 0 1 2 3 ) + edges: ( 0 1 2 ) +path to node 4: + vertices: ( 0 1 2 3 4 ) + edges: ( 0 1 2 3 ) + +parents: ( -1 0 1 2 3 ) +inbound_edges: ( -1 0 1 2 3 ) + +vertex path: ( 0 1 2 3 4 ) +edge path: ( 0 1 2 3 ) + + +=== 13. Testing 7 Node Wikipedia Graph === +[ 40.000000 46.000000 40.000000 Inf 31.000000 29.000000 29.000000 ] + +path to node 0: + vertices: ( 3 1 2 0 ) + edges: ( 3 2 1 ) +path to node 1: + vertices: ( 3 1 ) + edges: ( 3 ) +path to node 2: + vertices: ( 3 1 2 ) + edges: ( 3 2 ) +path to node 3: + vertices: ( 3 ) + edges: ( ) +path to node 4: + vertices: ( 3 1 2 4 ) + edges: ( 3 2 4 ) +path to node 5: + vertices: ( 3 1 2 4 5 ) + edges: ( 3 2 4 8 ) +path to node 6: + vertices: ( 3 1 2 4 5 6 ) + edges: ( 3 2 4 8 10 ) + +parents: ( 2 3 1 -1 2 4 5 ) +inbound_edges: ( 1 3 2 -1 4 8 10 ) + +vertex path: ( 3 1 2 4 5 6 ) +edge path: ( 3 2 4 8 10 ) + + +=== 14. Testing Negative Weights === +[ Inf 2.000000 -3.000000 -5.000000 ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) +path to node 1: + vertices: ( 0 1 ) + edges: ( 0 ) +path to node 2: + vertices: ( 0 3 2 ) + edges: ( 2 3 ) +path to node 3: + vertices: ( 0 3 ) + edges: ( 2 ) + +parents: ( -1 0 3 0 ) +inbound_edges: ( -1 0 3 2 ) + +vertex path: ( 0 1 2 ) +edge path: ( 0 1 ) + + +=== 15. Testing Disconnected Graphs === +[ Inf 5.000000 5.000000 -Inf -Inf + 5.000000 Inf 5.000000 -Inf -Inf + 5.000000 5.000000 Inf -Inf -Inf + -Inf -Inf -Inf Inf 7.000000 + -Inf -Inf -Inf 7.000000 Inf ] + +path to node 0: + vertices: ( 0 ) + edges: ( ) +path to node 1: + vertices: ( 0 2 1 ) + edges: ( 2 1 ) +path to node 2: + vertices: ( 0 2 ) + edges: ( 2 ) +path to node 3: + vertices: ( ) + edges: ( ) +path to node 4: + vertices: ( ) + edges: ( ) + +parents: ( -1 2 0 -2 -2 ) +inbound_edges: ( -1 1 2 -1 -1 ) + +vertex path: ( ) +edge path: ( ) + diff --git a/tests/unit/igraph_write_graph_dimacs_flow.c b/tests/unit/igraph_write_graph_dimacs_flow.c new file mode 100644 index 0000000..44c5ad1 --- /dev/null +++ b/tests/unit/igraph_write_graph_dimacs_flow.c @@ -0,0 +1,69 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_vector_t capacity; + igraph_integer_t source = 0; + igraph_integer_t target = 5; + + /* + Expected output: + + ``` + c created by igraph + p problem n_vertices n_edges + n source s + n target t + a arc_node_1 arc_node2 capacity + ``` + + We always outout max as the problem. + + */ + + igraph_small(&g, 6, IGRAPH_DIRECTED, + 0, 1, 0, 2, 1, 2, 1, 3, 2, 4, 3, 4, 3, 5, 4, 5, -1); + igraph_vector_init_int_end(&capacity, -1, 5, 2, 2, 3, 4, 1, 2, 5, -1); + + printf("DIMACS graph output:\n"); + igraph_write_graph_dimacs_flow(&g, stdout, source, target, &capacity); + + igraph_destroy(&g); + igraph_vector_destroy(&capacity); + + igraph_small(&g, 0, IGRAPH_DIRECTED, -1); + igraph_vector_init(&capacity, 0); + + /* Check that the function does not crash/misbehave on a null graph. + Note that currently igraph outputs DIMACS files for the max-flow + problem, which only makes sense if there are at least two vertices, + a source and the target. Here we use dummy values for them. */ + printf("\nDIMACS graph output for null graph:\n"); + igraph_write_graph_dimacs_flow(&g, stdout, source, target, &capacity); + + igraph_vector_destroy(&capacity); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_write_graph_dimacs_flow.out b/tests/unit/igraph_write_graph_dimacs_flow.out new file mode 100644 index 0000000..0d90169 --- /dev/null +++ b/tests/unit/igraph_write_graph_dimacs_flow.out @@ -0,0 +1,19 @@ +DIMACS graph output: +c created by igraph +p max 6 8 +n 1 s +n 6 t +a 1 2 5 +a 1 3 2 +a 2 3 2 +a 2 4 3 +a 3 5 4 +a 4 5 1 +a 4 6 2 +a 5 6 5 + +DIMACS graph output for null graph: +c created by igraph +p max 0 0 +n 1 s +n 6 t diff --git a/tests/unit/igraph_write_graph_dot.c b/tests/unit/igraph_write_graph_dot.c new file mode 100644 index 0000000..f7938f4 --- /dev/null +++ b/tests/unit/igraph_write_graph_dot.c @@ -0,0 +1,56 @@ +/* + 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, see . + */ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g; + + igraph_set_warning_handler(igraph_warning_handler_ignore); + igraph_set_attribute_table(&igraph_cattribute_table); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 4,3, 4,3, -1); + /* Set vertex attributes */ + SETVAN(&g, "VAN", 0, -1); + SETVAN(&g, "VAN", 1, 2.1); + SETVAN(&g, "VAN", 2, 1.23e-6); + SETVAN(&g, "VAN", 3, 1e7); + + SETVAS(&g, "VAS", 0, "foo"); + SETVAS(&g, "VAS", 1, "bar"); + + SETVAB(&g, "VAB", 0, 1); + SETVAB(&g, "VAB", 1, 0); + + /* Set edge attributes */ + SETEAN(&g, "EAN", 0, -100.1); + SETEAN(&g, "EAN", 2, 100.0); + + SETEAS(&g, "EAS", 0, "Blue"); + SETEAS(&g, "EAS", 2, "RED"); + + SETEAB(&g, "EAB", 0, 1); + SETEAB(&g, "EAB", 2, 0); + + igraph_write_graph_dot(&g, stdout); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/igraph_write_graph_dot.out b/tests/unit/igraph_write_graph_dot.out new file mode 100644 index 0000000..13e54e8 --- /dev/null +++ b/tests/unit/igraph_write_graph_dot.out @@ -0,0 +1,74 @@ +/* Created by igraph @VERSION@ */ +graph { + 0 [ + VAN=-1 + VAS=foo + VAB=1 + ]; + 1 [ + VAN=2.1 + VAS=bar + VAB=0 + ]; + 2 [ + VAN="1.23e-06" + VAS="" + VAB=0 + ]; + 3 [ + VAN=10000000 + VAS="" + VAB=0 + ]; + 4 [ + VAN=NaN + VAS="" + VAB=0 + ]; + 5 [ + VAN=NaN + VAS="" + VAB=0 + ]; + + 1 -- 0 [ + EAN=-100.1 + EAS=Blue + EAB=1 + ]; + 2 -- 0 [ + EAN=NaN + EAS="" + EAB=0 + ]; + 1 -- 1 [ + EAN=100 + EAS=RED + EAB=0 + ]; + 3 -- 1 [ + EAN=NaN + EAS="" + EAB=0 + ]; + 2 -- 0 [ + EAN=NaN + EAS="" + EAB=0 + ]; + 3 -- 2 [ + EAN=NaN + EAS="" + EAB=0 + ]; + 4 -- 3 [ + EAN=NaN + EAS="" + EAB=0 + ]; + 4 -- 3 [ + EAN=NaN + EAS="" + EAB=0 + ]; +} diff --git a/tests/unit/igraph_write_graph_leda.c b/tests/unit/igraph_write_graph_leda.c new file mode 100644 index 0000000..efd9659 --- /dev/null +++ b/tests/unit/igraph_write_graph_leda.c @@ -0,0 +1,113 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ +#include +#include + +#include "test_utilities.h" + +int main(void) { + int i; + igraph_t g; + igraph_vector_t values; + igraph_strvector_t strvalues; + igraph_vector_bool_t boolvalues; + const char *strings[] = {"foo", "bar", "baz", "spam", "eggs", "bacon"}; + + /* Setting up attribute handler */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* Saving directed graph, no attributes */ + igraph_ring(&g, 5, /* directed = */ 1, + /* mutual = */ 0, + /* circular = */ 1); + igraph_write_graph_leda(&g, stdout, 0, 0); + printf("===\n"); + igraph_destroy(&g); + + /* Saving undirected graph, no attributes */ + igraph_ring(&g, 5, /* directed = */ 0, + /* mutual = */ 0, + /* circular = */ 1); + igraph_write_graph_leda(&g, stdout, 0, 0); + printf("===\n"); + igraph_destroy(&g); + + /* Saving directed graph with vertex attributes */ + igraph_ring(&g, 5, /* directed = */ 1, + /* mutual = */ 0, + /* circular = */ 1); + igraph_vector_init_range(&values, 5, 10); + SETVANV(&g, "name", &values); + igraph_write_graph_leda(&g, stdout, "name", 0); + igraph_vector_destroy(&values); + printf("===\n"); + DELVAS(&g); + igraph_strvector_init(&strvalues, 5); + for (i = 0; i < 5; i++) { + igraph_strvector_set(&strvalues, i, strings[i]); + } + SETVASV(&g, "name", &strvalues); + igraph_write_graph_leda(&g, stdout, "name", 0); + igraph_strvector_destroy(&strvalues); + printf("===\n"); + igraph_destroy(&g); + + /* Saving undirected graph with edge attributes */ + igraph_ring(&g, 5, /* directed = */ 0, + /* mutual = */ 0, + /* circular = */ 1); + igraph_vector_init_range(&values, 5, 10); + SETEANV(&g, "weight", &values); + igraph_write_graph_leda(&g, stdout, 0, "weight"); + igraph_vector_destroy(&values); + printf("===\n"); + DELEAS(&g); + igraph_strvector_init(&strvalues, 5); + for (i = 0; i < 5; i++) { + igraph_strvector_set(&strvalues, i, strings[i]); + } + SETEASV(&g, "weight", &strvalues); + igraph_write_graph_leda(&g, stdout, 0, "weight"); + igraph_strvector_destroy(&strvalues); + printf("===\n"); + igraph_destroy(&g); + + /* Saving undirected graph with numerical edge attributes and boolean vertex attributes */ + igraph_ring(&g, 5, /* directed = */ 0, + /* mutual = */ 0, + /* circular = */ 1); + igraph_vector_init_range(&values, 123456789, 123456794); + SETEANV(&g, "weight", &values); + igraph_vector_bool_init(&boolvalues, igraph_vcount(&g)); + VECTOR(boolvalues)[1] = true; VECTOR(boolvalues)[3] = true; + SETVABV(&g, "binary", &boolvalues); + igraph_write_graph_leda(&g, stdout, "binary", "weight"); + igraph_vector_bool_destroy(&boolvalues); + igraph_vector_destroy(&values); + printf("===\n"); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_write_graph_leda.out b/tests/unit/igraph_write_graph_leda.out new file mode 100644 index 0000000..4adfcec --- /dev/null +++ b/tests/unit/igraph_write_graph_leda.out @@ -0,0 +1,133 @@ +LEDA.GRAPH +void +void +-1 +# Vertices +5 +|{}| +|{}| +|{}| +|{}| +|{}| +# Edges +5 +1 2 0 |{}| +2 3 0 |{}| +3 4 0 |{}| +4 5 0 |{}| +5 1 0 |{}| +=== +LEDA.GRAPH +void +void +-2 +# Vertices +5 +|{}| +|{}| +|{}| +|{}| +|{}| +# Edges +5 +1 2 0 |{}| +1 5 0 |{}| +2 3 0 |{}| +3 4 0 |{}| +4 5 0 |{}| +=== +LEDA.GRAPH +double +void +-1 +# Vertices +5 +|{5}| +|{6}| +|{7}| +|{8}| +|{9}| +# Edges +5 +1 2 0 |{}| +2 3 0 |{}| +3 4 0 |{}| +4 5 0 |{}| +5 1 0 |{}| +=== +LEDA.GRAPH +string +void +-1 +# Vertices +5 +|{foo}| +|{bar}| +|{baz}| +|{spam}| +|{eggs}| +# Edges +5 +1 2 0 |{}| +2 3 0 |{}| +3 4 0 |{}| +4 5 0 |{}| +5 1 0 |{}| +=== +LEDA.GRAPH +void +double +-2 +# Vertices +5 +|{}| +|{}| +|{}| +|{}| +|{}| +# Edges +5 +1 2 0 |{5}| +1 5 0 |{9}| +2 3 0 |{6}| +3 4 0 |{7}| +4 5 0 |{8}| +=== +LEDA.GRAPH +void +string +-2 +# Vertices +5 +|{}| +|{}| +|{}| +|{}| +|{}| +# Edges +5 +1 2 0 |{foo}| +1 5 0 |{eggs}| +2 3 0 |{bar}| +3 4 0 |{baz}| +4 5 0 |{spam}| +=== +LEDA.GRAPH +bool +double +-2 +# Vertices +5 +|{false|} +|{true|} +|{false|} +|{true|} +|{false|} +# Edges +5 +1 2 0 |{123456789}| +1 5 0 |{123456793}| +2 3 0 |{123456790}| +3 4 0 |{123456791}| +4 5 0 |{123456792}| +=== diff --git a/tests/unit/inclist.c b/tests/unit/inclist.c new file mode 100644 index 0000000..c63fee3 --- /dev/null +++ b/tests/unit/inclist.c @@ -0,0 +1,208 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +#define TEST_INCLIST(label, mode, loops) { \ + igraph_inclist_init(&g, &inclist, mode, loops); \ + printf(label ": "); \ + print_inclist(&inclist); \ + printf("\n"); \ + igraph_inclist_destroy(&inclist); \ +} + +#define TEST_LAZY_INCLIST(label, mode, loops) { \ + igraph_lazy_inclist_init(&g, &lazy_inclist, mode, loops); \ + printf(label ": "); \ + print_lazy_inclist(&lazy_inclist); \ + printf("\n"); \ + igraph_lazy_inclist_destroy(&lazy_inclist); \ +} + +int test_loop_elimination_for_undirected_graph(void) { + igraph_t g; + igraph_inclist_t inclist; + igraph_lazy_inclist_t lazy_inclist; + + igraph_small( + &g, 5, /* directed = */ 0, + /* edge 0 */ 0, 1, + /* edge 1 */ 0, 3, + /* edge 2 */ 1, 2, + /* edge 3 */ 2, 2, + /* edge 4 */ 2, 3, + /* edge 5 */ 3, 0, + /* edge 6 */ 3, 4, + /* edge 7 */ 4, 0, + /* edge 8 */ 4, 4, + /* edge 9 */ 4, 5, + /* edge 10 */ 4, 6, + /* edge 11 */ 4, 4, + /* edge 12 */ 6, 5, + -1 + ); + + printf("Testing loop edge elimination in undirected graph\n\n"); + + /* We are testing IGRAPH_ALL, IGRAPH_IN and IGRAPH_OUT below; it should + * make no difference */ + TEST_INCLIST("Loops eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS); + TEST_INCLIST("Loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE); + TEST_INCLIST("Loops listed twice", IGRAPH_OUT, IGRAPH_LOOPS_TWICE); + + printf("============================================================\n\n"); + + printf("Testing lazy loop edge elimination in undirected graph\n\n"); + + /* We are testing IGRAPH_ALL, IGRAPH_IN and IGRAPH_OUT below; it should + * make no difference */ + TEST_LAZY_INCLIST("Loops eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS); + TEST_LAZY_INCLIST("Loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE); + TEST_LAZY_INCLIST("Loops listed twice", IGRAPH_OUT, IGRAPH_LOOPS_TWICE); + + printf("============================================================\n\n"); + + igraph_destroy(&g); + + return 0; +} + +int test_loop_elimination_for_directed_graph(void) { + igraph_t g; + igraph_inclist_t inclist; + igraph_lazy_inclist_t lazy_inclist; + + igraph_small( + &g, 5, /* directed = */ 1, + /* edge 0 */ 0, 1, + /* edge 1 */ 0, 3, + /* edge 2 */ 1, 2, + /* edge 3 */ 2, 2, + /* edge 4 */ 2, 3, + /* edge 5 */ 3, 0, + /* edge 6 */ 3, 4, + /* edge 7 */ 4, 0, + /* edge 8 */ 4, 4, + /* edge 9 */ 4, 5, + /* edge 10 */ 4, 6, + /* edge 11 */ 4, 4, + /* edge 12 */ 6, 5, + -1 + ); + + printf("Testing loop edge elimination in directed graph\n\n"); + + TEST_INCLIST("In-edges, loops eliminated", IGRAPH_IN, IGRAPH_NO_LOOPS); + TEST_INCLIST("In-edges, loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE); + TEST_INCLIST("In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_IN, IGRAPH_LOOPS_TWICE); + + TEST_INCLIST("Out-edges, loops eliminated", IGRAPH_OUT, IGRAPH_NO_LOOPS); + TEST_INCLIST("Out-edges, loops listed once", IGRAPH_OUT, IGRAPH_LOOPS_ONCE); + TEST_INCLIST("Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_OUT, IGRAPH_LOOPS_TWICE); + + TEST_INCLIST("In- and out-edges, loops eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS); + TEST_INCLIST("In- and out-edges, loops listed once", IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + TEST_INCLIST("In- and out-edges, loops listed twice", IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("============================================================\n\n"); + + printf("Testing lazy loop edge elimination in directed graph\n\n"); + + TEST_LAZY_INCLIST("In-edges, loops eliminated", IGRAPH_IN, IGRAPH_NO_LOOPS); + TEST_LAZY_INCLIST("In-edges, loops listed once", IGRAPH_IN, IGRAPH_LOOPS_ONCE); + TEST_LAZY_INCLIST("In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_IN, IGRAPH_LOOPS_TWICE); + + TEST_LAZY_INCLIST("Out-edges, loops eliminated", IGRAPH_OUT, IGRAPH_NO_LOOPS); + TEST_LAZY_INCLIST("Out-edges, loops listed once", IGRAPH_OUT, IGRAPH_LOOPS_ONCE); + TEST_LAZY_INCLIST("Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given", + IGRAPH_OUT, IGRAPH_LOOPS_TWICE); + + TEST_LAZY_INCLIST("In- and out-edges, loops eliminated", IGRAPH_ALL, IGRAPH_NO_LOOPS); + TEST_LAZY_INCLIST("In- and out-edges, loops listed once", IGRAPH_ALL, IGRAPH_LOOPS_ONCE); + TEST_LAZY_INCLIST("In- and out-edges, loops listed twice", IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + + printf("============================================================\n\n"); + + igraph_destroy(&g); + + return 0; +} + +int test_adjlist_from_inclist(void) { + igraph_t g; + igraph_inclist_t inclist; + igraph_adjlist_t adjlist; + + igraph_small( + &g, 5, /* directed = */ 1, + /* edge 0 */ 0, 1, + /* edge 1 */ 0, 3, + /* edge 2 */ 1, 2, + /* edge 3 */ 2, 2, + /* edge 4 */ 2, 3, + /* edge 5 */ 3, 0, + /* edge 6 */ 3, 4, + /* edge 7 */ 4, 0, + /* edge 8 */ 4, 4, + /* edge 9 */ 4, 5, + /* edge 10 */ 4, 6, + /* edge 11 */ 4, 4, + /* edge 12 */ 6, 5, + -1 + ); + + printf("Testing incidence to adjacency list conversion\n\n"); + + igraph_inclist_init(&g, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE); + igraph_adjlist_init_from_inclist(&g, &adjlist, &inclist); + + printf("Incidence list (printed with igraph_inclist_fprint):\n"); + igraph_inclist_fprint(&inclist, stdout); + printf("\nCorresponding adjacency list: "); + print_adjlist(&adjlist); + printf("\n"); + printf("Cleared incidence list (printed with igraph_inclist_print):\n"); + igraph_inclist_clear(&inclist); + igraph_inclist_print(&inclist); + + igraph_adjlist_destroy(&adjlist); + igraph_inclist_destroy(&inclist); + + printf("============================================================\n\n"); + + igraph_destroy(&g); + + return 0; +} + +int main(void) { + + RUN_TEST(test_loop_elimination_for_undirected_graph); + RUN_TEST(test_loop_elimination_for_directed_graph); + + RUN_TEST(test_adjlist_from_inclist); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/inclist.out b/tests/unit/inclist.out new file mode 100644 index 0000000..e537bdc --- /dev/null +++ b/tests/unit/inclist.out @@ -0,0 +1,287 @@ +Testing loop edge elimination in undirected graph + +Loops eliminated: { + 0: ( 0 5 1 7 ) + 1: ( 0 2 ) + 2: ( 2 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +Loops listed once: { + 0: ( 0 5 1 7 ) + 1: ( 0 2 ) + 2: ( 2 3 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 11 8 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +Loops listed twice: { + 0: ( 0 5 1 7 ) + 1: ( 0 2 ) + 2: ( 2 3 3 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 11 8 11 8 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +============================================================ + +Testing lazy loop edge elimination in undirected graph + +Loops eliminated: { + 0: ( 0 5 1 7 ) + 1: ( 0 2 ) + 2: ( 2 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +Loops listed once: { + 0: ( 0 5 1 7 ) + 1: ( 0 2 ) + 2: ( 2 3 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 11 8 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +Loops listed twice: { + 0: ( 0 5 1 7 ) + 1: ( 0 2 ) + 2: ( 2 3 3 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 11 8 11 8 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +============================================================ + +Testing loop edge elimination in directed graph + +In-edges, loops eliminated: { + 0: ( 5 7 ) + 1: ( 0 ) + 2: ( 2 ) + 3: ( 1 4 ) + 4: ( 6 ) + 5: ( 9 12 ) + 6: ( 10 ) +} + +In-edges, loops listed once: { + 0: ( 5 7 ) + 1: ( 0 ) + 2: ( 2 3 ) + 3: ( 1 4 ) + 4: ( 6 11 8 ) + 5: ( 9 12 ) + 6: ( 10 ) +} + +In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 5 7 ) + 1: ( 0 ) + 2: ( 2 3 ) + 3: ( 1 4 ) + 4: ( 6 11 8 ) + 5: ( 9 12 ) + 6: ( 10 ) +} + +Out-edges, loops eliminated: { + 0: ( 0 1 ) + 1: ( 2 ) + 2: ( 4 ) + 3: ( 5 6 ) + 4: ( 7 9 10 ) + 5: ( ) + 6: ( 12 ) +} + +Out-edges, loops listed once: { + 0: ( 0 1 ) + 1: ( 2 ) + 2: ( 3 4 ) + 3: ( 5 6 ) + 4: ( 7 11 8 9 10 ) + 5: ( ) + 6: ( 12 ) +} + +Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 0 1 ) + 1: ( 2 ) + 2: ( 3 4 ) + 3: ( 5 6 ) + 4: ( 7 11 8 9 10 ) + 5: ( ) + 6: ( 12 ) +} + +In- and out-edges, loops eliminated: { + 0: ( 0 1 5 7 ) + 1: ( 0 2 ) + 2: ( 2 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +In- and out-edges, loops listed once: { + 0: ( 0 1 5 7 ) + 1: ( 0 2 ) + 2: ( 2 3 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 11 8 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +In- and out-edges, loops listed twice: { + 0: ( 0 1 5 7 ) + 1: ( 0 2 ) + 2: ( 2 3 3 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 11 11 8 8 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +============================================================ + +Testing lazy loop edge elimination in directed graph + +In-edges, loops eliminated: { + 0: ( 5 7 ) + 1: ( 0 ) + 2: ( 2 ) + 3: ( 1 4 ) + 4: ( 6 ) + 5: ( 9 12 ) + 6: ( 10 ) +} + +In-edges, loops listed once: { + 0: ( 5 7 ) + 1: ( 0 ) + 2: ( 2 3 ) + 3: ( 1 4 ) + 4: ( 6 11 8 ) + 5: ( 9 12 ) + 6: ( 10 ) +} + +In-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 5 7 ) + 1: ( 0 ) + 2: ( 2 3 ) + 3: ( 1 4 ) + 4: ( 6 11 8 ) + 5: ( 9 12 ) + 6: ( 10 ) +} + +Out-edges, loops eliminated: { + 0: ( 0 1 ) + 1: ( 2 ) + 2: ( 4 ) + 3: ( 5 6 ) + 4: ( 7 9 10 ) + 5: ( ) + 6: ( 12 ) +} + +Out-edges, loops listed once: { + 0: ( 0 1 ) + 1: ( 2 ) + 2: ( 3 4 ) + 3: ( 5 6 ) + 4: ( 7 11 8 9 10 ) + 5: ( ) + 6: ( 12 ) +} + +Out-edges, loops listed once even if IGRAPH_LOOPS_TWICE is given: { + 0: ( 0 1 ) + 1: ( 2 ) + 2: ( 3 4 ) + 3: ( 5 6 ) + 4: ( 7 11 8 9 10 ) + 5: ( ) + 6: ( 12 ) +} + +In- and out-edges, loops eliminated: { + 0: ( 0 1 5 7 ) + 1: ( 0 2 ) + 2: ( 2 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +In- and out-edges, loops listed once: { + 0: ( 0 1 5 7 ) + 1: ( 0 2 ) + 2: ( 2 3 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 11 8 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +In- and out-edges, loops listed twice: { + 0: ( 0 1 5 7 ) + 1: ( 0 2 ) + 2: ( 2 3 3 4 ) + 3: ( 5 1 4 6 ) + 4: ( 7 6 11 11 8 8 9 10 ) + 5: ( 9 12 ) + 6: ( 10 12 ) +} + +============================================================ + +Testing incidence to adjacency list conversion + +Incidence list (printed with igraph_inclist_fprint): +0 1 5 7 +0 2 +2 3 3 4 +5 1 4 6 +7 6 11 11 8 8 9 10 +9 12 +10 12 + +Corresponding adjacency list: { + 0: ( 1 3 3 4 ) + 1: ( 0 2 ) + 2: ( 1 2 2 3 ) + 3: ( 0 0 2 4 ) + 4: ( 0 3 4 4 4 4 5 6 ) + 5: ( 4 6 ) + 6: ( 4 5 ) +} + +Cleared incidence list (printed with igraph_inclist_print): + + + + + + + +============================================================ + diff --git a/tests/unit/input.dl b/tests/unit/input.dl new file mode 100644 index 0000000..50d75dd --- /dev/null +++ b/tests/unit/input.dl @@ -0,0 +1,104 @@ +DL n=66 +format = edgelist1 +labels embedded: +data: +R1 C1 +R1 C5 +R1 C7 +R1 C9 +R1 C11 +R1 C12 +R1 C13 +R1 C16 +R1 C17 +R1 C23 +R1 C24 +R1 C25 +R1 C28 +R2 C8 +R2 C11 +R2 C12 +R2 C17 +R2 C20 +R2 C24 +R2 C26 +R2 C27 +R2 C28 +R3 C2 +R3 C3 +R4 C17 +R4 C23 +R5 C6 +R5 C13 +R5 C19 +R5 C22 +R5 C24 +R6 C14 +R7 C17 +R7 C22 +R7 C26 +R8 C1 +R8 C17 +R8 C19 +R8 C22 +R9 C19 +R9 C22 +R9 C23 +R10 C6 +R10 C18 +R10 C28 +R11 C25 +R12 C25 +R13 C13 +R13 C19 +R14 C1 +R14 C4 +R14 C21 +R15 C15 +R15 C17 +R16 C17 +R16 C23 +R17 C4 +R18 C28 +R19 C6 +R20 C17 +R21 C28 +R22 C4 +R23 C6 +R23 C17 +R24 C11 +R25 C4 +R26 C16 +R26 C20 +R27 C1 +R27 C2 +R27 C5 +R27 C17 +R28 C13 +R28 C20 +R28 C21 +R29 C12 +R30 C1 +R30 C2 +R30 C22 +R31 C10 +R31 C13 +R31 C15 +R32 C6 +R32 C22 +R32 C28 +R33 C14 +R33 C23 +R34 C3 +R34 C28 +R35 C28 +R36 C13 +R36 C20 +R36 C27 +R36 C28 +R37 C28 +R38 C8 +R38 C10 +R38 C13 +R38 C14 +R38 C23 diff --git a/tests/unit/isoclasses.c b/tests/unit/isoclasses.c new file mode 100644 index 0000000..a4eccd7 --- /dev/null +++ b/tests/unit/isoclasses.c @@ -0,0 +1,68 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_vector_int_t edges; + igraph_vector_int_t vids; + igraph_integer_t class; + + igraph_vector_int_init_int_end(&edges, -1, + 0, 1, 1, 3, 1, 4, 1, 6, 3, 1, + 4, 1, 4, 2, 6, 4, 6, 5, 7, 8, + 8, 7, 7, 9, 9, 7, 8, 9, 9, 8, + -1); + igraph_create(&g, &edges, 0, IGRAPH_DIRECTED); + igraph_vector_int_destroy(&edges); + + igraph_vector_int_init_int_end(&vids, -1, 1, 4, 6, -1); + igraph_isoclass_subgraph(&g, &vids, &class); + printf("class: %i\n", (int)class); + igraph_vector_int_destroy(&vids); + + igraph_vector_int_init_int_end(&vids, -1, 0, 1, 3, -1); + igraph_isoclass_subgraph(&g, &vids, &class); + printf("class: %i\n", (int)class); + igraph_vector_int_destroy(&vids); + + igraph_vector_int_init_int_end(&vids, -1, 7, 8, 9, -1); + igraph_isoclass_subgraph(&g, &vids, &class); + printf("class: %i\n", (int)class); + igraph_vector_int_destroy(&vids); + + igraph_vector_int_init_int_end(&vids, -1, 0, 2, 5, -1); + igraph_isoclass_subgraph(&g, &vids, &class); + printf("class: %i\n", (int)class); + igraph_vector_int_destroy(&vids); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/isoclasses.out b/tests/unit/isoclasses.out new file mode 100644 index 0000000..c69226c --- /dev/null +++ b/tests/unit/isoclasses.out @@ -0,0 +1,4 @@ +class: 12 +class: 5 +class: 15 +class: 0 diff --git a/tests/unit/isoclasses2.c b/tests/unit/isoclasses2.c new file mode 100644 index 0000000..cd8dc55 --- /dev/null +++ b/tests/unit/isoclasses2.c @@ -0,0 +1,180 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include + +#include "test_utilities.h" + +/* Check that isoclass() and isoclass_create() are consistent with each other. */ +void verify_classes(void) { + igraph_integer_t class; + igraph_integer_t size; + + igraph_integer_t classcountD[] = { 1, 1, 3, 16, 218 }; /* no. of unlabelled directed graphs */ + igraph_integer_t classcountU[] = { 1, 1, 2, 4, 11, 34, 156 }; /* no. of unlabelled undirected graphs */ + + /* Directed */ + for (size=3; size <= 4; size++) { + for (class=0; class < classcountD[size]; class++) { + igraph_t g; + igraph_integer_t class2; + + igraph_isoclass_create(&g, size, class, IGRAPH_DIRECTED); + igraph_isoclass(&g, &class2); + igraph_destroy(&g); + + IGRAPH_ASSERT(class == class2); + } + } + + /* Undirected */ + for (size=3; size <= 6; size++) { + for (class=0; class < classcountU[size]; class++) { + igraph_t g; + igraph_integer_t class2; + + igraph_isoclass_create(&g, size, class, IGRAPH_UNDIRECTED); + igraph_isoclass(&g, &class2); + igraph_destroy(&g); + + IGRAPH_ASSERT(class == class2); + } + } +} + +/* Generate small random graphs and check that their isoclasses are identified correctly. */ +void random_test(void) { + igraph_integer_t size, i; + + igraph_rng_seed(igraph_rng_default(), 137); + + /* Directed */ + for (size=3; size <= 4; size++) { + for (i=0; i < 200; ++i) { + igraph_t g1, g2; + igraph_integer_t class; + igraph_bool_t iso; + + igraph_erdos_renyi_game_gnp(&g1, size, 0.5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + + igraph_isoclass(&g1, &class); + igraph_isoclass_create(&g2, size, class, IGRAPH_DIRECTED); + + igraph_isomorphic_bliss(&g1, &g2, NULL, NULL, &iso, NULL, NULL, IGRAPH_BLISS_F, NULL, NULL); + IGRAPH_ASSERT(iso); + + igraph_destroy(&g2); + igraph_destroy(&g1); + } + } + + /* Undirected */ + for (size=3; size <= 6; size++) { + for (i=0; i < 200; ++i) { + igraph_t g1, g2; + igraph_integer_t class; + igraph_bool_t iso; + + igraph_erdos_renyi_game_gnp(&g1, size, 0.5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + igraph_isoclass(&g1, &class); + igraph_isoclass_create(&g2, size, class, IGRAPH_UNDIRECTED); + + igraph_isomorphic_bliss(&g1, &g2, NULL, NULL, &iso, NULL, NULL, IGRAPH_BLISS_F, NULL, NULL); + IGRAPH_ASSERT(iso); + + igraph_destroy(&g2); + igraph_destroy(&g1); + } + } +} + +/* Generate a random graph, select random subgraphs, and check that their + * isoclasses are identified correctly. */ +void random_subgraph_test(void) { + igraph_t graph; + igraph_integer_t size, i; + igraph_vector_int_t vids; + + igraph_rng_seed(igraph_rng_default(), 42); + + igraph_vector_int_init(&vids, 0); + + /* Directed */ + + igraph_erdos_renyi_game_gnp(&graph, 40, 0.5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + + for (size=3; size <= 4; size++) { + for (i=0; i < 100; ++i) { + igraph_t sg1, sg2; + igraph_integer_t class; + igraph_bool_t iso; + + igraph_random_sample(&vids, 0, igraph_vcount(&graph) - 1, size); + igraph_isoclass_subgraph(&graph, &vids, &class); + igraph_isoclass_create(&sg1, size, class, igraph_is_directed(&graph)); + igraph_induced_subgraph(&graph, &sg2, igraph_vss_vector(&vids), IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH); + + igraph_isomorphic_bliss(&sg1, &sg2, NULL, NULL, &iso, NULL, NULL, IGRAPH_BLISS_F, NULL, NULL); + IGRAPH_ASSERT(iso); + + igraph_destroy(&sg1); + igraph_destroy(&sg2); + } + } + + igraph_destroy(&graph); + + /* Undirected */ + + igraph_erdos_renyi_game_gnp(&graph, 60, 0.5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + + for (size=3; size <= 6; size++) { + for (i=0; i < 100; ++i) { + igraph_t sg1, sg2; + igraph_integer_t class; + igraph_bool_t iso; + + igraph_random_sample(&vids, 0, igraph_vcount(&graph) - 1, size); + igraph_isoclass_subgraph(&graph, &vids, &class); + igraph_isoclass_create(&sg1, size, class, igraph_is_directed(&graph)); + igraph_induced_subgraph(&graph, &sg2, igraph_vss_vector(&vids), IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH); + + igraph_isomorphic_bliss(&sg1, &sg2, NULL, NULL, &iso, NULL, NULL, IGRAPH_BLISS_F, NULL, NULL); + IGRAPH_ASSERT(iso); + + igraph_destroy(&sg1); + igraph_destroy(&sg2); + } + } + + igraph_destroy(&graph); + + igraph_vector_int_destroy(&vids); +} + +int main(void) { + + verify_classes(); + random_test(); + random_subgraph_test(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/isomorphism_test.c b/tests/unit/isomorphism_test.c new file mode 100644 index 0000000..661986c --- /dev/null +++ b/tests/unit/isomorphism_test.c @@ -0,0 +1,243 @@ +/* + IGraph library. + Copyright (C) 2015-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, see . +*/ + +#include +#include +#include + +#include "test_utilities.h" + +void random_permutation(igraph_vector_int_t *vec) { + /* We just do size(vec) * 2 swaps */ + igraph_integer_t one, two, tmp, i, n = igraph_vector_int_size(vec); + for (i = 0; i < 2 * n; i++) { + one = RNG_INTEGER(0, n - 1); + two = RNG_INTEGER(0, n - 1); + tmp = VECTOR(*vec)[one]; + VECTOR(*vec)[one] = VECTOR(*vec)[two]; + VECTOR(*vec)[two] = tmp; + } +} + + +void test3(void) { + igraph_integer_t i, j; + igraph_graph_list_t graphs3; + igraph_t g; + + // Verify that no two 3-vertex graphs of distinct isoclasses are considered isomorphic by Bliss or VF2. + + igraph_graph_list_init(&graphs3, 0); + + for (i = 0; i < 16; i++) { + igraph_isoclass_create(&g, 3, i, /* directed = */ 1); + igraph_graph_list_push_back(&graphs3, &g); + } + + for (i = 0; i < 16; i++) + for (j = i + 1; j < 16; j++) { + igraph_bool_t iso; + igraph_isomorphic_bliss( + igraph_graph_list_get_ptr(&graphs3, i), + igraph_graph_list_get_ptr(&graphs3, j), + NULL, NULL, &iso, NULL, NULL, IGRAPH_BLISS_F, NULL, NULL + ); + if (iso) { + printf("Bliss failure, 3 vertex directed graphs of isoclass %" IGRAPH_PRId " and %" IGRAPH_PRId " are not isomorphic. Bliss reports otherwise.\n", i, j); + } + } + + for (i = 0; i < 16; i++) + for (j = i + 1; j < 16; j++) { + igraph_bool_t iso; + igraph_isomorphic_vf2( + igraph_graph_list_get_ptr(&graphs3, i), + igraph_graph_list_get_ptr(&graphs3, j), + NULL, NULL, NULL, NULL, &iso, NULL, NULL, NULL, NULL, NULL + ); + if (iso) { + printf("VF2 failure, 3 vertex directed graphs of isoclass %" IGRAPH_PRId " and %" IGRAPH_PRId " are not isomorphic. VF2 reports otherwise.\n", i, j); + } + } + + igraph_graph_list_destroy(&graphs3); +} + + +void test4(void) { + igraph_integer_t i, j; + igraph_graph_list_t graphs4; + igraph_t g; + + // Verify that no two 4-vertex graphs of distinct isoclasses are considered isomorphic by Bliss or VF2. + + igraph_graph_list_init(&graphs4, 0); + + for (i = 0; i < 218; i++) { + igraph_isoclass_create(&g, 4, i, /* directed = */ 1); + igraph_graph_list_push_back(&graphs4, &g); + } + + for (i = 0; i < 218; i++) + for (j = i + 1; j < 218; j++) { + igraph_bool_t iso; + igraph_isomorphic_bliss( + igraph_graph_list_get_ptr(&graphs4, i), + igraph_graph_list_get_ptr(&graphs4, j), + NULL, NULL, &iso, NULL, NULL, IGRAPH_BLISS_F, NULL, NULL + ); + if (iso) { + printf("Bliss failure, 4 vertex directed graphs of isoclass %" IGRAPH_PRId " and %" IGRAPH_PRId " are not isomorphic. Bliss reports otherwise.\n", i, j); + } + } + + for (i = 0; i < 218; i++) + for (j = i + 1; j < 218; j++) { + igraph_bool_t iso; + igraph_isomorphic_vf2( + igraph_graph_list_get_ptr(&graphs4, i), + igraph_graph_list_get_ptr(&graphs4, j), + NULL, NULL, NULL, NULL, &iso, NULL, NULL, NULL, NULL, NULL + ); + if (iso) { + printf("VF2 failure, 4 vertex directed graphs of isoclass %" IGRAPH_PRId " and %" IGRAPH_PRId " are not isomorphic. VF2 reports otherwise.\n", i, j); + } + } + + igraph_graph_list_destroy(&graphs4); +} + + +void test_bliss(void) { + igraph_t ring1, ring2, directed_ring; + igraph_vector_int_t perm; + igraph_bool_t iso; + igraph_bliss_info_t info; + igraph_vector_int_t color; + igraph_vector_int_list_t generators; + + igraph_ring(&ring1, 100, /*directed=*/ 0, /*mutual=*/ 0, /*circular=*/1); + igraph_vector_int_init_range(&perm, 0, igraph_vcount(&ring1)); + random_permutation(&perm); + igraph_permute_vertices(&ring1, &ring2, &perm); + + igraph_ring(&directed_ring, 100, /* directed= */ 1, /* mutual = */0, /* circular = */1); + + igraph_vector_int_list_init(&generators, 0); + + igraph_isomorphic_bliss(&ring1, &ring2, NULL, NULL, &iso, NULL, NULL, IGRAPH_BLISS_F, NULL, NULL); + if (! iso) { + printf("Bliss failed on ring isomorphism.\n"); + } + + igraph_count_automorphisms(&ring1, NULL, IGRAPH_BLISS_F, &info); + if (strcmp(info.group_size, "200") != 0) { + printf("Biss automorphism count failed: ring1.\n"); + } + igraph_free(info.group_size); + + igraph_count_automorphisms(&ring2, NULL, IGRAPH_BLISS_F, &info); + if (strcmp(info.group_size, "200") != 0) { + printf("Biss automorphism count failed: ring2.\n"); + } + igraph_free(info.group_size); + + igraph_count_automorphisms(&directed_ring, NULL, IGRAPH_BLISS_F, &info); + if (strcmp(info.group_size, "100") != 0) { + printf("Biss automorphism count failed: directed_ring.\n"); + } + igraph_free(info.group_size); + + // The follwing test is included so there is at least one call to igraph_automorphism_group + // in the test suite. However, the generator set returned may depend on the splitting + // heursitics as well as on the Bliss version. If the test fails, please verify manually + // that the generating set is valid. For a undirected cycle graph like ring2, there should + // be two generators: a cyclic permutation and a reversal of the vertex order. + igraph_automorphism_group(&ring2, NULL, &generators, IGRAPH_BLISS_F, NULL); + if (igraph_vector_int_list_size(&generators) != 2) + printf("Bliss automorphism generators may have failed with ring2. " + "Please verify the generators manually. " + "Note that the generator set is not guaranteed to be minimal.\n"); + igraph_vector_int_list_clear(&generators); + + // For a directed ring, the only generator should be a cyclic permutation. + igraph_automorphism_group(&directed_ring, NULL, &generators, IGRAPH_BLISS_F, NULL); + if (igraph_vector_int_list_size(&generators) != 1) + printf("Bliss automorphism generators may have failed with directed_ring. " + "Please verify the generators manually. " + "Note that the generator set is not guaranteed to be minimal.\n"); + igraph_vector_int_list_clear(&generators); + + igraph_vector_int_init_range(&color, 0, igraph_vcount(&ring1)); + + igraph_count_automorphisms(&ring1, &color, IGRAPH_BLISS_F, &info); + if (strcmp(info.group_size, "1") != 0) { + printf("Bliss automorphism count with color failed: ring1.\n"); + } + igraph_free(info.group_size); + + // There's only one automorphism for this coloured graph, so the generating set is empty. + igraph_automorphism_group(&ring1, &color, &generators, IGRAPH_BLISS_F, NULL); + if (igraph_vector_int_list_size(&generators) != 0) { + printf("Bliss automorphism generators failed with colored graph.\n"); + } + + igraph_vector_int_list_destroy(&generators); + + igraph_vector_int_destroy(&color); + + igraph_vector_int_destroy(&perm); + + igraph_destroy(&ring1); + igraph_destroy(&ring2); + igraph_destroy(&directed_ring); +} + +void test_bug_995(void) { + igraph_t g1, g2; + igraph_bool_t result; + + igraph_small(&g1, 3, 0, 0, 1, 1, 2, 2, 2, -1); + igraph_small(&g2, 3, 0, 0, 1, 1, 2, 1, 1, -1); + + igraph_isomorphic(&g1, &g2, &result); + if (result) { + printf("igraph_isomorphic() failed with loop edges, see bug #995\n"); + } + + igraph_destroy(&g1); + igraph_destroy(&g2); +} + +int main(void) { + + igraph_rng_seed(igraph_rng_default(), 293847); /* make tests deterministic */ + + RNG_BEGIN(); + + test3(); + test4(); + test_bliss(); + test_bug_995(); + + RNG_END(); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/isomorphism_test.out b/tests/unit/isomorphism_test.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/jdm.c b/tests/unit/jdm.c new file mode 100644 index 0000000..fce99b3 --- /dev/null +++ b/tests/unit/jdm.c @@ -0,0 +1,322 @@ +/* + IGraph library. + Copyright (C) 2023 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 +#include "test_utilities.h" + +void print_and_destroy(igraph_t* g, igraph_matrix_t* jdm, igraph_vector_t* weights) { + print_matrix(jdm); + + igraph_destroy(g); + if (weights) { + igraph_vector_destroy(weights); + } +} + +int main (void) { + igraph_t g; + igraph_matrix_t jdm; + igraph_vector_t weights; + + // This matrix will be re-used throughout the test + igraph_matrix_init(&jdm, 0, 0); + + printf("Graph with no vertices\n"); + igraph_small(&g, 0, false, -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("3-cycle\n"); + igraph_ring(&g, 3, IGRAPH_UNDIRECTED, false, true); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Three self-loops\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, + 0,0, 1,1, 2,2, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("One self-loop and two parallel edges\n"); + igraph_small(&g, 3, IGRAPH_UNDIRECTED, + 0,0, 1,2, 1,2, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Simple, undirected graph\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Simple, directed graph\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Undirected, self-loops, no multiedges\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 1, 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Directed, self-loops, no multiedges\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 1, 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Undirected multigraph, no self-loops\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 4, 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Directed multigraph, no self-loops\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, 4, 3, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Undirected multigraph with self-loops\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 1, 1, 4, 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Directed multigraph with self-loops\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 1, 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, 4, 3, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + // Weight tests + printf("Weighted, simple, undirected graph\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_vector_init_range(&weights, -1,6); + igraph_joint_degree_matrix(&g, &weights, &jdm, -1, -1); + print_and_destroy(&g, &jdm, &weights); + + printf("Weighted, simple, directed graph\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, + -1); + igraph_vector_init_range(&weights, -2,8); + igraph_joint_degree_matrix(&g, &weights, &jdm, -1, -1); + print_and_destroy(&g, &jdm, &weights); + + printf("Weighted, undirected, self-loops, no multiedges\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 1, 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_vector_init_range(&weights, 1,9); + igraph_joint_degree_matrix(&g, &weights, &jdm, -1, -1); + print_and_destroy(&g, &jdm, &weights); + + printf("Weighted, directed, self-loops, no multiedges\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 1, 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, + -1); + igraph_vector_init_range(&weights, 1,12); + igraph_joint_degree_matrix(&g, &weights, &jdm, -1, -1); + print_and_destroy(&g, &jdm, &weights); + + printf("Weighted, undirected multigraph, no self-loops\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 4, 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_vector_init_range(&weights, 1,9); + igraph_joint_degree_matrix(&g, &weights, &jdm, -1, -1); + print_and_destroy(&g, &jdm, &weights); + + printf("Weighted, directed multigraph, no self-loops\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, 4, 3, + -1); + igraph_vector_init_range(&weights, 1,12); + igraph_joint_degree_matrix(&g, &weights, &jdm, -1, -1); + print_and_destroy(&g, &jdm, &weights); + + printf("Weighted, undirected multigraph with self-loops\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 1, 1, 4, 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_vector_init_range(&weights, 1,10); + igraph_joint_degree_matrix(&g, &weights, &jdm, -1, -1); + print_and_destroy(&g, &jdm, &weights); + + printf("Weighted, directed multigraph with self-loops\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 1, 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, 4, 3, + -1); + igraph_vector_init_range(&weights, 1,13); + igraph_joint_degree_matrix(&g, &weights, &jdm, -1, -1); + print_and_destroy(&g, &jdm, &weights); + + // dout din tests + + // Directed + printf("Directed: dout is small, cropped JDM (3x3)\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, 3, 3); + print_and_destroy(&g, &jdm, NULL); + + printf("Directed: din is small, cropped JDM (4, 2)\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, 4, 2); + print_and_destroy(&g, &jdm, NULL); + + printf("Directed: Automatic resize, dout < 0 (4x2)\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, 2); + print_and_destroy(&g, &jdm, NULL); + + printf("Directed: Valid dout and din (5x5)\n"); + igraph_small(&g, 5, true, + 0, 1, 0, 2, 0, 4, + 1, 0, + 3, 2, 3, 4, + 4, 0, 4, 1, 4, 2, 4, 3, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, 5, 5); + print_and_destroy(&g, &jdm, NULL); + + // Undirected + printf("Undirected: dout is small, cropped JDM (3x4)\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, 3, 4); + print_and_destroy(&g, &jdm, NULL); + + printf("Undirected: din is small, cropped JDM (4x3)\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, 4, 3); + print_and_destroy(&g, &jdm, NULL); + + printf("Undirected: Automatic resize, dout or din < 0 (4x4)\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, -1, -1); + print_and_destroy(&g, &jdm, NULL); + + printf("Undirected: Valid dout and din (5x5)\n"); + igraph_small(&g, 5, false, + 0, 1, 0, 2, 0, 4, + 1, 4, + 2, 3, 2, 4, + 3, 4, + -1); + igraph_joint_degree_matrix(&g, NULL, &jdm, 5, 5); + print_and_destroy(&g, &jdm, NULL); + + // Clean up + igraph_matrix_destroy(&jdm); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/jdm.out b/tests/unit/jdm.out new file mode 100644 index 0000000..11974f3 --- /dev/null +++ b/tests/unit/jdm.out @@ -0,0 +1,139 @@ +Graph with no vertices +[ 0-by-0 ] +3-cycle +[ 0 0 + 0 3 ] +Three self-loops +[ 0 0 + 0 3 ] +One self-loop and two parallel edges +[ 0 0 + 0 3 ] +Simple, undirected graph +[ 0 0 0 0 + 0 0 2 2 + 0 2 1 2 + 0 2 2 0 ] +Simple, directed graph +[ 0 1 0 + 0 1 1 + 0 2 1 + 1 2 1 ] +Undirected, self-loops, no multiedges +[ 0 0 0 0 + 0 0 1 1 + 0 1 1 3 + 0 1 3 2 ] +Directed, self-loops, no multiedges +[ 0 0 0 + 0 2 2 + 0 1 2 + 1 1 2 ] +Undirected multigraph, no self-loops +[ 0 0 0 0 0 + 0 0 1 0 1 + 0 1 2 0 4 + 0 0 0 0 0 + 0 1 4 0 0 ] +Directed multigraph, no self-loops +[ 0 1 0 + 0 1 1 + 0 2 1 + 0 0 0 + 0 4 1 ] +Undirected multigraph with self-loops +[ 0 0 0 0 0 + 0 0 1 0 1 + 0 1 1 0 3 + 0 0 0 0 0 + 0 1 3 0 3 ] +Directed multigraph with self-loops +[ 0 0 0 + 0 2 2 + 0 1 2 + 0 0 0 + 0 3 2 ] +Weighted, simple, undirected graph +[ 0 0 0 0 + 0 0 2 7 + 0 2 0 5 + 0 7 5 0 ] +Weighted, simple, directed graph +[ 0 1 0 + 0 3 2 + 0 -2 -1 + 7 9 6 ] +Weighted, undirected, self-loops, no multiedges +[ 0 0 0 0 + 0 0 6 8 + 0 6 2 11 + 0 8 11 9 ] +Weighted, directed, self-loops, no multiedges +[ 0 0 0 + 0 12 10 + 0 3 3 + 11 8 19 ] +Weighted, undirected multigraph, no self-loops +[ 0 0 0 0 0 + 0 0 6 0 8 + 0 6 3 0 19 + 0 0 0 0 0 + 0 8 19 0 0 ] +Weighted, directed multigraph, no self-loops +[ 0 4 0 + 0 6 5 + 0 4 2 + 0 0 0 + 0 36 9 ] +Weighted, undirected multigraph with self-loops +[ 0 0 0 0 0 + 0 0 7 0 9 + 0 7 2 0 12 + 0 0 0 0 0 + 0 9 12 0 15 ] +Weighted, directed multigraph with self-loops +[ 0 0 0 + 0 12 10 + 0 3 3 + 0 0 0 + 0 31 19 ] +Directed: dout is small, cropped JDM (3x3) +[ 0 1 0 + 0 1 1 + 0 2 1 ] +Directed: din is small, cropped JDM (4, 2) +[ 0 1 + 0 1 + 0 2 + 1 2 ] +Directed: Automatic resize, dout < 0 (4x2) +[ 0 1 + 0 1 + 0 2 + 1 2 ] +Directed: Valid dout and din (5x5) +[ 0 1 0 0 0 + 0 1 1 0 0 + 0 2 1 0 0 + 1 2 1 0 0 + 0 0 0 0 0 ] +Undirected: dout is small, cropped JDM (3x4) +[ 0 0 0 0 + 0 0 2 2 + 0 2 1 2 ] +Undirected: din is small, cropped JDM (4x3) +[ 0 0 0 + 0 0 2 + 0 2 1 + 0 2 2 ] +Undirected: Automatic resize, dout or din < 0 (4x4) +[ 0 0 0 0 + 0 0 2 2 + 0 2 1 2 + 0 2 2 0 ] +Undirected: Valid dout and din (5x5) +[ 0 0 0 0 0 + 0 0 2 2 0 + 0 2 1 2 0 + 0 2 2 0 0 + 0 0 0 0 0 ] diff --git a/tests/unit/kary_tree.c b/tests/unit/kary_tree.c new file mode 100644 index 0000000..7aa9c4e --- /dev/null +++ b/tests/unit/kary_tree.c @@ -0,0 +1,57 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + + +#define PRINT_DESTROY(name) \ + printf(name "\n"); \ + print_graph_canon(&graph); \ + igraph_destroy(&graph); \ + printf("\n"); + + +int main(void) { + igraph_t graph; + + igraph_kary_tree(&graph, 0, 1, IGRAPH_TREE_UNDIRECTED); + PRINT_DESTROY("Null graph"); + + igraph_kary_tree(&graph, 0, 1, IGRAPH_TREE_OUT); + PRINT_DESTROY("Directed null graph"); + + igraph_kary_tree(&graph, 1, 1, IGRAPH_TREE_UNDIRECTED); + PRINT_DESTROY("Singleton graph"); + + igraph_kary_tree(&graph, 3, 1, IGRAPH_TREE_OUT); + PRINT_DESTROY("Path graph"); + + igraph_kary_tree(&graph, 3, 2, IGRAPH_TREE_OUT); + PRINT_DESTROY("Binary out-tree, n=3"); + + igraph_kary_tree(&graph, 3, 2, IGRAPH_TREE_IN); + PRINT_DESTROY("Binary in-tree, n=3"); + + igraph_kary_tree(&graph, 14, 3, IGRAPH_TREE_OUT); + PRINT_DESTROY("Ternary out-tree, n=14"); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/kary_tree.out b/tests/unit/kary_tree.out new file mode 100644 index 0000000..e296d53 --- /dev/null +++ b/tests/unit/kary_tree.out @@ -0,0 +1,61 @@ +Null graph +directed: false +vcount: 0 +edges: { +} + +Directed null graph +directed: true +vcount: 0 +edges: { +} + +Singleton graph +directed: false +vcount: 1 +edges: { +} + +Path graph +directed: true +vcount: 3 +edges: { +0 1 +1 2 +} + +Binary out-tree, n=3 +directed: true +vcount: 3 +edges: { +0 1 +0 2 +} + +Binary in-tree, n=3 +directed: true +vcount: 3 +edges: { +1 0 +2 0 +} + +Ternary out-tree, n=14 +directed: true +vcount: 14 +edges: { +0 1 +0 2 +0 3 +1 4 +1 5 +1 6 +2 7 +2 8 +2 9 +3 10 +3 11 +3 12 +4 13 +} + diff --git a/tests/unit/knn.c b/tests/unit/knn.c new file mode 100644 index 0000000..1dd7b6e --- /dev/null +++ b/tests/unit/knn.c @@ -0,0 +1,199 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +/* Check vector equality with tolerances. Consider NaN values equal. */ +igraph_bool_t vector_eq(const igraph_vector_t *a, const igraph_vector_t *b) { + igraph_integer_t na = igraph_vector_size(a); + igraph_integer_t nb = igraph_vector_size(b); + if (na != nb) { + return false; + } + for (igraph_integer_t i=0; i < na; i++) { + if (isnan(VECTOR(*a)[i]) && isnan(VECTOR(*b)[i])) { + continue; + } + if (! igraph_almost_equals(VECTOR(*a)[i], VECTOR(*b)[i], 1e-12)) { + return false; + } + } + return true; +} + +/* Compare results from igraph_avg_nearest_neighbor_degree() and igraph_degree_correlation_vector() */ +void compare_implementations(const igraph_t *g, igraph_neimode_t mode1, igraph_neimode_t mode2, const igraph_vector_t *weights) { + igraph_vector_t knn, knnk1, knnk2; + + igraph_vector_init(&knn, 0); + igraph_vector_init(&knnk1, 0); + igraph_vector_init(&knnk2, 0); + + igraph_avg_nearest_neighbor_degree(g, igraph_vss_all(), + mode1, mode2, + &knn, &knnk1, weights); + + printf("k_nn_i: "); + print_vector(&knn); + + igraph_degree_correlation_vector(g, weights, &knnk2, mode1, mode2, mode1 == IGRAPH_ALL ? false : true); + + printf("k_nn(k): "); + print_vector(&knnk2); + /* print_vector(&knnk1); */ + + IGRAPH_ASSERT(isnan(VECTOR(knnk2)[0])); + + igraph_vector_remove(&knnk2, 0); + IGRAPH_ASSERT(vector_eq(&knnk1, &knnk2)); + + igraph_vector_destroy(&knnk2); + igraph_vector_destroy(&knnk1); + igraph_vector_destroy(&knn); + + printf("\n"); +} + +/* Check that undirected graphs are treated as a directed ones with all-reciprocal edges. + * This helps verify non-simple graphs, especially those with self-loops. */ +void check_dir_undir_equiv(const igraph_t *ug) { + igraph_t rg; + igraph_vector_t knn1, knn2, knnk1, knnk2; + + igraph_vector_init(&knn1, 0); + igraph_vector_init(&knn2, 0); + igraph_vector_init(&knnk1, 0); + igraph_vector_init(&knnk2, 0); + + igraph_copy(&rg, ug); + igraph_to_directed(&rg, IGRAPH_TO_DIRECTED_MUTUAL); + + igraph_avg_nearest_neighbor_degree(ug, igraph_vss_all(), IGRAPH_ALL, IGRAPH_ALL, &knn1, &knnk1, NULL); + igraph_avg_nearest_neighbor_degree(&rg, igraph_vss_all(), IGRAPH_OUT, IGRAPH_OUT, &knn2, &knnk2, NULL); + + IGRAPH_ASSERT(vector_eq(&knn1, &knn2)); + IGRAPH_ASSERT(vector_eq(&knnk1, &knnk2)); + + igraph_avg_nearest_neighbor_degree(&rg, igraph_vss_all(), IGRAPH_IN, IGRAPH_IN, &knn2, &knnk2, NULL); + + IGRAPH_ASSERT(vector_eq(&knn1, &knn2)); + IGRAPH_ASSERT(vector_eq(&knnk1, &knnk2)); + + igraph_vector_null(&knnk1); + igraph_vector_null(&knnk2); + + igraph_degree_correlation_vector(ug, NULL, &knnk1, IGRAPH_ALL, IGRAPH_ALL, false); + igraph_degree_correlation_vector(&rg, NULL, &knnk2, IGRAPH_OUT, IGRAPH_OUT, false); + + IGRAPH_ASSERT(vector_eq(&knnk1, &knnk2)); + + igraph_destroy(&rg); + + igraph_vector_destroy(&knnk2); + igraph_vector_destroy(&knnk1); + igraph_vector_destroy(&knn2); + igraph_vector_destroy(&knn1); +} + +int main(void) { + igraph_t ug, dg, g; + igraph_vector_t uweights, dweights; + + igraph_small(&ug, 10, IGRAPH_UNDIRECTED, + 0, 4, 1, 1, 1, 2, 1, 2, 1, 8, 2, 3, 2, 6, 3, + 5, 3, 6, 3, 7, 4, 7, 7, 8, 7, 8, 7, 8, 6, 6, + -1); + + igraph_vector_init_range(&uweights, 0, igraph_ecount(&ug)); + igraph_vector_scale(&uweights, 1.0/8); + + igraph_small(&dg, 6, IGRAPH_DIRECTED, + 0, 0, 0, 1, 0, 2, 1, 0, 1, 3, 1, 4, 2, 0, 2, + 3, 3, 2, 3, 3, 4, 2, 4, 3, 0, 1, 1, 3, 1, 3, + -1); + + igraph_vector_init_range(&dweights, 0, igraph_ecount(&dg)); + igraph_vector_scale(&dweights, 1.0/8); + + printf("UNWEIGHTED\n\n"); + printf("Undirected\n"); + compare_implementations(&ug, IGRAPH_ALL, IGRAPH_ALL, NULL); + + printf("Directed treated as undirected\n"); + compare_implementations(&dg, IGRAPH_ALL, IGRAPH_ALL, NULL); + + printf("Directed: out, all\n"); + compare_implementations(&dg, IGRAPH_OUT, IGRAPH_ALL, NULL); + + printf("Directed: out, in\n"); + compare_implementations(&dg, IGRAPH_OUT, IGRAPH_IN, NULL); + + printf("Directed: out, out\n"); + compare_implementations(&dg, IGRAPH_OUT, IGRAPH_OUT, NULL); + + printf("\nWEIGHTED\n\n"); + printf("Undirected\n"); + compare_implementations(&ug, IGRAPH_ALL, IGRAPH_ALL, &uweights); + + printf("Directed treated as undirected\n"); + compare_implementations(&dg, IGRAPH_ALL, IGRAPH_ALL, &dweights); + + printf("Directed: out, all\n"); + compare_implementations(&dg, IGRAPH_OUT, IGRAPH_ALL, &dweights); + + printf("Directed: out, in\n"); + compare_implementations(&dg, IGRAPH_OUT, IGRAPH_IN, &dweights); + + printf("Directed: out, out\n"); + compare_implementations(&dg, IGRAPH_OUT, IGRAPH_OUT, &dweights); + + printf("\nSPECIAL CASES\n\n"); + printf("Null graph\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + compare_implementations(&g, IGRAPH_ALL, IGRAPH_ALL, NULL); + igraph_destroy(&g); + + printf("Singleton with loop\n"); + igraph_small(&g, 1, IGRAPH_UNDIRECTED, 0,0, -1); + compare_implementations(&g, IGRAPH_ALL, IGRAPH_ALL, NULL); + igraph_destroy(&g); + + printf("Two isolated vertices\n"); + igraph_empty(&g, 2, IGRAPH_UNDIRECTED); + compare_implementations(&g, IGRAPH_ALL, IGRAPH_ALL, NULL); + igraph_destroy(&g); + + printf("Check directed/undirected equivalence principle\n"); + check_dir_undir_equiv(&ug); + + igraph_vector_destroy(&dweights); + igraph_destroy(&dg); + igraph_vector_destroy(&uweights); + igraph_destroy(&ug); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/knn.out b/tests/unit/knn.out new file mode 100644 index 0000000..11387ee --- /dev/null +++ b/tests/unit/knn.out @@ -0,0 +1,61 @@ +UNWEIGHTED + +Undirected +k_nn_i: ( 2 4.4 4.5 3.5 3 4 4 3.6 5 NaN ) +k_nn(k): ( NaN 3 3 NaN 4.25 4 ) + +Directed treated as undirected +k_nn_i: ( 6.42857 6.85714 6.6 6.25 6.66667 NaN ) +k_nn(k): ( NaN NaN NaN 6.66667 NaN 6.6 NaN 6.64286 6.25 ) + +Directed: out, all +k_nn_i: ( 6.5 6.8 7.5 6.5 6.5 NaN ) +k_nn(k): ( NaN NaN 6.83333 NaN 6.5 6.8 ) + +Directed: out, in +k_nn_i: ( 2.5 4.4 4.5 4.5 4.5 NaN ) +k_nn(k): ( NaN NaN 4.5 NaN 2.5 4.4 ) + +Directed: out, out +k_nn_i: ( 4 2.4 3 2 2 NaN ) +k_nn(k): ( NaN NaN 2.33333 NaN 4 2.4 ) + + +WEIGHTED + +Undirected +k_nn_i: ( NaN 4.18182 4.3125 3.58621 5 4 4 3.63636 5 NaN ) +k_nn(k): ( NaN 4 5 NaN 4.25984 3.72727 ) + +Directed treated as undirected +k_nn_i: ( 6.33333 7.21154 6.24242 6.25333 6.65385 NaN ) +k_nn(k): ( NaN NaN NaN 6.65385 NaN 6.24242 NaN 6.93421 6.25333 ) + +Directed: out, all +k_nn_i: ( 6.73333 7.28205 7.53846 6.58824 6.57143 NaN ) +k_nn(k): ( NaN NaN 6.82353 NaN 6.73333 7.28205 ) + +Directed: out, in +k_nn_i: ( 2.13333 5.12821 4.61538 4.58824 4.57143 NaN ) +k_nn(k): ( NaN NaN 4.58824 NaN 2.13333 5.12821 ) + +Directed: out, out +k_nn_i: ( 4.6 2.15385 2.92308 2 2 NaN ) +k_nn(k): ( NaN NaN 2.23529 NaN 4.6 2.15385 ) + + +SPECIAL CASES + +Null graph +k_nn_i: ( ) +k_nn(k): ( NaN ) + +Singleton with loop +k_nn_i: ( 2 ) +k_nn(k): ( NaN NaN 2 ) + +Two isolated vertices +k_nn_i: ( NaN NaN ) +k_nn(k): ( NaN ) + +Check directed/undirected equivalence principle diff --git a/tests/unit/levc-stress.c b/tests/unit/levc-stress.c new file mode 100644 index 0000000..f8b6cc1 --- /dev/null +++ b/tests/unit/levc-stress.c @@ -0,0 +1,71 @@ +/* -*- mode: C -*- */ +/* vim:set sw=4 ts=4 sts=4 et: */ +/* + 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 + +*/ + +/* This is a test for bug #1002140, reported by Luiz Fernando + Bittencourt: https://bugs.launchpad.net/igraph/+bug/1002140 */ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_rng_seed(igraph_rng_default(), 42); /* make tests deterministic */ + + for (int k = 0; k < 20; k++) { + igraph_t g; + igraph_matrix_int_t merges; + igraph_vector_int_t membership; + igraph_arpack_options_t options; + double modularity; + igraph_vector_t history; + FILE *DLFile = fopen("input.dl", "r"); + + IGRAPH_ASSERT(DLFile != NULL); + + igraph_read_graph_dl(&g, DLFile, IGRAPH_UNDIRECTED); + fclose(DLFile); + + igraph_matrix_int_init(&merges, 0, 0); + igraph_vector_int_init(&membership, 0); + igraph_vector_init(&history, 0); + igraph_arpack_options_init(&options); + + igraph_community_leading_eigenvector(&g, /*weights=*/ NULL, &merges, + &membership, igraph_vcount(&g), + &options, &modularity, + /*start=*/ 0, /*eigenvalues=*/ NULL, + /*eigenvectors=*/ NULL, &history, + /*callback=*/ NULL, + /*callback_extra=*/ NULL); + + igraph_vector_destroy(&history); + igraph_vector_int_destroy(&membership); + igraph_matrix_int_destroy(&merges); + igraph_destroy(&g); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/lineendings.c b/tests/unit/lineendings.c new file mode 100644 index 0000000..30fa90b --- /dev/null +++ b/tests/unit/lineendings.c @@ -0,0 +1,71 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "../../tests/unit/test_utilities.h" + +int main(void) { + + igraph_t g; + FILE *ifile; + + /* turn on attribute handling */ + /* igraph_set_attribute_table(&igraph_cattribute_table); */ + + ifile = fopen("pajek1.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + igraph_write_graph_pajek(&g, stdout); + igraph_destroy(&g); + + ifile = fopen("pajek2.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + igraph_write_graph_pajek(&g, stdout); + igraph_destroy(&g); + + ifile = fopen("pajek3.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + igraph_write_graph_pajek(&g, stdout); + igraph_destroy(&g); + + ifile = fopen("pajek4.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + igraph_write_graph_pajek(&g, stdout); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/lineendings.out b/tests/unit/lineendings.out new file mode 100644 index 0000000..e313473 --- /dev/null +++ b/tests/unit/lineendings.out @@ -0,0 +1,44 @@ +*Vertices 10 +*Edges +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +*Vertices 10 +*Edges +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +*Vertices 10 +*Edges +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +*Vertices 10 +*Edges +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 diff --git a/tests/unit/links.net b/tests/unit/links.net new file mode 100644 index 0000000..175b90b --- /dev/null +++ b/tests/unit/links.net @@ -0,0 +1,16 @@ +% Example Pajek file + +*Network TRALALA +*vertices 4 + 1 "1" 0.0938 0.0896 ellipse x_fact 1 y_fact 1 + 2 "2" 0.8188 0.2458 ellipse x_fact 1 y_fact 1 + 3 "3" 0.3688 0.7792 ellipse x_fact 1 + 4 "4" 0.9583 0.8563 ellipse x_fact 1 +*arcs +1 1 1 h2 0 w 3 c Blue s 3 a1 -130 k1 0.6 a2 -130 k2 0.6 ap 0.5 l "Bezier loop" lc BlueViolet fos 20 lr 58 lp 0.3 la 360 +2 1 1 h2 0 a1 120 k1 1.3 a2 -120 k2 0.3 ap 25 l "Bezier arc" lphi 270 la 180 lr 19 lp 0.5 +1 2 1 h2 0 a1 40 k1 2.8 a2 30 k2 0.8 ap 25 l "Bezier arc" lphi 90 la 0 lp 0.65 +4 2 -1 h2 0 w 1 k1 -2 k2 250 ap 25 l "Circular arc" c Red lc OrangeRed +3 4 1 p Dashed h2 0 w 2 c OliveGreen ap 25 l "Straight arc" lc PineGreen +1 3 1 p Dashed h2 0 w 5 k1 -1 k2 -20 ap 25 l "Oval arc" c Brown lc Black +3 3 -1 h1 6 w 1 h2 12 k1 -2 k2 -15 ap 0.5 l "Circular loop" c Red lc OrangeRed lphi 270 la 180 diff --git a/tests/unit/marked_queue.c b/tests/unit/marked_queue.c new file mode 100644 index 0000000..fafb44a --- /dev/null +++ b/tests/unit/marked_queue.c @@ -0,0 +1,66 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include +#include "core/marked_queue.h" + +#include "test_utilities.h" + +int main(void) { + igraph_marked_queue_int_t Q; + igraph_integer_t i; + + igraph_marked_queue_int_init(&Q, 100); + for (i = 0; i < 50; i++) { + igraph_marked_queue_int_push(&Q, i); + if (!igraph_marked_queue_int_iselement(&Q, i)) { + return 4; + } + if (! ((i + 1) % 5)) { + igraph_marked_queue_int_start_batch(&Q); + } + } + + for (i = 1; i < 50; i++) { + if (!igraph_marked_queue_int_iselement(&Q, i)) { + printf("Problem with %" IGRAPH_PRId ".\n", i); + return 3; + } + } + + for (i = 0; i <= 50 / 5; i++) { + if (igraph_marked_queue_int_empty(&Q)) { + return 1; + } + igraph_marked_queue_int_pop_back_batch(&Q); + } + if (!igraph_marked_queue_int_empty(&Q)) { + return 2; + } + + igraph_marked_queue_int_destroy(&Q); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/matrix.c b/tests/unit/matrix.c new file mode 100644 index 0000000..4df0b32 --- /dev/null +++ b/tests/unit/matrix.c @@ -0,0 +1,178 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_matrix_t m, m1; + igraph_integer_t i, j, k; + igraph_real_t arr[] = { 1, 2, 11, 12, 21, 22 }; + + /* igraph_matrix_init, igraph_matrix_destroy */ + igraph_matrix_init(&m, 10, 10); + igraph_matrix_destroy(&m); + + igraph_matrix_init(&m, 0, 0); + igraph_matrix_destroy(&m); + + /* igraph_matrix_ncol, igraph_matrix_nrow */ + igraph_matrix_init(&m, 10, 5); + if (igraph_matrix_nrow(&m) != 10) { + return 1; + } + if (igraph_matrix_ncol(&m) != 5) { + return 2; + } + + /* igraph_matrix_size, igraph_matrix_resize */ + igraph_matrix_resize(&m, 6, 5); + if (igraph_matrix_size(&m) != 30) { + return 3; + } + if (igraph_matrix_nrow(&m) != 6) { + return 4; + } + if (igraph_matrix_ncol(&m) != 5) { + return 5; + } + igraph_matrix_resize(&m, 2, 4); + if (igraph_matrix_nrow(&m) != 2) { + return 6; + } + if (igraph_matrix_ncol(&m) != 4) { + return 7; + } + igraph_matrix_destroy(&m); + + /* MATRIX, igraph_matrix_null */ + igraph_matrix_init(&m, 3, 4); + for (i = 0; i < igraph_matrix_nrow(&m); i++) { + for (j = 0; j < igraph_matrix_ncol(&m); j++) { + MATRIX(m, i, j) = i + 1; + } + } + print_matrix(&m); + igraph_matrix_null(&m); + print_matrix(&m); + igraph_matrix_destroy(&m); + + /* igraph_matrix_add_cols, igraph_matrix_add_rows */ + igraph_matrix_init(&m, 4, 3); + for (i = 0; i < igraph_matrix_nrow(&m); i++) { + for (j = 0; j < igraph_matrix_ncol(&m); j++) { + MATRIX(m, i, j) = (i + 1) * (j + 1); + } + } + igraph_matrix_add_cols(&m, 2); + igraph_matrix_add_rows(&m, 2); + if (igraph_matrix_ncol(&m) != 5) { + return 8; + } + if (igraph_matrix_nrow(&m) != 6) { + return 9; + } + igraph_matrix_destroy(&m); + + /* igraph_matrix_remove_col */ + igraph_matrix_init(&m, 5, 3); + for (i = 0; i < igraph_matrix_nrow(&m); i++) { + for (j = 0; j < igraph_matrix_ncol(&m); j++) { + MATRIX(m, i, j) = (i + 1) * (j + 1); + } + } + igraph_matrix_remove_col(&m, 0); + print_matrix(&m); + igraph_matrix_remove_col(&m, 1); + print_matrix(&m); + igraph_matrix_destroy(&m); + + /* TODO: igraph_matrix_permdelete_rows */ + + /* igraph_matrix_copy */ + igraph_matrix_init(&m, 2, 3); + for (i = 0; i < igraph_matrix_nrow(&m); i++) { + for (j = 0; j < igraph_matrix_ncol(&m); j++) { + MATRIX(m, i, j) = (i + 1) * (j + 1); + } + } + igraph_matrix_init_copy(&m1, &m); + print_matrix(&m1); + igraph_matrix_destroy(&m); + igraph_matrix_destroy(&m1); + + /* igraph_matrix_init_array */ + igraph_matrix_init_array(&m, arr, 2, 3, IGRAPH_COLUMN_MAJOR); + print_matrix(&m); + igraph_matrix_destroy(&m); + + igraph_matrix_init_array(&m, arr, 2, 3, IGRAPH_ROW_MAJOR); + print_matrix(&m); + igraph_matrix_destroy(&m); + + igraph_matrix_init_array(&m, arr, 3, 2, IGRAPH_ROW_MAJOR); + print_matrix(&m); + igraph_matrix_destroy(&m); + + /* in-place transpose */ + igraph_matrix_init(&m, 5, 2); + k = 0; + for (i = 0; i < igraph_matrix_ncol(&m); i++) { + for (j = 0; j < igraph_matrix_nrow(&m); j++) { + MATRIX(m, j, i) = k++; + } + } + print_matrix(&m); + igraph_matrix_transpose(&m); + print_matrix(&m); + igraph_matrix_destroy(&m); + + igraph_matrix_init(&m, 5, 1); + k = 0; + for (i = 0; i < igraph_matrix_ncol(&m); i++) { + for (j = 0; j < igraph_matrix_nrow(&m); j++) { + MATRIX(m, j, i) = k++; + } + } + print_matrix(&m); + igraph_matrix_transpose(&m); + print_matrix(&m); + igraph_matrix_destroy(&m); + + igraph_matrix_init(&m, 1, 5); + k = 0; + for (i = 0; i < igraph_matrix_ncol(&m); i++) { + for (j = 0; j < igraph_matrix_nrow(&m); j++) { + MATRIX(m, j, i) = k++; + } + } + print_matrix(&m); + igraph_matrix_transpose(&m); + print_matrix(&m); + igraph_matrix_destroy(&m); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/matrix.out b/tests/unit/matrix.out new file mode 100644 index 0000000..4c20a62 --- /dev/null +++ b/tests/unit/matrix.out @@ -0,0 +1,44 @@ +[ 1 1 1 1 + 2 2 2 2 + 3 3 3 3 ] +[ 0 0 0 0 + 0 0 0 0 + 0 0 0 0 ] +[ 2 3 + 4 6 + 6 9 + 8 12 + 10 15 ] +[ 2 + 4 + 6 + 8 + 10 ] +[ 1 2 3 + 2 4 6 ] +[ 1 11 21 + 2 12 22 ] +[ 1 2 11 + 12 21 22 ] +[ 1 2 + 11 12 + 21 22 ] +[ 0 5 + 1 6 + 2 7 + 3 8 + 4 9 ] +[ 0 1 2 3 4 + 5 6 7 8 9 ] +[ 0 + 1 + 2 + 3 + 4 ] +[ 0 1 2 3 4 ] +[ 0 1 2 3 4 ] +[ 0 + 1 + 2 + 3 + 4 ] diff --git a/tests/unit/matrix2.c b/tests/unit/matrix2.c new file mode 100644 index 0000000..8d05c16 --- /dev/null +++ b/tests/unit/matrix2.c @@ -0,0 +1,345 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2007-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "test_utilities.h" + +void byrow(igraph_matrix_t *m) { + igraph_integer_t r = igraph_matrix_nrow(m), c = igraph_matrix_ncol(m); + igraph_integer_t n = 0, i, j; + for (i = 0; i < r; i++) { + for (j = 0; j < c; j++) { + MATRIX(*m, i, j) = n++; + } + } +} + +#define apply(m,a,b) \ + for (i=0; i + 334 Harvard st, Cambridge MA, USA 02139 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + 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 "test_utilities.h" + +int main(void) { + igraph_matrix_t m; + + igraph_matrix_init(&m, 10, 10); + if (igraph_matrix_capacity(&m) != 100) { + return 1; + } + + igraph_matrix_add_cols(&m, 5); + igraph_matrix_resize(&m, 5, 5); + igraph_matrix_resize_min(&m); + if (igraph_matrix_capacity(&m) != igraph_matrix_size(&m)) { + return 2; + } + + igraph_matrix_destroy(&m); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/matrix_complex.c b/tests/unit/matrix_complex.c new file mode 100644 index 0000000..aa89082 --- /dev/null +++ b/tests/unit/matrix_complex.c @@ -0,0 +1,94 @@ +/* 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, see . +*/ + +#include +#include "core/math.h" /* M_PI */ +#include "test_utilities.h" + +int main(void) { + igraph_matrix_complex_t c; + igraph_matrix_t real; + igraph_matrix_t imag; + int e[] = {1, 2, 3, 4}; + int e2[] = {5, 6, 7, 8}; + + matrix_init_int_row_major(&real, 2, 2, e); + matrix_init_int_row_major(&imag, 2, 2, e2); + + printf("Complex matrix:\n"); + igraph_matrix_complex_create(&c, &real, &imag); + igraph_matrix_complex_fprint(&c, stdout); + + printf("Real part:\n"); + igraph_matrix_resize(&real, 0, 0); + igraph_matrix_complex_real(&c, &real); + igraph_matrix_print(&real); + + printf("Imaginary part:\n"); + igraph_matrix_resize(&imag, 0, 0); + igraph_matrix_complex_imag(&c, &imag); + igraph_matrix_print(&imag); + + printf("Real and imaginary part:\n"); + igraph_matrix_resize(&real, 0, 0); + igraph_matrix_resize(&imag, 0, 0); + igraph_matrix_complex_realimag(&c, &real, &imag); + igraph_matrix_print(&real); + igraph_matrix_print(&imag); + igraph_matrix_complex_destroy(&c); + + igraph_matrix_destroy(&real); + igraph_matrix_destroy(&imag); + + { + printf("Complex matrix from polar:\n"); + igraph_matrix_complex_t p; + igraph_real_t r_e[] = {1, 2, 3, 4}; + igraph_real_t theta_e[] = {0, .5 * M_PI, M_PI, 1.5 * M_PI}; + igraph_matrix_t r; + igraph_matrix_t theta; + igraph_matrix_view(&r, r_e, 2, 2); + igraph_matrix_view(&theta, theta_e, 2, 2); + igraph_matrix_complex_create_polar(&p, &r, &theta); + print_matrix_complex_round(&p); + igraph_matrix_complex_destroy(&p); + } + + VERIFY_FINALLY_STACK(); + + { + igraph_real_t e3[] = {1, 2, 3}; + igraph_real_t e4[] = {5, 6, 7, 8}; + printf("Check if unequal number of imaginary and real rows is handled correctly.\n"); + igraph_matrix_view(&real, e3, 1, 2); + igraph_matrix_view(&imag, e4, 2, 2); + CHECK_ERROR(igraph_matrix_complex_create(&c, &real, &imag), IGRAPH_EINVAL); + CHECK_ERROR(igraph_matrix_complex_create_polar(&c, &real, &imag), IGRAPH_EINVAL); + } + { + igraph_real_t e3[] = {1, 2, 3}; + igraph_real_t e4[] = {5, 6, 7, 8}; + printf("Check if unequal number of imaginary and real columns is handled correctly.\n"); + igraph_matrix_view(&real, e3, 2, 1); + igraph_matrix_view(&imag, e4, 2, 2); + CHECK_ERROR(igraph_matrix_complex_create(&c, &real, &imag), IGRAPH_EINVAL); + CHECK_ERROR(igraph_matrix_complex_create_polar(&c, &real, &imag), IGRAPH_EINVAL); + } + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/matrix_complex.out b/tests/unit/matrix_complex.out new file mode 100644 index 0000000..58e61fa --- /dev/null +++ b/tests/unit/matrix_complex.out @@ -0,0 +1,19 @@ +Complex matrix: +1+5i 2+6i +3+7i 4+8i +Real part: +1 2 +3 4 +Imaginary part: +5 6 +7 8 +Real and imaginary part: +1 2 +3 4 +5 6 +7 8 +Complex matrix from polar: +1+0i -3+0i +0+2i -0-4i +Check if unequal number of imaginary and real rows is handled correctly. +Check if unequal number of imaginary and real columns is handled correctly. diff --git a/tests/unit/maximal_cliques_callback.c b/tests/unit/maximal_cliques_callback.c new file mode 100644 index 0000000..e8f3bc3 --- /dev/null +++ b/tests/unit/maximal_cliques_callback.c @@ -0,0 +1,106 @@ +/* + IGraph library. + Copyright (C) 2020-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, see . +*/ + +#include +#include + +#include "test_utilities.h" + +struct userdata { + int i; + igraph_vector_int_list_t *list; +}; + +int compare_vectors(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2) { + igraph_integer_t s1, s2, i; + + s1 = igraph_vector_int_size(v1); + s2 = igraph_vector_int_size(v2); + if (s1 < s2) { + return -1; + } + if (s1 > s2) { + return 1; + } + for (i = 0; i < s1; ++i) { + if (VECTOR(*v1)[i] < VECTOR(*v2)[i]) { + return -1; + } + if (VECTOR(*v1)[i] > VECTOR(*v2)[i]) { + return 1; + } + } + return 0; +} + + +igraph_error_t handler(const igraph_vector_int_t *clique, void *arg) { + struct userdata *ud; + igraph_bool_t cont; + + ud = (struct userdata *) arg; + cont = 1; /* true */ + + if (compare_vectors(clique, igraph_vector_int_list_get_ptr(ud->list, ud->i)) != 0) { + printf("igraph_maximal_cliques() and igraph_maximal_cliques_callback() give different results.\n"); + cont = 0; /* false */ + } + + ud->i += 1; + + return cont ? IGRAPH_SUCCESS : IGRAPH_STOP; +} + + +igraph_error_t handler_stop(const igraph_vector_int_t *clique, void *arg) { + /* Stop search as soon as a 3-clique is found. */ + /* Since there are two 3-cliques in the test graph, this will stop the search before it is complete. */ + IGRAPH_UNUSED(arg); + return (igraph_vector_int_size(clique) == 3) ? IGRAPH_STOP : IGRAPH_SUCCESS; +} + + +int main(void) { + igraph_t graph; + igraph_vector_int_list_t list; + struct userdata ud; + + igraph_small(&graph, 6, 0, + 1, 2, 2, 3, 3, 4, 4, 5, 5, 2, 2, 4, + -1); + + igraph_vector_int_list_init(&list, 0); + igraph_maximal_cliques(&graph, &list, 0, 0); + + ud.i = 0; + ud.list = &list; + + /* Check that the callback function finds the same cliques as igraph_maximal_cliques() */ + igraph_maximal_cliques_callback(&graph, &handler, (void *) &ud, 0, 0); + + /* Check that the search can be stopped correctly */ + igraph_maximal_cliques_callback(&graph, &handler_stop, NULL, 0, 0); + + igraph_vector_int_list_destroy(&list); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/maximal_cliques_hist.c b/tests/unit/maximal_cliques_hist.c new file mode 100644 index 0000000..269b44d --- /dev/null +++ b/tests/unit/maximal_cliques_hist.c @@ -0,0 +1,42 @@ +/* + IGraph library. + Copyright (C) 2020-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, see . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_t hist; + + igraph_small(&graph, 6, 0, + 1, 2, 2, 3, 3, 4, 4, 5, 5, 2, 2, 4, + -1); + + igraph_vector_init(&hist, 0); + + igraph_maximal_cliques_hist(&graph, &hist, 0, 0); + igraph_vector_print(&hist); + + igraph_vector_destroy(&hist); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/maximal_cliques_hist.out b/tests/unit/maximal_cliques_hist.out new file mode 100644 index 0000000..0586d9f --- /dev/null +++ b/tests/unit/maximal_cliques_hist.out @@ -0,0 +1 @@ +1 1 2 diff --git a/tests/unit/ncol.c b/tests/unit/ncol.c new file mode 100644 index 0000000..afdb569 --- /dev/null +++ b/tests/unit/ncol.c @@ -0,0 +1,73 @@ +/* + 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 . + */ + +#include +#include +#include "test_utilities.h" + +int main(void) { + igraph_t g_in; + igraph_t g_out; + FILE *file; + igraph_bool_t same; + + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_small(&g_in, 4, IGRAPH_DIRECTED, + 0, 1, 0, 1, 0, 1, + 1, 2, 2, 3, 3, 0, + -1); + + SETEAN(&g_in, "edge_attr", 0, 12.3); /* edge 0-1 */ + SETEAN(&g_in, "edge_attr", 4, -IGRAPH_INFINITY); /* edge 2-3 */ + SETVAS(&g_in, "vertex_attr", 0, "vertex_name0"); + SETVAS(&g_in, "vertex_attr", 1, "vertex_name1"); + SETVAS(&g_in, "vertex_attr", 2, "vertex_name2"); + SETVAS(&g_in, "vertex_attr", 3, "vertex_name3"); + + char filename[] = "ncol.tmp"; + file = fopen(filename, "w"); + IGRAPH_ASSERT(file != NULL); /* make sure that the file was created successfully */ + + igraph_write_graph_ncol(&g_in, file, "vertex_attr", "edge_attr"); + fclose(file); + + + file = fopen(filename, "r"); + IGRAPH_ASSERT(file); + igraph_read_graph_ncol(&g_out, file, NULL, 1, IGRAPH_ADD_WEIGHTS_YES, + IGRAPH_DIRECTED); + + /* is_same_graph() checks that vertex and edge lists are the same, + * but does not verify attributes. */ + igraph_is_same_graph(&g_in, &g_out, &same); + if (!same) { + IGRAPH_FATAL("Written and read graph are not the same."); + } + + print_attributes(&g_out); + + fclose(file); + unlink(filename); + igraph_destroy(&g_in); + igraph_destroy(&g_out); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/ncol.out b/tests/unit/ncol.out new file mode 100644 index 0000000..6c79624 --- /dev/null +++ b/tests/unit/ncol.out @@ -0,0 +1,11 @@ +Vertex 0: name="vertex_name0" +Vertex 1: name="vertex_name1" +Vertex 2: name="vertex_name2" +Vertex 3: name="vertex_name3" +Edge 0 (0-1): weight=12.3 +Edge 1 (0-1): weight=NaN +Edge 2 (0-1): weight=NaN +Edge 3 (1-2): weight=NaN +Edge 4 (2-3): weight=-Inf +Edge 5 (3-0): weight=NaN + diff --git a/tests/unit/null_communities.c b/tests/unit/null_communities.c new file mode 100644 index 0000000..47b2ce6 --- /dev/null +++ b/tests/unit/null_communities.c @@ -0,0 +1,191 @@ +/* + 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, see . +*/ + +#include + +#include "test_utilities.h" + +/* Testing community detection on the null graph: + * + * - The modularity should be NaN. + * - In hierarchical methods, the modularity vector should have size 1. + */ + +int main(void) { + igraph_t g; + igraph_vector_t modularity; + igraph_vector_int_t membership; + igraph_matrix_int_t merges; + igraph_real_t m; + igraph_arpack_options_t ao; + + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + + igraph_vector_init(&modularity, 2); + igraph_vector_int_init(&membership, 1); + igraph_matrix_int_init(&merges, 1, 1); + + /* Edge betweenness */ + + igraph_community_edge_betweenness(&g, NULL, NULL, &merges, NULL, &modularity, &membership, 0, NULL); + + IGRAPH_ASSERT(igraph_matrix_int_nrow(&merges) == 0); + IGRAPH_ASSERT(igraph_matrix_int_ncol(&merges) == 2); + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(igraph_vector_size(&modularity) == 1); + IGRAPH_ASSERT(isnan(VECTOR(modularity)[0])); + + /* Fast greedy */ + + igraph_vector_resize(&modularity, 2); + igraph_vector_int_resize(&membership, 1); + igraph_matrix_int_resize(&merges, 1, 1); + + igraph_community_fastgreedy(&g, NULL, &merges, &modularity, &membership); + + IGRAPH_ASSERT(igraph_matrix_int_nrow(&merges) == 0); + IGRAPH_ASSERT(igraph_matrix_int_ncol(&merges) == 2); + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(igraph_vector_size(&modularity) == 1); + IGRAPH_ASSERT(isnan(VECTOR(modularity)[0])); + + /* Fluid communities */ + + igraph_vector_int_resize(&membership, 1); + + igraph_community_fluid_communities(&g, 0, &membership); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + + /* InfoMAP */ + + m = 2; + igraph_vector_int_resize(&membership, 1); + + igraph_community_infomap(&g, NULL, NULL, 3, &membership, &m); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + + /* Label propagation */ + + igraph_vector_int_resize(&membership, 1); + + igraph_community_label_propagation(&g, &membership, IGRAPH_ALL, NULL, NULL, NULL); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + + /* Leading eigenvector */ + + m = 2; + igraph_vector_int_resize(&membership, 1); + igraph_matrix_int_resize(&merges, 1, 1); + + igraph_arpack_options_init(&ao); + igraph_community_leading_eigenvector(&g, NULL, &merges, &membership, 1, &ao, &m, 0, NULL, NULL, NULL, NULL, NULL); + + IGRAPH_ASSERT(igraph_matrix_int_nrow(&merges) == 0); + IGRAPH_ASSERT(igraph_matrix_int_ncol(&merges) == 2); + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(isnan(m)); + + /* Leiden */ + + m = 2; + igraph_vector_int_resize(&membership, 1); + + igraph_community_leiden(&g, NULL, NULL, 1, 0.01, 0, 1, &membership, NULL, &m); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(isnan(m)); + + /* Multilevel */ + + igraph_vector_int_resize(&membership, 1); + igraph_vector_resize(&modularity, 2); + + igraph_community_multilevel(&g, NULL, 1, &membership, NULL, &modularity); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(igraph_vector_size(&modularity) == 1); + IGRAPH_ASSERT(isnan(VECTOR(modularity)[0])); + + /* Optimal modularity */ + /* Test only when GLPK is available */ + + { + igraph_error_t ret; + igraph_error_handler_t *handler; + + m = 2; + igraph_vector_int_resize(&membership, 1); + + handler = igraph_set_error_handler(igraph_error_handler_ignore); + ret = igraph_community_optimal_modularity(&g, &m, &membership, NULL); + igraph_set_error_handler(handler); + + if (ret != IGRAPH_UNIMPLEMENTED) { + IGRAPH_ASSERT(ret == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(isnan(m)); + } + } + + /* Spinglass */ + + m = 2; + igraph_vector_int_resize(&membership, 1); + + igraph_community_spinglass(&g, NULL, &m, NULL, &membership, NULL, 5, 0, 1, 0.01, 0.99, IGRAPH_SPINCOMM_UPDATE_SIMPLE, 1, IGRAPH_SPINCOMM_IMP_ORIG, 1); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(isnan(m)); + + m = 2; + igraph_vector_int_resize(&membership, 1); + + igraph_community_spinglass(&g, NULL, &m, NULL, &membership, NULL, 5, 0, 1, 0.01, 0.99, IGRAPH_SPINCOMM_UPDATE_SIMPLE, 1, IGRAPH_SPINCOMM_IMP_NEG, 1); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(isnan(m)); + + /* Walktrap */ + + igraph_vector_resize(&modularity, 2); + igraph_vector_int_resize(&membership, 1); + igraph_matrix_int_resize(&merges, 1, 1); + + igraph_community_walktrap(&g, NULL, 4, &merges, &modularity, &membership); + + IGRAPH_ASSERT(igraph_matrix_int_nrow(&merges) == 0); + IGRAPH_ASSERT(igraph_matrix_int_ncol(&merges) == 2); + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 0); + IGRAPH_ASSERT(igraph_vector_size(&modularity) == 1); + IGRAPH_ASSERT(isnan(VECTOR(modularity)[0])); + + /* Cleanup */ + + igraph_matrix_int_destroy(&merges); + igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&modularity); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/overflow.c b/tests/unit/overflow.c new file mode 100644 index 0000000..dc8f7f8 --- /dev/null +++ b/tests/unit/overflow.c @@ -0,0 +1,73 @@ +/* + 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 . +*/ + +#include +#include "math/safe_intop.h" + +#define EXPECT_SUCC(expr) IGRAPH_ASSERT((expr) == IGRAPH_SUCCESS) +#define EXPECT_FAIL(expr) IGRAPH_ASSERT((expr) != IGRAPH_SUCCESS) + +int main(void) { + igraph_integer_t res; + + igraph_set_error_handler(igraph_error_handler_printignore); + + /* Addition */ + + EXPECT_SUCC(igraph_i_safe_add(1, 2, &res)); + EXPECT_SUCC(igraph_i_safe_add(-123, 42, &res)); + EXPECT_SUCC(igraph_i_safe_add(-5, -137, &res)); + EXPECT_SUCC(igraph_i_safe_add(IGRAPH_INTEGER_MAX-1, 1, &res)); + EXPECT_SUCC(igraph_i_safe_add(IGRAPH_INTEGER_MIN+1, -1, &res)); + EXPECT_SUCC(igraph_i_safe_add(IGRAPH_INTEGER_MIN, IGRAPH_INTEGER_MAX, &res)); + EXPECT_SUCC(igraph_i_safe_add(IGRAPH_INTEGER_MAX/2, IGRAPH_INTEGER_MAX/2, &res)); + + EXPECT_FAIL(igraph_i_safe_add(IGRAPH_INTEGER_MAX, IGRAPH_INTEGER_MAX, &res)); + EXPECT_FAIL(igraph_i_safe_add(IGRAPH_INTEGER_MAX, 1, &res)); + EXPECT_FAIL(igraph_i_safe_add(IGRAPH_INTEGER_MIN, -1, &res)); + EXPECT_FAIL(igraph_i_safe_add(IGRAPH_INTEGER_MAX/2 + 2, IGRAPH_INTEGER_MAX/2, &res)); + + /* Multiplication */ + + EXPECT_SUCC(igraph_i_safe_mult(3, 4, &res)); + EXPECT_SUCC(igraph_i_safe_mult(-35, 14, &res)); + EXPECT_SUCC(igraph_i_safe_mult(-5, -9, &res)); + EXPECT_SUCC(igraph_i_safe_mult(IGRAPH_INTEGER_MAX, 1, &res)); + EXPECT_SUCC(igraph_i_safe_mult(1, IGRAPH_INTEGER_MAX, &res)); + EXPECT_SUCC(igraph_i_safe_mult(2, IGRAPH_INTEGER_MAX/2, &res)); + EXPECT_SUCC(igraph_i_safe_mult(2, IGRAPH_INTEGER_MIN/2, &res)); + + EXPECT_FAIL(igraph_i_safe_mult(2, IGRAPH_INTEGER_MAX, &res)); + EXPECT_FAIL(igraph_i_safe_mult(2, IGRAPH_INTEGER_MIN, &res)); + EXPECT_FAIL(igraph_i_safe_mult(IGRAPH_INTEGER_MAX, IGRAPH_INTEGER_MAX, &res)); + EXPECT_FAIL(igraph_i_safe_mult(IGRAPH_INTEGER_MAX, IGRAPH_INTEGER_MIN, &res)); + EXPECT_FAIL(igraph_i_safe_mult(IGRAPH_INTEGER_MAX/2, 3, &res)); + EXPECT_FAIL(igraph_i_safe_mult(IGRAPH_INTEGER_MIN/2, 3, &res)); + EXPECT_FAIL(igraph_i_safe_mult(IGRAPH_INTEGER_MAX/2, -3, &res)); + EXPECT_FAIL(igraph_i_safe_mult(IGRAPH_INTEGER_MIN/2, -3, &res)); + + /* The following tests assume that, mathematically, INTEGER_MIN = -INTEGER_MAX - 1 */ + + EXPECT_SUCC(igraph_i_safe_mult(IGRAPH_INTEGER_MAX, -1, &res)); + EXPECT_SUCC(igraph_i_safe_mult(-1, IGRAPH_INTEGER_MAX, &res)); + + EXPECT_FAIL(igraph_i_safe_mult(-1, IGRAPH_INTEGER_MIN, &res)); + EXPECT_FAIL(igraph_i_safe_mult(IGRAPH_INTEGER_MIN, -1, &res)); + + return 0; +} diff --git a/tests/unit/pajek.c b/tests/unit/pajek.c new file mode 100644 index 0000000..78d684f --- /dev/null +++ b/tests/unit/pajek.c @@ -0,0 +1,106 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_bool_t simple; + FILE *ifile; + + /* turn on attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + ifile = fopen("pajek5.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 9); + + igraph_destroy(&g); + + ifile = fopen("pajek6.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 9); + + igraph_destroy(&g); + + /* This file starts with a UTF-8 BOM, which Pajek itself supports. + * It also contains some custom attributes. */ + ifile = fopen("utf8_with_bom.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + IGRAPH_ASSERT(igraph_vcount(&g) == 10); + IGRAPH_ASSERT(igraph_ecount(&g) == 6); + + igraph_destroy(&g); + + /* File in Arcslist format */ + ifile = fopen("pajek_arcslist.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + IGRAPH_ASSERT(igraph_vcount(&g) == 18); + IGRAPH_ASSERT(igraph_ecount(&g) == 55); + IGRAPH_ASSERT(igraph_is_directed(&g)); + + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + + igraph_destroy(&g); + + /* File in Edgeslist format */ + ifile = fopen("pajek_edgeslist.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + IGRAPH_ASSERT(igraph_vcount(&g) == 3); + IGRAPH_ASSERT(igraph_ecount(&g) == 3); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/pajek1.net b/tests/unit/pajek1.net new file mode 100644 index 0000000..8d8c1fb --- /dev/null +++ b/tests/unit/pajek1.net @@ -0,0 +1,21 @@ +*Vertices 10 +1 "Vert 1" 0 0 box x_fact 1 y_fact 1 ic Green +2 "Vert 2" 0 0 box x_fact 1 y_fact 1 ic Green +3 "Vert 3" 0 0 box x_fact 1 y_fact 1 ic Green +4 "Vert 4" 0 0 box x_fact 1 y_fact 1 ic Green +5 "Vert 5" 0 0 box x_fact 1 y_fact 1 ic Green +6 "Vert 6" 0 0 box x_fact 1 y_fact 1 ic Blue +7 "Vert 7" 0 0 box x_fact 1 y_fact 1 ic Red +8 "Vert 8" 0 0 box x_fact 1 y_fact 1 ic Green +9 "Vert 9" 0 0 box x_fact 1 y_fact 1 ic Green +10 "Vert 10" 0 0 box x_fact 1 y_fact 1 ic Green +*Edges +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 diff --git a/tests/unit/pajek2.c b/tests/unit/pajek2.c new file mode 100644 index 0000000..8a69d40 --- /dev/null +++ b/tests/unit/pajek2.c @@ -0,0 +1,55 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + FILE *ifile; + int i, n; + + /* turn on attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + ifile = fopen("bipartite.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + IGRAPH_ASSERT(igraph_vcount(&g) == 13); + IGRAPH_ASSERT(igraph_ecount(&g) == 11); + + for (i = 0, n = igraph_vcount(&g); i < n; i++) { + printf("%d ", (int) VAB(&g, "type", i)); + } + printf("\n"); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/pajek2.net b/tests/unit/pajek2.net new file mode 100644 index 0000000..6fb83b8 --- /dev/null +++ b/tests/unit/pajek2.net @@ -0,0 +1 @@ +*Vertices 10 1 "Vert 1" 0 0 box x_fact 1 y_fact 1 ic Green 2 "Vert 2" 0 0 box x_fact 1 y_fact 1 ic Green 3 "Vert 3" 0 0 box x_fact 1 y_fact 1 ic Green 4 "Vert 4" 0 0 box x_fact 1 y_fact 1 ic Green 5 "Vert 5" 0 0 box x_fact 1 y_fact 1 ic Green 6 "Vert 6" 0 0 box x_fact 1 y_fact 1 ic Blue 7 "Vert 7" 0 0 box x_fact 1 y_fact 1 ic Red 8 "Vert 8" 0 0 box x_fact 1 y_fact 1 ic Green 9 "Vert 9" 0 0 box x_fact 1 y_fact 1 ic Green 10 "Vert 10" 0 0 box x_fact 1 y_fact 1 ic Green *Edges 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 diff --git a/tests/unit/pajek2.out b/tests/unit/pajek2.out new file mode 100644 index 0000000..6816138 --- /dev/null +++ b/tests/unit/pajek2.out @@ -0,0 +1 @@ +0 0 0 0 0 0 0 0 1 1 1 1 1 diff --git a/tests/unit/pajek3.net b/tests/unit/pajek3.net new file mode 100644 index 0000000..4017af3 --- /dev/null +++ b/tests/unit/pajek3.net @@ -0,0 +1,21 @@ +*Vertices 10 +1 "Vert 1" 0 0 box x_fact 1 y_fact 1 ic Green +2 "Vert 2" 0 0 box x_fact 1 y_fact 1 ic Green +3 "Vert 3" 0 0 box x_fact 1 y_fact 1 ic Green +4 "Vert 4" 0 0 box x_fact 1 y_fact 1 ic Green +5 "Vert 5" 0 0 box x_fact 1 y_fact 1 ic Green +6 "Vert 6" 0 0 box x_fact 1 y_fact 1 ic Blue +7 "Vert 7" 0 0 box x_fact 1 y_fact 1 ic Red +8 "Vert 8" 0 0 box x_fact 1 y_fact 1 ic Green +9 "Vert 9" 0 0 box x_fact 1 y_fact 1 ic Green +10 "Vert 10" 0 0 box x_fact 1 y_fact 1 ic Green +*Edges +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 diff --git a/tests/unit/pajek4.net b/tests/unit/pajek4.net new file mode 100644 index 0000000..a5f0db4 --- /dev/null +++ b/tests/unit/pajek4.net @@ -0,0 +1,21 @@ +*Vertices 10 + 1 "Vert 1" 0 0 box x_fact 1 y_fact 1 ic Green + 2 "Vert 2" 0 0 box x_fact 1 y_fact 1 ic Green + 3 "Vert 3" 0 0 box x_fact 1 y_fact 1 ic Green + 4 "Vert 4" 0 0 box x_fact 1 y_fact 1 ic Green + 5 "Vert 5" 0 0 box x_fact 1 y_fact 1 ic Green + 6 "Vert 6" 0 0 box x_fact 1 y_fact 1 ic Blue + 7 "Vert 7" 0 0 box x_fact 1 y_fact 1 ic Red + 8 "Vert 8" 0 0 box x_fact 1 y_fact 1 ic Green + 9 "Vert 9" 0 0 box x_fact 1 y_fact 1 ic Green + 10 "Vert 10" 0 0 box x_fact 1 y_fact 1 ic Green + *Edges + 1 2 + 2 3 + 3 4 + 4 5 + 5 6 + 6 7 + 7 8 + 8 9 + 9 10 diff --git a/tests/unit/pajek5.net b/tests/unit/pajek5.net new file mode 100644 index 0000000..239e842 --- /dev/null +++ b/tests/unit/pajek5.net @@ -0,0 +1,21 @@ +*Vertices 10 +1 "Vert 1" 0 0 box x_fact 1 y_fact 1 ic Green +2 "Vert 2" 0 0 box x_fact 1 y_fact 1 ic Green +3 "Vert 3" 0 0 box x_fact 1 y_fact 1 ic Green +4 "Vert 4" 0 0 box x_fact 1 y_fact 1 ic Green +5 "Vert 5" 0 0 box x_fact 1 y_fact 1 ic Green +6 "Vert 6" 0 0 box x_fact 1 y_fact 1 ic Blue +7 "Vert 7" 0 0 box x_fact 1 y_fact 1 ic Red +8 "Vert 8" 0 0 box x_fact 1 y_fact 1 ic Green +9 "Vert 9" 0 0 box x_fact 1 y_fact 1 ic Green +10 "Vert 10" 0 0 box x_fact 1 y_fact 1 ic Green +*Edges 9 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 diff --git a/tests/unit/pajek6.net b/tests/unit/pajek6.net new file mode 100644 index 0000000..9badd2d --- /dev/null +++ b/tests/unit/pajek6.net @@ -0,0 +1,21 @@ +*Vertices 10 +1 "Vert 1" 0 0 box x_fact 1 y_fact 1 ic Green +2 "Vert 2" 0 0 box x_fact 1 y_fact 1 ic Green +3 "Vert 3" 0 0 box x_fact 1 y_fact 1 ic Green +4 "Vert 4" 0 0 box x_fact 1 y_fact 1 ic Green +5 "Vert 5" 0 0 box x_fact 1 y_fact 1 ic Green +6 "Vert 6" 0 0 box x_fact 1 y_fact 1 ic Blue +7 "Vert 7" 0 0 box x_fact 1 y_fact 1 ic Red +8 "Vert 8" 0 0 box x_fact 1 y_fact 1 ic Green +9 "Vert 9" 0 0 box x_fact 1 y_fact 1 ic Green +10 "Vert 10" 0 0 box x_fact 1 y_fact 1 ic Green +*Arcs 9 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 diff --git a/tests/unit/pajek_arcslist.net b/tests/unit/pajek_arcslist.net new file mode 100644 index 0000000..366de7e --- /dev/null +++ b/tests/unit/pajek_arcslist.net @@ -0,0 +1,40 @@ +This is a simplified version of http://mrvar.fdv.uni-lj.si/pajek/data/sampsonl.net + +*Vertices 18 +1 "ROMUL_10" +2 "BONAVEN_5" +3 "AMBROSE_9" +4 "BERTH_6" +5 "PETER_4" +6 "LOUIS_11" +7 "VICTOR_8" +8 "WINF_12" +9 "JOHN_1" +10 "GREG_2" +11 "HUGH_14" +12 "BONI_15" +13 "MARK_7" +14 "ALBERT_16" +15 "AMAND_13" +16 "BASIL_3" +17 "ELIAS_17" +18 "SIMP_18" +*Arcslist +1 3 5 14 +2 1 7 14 +3 1 2 17 +4 5 6 10 +5 4 11 13 +6 1 4 9 +7 2 8 16 +8 1 2 9 +9 5 8 16 +10 4 8 14 +11 5 8 14 +12 1 2 14 +13 5 7 18 +14 1 11 12 15 +15 1 2 14 +16 1 2 7 +17 3 13 18 +18 1 2 7 diff --git a/tests/unit/pajek_bip.net b/tests/unit/pajek_bip.net new file mode 100644 index 0000000..4040647 --- /dev/null +++ b/tests/unit/pajek_bip.net @@ -0,0 +1,27 @@ +*vertices 15 10 + 1 "A" + 2 "B" + 3 "C" + 4 "D" + 5 "E" + 6 "F" + 7 "G" + 8 "H" + 9 "I" +10 "J" +11 "1" +12 "2" +13 "3" +14 "4" +15 "5" +*matrix +1 0 0 0 0 +1 1 0 0 0 +1 1 1 0 0 +1 1 1 1 0 +1 1 1 1 1 +0 0 0 0 1 +1 0 0 0 1 +1 1 0 1 1 +0 0 0 0 0 +1 0 1 0 1 diff --git a/tests/unit/pajek_bip2.net b/tests/unit/pajek_bip2.net new file mode 100644 index 0000000..bb4fc6a --- /dev/null +++ b/tests/unit/pajek_bip2.net @@ -0,0 +1,27 @@ +*vertices 15 10 + 1 "A" + 2 "B" + 3 "C" + 4 "D" + 5 "E" + 6 "F" + 7 "G" + 8 "H" + 9 "I" +10 "J" +11 "1" +12 "2" +13 "3" +14 "4" +15 "5" +*matrix +1 0 0 0 0 1 +1 1 0 0 0 2 +1 1 1 0 0 3 +1 1 1 1 0 4 +1 1 1 1 1 5 +0 0 0 0 1 1 +1 0 0 0 1 2 +1 1 0 1 1 4 +0 0 0 0 0 0 +1 0 1 0 1 3 diff --git a/tests/unit/pajek_bipartite.c b/tests/unit/pajek_bipartite.c new file mode 100644 index 0000000..d4273ff --- /dev/null +++ b/tests/unit/pajek_bipartite.c @@ -0,0 +1,47 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_vector_bool_t type; + igraph_bool_t typev[] = { false, true, false, true, false, true, false, true, false, true }; + + /* turn on attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + igraph_ring(&graph, 10, IGRAPH_UNDIRECTED, /*mutual=*/ false, /*circular=*/ true); + igraph_vector_bool_view(&type, typev, sizeof(typev) / sizeof(typev[0])); + SETVABV(&graph, "type", &type); + + igraph_write_graph_pajek(&graph, stdout); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/pajek_bipartite.out b/tests/unit/pajek_bipartite.out new file mode 100644 index 0000000..8b72b17 --- /dev/null +++ b/tests/unit/pajek_bipartite.out @@ -0,0 +1,22 @@ +*Vertices 10 5 +1 "1" +2 "3" +3 "5" +4 "7" +5 "9" +6 "2" +7 "4" +8 "6" +9 "8" +10 "10" +*Edges +1 6 +6 2 +2 7 +7 3 +3 8 +8 4 +4 9 +9 5 +5 10 +1 10 diff --git a/tests/unit/pajek_bipartite2.c b/tests/unit/pajek_bipartite2.c new file mode 100644 index 0000000..a06f810 --- /dev/null +++ b/tests/unit/pajek_bipartite2.c @@ -0,0 +1,64 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + FILE *ifile; + + /* turn on attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + /* first file, without marginals */ + + ifile = fopen("pajek_bip.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&graph, ifile); + fclose(ifile); + + print_attributes(&graph); + + igraph_destroy(&graph); + + /* second file, with marginals */ + + printf("---\n"); + + ifile = fopen("pajek_bip2.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&graph, ifile); + fclose(ifile); + + print_attributes(&graph); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/pajek_bipartite2.out b/tests/unit/pajek_bipartite2.out new file mode 100644 index 0000000..6ed4ce9 --- /dev/null +++ b/tests/unit/pajek_bipartite2.out @@ -0,0 +1,83 @@ +Vertex 0: type=0 id="A" name="A" +Vertex 1: type=0 id="B" name="B" +Vertex 2: type=0 id="C" name="C" +Vertex 3: type=0 id="D" name="D" +Vertex 4: type=0 id="E" name="E" +Vertex 5: type=0 id="F" name="F" +Vertex 6: type=0 id="G" name="G" +Vertex 7: type=0 id="H" name="H" +Vertex 8: type=0 id="I" name="I" +Vertex 9: type=0 id="J" name="J" +Vertex 10: type=1 id="1" name="1" +Vertex 11: type=1 id="2" name="2" +Vertex 12: type=1 id="3" name="3" +Vertex 13: type=1 id="4" name="4" +Vertex 14: type=1 id="5" name="5" +Edge 0 (10-0): weight=1 +Edge 1 (10-1): weight=1 +Edge 2 (11-1): weight=1 +Edge 3 (10-2): weight=1 +Edge 4 (11-2): weight=1 +Edge 5 (12-2): weight=1 +Edge 6 (10-3): weight=1 +Edge 7 (11-3): weight=1 +Edge 8 (12-3): weight=1 +Edge 9 (13-3): weight=1 +Edge 10 (10-4): weight=1 +Edge 11 (11-4): weight=1 +Edge 12 (12-4): weight=1 +Edge 13 (13-4): weight=1 +Edge 14 (14-4): weight=1 +Edge 15 (14-5): weight=1 +Edge 16 (10-6): weight=1 +Edge 17 (14-6): weight=1 +Edge 18 (10-7): weight=1 +Edge 19 (11-7): weight=1 +Edge 20 (13-7): weight=1 +Edge 21 (14-7): weight=1 +Edge 22 (10-9): weight=1 +Edge 23 (12-9): weight=1 +Edge 24 (14-9): weight=1 + +--- +Vertex 0: type=0 id="A" name="A" +Vertex 1: type=0 id="B" name="B" +Vertex 2: type=0 id="C" name="C" +Vertex 3: type=0 id="D" name="D" +Vertex 4: type=0 id="E" name="E" +Vertex 5: type=0 id="F" name="F" +Vertex 6: type=0 id="G" name="G" +Vertex 7: type=0 id="H" name="H" +Vertex 8: type=0 id="I" name="I" +Vertex 9: type=0 id="J" name="J" +Vertex 10: type=1 id="1" name="1" +Vertex 11: type=1 id="2" name="2" +Vertex 12: type=1 id="3" name="3" +Vertex 13: type=1 id="4" name="4" +Vertex 14: type=1 id="5" name="5" +Edge 0 (10-0): weight=1 +Edge 1 (10-1): weight=1 +Edge 2 (11-1): weight=1 +Edge 3 (10-2): weight=1 +Edge 4 (11-2): weight=1 +Edge 5 (12-2): weight=1 +Edge 6 (10-3): weight=1 +Edge 7 (11-3): weight=1 +Edge 8 (12-3): weight=1 +Edge 9 (13-3): weight=1 +Edge 10 (10-4): weight=1 +Edge 11 (11-4): weight=1 +Edge 12 (12-4): weight=1 +Edge 13 (13-4): weight=1 +Edge 14 (14-4): weight=1 +Edge 15 (14-5): weight=1 +Edge 16 (10-6): weight=1 +Edge 17 (14-6): weight=1 +Edge 18 (10-7): weight=1 +Edge 19 (11-7): weight=1 +Edge 20 (13-7): weight=1 +Edge 21 (14-7): weight=1 +Edge 22 (10-9): weight=1 +Edge 23 (12-9): weight=1 +Edge 24 (14-9): weight=1 + diff --git a/tests/unit/pajek_edgeslist.net b/tests/unit/pajek_edgeslist.net new file mode 100644 index 0000000..4ccf5c1 --- /dev/null +++ b/tests/unit/pajek_edgeslist.net @@ -0,0 +1,8 @@ +*Network "C_3 cycle graph" +*Vertices 3 +1 "Alice" +2 "Bob" +3 "Cedric" +*Edgeslist +1 2 3 +2 3 diff --git a/tests/unit/pajek_signed.c b/tests/unit/pajek_signed.c new file mode 100644 index 0000000..37a6989 --- /dev/null +++ b/tests/unit/pajek_signed.c @@ -0,0 +1,48 @@ +/* -*- 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + FILE *ifile; + + /* turn on attribute handling */ + igraph_set_attribute_table(&igraph_cattribute_table); + + ifile = fopen("pajek_signed.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&graph, ifile); + fclose(ifile); + + print_attributes(&graph); + + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/pajek_signed.net b/tests/unit/pajek_signed.net new file mode 100644 index 0000000..9279c7b --- /dev/null +++ b/tests/unit/pajek_signed.net @@ -0,0 +1,23 @@ +*NETWORK First.net; 14.04.2009 / 09:46:56 +*Vertices 10 +1 "S65" +2 "S29" +3 "S04" +4 "S75" +5 "S24" +6 "S81" +7 "S51" +8 "S78" +9 "S86" +10 "S39" +*Matrix + 0 0 0 0 0 1 0 0 0 -1 + 0 0 1 1 0 1 1 0 1 0 + -1 0 0 1 0 0 1 0 1 0 + -1 1 0 0 1 1 1 0 1 0 + 0 1 0 1 0 0 0 0 -1 -1 + 1 -1 0 0 0 0 0 1 0 0 + 0 -1 1 1 0 -1 0 0 1 0 + 0 0 1 1 0 1 -1 0 1 1 + 0 0 0 0 0 0 0 0 0 -1 + 1 1 1 1 1 1 1 1 1 0 diff --git a/tests/unit/pajek_signed.out b/tests/unit/pajek_signed.out new file mode 100644 index 0000000..5140cea --- /dev/null +++ b/tests/unit/pajek_signed.out @@ -0,0 +1,56 @@ +Vertex 0: id="S65" name="S65" +Vertex 1: id="S29" name="S29" +Vertex 2: id="S04" name="S04" +Vertex 3: id="S75" name="S75" +Vertex 4: id="S24" name="S24" +Vertex 5: id="S81" name="S81" +Vertex 6: id="S51" name="S51" +Vertex 7: id="S78" name="S78" +Vertex 8: id="S86" name="S86" +Vertex 9: id="S39" name="S39" +Edge 0 (0-5): weight=1 +Edge 1 (0-9): weight=-1 +Edge 2 (1-2): weight=1 +Edge 3 (1-3): weight=1 +Edge 4 (1-5): weight=1 +Edge 5 (1-6): weight=1 +Edge 6 (1-8): weight=1 +Edge 7 (2-0): weight=-1 +Edge 8 (2-3): weight=1 +Edge 9 (2-6): weight=1 +Edge 10 (2-8): weight=1 +Edge 11 (3-0): weight=-1 +Edge 12 (3-1): weight=1 +Edge 13 (3-4): weight=1 +Edge 14 (3-5): weight=1 +Edge 15 (3-6): weight=1 +Edge 16 (3-8): weight=1 +Edge 17 (4-1): weight=1 +Edge 18 (4-3): weight=1 +Edge 19 (4-8): weight=-1 +Edge 20 (4-9): weight=-1 +Edge 21 (5-0): weight=1 +Edge 22 (5-1): weight=-1 +Edge 23 (5-7): weight=1 +Edge 24 (6-1): weight=-1 +Edge 25 (6-2): weight=1 +Edge 26 (6-3): weight=1 +Edge 27 (6-5): weight=-1 +Edge 28 (6-8): weight=1 +Edge 29 (7-2): weight=1 +Edge 30 (7-3): weight=1 +Edge 31 (7-5): weight=1 +Edge 32 (7-6): weight=-1 +Edge 33 (7-8): weight=1 +Edge 34 (7-9): weight=1 +Edge 35 (8-9): weight=-1 +Edge 36 (9-0): weight=1 +Edge 37 (9-1): weight=1 +Edge 38 (9-2): weight=1 +Edge 39 (9-3): weight=1 +Edge 40 (9-4): weight=1 +Edge 41 (9-5): weight=1 +Edge 42 (9-6): weight=1 +Edge 43 (9-7): weight=1 +Edge 44 (9-8): weight=1 + diff --git a/tests/unit/prop_caching.c b/tests/unit/prop_caching.c new file mode 100644 index 0000000..44b356d --- /dev/null +++ b/tests/unit/prop_caching.c @@ -0,0 +1,159 @@ +/* + 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, see . +*/ + +#include + +#include "test_utilities.h" + +/* + * This test tries to ensure that property caching works correctly in the most + * "dangerous" scenarios (going from disconnected to connected graphs and + * back). Feel free to extend the file later on with regression tests for any + * issues that we might find related to caching. + * + * There are no direct APIs to reach into the cache internals (because the + * presence of the cache is an implementation detail). Therefore, we simply do + * the following: + * + * - perform some operation + * - query the value of certain properties that we know that are typically + * cached (e.g., connectedness, presence of multi- and loop edges) etc + * - invalidate the cache + * - perform the query again, check whether the results match + * + * The assumption is that functions like igraph_is_simple(), igraph_has_loops() + * etc work correctly if the cache is empty; correctness of these functions + * without a cache should be tested in other unit tests, not here. + */ + +igraph_error_t has_mutual_nonloop_edge(const igraph_t* graph, igraph_bool_t* result) { + return igraph_has_mutual(graph, result, /* loops = */ false); +} + +igraph_error_t has_mutual_edge(const igraph_t* graph, igraph_bool_t* result) { + return igraph_has_mutual(graph, result, /* loops = */ true); +} + +igraph_error_t is_weakly_connected(const igraph_t* graph, igraph_bool_t* result) { + return igraph_is_connected(graph, result, IGRAPH_WEAK); +} + +igraph_error_t is_strongly_connected(const igraph_t* graph, igraph_bool_t* result) { + return igraph_is_connected(graph, result, IGRAPH_STRONG); +} + +igraph_error_t is_forest(const igraph_t* graph, igraph_bool_t* result) { + return igraph_is_forest(graph, result, /* roots = */ NULL, IGRAPH_ALL); +} + +void validate_properties(const igraph_t* graph) { + igraph_bool_t result, cached_result, recalculated_result; + +#define CHECK(func) { \ + igraph_invalidate_cache(graph); \ + func(graph, &result); \ + func(graph, &cached_result); \ + igraph_invalidate_cache(graph); \ + func(graph, &recalculated_result); \ + IGRAPH_ASSERT(result == cached_result); \ + IGRAPH_ASSERT(recalculated_result == cached_result); \ +} + + CHECK(igraph_is_simple); + CHECK(igraph_has_loop); + CHECK(igraph_has_multiple); + CHECK(igraph_is_dag); + CHECK(is_forest); + CHECK(is_weakly_connected); + CHECK(is_strongly_connected); + CHECK(has_mutual_edge); + CHECK(has_mutual_nonloop_edge); +} + +void test_basic_operations(igraph_t* graph) { + validate_properties(graph); + igraph_add_vertices(graph, 1, /* attr = */ NULL); + validate_properties(graph); + igraph_add_vertices(graph, 2, /* attr = */ NULL); + validate_properties(graph); + igraph_add_edge(graph, 0, 1); + validate_properties(graph); + igraph_add_edge(graph, 0, 2); + validate_properties(graph); + igraph_add_edge(graph, 1, 2); + validate_properties(graph); + igraph_add_edge(graph, 2, 0); + validate_properties(graph); + igraph_delete_edges(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID)); + validate_properties(graph); + igraph_add_edge(graph, 0, 2); + validate_properties(graph); + igraph_delete_vertices(graph, igraph_vss_1(1)); + validate_properties(graph); + igraph_delete_vertices(graph, igraph_vss_all()); + validate_properties(graph); +} + +int test_basic_operations_directed(void) { + igraph_t g; + + igraph_empty(&g, 0, IGRAPH_DIRECTED); + test_basic_operations(&g); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} + +int test_basic_operations_undirected(void) { + igraph_t g; + + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + test_basic_operations(&g); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} + +int test_multi_loops_adjlist_init(void) { + igraph_t g; + igraph_adjlist_t al; + igraph_bool_t multi; + + igraph_small(&g, 1, IGRAPH_UNDIRECTED, + 0,0, 0,0, -1); + igraph_adjlist_init(&g, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(multi); + igraph_adjlist_destroy(&al); + igraph_destroy(&g); + + return 0; +} + +int main(void) { + + RUN_TEST(test_basic_operations_directed); + RUN_TEST(test_basic_operations_undirected); + RUN_TEST(test_multi_loops_adjlist_init); + + return 0; +} diff --git a/tests/unit/random_sampling.c b/tests/unit/random_sampling.c new file mode 100644 index 0000000..c217c68 --- /dev/null +++ b/tests/unit/random_sampling.c @@ -0,0 +1,292 @@ +/* + 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, see . +*/ + +#include + +#include "test_utilities.h" + +/* Basic check: Are means within tol*sigma from the expected value? + * This is meant to catch gross bugs while changing RNG/sampler code. */ +void stats(void) { + igraph_integer_t k; + const igraph_integer_t n = 100000; + igraph_real_t m, tm, tsd; + igraph_real_t tol = 3; + + printf("\n"); + + /* Binary trials with success of probability p. Counting successes. */ + { + igraph_real_t p = 0.1; + m = 0; + for (k = 0; k < n; k++) { + if (RNG_UNIF01() < p) { + m += 1; + } + } + tm = n*p; + tsd = sqrt(n*p*(1-p)); + printf("binary trials: %g; expected: %g; std. dev.: %g\n", m, tm, tsd); + IGRAPH_ASSERT(tm - tol*tsd < m && m < tm + tol*tsd); + } + + /* Mean of Poisson distributed values. */ + { + tm = 2; + m = 0; + for (k = 0; k < n; k++) { + m += RNG_POIS(tm); + } + m /= n; + tsd = sqrt(tm) / sqrt(n); + printf("pois: %g; expected: %g; std. dev.: %g\n", m, tm, tsd); + IGRAPH_ASSERT(tm - tol*tsd < m && m < tm + tol*tsd); + } + + /* Mean of geometrically distributed values. */ + { + igraph_real_t p = 0.25; + m = 0; + for (k = 0; k < n; k++) { + m += RNG_GEOM(p); + } + m /= n; + tm = (1-p) / p; + tsd = sqrt(1 - p) / p / sqrt(n); + printf("geom: %g; expected: %g; std. dev.: %g\n", m, tm, tsd); + IGRAPH_ASSERT(tm - tol*tsd < m && m < tm + tol*tsd); + } + + /* Mean of binomially distributed values. */ + { + igraph_real_t p = 0.33; + igraph_integer_t nn = 77; + m = 0; + for (k = 0; k < n; k++) { + m += RNG_BINOM(nn, p); + } + m /= n; + tm = nn * p; + tsd = sqrt(nn*p*(1-p)) / sqrt(n); + printf("binom: %g; expected: %g; std. dev.: %g\n", m, tm, tsd); + IGRAPH_ASSERT(tm - tol*tsd < m && m < tm + tol*tsd); + } + + /* Mean of exponentially distributed values. */ + { + tm = 3; + m = 0; + for (k = 0; k < n; k++) { + m += RNG_EXP(1 / tm); + } + m /= n; + tsd = tm / sqrt(n); + printf("exp: %g; expected: %g; std. dev.: %g\n", m, tm, tsd); + IGRAPH_ASSERT(tm - tol*tsd < m && m < tm + tol*tsd); + } + + /* Mean of gamma distributed values. */ + { + igraph_real_t shape = 1.5, scale = 3.5; + m = 0; + for (k = 0; k < n; k++) { + m += RNG_GAMMA(shape, scale); + } + m /= n; + tm = shape*scale; + tsd = sqrt(shape) * scale / sqrt(n); + printf("gamma: %g; expected: %g; std. dev.: %g\n", m, tm, tsd); + IGRAPH_ASSERT(tm - tol*tsd < m && m < tm + tol*tsd); + } + + /* Mean of normally distributed values. */ + { + tm = 3.0; tsd = 2.0; + m = 0; + for (k = 0; k < n; k++) { + m += RNG_NORMAL(tm, tsd); + } + m /= n; + tsd /= sqrt(n); + printf("norm: %g; expected: %g; std. dev.: %g\n", m, tm, tsd); + IGRAPH_ASSERT(tm - tol*tsd < m && m < tm + tol*tsd); + } +} + +/* These is merely a smoke test for various random samplers. + * It does not verify the correctness of the result, except + * for some special edge cases. */ + +void sample(void) { + igraph_integer_t i; + igraph_real_t x; + + i = RNG_INTEGER(-100, 100); + printf("integer: %" IGRAPH_PRId "\n", i); + IGRAPH_ASSERT(-100 <= i && i <= 100); + + i = RNG_INTEGER(2, 2); + printf("integer: %" IGRAPH_PRId "\n", i); + IGRAPH_ASSERT(i == 2); + + i = RNG_INTEGER(-5, -5); + printf("integer: %" IGRAPH_PRId "\n", i); + IGRAPH_ASSERT(i == -5); + + x = RNG_UNIF(-100, 100); + printf("unif: %g\n", x); + IGRAPH_ASSERT(-100 <= x && x < 100); + + x = RNG_UNIF(3, 3); + printf("unif: %g\n", x); + IGRAPH_ASSERT(x == 3); + + x = RNG_UNIF(-4.5, -4.5); + printf("unif: %g\n", x); + IGRAPH_ASSERT(x == -4.5); + + x = RNG_UNIF01(); + printf("unif01: %g\n", x); + IGRAPH_ASSERT(0 <= x); + IGRAPH_ASSERT(x < 1); + + x = RNG_BINOM(10, 0.3); + printf("binom: %g\n", x); + IGRAPH_ASSERT(0 <= x && x <= 10); + + x = RNG_BINOM(5, 0); + IGRAPH_ASSERT(x == 0); + + x = RNG_BINOM(5, 1); + IGRAPH_ASSERT(x == 5); + + x = RNG_BINOM((1LL << 31) - 1, 0.5); + IGRAPH_ASSERT(!isnan(x)); + IGRAPH_ASSERT(0 <= x && x <= (1LL << 31) - 1); + +#if IGRAPH_INTEGER_SIZE > 32 + x = RNG_BINOM((1LL << 31), 0.5); + IGRAPH_ASSERT(!isnan(x)); + IGRAPH_ASSERT(0 <= x && x <= (1LL << 31) - 1); +#endif + + x = RNG_GEOM(0.2); + printf("geom: %g\n", x); + IGRAPH_ASSERT(0 <= x); + + x = RNG_GEOM(1); + IGRAPH_ASSERT(x == 0); + + x = RNG_GEOM(0); + IGRAPH_ASSERT(isnan(x)); + + x = RNG_NORMAL(0, 1); + printf("normal: %g\n", x); + IGRAPH_ASSERT(isfinite(x)); + + x = RNG_EXP(1); + printf("exp: %g\n", x); + IGRAPH_ASSERT(0 <= x); + + x = RNG_EXP(0); + IGRAPH_ASSERT(isnan(x)); + + x = RNG_GAMMA(1, 1); + printf("gamma: %g\n", x); + IGRAPH_ASSERT(0 <= x); + + /* Note: Some systems would reject 0 as a parameter value instead of returning 0 */ + x = RNG_GAMMA(0, 1); + IGRAPH_ASSERT(x == 0); + + x = RNG_GAMMA(1, 0); + IGRAPH_ASSERT(x == 0); + + x = RNG_POIS(10); + printf("poisson: %g\n", x); + IGRAPH_ASSERT(0 <= x && isfinite(x)); + + x = RNG_POIS(0); + IGRAPH_ASSERT(x == 0); + + x = RNG_POIS(-1); + IGRAPH_ASSERT(isnan(x)); + + x = RNG_POIS((1LL << 31) - 1); + IGRAPH_ASSERT(0 <= x && isfinite(x)); + + x = RNG_POIS((1LL << 31)); + IGRAPH_ASSERT(0 <= x && isfinite(x)); + +#if IGRAPH_INTEGER_SIZE > 32 + x = RNG_POIS((1LL << 32)); + IGRAPH_ASSERT(0 <= x && isfinite(x)); +#endif + +} + +void test_and_destroy(igraph_rng_type_t *rng_type) { + igraph_rng_t *def = igraph_rng_default(); + igraph_rng_t rng; + + igraph_error_handler_t *oldhandler = igraph_set_error_handler(&igraph_error_handler_printignore); + igraph_error_t err = igraph_rng_init(&rng, rng_type); + switch (err) { + case IGRAPH_SUCCESS: + break; + case IGRAPH_UNIMPLEMENTED: + return; + default: + IGRAPH_FATAL("Error while initializing RNG."); + } + igraph_set_error_handler(oldhandler); + + printf("\n%s\n\n", igraph_rng_name(&rng)); + + igraph_rng_set_default(&rng); + igraph_rng_seed(igraph_rng_default(), 137); + + sample(); + stats(); + + igraph_rng_set_default(def); + igraph_rng_destroy(&rng); +} + +int main(void) { + igraph_rng_type_t rng_types[] = { + igraph_rngtype_glibc2, + igraph_rngtype_mt19937, + igraph_rngtype_pcg32, + igraph_rngtype_pcg64 + }; + + printf("Default\n\n"); + igraph_rng_seed(igraph_rng_default(), 709); + + sample(); + stats(); + + for (size_t i = 0; i < sizeof(rng_types) / sizeof(rng_types[0]); i++) { + test_and_destroy(&rng_types[i]); + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/random_spanning_tree.c b/tests/unit/random_spanning_tree.c new file mode 100644 index 0000000..cff36b6 --- /dev/null +++ b/tests/unit/random_spanning_tree.c @@ -0,0 +1,75 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph, spanning_tree; + igraph_vector_int_t tree_edges; + igraph_bool_t is_tree; + int err; + + igraph_rng_seed(igraph_rng_default(), 987); + + igraph_vector_int_init(&tree_edges, 0); + + /* This is guaranteed to create a connected graph. */ + igraph_barabasi_game(&graph, 100, 2, 2, NULL, 0, 1, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE, NULL); + + err = igraph_random_spanning_tree(&graph, &tree_edges, 0); + IGRAPH_ASSERT(!err); + + IGRAPH_ASSERT(igraph_vector_int_size(&tree_edges) == igraph_vcount(&graph) - 1); + + err = igraph_subgraph_from_edges(&graph, &spanning_tree, igraph_ess_vector(&tree_edges), /* delete_vertices= */ 0); + IGRAPH_ASSERT(!err); + + IGRAPH_ASSERT(igraph_vcount(&spanning_tree) == igraph_vcount(&graph)); + + igraph_is_tree(&spanning_tree, &is_tree, NULL, IGRAPH_ALL); + IGRAPH_ASSERT(is_tree); + + igraph_destroy(&spanning_tree); + igraph_destroy(&graph); + + /* Non-connected forest graph. There is only one solution. */ + igraph_small(&graph, 4, IGRAPH_UNDIRECTED, 0,1, 2,3, -1); + + /* Find a spanning tree of the component containing vertex 0 */ + err = igraph_random_spanning_tree(&graph, &tree_edges, 0); + IGRAPH_ASSERT(!err); + + IGRAPH_ASSERT(igraph_vector_int_size(&tree_edges) == 1); + IGRAPH_ASSERT(VECTOR(tree_edges)[0] == 0); + + /* Find a spanning forest */ + err = igraph_random_spanning_tree(&graph, &tree_edges, -1); + IGRAPH_ASSERT(!err); + + IGRAPH_ASSERT(igraph_vector_int_size(&tree_edges) == 2); + IGRAPH_ASSERT(VECTOR(tree_edges)[0] == 0 && VECTOR(tree_edges)[1] == 1); + + igraph_destroy(&graph); + + igraph_vector_int_destroy(&tree_edges); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/reachability.c b/tests/unit/reachability.c new file mode 100644 index 0000000..0068f66 --- /dev/null +++ b/tests/unit/reachability.c @@ -0,0 +1,129 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +void compute_and_print(const igraph_t *g, igraph_neimode_t mode) { + igraph_vector_int_t membership, csize, reach_counts; + igraph_bitset_list_t reach; + igraph_integer_t no_of_nodes, no_of_components; + + no_of_nodes = igraph_vcount(g); + + igraph_vector_int_init(&membership, 0); + igraph_vector_int_init(&csize, 0); + igraph_bitset_list_init(&reach, 0); + + igraph_reachability(g, &membership, &csize, &no_of_components, &reach, mode); + + igraph_vector_int_init(&reach_counts, no_of_nodes); + + igraph_count_reachable(g, &reach_counts, mode); + + printf("Mode: "); + switch (mode) { + case IGRAPH_OUT: + printf("OUT\n"); break; + case IGRAPH_IN: + printf("IN\n"); break; + case IGRAPH_ALL: + printf("ALL\n"); break; + } + print_vector_int(&membership); + print_vector_int(&csize); + printf("No. of components: %" IGRAPH_PRId "\n", no_of_components); + print_bitset_list(&reach); + print_vector_int(&reach_counts); + + igraph_bitset_list_destroy(&reach); + igraph_vector_int_destroy(&csize); + igraph_vector_int_destroy(&membership); + igraph_vector_int_destroy(&reach_counts); +} + +int main(void) { + igraph_t g; + + /* Component calculations are run twice to exercise the cache */ + + printf("\nNull graph (not connected)\n"); + igraph_empty(&g, 0, IGRAPH_DIRECTED); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nSingleton graph (connected)\n"); + igraph_empty(&g, 1, IGRAPH_DIRECTED); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nKautz graph (connected)\n"); + igraph_kautz(&g, 2, 2); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nDirected 2-path\n"); + igraph_small(&g, 2, IGRAPH_DIRECTED, 0, 1, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nTwo disjoint 3-cycles\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 3, 4, 4, 5, 5, 3, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_IN); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nPath graph with 6 vertices ascending\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_IN); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nPath graph with 6 vertices descending\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_IN); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nSmall directed graph\n"); + igraph_small(&g, 13, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 1, 3, 3, 4, 4, 5, 5, 4, 7, 4, 7, 8, 9, 8, 10, 9, 8, 10, 11, 6, 12, 6, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_IN); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nSmall undirected graph\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 0,2, + 3,4, + -1); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/reachability.out b/tests/unit/reachability.out new file mode 100644 index 0000000..dd27b85 --- /dev/null +++ b/tests/unit/reachability.out @@ -0,0 +1,225 @@ + +Null graph (not connected) +Mode: OUT +( ) +( ) +No. of components: 0 +{ +} +( ) +Mode: ALL +( ) +( ) +No. of components: 0 +{ +} +( ) + +Singleton graph (connected) +Mode: OUT +( 0 ) +( 1 ) +No. of components: 1 +{ + 0: ( 1 ) +} +( 1 ) +Mode: ALL +( 0 ) +( 1 ) +No. of components: 1 +{ + 0: ( 1 ) +} +( 1 ) + +Kautz graph (connected) +Mode: OUT +( 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 12 ) +No. of components: 1 +{ + 0: ( 1 1 1 1 1 1 1 1 1 1 1 1 ) +} +( 12 12 12 12 12 12 12 12 12 12 12 12 ) +Mode: ALL +( 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 12 ) +No. of components: 1 +{ + 0: ( 1 1 1 1 1 1 1 1 1 1 1 1 ) +} +( 12 12 12 12 12 12 12 12 12 12 12 12 ) + +Directed 2-path +Mode: OUT +( 0 1 ) +( 1 1 ) +No. of components: 2 +{ + 0: ( 1 1 ) + 1: ( 1 0 ) +} +( 2 1 ) +Mode: ALL +( 0 0 ) +( 2 ) +No. of components: 1 +{ + 0: ( 1 1 ) +} +( 2 2 ) + +Two disjoint 3-cycles +Mode: OUT +( 1 1 1 0 0 0 ) +( 3 3 ) +No. of components: 2 +{ + 0: ( 1 1 1 0 0 0 ) + 1: ( 0 0 0 1 1 1 ) +} +( 3 3 3 3 3 3 ) +Mode: IN +( 1 1 1 0 0 0 ) +( 3 3 ) +No. of components: 2 +{ + 0: ( 1 1 1 0 0 0 ) + 1: ( 0 0 0 1 1 1 ) +} +( 3 3 3 3 3 3 ) +Mode: ALL +( 0 0 0 1 1 1 ) +( 3 3 ) +No. of components: 2 +{ + 0: ( 0 0 0 1 1 1 ) + 1: ( 1 1 1 0 0 0 ) +} +( 3 3 3 3 3 3 ) + +Path graph with 6 vertices ascending +Mode: OUT +( 0 1 2 3 4 5 ) +( 1 1 1 1 1 1 ) +No. of components: 6 +{ + 0: ( 1 1 1 1 1 1 ) + 1: ( 1 1 1 1 1 0 ) + 2: ( 1 1 1 1 0 0 ) + 3: ( 1 1 1 0 0 0 ) + 4: ( 1 1 0 0 0 0 ) + 5: ( 1 0 0 0 0 0 ) +} +( 6 5 4 3 2 1 ) +Mode: IN +( 0 1 2 3 4 5 ) +( 1 1 1 1 1 1 ) +No. of components: 6 +{ + 0: ( 0 0 0 0 0 1 ) + 1: ( 0 0 0 0 1 1 ) + 2: ( 0 0 0 1 1 1 ) + 3: ( 0 0 1 1 1 1 ) + 4: ( 0 1 1 1 1 1 ) + 5: ( 1 1 1 1 1 1 ) +} +( 1 2 3 4 5 6 ) +Mode: ALL +( 0 0 0 0 0 0 ) +( 6 ) +No. of components: 1 +{ + 0: ( 1 1 1 1 1 1 ) +} +( 6 6 6 6 6 6 ) + +Path graph with 6 vertices descending +Mode: OUT +( 5 4 3 2 1 0 ) +( 1 1 1 1 1 1 ) +No. of components: 6 +{ + 0: ( 1 1 1 1 1 1 ) + 1: ( 0 1 1 1 1 1 ) + 2: ( 0 0 1 1 1 1 ) + 3: ( 0 0 0 1 1 1 ) + 4: ( 0 0 0 0 1 1 ) + 5: ( 0 0 0 0 0 1 ) +} +( 1 2 3 4 5 6 ) +Mode: IN +( 5 4 3 2 1 0 ) +( 1 1 1 1 1 1 ) +No. of components: 6 +{ + 0: ( 1 0 0 0 0 0 ) + 1: ( 1 1 0 0 0 0 ) + 2: ( 1 1 1 0 0 0 ) + 3: ( 1 1 1 1 0 0 ) + 4: ( 1 1 1 1 1 0 ) + 5: ( 1 1 1 1 1 1 ) +} +( 6 5 4 3 2 1 ) +Mode: ALL +( 0 0 0 0 0 0 ) +( 6 ) +No. of components: 1 +{ + 0: ( 1 1 1 1 1 1 ) +} +( 6 6 6 6 6 6 ) + +Small directed graph +Mode: OUT +( 5 5 5 6 7 7 4 2 3 3 3 1 0 ) +( 1 1 1 3 1 3 1 2 ) +No. of components: 8 +{ + 0: ( 1 0 0 0 0 0 1 0 0 0 0 0 0 ) + 1: ( 0 1 0 0 0 0 1 0 0 0 0 0 0 ) + 2: ( 0 0 1 1 1 1 0 1 1 0 0 0 0 ) + 3: ( 0 0 1 1 1 0 0 0 0 0 0 0 0 ) + 4: ( 0 0 0 0 0 0 1 0 0 0 0 0 0 ) + 5: ( 0 0 0 0 0 0 0 1 1 1 1 1 1 ) + 6: ( 0 0 0 0 0 0 0 1 1 1 0 0 0 ) + 7: ( 0 0 0 0 0 0 0 1 1 0 0 0 0 ) +} +( 6 6 6 3 2 2 1 6 3 3 3 2 2 ) +Mode: IN +( 5 5 5 6 7 7 4 2 3 3 3 1 0 ) +( 1 1 1 3 1 3 1 2 ) +No. of components: 8 +{ + 0: ( 1 0 0 0 0 0 0 0 0 0 0 0 0 ) + 1: ( 0 1 0 0 0 0 0 0 0 0 0 0 0 ) + 2: ( 0 0 0 0 0 1 0 0 0 0 0 0 0 ) + 3: ( 0 0 1 1 1 1 0 0 0 0 0 0 0 ) + 4: ( 1 1 0 0 0 0 1 0 0 0 0 0 0 ) + 5: ( 0 0 0 0 0 0 0 0 0 0 1 1 1 ) + 6: ( 0 0 0 0 0 0 0 0 0 1 1 1 1 ) + 7: ( 0 0 0 0 0 1 0 1 1 1 1 1 1 ) +} +( 3 3 3 4 7 7 3 1 4 4 4 1 1 ) +Mode: ALL +( 0 0 0 0 0 0 1 0 0 0 0 1 1 ) +( 10 3 ) +No. of components: 2 +{ + 0: ( 0 0 1 1 1 1 0 1 1 1 1 1 1 ) + 1: ( 1 1 0 0 0 0 1 0 0 0 0 0 0 ) +} +( 10 10 10 10 10 10 3 10 10 10 10 3 3 ) + +Small undirected graph +Mode: ALL +( 0 0 0 1 1 2 ) +( 3 2 1 ) +No. of components: 3 +{ + 0: ( 0 0 0 1 1 1 ) + 1: ( 0 1 1 0 0 0 ) + 2: ( 1 0 0 0 0 0 ) +} +( 3 3 3 2 2 1 ) diff --git a/tests/unit/ring.c b/tests/unit/ring.c new file mode 100644 index 0000000..1681b0d --- /dev/null +++ b/tests/unit/ring.c @@ -0,0 +1,52 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2009-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 +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + int directed, circular, mutual; + int n; + + for (n=0; n < 4; ++n) { + for (directed=0; directed < 2; ++directed) { + for (circular=0; circular < 2; ++circular) { + for (mutual=0; mutual < 2; ++mutual) { + igraph_t graph; + igraph_ring(&graph, n, directed, mutual, circular); + printf("n=%d, %s, %s, %s\n", n, + directed ? "directed" : "undirected", + circular ? "cycle" : "path", + mutual ? "bidirectional" : "unidirectional" + ); + print_graph_canon(&graph); + printf("\n"); + igraph_destroy(&graph); + } + } + } + } + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/ring.out b/tests/unit/ring.out new file mode 100644 index 0000000..9f7848e --- /dev/null +++ b/tests/unit/ring.out @@ -0,0 +1,237 @@ +n=0, undirected, path, unidirectional +directed: false +vcount: 0 +edges: { +} + +n=0, undirected, path, bidirectional +directed: false +vcount: 0 +edges: { +} + +n=0, undirected, cycle, unidirectional +directed: false +vcount: 0 +edges: { +} + +n=0, undirected, cycle, bidirectional +directed: false +vcount: 0 +edges: { +} + +n=0, directed, path, unidirectional +directed: true +vcount: 0 +edges: { +} + +n=0, directed, path, bidirectional +directed: true +vcount: 0 +edges: { +} + +n=0, directed, cycle, unidirectional +directed: true +vcount: 0 +edges: { +} + +n=0, directed, cycle, bidirectional +directed: true +vcount: 0 +edges: { +} + +n=1, undirected, path, unidirectional +directed: false +vcount: 1 +edges: { +} + +n=1, undirected, path, bidirectional +directed: false +vcount: 1 +edges: { +} + +n=1, undirected, cycle, unidirectional +directed: false +vcount: 1 +edges: { +0 0 +} + +n=1, undirected, cycle, bidirectional +directed: false +vcount: 1 +edges: { +0 0 +} + +n=1, directed, path, unidirectional +directed: true +vcount: 1 +edges: { +} + +n=1, directed, path, bidirectional +directed: true +vcount: 1 +edges: { +} + +n=1, directed, cycle, unidirectional +directed: true +vcount: 1 +edges: { +0 0 +} + +n=1, directed, cycle, bidirectional +directed: true +vcount: 1 +edges: { +0 0 +0 0 +} + +n=2, undirected, path, unidirectional +directed: false +vcount: 2 +edges: { +0 1 +} + +n=2, undirected, path, bidirectional +directed: false +vcount: 2 +edges: { +0 1 +} + +n=2, undirected, cycle, unidirectional +directed: false +vcount: 2 +edges: { +0 1 +0 1 +} + +n=2, undirected, cycle, bidirectional +directed: false +vcount: 2 +edges: { +0 1 +0 1 +} + +n=2, directed, path, unidirectional +directed: true +vcount: 2 +edges: { +0 1 +} + +n=2, directed, path, bidirectional +directed: true +vcount: 2 +edges: { +0 1 +1 0 +} + +n=2, directed, cycle, unidirectional +directed: true +vcount: 2 +edges: { +0 1 +1 0 +} + +n=2, directed, cycle, bidirectional +directed: true +vcount: 2 +edges: { +0 1 +0 1 +1 0 +1 0 +} + +n=3, undirected, path, unidirectional +directed: false +vcount: 3 +edges: { +0 1 +1 2 +} + +n=3, undirected, path, bidirectional +directed: false +vcount: 3 +edges: { +0 1 +1 2 +} + +n=3, undirected, cycle, unidirectional +directed: false +vcount: 3 +edges: { +0 1 +0 2 +1 2 +} + +n=3, undirected, cycle, bidirectional +directed: false +vcount: 3 +edges: { +0 1 +0 2 +1 2 +} + +n=3, directed, path, unidirectional +directed: true +vcount: 3 +edges: { +0 1 +1 2 +} + +n=3, directed, path, bidirectional +directed: true +vcount: 3 +edges: { +0 1 +1 0 +1 2 +2 1 +} + +n=3, directed, cycle, unidirectional +directed: true +vcount: 3 +edges: { +0 1 +1 2 +2 0 +} + +n=3, directed, cycle, bidirectional +directed: true +vcount: 3 +edges: { +0 1 +0 2 +1 0 +1 2 +2 0 +2 1 +} + diff --git a/tests/unit/rng_init_destroy_max_bits_name_set_default.c b/tests/unit/rng_init_destroy_max_bits_name_set_default.c new file mode 100644 index 0000000..ec57c03 --- /dev/null +++ b/tests/unit/rng_init_destroy_max_bits_name_set_default.c @@ -0,0 +1,133 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void test_and_destroy(igraph_rng_type_t *rng_type, igraph_rng_t *rng_def) { + int i; + igraph_rng_t rng; + + igraph_error_handler_t *oldhandler = igraph_set_error_handler(&igraph_error_handler_printignore); + igraph_error_t err = igraph_rng_init(&rng, rng_type); + switch (err) { + case IGRAPH_SUCCESS: + break; + case IGRAPH_UNIMPLEMENTED: + return; + default: + IGRAPH_FATAL("Error while initializing RNG."); + } + igraph_set_error_handler(oldhandler); + + printf("rng name: %s\n", igraph_rng_name(&rng)); + + igraph_rng_seed(&rng, 42); + for (i = 0; i < 5; i++) { + printf("%" IGRAPH_PRId "\n", igraph_rng_get_integer(&rng, 0, 100)); + } + printf("\n"); + + igraph_rng_set_default(&rng); + igraph_rng_seed(igraph_rng_default(), 42); + for (i = 0; i < 5; i++) { + printf("%" IGRAPH_PRId "\n", igraph_rng_get_integer(igraph_rng_default(), 0, 100)); + } + printf("\n"); + + IGRAPH_ASSERT(igraph_rng_max(&rng) >= 0x7fffffff); + IGRAPH_ASSERT(igraph_rng_bits(&rng) >= 31); + + igraph_rng_set_default(rng_def); + igraph_rng_destroy(&rng); +} + +void test_and_destroy_with_expected_values( + igraph_rng_type_t *rng_type, const igraph_vector_int_t *expected +) { + int i; + igraph_error_t retval; + igraph_rng_t rng; + + igraph_set_error_handler(igraph_error_handler_ignore); + retval = igraph_rng_init(&rng, rng_type); + igraph_set_error_handler(igraph_error_handler_abort); + + if (retval == IGRAPH_UNIMPLEMENTED) { + /* not supported in the current setup, this is OK */ + return; + } + + igraph_rng_seed(&rng, 42); + for (i = 0; i < 5; i++) { + IGRAPH_ASSERT(VECTOR(*expected)[i] == igraph_rng_get_integer(&rng, 0, 100)); + } + + igraph_rng_seed(&rng, 42); + for (i = 0; i < 5; i++) { + IGRAPH_ASSERT(VECTOR(*expected)[i] == igraph_rng_get_integer(&rng, 0, 100)); + } + + IGRAPH_ASSERT(igraph_rng_max(&rng) >= 0x7fffffff); + IGRAPH_ASSERT(igraph_rng_bits(&rng) >= 31); + + igraph_rng_destroy(&rng); +} + +void test_mandatory_rngtypes(void) { + int i; + igraph_rng_type_t rng_types[] = { + igraph_rngtype_glibc2, + igraph_rngtype_mt19937, + igraph_rngtype_pcg32, + }; + const int NUM_RNG_TYPES = sizeof(rng_types) / sizeof(rng_types[0]); + igraph_rng_t rng_def; + + IGRAPH_ASSERT(igraph_rng_init(&rng_def, &igraph_rngtype_glibc2) == IGRAPH_SUCCESS); + + for (i = 0; i < NUM_RNG_TYPES; i++) { + test_and_destroy(&rng_types[i], &rng_def); + } + + igraph_rng_destroy(&rng_def); + + VERIFY_FINALLY_STACK(); +} + +void test_optional_rngtypes(void) { + igraph_rng_type_t rng_types[] = { + igraph_rngtype_pcg64 + }; + igraph_vector_int_t expected; + igraph_integer_t expected_values[1][5] = { + { 61, 38, 73, 84, 67 } + }; + int i; + + for (i = 0; i < 1; i++) { + igraph_vector_int_view(&expected, expected_values[i], 5); + test_and_destroy_with_expected_values(&rng_types[i], &expected); + } +} + +int main(void) { + test_mandatory_rngtypes(); + test_optional_rngtypes(); + return 0; +} diff --git a/tests/unit/rng_init_destroy_max_bits_name_set_default.out b/tests/unit/rng_init_destroy_max_bits_name_set_default.out new file mode 100644 index 0000000..b09e6df --- /dev/null +++ b/tests/unit/rng_init_destroy_max_bits_name_set_default.out @@ -0,0 +1,39 @@ +rng name: LIBC +3 +69 +20 +64 +30 + +3 +69 +20 +64 +30 + +rng name: MT19937 +37 +80 +96 +18 +73 + +37 +80 +96 +18 +73 + +rng name: PCG32 +13 +54 +84 +99 +99 + +13 +54 +84 +99 +99 + diff --git a/tests/unit/rng_reproducibility.c b/tests/unit/rng_reproducibility.c new file mode 100644 index 0000000..8c62755 --- /dev/null +++ b/tests/unit/rng_reproducibility.c @@ -0,0 +1,38 @@ +/* + IGraph library. + Copyright (C) 2019-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, see . +*/ + +#include + +/* + * This test serves to ensure that the same sequence of random numbers are generated for the + * same seed on all platforms (different operating systems and 32- or 64-bit systems). + */ + +int main(void) { + int i; + igraph_rng_seed(igraph_rng_default(), 137); + + for (i = 0; i < 32; ++i) { + printf("%" IGRAPH_PRId "\n", RNG_INTEGER(0, 100)); + } + + for (i = 0; i < 32; ++i) { + printf("%g\n", RNG_UNIF(0, 1e-6)); + } + return 0; +} diff --git a/tests/unit/rng_reproducibility.out b/tests/unit/rng_reproducibility.out new file mode 100644 index 0000000..6f30475 --- /dev/null +++ b/tests/unit/rng_reproducibility.out @@ -0,0 +1,64 @@ +70 +74 +60 +48 +5 +57 +56 +42 +14 +56 +41 +20 +7 +78 +38 +41 +97 +28 +30 +9 +68 +73 +30 +66 +11 +41 +22 +41 +4 +99 +35 +71 +4.87906e-07 +8.04065e-07 +8.5431e-07 +3.76015e-07 +4.00421e-07 +9.8439e-07 +3.55795e-08 +5.80515e-07 +6.83342e-07 +3.29069e-07 +3.99956e-07 +1.48455e-07 +5.61698e-07 +6.7363e-07 +4.30492e-07 +1.96356e-07 +3.13386e-07 +7.64698e-07 +5.05553e-07 +7.90849e-07 +2.74013e-07 +8.73595e-08 +8.06211e-07 +7.2246e-07 +2.00342e-07 +4.3115e-07 +3.00813e-07 +4.68466e-07 +6.67159e-07 +1.52378e-07 +7.37829e-07 +4.32757e-07 diff --git a/tests/unit/set.c b/tests/unit/set.c new file mode 100644 index 0000000..dd5cef6 --- /dev/null +++ b/tests/unit/set.c @@ -0,0 +1,86 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "core/set.h" + +#include "test_utilities.h" + +void print_set(igraph_set_t *set, FILE *f) { + igraph_integer_t state = 0; + igraph_integer_t element; + while (igraph_set_iterate(set, &state, &element)) { + fprintf(f, " %" IGRAPH_PRId , element); + } + fprintf(f, "\n"); +} + +int main(void) { + + igraph_set_t set; + igraph_integer_t i; + + /* simple init */ + igraph_set_init(&set, 0); + igraph_set_destroy(&set); + + /* addition, igraph_set_size */ + igraph_set_init(&set, 10); + i = 10; + while (igraph_set_size(&set) < 10) { + igraph_set_add(&set, 2 * i); + i--; + } + while (igraph_set_size(&set) < 21) { + igraph_set_add(&set, 2 * i + 1); + i++; + } + print_set(&set, stdout); + + /* adding existing element */ + igraph_set_add(&set, 8); + if (igraph_set_size(&set) != 21) { + return 4; + } + + /* igraph_set_contains */ + if (igraph_set_contains(&set, 42) || !igraph_set_contains(&set, 7)) { + return 3; + } + + /* igraph_set_empty, igraph_set_clear */ + if (igraph_set_empty(&set)) { + return 1; + } + igraph_set_clear(&set); + if (!igraph_set_empty(&set)) { + return 2; + } + igraph_set_destroy(&set); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/set.out b/tests/unit/set.out new file mode 100644 index 0000000..08c0614 --- /dev/null +++ b/tests/unit/set.out @@ -0,0 +1 @@ + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 diff --git a/tests/unit/si2_b06m_s20-bad1.A98 b/tests/unit/si2_b06m_s20-bad1.A98 new file mode 100644 index 0000000000000000000000000000000000000000..5c422aa607cb81028cc43b783aa2f78cbeeed5fb GIT binary patch literal 18 RcmZQ(U}RtdVh~^gV*mh<01N;C literal 0 HcmV?d00001 diff --git a/tests/unit/si2_b06m_s20-bad2.A98 b/tests/unit/si2_b06m_s20-bad2.A98 new file mode 100644 index 0000000000000000000000000000000000000000..1934c4e60ff50f7f4a717721f0320c515dc78d13 GIT binary patch literal 17 RcmZQ!U}RtdVh~^gVE_P?01W^D literal 0 HcmV?d00001 diff --git a/tests/unit/si2_b06m_s20.A98 b/tests/unit/si2_b06m_s20.A98 new file mode 100644 index 0000000000000000000000000000000000000000..8603c7d254530083b084d663e16092e7841ff4f7 GIT binary patch literal 18 RcmZQ!U}RtdVh~^gV*mi601W^D literal 0 HcmV?d00001 diff --git a/tests/unit/simplify_and_colorize.c b/tests/unit/simplify_and_colorize.c new file mode 100644 index 0000000..20ebbe9 --- /dev/null +++ b/tests/unit/simplify_and_colorize.c @@ -0,0 +1,78 @@ +/* + IGraph library. + Copyright (C) 2019-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, see . +*/ + +#include + +#include "test_utilities.h" + +#define SIMPLIFY_PRINT_DESTROY(name) \ + printf(name "\n"); \ + igraph_simplify_and_colorize(&graph, &res, &vcol, &ecol); \ + print_graph(&res); \ + print_vector_int(&vcol); \ + print_vector_int(&ecol); \ + printf("\n"); \ + igraph_destroy(&res); \ + igraph_destroy(&graph); + +int main(void) { + igraph_t graph, res; + igraph_vector_int_t vcol, ecol; + + igraph_vector_int_init(&vcol, 0); + igraph_vector_int_init(&ecol, 0); + + /* null graph */ + igraph_empty(&graph, 0, 0); + SIMPLIFY_PRINT_DESTROY("K0"); + + /* singleton graph */ + igraph_empty(&graph, 1, 0); + SIMPLIFY_PRINT_DESTROY("K1"); + + /* 4-cycle-graph */ + igraph_ring(&graph, 4, 0, 0, 1); + SIMPLIFY_PRINT_DESTROY("C4"); + + /* both multi-edges and self loops */ + igraph_small(&graph, 2, 0, + 0, 1, 0, 1, 1, 1, -1); + SIMPLIFY_PRINT_DESTROY("Undirected graph 1"); + + /* parallel edges specified with different vertex orderings */ + igraph_small(&graph, 3, 0, + 0, 1, 1, 2, 2, 0, 2, 2, 2, 2, 2, 1, -1); + SIMPLIFY_PRINT_DESTROY("Undirected graph 2"); + + /* directed version of the same as above */ + igraph_small(&graph, 3, 1, + 0, 1, 1, 2, 2, 0, 2, 2, 2, 2, 2, 1, -1); + SIMPLIFY_PRINT_DESTROY("Directed graph 1"); + + /* isolated vertices */ + igraph_small(&graph, 4, 1, + 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, -1); + SIMPLIFY_PRINT_DESTROY("Directed graph 2"); + + igraph_vector_int_destroy(&vcol); + igraph_vector_int_destroy(&ecol); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/simplify_and_colorize.out b/tests/unit/simplify_and_colorize.out new file mode 100644 index 0000000..c6ce220 --- /dev/null +++ b/tests/unit/simplify_and_colorize.out @@ -0,0 +1,70 @@ +K0 +directed: false +vcount: 0 +edges: { +} +( ) +( ) + +K1 +directed: false +vcount: 1 +edges: { +} +( 0 ) +( ) + +C4 +directed: false +vcount: 4 +edges: { +1 0 +3 0 +2 1 +3 2 +} +( 0 0 0 0 ) +( 1 1 1 1 ) + +Undirected graph 1 +directed: false +vcount: 2 +edges: { +1 0 +} +( 0 1 ) +( 2 ) + +Undirected graph 2 +directed: false +vcount: 3 +edges: { +1 0 +2 0 +2 1 +} +( 0 0 2 ) +( 1 1 2 ) + +Directed graph 1 +directed: true +vcount: 3 +edges: { +0 1 +1 2 +2 0 +2 1 +} +( 0 0 2 ) +( 1 1 1 1 ) + +Directed graph 2 +directed: true +vcount: 4 +edges: { +0 1 +1 0 +} +( 0 2 0 0 ) +( 2 3 ) + diff --git a/tests/unit/single_target_shortest_path.c b/tests/unit/single_target_shortest_path.c new file mode 100644 index 0000000..f125285 --- /dev/null +++ b/tests/unit/single_target_shortest_path.c @@ -0,0 +1,79 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_t vpath, epath; + igraph_vector_t w; + + /* Unweighted */ + + igraph_small(&g, 5, IGRAPH_DIRECTED, + 0, 1, 1, 2, 2, 3, 3, 4, 0, 3, + -1); + igraph_vector_int_init(&vpath, 0); + igraph_vector_int_init(&epath, 0); + igraph_get_shortest_path(&g, &vpath, &epath, 0, 4, IGRAPH_OUT); + igraph_vector_int_print(&vpath); + igraph_vector_int_print(&epath); + + igraph_get_shortest_path(&g, &vpath, &epath, 0, 0, IGRAPH_OUT); + igraph_vector_int_print(&vpath); + igraph_vector_int_print(&epath); + + igraph_set_warning_handler(igraph_warning_handler_ignore); + igraph_get_shortest_path(&g, &vpath, &epath, 4, 0, IGRAPH_OUT); + igraph_vector_int_print(&vpath); + igraph_vector_int_print(&epath); + igraph_set_warning_handler(igraph_warning_handler_print); + + igraph_get_shortest_path(&g, &vpath, &epath, 4, 0, IGRAPH_ALL); + igraph_vector_int_print(&vpath); + igraph_vector_int_print(&epath); + + /* Weighted */ + + igraph_vector_init(&w, 5); + VECTOR(w)[0] = 1; + VECTOR(w)[1] = 1; + VECTOR(w)[2] = 1; + VECTOR(w)[3] = 1; + VECTOR(w)[4] = 3.1; + + igraph_get_shortest_path_dijkstra(&g, &vpath, &epath, 0, 4, &w, IGRAPH_OUT); + igraph_vector_int_print(&vpath); + igraph_vector_int_print(&epath); + + igraph_vector_destroy(&w); + igraph_vector_int_destroy(&epath); + igraph_vector_int_destroy(&vpath); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/single_target_shortest_path.out b/tests/unit/single_target_shortest_path.out new file mode 100644 index 0000000..972ab28 --- /dev/null +++ b/tests/unit/single_target_shortest_path.out @@ -0,0 +1,10 @@ +0 3 4 +4 3 +0 + + + +4 3 0 +3 4 +0 1 2 3 4 +0 1 2 3 diff --git a/tests/unit/spinglass.c b/tests/unit/spinglass.c new file mode 100644 index 0000000..dfba86e --- /dev/null +++ b/tests/unit/spinglass.c @@ -0,0 +1,347 @@ +/* -*- 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, see . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_real_t modularity, temperature; + igraph_vector_int_t membership, csize; + /* igraph_integer_t i; */ + igraph_real_t cohesion, adhesion; + igraph_integer_t inner_links; + igraph_integer_t outer_links; + + igraph_rng_seed(igraph_rng_default(), 137); + + /* Two 5-cliques connected by a single edge */ + igraph_small(&g, 10, IGRAPH_UNDIRECTED, + 0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 4, 2, 3, 2, 4, 3, 4, + 5, 6, 5, 7, 5, 8, 5, 9, 6, 7, 6, 8, 6, 9, 7, 8, 7, 9, 8, 9, 0, 5, -1); + igraph_vector_int_init(&membership, 0); + igraph_vector_int_init(&csize, 0); + + printf("\nOriginal implementation.\n"); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + 0, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == igraph_vcount(&g)); + IGRAPH_ASSERT(igraph_vector_int_size(&csize) == igraph_vector_int_max(&membership) + 1); + + /* The following depend on the random seed, however, for this graph, + the result is almost always the same (i.e. two clusters). */ + printf("Modularity: %g\n", modularity); + print_vector_int(&membership); + + printf("\nOriginal implementation, parallel updating.\n"); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + 1, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == igraph_vcount(&g)); + IGRAPH_ASSERT(igraph_vector_int_size(&csize) == igraph_vector_int_max(&membership) + 1); + + /* The following depend on the random seed, however, for this graph, + the result is almost always the same (i.e. two clusters). */ + printf("Modularity: %g\n", modularity); + print_vector_int(&membership); + + printf("\nNegative implementation.\n"); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + 0, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_NEG, + /*gamma_minus =*/ 0); + + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == igraph_vcount(&g)); + IGRAPH_ASSERT(igraph_vector_int_size(&csize) == igraph_vector_int_max(&membership) + 1); + + /* The following depend on the random seed, however, for this graph, + the result is almost always the same (i.e. two clusters). */ + printf("Modularity: %g\n", modularity); + print_vector_int(&membership); + + /* Try to call this as well, we don't check the results currently.... */ + + igraph_community_spinglass_single(&g, + /*weights= */ 0, + /*vertex= */ 0, + /*community=*/ &membership, + /*cohesion= */ &cohesion, + /*adhesion= */ &adhesion, + /*inner_links= */ &inner_links, + /*outer_links= */ &outer_links, + /*spins= */ 2, + /*update_rule= */ IGRAPH_SPINCOMM_UPDATE_CONFIG, + /*gamma= */ 1.0); + + igraph_destroy(&g); + + printf("\nTrivial case: null graph.\n"); + igraph_empty(&g, 0, IGRAPH_UNDIRECTED); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + 0, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + IGRAPH_ASSERT(igraph_vector_int_empty(&membership)); + IGRAPH_ASSERT(igraph_vector_int_empty(&csize)); + IGRAPH_ASSERT(temperature == 0.01); + IGRAPH_ASSERT(isnan(modularity)); + igraph_destroy(&g); + + printf("\nTrivial case: singleton graph.\n"); + igraph_empty(&g, 1, IGRAPH_UNDIRECTED); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + 0, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + IGRAPH_ASSERT(igraph_vector_int_size(&membership) == 1 && VECTOR(membership)[0] == 0); + IGRAPH_ASSERT(igraph_vector_int_size(&csize) == 1 && VECTOR(csize)[0] == 1); + IGRAPH_ASSERT(temperature == 0.01); + IGRAPH_ASSERT(isnan(modularity)); + igraph_destroy(&g); + + printf("\nTest on a random graph to verify consistency of output.\n"); + igraph_barabasi_game(&g, 20, 1, 3, NULL, true, 0, false, IGRAPH_BARABASI_PSUMTREE, NULL); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + false, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + printf("Modularity: %g\nTemperature: %g\n", modularity, temperature); + print_vector_int(&membership); + + printf("Zero temp:\n"); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + false, /* parallel update */ + 0.0, /* start temperature */ + 0.0, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + printf("Modularity: %g\nTemperature: %g\n", modularity, temperature); + print_vector_int(&membership); + + printf("Parallel update:\n"); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + true, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + printf("Modularity: %g\nTemperature: %g\n", modularity, temperature); + print_vector_int(&membership); + + printf("Parallel update, zero temp:\n"); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + true, /* parallel update */ + 0.0, /* start temperature */ + 0.0, /* stop temperature */ + 0.0, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + printf("Modularity: %g\nTemperature: %g\n", modularity, temperature); + print_vector_int(&membership); + + printf("Negative implementation:\n"); + igraph_community_spinglass(&g, + NULL, /* no weights */ + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + false, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_NEG, + /*gamma_minus =*/ 0); + printf("Modularity: %g\nTemperature: %g\n", modularity, temperature); + print_vector_int(&membership); + + printf("With weights:\n"); + igraph_vector_t weights; + igraph_vector_init_range(&weights, 1, igraph_ecount(&g)+1); + + igraph_community_spinglass(&g, + &weights, + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + false, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + printf("Modularity: %g\nTemperature: %g\n", modularity, temperature); + print_vector_int(&membership); + + printf("Parallel update:\n"); + igraph_community_spinglass(&g, + &weights, + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + true, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_ORIG, + /*gamma_minus =*/ 0); + printf("Modularity: %g\nTemperature: %g\n", modularity, temperature); + print_vector_int(&membership); + + const igraph_integer_t half_ec = igraph_ecount(&g) / 2; + igraph_vector_range(&weights, -half_ec, igraph_ecount(&g) - half_ec); + printf("Negative implementation:\n"); + igraph_community_spinglass(&g, + &weights, + &modularity, + &temperature, + &membership, + &csize, + 10, /* no of spins */ + false, /* parallel update */ + 1.0, /* start temperature */ + 0.01, /* stop temperature */ + 0.99, /* cooling factor */ + IGRAPH_SPINCOMM_UPDATE_CONFIG, + 1.0, /* gamma */ + IGRAPH_SPINCOMM_IMP_NEG, + /*gamma_minus =*/ 0); + printf("Modularity: %g\nTemperature: %g\n", modularity, temperature); + print_vector_int(&membership); + + igraph_vector_destroy(&weights); + + igraph_destroy(&g); + + + igraph_vector_int_destroy(&membership); + igraph_vector_int_destroy(&csize); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/spinglass.out b/tests/unit/spinglass.out new file mode 100644 index 0000000..14e64d9 --- /dev/null +++ b/tests/unit/spinglass.out @@ -0,0 +1,49 @@ + +Original implementation. +Modularity: 0.452381 +( 1 1 1 1 1 0 0 0 0 0 ) + +Original implementation, parallel updating. +Modularity: 0.452381 +( 1 1 1 1 1 0 0 0 0 0 ) + +Negative implementation. +Modularity: 0.452381 +( 0 0 0 0 0 1 1 1 1 1 ) + +Trivial case: null graph. + +Trivial case: singleton graph. + +Test on a random graph to verify consistency of output. +Modularity: 0.209191 +Temperature: 0.0878155 +( 3 0 0 3 2 3 3 0 0 0 3 3 0 2 2 0 2 1 1 0 ) +Zero temp: +Modularity: 0.188443 +Temperature: 0 +( 2 3 2 2 0 1 1 2 2 0 1 1 1 0 0 3 0 3 3 3 ) +Parallel update: +Modularity: 0 +Temperature: 0.470429 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Parallel update, zero temp: +Modularity: -0.13323 +Temperature: 0 +( 1 2 2 1 2 2 2 1 1 1 0 2 2 2 2 1 2 2 1 1 ) +Negative implementation: +Modularity: 0.206619 +Temperature: 0.0570013 +( 0 1 1 0 2 2 2 1 1 1 2 2 1 0 2 1 2 3 3 1 ) +With weights: +Modularity: 0.0164729 +Temperature: 1.17406 +( 1 3 2 2 0 0 0 1 3 2 0 0 3 2 0 2 0 3 3 3 ) +Parallel update: +Modularity: 0.00242651 +Temperature: 1.1979 +( 4 2 2 0 0 3 0 1 1 1 4 3 1 3 0 1 3 3 4 1 ) +Negative implementation: +Modularity: 0.247814 +Temperature: 0.00992249 +( 0 1 2 3 4 1 4 4 5 6 5 0 5 2 4 2 4 1 1 1 ) diff --git a/tests/unit/stack.c b/tests/unit/stack.c new file mode 100644 index 0000000..44085d9 --- /dev/null +++ b/tests/unit/stack.c @@ -0,0 +1,94 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_stack_t st; + int i; + + /* igraph_stack_init, igraph_stack_destroy */ + igraph_stack_init(&st, 0); + igraph_stack_destroy(&st); + igraph_stack_init(&st, 10); + igraph_stack_destroy(&st); + + /* igraph_stack_reserve */ + igraph_stack_init(&st, 0); + igraph_stack_reserve(&st, 10); + igraph_stack_reserve(&st, 5); + + /* igraph_stack_empty */ + if (!igraph_stack_empty(&st)) { + return 1; + } + igraph_stack_push(&st, 1); + if (igraph_stack_empty(&st)) { + return 2; + } + + /* igraph_stack_size */ + if (igraph_stack_size(&st) != 1) { + return 3; + } + for (i = 0; i < 10; i++) { + igraph_stack_push(&st, i); + } + if (igraph_stack_size(&st) != 11) { + return 4; + } + + /* igraph_stack_clear */ + igraph_stack_clear(&st); + if (!igraph_stack_empty(&st)) { + return 5; + } + igraph_stack_push(&st, 100); + if (igraph_stack_pop(&st) != 100) { + return 6; + } + igraph_stack_clear(&st); + igraph_stack_clear(&st); + + /* igraph_stack_push, igraph_stack_pop */ + for (i = 0; i < 100; i++) { + igraph_stack_push(&st, 100 - i); + } + for (i = 0; i < 100; i++) { + if (igraph_stack_pop(&st) != i + 1) { + return 7; + } + } + if (!igraph_stack_empty(&st)) { + return 8; + } + + igraph_stack_destroy(&st); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/strvector_set_len_remove_print.c b/tests/unit/strvector_set_len_remove_print.c new file mode 100644 index 0000000..2bf43b1 --- /dev/null +++ b/tests/unit/strvector_set_len_remove_print.c @@ -0,0 +1,53 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_strvector_t sv; + char *test_string = "This is a string."; + char *test_string2 = "A completely different one."; + + printf("Two strings in a vector.\n"); + igraph_strvector_init(&sv, 5); + IGRAPH_ASSERT(igraph_strvector_set_len(&sv, 0, test_string, strlen(test_string)) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_strvector_set_len(&sv, 4, test_string2, strlen(test_string2)) == IGRAPH_SUCCESS); + igraph_strvector_print(&sv, stdout, " | "); + + printf("\nRemove a nonexistent one.\n"); + igraph_strvector_remove(&sv, 1); + igraph_strvector_print(&sv, stdout, " | "); + + printf("\nRemove one.\n"); + igraph_strvector_remove(&sv, 0); + igraph_strvector_print(&sv, stdout, " | "); + igraph_strvector_destroy(&sv); + + printf("\nOverwriting a string.\n"); + igraph_strvector_init(&sv, 5); + IGRAPH_ASSERT(igraph_strvector_set_len(&sv, 2, test_string2, strlen(test_string2)) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_strvector_set_len(&sv, 2, test_string, strlen(test_string)) == IGRAPH_SUCCESS); + igraph_strvector_print(&sv, stdout, " | "); + igraph_strvector_destroy(&sv); + + printf("\n"); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/strvector_set_len_remove_print.out b/tests/unit/strvector_set_len_remove_print.out new file mode 100644 index 0000000..e08244f --- /dev/null +++ b/tests/unit/strvector_set_len_remove_print.out @@ -0,0 +1,8 @@ +Two strings in a vector. +This is a string. | | | | A completely different one. +Remove a nonexistent one. +This is a string. | | | A completely different one. +Remove one. + | | A completely different one. +Overwriting a string. + | | This is a string. | | diff --git a/tests/unit/symmetric_tree.c b/tests/unit/symmetric_tree.c new file mode 100644 index 0000000..fc3ad4a --- /dev/null +++ b/tests/unit/symmetric_tree.c @@ -0,0 +1,111 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +#define PRINT_DESTROY(name) \ + printf(name "\n"); \ + print_graph_canon(&g); \ + igraph_destroy(&g); \ + igraph_vector_int_destroy(&v); \ + printf("\n"); + +int main(void) { + + igraph_t g; + igraph_vector_int_t v; + + /**** Test with empty vector ****/ + // initialize variables for test + igraph_vector_int_init_int(&v, 0); + // test + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_UNDIRECTED) == IGRAPH_SUCCESS); + // root vertex always gets created, cannot create null graph with this function + PRINT_DESTROY("Singleton graph"); + + igraph_vector_int_init_int(&v, 0); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_OUT) == IGRAPH_SUCCESS); + PRINT_DESTROY("Directed singleton graph"); + + /**** Test with -1 value ****/ + // invalid number of children + igraph_vector_int_init_int(&v, 1, -1); + CHECK_ERROR(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_UNDIRECTED), IGRAPH_EINVAL); + igraph_destroy(&g); + igraph_vector_int_destroy(&v); + + /**** Test undirected symmetric graph with 1 child ****/ + // 1 edge, 2 vertices (root and child) + igraph_vector_int_init_int(&v, 1, 1); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_UNDIRECTED) == IGRAPH_SUCCESS); + PRINT_DESTROY("Undirected graph with 2 vertices and 1 edge"); + + /**** Test directed symmetric graph with 1 child ****/ + // 1 edge, 2 vertices (root and child) + igraph_vector_int_init_int(&v, 1, 1); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_IN) == IGRAPH_SUCCESS); + PRINT_DESTROY("Directed graph with 2 vertices and 1 edge"); + + /**** Test directed symmetric path graph with 1 child in each level ****/ + igraph_vector_int_init_int(&v, 3, 1, 1, 1); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_OUT) == IGRAPH_SUCCESS); + PRINT_DESTROY("Directed path graph with 4 level and 1 child in each"); + + /**** Test undirected symmetric path graph with 1 child in each level ****/ + igraph_vector_int_init_int(&v, 3, 1, 1, 1); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_UNDIRECTED) == IGRAPH_SUCCESS); + PRINT_DESTROY("Undirected path graph with 4 level and 1 child in each"); + + /**** Test directed symmetric graph as binary tree with 1 level ****/ + igraph_vector_int_init_int(&v, 1, 2); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_OUT) == IGRAPH_SUCCESS); + PRINT_DESTROY("Binary out-tree with 3 vertices"); + + /**** Test undirected symmetric graph as binary tree with 1 level ****/ + igraph_vector_int_init_int(&v, 1, 2); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_UNDIRECTED) == IGRAPH_SUCCESS); + PRINT_DESTROY("Undirected binery tree with 3 vertices"); + + + /**** Test directed symmetric graph with 2 level, each 3 children ****/ + igraph_vector_int_init_int(&v, 2, 3, 3); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_OUT) == IGRAPH_SUCCESS); + PRINT_DESTROY("Symmetric out-tree with 2 level, 3 children in each"); + + /**** Test undirected symmetric graph with 2 level, each 3 children ****/ + igraph_vector_int_init_int(&v, 2, 3, 3); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_UNDIRECTED) == IGRAPH_SUCCESS); + PRINT_DESTROY("Symmetric undirected tree with 2 level, 3 children in each"); + + /**** Test directed symmetric graph with 2 level, first with 3 and second with 4 children ****/ + igraph_vector_int_init_int(&v, 2, 3, 4); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_OUT) == IGRAPH_SUCCESS); + PRINT_DESTROY("Symmetric out-tree with 2 level, 3 children in first, 4 in second"); + + /**** Test undirected symmetric graph with 2 level, first with 4 and second with 3 children ****/ + igraph_vector_int_init_int(&v, 2, 4, 3); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_UNDIRECTED) == IGRAPH_SUCCESS); + PRINT_DESTROY("Undirected symmetric tree with 2 level, 4 children in first, 3 in second"); + + /**** Test undirected symmetric graph with 3 level, first with 3, second with 4, third with 5 children ****/ + igraph_vector_int_init_int(&v, 3, 3, 4, 5); + IGRAPH_ASSERT(igraph_symmetric_tree(&g, &v, IGRAPH_TREE_UNDIRECTED) == IGRAPH_SUCCESS); + PRINT_DESTROY("Undirected symmetric tree with 3 level, 3 children in first, 4 in second, 5 in third"); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/symmetric_tree.out b/tests/unit/symmetric_tree.out new file mode 100644 index 0000000..4c7fb88 --- /dev/null +++ b/tests/unit/symmetric_tree.out @@ -0,0 +1,220 @@ +Singleton graph +directed: false +vcount: 1 +edges: { +} + +Directed singleton graph +directed: true +vcount: 1 +edges: { +} + +Undirected graph with 2 vertices and 1 edge +directed: false +vcount: 2 +edges: { +0 1 +} + +Directed graph with 2 vertices and 1 edge +directed: true +vcount: 2 +edges: { +1 0 +} + +Directed path graph with 4 level and 1 child in each +directed: true +vcount: 4 +edges: { +0 1 +1 2 +2 3 +} + +Undirected path graph with 4 level and 1 child in each +directed: false +vcount: 4 +edges: { +0 1 +1 2 +2 3 +} + +Binary out-tree with 3 vertices +directed: true +vcount: 3 +edges: { +0 1 +0 2 +} + +Undirected binery tree with 3 vertices +directed: false +vcount: 3 +edges: { +0 1 +0 2 +} + +Symmetric out-tree with 2 level, 3 children in each +directed: true +vcount: 13 +edges: { +0 1 +0 2 +0 3 +1 4 +1 5 +1 6 +2 7 +2 8 +2 9 +3 10 +3 11 +3 12 +} + +Symmetric undirected tree with 2 level, 3 children in each +directed: false +vcount: 13 +edges: { +0 1 +0 2 +0 3 +1 4 +1 5 +1 6 +2 7 +2 8 +2 9 +3 10 +3 11 +3 12 +} + +Symmetric out-tree with 2 level, 3 children in first, 4 in second +directed: true +vcount: 16 +edges: { +0 1 +0 2 +0 3 +1 4 +1 5 +1 6 +1 7 +2 8 +2 9 +2 10 +2 11 +3 12 +3 13 +3 14 +3 15 +} + +Undirected symmetric tree with 2 level, 4 children in first, 3 in second +directed: false +vcount: 17 +edges: { +0 1 +0 2 +0 3 +0 4 +1 5 +1 6 +1 7 +2 8 +2 9 +2 10 +3 11 +3 12 +3 13 +4 14 +4 15 +4 16 +} + +Undirected symmetric tree with 3 level, 3 children in first, 4 in second, 5 in third +directed: false +vcount: 76 +edges: { +0 1 +0 2 +0 3 +1 4 +1 5 +1 6 +1 7 +2 8 +2 9 +2 10 +2 11 +3 12 +3 13 +3 14 +3 15 +4 16 +4 17 +4 18 +4 19 +4 20 +5 21 +5 22 +5 23 +5 24 +5 25 +6 26 +6 27 +6 28 +6 29 +6 30 +7 31 +7 32 +7 33 +7 34 +7 35 +8 36 +8 37 +8 38 +8 39 +8 40 +9 41 +9 42 +9 43 +9 44 +9 45 +10 46 +10 47 +10 48 +10 49 +10 50 +11 51 +11 52 +11 53 +11 54 +11 55 +12 56 +12 57 +12 58 +12 59 +12 60 +13 61 +13 62 +13 63 +13 64 +13 65 +14 66 +14 67 +14 68 +14 69 +14 70 +15 71 +15 72 +15 73 +15 74 +15 75 +} + diff --git a/tests/unit/test.graphml b/tests/unit/test.graphml new file mode 100644 index 0000000..a6ab576 --- /dev/null +++ b/tests/unit/test.graphml @@ -0,0 +1,55 @@ + + + + + yellow + + + + + + 1 + + + 2006-11-12 + + + + green + incorrect + true + + + + blue + 0 + + + red "with entities" + + + + false + + + turquoise + fAlSe + + + 1.0 + + + 1.0 + + + 2.0 + + + + + + 1.1 + + + diff --git a/tests/unit/test_utilities.c b/tests/unit/test_utilities.c new file mode 100644 index 0000000..5f8a254 --- /dev/null +++ b/tests/unit/test_utilities.c @@ -0,0 +1,610 @@ + +/* + * This file contains functions that are useful when writing tests. + * Add #include "test_utilities.h" to the test program to use them. + */ + +#include "test_utilities.h" +#include +#include + +/* Print an igraph_real_t value. Be consistent in printing NaN/Inf across platforms. */ +void print_real(FILE *f, igraph_real_t x, const char *format) { + igraph_bool_t g8 = !strcmp(format, "%8g"); + if (isfinite(x)) { + if (x == 0 && signbit(x)) { + /* print negative zeros as positive zeros for sake of consistency */ + x = +0.0; + } + fprintf(f, format, x); + } else if (isnan(x)) { + fprintf(f, g8 ? " NaN" : "NaN"); + } else if (isinf(x) && x > 0) { + fprintf(f, g8 ? " Inf" : "Inf"); + } else if (isinf(x) && x < 0) { + fprintf(f, g8 ? " -Inf" : "-Inf"); + } +} + +void print_vector_format(const igraph_vector_t *v, FILE *f, const char *format) { + igraph_integer_t i, n = igraph_vector_size(v); + fprintf(f, "("); + for (i=0; i < n; i++) { + fprintf(f, " "); + print_real(f, VECTOR(*v)[i], format); + } + fprintf(f, " )\n"); +} + +/* Print elements of a vector. Use parentheses to make it clear when a vector has size zero. */ +void print_vector(const igraph_vector_t *v) { + print_vector_format(v, stdout, "%g"); +} + +/* Round elements of a vector to integers and print them. */ +/* This is meant to be used when the elements of a vector are integer values. */ +void print_vector_round(const igraph_vector_t *v) { + print_vector_format(v, stdout, "%.f"); +} + + +/* Print elements of an integer vector */ +void print_vector_int(const igraph_vector_int_t *v) { + igraph_integer_t i, n = igraph_vector_int_size(v); + printf("("); + for (i=0; i < n; i++) { + printf(" %" IGRAPH_PRId, VECTOR(*v)[i]); + } + printf(" )\n"); +} + +/* Print all vectors in an integer vector list. Use brackets around each vector + * and also use brackets around the entire list to make it clear when the list + * is empty */ +void print_vector_int_list(const igraph_vector_int_list_t *v) { + igraph_integer_t i, n = igraph_vector_int_list_size(v); + printf("{\n"); + for (i = 0; i < n; ++i) { + printf(" %" IGRAPH_PRId ": ", i); + print_vector_int(igraph_vector_int_list_get_ptr(v, i)); + } + printf("}\n"); +} + +/* Print elements of a matrix. Use brackets to make it clear when a vector has size zero. */ +void print_matrix_format(const igraph_matrix_t *m, FILE *f, const char *format) { + igraph_integer_t i, j, nrow = igraph_matrix_nrow(m), ncol = igraph_matrix_ncol(m); + if (nrow == 0 || ncol == 0) { + /* When the matrix is empty, output the dimensions */ + fprintf(f, "[ %" IGRAPH_PRId "-by-%" IGRAPH_PRId" ]\n", nrow, ncol); + return; + } + for (i = 0; i < nrow; i++) { + fprintf(f, i == 0 ? "[" : " "); + for (j = 0; j < ncol; j++) { + fprintf(f, " "); + print_real(f, MATRIX(*m, i, j), format); + } + fprintf(f, i == nrow-1 ? " ]\n" : "\n"); + } +} + +void print_matrix(const igraph_matrix_t *m) { + print_matrix_format(m, stdout, "%8g"); +} + +void print_matrix_int(const igraph_matrix_int_t *m) { + igraph_integer_t i, j, nrow = igraph_matrix_int_nrow(m), ncol = igraph_matrix_int_ncol(m); + if (nrow == 0 || ncol == 0) { + /* When the matrix is empty, output the dimensions */ + printf("[ %" IGRAPH_PRId "-by-%" IGRAPH_PRId" ]\n", nrow, ncol); + return; + } + for (i = 0; i < nrow; i++) { + printf(i == 0 ? "[" : " "); + for (j = 0; j < ncol; j++) { + printf(" "); + printf("%8" IGRAPH_PRId, MATRIX(*m, i, j)); + } + printf(i == nrow-1 ? " ]\n" : "\n"); + } +} + +/* Round elements of a matrix to integers and print them. */ +/* This is meant to be used when the elements of a matrix are integer values. */ +void print_matrix_round(const igraph_matrix_t *m) { + print_matrix_format(m, stdout, "%4.f"); +} + +void print_matrix_complex_round(const igraph_matrix_complex_t *m) { + + igraph_integer_t nr = igraph_matrix_complex_nrow(m); + igraph_integer_t nc = igraph_matrix_complex_ncol(m); + igraph_integer_t 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("%.f%+.fi", IGRAPH_REAL(z), IGRAPH_IMAG(z)); + } + printf("\n"); + } +} + +/* Print an adjacency list. Use brackets around each vector and also use + * brackets around the entire adjacency list to make it clear when the list + * is empty. + */ +void print_adjlist(const igraph_adjlist_t *adjlist) { + igraph_integer_t vcount = igraph_adjlist_size(adjlist); + igraph_integer_t i; + + printf("{\n"); + for (i = 0; i < vcount; ++i) { + printf(" %" IGRAPH_PRId ": ", i); + print_vector_int(igraph_adjlist_get(adjlist, i)); + } + printf("}\n"); +} + +/* Print a graph. Use brackets to make it obvious when the edge list is empty. */ +void print_graph(const igraph_t *graph) { + print_weighted_graph(graph, NULL); +} + +/* Print a graph with edge weights from a vector. Use brackets to make it + * obvious when the edge list is empty. */ +void print_weighted_graph(const igraph_t *graph, const igraph_vector_t* weights) { + igraph_integer_t ecount = igraph_ecount(graph); + igraph_integer_t vcount = igraph_vcount(graph); + igraph_integer_t i; + + printf("directed: %s\n", igraph_is_directed(graph) ? "true" : "false"); + printf("vcount: %" IGRAPH_PRId "\n", vcount); + printf("edges: {\n"); + for (i=0; i < ecount; ++i) { + if (weights) { + printf( + "%" IGRAPH_PRId " %" IGRAPH_PRId ": %g\n", + IGRAPH_FROM(graph, i), IGRAPH_TO(graph, i), + VECTOR(*weights)[i] + ); + } else { + printf( + "%" IGRAPH_PRId " %" IGRAPH_PRId "\n", + IGRAPH_FROM(graph, i), IGRAPH_TO(graph, i) + ); + } + } + printf("}\n"); +} + +/* Print a graph with edge weights from an edge attribute. Use brackets to make + * it obvious when the edge list is empty. */ +void print_weighted_graph_attr(const igraph_t *graph, const char* attr) { + igraph_integer_t ecount = igraph_ecount(graph); + igraph_integer_t vcount = igraph_vcount(graph); + igraph_integer_t i; + + printf("directed: %s\n", igraph_is_directed(graph) ? "true" : "false"); + printf("vcount: %" IGRAPH_PRId "\n", vcount); + printf("edges: {\n"); + for (i=0; i < ecount; ++i) + printf + ("%" IGRAPH_PRId " %" IGRAPH_PRId ": %g\n", + IGRAPH_FROM(graph, i), IGRAPH_TO(graph, i), + EAN(graph, attr, i) + ); + printf("}\n"); +} + +/* Print an incidence list. Use brackets around each vector and also use + * brackets around the entire incidence list to make it clear when the list + * is empty. + */ +void print_inclist(const igraph_inclist_t *inclist) { + igraph_integer_t vcount = igraph_inclist_size(inclist); + igraph_integer_t i; + + printf("{\n"); + for (i = 0; i < vcount; ++i) { + printf(" %" IGRAPH_PRId ": ", i); + print_vector_int(igraph_inclist_get(inclist, i)); + } + printf("}\n"); +} + +/* Print a lazy adjacency list. Use brackets around each vector and also use + * brackets around the entire lazy adjacency list to make it clear when the list + * is empty. + */ +void print_lazy_adjlist(igraph_lazy_adjlist_t *adjlist) { + igraph_integer_t vcount = igraph_lazy_adjlist_size(adjlist); + igraph_integer_t i; + + printf("{\n"); + for (i = 0; i < vcount; ++i) { + printf(" %" IGRAPH_PRId ": ", i); + print_vector_int(igraph_lazy_adjlist_get(adjlist, i)); + } + printf("}\n"); +} + +/* Print a lazy incidence list. Use brackets around each vector and also use + * brackets around the entire incidence list to make it clear when the list + * is empty. + */ +void print_lazy_inclist(igraph_lazy_inclist_t *inclist) { + igraph_integer_t vcount = igraph_lazy_inclist_size(inclist); + igraph_integer_t i; + + printf("{\n"); + for (i = 0; i < vcount; ++i) { + printf(" %" IGRAPH_PRId ": ", i); + print_vector_int(igraph_lazy_inclist_get(inclist, i)); + } + printf("}\n"); +} + +/* Edge comparison function used for sorting in print_graph_canon(). */ +int edge_compare(const void *e1, const void *e2) { + const igraph_integer_t *edge1 = (const igraph_integer_t *) e1; + const igraph_integer_t *edge2 = (const igraph_integer_t *) e2; + if (edge1[0] < edge2[0]) { + return -1; + } else if (edge1[0] > edge2[0]) { + return 1; + } else if (edge1[1] < edge2[1]) { + return -1; + } else if (edge1[1] > edge2[1]) { + return 1; + } else { + return 0; + } +} + +/* Print a graph using a sorted edge list. Other than sorting (i.e. canonicalizing) the edge list, + * this function is identical to print_graph(). */ +void print_graph_canon(const igraph_t *graph) { + igraph_integer_t ecount = igraph_ecount(graph); + igraph_integer_t vcount = igraph_vcount(graph); + igraph_integer_t i; + igraph_vector_int_t edges; + + printf("directed: %s\n", igraph_is_directed(graph) ? "true" : "false"); + printf("vcount: %" IGRAPH_PRId "\n", vcount); + printf("edges: {\n"); + + igraph_vector_int_init(&edges, 0); + igraph_get_edgelist(graph, &edges, false); + + /* If the graph is undirected, we make sure that the first vertex of undirected edges + * is always the one with the lower ID. */ + if (! igraph_is_directed(graph)) { + for (i=0; i < ecount; ++i) { + if (VECTOR(edges)[2*i] > VECTOR(edges)[2*i+1]) { + igraph_integer_t tmp = VECTOR(edges)[2*i]; + VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; + VECTOR(edges)[2*i+1] = tmp; + } + } + } + + /* Sort the edge list */ + igraph_qsort(&VECTOR(edges)[0], ecount, 2*sizeof(igraph_integer_t), &edge_compare); + + for (i=0; i < ecount; ++i) { + printf("%" IGRAPH_PRId " %" IGRAPH_PRId "\n", VECTOR(edges)[2*i], VECTOR(edges)[2*i+1]); + } + + igraph_vector_int_destroy(&edges); + + printf("}\n"); +} + +/* Print a vector, ensuring that the first nonzero element is positive. */ +void print_vector_first_nonzero_element_positive(const igraph_vector_t *vector, const char* format) { + igraph_vector_t copy; + igraph_integer_t i, n; + + igraph_vector_init_copy(©, vector); + + n = igraph_vector_size(©); + + for (i = 0; i < n; i++) { + if (VECTOR(copy)[i] < 0) { + for (; i < n; i++) { + if (VECTOR(copy)[i] != 0) { + VECTOR(copy)[i] *= -1; + } + } + break; + } else if (VECTOR(copy)[i] > 0) { + break; + } + } + + igraph_vector_printf(©, format); + igraph_vector_destroy(©); +} + +/* Print a complex vector, ensuring that the first element with nonzero real + * part has a positive real part. */ +void print_vector_complex_first_nonzero_real_part_positive(const igraph_vector_complex_t *vector) { + igraph_vector_complex_t copy; + igraph_integer_t i, n; + + igraph_vector_complex_init_copy(©, vector); + + n = igraph_vector_complex_size(©); + + for (i = 0; i < n; i++) { + if (IGRAPH_REAL(VECTOR(copy)[i]) < 0) { + for (; i < n; i++) { + if (IGRAPH_REAL(VECTOR(copy)[i]) != 0) { + IGRAPH_REAL(VECTOR(copy)[i]) *= -1; + } + if (IGRAPH_IMAG(VECTOR(copy)[i]) != 0) { + IGRAPH_IMAG(VECTOR(copy)[i]) *= -1; + } + } + break; + } else if (IGRAPH_REAL(VECTOR(copy)[i]) > 0) { + break; + } + } + + igraph_vector_complex_print(©); + igraph_vector_complex_destroy(©); +} + +/* Print a matrix, ensuring that the first nonzero element in each column is + * positive. */ +void print_matrix_first_row_positive(const igraph_matrix_t *matrix, const char* format) { + igraph_matrix_t copy; + igraph_integer_t i, j, nrow, ncol; + + igraph_matrix_init_copy(©, matrix); + + nrow = igraph_matrix_nrow(©); + ncol = igraph_matrix_ncol(©); + + for (i = 0; i < ncol; i++) { + for (j = 0; j < nrow; j++) { + if (MATRIX(copy, j, i) < 0) { + for (; j < nrow; j++) { + if (MATRIX(copy, j, i) != 0) { + MATRIX(copy, j, i) *= -1; + } + } + break; + } else if (MATRIX(copy, j, i) > 0) { + break; + } + } + } + + igraph_matrix_printf(©, format); + igraph_matrix_destroy(©); +} + +/* Print a complex matrix, ensuring that the first element with nonzero real + * part in each column has a positive real part. */ +void print_matrix_complex_first_row_positive(const igraph_matrix_complex_t *matrix) { + igraph_matrix_complex_t copy; + igraph_integer_t i, j, nrow, ncol; + igraph_complex_t z; + char buf[256]; + size_t len; + + igraph_matrix_complex_init_copy(©, matrix); + + nrow = igraph_matrix_complex_nrow(©); + ncol = igraph_matrix_complex_ncol(©); + + for (i = 0; i < ncol; i++) { + for (j = 0; j < nrow; j++) { + if (IGRAPH_REAL(MATRIX(copy, j, i)) < 0) { + for (; j < nrow; j++) { + if (IGRAPH_REAL(MATRIX(copy, j, i)) != 0) { + IGRAPH_REAL(MATRIX(copy, j, i)) *= -1; + } + if (IGRAPH_IMAG(MATRIX(copy, j, i)) != 0) { + IGRAPH_IMAG(MATRIX(copy, j, i)) *= -1; + } + } + break; + } else if (IGRAPH_REAL(MATRIX(copy, j, i)) > 0) { + break; + } + } + } + + for (i = 0; i < nrow; i++) { + for (j = 0; j < ncol; j++) { + z = MATRIX(copy, i, j); + if (j != 0) { + putchar(' '); + } + + snprintf(buf, sizeof(buf), "%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z)); + len = strlen(buf); + + /* ensure that we don't print -0 in the imaginary part */ + if (len > 3 && buf[len-3] == '-' && buf[len-2] == '0' && buf[len-1] == 'i') { + buf[len-3] = '+'; + } + + /* ensure that we don't print -0 in the real part either */ + if (buf[0] == '-' && buf[1] == '0' && (buf[2] == '+' || buf[2] == '-')) { + printf("%s", buf + 1); + } else { + printf("%s", buf); + } + } + printf("\n"); + } + + igraph_matrix_complex_destroy(©); +} + +void matrix_init_int_row_major(igraph_matrix_t *mat, igraph_integer_t nrow, igraph_integer_t ncol, const int *elem) { + igraph_integer_t c, r; + size_t i_elem = 0; + igraph_matrix_init(mat, nrow, ncol); + for (r = 0; r < nrow; r++) { + for (c = 0; c < ncol; c++) { + MATRIX(*mat, r, c) = elem[i_elem]; + i_elem++; + } + } +} + +void matrix_int_init_int_row_major(igraph_matrix_int_t *mat, igraph_integer_t nrow, igraph_integer_t ncol, const int *elem) { + igraph_integer_t c, r; + size_t i_elem = 0; + igraph_matrix_int_init(mat, nrow, ncol); + for (r = 0; r < nrow; r++) { + for (c = 0; c < ncol; c++) { + MATRIX(*mat, r, c) = elem[i_elem]; + i_elem++; + } + } +} + +void matrix_init_real_row_major(igraph_matrix_t *mat, igraph_integer_t nrow, igraph_integer_t ncol, const igraph_real_t *elem) { + igraph_integer_t c, r; + size_t i_elem = 0; + igraph_matrix_init(mat, nrow, ncol); + for (r = 0; r < nrow; r++) { + for (c = 0; c < ncol; c++) { + MATRIX(*mat, r, c) = elem[i_elem]; + i_elem++; + } + } +} + +void matrix_chop(igraph_matrix_t *mat, igraph_real_t cutoff) { + igraph_integer_t nelems = igraph_matrix_nrow(mat) * igraph_matrix_ncol(mat); + for (igraph_integer_t i = 0; i < nelems; i++) { + if (fabs(VECTOR(mat->data)[i]) < cutoff) { + VECTOR(mat->data)[i] = 0; + } + } +} + +void vector_chop(igraph_vector_t *vec, igraph_real_t cutoff) { + igraph_integer_t nelems = igraph_vector_size(vec); + for (igraph_integer_t i = 0; i < nelems; i++) { + if (fabs(VECTOR(*vec)[i]) < cutoff) { + VECTOR(*vec)[i] = 0; + } + } +} + +/* print all graph, edge and vertex attributes of a graph */ +void print_attributes(const igraph_t *g) { + igraph_vector_int_t gtypes, vtypes, etypes; + igraph_strvector_t gnames, vnames, enames; + igraph_integer_t i; + + igraph_integer_t j; + + igraph_vector_int_init(>ypes, 0); + igraph_vector_int_init(&vtypes, 0); + igraph_vector_int_init(&etypes, 0); + igraph_strvector_init(&gnames, 0); + igraph_strvector_init(&vnames, 0); + igraph_strvector_init(&enames, 0); + + igraph_cattribute_list(g, &gnames, >ypes, &vnames, &vtypes, + &enames, &etypes); + + /* Graph attributes */ + for (i = 0; i < igraph_strvector_size(&gnames); i++) { + if (i != 0) + putchar(' '); + printf("%s=", igraph_strvector_get(&gnames, i)); + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_real_printf(GAN(g, igraph_strvector_get(&gnames, i))); + } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { + printf("%d", GAB(g, igraph_strvector_get(&gnames, i))); + } else { + printf("\"%s\"", GAS(g, igraph_strvector_get(&gnames, i))); + } + } + if (igraph_strvector_size(&gnames)) + printf("\n"); + + for (i = 0; i < igraph_vcount(g); i++) { + printf("Vertex %" IGRAPH_PRId ":", i); + for (j = 0; j < igraph_strvector_size(&vnames); j++) { + putchar(' '); + printf("%s=", igraph_strvector_get(&vnames, j)); + if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_real_printf(VAN(g, igraph_strvector_get(&vnames, j), i)); + } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_BOOLEAN) { + printf("%d", VAB(g, igraph_strvector_get(&vnames, j), i)); + } else { + printf("\"%s\"", VAS(g, igraph_strvector_get(&vnames, j), i)); + } + } + printf("\n"); + } + + for (i = 0; i < igraph_ecount(g); i++) { + printf("Edge %" IGRAPH_PRId " (%" IGRAPH_PRId "-%" IGRAPH_PRId "):", i, IGRAPH_FROM(g, i), IGRAPH_TO(g, i)); + for (j = 0; j < igraph_strvector_size(&enames); j++) { + putchar(' '); + printf("%s=", igraph_strvector_get(&enames, j)); + if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_real_printf(EAN(g, igraph_strvector_get(&enames, j), i)); + } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_BOOLEAN) { + printf("%d", EAB(g, igraph_strvector_get(&enames, j), i)); + } else { + printf("\"%s\"", EAS(g, igraph_strvector_get(&enames, j), i)); + } + } + printf("\n"); + } + printf("\n"); + + igraph_strvector_destroy(&enames); + igraph_strvector_destroy(&vnames); + igraph_strvector_destroy(&gnames); + igraph_vector_int_destroy(&etypes); + igraph_vector_int_destroy(&vtypes); + igraph_vector_int_destroy(>ypes); +} + +expect_warning_context_t expect_warning_ctx; + +void record_last_warning(const char *reason, const char *file, int line) { + IGRAPH_UNUSED(file); IGRAPH_UNUSED(line); + + if (expect_warning_ctx.observed) { + igraph_free(expect_warning_ctx.observed); + } + + expect_warning_ctx.observed = strdup(reason); +} + +void print_bitset(const igraph_bitset_t* bitset) { + printf("("); + for (igraph_integer_t i = bitset->size - 1; i >= 0; --i) { + printf(" %d", !!IGRAPH_BIT_TEST(*bitset, i)); + } + printf(" )\n"); +} + +void print_bitset_list(const igraph_bitset_list_t *v) { + igraph_integer_t i, n = igraph_bitset_list_size(v); + printf("{\n"); + for (i = 0; i < n; ++i) { + printf(" %" IGRAPH_PRId ": ", i); + print_bitset(igraph_bitset_list_get_ptr(v, i)); + } + printf("}\n"); +} diff --git a/tests/unit/test_utilities.h b/tests/unit/test_utilities.h new file mode 100644 index 0000000..98d5abb --- /dev/null +++ b/tests/unit/test_utilities.h @@ -0,0 +1,174 @@ +#ifndef TEST_UTILITIES_H +#define TEST_UTILITIES_H + +/* + * This file declares functions that are useful when writing tests. + */ + +#include +#include +#include + +/* Structure used by the EXPECT_WARNING macro to test whether a warning was + * triggered in a section of the code in unit tests */ +typedef struct { + const char* expected; + char* observed; +} expect_warning_context_t; + +extern expect_warning_context_t expect_warning_ctx; + +/* Print an igraph_real_t value. Be consistent in printing NaN/Inf across platforms. */ +void print_real(FILE *f, igraph_real_t x, const char *format); + +void print_vector_format(const igraph_vector_t *v, FILE *f, const char *format); + +/* Print elements of a vector. Use parentheses to make it clear when a vector has size zero. */ +void print_vector(const igraph_vector_t *v); + +/* Round elements of a vector to integers and print them. */ +/* This is meant to be used when the elements of a vector are integer values. */ +void print_vector_round(const igraph_vector_t *v); + +/* Print elements of an integer vector */ +void print_vector_int(const igraph_vector_int_t *v); + +/* Print all vectors in an integer vector list. Use brackets around each vector + * and also use brackets around the entire list to make it clear when the list + * is empty */ +void print_vector_int_list(const igraph_vector_int_list_t *v); + +/* Print elements of a matrix. Use brackets to make it clear when a vector has size zero. */ +void print_matrix_format(const igraph_matrix_t *m, FILE *f, const char *format); + +void print_matrix(const igraph_matrix_t *m); + +void print_matrix_int(const igraph_matrix_int_t *m); + +/* Round elements of a matrix to integers and print them. */ +/* This is meant to be used when the elements of a matrix are integer values. */ +void print_matrix_round(const igraph_matrix_t *m); + +/* Round elements of a complex matrix to integers and print them. */ +/* This is meant to be used when the elements of a matrix are integer values. */ +void print_matrix_complex_round(const igraph_matrix_complex_t *m); + +/* Print an adjacency list. Use brackets around each vector and also use + * brackets around the entire adjacency list to make it clear when the list + * is empty. + */ +void print_adjlist(const igraph_adjlist_t *adjlist); + +/* Print a graph. Use brackets to make it obvious when the edge list is empty. */ +void print_graph(const igraph_t *graph); + +/* Print a graph with edge weights from a vector. Use brackets to make it + * obvious when the edge list is empty. */ +void print_weighted_graph(const igraph_t *graph, const igraph_vector_t* weights); + +/* Print a graph with edge weights from an edge attribute. Use brackets to make + * it obvious when the edge list is empty. */ +void print_weighted_graph_attr(const igraph_t *graph, const char* attr); + +/* Print an incidence list. Use brackets around each vector and also use + * brackets around the entire incidence list to make it clear when the list + * is empty. + */ +void print_inclist(const igraph_inclist_t *inclist); + +/* Print a lazy adjacency list. Use brackets around each vector and also use + * brackets around the entire lazy adjacency list to make it clear when the list + * is empty. + */ +void print_lazy_adjlist(igraph_lazy_adjlist_t *adjlist); + +/* Print a lazy incidence list. Use brackets around each vector and also use + * brackets around the entire incidence list to make it clear when the list + * is empty. + */ +void print_lazy_inclist(igraph_lazy_inclist_t *inclist); + +/* Edge comparison function used for sorting in print_graph_canon(). */ +int edge_compare(const void *e1, const void *e2); + +/* Print a graph using a sorted edge list. Other than sorting (i.e. canonicalizing) the edge list, + * this function is identical to print_graph(). */ +void print_graph_canon(const igraph_t *graph); + +/* Print a vector, ensuring that the first nonzero element is positive. */ +void print_vector_first_nonzero_element_positive(const igraph_vector_t *vector, const char* format); + +/* Print a complex vector, ensuring that the first element with nonzero real + * part has a positive real part. */ +void print_vector_complex_first_nonzero_real_part_positive(const igraph_vector_complex_t *vector); + +/* Print a matrix, ensuring that the first nonzero element in each column is + * positive. */ +void print_matrix_first_row_positive(const igraph_matrix_t *matrix, const char* format); + +/* Print a complex matrix, ensuring that the first element with nonzero real + * part in each column has a positive real part. */ +void print_matrix_complex_first_row_positive(const igraph_matrix_complex_t *matrix); + +/* print all graph, edge and vertex attributes of a graph */ +void print_attributes(const igraph_t *g); + +void matrix_init_int_row_major(igraph_matrix_t *mat, igraph_integer_t nrow, igraph_integer_t ncol, const int *elem); +void matrix_int_init_int_row_major(igraph_matrix_int_t *mat, igraph_integer_t nrow, igraph_integer_t ncol, const int *elem); +void matrix_init_real_row_major(igraph_matrix_t *mat, igraph_integer_t nrow, igraph_integer_t ncol, const igraph_real_t *elem); + +void matrix_chop(igraph_matrix_t *mat, igraph_real_t cutoff); +void vector_chop(igraph_vector_t *vec, igraph_real_t cutoff); + +#define VERIFY_FINALLY_STACK() \ + if (!IGRAPH_FINALLY_STACK_EMPTY) { \ + IGRAPH_FATALF( \ + "Finally stack is not empty (stack size is %d). " \ + "Check that the number in IGRAPH_FINALLY_CLEAN matches the IGRAPH_FINALLY count.\n", \ + IGRAPH_FINALLY_STACK_SIZE()); \ + } + +/* Run a test in a separate function; return the return value of the function + * if it is nonzero. Also verify the FINALLY stack and bail out if it is not + * empty. Needs an integer variable named 'retval' in the local context. */ +#define RUN_TEST(func) \ + { \ + int retval = func(); \ + if (retval) { \ + return retval; \ + } \ + VERIFY_FINALLY_STACK(); \ + } + +#define CHECK_ERROR(funcall, expected_err) \ + do { \ + igraph_error_handler_t *handler; \ + handler = igraph_set_error_handler(igraph_error_handler_ignore); \ + IGRAPH_ASSERT(funcall == expected_err); \ + igraph_set_error_handler(handler); \ + } while (0) + +void record_last_warning(const char *reason, const char *file, int line); + +void print_bitset(const igraph_bitset_t* bitset); + +void print_bitset_list(const igraph_bitset_list_t* bitset); + +#define EXPECT_WARNING(funcall, expected_warning) \ + do { \ + igraph_warning_handler_t *handler; \ + expect_warning_ctx.expected = expected_warning; \ + expect_warning_ctx.observed = NULL; \ + handler = igraph_set_warning_handler(record_last_warning); \ + IGRAPH_ASSERT(IGRAPH_SUCCESS == funcall); \ + igraph_set_warning_handler(handler); \ + if (expect_warning_ctx.observed == NULL) { \ + IGRAPH_FATALF("Expected this warning but none was raised:\n %s\n", expected_warning); \ + } else if (strcmp(expect_warning_ctx.observed, expect_warning_ctx.expected)) { \ + IGRAPH_FATALF("Expected warning:\n %s\ngot:\n %s\n", expected_warning, expect_warning_ctx.observed); \ + /* no need to free(expect_warning_ctx.observed); as previous line aborts immediately */ \ + } else { \ + free(expect_warning_ctx.observed); \ + } \ + } while (0) +#endif /* TEST_UTILITIES_H */ diff --git a/tests/unit/tls1.c b/tests/unit/tls1.c new file mode 100644 index 0000000..9028745 --- /dev/null +++ b/tests/unit/tls1.c @@ -0,0 +1,53 @@ +/* -*- 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 + +*/ + +#include +#include + +void *thread_function(void *arg) { + IGRAPH_UNUSED(arg); + IGRAPH_FINALLY(igraph_free, NULL); + return 0; +} + +int main(void) { + pthread_t thread_id; + void *exit_status; + + /* Skip if igraph is not thread-safe */ + if (!IGRAPH_THREAD_SAFE) { + return 77; + } + + /* Run a thread that leaves some junk in the error stack */ + pthread_create(&thread_id, NULL, thread_function, 0); + pthread_join(thread_id, &exit_status); + + /* Check that the error stack is not common */ + if (!IGRAPH_FINALLY_STACK_EMPTY) { + printf("Foobar\n"); + return 1; + } + + return 0; +} diff --git a/tests/unit/tls2.c b/tests/unit/tls2.c new file mode 100644 index 0000000..30e030c --- /dev/null +++ b/tests/unit/tls2.c @@ -0,0 +1,244 @@ +/* -*- 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 + +*/ + +#include +#include +#include + +#include "linalg/arpack_internal.h" + +#include "test_utilities.h" + +/* Test whether ARPACK is thread-safe. We will create two threads, + each calling a different ARPACK eigensolver. We will make sure that + the ARPACK calls from the two threads overlap */ + +typedef struct thread_data_t { + igraph_matrix_t *m; + igraph_vector_t *result; + pthread_cond_t *cond; + pthread_mutex_t *mutex; + int *steps, *othersteps; +} thread_data_t; + +int arpack_mult(igraph_real_t *to, igraph_real_t *from, + igraph_matrix_t *matrix) { + /* TODO */ + igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0, matrix, + from, /*beta=*/ 0.0, to); + + return 0; +} + +/* This is the function performed by each thread. It calls the + low-level ARPACK symmetric eigensolver, step by step. After each + step, it synchronizes with the other thread. + + The synchronization ensures that the two threads are using the + thread-local variables at the same time. If they are really + thread-local, then ARPACK still delivers the correct solution for + the two matrices. Otherwise the result is undefined: maybe results + will be incorrect, or the program will crash. + + This function is basically a simplified copy of igraph_arpack_rssolve. +*/ + +void *thread_function(void *arg) { + thread_data_t *data = (thread_data_t*) arg; + igraph_matrix_t *M = data->m; + igraph_vector_t *result = data->result; + pthread_cond_t *cond = data->cond; + pthread_mutex_t *mutex = data->mutex; + igraph_arpack_options_t options; + igraph_real_t *v, *workl, *workd, *d, *resid, *ax; + int *select; + int ido = 0; +#if IGRAPH_THREAD_SAFE + int rvec = 1; + char *all = "All"; +#endif + int i; + + igraph_arpack_options_init(&options); + options.n = igraph_matrix_nrow(M); + options.ldv = options.n; + options.nev = 1; + options.ncv = 3; + options.lworkl = options.ncv * (options.ncv + 8); + options.which[0] = 'L'; + options.which[1] = 'M'; + + options.iparam[0] = options.ishift; + options.iparam[2] = options.mxiter; + options.iparam[3] = options.nb; + options.iparam[4] = 0; + options.iparam[6] = options.mode; + options.info = options.start; + + v = IGRAPH_CALLOC(options.ldv * options.ncv, igraph_real_t); + workl = IGRAPH_CALLOC(options.lworkl, igraph_real_t); + workd = IGRAPH_CALLOC(3 * options.n, igraph_real_t); + d = IGRAPH_CALLOC(2 * options.ncv, igraph_real_t); + resid = IGRAPH_CALLOC(options.n, igraph_real_t); + ax = IGRAPH_CALLOC(options.n, igraph_real_t); + select = IGRAPH_CALLOC(options.ncv, int); + + if (!v || !workl || !workd || !d || !resid || !ax || !select) { + printf("Out of memory\n"); + return 0; + } + + while (1) { +#if IGRAPH_THREAD_SAFE + 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; + arpack_mult(to, from, M); + + } else { + break; + } + + pthread_mutex_lock(mutex); + *(data->steps) += 1; + if ( *(data->othersteps) == *(data->steps) ) { + pthread_cond_signal(cond); + } + + while ( *(data->othersteps) < * (data->steps) && *(data->othersteps) != -1 ) { + pthread_cond_wait(cond, mutex); + } + pthread_mutex_unlock(mutex); + } + + pthread_mutex_lock(mutex); + *data->steps = -1; + pthread_cond_signal(cond); + pthread_mutex_unlock(mutex); + + if (options.info != 0) { + printf("ARPACK error\n"); + return 0; + } + +#if IGRAPH_THREAD_SAFE + 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) { + printf("ARPACK error\n"); + return 0; + } + + igraph_vector_resize(result, options.n); + for (i = 0; i < options.n; i++) { + VECTOR(*result)[i] = v[i]; + } + + if (VECTOR(*result)[0] < 0) { + igraph_vector_scale(result, -1.0); + } + + free(v); + free(workl); + free(workd); + free(d); + free(resid); + free(ax); + free(select); + + return 0; +} + +int main(void) { + pthread_t thread_id1, thread_id2; + void *exit_status1, *exit_status2; + igraph_matrix_t m1, m2; + igraph_vector_t result1, result2; + pthread_cond_t steps_cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t steps_mutex = PTHREAD_MUTEX_INITIALIZER; + int steps1 = 0, steps2 = 0; + thread_data_t + data1 = { &m1, &result1, &steps_cond, &steps_mutex, &steps1, &steps2 }, + data2 = { &m2, &result2, &steps_cond, &steps_mutex, &steps2, &steps1 }; + int i, j; + + /* Skip if igraph is not thread safe */ + if (!IGRAPH_THREAD_SAFE) { + return 77; + } + + igraph_matrix_init(&m1, 10, 10); + igraph_matrix_init(&m2, 10, 10); + igraph_vector_init(&result1, igraph_matrix_nrow(&m1)); + igraph_vector_init(&result2, igraph_matrix_nrow(&m2)); + + igraph_rng_seed(igraph_rng_default(), 42); + + for (i = 0; i < igraph_matrix_nrow(&m1); i++) { + for (j = 0; j <= i; j++) { + MATRIX(m1, i, j) = MATRIX(m1, j, i) = + igraph_rng_get_integer(igraph_rng_default(), 0, 10); + } + } + + for (i = 0; i < igraph_matrix_nrow(&m2); i++) { + for (j = 0; j <= i; j++) { + MATRIX(m2, i, j) = MATRIX(m2, j, i) = + igraph_rng_get_integer(igraph_rng_default(), 0, 10); + } + } + + pthread_create(&thread_id1, NULL, thread_function, (void *) &data1); + pthread_create(&thread_id2, NULL, thread_function, (void *) &data2); + + pthread_join(thread_id1, &exit_status1); + pthread_join(thread_id2, &exit_status2); + + igraph_matrix_print(&m1); + igraph_vector_print(&result1); + printf("---\n"); + igraph_matrix_print(&m2); + igraph_vector_print(&result2); + + igraph_vector_destroy(&result1); + igraph_vector_destroy(&result2); + igraph_matrix_destroy(&m1); + igraph_matrix_destroy(&m2); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/tls2.out b/tests/unit/tls2.out new file mode 100644 index 0000000..fd28f95 --- /dev/null +++ b/tests/unit/tls2.out @@ -0,0 +1,23 @@ + 1 5 10 10 9 8 4 3 1 7 + 5 9 10 0 10 1 0 10 10 8 +10 10 8 1 10 6 4 7 2 2 +10 0 1 6 7 1 4 5 7 3 + 9 10 10 7 3 7 10 7 2 6 + 8 1 6 1 7 0 10 1 3 10 + 4 0 4 4 10 10 5 4 1 7 + 3 10 7 5 7 1 4 4 4 1 + 1 10 2 7 2 3 1 4 4 8 + 7 8 2 3 6 10 7 1 8 7 +0.335098 0.373089 0.362013 0.241464 0.403622 0.277745 0.281592 0.271598 0.235929 0.332236 +--- +3 7 7 7 4 7 9 0 2 6 +7 7 6 3 9 6 10 0 2 7 +7 6 3 8 9 8 9 7 5 9 +7 3 8 9 2 4 10 10 5 2 +4 9 9 2 3 7 1 3 6 9 +7 6 8 4 7 6 8 0 1 7 +9 10 9 10 1 8 3 2 4 4 +0 0 7 10 3 0 2 10 5 3 +2 2 5 5 6 1 4 5 10 10 +6 7 9 2 9 7 4 3 10 9 +0.300487 0.32522 0.384798 0.32095 0.299319 0.311637 0.33754 0.209222 0.272569 0.366266 diff --git a/tests/unit/topological_sorting.c b/tests/unit/topological_sorting.c new file mode 100644 index 0000000..addfbc9 --- /dev/null +++ b/tests/unit/topological_sorting.c @@ -0,0 +1,100 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_t g; + igraph_vector_int_t v; + igraph_vector_int_t res; + igraph_bool_t is_dag; + int ret; + + /* Test graph taken from http://en.wikipedia.org/wiki/Topological_sorting + * @ 05.03.2006 */ + igraph_small(&g, 8, IGRAPH_DIRECTED, + 0, 3, 0, 4, 1, 3, 2, 4, 2, 7, 3, 5, 3, 6, 3, 7, 4, 6, + -1); + + igraph_vector_int_init(&res, 0); + + igraph_is_dag(&g, &is_dag); + if (!is_dag) { + return 2; + } + + igraph_topological_sorting(&g, &res, IGRAPH_OUT); + print_vector_int(&res); + igraph_topological_sorting(&g, &res, IGRAPH_IN); + print_vector_int(&res); + + /* Error handling */ + VERIFY_FINALLY_STACK(); + igraph_set_error_handler(igraph_error_handler_ignore); + + /* Add a cycle: 5 -> 0 */ + igraph_vector_int_init_int(&v, 2, 5, 0); + igraph_add_edges(&g, &v, 0); + igraph_is_dag(&g, &is_dag); + if (is_dag) { + return 3; + } + ret = igraph_topological_sorting(&g, &res, IGRAPH_OUT); + if (ret != IGRAPH_EINVAL) { + return 1; + } + + igraph_vector_int_destroy(&v); + igraph_destroy(&g); + + /* This graph is the same but undirected */ + igraph_small(&g, 8, IGRAPH_UNDIRECTED, + 0, 3, 0, 4, 1, 3, 2, 4, 2, 7, 3, 5, 3, 6, 3, 7, 4, 6, + -1); + + igraph_is_dag(&g, &is_dag); + if (is_dag) { + return 4; + } + + ret = igraph_topological_sorting(&g, &res, IGRAPH_ALL); + if (ret != IGRAPH_EINVAL) { + return 1; + } + + ret = igraph_topological_sorting(&g, &res, IGRAPH_OUT); + if (ret != IGRAPH_EINVAL) { + return 1; + } + + igraph_destroy(&g); + + igraph_vector_int_destroy(&res); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/topological_sorting.out b/tests/unit/topological_sorting.out new file mode 100644 index 0000000..d6422ff --- /dev/null +++ b/tests/unit/topological_sorting.out @@ -0,0 +1,2 @@ +( 0 1 2 3 4 5 7 6 ) +( 5 6 7 4 3 2 0 1 ) diff --git a/tests/unit/tree_game.c b/tests/unit/tree_game.c new file mode 100644 index 0000000..0cac536 --- /dev/null +++ b/tests/unit/tree_game.c @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_bool_t is_tree = 0, are_connected = 0; + + igraph_rng_seed(igraph_rng_default(), 74088); + + /* Undirected */ + + IGRAPH_ASSERT(igraph_tree_game(&graph, 123, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_is_tree(&graph, &is_tree, NULL, IGRAPH_OUT) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(is_tree); + igraph_destroy(&graph); + + IGRAPH_ASSERT(igraph_tree_game(&graph, 123, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_is_tree(&graph, &is_tree, NULL, IGRAPH_OUT) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(is_tree); + igraph_destroy(&graph); + + /* Directed out-tree */ + + IGRAPH_ASSERT(igraph_tree_game(&graph, 123, IGRAPH_DIRECTED, IGRAPH_RANDOM_TREE_LERW) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_is_tree(&graph, &is_tree, NULL, IGRAPH_OUT) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(is_tree); + igraph_destroy(&graph); + + /* IGRAPH_RANDOM_TREE_PRUFER does not currently support directed graphs */ + + /* Null graph */ + + IGRAPH_ASSERT(igraph_tree_game(&graph, 0, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 0); + igraph_destroy(&graph); + + IGRAPH_ASSERT(igraph_tree_game(&graph, 0, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 0); + igraph_destroy(&graph); + + /* Singleton graph */ + + IGRAPH_ASSERT(igraph_tree_game(&graph, 1, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 1); + igraph_destroy(&graph); + + IGRAPH_ASSERT(igraph_tree_game(&graph, 1, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 1); + igraph_destroy(&graph); + + /* P_2 */ + + IGRAPH_ASSERT(igraph_tree_game(&graph, 2, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 2); + IGRAPH_ASSERT(igraph_ecount(&graph) == 1); + IGRAPH_ASSERT(igraph_are_adjacent(&graph, 0, 1, &are_connected) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(are_connected); + igraph_destroy(&graph); + + IGRAPH_ASSERT(igraph_tree_game(&graph, 2, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vcount(&graph) == 2); + IGRAPH_ASSERT(igraph_ecount(&graph) == 1); + IGRAPH_ASSERT(igraph_are_adjacent(&graph, 0, 1, &are_connected) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(are_connected); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/triad_census.c b/tests/unit/triad_census.c new file mode 100644 index 0000000..0c4ab6d --- /dev/null +++ b/tests/unit/triad_census.c @@ -0,0 +1,56 @@ +/* + IGraph library. + Copyright (C) 2015-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, see . +*/ + +#include + +#include "test_utilities.h" + +int main(void) { + /* this is a directed graph with 10 vertices and 20 edges: */ + igraph_integer_t vc = 10, ec = 20; + igraph_integer_t edge_data[] = { + 0, 2, 1, 4, 2, 5, 2, 7, 3, 7, 3, 8, 4, 2, 5, 8, 6, 0, 6, 1, 6, 2, 7, + 0, 8, 0, 8, 2, 8, 3, 8, 5, 9, 2, 9, 3, 9, 4, 9, 5 + }; + + igraph_vector_int_t edges; + igraph_vector_t tri; + igraph_t graph; + + igraph_set_warning_handler(igraph_warning_handler_ignore); + + igraph_vector_int_view(&edges, edge_data, 2 * ec); + + igraph_create(&graph, &edges, vc, 1 /* directed=true */); + + igraph_vector_init(&tri, 0); + + igraph_triad_census(&graph, &tri); + print_vector_round(&tri); + + igraph_to_undirected(&graph, IGRAPH_TO_UNDIRECTED_COLLAPSE, NULL); /* convert to undirected */ + igraph_triad_census(&graph, &tri); + print_vector_round(&tri); + + igraph_vector_destroy(&tri); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/triad_census.out b/tests/unit/triad_census.out new file mode 100644 index 0000000..ee099b7 --- /dev/null +++ b/tests/unit/triad_census.out @@ -0,0 +1,2 @@ +( 25 45 7 7 12 11 2 4 4 1 1 0 0 1 0 0 ) +( 25 0 52 0 0 0 0 0 0 0 37 0 0 0 0 6 ) diff --git a/tests/unit/trie.c b/tests/unit/trie.c new file mode 100644 index 0000000..c9b23b8 --- /dev/null +++ b/tests/unit/trie.c @@ -0,0 +1,138 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-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 + 02110-1301 USA + +*/ + +#include +#include + +#include "core/trie.h" + +#include "test_utilities.h" + +int main(void) { + + igraph_trie_t trie; + igraph_integer_t id; + igraph_integer_t i; + const char *str; + + /* init */ + igraph_trie_init(&trie, 0); + + /* add and get values */ + igraph_trie_get(&trie, "hello", &id); + printf("hello: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "hepp", &id); + printf("hepp: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "alma", &id); + printf("alma: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "also", &id); + printf("also: %" IGRAPH_PRId "\n", id); + + igraph_trie_get(&trie, "hello", &id); + printf("hello: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "hepp", &id); + printf("hepp: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "alma", &id); + printf("alma: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "also", &id); + printf("also: %" IGRAPH_PRId "\n", id); + + igraph_trie_get(&trie, "a", &id); + printf("a: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "axon", &id); + printf("axon: %" IGRAPH_PRId "\n", id); + + igraph_trie_get(&trie, "hello", &id); + printf("hello: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "hepp", &id); + printf("hepp: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "alma", &id); + printf("alma: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "also", &id); + printf("also: %" IGRAPH_PRId "\n", id); + + /* check for existence */ + igraph_trie_check(&trie, "head", &id); + printf("head: %" IGRAPH_PRId "\n", id); + igraph_trie_check(&trie, "alma", &id); + printf("alma: %" IGRAPH_PRId "\n", id); + + /* destroy */ + igraph_trie_destroy(&trie); + + /* the same with index */ + igraph_trie_init(&trie, 1); + + igraph_trie_get(&trie, "hello", &id); + printf("hello: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "hepp", &id); + printf("hepp: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "alma", &id); + printf("alma: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "also", &id); + printf("also: %" IGRAPH_PRId "\n", id); + + igraph_trie_get(&trie, "hello", &id); + printf("hello: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "hepp", &id); + printf("hepp: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "alma", &id); + printf("alma: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "also", &id); + printf("also: %" IGRAPH_PRId "\n", id); + + igraph_trie_get(&trie, "a", &id); + printf("a: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "axon", &id); + printf("axon: %" IGRAPH_PRId "\n", id); + + igraph_trie_get(&trie, "hello", &id); + printf("hello: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "hepp", &id); + printf("hepp: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "alma", &id); + printf("alma: %" IGRAPH_PRId "\n", id); + igraph_trie_get(&trie, "also", &id); + printf("also: %" IGRAPH_PRId "\n", id); + + /* check for existence */ + igraph_trie_check(&trie, "head", &id); + printf("head: %" IGRAPH_PRId "\n", id); + igraph_trie_check(&trie, "alma", &id); + printf("alma: %" IGRAPH_PRId "\n", id); + + for (i = 0; i < igraph_trie_size(&trie); i++) { + str = igraph_trie_idx(&trie, i); + printf("%" IGRAPH_PRId ": %s\n", i, str); + } + + /* prevent insertion of empty key */ + igraph_set_error_handler(igraph_error_handler_ignore); + IGRAPH_ASSERT(igraph_trie_get(&trie, "", &id) == IGRAPH_EINVAL); + + igraph_trie_destroy(&trie); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/trie.out b/tests/unit/trie.out new file mode 100644 index 0000000..7367a56 --- /dev/null +++ b/tests/unit/trie.out @@ -0,0 +1,38 @@ +hello: 0 +hepp: 1 +alma: 2 +also: 3 +hello: 0 +hepp: 1 +alma: 2 +also: 3 +a: 4 +axon: 5 +hello: 0 +hepp: 1 +alma: 2 +also: 3 +head: -1 +alma: 2 +hello: 0 +hepp: 1 +alma: 2 +also: 3 +hello: 0 +hepp: 1 +alma: 2 +also: 3 +a: 4 +axon: 5 +hello: 0 +hepp: 1 +alma: 2 +also: 3 +head: -1 +alma: 2 +0: hello +1: hepp +2: alma +3: also +4: a +5: axon diff --git a/tests/unit/utf8_with_bom.net b/tests/unit/utf8_with_bom.net new file mode 100644 index 0000000..57abfdf --- /dev/null +++ b/tests/unit/utf8_with_bom.net @@ -0,0 +1,18 @@ +*Vertices 10 + 1 "Vlado" 0.4682 0.4841 0.5000 ellipse foo bar + 2 "Андреј" 0.7125 0.8376 0.5000 ellipse foo baz + 3 "ŽnidarÅ¡ič" 0.3346 0.7739 0.5000 ellipse foo 123 + 4 "Đurđić" 0.2455 0.8725 0.5000 ellipse foo 电脑 + 5 "Jürgen Müller" 0.6361 0.6373 0.5000 ellipse foo "a b c" + 6 "∰∈∀≋∞⊚" 0.5153 0.6935 0.5000 ellipse foo "þæð" + 7 "⑤⑨④②⑦" 0.8053 0.4917 0.5000 ellipse foo 123.456 + 8 "♔♕♖♗♘♙" 0.4173 0.6161 0.5000 ellipse foo xxx + 9 "焹焓燝牕犫獨" 0.5623 0.5539 0.5000 ellipse x -1 + 10 "ανδρει" 0.3690 0.3202 0.5000 ellipse y 1 +*Edges + 2 3 1 l "焹焓燝牕犫獨" + 3 4 1 + 2 6 1 + 3 6 1 + 5 6 1 + 2 7 1 diff --git a/tests/unit/vector.c b/tests/unit/vector.c new file mode 100644 index 0000000..20581f5 --- /dev/null +++ b/tests/unit/vector.c @@ -0,0 +1,388 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_vector_t v; + igraph_vector_int_t v4, v5, v6; + igraph_integer_t i; + igraph_real_t *ptr; + igraph_integer_t pos; + igraph_real_t min, max, min2, max2; + igraph_integer_t which_min, which_max, which_min2, which_max2; + + printf("Initialise empty vector\n"); + igraph_vector_init(&v, 0); + igraph_vector_destroy(&v); + + printf("Initialise vector of length 10\n"); + igraph_vector_init(&v, 10); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test VECTOR() and igraph_vector_size\n"); + igraph_vector_init(&v, 10); + for (i = 0; i < igraph_vector_size(&v); i++) { + VECTOR(v)[i] = 10 - i; + } + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_reserve and igraph_vector_push_back\n"); + igraph_vector_init(&v, 0); + igraph_vector_reserve(&v, 10); + for (i = 0; i < 10; i++) { + igraph_vector_push_back(&v, i); + } + + printf("Test igraph_vector_empty and igraph_vector_clear\n"); + IGRAPH_ASSERT(!igraph_vector_empty(&v)); + igraph_vector_clear(&v); + IGRAPH_ASSERT(igraph_vector_empty(&v)); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_get and igraph_vector_get_ptr\n"); + igraph_vector_init(&v, 5); + for (i = 0; i < igraph_vector_size(&v); i++) { + *igraph_vector_get_ptr(&v, i) = 100 * i; + } + for (i = 0; i < igraph_vector_size(&v); i++) { + printf(" %" IGRAPH_PRId "", (igraph_integer_t)igraph_vector_get(&v, i)); + } + printf("\n"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_set\n"); + igraph_vector_init(&v, 5); + for (i = 0; i < igraph_vector_size(&v); i++) { + igraph_vector_set(&v, i, 20 * i); + } + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_null\n"); + igraph_vector_init(&v, 0); + igraph_vector_null(&v); + igraph_vector_destroy(&v); + igraph_vector_init(&v, 10); + for (i = 0; i < igraph_vector_size(&v); i++) { + VECTOR(v)[i] = i + 1; + } + igraph_vector_null(&v); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_tail, igraph_vector_pop_back\n"); + igraph_vector_init(&v, 10); + for (i = 0; i < igraph_vector_size(&v); i++) { + VECTOR(v)[i] = i + 1; + } + while (!igraph_vector_empty(&v)) { + printf(" %" IGRAPH_PRId "", (igraph_integer_t)igraph_vector_tail(&v)); + printf(" %" IGRAPH_PRId "", (igraph_integer_t)igraph_vector_pop_back(&v)); + } + printf("\n"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_resize, igraph_vector_sort\n"); + igraph_vector_init(&v, 20); + for (i = 0; i < 10; i++) { + VECTOR(v)[i] = 10 - i; + } + igraph_vector_resize(&v, 10); + igraph_vector_sort(&v); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_{which}_{min, max}\n"); + igraph_vector_init(&v, 10); + for (i = 0; i < igraph_vector_size(&v); i++) { + VECTOR(v)[i] = 100 - i; + } + for (i = 0; i < 10; i++) { + printf(" %" IGRAPH_PRId "", (igraph_integer_t)VECTOR(v)[i]); + } + printf("\n"); + + min = igraph_vector_min(&v); + which_min = igraph_vector_which_min(&v); + + IGRAPH_ASSERT(min == 91); + IGRAPH_ASSERT(which_min == 9); + IGRAPH_ASSERT(min == VECTOR(v)[which_min]); + + max = igraph_vector_max(&v); + which_max = igraph_vector_which_max(&v); + + IGRAPH_ASSERT(max == 100); + IGRAPH_ASSERT(which_max == 0); + IGRAPH_ASSERT(max == VECTOR(v)[which_max]); + + igraph_vector_minmax(&v, &min2, &max2); + igraph_vector_which_minmax(&v, &which_min2, &which_max2); + + IGRAPH_ASSERT(min == min2); + IGRAPH_ASSERT(max == max2); + IGRAPH_ASSERT(which_min == which_min2); + IGRAPH_ASSERT(which_max == which_max2); + IGRAPH_ASSERT(min2 == VECTOR(v)[which_min2]); + IGRAPH_ASSERT(max2 == VECTOR(v)[which_max2]); + + printf("Test NaN values\n"); + igraph_vector_push_back(&v, IGRAPH_NAN); + igraph_vector_push_back(&v, IGRAPH_NAN); + igraph_vector_push_back(&v, 1); + + IGRAPH_ASSERT(igraph_vector_is_any_nan(&v)); + + min = igraph_vector_min(&v); + which_min = igraph_vector_which_min(&v); + + IGRAPH_ASSERT(isnan(min)); + /* Index should be to first NaN value */ + IGRAPH_ASSERT(which_min == 10); + IGRAPH_ASSERT(isnan(VECTOR(v)[which_min])); + + max = igraph_vector_max(&v); + which_max = igraph_vector_which_max(&v); + + IGRAPH_ASSERT(isnan(max)); + /* Index should be to first NaN value */ + IGRAPH_ASSERT(which_max == 10); + /* In case of NaN it should hold that which_max == which_min */ + IGRAPH_ASSERT(which_max == which_min); + + igraph_vector_minmax(&v, &min2, &max2); + igraph_vector_which_minmax(&v, &which_min2, &which_max2); + + IGRAPH_ASSERT(isnan(min2)); + IGRAPH_ASSERT(isnan(max2)); + IGRAPH_ASSERT(which_min == which_min2); + IGRAPH_ASSERT(which_max == which_max2); + /* In case of NaN it should hold that which_max == which_min */ + IGRAPH_ASSERT(which_min2 == which_max2); + IGRAPH_ASSERT(isnan(VECTOR(v)[which_min2])); + IGRAPH_ASSERT(isnan(VECTOR(v)[which_max2])); + + printf("Test igraph_vector_init_array\n"); + igraph_vector_destroy(&v); + ptr = (igraph_real_t*) malloc(10 * sizeof(igraph_real_t)); + igraph_vector_init_array(&v, ptr, 10); + free(ptr); + for (i = 0; i < 10; i++) { + VECTOR(v)[i] = 100 - i; + } + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_copy_to\n"); + ptr = (igraph_real_t*) malloc(10 * sizeof(igraph_real_t)); + igraph_vector_init_range(&v, 11, 21); + igraph_vector_copy_to(&v, ptr); + for (i = 0; i < 10; i++) { + printf(" %" IGRAPH_PRId "", (igraph_integer_t)ptr[i]); + } + printf("\n"); + free(ptr); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_init_range, igraph_vector_sum, igraph_vector_prod\n"); + igraph_vector_init_range(&v, 1, 6); + printf(" %" IGRAPH_PRId "", (igraph_integer_t)igraph_vector_sum(&v)); + printf(" %" IGRAPH_PRId "\n", (igraph_integer_t)igraph_vector_prod(&v)); + + printf("Test igraph_vector_remove_section\n"); + igraph_vector_remove_section(&v, 2, 4); + print_vector_format(&v, stdout, "%g"); + + printf("Test igraph_vector_remove_section with invalid limits\n"); + igraph_vector_remove_section(&v, -3, -1); + igraph_vector_remove_section(&v, 100, 120); + igraph_vector_remove_section(&v, 2, 0); + print_vector_format(&v, stdout, "%g"); + igraph_vector_remove_section(&v, 1, 20); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_remove\n"); + igraph_vector_init_range(&v, 1, 11); + igraph_vector_remove(&v, 9); + igraph_vector_remove(&v, 0); + igraph_vector_remove(&v, 4); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_remove_fast\n"); + igraph_vector_init_range(&v, 1, 11); + igraph_vector_remove_fast(&v, 9); + igraph_vector_remove_fast(&v, 0); + igraph_vector_remove_fast(&v, 4); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_move_interval\n"); + igraph_vector_init_range(&v, 0, 10); + igraph_vector_move_interval(&v, 5, 10, 0); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_isininterval\n"); + igraph_vector_init_range(&v, 1, 11); + IGRAPH_ASSERT(igraph_vector_isininterval(&v, 1, 10)); + IGRAPH_ASSERT(!igraph_vector_isininterval(&v, 2, 10)); + IGRAPH_ASSERT(!igraph_vector_isininterval(&v, 1, 9)); + + printf("Test igraph_vector_any_smaller\n"); + IGRAPH_ASSERT(!igraph_vector_any_smaller(&v, 1)); + IGRAPH_ASSERT(igraph_vector_any_smaller(&v, 2)); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_all_e\n"); + + printf("Test igraph_vector_binsearch\n"); + igraph_vector_init_range(&v, 0, 10); + for (i = 0; i < igraph_vector_size(&v); i++) { + IGRAPH_ASSERT(igraph_vector_binsearch(&v, 0, 0)); + } + IGRAPH_ASSERT(!igraph_vector_binsearch(&v, 10, 0)); + IGRAPH_ASSERT(!igraph_vector_binsearch(&v, -1, 0)); + + for (i = 0; i < igraph_vector_size(&v); i++) { + VECTOR(v)[i] = 2 * i; + } + for (i = 0; i < igraph_vector_size(&v); i++) { + IGRAPH_ASSERT(igraph_vector_binsearch(&v, VECTOR(v)[i], &pos)); + IGRAPH_ASSERT(pos == i); + IGRAPH_ASSERT(!igraph_vector_binsearch(&v, VECTOR(v)[i] + 1, &pos)); + } + igraph_vector_destroy(&v); + + printf("Test Binsearch in empty vector\n"); + igraph_vector_init(&v, 0); + IGRAPH_ASSERT(!igraph_vector_binsearch2(&v, 0)); + IGRAPH_ASSERT(!igraph_vector_binsearch(&v, 1, &pos)); + IGRAPH_ASSERT(pos == 0); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_init_real\n"); + igraph_vector_init_real(&v, 10, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_init_int\n"); + igraph_vector_init_int(&v, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_init_real\n"); + igraph_vector_init_real_end(&v, -1, 1.0, 2.0, 3.0, 4.0, 5.0, + 6.0, 7.0, 8.0, 9.0, 10.0, -1.0); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test igraph_vector_init_int\n"); + igraph_vector_init_int_end(&v, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test order2\n"); + igraph_vector_init_int_end(&v, -1, 10, 9, 8, 7, 6, 7, 8, 9, 10, -1); + igraph_vector_order2(&v); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test order2 on empty vector\n"); + igraph_vector_init_int_end(&v, -1, -1); + igraph_vector_order2(&v); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test filter_smaller, quite special....\n"); + igraph_vector_init_int_end(&v, -1, 0, 1, 2, 3, 4, 4, 4, 4, 5, 6, 7, 8, -1); + igraph_vector_filter_smaller(&v, 4); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + igraph_vector_init_int_end(&v, -1, 1, 2, 3, 4, 4, 4, 4, 5, 6, 7, 8, -1); + igraph_vector_filter_smaller(&v, 0); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + igraph_vector_init_int_end(&v, -1, 0, 0, 1, 2, 3, 4, 4, 4, 4, 5, 6, 7, 8, -1); + igraph_vector_filter_smaller(&v, 0); + print_vector_format(&v, stdout, "%g"); + igraph_vector_destroy(&v); + + printf("Test rank\n"); + igraph_vector_init_int_end(&v, -1, 0, 1, 2, 6, 5, 2, 1, 0, -1); + igraph_vector_int_init(&v4, 0); + igraph_vector_rank(&v, &v4, 7); + print_vector_format(&v, stdout, "%g"); + print_vector_int(&v4); + igraph_vector_destroy(&v); + igraph_vector_int_destroy(&v4); + + printf("Test pair order\n"); + igraph_vector_int_init_int_end(&v5, -1, 1, 1, 2, 2, -1); + igraph_vector_int_init_int_end(&v6, -1, 2, 3, 1, 3, -1); + igraph_vector_int_init(&v4, 0); + igraph_vector_int_pair_order(&v5, &v6, &v4, 3); + print_vector_int(&v4); + igraph_vector_int_destroy(&v5); + igraph_vector_int_destroy(&v6); + igraph_vector_int_destroy(&v4); + + printf("Test fill\n"); + + igraph_vector_init(&v, 100); + igraph_vector_fill(&v, 1.234567); + for (i = 0; i < igraph_vector_size(&v); i++) { + IGRAPH_ASSERT(VECTOR(v)[i] == 1.234567); + } + igraph_vector_destroy(&v); + + printf("Test range\n"); + + igraph_vector_init(&v, 100); + igraph_vector_range(&v, 20, 50); + IGRAPH_ASSERT(igraph_vector_size(&v) == 30); + for (i = 0; i < igraph_vector_size(&v); i++) { + IGRAPH_ASSERT(VECTOR(v)[i] == 20 + i); + } + igraph_vector_destroy(&v); + + printf("Test igraph_vector_int_init_range, igraph_vector_int_order1\n"); + igraph_vector_int_init_range(&v4, 1, 11); + igraph_vector_int_init(&v5, 0); + igraph_vector_int_order1(&v4, &v5, 10); + print_vector_int(&v5); + igraph_vector_int_destroy(&v4); + igraph_vector_int_destroy(&v5); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/vector.out b/tests/unit/vector.out new file mode 100644 index 0000000..628bee5 --- /dev/null +++ b/tests/unit/vector.out @@ -0,0 +1,67 @@ +Initialise empty vector +Initialise vector of length 10 +( 0 0 0 0 0 0 0 0 0 0 ) +Test VECTOR() and igraph_vector_size +( 10 9 8 7 6 5 4 3 2 1 ) +Test igraph_vector_reserve and igraph_vector_push_back +Test igraph_vector_empty and igraph_vector_clear +Test igraph_vector_get and igraph_vector_get_ptr + 0 100 200 300 400 +Test igraph_vector_set +( 0 20 40 60 80 ) +Test igraph_vector_null +( 0 0 0 0 0 0 0 0 0 0 ) +Test igraph_vector_tail, igraph_vector_pop_back + 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 +Test igraph_vector_resize, igraph_vector_sort +( 1 2 3 4 5 6 7 8 9 10 ) +Test igraph_vector_{which}_{min, max} + 100 99 98 97 96 95 94 93 92 91 +Test NaN values +Test igraph_vector_init_array +( 100 99 98 97 96 95 94 93 92 91 ) +Test igraph_vector_copy_to + 11 12 13 14 15 16 17 18 19 20 +Test igraph_vector_init_range, igraph_vector_sum, igraph_vector_prod + 15 120 +Test igraph_vector_remove_section +( 1 2 5 ) +Test igraph_vector_remove_section with invalid limits +( 1 2 5 ) +( 1 ) +Test igraph_vector_remove +( 2 3 4 5 7 8 9 ) +Test igraph_vector_remove_fast +( 9 2 3 4 8 6 7 ) +Test igraph_vector_move_interval +( 5 6 7 8 9 5 6 7 8 9 ) +Test igraph_vector_isininterval +Test igraph_vector_any_smaller +Test igraph_vector_all_e +Test igraph_vector_binsearch +Test Binsearch in empty vector +Test igraph_vector_init_real +( 1 2 3 4 5 6 7 8 9 10 ) +Test igraph_vector_init_int +( 1 2 3 4 5 6 7 8 9 10 ) +Test igraph_vector_init_real +( 1 2 3 4 5 6 7 8 9 10 ) +Test igraph_vector_init_int +( 1 2 3 4 5 6 7 8 9 10 ) +Test order2 +( 0 8 1 7 6 2 3 5 4 ) +Test order2 on empty vector +( ) +Test filter_smaller, quite special.... +( 4 4 5 6 7 8 ) +( 1 2 3 4 4 4 4 5 6 7 8 ) +( 0 1 2 3 4 4 4 4 5 6 7 8 ) +Test rank +( 0 1 2 6 5 2 1 0 ) +( 1 3 5 7 6 4 2 0 ) +Test pair order +( 0 1 2 3 ) +Test fill +Test range +Test igraph_vector_int_init_range, igraph_vector_int_order1 +( 0 1 2 3 4 5 6 7 8 9 ) diff --git a/tests/unit/vector2.c b/tests/unit/vector2.c new file mode 100644 index 0000000..e9f2117 --- /dev/null +++ b/tests/unit/vector2.c @@ -0,0 +1,136 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +int main(void) { + + igraph_vector_t v1, v2, v3; + igraph_real_t min, max; + igraph_integer_t imin, imax; + int i; + + igraph_vector_init_range(&v1, 1, 11); + igraph_vector_init_range(&v2, 0, 10); + + igraph_vector_swap(&v1, &v2); + print_vector_format(&v1, stdout, "%g"); + print_vector_format(&v2, stdout, "%g"); + + igraph_vector_swap_elements(&v1, 0, 9); + igraph_vector_swap_elements(&v1, 3, 6); + print_vector_format(&v1, stdout, "%g"); + + igraph_vector_reverse(&v2); + print_vector_format(&v2, stdout, "%g"); + igraph_vector_reverse(&v2); + print_vector_format(&v2, stdout, "%g"); + + igraph_vector_destroy(&v1); + igraph_vector_destroy(&v2); + + igraph_vector_init(&v1, 10); + igraph_vector_init(&v2, 10); + igraph_vector_fill(&v1, 4); + igraph_vector_fill(&v2, 2); + + igraph_vector_add(&v1, &v2); + print_vector_format(&v1, stdout, "%g"); + igraph_vector_sub(&v1, &v2); + print_vector_format(&v1, stdout, "%g"); + igraph_vector_div(&v1, &v2); + print_vector_format(&v1, stdout, "%g"); + igraph_vector_mul(&v1, &v2); + print_vector_format(&v1, stdout, "%g"); + + igraph_vector_minmax(&v1, &min, &max); + igraph_vector_which_minmax(&v1, &imin, &imax); + printf("%g %g %" IGRAPH_PRId " %" IGRAPH_PRId "\n", min, max, imin, imax); + + igraph_vector_destroy(&v1); + igraph_vector_destroy(&v2); + + igraph_vector_init_range(&v1, 1, 11); + igraph_vector_init(&v2, 10); + for (i = 0; i < 10; i++) { + VECTOR(v2)[i] = 10 - i; + } + + igraph_vector_minmax(&v1, &min, &max); + igraph_vector_which_minmax(&v1, &imin, &imax); + printf("%g %g %" IGRAPH_PRId " %" IGRAPH_PRId "\n", min, max, imin, imax); + igraph_vector_minmax(&v2, &min, &max); + igraph_vector_which_minmax(&v2, &imin, &imax); + printf("%g %g %" IGRAPH_PRId " %" IGRAPH_PRId "\n", min, max, imin, imax); + + if (igraph_vector_isnull(&v1)) { + return 1; + } + igraph_vector_null(&v1); + if (!igraph_vector_isnull(&v1)) { + return 2; + } + + igraph_vector_destroy(&v1); + igraph_vector_destroy(&v2); + + igraph_vector_init_int(&v1, 10, 3, 5, 6, 6, 6, 7, 8, 8, 9, 10); + igraph_vector_init_int(&v2, 10, 1, 3, 3, 6, 6, 9, 12, 15, 17, 20); + igraph_vector_init(&v3, 0); + + igraph_vector_intersect_sorted(&v1, &v2, &v3); + print_vector_format(&v3, stdout, "%g"); + + igraph_vector_difference_sorted(&v1, &v2, &v3); + print_vector_format(&v3, stdout, "%g"); + igraph_vector_difference_sorted(&v2, &v1, &v3); + print_vector_format(&v3, stdout, "%g"); + igraph_vector_difference_sorted(&v2, &v2, &v3); + print_vector_format(&v3, stdout, "%g"); + + igraph_vector_destroy(&v1); + igraph_vector_destroy(&v2); + igraph_vector_destroy(&v3); + + igraph_vector_init_range(&v1, 0, 50); + igraph_vector_init_range(&v2, 20, 23); + igraph_vector_init(&v3, 0); + + igraph_vector_intersect_sorted(&v1, &v2, &v3); + print_vector_format(&v3, stdout, "%g"); + igraph_vector_difference_sorted(&v1, &v2, &v3); + print_vector_format(&v3, stdout, "%g"); + igraph_vector_difference_sorted(&v2, &v1, &v3); + print_vector_format(&v3, stdout, "%g"); + + igraph_vector_destroy(&v1); + igraph_vector_destroy(&v2); + igraph_vector_destroy(&v3); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/vector2.out b/tests/unit/vector2.out new file mode 100644 index 0000000..3dc75b4 --- /dev/null +++ b/tests/unit/vector2.out @@ -0,0 +1,19 @@ +( 0 1 2 3 4 5 6 7 8 9 ) +( 1 2 3 4 5 6 7 8 9 10 ) +( 9 1 2 6 4 5 3 7 8 0 ) +( 10 9 8 7 6 5 4 3 2 1 ) +( 1 2 3 4 5 6 7 8 9 10 ) +( 6 6 6 6 6 6 6 6 6 6 ) +( 4 4 4 4 4 4 4 4 4 4 ) +( 2 2 2 2 2 2 2 2 2 2 ) +( 4 4 4 4 4 4 4 4 4 4 ) +4 4 0 0 +1 10 0 9 +1 10 9 0 +( 3 6 6 9 ) +( 5 7 8 8 10 ) +( 1 12 15 17 20 ) +( ) +( 20 21 22 ) +( 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 ) +( ) diff --git a/tests/unit/vector3.c b/tests/unit/vector3.c new file mode 100644 index 0000000..5e0ab5d --- /dev/null +++ b/tests/unit/vector3.c @@ -0,0 +1,53 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2012 Gabor Csardi + 334 Harvard st, Cambridge MA, USA 02139 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + 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 "test_utilities.h" + +int main(void) { + igraph_vector_t v; + + igraph_vector_init_range(&v, 1, 1001); + IGRAPH_ASSERT(igraph_vector_capacity(&v) == 1000); + + igraph_vector_push_back(&v, 1001); + IGRAPH_ASSERT(igraph_vector_capacity(&v) == 2000); + + igraph_vector_resize_min(&v); + IGRAPH_ASSERT(igraph_vector_capacity(&v) == igraph_vector_size(&v)); + + igraph_vector_destroy(&v); + + /* regression test for #1479 -- calling resize_min() on an empty vector */ + igraph_vector_init_range(&v, 1, 1001); + igraph_vector_clear(&v); + igraph_vector_resize_min(&v); + IGRAPH_ASSERT(igraph_vector_capacity(&v) == 0); + IGRAPH_ASSERT(igraph_vector_size(&v) == 0); + igraph_vector_destroy(&v); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/vector4.c b/tests/unit/vector4.c new file mode 100644 index 0000000..4c206bb --- /dev/null +++ b/tests/unit/vector4.c @@ -0,0 +1,112 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_vector_t v; + igraph_vector_t v2; + igraph_real_t result; + igraph_bool_t result_bool; + igraph_real_t nan[3] = {1, IGRAPH_NAN, 2}; + igraph_real_t basic[3] = {1, 5, 2}; + igraph_real_t basic_small[3] = {0, -5, -2}; + + printf("Taking sum of squares of empty vector:\n"); + igraph_vector_view(&v, NULL, 0); + result = igraph_vector_sumsq(&v); + printf("%g\n", result); + + printf("Taking sum of squares of vector with NaN:\n"); + igraph_vector_view(&v, nan, 3); + result = igraph_vector_sumsq(&v); + print_real(stdout, result, "%g"); + + printf("\nTaking sum of squares of vector:\n"); + igraph_vector_view(&v, basic, 3); + result = igraph_vector_sumsq(&v); + printf("%g\n", result); + + printf("Checking if vector is equal to itself:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_view(&v2, basic, 3); + result_bool = igraph_vector_is_equal(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if vector is equal to other vector:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_view(&v2, nan, 3); + result_bool = igraph_vector_is_equal(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if all elements of vector are less than its own elements:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_view(&v2, basic, 3); + result_bool = igraph_vector_all_l(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if all elements of vector are less than vector with nan:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_view(&v2, nan, 3); + result_bool = igraph_vector_all_l(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if all elements of vector are less than vector with smaller elements:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_view(&v2, basic_small, 3); + result_bool = igraph_vector_all_l(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if all elements of vector are less than vector with larger elements:\n"); + igraph_vector_view(&v, basic_small, 3); + igraph_vector_view(&v2, basic, 3); + result_bool = igraph_vector_all_l(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if all elements of vector are greater than its own elements:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_view(&v2, basic, 3); + result_bool = igraph_vector_all_g(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if all elements of vector are greater than vector with nan:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_view(&v2, nan, 3); + result_bool = igraph_vector_all_g(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if all elements of vector are greater than vector with smaller elements:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_view(&v2, basic_small, 3); + result_bool = igraph_vector_all_g(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking if all elements of vector are greater than vector with larger elements:\n"); + igraph_vector_view(&v, basic_small, 3); + igraph_vector_view(&v2, basic, 3); + result_bool = igraph_vector_all_g(&v, &v2); + printf("%d\n", result_bool); + + printf("Checking vector_printf without nans:\n"); + igraph_vector_view(&v, basic, 3); + igraph_vector_printf(&v, "--%4g--"); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/vector4.out b/tests/unit/vector4.out new file mode 100644 index 0000000..11045e8 --- /dev/null +++ b/tests/unit/vector4.out @@ -0,0 +1,28 @@ +Taking sum of squares of empty vector: +0 +Taking sum of squares of vector with NaN: +NaN +Taking sum of squares of vector: +30 +Checking if vector is equal to itself: +1 +Checking if vector is equal to other vector: +0 +Checking if all elements of vector are less than its own elements: +0 +Checking if all elements of vector are less than vector with nan: +0 +Checking if all elements of vector are less than vector with smaller elements: +0 +Checking if all elements of vector are less than vector with larger elements: +1 +Checking if all elements of vector are greater than its own elements: +0 +Checking if all elements of vector are greater than vector with nan: +0 +Checking if all elements of vector are greater than vector with smaller elements: +1 +Checking if all elements of vector are greater than vector with larger elements: +0 +Checking vector_printf without nans: +-- 1-- -- 5-- -- 2-- diff --git a/tests/unit/vector_list.c b/tests/unit/vector_list.c new file mode 100644 index 0000000..14bcf55 --- /dev/null +++ b/tests/unit/vector_list.c @@ -0,0 +1,228 @@ +/* -*- 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 + +*/ + +#include +#include + +#include "test_utilities.h" + +int main(void) { + igraph_vector_int_list_t list, list2; + igraph_vector_int_t v; + igraph_vector_int_t* v_ptr; + igraph_integer_t i; + + printf("Initialise empty vector list\n"); + igraph_vector_int_list_init(&list, 0); + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Initialise vector list of length 10\n"); + igraph_vector_int_list_init(&list, 10); + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_get_ptr and igraph_vector_int_list_size\n"); + igraph_vector_int_list_init(&list, 10); + for (i = 0; i < igraph_vector_int_list_size(&list); i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), 10 - i); + } + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_reserve and igraph_vector_int_list_push_back_new\n"); + igraph_vector_int_list_init(&list, 0); + igraph_vector_int_list_reserve(&list, 10); + for (i = 0; i < 10; i++) { + igraph_vector_int_list_push_back_new(&list, /* item = */ 0); + } + + printf("Test igraph_vector_int_list_empty and igraph_vector_int_list_clear\n"); + IGRAPH_ASSERT(!igraph_vector_int_list_empty(&list)); + igraph_vector_int_list_clear(&list); + IGRAPH_ASSERT(igraph_vector_int_list_empty(&list)); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_list_set\n"); + igraph_vector_int_list_init(&list, 5); + for (i = 0; i < igraph_vector_int_list_size(&list); i++) { + igraph_vector_int_init(&v, 1); + VECTOR(v)[0] = 20 * i; + igraph_vector_int_list_set(&list, i, &v); /* ownership taken */ + } + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_tail_ptr, igraph_vector_int_list_pop_back\n"); + igraph_vector_int_list_init(&list, 10); + for (i = 0; i < igraph_vector_int_list_size(&list); i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), i + 1); + } + while (!igraph_vector_int_list_empty(&list)) { + print_vector_int(igraph_vector_int_list_tail_ptr(&list)); + v = igraph_vector_int_list_pop_back(&list); + /* v is now owned by us, not the vector_int_list */ + print_vector_int(&v); + igraph_vector_int_destroy(&v); + } + printf("\n"); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_resize, igraph_vector_int_list_sort\n"); + igraph_vector_int_list_init(&list, 20); + for (i = 0; i < 10; i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), 10 - i); + } + igraph_vector_int_list_resize(&list, 10); + igraph_vector_int_list_sort(&list, igraph_vector_int_lex_cmp); + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_remove\n"); + igraph_vector_int_list_init(&list, 10); + for (i = 0; i < 10; i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), i + 1); + } + igraph_vector_int_list_remove(&list, 9, /* item = */ &v); + print_vector_int(&v); /* v is now owned by us */ + igraph_vector_int_destroy(&v); + igraph_vector_int_list_remove(&list, 0, /* item = */ &v); + print_vector_int(&v); /* v is now owned by us */ + igraph_vector_int_destroy(&v); + igraph_vector_int_list_remove(&list, 4, /* item = */ &v); + print_vector_int(&v); /* v is now owned by us */ + igraph_vector_int_destroy(&v); + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_remove_fast\n"); + igraph_vector_int_list_init(&list, 10); + for (i = 0; i < 10; i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), i + 1); + } + igraph_vector_int_list_remove_fast(&list, 9, /* item = */ &v); + print_vector_int(&v); /* v is now owned by us */ + igraph_vector_int_destroy(&v); + igraph_vector_int_list_remove_fast(&list, 0, /* item = */ &v); + print_vector_int(&v); /* v is now owned by us */ + igraph_vector_int_destroy(&v); + igraph_vector_int_list_remove_fast(&list, 4, /* item = */ &v); + print_vector_int(&v); /* v is now owned by us */ + igraph_vector_int_destroy(&v); + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_discard\n"); + igraph_vector_int_list_init(&list, 10); + for (i = 0; i < 10; i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), i + 1); + } + igraph_vector_int_list_discard(&list, 9); + igraph_vector_int_list_discard(&list, 0); + igraph_vector_int_list_discard(&list, 4); + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_discard_fast\n"); + igraph_vector_int_list_init(&list, 10); + for (i = 0; i < 10; i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), i + 1); + } + igraph_vector_int_list_discard_fast(&list, 9); + igraph_vector_int_list_discard_fast(&list, 0); + igraph_vector_int_list_discard_fast(&list, 4); + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_swap and igraph_vector_int_list_swap_elements\n"); + igraph_vector_int_list_init(&list, 5); + igraph_vector_int_list_init(&list2, 10); + for (i = 0; i < igraph_vector_int_list_size(&list); i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), 20 * i); + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list2, i * 2), i); + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list2, i * 2 + 1), 2 * i); + } + igraph_vector_int_list_swap(&list, &list2); + igraph_vector_int_list_swap_elements(&list, 9, 8); + igraph_vector_int_list_swap_elements(&list, 3, 6); + print_vector_int_list(&list); + print_vector_int_list(&list2); + igraph_vector_int_list_destroy(&list); + igraph_vector_int_list_destroy(&list2); + + printf("Test igraph_vector_int_list_replace\n"); + igraph_vector_int_list_init(&list, 3); + for (i = 0; i < 3; i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), i + 1); + } + igraph_vector_int_init(&v, 3); + VECTOR(v)[0] = 77; VECTOR(v)[1] = 42; VECTOR(v)[2] = 317; + igraph_vector_int_list_replace(&list, 1, &v); + print_vector_int_list(&list); + print_vector_int(&v); + igraph_vector_int_destroy(&v); + igraph_vector_int_list_destroy(&list); + + printf("Test igraph_vector_int_list_insert, igraph_vector_int_list_insert_new and igraph_vector_int_list_insert_copy\n"); + igraph_vector_int_list_init(&list, 3); + for (i = 0; i < 3; i++) { + igraph_vector_int_push_back(igraph_vector_int_list_get_ptr(&list, i), i + 1); + } + + igraph_vector_int_list_insert_new(&list, 2, &v_ptr); + igraph_vector_int_push_back(v_ptr, 13); + igraph_vector_int_push_back(v_ptr, 61); + + igraph_vector_int_init(&v, 3); + VECTOR(v)[0] = 77; VECTOR(v)[1] = 42; VECTOR(v)[2] = 317; + igraph_vector_int_list_insert(&list, 1, &v); + + igraph_vector_int_init(&v, 2); + VECTOR(v)[0] = 11; VECTOR(v)[1] = -3; + igraph_vector_int_list_insert_copy(&list, 1, &v); + igraph_vector_int_push_back(&v, -51); + igraph_vector_int_destroy(&v); + + print_vector_int_list(&list); + igraph_vector_int_list_destroy(&list); + + printf("Test errors\n"); + igraph_set_error_handler(igraph_error_handler_ignore); + igraph_vector_int_list_init(&list, 10); + IGRAPH_ASSERT( + igraph_vector_int_list_remove(&list, 17, /* item = */ &v) == IGRAPH_EINVAL + ); + IGRAPH_ASSERT( + igraph_vector_int_list_remove(&list, -2, /* item = */ &v) == IGRAPH_EINVAL + ); + IGRAPH_ASSERT( + igraph_vector_int_list_remove_fast(&list, 17, /* item = */ &v) == IGRAPH_EINVAL + ); + IGRAPH_ASSERT( + igraph_vector_int_list_remove_fast(&list, -2, /* item = */ &v) == IGRAPH_EINVAL + ); + igraph_vector_int_list_destroy(&list); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/vector_list.out b/tests/unit/vector_list.out new file mode 100644 index 0000000..c08eb00 --- /dev/null +++ b/tests/unit/vector_list.out @@ -0,0 +1,157 @@ +Initialise empty vector list +{ +} +Initialise vector list of length 10 +{ + 0: ( ) + 1: ( ) + 2: ( ) + 3: ( ) + 4: ( ) + 5: ( ) + 6: ( ) + 7: ( ) + 8: ( ) + 9: ( ) +} +Test igraph_vector_int_list_get_ptr and igraph_vector_int_list_size +{ + 0: ( 10 ) + 1: ( 9 ) + 2: ( 8 ) + 3: ( 7 ) + 4: ( 6 ) + 5: ( 5 ) + 6: ( 4 ) + 7: ( 3 ) + 8: ( 2 ) + 9: ( 1 ) +} +Test igraph_vector_int_list_reserve and igraph_vector_int_list_push_back_new +Test igraph_vector_int_list_empty and igraph_vector_int_list_clear +Test igraph_vector_list_set +{ + 0: ( 0 ) + 1: ( 20 ) + 2: ( 40 ) + 3: ( 60 ) + 4: ( 80 ) +} +Test igraph_vector_int_list_tail_ptr, igraph_vector_int_list_pop_back +( 10 ) +( 10 ) +( 9 ) +( 9 ) +( 8 ) +( 8 ) +( 7 ) +( 7 ) +( 6 ) +( 6 ) +( 5 ) +( 5 ) +( 4 ) +( 4 ) +( 3 ) +( 3 ) +( 2 ) +( 2 ) +( 1 ) +( 1 ) + +Test igraph_vector_int_list_resize, igraph_vector_int_list_sort +{ + 0: ( 1 ) + 1: ( 2 ) + 2: ( 3 ) + 3: ( 4 ) + 4: ( 5 ) + 5: ( 6 ) + 6: ( 7 ) + 7: ( 8 ) + 8: ( 9 ) + 9: ( 10 ) +} +Test igraph_vector_int_list_remove +( 10 ) +( 1 ) +( 6 ) +{ + 0: ( 2 ) + 1: ( 3 ) + 2: ( 4 ) + 3: ( 5 ) + 4: ( 7 ) + 5: ( 8 ) + 6: ( 9 ) +} +Test igraph_vector_int_list_remove_fast +( 10 ) +( 1 ) +( 5 ) +{ + 0: ( 9 ) + 1: ( 2 ) + 2: ( 3 ) + 3: ( 4 ) + 4: ( 8 ) + 5: ( 6 ) + 6: ( 7 ) +} +Test igraph_vector_int_list_discard +{ + 0: ( 2 ) + 1: ( 3 ) + 2: ( 4 ) + 3: ( 5 ) + 4: ( 7 ) + 5: ( 8 ) + 6: ( 9 ) +} +Test igraph_vector_int_list_discard_fast +{ + 0: ( 9 ) + 1: ( 2 ) + 2: ( 3 ) + 3: ( 4 ) + 4: ( 8 ) + 5: ( 6 ) + 6: ( 7 ) +} +Test igraph_vector_int_list_swap and igraph_vector_int_list_swap_elements +{ + 0: ( 0 ) + 1: ( 0 ) + 2: ( 1 ) + 3: ( 3 ) + 4: ( 2 ) + 5: ( 4 ) + 6: ( 2 ) + 7: ( 6 ) + 8: ( 8 ) + 9: ( 4 ) +} +{ + 0: ( 0 ) + 1: ( 20 ) + 2: ( 40 ) + 3: ( 60 ) + 4: ( 80 ) +} +Test igraph_vector_int_list_replace +{ + 0: ( 1 ) + 1: ( 77 42 317 ) + 2: ( 3 ) +} +( 2 ) +Test igraph_vector_int_list_insert, igraph_vector_int_list_insert_new and igraph_vector_int_list_insert_copy +{ + 0: ( 1 ) + 1: ( 11 -3 ) + 2: ( 77 42 317 ) + 3: ( 2 ) + 4: ( 13 61 ) + 5: ( 3 ) +} +Test errors diff --git a/tests/unit/vector_ptr.c b/tests/unit/vector_ptr.c new file mode 100644 index 0000000..72cd929 --- /dev/null +++ b/tests/unit/vector_ptr.c @@ -0,0 +1,285 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +igraph_vector_ptr_t custom_destructor_stack; + +void custom_destructor(void* ptr) { + igraph_vector_ptr_push_back(&custom_destructor_stack, ptr); +} + +int main(void) { + + igraph_vector_ptr_t v1, v2; + igraph_vector_ptr_t v3 = IGRAPH_VECTOR_PTR_NULL; + int i; + void ** ptr; + int d1 = 1, d2 = 2, d3 = 3, d4 = 4, d5 = 5; + char *block1 = 0, *block2 = 0; + + /* igraph_vector_ptr_init, igraph_vector_ptr_destroy */ + igraph_vector_ptr_init(&v1, 10); + igraph_vector_ptr_destroy(&v1); + igraph_vector_ptr_init(&v1, 0); + igraph_vector_ptr_destroy(&v1); + + /* igraph_vector_ptr_free_all, igraph_vector_ptr_destroy_all */ + igraph_vector_ptr_init(&v1, 5); + for (i = 0; i < igraph_vector_ptr_size(&v1); i++) { + VECTOR(v1)[i] = (void*)malloc(i * 10); + } + igraph_vector_ptr_free_all(&v1); + for (i = 0; i < igraph_vector_ptr_size(&v1); i++) { + VECTOR(v1)[i] = (void*)malloc(i * 10); + } + igraph_vector_ptr_destroy_all(&v1); + + /* igraph_vector_ptr_reserve */ + igraph_vector_ptr_init(&v1, 0); + igraph_vector_ptr_reserve(&v1, 5); + igraph_vector_ptr_reserve(&v1, 15); + igraph_vector_ptr_reserve(&v1, 1); + igraph_vector_ptr_reserve(&v1, 0); + igraph_vector_ptr_destroy(&v1); + + /* igraph_vector_ptr_empty, igraph_vector_ptr_clear */ + igraph_vector_ptr_init(&v1, 10); + if (igraph_vector_ptr_empty(&v1)) { + return 1; + } + igraph_vector_ptr_clear(&v1); + if (!igraph_vector_ptr_empty(&v1)) { + return 2; + } + + /* igraph_vector_ptr_size */ + if (igraph_vector_ptr_size(&v1) != 0) { + return 3; + } + igraph_vector_ptr_resize(&v1, 10); + if (igraph_vector_ptr_size(&v1) != 10) { + return 4; + } + igraph_vector_ptr_destroy(&v1); + + /* igraph_vector_ptr_push_back */ + igraph_vector_ptr_init(&v1, 0); + for (i = 0; i < 10; i++) { + igraph_vector_ptr_push_back(&v1, (void*)malloc(i * 10)); + } + igraph_vector_ptr_destroy_all(&v1); + + /* igraph_vector_ptr_get */ + igraph_vector_ptr_init(&v1, 5); + VECTOR(v1)[0] = &d1; + VECTOR(v1)[1] = &d2; + VECTOR(v1)[2] = &d3; + VECTOR(v1)[3] = &d4; + VECTOR(v1)[4] = &d5; + if (igraph_vector_ptr_get(&v1, 0) != &d1) { + return 5; + } + if (igraph_vector_ptr_get(&v1, 1) != &d2) { + return 6; + } + if (igraph_vector_ptr_get(&v1, 2) != &d3) { + return 7; + } + if (igraph_vector_ptr_get(&v1, 3) != &d4) { + return 8; + } + if (igraph_vector_ptr_get(&v1, 4) != &d5) { + return 9; + } + igraph_vector_ptr_destroy(&v1); + + /* igraph_vector_ptr_set */ + igraph_vector_ptr_init(&v1, 5); + igraph_vector_ptr_set(&v1, 0, &d1); + igraph_vector_ptr_set(&v1, 1, &d2); + igraph_vector_ptr_set(&v1, 2, &d3); + igraph_vector_ptr_set(&v1, 3, &d4); + igraph_vector_ptr_set(&v1, 4, &d5); + if (igraph_vector_ptr_get(&v1, 0) != &d1) { + return 5; + } + if (igraph_vector_ptr_get(&v1, 1) != &d2) { + return 6; + } + if (igraph_vector_ptr_get(&v1, 2) != &d3) { + return 7; + } + if (igraph_vector_ptr_get(&v1, 3) != &d4) { + return 8; + } + if (igraph_vector_ptr_get(&v1, 4) != &d5) { + return 9; + } + igraph_vector_ptr_destroy(&v1); + + /* igraph_vector_ptr_null */ + igraph_vector_ptr_init(&v1, 5); + igraph_vector_ptr_set(&v1, 0, &d1); + igraph_vector_ptr_set(&v1, 1, &d2); + igraph_vector_ptr_set(&v1, 2, &d3); + igraph_vector_ptr_set(&v1, 3, &d4); + igraph_vector_ptr_set(&v1, 4, &d5); + igraph_vector_ptr_null(&v1); + for (i = 0; i < igraph_vector_ptr_size(&v1); i++) { + if (VECTOR(v1)[i] != 0) { + return 10; + } + } + igraph_vector_ptr_destroy(&v1); + + /* igraph_vector_ptr_resize */ + igraph_vector_ptr_init(&v1, 10); + igraph_vector_ptr_set(&v1, 0, &d1); + igraph_vector_ptr_set(&v1, 1, &d2); + igraph_vector_ptr_set(&v1, 2, &d3); + igraph_vector_ptr_set(&v1, 3, &d4); + igraph_vector_ptr_set(&v1, 4, &d5); + igraph_vector_ptr_resize(&v1, 10); + igraph_vector_ptr_resize(&v1, 15); + igraph_vector_ptr_resize(&v1, 5); + if (igraph_vector_ptr_size(&v1) != 5) { + return 11; + } + if (igraph_vector_ptr_get(&v1, 0) != &d1) { + return 12; + } + if (igraph_vector_ptr_get(&v1, 1) != &d2) { + return 13; + } + if (igraph_vector_ptr_get(&v1, 2) != &d3) { + return 14; + } + if (igraph_vector_ptr_get(&v1, 3) != &d4) { + return 15; + } + if (igraph_vector_ptr_get(&v1, 4) != &d5) { + return 16; + } + igraph_vector_ptr_destroy(&v1); + + /* igraph_vector_ptr_view */ + ptr = (void**) malloc(5 * sizeof(void*)); + igraph_vector_ptr_view(&v3, ptr, 5); + ptr[0] = &d1; + ptr[1] = &d2; + ptr[2] = &d3; + ptr[3] = &d4; + ptr[4] = &d5; + for (i = 0; i < igraph_vector_ptr_size(&v3); i++) { + if ( *((int*)VECTOR(v3)[i]) != i + 1) { + return 17; + } + } + + /* igraph_vector_ptr_init_array */ + igraph_vector_ptr_init_array(&v1, ptr, 5); + for (i = 0; i < igraph_vector_ptr_size(&v1); i++) { + if ( *((int*)VECTOR(v1)[i]) != i + 1) { + return 18; + } + } + + /* igraph_vector_ptr_copy_to */ + igraph_vector_ptr_copy_to(&v1, ptr); + for (i = 0; i < igraph_vector_ptr_size(&v1); i++) { + if ( *((int*)ptr[i]) != i + 1) { + return 19; + } + } + free(ptr); + igraph_vector_ptr_destroy(&v1); + + /* igraph_vector_ptr_copy */ + igraph_vector_ptr_init(&v1, 5); + igraph_vector_ptr_set(&v1, 0, &d1); + igraph_vector_ptr_set(&v1, 1, &d2); + igraph_vector_ptr_set(&v1, 2, &d3); + igraph_vector_ptr_set(&v1, 3, &d4); + igraph_vector_ptr_set(&v1, 4, &d5); + igraph_vector_ptr_init_copy(&v2, &v1); + igraph_vector_ptr_destroy(&v1); + for (i = 0; i < igraph_vector_ptr_size(&v2); i++) { + if ( *((int*)VECTOR(v2)[i]) != i + 1) { + return 20; + } + } + + /* igraph_vector_ptr_remove */ + igraph_vector_ptr_remove(&v2, 0); + igraph_vector_ptr_remove(&v2, 3); + if ( *((int*)VECTOR(v2)[0]) != 2) { + return 21; + } + if ( *((int*)VECTOR(v2)[1]) != 3) { + return 22; + } + if ( *((int*)VECTOR(v2)[2]) != 4) { + return 23; + } + + igraph_vector_ptr_destroy(&v2); + + /* Testing destructor */ + igraph_vector_ptr_init(&custom_destructor_stack, 0); + igraph_vector_ptr_init(&v1, 2); + block1 = IGRAPH_CALLOC(32, char); + block2 = IGRAPH_CALLOC(64, char); + VECTOR(v1)[0] = block1; + VECTOR(v1)[1] = block2; + if (igraph_vector_ptr_get_item_destructor(&v1) != 0) { + return 24; + } + if (igraph_vector_ptr_set_item_destructor(&v1, &custom_destructor) != 0) { + return 25; + } + /* Okay, let's clear the vector. This should push the blocks in the + * custom destructor stack */ + igraph_vector_ptr_clear(&v1); + /* Put the blocks back and destroy the vector */ + igraph_vector_ptr_push_back(&v1, block1); + igraph_vector_ptr_push_back(&v1, block2); + igraph_vector_ptr_destroy_all(&v1); + + if (VECTOR(custom_destructor_stack)[0] != block1 || + VECTOR(custom_destructor_stack)[1] != block2 || + VECTOR(custom_destructor_stack)[2] != block1 || + VECTOR(custom_destructor_stack)[3] != block2 + ) { + return 26; + } + + igraph_vector_ptr_destroy(&custom_destructor_stack); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/vector_ptr_qsort_ind.out b/tests/unit/vector_ptr_qsort_ind.out new file mode 100644 index 0000000..013a691 --- /dev/null +++ b/tests/unit/vector_ptr_qsort_ind.out @@ -0,0 +1,12 @@ +( ) +( 2 8 6 0 9 1 5 3 7 4 ) +( 0 ) +( 1 ) +( 2 ) +( 3 ) +( 4 ) +( 5 ) +( 6 ) +( 7 ) +( 8 ) +( 9 ) diff --git a/tests/unit/vector_ptr_sort_ind.c b/tests/unit/vector_ptr_sort_ind.c new file mode 100644 index 0000000..f6784c1 --- /dev/null +++ b/tests/unit/vector_ptr_sort_ind.c @@ -0,0 +1,81 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +static int compare_first_items(const void* a, const void* b) { + igraph_vector_t *vec1 = (igraph_vector_t*) a; + igraph_vector_t *vec2 = (igraph_vector_t*) b; + + return VECTOR(*vec1)[0] - VECTOR(*vec2)[0]; +} + +int main(void) { + igraph_vector_ptr_t vectors; + igraph_vector_t* vec; + igraph_vector_int_t indices; + int values[] = { 3, 5, 0, 7, 9, 6, 2, 8, 1, 4, -1 }; + int i, *ptr; + + /* Special case: empty vector */ + igraph_vector_ptr_init(&vectors, 0); + igraph_vector_int_init(&indices, 0); + igraph_vector_ptr_sort_ind(&vectors, &indices, compare_first_items); + print_vector_int(&indices); + igraph_vector_int_destroy(&indices); + igraph_vector_ptr_destroy_all(&vectors); + + /* Create vectors of length 1, each containing a value from 'values', and + * put them in a vector of pointers */ + igraph_vector_ptr_init(&vectors, 0); + for (ptr = values; *ptr >= 0; ptr++) { + vec = IGRAPH_CALLOC(1, igraph_vector_t); + igraph_vector_init(vec, 1); + VECTOR(*vec)[0] = *ptr; + igraph_vector_ptr_push_back(&vectors, vec); + } + + /* Sort the vector of vectors by the first item of each vector, and get + * the index vector */ + igraph_vector_int_init(&indices, 0); + igraph_vector_ptr_sort_ind(&vectors, &indices, compare_first_items); + print_vector_int(&indices); + + /* Permute the vector of vectors by the index vector */ + igraph_vector_ptr_permute(&vectors, &indices); + + /* Print and clean up */ + for (i = 0; i < igraph_vector_ptr_size(&vectors); i++) { + print_vector(VECTOR(vectors)[i]); + igraph_vector_destroy(VECTOR(vectors)[i]); + } + igraph_vector_ptr_destroy_all(&vectors); + igraph_vector_int_destroy(&indices); + + /* Check finalizer stack */ + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/vector_qsort_ind.c b/tests/unit/vector_qsort_ind.c new file mode 100644 index 0000000..cb30c76 --- /dev/null +++ b/tests/unit/vector_qsort_ind.c @@ -0,0 +1,64 @@ +/* -*- 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 +#include + +#include "test_utilities.h" + +int main(void) { + igraph_vector_t vector; + igraph_vector_int_t indices; + igraph_real_t values[] = { 87, 23, 8, 82, 94, 56, 36, 33, 76, 66 }; + igraph_real_t values2[] = { 87, 23, 8, 82, 94, 56, 36, 33, 76, 66 }; + + /* Special case: empty vector */ + igraph_vector_init(&vector, 0); + igraph_vector_int_init(&indices, 0); + igraph_vector_qsort_ind(&vector, &indices, IGRAPH_ASCENDING); + print_vector_int(&indices); + igraph_vector_int_destroy(&indices); + igraph_vector_destroy(&vector); + + /* Non-empty vector, descending */ + igraph_vector_view(&vector, values, sizeof(values) / sizeof(values[0])); + igraph_vector_int_init(&indices, 0); + igraph_vector_qsort_ind(&vector, &indices, IGRAPH_DESCENDING); + print_vector_int(&indices); + + /* Non-empty vector, ascending */ + igraph_vector_view(&vector, values2, sizeof(values2) / sizeof(values2[0])); + igraph_vector_qsort_ind(&vector, &indices, IGRAPH_ASCENDING); + print_vector_int(&indices); + + /* Permute the vector by the index vector */ + igraph_vector_permute(&vector, &indices); + print_vector(&vector); + + /* Print and clean up */ + igraph_vector_int_destroy(&indices); + + /* Check finalizer stack */ + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/vector_qsort_ind.out b/tests/unit/vector_qsort_ind.out new file mode 100644 index 0000000..9ca5dc0 --- /dev/null +++ b/tests/unit/vector_qsort_ind.out @@ -0,0 +1,4 @@ +( ) +( 4 0 3 8 9 5 6 7 1 2 ) +( 2 1 7 6 5 9 8 3 0 4 ) +( 8 23 33 36 56 66 76 82 87 94 ) diff --git a/tests/unit/vertex_selectors.c b/tests/unit/vertex_selectors.c new file mode 100644 index 0000000..27da778 --- /dev/null +++ b/tests/unit/vertex_selectors.c @@ -0,0 +1,116 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +void check(igraph_t *graph, igraph_vs_t *vs) { + igraph_vit_t vit; + + IGRAPH_ASSERT(igraph_vit_create(graph, *vs, &vit) == IGRAPH_SUCCESS); + for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + printf("%" IGRAPH_PRId "\n", IGRAPH_VIT_GET(vit)); + } +} + +int main(void) { + igraph_t g, g_no_vertices, g_no_edges; + igraph_vs_t vs, vs_copy; + igraph_vector_int_t v; + igraph_vit_t vit; + + igraph_small(&g, 5, IGRAPH_DIRECTED, 0,1, 0,2, 1,1, 1,3, 2,0, 2,3, 3,4, -1); + igraph_small(&g_no_vertices, 0, IGRAPH_UNDIRECTED, -1); + igraph_small(&g_no_edges, 5, IGRAPH_UNDIRECTED, -1); + + printf("Checking vs_none vertex selector:\n"); + IGRAPH_ASSERT(igraph_vs_none(&vs) == IGRAPH_SUCCESS); + check(&g, &vs); + check(&g_no_edges, &vs); + check(&g_no_vertices, &vs); + + igraph_set_error_handler(igraph_error_handler_ignore); + + printf("Checking vector selector:\n"); + igraph_vector_int_init_int(&v, 3, 2, 3, 4); + IGRAPH_ASSERT(igraph_vs_vector(&vs, &v) == IGRAPH_SUCCESS); + printf("Some graph:\n"); + check(&g, &vs); + printf("Edgeless graph:\n"); + check(&g_no_edges, &vs); + printf("Graph without vertices should fail.\n"); + IGRAPH_ASSERT(igraph_vit_create(&g_no_vertices, vs, &vit) == IGRAPH_EINVVID); + igraph_vector_int_destroy(&v); + + printf("Vertex selector with negative index should fail\n"); + igraph_vector_int_init_int(&v, 3, -2, 3, 4); + IGRAPH_ASSERT(igraph_vs_vector(&vs, &v) == IGRAPH_SUCCESS); + IGRAPH_ASSERT(igraph_vit_create(&g, vs, &vit) == IGRAPH_EINVVID); + igraph_vector_int_destroy(&v); + + printf("Checking copy vector selector:\n"); + igraph_vector_int_init_int(&v, 3, 2, 3, 4); + IGRAPH_ASSERT(igraph_vs_vector_copy(&vs, &v) == IGRAPH_SUCCESS); + printf("Some graph:\n"); + check(&g, &vs); + printf("Edgeless graph:\n"); + check(&g_no_edges, &vs); + printf("Copy of vs:\n"); + igraph_vs_copy(&vs_copy, &vs); + check(&g_no_edges, &vs_copy); + igraph_vs_destroy(&vs_copy); + + printf("Graph without vertices should fail.\n"); + IGRAPH_ASSERT(igraph_vit_create(&g_no_vertices, vs, &vit) == IGRAPH_EINVVID); + IGRAPH_ASSERT(igraph_vs_type(&vs) == IGRAPH_VS_VECTOR); + igraph_vs_destroy(&vs); + + printf("As vector should give all 5 vertices:\n"); + igraph_vs_all(&vs); + igraph_vs_as_vector(&g, vs, &v); + igraph_vector_int_print(&v); + + printf("As vector should give 0 vertices:\n"); + igraph_vs_as_vector(&g_no_vertices, vs, &v); + igraph_vector_int_print(&v); + + printf("Checking vs_range:\n"); + igraph_vs_range(&vs, 2, 5); + check(&g, &vs); + CHECK_ERROR(igraph_vit_create(&g_no_vertices, vs, &vit), IGRAPH_EINVAL); + + printf("Checking vss_range using vs_range parameters:\n"); + vs = igraph_vss_range(2, 5); + check(&g, &vs); + CHECK_ERROR(igraph_vit_create(&g_no_vertices, vs, &vit), IGRAPH_EINVAL); + + printf("Checking whether vss_range accepts an empty range.\n"); + vs = igraph_vss_range(2, 2); + check(&g, &vs); + CHECK_ERROR(igraph_vit_create(&g_no_vertices, vs, &vit), IGRAPH_EINVAL); + + igraph_destroy(&g); + igraph_destroy(&g_no_vertices); + igraph_destroy(&g_no_edges); + + igraph_vector_int_destroy(&v); + igraph_vs_destroy(&vs); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tests/unit/vertex_selectors.out b/tests/unit/vertex_selectors.out new file mode 100644 index 0000000..e78c825 --- /dev/null +++ b/tests/unit/vertex_selectors.out @@ -0,0 +1,39 @@ +Checking vs_none vertex selector: +Checking vector selector: +Some graph: +2 +3 +4 +Edgeless graph: +2 +3 +4 +Graph without vertices should fail. +Vertex selector with negative index should fail +Checking copy vector selector: +Some graph: +2 +3 +4 +Edgeless graph: +2 +3 +4 +Copy of vs: +2 +3 +4 +Graph without vertices should fail. +As vector should give all 5 vertices: +0 1 2 3 4 +As vector should give 0 vertices: + +Checking vs_range: +2 +3 +4 +Checking vss_range using vs_range parameters: +2 +3 +4 +Checking whether vss_range accepts an empty range. diff --git a/tests/unit/watts_strogatz_game.c b/tests/unit/watts_strogatz_game.c new file mode 100644 index 0000000..ea87d39 --- /dev/null +++ b/tests/unit/watts_strogatz_game.c @@ -0,0 +1,125 @@ +/* + IGraph library. + Copyright (C) 2011-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, see . +*/ + +#include +#include + +#include "test_utilities.h" + +#define N 1000 + +igraph_bool_t has_loops(const igraph_t *graph) { + igraph_bool_t res; + igraph_has_loop(graph, &res); + return res; +} + +igraph_bool_t has_multiple(const igraph_t *graph) { + igraph_bool_t res; + igraph_has_multiple(graph, &res); + return res; +} + +#define ERR() do { \ + printf("Seed: %" IGRAPH_PRIu "\n", seed); \ + print_graph(&ws); \ + } while (0) + +#define SEED() do { \ + seed=igraph_rng_get_integer(igraph_rng_default(), 1, 10000); \ + igraph_rng_seed(igraph_rng_default(), seed); \ + } while (0) + +int main(void) { + + igraph_t ws; + igraph_bool_t sim, seen_loops, seen_multiple; + igraph_integer_t i; + igraph_uint_t seed = 1305473657; + + igraph_rng_seed(igraph_rng_default(), seed); + + /* No loops, no multiple edges */ + for (i = 0; i < N; i++) { + SEED(); + igraph_watts_strogatz_game(&ws, /*dim=*/ 1, /*size=*/ 5, /*nei=*/ 1, + /*p=*/ 0.5, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + igraph_is_simple(&ws, &sim); + if (!sim) { + ERR(); + return 1; + } + if (has_loops(&ws)) { + ERR(); + return 1; + } + if (has_multiple(&ws)) { + ERR(); + return 2; + } + igraph_destroy(&ws); + } + + /* No loops, multiple edges possible */ + seen_multiple = 0; + for (i = 0; i < N; i++) { + SEED(); + igraph_watts_strogatz_game(&ws, /*dim=*/ 1, /*size=*/ 5, /*nei=*/ 1, + /*p=*/ 0.5, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE); + if (has_loops(&ws)) { + ERR(); + return 3; + } + seen_multiple = seen_multiple || has_multiple(&ws); + igraph_destroy(&ws); + } + /* This might actually happen */ + /* if (!seen_multiple) { return 4; } */ + + /* Loops possible, no multiple edges */ + seen_loops = 0; + for (i = 0; i < N; i++) { + SEED(); + igraph_watts_strogatz_game(&ws, /*dim=*/ 1, /*size=*/ 5, /*nei=*/ 1, + /*p=*/ 0.5, IGRAPH_LOOPS, IGRAPH_NO_MULTIPLE); + if (has_multiple(&ws)) { + return 5; + } + seen_loops = seen_loops || has_loops(&ws); + igraph_destroy(&ws); + } + /* This might actually happen */ + /* if (!seen_loops) { return 6; } */ + + /* Both loops and multiple edges are possible */ + for (i = 0; i < N; i++) { + SEED(); + igraph_watts_strogatz_game(&ws, /*dim=*/ 1, /*size=*/ 5, /*nei=*/ 1, + /*p=*/ 0.5, IGRAPH_LOOPS, IGRAPH_MULTIPLE); + seen_loops = seen_loops || has_loops(&ws); + seen_multiple = seen_multiple || has_multiple(&ws); + igraph_destroy(&ws); + } + /* This might actually happen */ + /* if (!seen_loops) { return 7; } */ + /* if (!seen_multiple) { return 8; } */ + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/wikti_en_V_syn.elist b/tests/unit/wikti_en_V_syn.elist new file mode 100644 index 0000000..2b0e794 --- /dev/null +++ b/tests/unit/wikti_en_V_syn.elist @@ -0,0 +1,8293 @@ +0 1 +0 2 +0 220 +0 229 +0 1010 +1 2 +1 12 +1 3849 +3 4 +3 5 +3 6 +3 7 +3 8 +3 9 +3 10 +3 11 +4 7 +7 671 +8 2285 +9 671 +10 671 +10 939 +10 1355 +10 1844 +10 2546 +10 3054 +11 671 +12 3849 +13 14 +13 15 +13 16 +13 17 +13 2270 +14 1795 +14 2270 +14 2531 +15 2270 +17 1466 +18 19 +18 20 +18 21 +18 22 +18 23 +18 24 +18 25 +18 26 +18 2041 +21 1970 +22 838 +24 5101 +25 2531 +25 5101 +27 1079 +27 2041 +27 2126 +27 3081 +28 29 +28 30 +28 31 +28 32 +28 33 +28 34 +28 35 +28 36 +28 37 +28 38 +29 31 +29 35 +29 881 +29 882 +29 883 +29 884 +29 885 +38 3930 +39 40 +39 41 +39 1251 +42 6340 +43 44 +43 45 +43 46 +44 318 +47 48 +47 49 +47 50 +47 5319 +48 50 +48 51 +48 3380 +50 51 +50 52 +50 53 +50 54 +50 55 +50 79 +51 3380 +52 58 +52 59 +52 60 +52 61 +52 62 +52 63 +52 64 +52 65 +52 66 +52 67 +52 68 +52 69 +52 70 +52 71 +52 72 +52 73 +52 74 +52 75 +52 76 +52 77 +52 78 +52 1697 +53 3380 +56 7312 +57 1376 +58 61 +58 64 +58 67 +58 73 +58 2730 +60 3371 +61 3371 +66 68 +66 70 +66 1949 +66 3641 +66 4869 +66 4870 +66 4871 +66 4872 +74 2543 +74 4679 +75 76 +76 389 +76 665 +76 827 +76 853 +76 883 +76 1017 +76 1152 +76 1416 +76 1468 +76 1469 +76 1470 +76 1471 +76 1472 +76 1473 +76 1474 +76 1475 +76 1476 +76 1477 +76 1478 +76 1479 +76 1480 +76 1481 +76 1482 +76 1483 +76 1484 +76 1485 +76 1486 +77 725 +78 5319 +80 2539 +80 3798 +81 2467 +82 83 +82 84 +82 85 +82 86 +82 87 +82 88 +82 89 +82 90 +82 91 +82 92 +82 93 +82 94 +82 95 +82 96 +82 97 +82 1006 +82 2140 +83 85 +83 87 +83 88 +83 90 +83 91 +83 92 +83 93 +83 95 +83 114 +83 115 +84 612 +85 2140 +85 4577 +88 90 +88 91 +88 95 +88 241 +88 4572 +88 5016 +89 1003 +89 1164 +89 1180 +89 1201 +89 1909 +89 2390 +89 4415 +89 5125 +90 93 +90 1201 +90 1341 +90 2390 +91 241 +91 5016 +92 130 +92 3619 +95 4055 +97 612 +97 2531 +97 2937 +97 3516 +97 4023 +97 4024 +97 4025 +97 4026 +97 4027 +97 4028 +97 4029 +97 4030 +97 4031 +97 5016 +98 99 +98 100 +99 100 +99 106 +99 3481 +99 3616 +99 5874 +100 3616 +100 4755 +101 102 +101 103 +101 104 +101 105 +101 106 +101 107 +101 1095 +101 4777 +102 103 +102 104 +102 312 +102 400 +102 1189 +102 1567 +102 2699 +102 2760 +102 3393 +102 3448 +102 4243 +102 5088 +103 2760 +103 3393 +104 107 +104 1498 +104 1567 +104 1999 +104 3892 +104 4777 +105 106 +105 107 +106 107 +106 3622 +106 3952 +106 4515 +107 3997 +107 4777 +108 6079 +109 110 +109 111 +109 112 +109 113 +109 1679 +109 2263 +116 117 +116 118 +117 355 +118 251 +118 2571 +119 5857 +120 4825 +121 122 +123 124 +123 125 +123 126 +123 127 +124 125 +124 126 +124 127 +124 1398 +125 126 +125 127 +126 127 +128 129 +130 138 +130 2975 +130 3618 +131 132 +132 380 +132 384 +132 385 +132 386 +132 742 +132 1394 +132 1704 +132 2084 +132 2085 +132 2086 +132 3018 +132 3139 +132 3201 +132 3648 +132 3653 +132 3654 +132 3655 +132 4414 +132 4987 +132 5041 +133 2975 +134 135 +134 136 +135 5623 +136 6204 +137 138 +137 139 +137 140 +137 141 +137 142 +137 143 +137 144 +137 145 +137 146 +137 147 +137 148 +137 149 +137 150 +137 151 +137 152 +137 153 +137 154 +137 155 +137 156 +137 157 +137 158 +137 159 +137 160 +137 161 +137 162 +137 163 +137 164 +137 165 +137 166 +137 167 +137 168 +137 169 +137 170 +137 171 +137 172 +137 173 +137 174 +137 175 +137 176 +138 139 +138 140 +138 141 +138 142 +138 143 +138 185 +138 186 +138 187 +138 188 +138 189 +138 190 +138 191 +138 192 +138 193 +138 194 +138 195 +138 196 +138 197 +138 198 +138 199 +138 200 +138 201 +138 202 +138 203 +138 204 +138 205 +138 206 +138 207 +138 208 +138 209 +138 210 +138 211 +139 208 +140 142 +140 205 +140 208 +140 1704 +140 3960 +141 204 +141 3960 +142 143 +142 206 +159 297 +166 422 +166 423 +166 3804 +177 3930 +178 2725 +178 2728 +178 3816 +179 2120 +179 2122 +180 181 +181 4287 +181 6173 +181 6401 +181 6444 +181 7287 +181 7292 +182 445 +182 2260 +183 184 +185 1138 +185 1425 +185 1797 +185 2373 +185 2984 +185 3914 +187 1210 +187 1616 +187 4154 +188 597 +189 567 +189 1904 +189 2390 +189 4906 +190 1882 +190 2027 +190 4158 +191 1138 +191 1425 +191 3960 +194 1498 +195 1884 +198 211 +198 4920 +199 205 +202 621 +202 622 +202 623 +203 385 +203 1223 +203 3270 +204 567 +204 587 +204 1022 +204 1704 +204 1904 +204 2960 +205 1010 +205 1013 +205 1014 +205 1494 +205 1495 +205 1496 +206 2974 +207 354 +208 297 +208 1748 +208 3960 +210 597 +210 1138 +210 1425 +211 445 +211 1653 +211 3421 +212 6259 +213 1590 +214 498 +215 239 +215 240 +215 1153 +215 2672 +216 217 +216 218 +216 219 +216 220 +216 4766 +217 220 +217 2058 +219 220 +219 818 +219 819 +219 820 +219 821 +219 822 +219 823 +219 824 +219 825 +219 1722 +219 4766 +220 232 +220 283 +220 284 +220 285 +220 286 +220 287 +220 6453 +221 222 +221 223 +221 224 +221 225 +221 226 +221 227 +221 784 +221 3940 +222 3940 +223 1558 +224 1653 +224 1925 +224 3098 +225 226 +225 564 +225 1026 +225 1558 +225 2037 +225 2363 +225 3169 +225 6678 +226 227 +226 564 +226 1026 +226 1558 +226 1946 +226 1947 +226 2037 +227 1558 +227 2037 +228 1589 +228 3093 +228 6782 +229 230 +229 231 +229 232 +229 233 +229 234 +229 235 +229 236 +229 237 +229 238 +230 236 +230 1167 +230 3423 +230 3424 +232 233 +232 1010 +233 234 +233 235 +233 1563 +233 2064 +233 3091 +234 236 +234 4496 +236 4833 +239 240 +241 242 +241 243 +241 244 +241 245 +244 2390 +245 1254 +246 3094 +247 248 +249 250 +249 251 +249 252 +249 604 +250 251 +250 400 +250 965 +251 354 +251 393 +251 394 +251 395 +251 396 +251 397 +251 398 +251 399 +251 400 +251 401 +251 402 +251 985 +251 1105 +251 1126 +251 1918 +251 1980 +252 297 +253 254 +253 255 +253 256 +253 257 +253 258 +253 259 +253 260 +253 261 +253 262 +253 263 +253 264 +255 454 +255 1619 +255 1971 +257 523 +262 3571 +265 266 +265 267 +265 268 +265 269 +265 270 +265 271 +265 272 +265 273 +265 274 +265 275 +265 276 +265 277 +265 278 +265 279 +265 1821 +280 281 +280 282 +281 5678 +282 787 +282 1636 +282 2445 +282 6200 +283 6453 +285 6453 +286 2053 +288 839 +288 5033 +289 290 +289 291 +289 292 +289 293 +289 294 +289 295 +289 296 +289 297 +289 298 +289 299 +289 300 +289 301 +289 302 +289 303 +289 304 +289 305 +289 306 +289 307 +289 308 +289 706 +289 1201 +289 1722 +289 2059 +289 3615 +289 3935 +289 4539 +289 4554 +289 5254 +289 6307 +289 7197 +289 7300 +290 306 +290 7300 +291 306 +291 4554 +291 4555 +291 7300 +292 325 +294 3279 +296 4539 +296 4540 +296 7197 +297 298 +297 389 +297 404 +297 408 +297 409 +297 478 +297 581 +297 612 +297 614 +297 617 +297 700 +297 972 +297 1180 +297 1305 +297 1318 +297 1353 +297 1371 +297 1419 +297 1431 +297 1487 +297 1721 +297 1722 +297 1723 +297 1724 +297 1725 +297 1726 +297 1727 +297 1728 +297 1729 +297 1730 +297 1731 +297 1732 +297 1733 +297 1734 +297 1735 +297 1736 +297 1737 +297 1738 +297 1739 +297 1740 +297 1741 +297 1742 +297 1743 +297 1744 +297 1745 +297 1746 +297 1747 +297 1748 +297 1749 +297 1750 +297 1751 +297 1752 +297 1753 +297 1754 +297 1755 +297 1756 +297 1757 +297 2733 +298 1722 +298 2733 +298 3935 +299 498 +299 1722 +306 4554 +306 7300 +309 310 +309 311 +312 313 +312 314 +312 315 +312 316 +312 317 +312 368 +313 625 +313 626 +316 3390 +317 1674 +318 400 +319 320 +319 321 +319 6552 +322 935 +322 6054 +323 324 +325 326 +325 327 +325 328 +325 329 +325 330 +325 331 +325 332 +326 438 +328 2041 +328 2833 +329 1079 +330 587 +330 2200 +333 2495 +333 2531 +334 400 +334 1402 +334 2650 +334 3156 +334 3453 +335 336 +337 338 +337 339 +337 340 +337 341 +337 342 +337 343 +337 344 +337 345 +337 346 +337 347 +337 348 +337 349 +337 350 +337 351 +337 352 +337 353 +338 339 +338 3852 +339 341 +339 342 +339 343 +339 346 +339 347 +339 351 +339 4119 +341 342 +341 346 +341 347 +342 343 +342 347 +342 349 +342 1597 +346 347 +351 3169 +354 355 +354 356 +354 357 +354 358 +356 600 +359 3508 +359 3510 +360 1180 +361 362 +362 2732 +363 672 +364 2488 +365 366 +366 4491 +367 2213 +368 369 +368 370 +369 370 +369 1047 +369 1239 +369 1240 +369 1241 +369 1242 +369 1243 +371 6782 +372 373 +372 374 +372 375 +372 376 +372 377 +372 378 +372 379 +373 374 +373 375 +373 376 +373 377 +373 378 +373 379 +373 4791 +374 375 +374 378 +374 379 +375 1223 +376 1223 +381 3117 +381 5859 +382 3117 +382 5215 +382 5217 +383 1400 +384 385 +384 386 +384 387 +384 388 +384 5641 +385 610 +385 1889 +385 3269 +385 3270 +385 3271 +386 387 +386 1889 +386 3270 +386 5640 +386 7109 +386 7110 +387 5359 +387 5641 +388 1010 +388 2958 +389 390 +389 391 +389 392 +389 496 +389 1126 +391 763 +392 2079 +393 1105 +393 1106 +394 672 +396 453 +396 623 +396 2344 +396 2571 +396 2792 +396 4217 +397 1105 +398 596 +398 1413 +399 400 +399 1188 +399 1925 +399 1980 +399 2213 +400 470 +400 581 +400 617 +400 666 +400 671 +400 672 +400 689 +400 706 +400 722 +400 767 +400 857 +400 905 +400 914 +400 956 +400 968 +400 1090 +400 1105 +400 1114 +400 1118 +400 1171 +400 1172 +400 1173 +400 1174 +400 1175 +400 1176 +400 1177 +400 1178 +400 1179 +400 1180 +400 1181 +400 1182 +400 1183 +400 1184 +400 1185 +400 1186 +400 1187 +400 1188 +400 1189 +400 1190 +400 1191 +400 1192 +400 1193 +400 1194 +400 1195 +400 1219 +400 1653 +400 2263 +400 3963 +401 454 +402 604 +402 1137 +402 1138 +403 861 +405 406 +405 407 +405 408 +405 409 +405 410 +405 4500 +406 407 +406 1037 +406 1038 +406 1040 +406 3643 +407 1038 +407 1039 +407 1040 +407 2515 +407 3877 +407 5069 +407 5070 +407 5071 +408 1253 +408 1755 +408 3687 +408 3978 +408 3979 +408 3980 +408 3981 +408 4590 +409 454 +409 1724 +410 3755 +411 412 +411 413 +411 414 +411 415 +411 416 +411 417 +411 418 +411 832 +411 1096 +411 3062 +411 3488 +411 4018 +412 414 +412 4676 +412 5769 +413 800 +413 2753 +413 4876 +414 1153 +414 1349 +414 1350 +414 1351 +414 1352 +414 2924 +415 1153 +417 1688 +417 4016 +417 4018 +418 2159 +418 4018 +419 420 +421 827 +422 423 +422 701 +422 702 +422 703 +422 704 +422 705 +422 2999 +424 425 +424 426 +424 427 +424 428 +424 429 +424 430 +424 1932 +424 2293 +425 3533 +426 427 +426 428 +426 1766 +426 1767 +426 1768 +427 428 +427 1609 +427 3488 +427 4014 +427 5101 +428 1768 +428 3488 +428 3533 +429 3798 +430 915 +430 5101 +430 5561 +431 432 +431 4226 +432 3762 +432 4226 +433 985 +433 1180 +433 1212 +434 597 +434 3645 +434 3646 +435 436 +435 437 +435 1936 +436 1070 +436 1936 +436 1939 +436 3488 +436 4059 +437 1936 +437 5016 +439 1234 +439 1679 +439 2263 +439 3412 +440 441 +440 6135 +442 3553 +443 535 +444 4580 +444 4583 +445 446 +445 447 +445 448 +445 449 +445 450 +445 451 +445 452 +445 453 +446 954 +446 3436 +447 3371 +447 3428 +448 3624 +448 3625 +448 4036 +448 4908 +449 2296 +452 2607 +453 591 +453 679 +453 1106 +453 1628 +453 1688 +453 1927 +453 2344 +453 2345 +453 2607 +453 3026 +453 3224 +453 3806 +453 5490 +453 5794 +454 455 +454 456 +454 457 +454 458 +454 459 +454 460 +454 461 +454 462 +454 463 +454 464 +454 465 +454 466 +454 467 +454 468 +454 469 +454 470 +454 471 +454 472 +454 473 +454 474 +454 475 +454 476 +454 477 +454 478 +454 479 +454 480 +454 481 +454 482 +457 2054 +459 3997 +460 2531 +462 542 +465 1434 +465 1649 +465 1650 +465 1651 +465 1652 +465 6577 +469 4893 +470 541 +470 604 +470 686 +470 687 +470 1173 +470 1987 +470 2440 +470 2441 +471 835 +471 965 +471 2291 +471 4741 +471 4949 +472 1138 +473 985 +473 1955 +475 3907 +477 1138 +478 1724 +478 2213 +479 1877 +479 2483 +480 4289 +481 936 +481 1248 +483 484 +483 485 +483 486 +483 487 +483 488 +483 489 +483 490 +483 491 +483 492 +483 493 +483 4035 +485 644 +485 645 +485 1424 +485 2060 +485 2061 +485 2062 +485 2063 +486 487 +486 488 +486 489 +486 2597 +487 892 +487 894 +487 2452 +487 2599 +487 3622 +487 4900 +488 2105 +488 2106 +488 2107 +488 2108 +488 2110 +488 2111 +488 2112 +488 2113 +488 2114 +488 5916 +489 1036 +489 2106 +489 2597 +490 2114 +491 2112 +491 2113 +491 2114 +492 2114 +493 644 +493 3546 +493 3729 +493 4001 +493 4035 +494 495 +494 496 +494 497 +494 498 +494 499 +494 500 +494 501 +494 502 +495 3798 +495 4791 +495 5579 +496 587 +496 1425 +496 3211 +496 7179 +498 1331 +498 3152 +498 3205 +498 3206 +498 3207 +498 3208 +498 3209 +500 1356 +500 1489 +503 504 +503 681 +503 826 +503 4652 +505 506 +505 507 +505 508 +505 4015 +506 915 +506 1949 +507 4015 +507 5146 +508 5690 +509 3533 +510 4984 +511 512 +511 513 +511 514 +511 515 +511 516 +511 517 +511 518 +511 519 +512 1848 +513 2516 +513 5501 +514 634 +514 1619 +515 1848 +515 2030 +515 2516 +515 3112 +516 1848 +516 3112 +520 521 +520 522 +520 4085 +520 6529 +524 2405 +525 526 +527 528 +527 529 +527 530 +527 531 +528 2555 +529 1219 +530 3572 +531 1139 +531 1299 +531 1447 +531 2520 +531 2549 +531 2555 +531 2846 +531 3572 +531 3963 +531 6647 +531 7022 +532 1611 +533 534 +533 681 +533 3056 +535 536 +535 537 +535 538 +537 626 +537 900 +538 3075 +539 2827 +540 2228 +540 4160 +542 543 +542 544 +542 545 +542 546 +543 2041 +543 2425 +544 2425 +545 980 +547 1092 +548 549 +548 550 +548 551 +548 552 +548 553 +548 554 +548 555 +548 556 +548 557 +548 558 +548 559 +548 560 +548 561 +548 562 +548 745 +550 551 +550 745 +551 556 +551 558 +551 745 +551 906 +556 745 +558 745 +563 6075 +564 695 +564 696 +564 697 +564 5962 +564 6849 +565 6458 +566 782 +566 1096 +566 3434 +567 568 +567 569 +567 570 +567 571 +567 572 +567 573 +567 574 +567 575 +567 576 +567 577 +567 578 +567 3228 +568 1904 +568 3228 +569 575 +570 681 +570 985 +570 1722 +570 4135 +570 5317 +571 732 +573 826 +573 2816 +574 1904 +574 4906 +575 1904 +575 5507 +578 1180 +578 5125 +579 672 +580 581 +580 582 +580 583 +580 584 +580 585 +580 586 +580 1096 +580 1376 +581 1097 +581 1126 +581 1376 +581 2178 +581 3056 +582 1376 +582 2431 +583 1376 +584 1376 +584 2413 +584 2431 +587 588 +587 589 +587 590 +587 591 +587 592 +587 593 +587 594 +587 595 +587 596 +587 597 +587 598 +587 599 +587 600 +587 601 +587 602 +587 603 +587 1425 +587 3225 +588 593 +588 596 +589 783 +589 2200 +589 5340 +591 783 +591 5340 +593 595 +593 596 +593 597 +593 1633 +593 1843 +593 2373 +593 2374 +593 2375 +593 2376 +595 597 +595 1079 +596 597 +596 1310 +596 1413 +596 1414 +597 651 +597 905 +597 1305 +597 1371 +597 1729 +597 1730 +597 1731 +597 1986 +597 2324 +597 2325 +597 2326 +597 2327 +600 735 +600 813 +600 1075 +600 1076 +600 1077 +600 1078 +600 1079 +600 1080 +600 1081 +600 1082 +600 1083 +600 1084 +600 1085 +600 1086 +600 1087 +600 1088 +600 1089 +600 5016 +600 6387 +603 5772 +604 605 +604 606 +604 607 +604 608 +604 2441 +607 672 +608 985 +608 2213 +609 4962 +610 3270 +610 4204 +611 2362 +611 5380 +612 613 +612 614 +612 615 +612 616 +612 617 +612 618 +612 619 +612 620 +612 1931 +612 2258 +612 2531 +613 1931 +614 617 +614 794 +614 1918 +614 3378 +614 3379 +614 3380 +615 2937 +617 875 +617 876 +617 1035 +617 1126 +617 1130 +617 1180 +617 1446 +617 1769 +617 1918 +617 1999 +617 2213 +617 2214 +617 2234 +617 2587 +617 2588 +617 2589 +617 2590 +617 2591 +617 2592 +617 2593 +617 2594 +617 6980 +618 619 +618 935 +618 2333 +618 3460 +619 928 +619 2241 +619 2242 +620 6692 +621 622 +621 3454 +622 2862 +622 3748 +622 5404 +622 5405 +622 5406 +623 4217 +624 6148 +624 6149 +625 626 +625 627 +625 628 +626 897 +626 900 +626 1395 +626 1587 +626 2114 +626 3608 +626 4084 +627 4121 +628 4121 +629 630 +629 4876 +631 3440 +632 4570 +633 863 +633 864 +633 866 +633 1037 +633 1325 +633 4304 +634 635 +634 636 +634 637 +634 638 +634 639 +634 640 +634 641 +634 642 +634 643 +634 7002 +639 642 +639 1236 +640 642 +640 2228 +640 2495 +640 2531 +640 2653 +640 5561 +641 5561 +642 1223 +642 1236 +642 1339 +642 1399 +642 1400 +642 1401 +642 2329 +642 2531 +644 645 +646 647 +646 648 +646 649 +646 650 +649 650 +649 3788 +649 3789 +650 1385 +650 1832 +650 1909 +652 653 +652 654 +652 655 +652 656 +652 657 +652 658 +652 659 +652 660 +654 655 +656 660 +657 3763 +661 662 +661 663 +661 664 +661 1268 +661 1367 +661 2555 +662 1268 +662 2553 +662 3321 +663 1444 +663 2555 +663 6674 +664 3277 +664 3527 +665 827 +665 1648 +666 667 +666 668 +666 669 +666 670 +666 671 +666 672 +666 1180 +666 3443 +667 1071 +668 3443 +669 937 +671 2606 +671 3706 +672 965 +672 1111 +672 1491 +672 1492 +672 1493 +672 2333 +673 4580 +674 2267 +675 676 +676 4144 +677 678 +680 2531 +681 682 +681 683 +681 684 +681 685 +681 686 +681 687 +681 688 +681 689 +681 690 +681 691 +681 692 +681 5736 +684 2041 +685 1722 +685 5317 +689 1487 +689 3056 +690 4472 +691 692 +691 1923 +691 2604 +691 4472 +692 2604 +692 4472 +693 694 +695 1946 +696 2037 +696 2126 +696 5157 +698 699 +698 2487 +698 2488 +698 5456 +703 5691 +703 6782 +706 707 +706 708 +706 709 +706 710 +706 711 +706 712 +706 713 +706 714 +706 715 +706 716 +706 717 +706 718 +706 719 +706 720 +706 721 +706 722 +706 723 +706 724 +706 725 +706 726 +706 727 +706 1234 +706 1679 +706 6569 +707 708 +707 1002 +707 1008 +707 1223 +707 1234 +707 1679 +707 6569 +708 1119 +708 1679 +711 1681 +711 3516 +721 806 +721 1681 +722 783 +722 1069 +722 1070 +722 1180 +722 1219 +722 2178 +722 4736 +725 726 +725 1425 +725 1680 +725 2420 +725 4862 +728 2531 +728 3867 +729 3581 +730 915 +731 1353 +732 733 +734 1546 +736 737 +736 738 +736 739 +736 740 +736 741 +737 740 +737 741 +737 2858 +737 3346 +737 3347 +737 3348 +738 740 +739 740 +739 6039 +740 741 +740 1687 +740 1712 +740 6405 +741 2858 +741 3346 +741 3347 +742 743 +742 744 +745 746 +745 747 +745 748 +745 749 +745 750 +745 751 +747 4091 +752 753 +752 754 +753 754 +753 1951 +753 4065 +754 1283 +754 1951 +754 2755 +754 3631 +754 4065 +754 5154 +754 6147 +755 756 +757 758 +757 759 +757 760 +757 761 +761 1947 +761 2706 +762 4195 +764 765 +764 766 +764 2725 +764 2728 +764 3816 +764 4597 +765 2725 +765 2728 +765 3816 +765 4597 +767 1070 +767 1690 +767 2347 +767 5529 +768 769 +768 770 +768 771 +768 772 +768 773 +770 6933 +771 6940 +772 5696 +774 775 +776 3553 +777 778 +779 780 +779 781 +780 781 +780 799 +780 800 +780 2753 +780 3855 +781 800 +781 2779 +783 784 +783 5340 +784 986 +784 988 +784 1363 +784 1372 +784 3012 +784 4109 +784 5329 +785 786 +785 4833 +787 788 +787 789 +787 790 +788 1636 +788 2445 +789 2320 +789 2321 +791 1153 +792 4197 +793 794 +793 795 +794 1387 +794 1918 +796 906 +797 1697 +798 799 +798 800 +799 800 +799 2753 +799 2779 +799 4580 +800 2753 +800 2777 +800 2778 +800 2779 +800 4580 +800 4876 +801 802 +801 803 +801 804 +801 805 +802 6270 +806 807 +806 808 +808 1681 +809 810 +809 811 +809 3255 +810 811 +810 3247 +810 3254 +810 3255 +810 3256 +810 3257 +810 4172 +810 5949 +810 5950 +811 3244 +811 3245 +811 3246 +811 3247 +811 3248 +811 3249 +811 3250 +811 3251 +811 3252 +811 3253 +811 3254 +811 3255 +811 3256 +811 3257 +812 813 +812 814 +812 815 +812 5010 +813 3098 +815 7233 +816 3030 +817 906 +817 5926 +817 5981 +821 5548 +822 1657 +826 1219 +826 1653 +826 2816 +826 4782 +827 828 +827 829 +827 830 +827 831 +827 832 +827 833 +827 834 +827 2531 +832 1100 +832 1213 +832 1353 +832 2655 +832 4079 +832 4080 +832 4204 +832 5024 +833 922 +834 5097 +834 6692 +835 965 +836 837 +836 2092 +837 2092 +839 840 +839 841 +839 842 +839 843 +839 870 +839 2581 +840 870 +840 2581 +842 870 +844 4215 +845 846 +846 7056 +847 6895 +847 7117 +848 849 +848 850 +848 851 +849 4830 +852 4623 +854 855 +854 856 +857 1180 +857 1353 +857 2213 +857 5317 +858 3740 +859 7122 +860 6567 +861 862 +861 863 +861 864 +861 865 +861 866 +861 867 +861 868 +861 4304 +863 864 +863 865 +863 866 +863 868 +863 1325 +863 6279 +864 865 +864 866 +864 868 +864 1325 +865 1320 +866 1320 +866 1321 +866 1322 +866 1323 +866 1324 +866 1325 +867 5861 +868 1325 +869 5448 +870 871 +870 872 +870 873 +870 874 +870 875 +870 876 +870 877 +870 5033 +871 2581 +875 876 +875 926 +875 1304 +875 1386 +875 1542 +875 1543 +875 2145 +875 2146 +875 2147 +876 6581 +877 949 +877 1896 +877 2016 +877 2367 +877 3221 +877 3588 +877 5689 +877 7251 +878 1400 +878 2412 +879 3679 +879 5121 +880 3761 +883 1010 +883 1017 +884 1353 +884 2041 +885 1846 +885 4431 +886 887 +887 1877 +888 889 +888 890 +888 891 +889 890 +889 2065 +889 2066 +889 2067 +889 2068 +889 2069 +889 2070 +889 2071 +889 2072 +889 2073 +889 2074 +889 2075 +890 891 +890 2371 +890 2822 +890 3222 +890 3483 +890 3484 +890 3485 +890 3486 +890 3487 +890 3581 +890 4635 +890 4864 +890 5141 +890 5563 +890 6529 +891 2370 +891 3581 +891 4431 +892 893 +892 894 +892 895 +892 896 +895 2452 +896 1995 +896 2105 +896 2108 +896 2120 +896 3756 +896 5916 +897 898 +897 899 +897 900 +897 4134 +900 1583 +900 1584 +900 1585 +900 1586 +900 1587 +901 902 +901 903 +901 904 +901 2084 +902 903 +902 904 +903 904 +903 2084 +905 1980 +905 3836 +906 907 +906 908 +906 909 +906 910 +906 911 +906 912 +906 1314 +906 1821 +906 1822 +907 908 +907 2443 +907 2445 +907 3311 +907 3313 +908 3311 +908 6957 +911 1821 +912 2872 +913 4802 +914 915 +914 965 +915 916 +915 917 +915 918 +915 919 +915 920 +915 921 +915 922 +915 923 +915 924 +915 925 +915 5274 +919 5101 +921 923 +921 924 +921 3051 +922 923 +922 2566 +922 2567 +922 6830 +922 6831 +923 1358 +923 3213 +926 1697 +926 4700 +927 2662 +928 929 +928 930 +928 931 +928 932 +928 933 +928 934 +928 935 +928 936 +928 937 +928 938 +928 939 +928 940 +928 941 +928 4085 +929 930 +930 931 +930 932 +930 2003 +930 2662 +930 2668 +930 3422 +930 3924 +930 3925 +930 5287 +930 5630 +931 1363 +932 2662 +934 2212 +934 4085 +935 1162 +935 1941 +935 2015 +935 2332 +935 2604 +935 2605 +935 3404 +935 4113 +936 4085 +937 938 +937 941 +937 985 +937 2410 +937 2675 +937 3399 +937 4113 +937 5092 +939 1268 +939 2413 +942 972 +942 1598 +942 2602 +942 4683 +942 4690 +943 944 +943 945 +943 6554 +944 1653 +944 1657 +946 4690 +947 948 +947 949 +947 3221 +948 4430 +948 5302 +948 6559 +948 6560 +949 3221 +949 3222 +949 4012 +949 4174 +949 4924 +949 6605 +950 951 +950 952 +950 953 +950 954 +950 955 +950 2662 +951 3712 +952 2662 +954 1151 +954 2423 +954 3397 +956 4590 +957 6773 +958 959 +958 960 +958 961 +958 3169 +959 1387 +959 4465 +962 963 +962 964 +962 3007 +962 5633 +963 4433 +964 4433 +965 966 +965 967 +965 968 +965 969 +965 970 +965 971 +965 972 +965 2531 +965 3098 +966 2531 +966 3098 +966 4765 +967 2401 +972 1296 +972 1297 +973 2661 +974 5215 +974 5217 +975 5380 +976 977 +976 978 +976 979 +976 980 +976 981 +977 1827 +980 3928 +980 5371 +982 4443 +983 984 +983 985 +985 1690 +985 1691 +985 1697 +985 1698 +985 1699 +985 3919 +985 4820 +985 5203 +985 5331 +985 5332 +985 5333 +985 5334 +985 5335 +985 5336 +985 5337 +985 5338 +985 5339 +985 5340 +985 5341 +986 987 +986 988 +986 989 +986 990 +987 989 +987 990 +988 990 +989 990 +989 1729 +989 3291 +989 4284 +989 4373 +989 4736 +989 6378 +991 6458 +992 993 +992 994 +992 995 +995 1434 +995 1435 +996 997 +996 998 +996 999 +1000 2531 +1000 3098 +1001 3539 +1002 1003 +1002 1004 +1002 1005 +1002 1006 +1002 1007 +1002 1008 +1002 1009 +1005 1006 +1005 1008 +1006 1007 +1006 1009 +1006 1820 +1006 2526 +1006 3292 +1007 1009 +1007 1820 +1007 2488 +1007 3292 +1008 1820 +1008 2526 +1008 2527 +1008 2529 +1009 1472 +1009 1481 +1009 1820 +1009 2526 +1009 3223 +1010 1011 +1010 1012 +1010 1013 +1010 1014 +1010 1015 +1010 1016 +1010 1017 +1011 2958 +1013 1014 +1013 1495 +1013 1496 +1015 2956 +1015 2958 +1015 3156 +1015 3234 +1015 3567 +1015 3897 +1016 1022 +1016 5174 +1017 1021 +1017 1022 +1017 1120 +1017 1121 +1017 1122 +1017 1123 +1017 1124 +1017 2555 +1017 3321 +1017 4855 +1017 6654 +1018 1512 +1019 2041 +1020 3440 +1021 1022 +1021 1023 +1022 1023 +1022 2527 +1022 3187 +1022 4849 +1022 4850 +1022 4851 +1022 4852 +1022 4853 +1022 4854 +1022 4855 +1022 4856 +1024 1025 +1024 1026 +1024 1027 +1024 1028 +1024 1029 +1024 1030 +1024 1031 +1024 1032 +1024 3540 +1025 3540 +1026 1947 +1026 3540 +1028 3540 +1029 1423 +1029 1427 +1029 2141 +1029 2142 +1029 3750 +1031 3607 +1031 3702 +1031 3750 +1031 4182 +1031 5252 +1032 3540 +1032 3546 +1033 1126 +1033 1487 +1034 1035 +1037 1038 +1037 1039 +1037 1040 +1038 1039 +1039 1040 +1039 3771 +1040 3771 +1041 3143 +1042 1043 +1044 1054 +1045 2610 +1046 4561 +1047 1048 +1047 1049 +1049 3679 +1050 3546 +1051 5340 +1052 1053 +1053 7023 +1055 1353 +1055 4015 +1056 3706 +1056 6453 +1057 1058 +1057 1059 +1058 1769 +1060 1061 +1061 2449 +1061 2499 +1061 2591 +1061 3024 +1061 3695 +1061 3843 +1061 4273 +1062 1063 +1062 1064 +1065 5091 +1066 6286 +1067 6286 +1068 6887 +1069 3759 +1070 1806 +1072 6774 +1073 1791 +1073 2289 +1074 1434 +1074 4057 +1075 3930 +1075 4556 +1077 1079 +1078 3371 +1079 1473 +1079 2049 +1079 2375 +1080 3371 +1080 3712 +1083 2131 +1084 1679 +1084 1681 +1086 3156 +1090 1932 +1091 6288 +1092 1093 +1092 1094 +1092 1095 +1093 1095 +1093 3652 +1093 4545 +1093 4993 +1093 4994 +1095 3529 +1096 1097 +1096 1098 +1096 1099 +1096 1100 +1096 1101 +1096 1102 +1096 4016 +1097 1379 +1097 1520 +1097 2397 +1098 4016 +1100 2655 +1100 3062 +1101 4115 +1101 4741 +1103 1138 +1103 2553 +1104 6777 +1105 1106 +1105 1107 +1105 1108 +1105 1109 +1105 1110 +1105 1111 +1105 1112 +1105 2017 +1105 2263 +1108 1223 +1113 1716 +1113 3020 +1114 1180 +1114 3510 +1115 1116 +1116 3930 +1117 1234 +1117 1679 +1118 3030 +1118 4706 +1125 2212 +1126 1127 +1126 1128 +1126 1129 +1126 1130 +1126 1131 +1126 1132 +1126 1133 +1126 1134 +1126 1135 +1126 1136 +1126 1479 +1128 1133 +1128 1479 +1128 1565 +1128 1635 +1128 1666 +1128 1667 +1128 1668 +1128 1669 +1128 1670 +1129 1212 +1130 1133 +1130 1479 +1130 2589 +1132 3672 +1133 1444 +1133 1445 +1133 1448 +1133 2587 +1133 2589 +1133 3798 +1134 1487 +1134 4545 +1136 1831 +1137 1138 +1137 1139 +1137 1140 +1137 1141 +1137 1142 +1137 1143 +1137 1144 +1137 1145 +1137 1146 +1138 1310 +1138 1425 +1138 1758 +1138 1955 +1138 2010 +1138 2011 +1138 2012 +1138 2013 +1138 2014 +1139 1140 +1139 1299 +1139 1888 +1139 2520 +1139 2846 +1139 3853 +1139 6647 +1140 1498 +1140 3853 +1141 1964 +1141 3853 +1141 4446 +1143 1838 +1144 4459 +1144 6263 +1147 1697 +1148 1781 +1148 2284 +1148 3490 +1149 1180 +1149 3800 +1149 4082 +1150 5238 +1151 1153 +1151 2774 +1151 3895 +1153 1154 +1153 1155 +1153 1156 +1153 1157 +1153 1158 +1153 1159 +1153 1160 +1154 1223 +1155 6270 +1156 1415 +1156 4116 +1158 1223 +1159 1960 +1161 3089 +1163 1487 +1164 1165 +1164 1166 +1168 5309 +1169 1170 +1169 1223 +1169 6930 +1172 1653 +1173 1653 +1173 2441 +1173 7200 +1180 1199 +1180 1200 +1180 1201 +1180 1202 +1180 1203 +1180 1204 +1180 1205 +1180 1206 +1180 1207 +1180 1208 +1180 1209 +1180 1210 +1180 1211 +1180 1212 +1180 1213 +1180 1214 +1180 1215 +1180 1216 +1180 1217 +1180 1218 +1180 1219 +1180 1220 +1180 1221 +1180 1353 +1180 1846 +1180 4415 +1181 4590 +1182 1201 +1182 1660 +1182 3733 +1182 4590 +1184 1188 +1184 1919 +1184 1925 +1184 4227 +1185 1188 +1185 1280 +1185 1281 +1185 2402 +1185 3148 +1186 1188 +1186 1925 +1186 3148 +1187 1188 +1188 1595 +1188 2402 +1188 3148 +1188 3396 +1188 3397 +1188 3398 +1188 6861 +1193 6820 +1194 2210 +1194 4191 +1194 5329 +1194 5330 +1196 1197 +1198 4961 +1198 5834 +1199 4503 +1199 5168 +1200 1722 +1200 2095 +1200 2096 +1201 1202 +1201 2547 +1201 2747 +1201 2748 +1201 2749 +1201 2750 +1201 2751 +1201 2752 +1201 2753 +1201 2754 +1201 2755 +1201 2756 +1201 2757 +1201 2758 +1201 2759 +1201 4415 +1201 5125 +1202 2771 +1202 2773 +1202 4415 +1202 4416 +1202 5922 +1203 5125 +1204 1205 +1204 3155 +1209 6569 +1210 3507 +1210 4155 +1210 4156 +1210 6387 +1211 2937 +1211 5016 +1212 1353 +1212 1948 +1212 1949 +1213 3574 +1213 5024 +1214 4379 +1214 5785 +1219 1420 +1219 1423 +1219 1475 +1219 1911 +1219 2161 +1219 2162 +1219 2163 +1219 2164 +1219 2165 +1219 2166 +1219 2167 +1219 2168 +1219 2169 +1221 1845 +1221 1846 +1221 2415 +1221 2416 +1221 2417 +1221 2418 +1221 2419 +1221 5163 +1221 6057 +1222 2662 +1223 1224 +1223 1225 +1223 1226 +1223 1227 +1223 1228 +1223 1229 +1223 1230 +1223 1231 +1223 1232 +1223 1233 +1223 1234 +1223 1235 +1223 1236 +1223 1237 +1223 1238 +1223 2306 +1223 2307 +1226 1227 +1229 1997 +1230 1231 +1230 2306 +1230 2307 +1230 3897 +1230 5189 +1231 2306 +1231 2307 +1231 5189 +1231 5198 +1232 5189 +1232 5198 +1233 1904 +1233 2103 +1234 1391 +1234 1679 +1234 1680 +1234 2170 +1234 3318 +1234 3616 +1234 4005 +1234 4006 +1234 4007 +1234 4008 +1234 4009 +1234 4010 +1236 1401 +1236 2160 +1236 2531 +1237 1619 +1237 1932 +1243 6700 +1244 2126 +1244 2200 +1244 3732 +1244 3836 +1245 4144 +1246 1247 +1246 2495 +1246 2499 +1246 2650 +1246 2653 +1246 4113 +1246 4443 +1247 2495 +1247 4113 +1248 1249 +1248 1250 +1249 1250 +1249 4906 +1251 4042 +1252 6373 +1253 1254 +1253 1255 +1253 1256 +1253 2213 +1256 3433 +1257 1258 +1259 1791 +1260 1558 +1260 3056 +1260 3156 +1261 3156 +1262 4963 +1263 1932 +1264 1265 +1264 1266 +1264 3527 +1265 1724 +1265 1980 +1265 3527 +1265 6429 +1266 3527 +1266 6429 +1267 3156 +1268 1269 +1268 1270 +1268 1271 +1268 1272 +1268 1273 +1268 1274 +1268 1275 +1268 1276 +1268 1277 +1270 2457 +1270 2808 +1270 4527 +1273 6573 +1273 6574 +1273 6575 +1278 3440 +1279 3581 +1279 5154 +1280 1281 +1280 1282 +1280 1283 +1280 1284 +1283 1925 +1283 6861 +1285 2196 +1286 1287 +1286 1288 +1286 1289 +1287 1359 +1289 1359 +1290 1828 +1291 1292 +1291 1293 +1291 1294 +1291 2650 +1291 6706 +1293 1877 +1293 2483 +1293 3156 +1293 3317 +1294 2054 +1294 2845 +1295 2265 +1297 4040 +1298 1299 +1298 2798 +1298 5000 +1298 6647 +1299 2477 +1299 2521 +1299 2846 +1299 2848 +1299 3963 +1299 6647 +1299 6674 +1300 6127 +1300 6129 +1300 6130 +1301 1302 +1303 2356 +1304 1487 +1304 2108 +1304 3732 +1304 4766 +1305 1306 +1305 1307 +1308 2919 +1309 5194 +1310 1980 +1310 3689 +1311 1312 +1311 6748 +1312 4716 +1313 2041 +1314 1315 +1314 1316 +1314 1317 +1314 1821 +1319 4431 +1321 1322 +1321 2568 +1321 3419 +1322 2569 +1322 3419 +1322 3698 +1325 1701 +1325 3600 +1325 4304 +1326 4804 +1327 6289 +1328 5181 +1329 1330 +1329 1331 +1330 6129 +1331 1862 +1332 3498 +1333 4135 +1334 2127 +1334 5274 +1335 3054 +1335 4015 +1336 1413 +1336 1980 +1336 4777 +1336 5404 +1337 6366 +1338 4831 +1339 2531 +1340 3156 +1342 1464 +1342 2108 +1343 3156 +1343 5189 +1344 3344 +1345 1346 +1345 7117 +1346 1544 +1346 5329 +1346 7117 +1347 3379 +1348 1896 +1348 2016 +1348 5689 +1348 7251 +1353 1354 +1353 1355 +1353 1356 +1353 1660 +1353 3588 +1353 4015 +1355 4862 +1356 1489 +1357 1358 +1357 1359 +1358 1359 +1360 1361 +1361 3192 +1361 3193 +1361 3194 +1362 1997 +1362 4195 +1362 4197 +1362 7186 +1363 1364 +1363 1365 +1363 1366 +1363 1367 +1363 1368 +1363 1369 +1363 1370 +1363 1371 +1363 1372 +1363 1373 +1363 4109 +1363 4476 +1365 1367 +1366 4063 +1366 4476 +1367 1368 +1367 1720 +1367 3649 +1367 3650 +1367 4476 +1368 1720 +1369 4476 +1370 4476 +1374 3798 +1375 3490 +1376 1377 +1376 1378 +1376 1379 +1376 1447 +1379 3488 +1380 1381 +1380 1382 +1380 1383 +1380 1384 +1380 1385 +1380 3287 +1380 3600 +1380 3601 +1381 1383 +1381 1384 +1381 1842 +1381 2956 +1381 2957 +1381 2958 +1381 2959 +1381 3600 +1381 3963 +1382 4188 +1383 4144 +1384 3287 +1384 3600 +1385 3458 +1385 3540 +1385 3601 +1386 1387 +1387 1574 +1387 2753 +1387 2917 +1387 3669 +1388 1389 +1388 1390 +1388 1391 +1388 1392 +1388 1393 +1388 1394 +1391 2260 +1391 2263 +1391 4051 +1392 2054 +1393 2058 +1393 2818 +1394 2054 +1394 2058 +1394 4493 +1395 4464 +1396 1397 +1396 1398 +1397 1398 +1397 1569 +1397 3459 +1398 3459 +1400 2053 +1400 2258 +1400 2329 +1400 2331 +1400 2411 +1400 2412 +1400 2413 +1401 2531 +1402 1403 +1402 1404 +1402 1405 +1402 1406 +1402 1407 +1402 1408 +1402 1409 +1402 1410 +1402 1411 +1402 1412 +1402 7243 +1404 7243 +1413 1763 +1413 1764 +1413 1980 +1413 5595 +1417 1418 +1419 1758 +1421 1422 +1423 1424 +1423 1425 +1423 1426 +1423 1427 +1423 1428 +1423 3018 +1425 1426 +1425 1797 +1425 2013 +1425 2014 +1425 2235 +1425 2373 +1425 2420 +1425 2984 +1425 3225 +1425 3748 +1425 3914 +1426 1883 +1426 3471 +1426 4656 +1426 4755 +1427 2141 +1427 2142 +1427 3813 +1429 1430 +1429 1431 +1429 1432 +1429 1433 +1429 2655 +1429 4135 +1430 1431 +1430 1432 +1430 1433 +1431 1433 +1434 1435 +1434 1436 +1437 1438 +1437 1439 +1438 2362 +1440 2414 +1440 3490 +1441 1442 +1442 1443 +1444 1445 +1444 1446 +1444 1447 +1444 1448 +1444 1449 +1444 1450 +1444 1451 +1444 1452 +1444 2589 +1445 2589 +1445 4160 +1445 4274 +1445 4741 +1446 1447 +1446 2589 +1446 2591 +1446 3585 +1446 4703 +1447 2322 +1447 2555 +1447 2587 +1447 2589 +1447 2593 +1447 2751 +1447 3585 +1447 3586 +1447 3587 +1447 3588 +1447 3589 +1447 3590 +1447 3854 +1447 4160 +1447 6674 +1448 2589 +1451 2555 +1453 2126 +1453 2960 +1453 4766 +1453 7154 +1454 1455 +1454 1456 +1457 1994 +1458 1781 +1459 3930 +1460 1461 +1460 1462 +1460 4741 +1461 1462 +1462 2524 +1462 4741 +1463 1464 +1463 1465 +1463 1466 +1463 2258 +1465 1466 +1466 2051 +1466 2052 +1466 2053 +1467 5549 +1470 2367 +1470 3929 +1470 4431 +1472 3292 +1474 2460 +1474 4015 +1474 4032 +1475 1481 +1475 1507 +1477 3948 +1478 1660 +1478 1694 +1481 2714 +1481 3292 +1481 7156 +1483 1900 +1483 2064 +1483 2519 +1483 2527 +1483 2837 +1483 2859 +1483 2860 +1483 2861 +1483 2862 +1483 2863 +1483 2864 +1483 2865 +1483 2866 +1483 2867 +1483 2868 +1483 2869 +1483 3510 +1485 4178 +1486 2214 +1486 6980 +1487 1488 +1487 1489 +1487 1490 +1488 1956 +1488 2108 +1488 4113 +1489 3211 +1491 4145 +1497 2956 +1497 3234 +1497 3599 +1497 6165 +1498 1499 +1498 1500 +1498 1501 +1498 1502 +1498 1503 +1498 1504 +1498 1505 +1498 1506 +1498 1507 +1498 1997 +1508 3761 +1509 1510 +1509 2807 +1509 4642 +1510 2807 +1511 3112 +1512 1513 +1512 1514 +1512 1515 +1512 1516 +1512 1517 +1512 1518 +1512 2238 +1512 4487 +1513 3750 +1514 1897 +1516 2238 +1516 4487 +1519 1520 +1519 1521 +1520 5106 +1522 2653 +1523 1524 +1523 1525 +1523 1526 +1523 1527 +1523 1528 +1523 1529 +1523 1530 +1523 1531 +1523 1532 +1523 1533 +1523 1534 +1523 1535 +1523 1536 +1523 1537 +1523 1538 +1523 1539 +1523 1540 +1524 4446 +1526 3299 +1526 3300 +1526 3301 +1526 3302 +1526 3303 +1526 3304 +1526 4446 +1527 2431 +1530 2431 +1530 5106 +1537 2671 +1541 5016 +1542 1543 +1542 1544 +1542 1545 +1543 1544 +1543 1545 +1543 1872 +1544 2447 +1544 3569 +1544 4260 +1544 4284 +1545 1736 +1545 3002 +1545 3026 +1546 1547 +1546 1548 +1546 1549 +1546 1550 +1546 1551 +1546 1552 +1546 1553 +1550 2260 +1550 3074 +1554 1674 +1554 3856 +1555 2454 +1556 4015 +1557 3608 +1559 1704 +1560 1561 +1560 1562 +1563 2808 +1563 5954 +1564 1565 +1565 1795 +1565 2260 +1566 1932 +1566 2258 +1568 1569 +1568 1570 +1568 1571 +1568 1572 +1568 1573 +1568 1574 +1568 1575 +1568 4801 +1569 1570 +1569 1571 +1571 1575 +1571 5586 +1574 2089 +1574 5586 +1576 2974 +1576 2975 +1577 3585 +1577 3706 +1578 6440 +1579 2674 +1579 4085 +1580 1581 +1580 1582 +1588 1869 +1589 1590 +1589 1591 +1589 1592 +1589 1593 +1589 1594 +1590 2645 +1590 3770 +1591 5796 +1593 2513 +1593 3094 +1593 4244 +1593 5056 +1593 5273 +1593 5480 +1593 5865 +1594 3093 +1596 6500 +1597 2798 +1598 1599 +1598 1600 +1598 1601 +1600 2388 +1600 3930 +1600 3976 +1602 1603 +1604 2424 +1605 1606 +1605 3988 +1607 1608 +1608 3972 +1608 3973 +1608 3974 +1608 3975 +1609 3533 +1610 1917 +1611 1612 +1611 1613 +1611 1614 +1611 1615 +1611 3081 +1616 1617 +1616 4797 +1618 2260 +1618 2261 +1618 4797 +1619 1620 +1619 1621 +1619 1622 +1622 2054 +1622 3228 +1623 1624 +1623 1625 +1626 1627 +1626 1628 +1626 1629 +1626 1630 +1626 1631 +1626 1632 +1626 1633 +1626 1634 +1626 2041 +1626 3590 +1626 6340 +1627 1633 +1633 1634 +1633 3836 +1633 4447 +1635 2725 +1635 2728 +1635 3816 +1636 1637 +1638 3258 +1639 1640 +1639 1641 +1639 1642 +1639 1643 +1639 1644 +1639 1645 +1639 1646 +1647 1648 +1647 4556 +1648 2313 +1648 2921 +1648 3111 +1648 3214 +1648 3470 +1648 3707 +1648 4257 +1648 5351 +1648 5841 +1648 6635 +1652 3955 +1652 4057 +1652 4058 +1653 1654 +1653 1655 +1653 1656 +1653 1657 +1653 1658 +1653 1659 +1653 1660 +1653 1661 +1653 1662 +1653 1663 +1653 1664 +1653 1665 +1654 1781 +1656 1657 +1660 3588 +1660 7143 +1662 2531 +1668 1877 +1668 4018 +1671 6054 +1672 1673 +1674 1675 +1674 1676 +1674 1677 +1677 3856 +1677 4134 +1678 2388 +1678 2394 +1679 1680 +1679 1681 +1679 1682 +1679 2263 +1679 3071 +1680 2263 +1680 2958 +1680 4755 +1680 6458 +1681 3907 +1683 1684 +1683 1685 +1683 5805 +1686 1687 +1686 1970 +1686 4284 +1686 4373 +1686 4460 +1687 3088 +1687 3668 +1687 4260 +1687 4284 +1687 4360 +1687 4373 +1687 4374 +1687 4460 +1687 4964 +1687 5585 +1687 6405 +1688 4016 +1688 4018 +1689 3488 +1689 4741 +1690 1691 +1690 1692 +1691 3317 +1693 3127 +1694 1695 +1694 1696 +1697 1698 +1697 1699 +1697 4289 +1698 4015 +1698 4032 +1698 4289 +1700 3555 +1700 5032 +1700 7315 +1700 7325 +1702 1703 +1704 1705 +1704 1706 +1704 1707 +1704 1708 +1704 2390 +1704 5189 +1705 2390 +1709 4000 +1710 4828 +1711 2332 +1712 1713 +1712 1714 +1712 1715 +1712 1716 +1712 1717 +1712 1718 +1712 1719 +1712 3488 +1712 5371 +1712 5477 +1713 7233 +1714 3617 +1715 5106 +1716 1717 +1716 4142 +1716 5477 +1717 4949 +1718 2531 +1718 3992 +1718 4016 +1718 4949 +1718 5106 +1722 1727 +1722 2733 +1722 2821 +1722 3403 +1722 4220 +1722 5293 +1722 5294 +1724 1885 +1724 4802 +1729 4373 +1730 1731 +1735 2531 +1737 4015 +1742 1744 +1742 4421 +1742 4802 +1742 6429 +1743 5106 +1752 3259 +1758 1759 +1758 1760 +1758 1761 +1759 1761 +1760 1761 +1761 2199 +1761 2941 +1761 4396 +1761 5614 +1762 4706 +1763 6340 +1764 1980 +1764 4777 +1765 3690 +1766 1768 +1766 3390 +1767 1768 +1767 3390 +1767 3488 +1768 3901 +1768 4181 +1769 1770 +1771 1772 +1771 2563 +1771 4195 +1772 2563 +1772 6409 +1773 6567 +1774 1775 +1774 3798 +1774 4750 +1775 3798 +1775 7173 +1776 2285 +1777 3056 +1778 2460 +1779 2690 +1780 2260 +1781 1782 +1781 1783 +1781 1784 +1781 1785 +1781 1786 +1781 1787 +1781 1788 +1781 1789 +1781 1790 +1781 1791 +1781 1792 +1781 1793 +1781 1794 +1785 3503 +1787 2284 +1787 2292 +1789 3490 +1789 3621 +1790 3503 +1790 3997 +1791 2144 +1794 2017 +1795 1796 +1795 1797 +1795 1798 +1795 1799 +1797 1888 +1797 3630 +1797 3917 +1800 1801 +1801 4208 +1802 1803 +1802 1804 +1803 4583 +1805 2674 +1805 3837 +1805 4716 +1806 2038 +1807 1808 +1807 1809 +1807 1810 +1811 1812 +1811 1813 +1811 1814 +1811 1815 +1811 1816 +1811 1817 +1811 1818 +1811 1819 +1813 1817 +1813 3191 +1816 5696 +1817 1819 +1817 5533 +1818 5061 +1819 3426 +1819 4811 +1820 1972 +1820 2526 +1820 2557 +1820 3292 +1820 3293 +1821 1822 +1821 1823 +1821 1824 +1821 1825 +1821 1826 +1822 1823 +1822 6796 +1822 6797 +1822 6798 +1822 6799 +1822 6800 +1828 1829 +1828 1830 +1831 1832 +1831 1833 +1831 1834 +1831 1835 +1831 1836 +1831 1837 +1832 3081 +1839 3844 +1839 4690 +1840 1841 +1840 1842 +1840 3600 +1842 1904 +1845 1846 +1845 5163 +1845 6057 +1846 1996 +1846 2417 +1846 2419 +1846 2603 +1846 2612 +1846 2613 +1846 2614 +1846 2615 +1846 2616 +1846 2617 +1846 2618 +1846 2619 +1846 2620 +1846 2621 +1846 2622 +1846 2623 +1846 2624 +1846 2625 +1846 2626 +1846 2627 +1846 2628 +1846 2629 +1846 2630 +1846 2631 +1846 2632 +1846 2633 +1846 2634 +1846 2635 +1846 2636 +1846 2637 +1846 2638 +1846 4513 +1846 6057 +1847 1997 +1848 1849 +1848 1850 +1848 1851 +1848 1852 +1848 1853 +1848 1891 +1848 3911 +1849 1891 +1850 1891 +1850 2312 +1853 3112 +1854 1855 +1854 1856 +1854 1857 +1855 5044 +1855 5377 +1855 5522 +1856 7225 +1856 7331 +1858 1862 +1858 1863 +1858 1864 +1858 1865 +1858 3053 +1858 3818 +1858 4939 +1858 5052 +1858 5094 +1859 3673 +1859 7193 +1860 1861 +1862 1863 +1862 1864 +1862 1865 +1862 1866 +1862 1867 +1862 1868 +1862 1869 +1862 3353 +1863 1864 +1865 3052 +1866 3353 +1866 5285 +1867 3353 +1870 4500 +1871 2523 +1872 1873 +1872 1874 +1875 2531 +1876 1877 +1876 1878 +1876 1879 +1876 1880 +1876 1881 +1876 2483 +1876 4460 +1877 1878 +1877 2482 +1877 2483 +1877 2484 +1878 1932 +1878 2483 +1886 3056 +1886 3879 +1886 4862 +1887 4364 +1887 4707 +1887 4708 +1887 4710 +1889 3270 +1890 1891 +1890 1892 +1891 1892 +1891 1928 +1891 3617 +1893 1894 +1895 2103 +1896 2016 +1896 2367 +1897 1898 +1897 1899 +1900 7179 +1901 1902 +1901 1903 +1901 1904 +1901 1905 +1901 1906 +1901 1907 +1901 3964 +1901 3967 +1901 3969 +1902 3964 +1902 3967 +1902 3969 +1903 3964 +1903 3967 +1903 3969 +1904 2130 +1904 2882 +1904 2883 +1904 2884 +1904 2885 +1904 2886 +1904 2887 +1904 2888 +1904 2889 +1904 2890 +1904 2891 +1904 2892 +1904 2893 +1904 2894 +1904 2895 +1904 2896 +1904 2897 +1904 2898 +1904 2899 +1904 2900 +1904 2901 +1904 2902 +1904 2903 +1904 2904 +1904 2905 +1904 2906 +1904 2907 +1904 3964 +1904 3967 +1904 3969 +1904 5115 +1904 5575 +1908 6503 +1909 1989 +1910 1932 +1911 1912 +1911 1913 +1914 6422 +1915 4061 +1916 2126 +1919 1926 +1920 2696 +1921 5598 +1922 4741 +1923 1924 +1925 1926 +1926 3559 +1926 4809 +1929 2332 +1930 5000 +1931 1932 +1932 2257 +1932 2258 +1932 2259 +1933 1934 +1935 3795 +1936 1937 +1936 1938 +1936 1939 +1936 1940 +1939 4059 +1940 2937 +1941 2332 +1941 2333 +1942 2571 +1942 2574 +1943 3224 +1944 1945 +1946 1947 +1946 3476 +1946 3902 +1946 5447 +1946 5496 +1946 5592 +1946 6366 +1947 2377 +1947 2378 +1947 2379 +1947 2380 +1947 5380 +1949 2881 +1950 1951 +1951 2981 +1952 3219 +1953 3353 +1954 1955 +1955 2193 +1955 2600 +1955 2601 +1955 3176 +1955 3177 +1955 4569 +1956 1957 +1958 1959 +1961 2053 +1961 2329 +1961 2413 +1962 1963 +1962 5404 +1963 2558 +1964 1965 +1964 1966 +1964 1967 +1964 1968 +1964 1969 +1964 3259 +1964 4446 +1965 3259 +1967 2388 +1969 4446 +1969 5156 +1970 2450 +1970 2452 +1970 2599 +1970 3709 +1970 3888 +1972 2301 +1972 3292 +1973 6162 +1973 6814 +1974 1975 +1974 1976 +1977 1978 +1977 1979 +1980 1981 +1980 1982 +1980 1983 +1980 1984 +1980 1985 +1980 1986 +1984 5414 +1986 2361 +1987 2289 +1988 3510 +1989 2258 +1990 1991 +1992 3800 +1992 5357 +1993 6776 +1995 2108 +1995 3447 +1995 5916 +1997 1998 +1997 1999 +1997 2000 +1997 2001 +1997 4195 +1997 5629 +1999 2480 +1999 2520 +1999 3586 +1999 7186 +2001 5629 +2002 5786 +2003 2662 +2004 2041 +2005 2372 +2006 4002 +2007 2008 +2007 2009 +2010 3469 +2010 4200 +2011 2213 +2013 2526 +2013 2529 +2013 2988 +2013 2989 +2013 2990 +2013 2991 +2013 2992 +2013 5093 +2015 6054 +2016 5689 +2016 7251 +2017 2018 +2017 2019 +2017 2020 +2021 2022 +2023 6127 +2023 6129 +2023 6130 +2023 7077 +2024 2214 +2024 7179 +2025 2026 +2025 2027 +2027 4977 +2027 6444 +2027 7287 +2028 2029 +2028 2030 +2031 2032 +2031 2033 +2033 2089 +2034 2035 +2034 2036 +2035 6440 +2038 2039 +2038 2040 +2041 2042 +2041 2043 +2041 2044 +2041 2045 +2041 2046 +2041 2047 +2041 2048 +2041 2049 +2041 2050 +2043 6289 +2044 2144 +2048 6340 +2050 5163 +2050 6057 +2052 2531 +2052 2539 +2053 2258 +2053 2331 +2053 2412 +2053 2413 +2053 3114 +2053 3115 +2053 3116 +2054 2055 +2054 2056 +2054 2057 +2054 2058 +2054 2059 +2055 2916 +2056 2058 +2057 2058 +2058 4545 +2058 4643 +2059 4366 +2061 3960 +2062 3334 +2062 3960 +2064 2115 +2076 4113 +2076 4114 +2077 2078 +2077 3761 +2080 2081 +2080 2082 +2083 6512 +2084 2085 +2084 2086 +2084 2087 +2085 3015 +2085 3228 +2085 3540 +2086 3228 +2088 7179 +2089 2090 +2089 2091 +2093 2094 +2097 2098 +2097 2099 +2097 2100 +2097 2101 +2097 3510 +2097 3632 +2099 2101 +2102 2103 +2102 2104 +2103 2104 +2103 3335 +2103 4179 +2103 4180 +2104 5272 +2105 2106 +2105 2107 +2105 2108 +2105 2109 +2105 2115 +2106 2452 +2106 2922 +2106 3829 +2106 3836 +2106 4862 +2107 2108 +2107 6820 +2108 2111 +2108 2143 +2108 4158 +2108 5170 +2110 6820 +2111 4482 +2111 4921 +2112 2113 +2112 2114 +2113 2114 +2113 4164 +2114 4164 +2114 4259 +2114 4755 +2114 4921 +2114 5318 +2115 2116 +2117 2118 +2117 2119 +2117 2120 +2117 2121 +2117 2122 +2117 2123 +2117 2124 +2117 3682 +2118 2381 +2119 2120 +2119 2122 +2120 2123 +2120 3623 +2120 4494 +2120 4495 +2120 5916 +2120 6488 +2120 6820 +2121 2122 +2122 2269 +2122 4771 +2125 2412 +2125 2558 +2126 2127 +2126 2128 +2126 2129 +2127 5274 +2129 2189 +2131 5817 +2132 2133 +2132 2440 +2133 2440 +2134 2135 +2134 2136 +2134 2137 +2134 2138 +2135 2136 +2135 2137 +2135 5755 +2136 2137 +2136 2138 +2137 4514 +2138 6000 +2139 2140 +2139 4115 +2141 3813 +2142 3813 +2144 2289 +2144 6289 +2148 2149 +2148 2150 +2149 4621 +2150 4621 +2151 2152 +2153 4446 +2154 5353 +2155 4431 +2156 2157 +2158 2159 +2158 2531 +2159 4018 +2160 6379 +2161 4748 +2161 4749 +2163 3075 +2163 4015 +2164 2755 +2166 3546 +2170 2974 +2170 2975 +2171 2172 +2171 2173 +2171 2174 +2171 2175 +2173 3098 +2175 2604 +2175 4949 +2176 3400 +2177 2798 +2177 4801 +2178 2179 +2178 4736 +2179 7252 +2180 2531 +2181 4906 +2182 2183 +2183 5933 +2184 2185 +2186 2924 +2187 3287 +2188 4655 +2189 3440 +2190 2191 +2192 7288 +2193 2194 +2194 2213 +2195 2196 +2195 2197 +2196 4636 +2196 4637 +2196 4638 +2196 4639 +2197 2442 +2198 4984 +2200 2201 +2202 6572 +2203 2206 +2203 2753 +2203 4465 +2204 2960 +2204 6153 +2205 2604 +2205 3027 +2206 2207 +2208 2209 +2210 4976 +2211 2212 +2212 2531 +2212 2537 +2212 3846 +2213 2214 +2213 2215 +2213 2216 +2213 2217 +2213 4015 +2213 4474 +2214 2218 +2214 2219 +2214 2220 +2214 2221 +2214 2222 +2214 2223 +2214 2224 +2214 2225 +2214 2226 +2214 2227 +2215 2531 +2215 4518 +2218 2653 +2218 4015 +2220 5314 +2221 2223 +2221 2862 +2228 2229 +2228 2230 +2228 2231 +2228 2232 +2228 4160 +2228 4162 +2228 4831 +2233 3454 +2233 3457 +2234 2364 +2235 3761 +2236 2251 +2237 2238 +2237 2239 +2238 4288 +2238 4356 +2238 4958 +2238 4959 +2238 4960 +2240 4651 +2243 3829 +2244 2958 +2245 2246 +2247 2248 +2247 2249 +2250 3760 +2250 5156 +2251 2252 +2253 3997 +2253 4145 +2254 2255 +2254 2256 +2258 2412 +2259 2531 +2260 2261 +2260 2262 +2260 2263 +2260 2264 +2263 3318 +2263 3661 +2264 4672 +2266 5248 +2267 2268 +2270 2271 +2270 2272 +2270 2273 +2274 3283 +2274 5020 +2274 5198 +2275 2276 +2275 2277 +2275 2278 +2279 2280 +2281 2282 +2281 2283 +2283 3605 +2283 4306 +2284 5668 +2285 2286 +2285 2287 +2285 2288 +2289 2290 +2292 4291 +2292 4490 +2292 5011 +2292 5668 +2293 2294 +2293 2295 +2293 2296 +2293 2297 +2293 2298 +2293 2299 +2293 2300 +2293 2301 +2293 2302 +2293 2303 +2295 2301 +2296 4915 +2296 4922 +2301 2420 +2301 3442 +2304 3540 +2305 2602 +2305 3490 +2306 2307 +2306 2308 +2306 2309 +2307 2308 +2307 2309 +2308 2309 +2310 2311 +2310 2312 +2314 2315 +2314 2316 +2314 2317 +2314 2318 +2314 2319 +2314 6120 +2321 7092 +2323 6910 +2328 6075 +2329 2330 +2329 2331 +2331 2413 +2331 3625 +2331 3629 +2332 2333 +2332 2334 +2332 2335 +2332 2336 +2333 3702 +2337 2338 +2337 2339 +2337 2340 +2338 2339 +2338 2340 +2339 2340 +2341 2796 +2342 2343 +2342 5438 +2344 2345 +2345 5794 +2346 4704 +2347 2348 +2347 2349 +2347 2350 +2347 2351 +2347 2352 +2347 2353 +2347 2354 +2350 5831 +2354 2531 +2355 3776 +2356 2357 +2356 2358 +2356 2359 +2356 2360 +2359 2360 +2360 6822 +2365 2366 +2365 7018 +2367 5689 +2367 6038 +2368 3498 +2369 2370 +2370 2371 +2373 2984 +2373 3748 +2373 3914 +2373 4347 +2375 3829 +2375 5376 +2379 2704 +2379 2731 +2382 2383 +2382 2384 +2382 2385 +2386 2387 +2388 2389 +2388 2390 +2388 2391 +2388 2392 +2388 2393 +2388 2394 +2388 5317 +2390 3037 +2390 3038 +2390 3039 +2390 3040 +2390 3041 +2390 3042 +2390 3043 +2390 3044 +2390 3045 +2395 2396 +2396 2500 +2397 3488 +2398 3490 +2398 3493 +2398 3494 +2399 2975 +2399 2996 +2399 3618 +2399 3621 +2400 2401 +2403 2404 +2406 2407 +2408 2531 +2409 4344 +2411 3843 +2413 2432 +2413 4949 +2418 6057 +2419 2613 +2419 2776 +2419 5163 +2419 6057 +2421 2422 +2421 2423 +2423 5954 +2424 6333 +2425 2426 +2426 2945 +2427 4876 +2427 6453 +2428 2429 +2430 6133 +2432 6573 +2432 6574 +2432 6575 +2433 2434 +2433 2435 +2433 2436 +2433 2437 +2433 2438 +2434 2437 +2434 2644 +2434 2645 +2434 2646 +2434 2647 +2434 2648 +2435 4763 +2435 4764 +2439 3081 +2441 3693 +2443 2444 +2443 2445 +2443 2446 +2443 6957 +2445 3096 +2446 4815 +2447 3800 +2447 6895 +2448 3443 +2449 2450 +2449 2451 +2449 2452 +2449 2922 +2450 2452 +2450 3829 +2450 4583 +2450 6820 +2451 2452 +2451 3829 +2452 2597 +2452 2598 +2452 2599 +2452 2923 +2452 3839 +2452 3888 +2452 4275 +2452 4530 +2452 5912 +2453 2555 +2454 2455 +2454 2456 +2457 2458 +2457 2808 +2459 2460 +2459 2495 +2460 2495 +2461 2462 +2463 4431 +2464 3477 +2465 3352 +2466 3960 +2467 2468 +2469 2470 +2471 2472 +2471 2473 +2471 2474 +2471 2475 +2471 2476 +2472 6096 +2473 5943 +2473 6096 +2473 6124 +2473 6301 +2473 6960 +2474 6096 +2475 6096 +2476 6096 +2477 2478 +2477 3963 +2479 5752 +2481 2531 +2481 5101 +2482 2494 +2482 3210 +2483 2484 +2484 2704 +2484 4115 +2484 4802 +2484 5911 +2485 3027 +2486 3733 +2487 2488 +2488 3070 +2488 3324 +2488 4060 +2488 4253 +2488 4254 +2488 4255 +2488 4256 +2489 4261 +2490 2491 +2492 3317 +2493 2494 +2495 2496 +2495 2497 +2495 2498 +2495 2499 +2496 2499 +2498 2653 +2499 2592 +2501 4203 +2502 2503 +2502 2504 +2502 2505 +2502 2506 +2502 2507 +2502 2508 +2502 2509 +2502 2510 +2502 2511 +2512 4594 +2513 2514 +2513 2515 +2513 5480 +2513 5677 +2515 3249 +2517 5981 +2518 2862 +2520 2549 +2520 4152 +2520 4153 +2521 3854 +2521 6647 +2522 3094 +2524 4015 +2525 4458 +2526 2527 +2526 2528 +2526 2529 +2526 2530 +2526 3292 +2527 2862 +2527 7179 +2528 2529 +2528 3292 +2528 3294 +2529 2530 +2530 2588 +2531 2532 +2531 2533 +2531 2534 +2531 2535 +2531 2536 +2531 2537 +2531 2538 +2531 2539 +2531 2540 +2531 2541 +2531 2542 +2531 2543 +2531 2544 +2531 2545 +2533 3798 +2535 7174 +2536 2910 +2536 3174 +2539 3341 +2539 4344 +2539 4398 +2539 4399 +2541 3858 +2541 5398 +2543 4564 +2543 4678 +2543 4679 +2548 3540 +2549 2550 +2549 2551 +2549 2552 +2549 2555 +2549 3963 +2549 5422 +2549 6674 +2550 2555 +2550 3267 +2553 2554 +2553 2555 +2553 2556 +2555 3321 +2558 2559 +2558 2560 +2558 2561 +2558 2562 +2558 5827 +2561 3310 +2563 2564 +2563 4195 +2564 7288 +2565 6659 +2566 5093 +2570 5181 +2571 2572 +2571 2573 +2571 2574 +2571 2575 +2571 2576 +2571 2577 +2574 2948 +2574 3298 +2574 6340 +2577 3721 +2578 2579 +2578 2580 +2579 2580 +2581 2582 +2581 2583 +2581 2584 +2581 2585 +2581 2586 +2587 2589 +2587 3585 +2588 4970 +2589 3268 +2589 4942 +2590 3458 +2590 4306 +2590 6581 +2592 2750 +2594 2846 +2594 3853 +2595 2872 +2596 4801 +2597 2598 +2597 2599 +2597 3829 +2597 3836 +2597 3839 +2597 4583 +2599 3829 +2599 6820 +2601 3176 +2604 2605 +2607 2608 +2607 2609 +2610 2611 +2613 6576 +2620 6057 +2639 2640 +2641 2642 +2641 2643 +2643 3198 +2643 3199 +2643 3733 +2643 4988 +2643 5939 +2643 6016 +2644 2646 +2644 2647 +2644 5015 +2648 6907 +2649 4831 +2650 2651 +2650 2652 +2650 2653 +2650 2654 +2651 4443 +2652 2653 +2652 3585 +2653 2654 +2653 3027 +2653 3028 +2653 3585 +2653 6638 +2653 6639 +2654 3585 +2655 2656 +2655 2657 +2655 2658 +2655 2659 +2655 2660 +2655 3062 +2655 4876 +2658 3098 +2659 3103 +2662 2663 +2662 2664 +2662 2665 +2662 2666 +2662 2667 +2662 2668 +2662 2669 +2670 4673 +2672 2673 +2674 2675 +2674 2676 +2674 2677 +2674 2678 +2674 2679 +2676 4195 +2678 2683 +2679 4195 +2680 2681 +2680 2682 +2683 3673 +2684 3344 +2685 2686 +2685 2687 +2685 2688 +2685 2689 +2685 2802 +2685 4126 +2686 7175 +2687 2688 +2687 2801 +2687 2802 +2687 2803 +2687 2804 +2687 2805 +2687 2806 +2687 4126 +2688 2799 +2688 2800 +2688 2801 +2688 2802 +2688 4126 +2690 2691 +2690 2692 +2690 2693 +2690 2694 +2692 2693 +2692 3154 +2695 2696 +2695 2697 +2696 2697 +2696 3142 +2696 3143 +2698 5699 +2700 4689 +2701 4160 +2702 2703 +2704 2705 +2706 2707 +2708 2709 +2708 2710 +2708 2711 +2708 2712 +2708 5874 +2710 2958 +2710 6722 +2713 5880 +2714 2715 +2714 2716 +2714 2717 +2714 2718 +2714 2719 +2715 2716 +2715 2717 +2715 2719 +2715 2925 +2715 2926 +2715 2927 +2715 2928 +2715 2929 +2715 2930 +2715 2931 +2715 2932 +2715 2933 +2715 2934 +2715 2935 +2715 2936 +2715 5631 +2715 5877 +2715 5962 +2715 6817 +2715 7194 +2720 2721 +2720 2722 +2720 2723 +2724 5862 +2725 2726 +2725 2727 +2725 2728 +2725 2729 +2726 2728 +2726 3816 +2727 2728 +2727 3816 +2728 3816 +2729 3816 +2732 3605 +2733 2734 +2733 2735 +2733 2736 +2733 2737 +2734 2737 +2738 2739 +2738 2740 +2741 2742 +2741 2743 +2741 2744 +2742 2774 +2742 7310 +2745 2746 +2750 2752 +2750 5139 +2752 3027 +2753 2774 +2753 2779 +2753 2915 +2753 2916 +2753 2917 +2753 2918 +2753 4415 +2753 6667 +2753 7179 +2755 3077 +2755 3736 +2761 5629 +2762 2763 +2762 2764 +2762 2765 +2762 2766 +2762 2767 +2762 2768 +2762 2769 +2763 2764 +2763 2765 +2763 2766 +2763 2767 +2763 2768 +2763 2769 +2763 5120 +2763 6645 +2763 7036 +2764 2765 +2764 2766 +2764 2767 +2764 2768 +2764 2769 +2765 2766 +2765 2767 +2765 2768 +2765 2769 +2765 4645 +2765 4945 +2766 2767 +2766 2768 +2766 2769 +2767 2768 +2767 2769 +2768 2769 +2768 5421 +2768 6385 +2768 6571 +2770 2771 +2770 2772 +2770 2773 +2770 2774 +2770 2775 +2771 2775 +2772 2775 +2773 2775 +2774 2775 +2774 3575 +2774 3895 +2774 5400 +2775 3807 +2779 4580 +2780 3829 +2781 2782 +2781 2783 +2781 2784 +2781 2785 +2785 5803 +2786 2787 +2786 2788 +2786 2789 +2789 4866 +2790 3493 +2791 6437 +2791 6438 +2792 4730 +2793 4716 +2794 2795 +2796 2797 +2798 2846 +2800 2802 +2800 7106 +2801 2802 +2802 4126 +2807 4642 +2808 2809 +2808 2810 +2808 2811 +2808 2812 +2808 2813 +2812 3617 +2814 2815 +2816 2817 +2819 2820 +2823 2824 +2823 6055 +2825 4991 +2826 2827 +2828 2829 +2828 2830 +2828 5541 +2831 5878 +2832 5879 +2833 2919 +2834 2835 +2836 2837 +2837 5125 +2838 2839 +2838 2840 +2841 2842 +2842 2843 +2842 2844 +2846 2847 +2846 2848 +2846 2849 +2850 3855 +2851 6532 +2852 2853 +2852 3510 +2853 3510 +2853 4146 +2853 4147 +2853 4148 +2854 2855 +2856 3936 +2857 3863 +2862 2975 +2862 3339 +2862 7179 +2863 3292 +2863 3294 +2863 3510 +2870 7100 +2871 4683 +2872 2873 +2872 2874 +2872 2875 +2872 2876 +2872 2877 +2874 5500 +2875 5926 +2876 5691 +2876 5926 +2878 2958 +2878 3317 +2879 2880 +2908 2909 +2910 2911 +2910 2912 +2910 3702 +2911 6036 +2913 4716 +2914 6667 +2915 3624 +2916 5590 +2919 2920 +2919 5477 +2922 2923 +2922 4803 +2923 3829 +2926 7193 +2932 5080 +2937 2938 +2937 2939 +2937 2940 +2937 5416 +2940 5016 +2941 3540 +2942 2943 +2944 3702 +2946 4708 +2947 4364 +2948 3334 +2949 2950 +2950 6913 +2951 2952 +2951 2953 +2951 2954 +2951 2955 +2953 5285 +2953 6962 +2956 3234 +2956 3235 +2956 3236 +2956 4923 +2956 6428 +2956 7054 +2957 6428 +2958 3287 +2958 3316 +2958 3317 +2958 3318 +2958 3319 +2958 4306 +2961 2962 +2963 2964 +2963 2965 +2963 2966 +2967 2968 +2967 2969 +2968 2969 +2970 2971 +2970 2972 +2973 3994 +2973 4145 +2974 2975 +2974 2976 +2974 2977 +2975 2996 +2975 2998 +2975 3618 +2975 3620 +2975 5093 +2975 7018 +2978 4580 +2979 4716 +2979 7154 +2980 2981 +2982 5189 +2983 3612 +2984 3748 +2984 3913 +2984 3914 +2985 3502 +2986 4195 +2987 3390 +2987 3488 +2993 6340 +2994 2995 +2996 2997 +2996 2998 +2997 3618 +2997 4158 +2998 3618 +2998 3619 +2999 3000 +3001 3844 +3003 3004 +3003 3005 +3003 3006 +3003 3311 +3003 3312 +3003 3315 +3003 4130 +3004 3005 +3004 3006 +3004 3315 +3005 3006 +3005 3315 +3005 4130 +3005 5285 +3006 3315 +3006 4130 +3007 3008 +3007 3009 +3007 3010 +3007 3011 +3007 3012 +3007 3013 +3008 5633 +3013 5357 +3014 3498 +3014 5003 +3015 3016 +3015 3017 +3015 3018 +3015 3019 +3016 3017 +3016 3018 +3016 3204 +3016 4777 +3018 3653 +3018 3654 +3018 4777 +3020 3021 +3022 6020 +3023 6020 +3025 4421 +3026 3458 +3027 3028 +3027 3029 +3031 3311 +3032 6900 +3033 3034 +3033 3035 +3033 3036 +3041 6333 +3041 6456 +3046 3047 +3046 3048 +3046 3049 +3046 4415 +3046 6227 +3046 6452 +3046 7022 +3050 5615 +3050 5618 +3052 3053 +3054 3055 +3055 4015 +3056 3057 +3056 3156 +3058 3059 +3060 3061 +3062 3063 +3062 3064 +3062 3065 +3066 3067 +3066 3068 +3069 3661 +3071 3072 +3071 3073 +3075 3076 +3075 3077 +3075 3078 +3075 3079 +3075 3080 +3081 3082 +3081 3083 +3081 3084 +3081 3085 +3081 3086 +3081 3087 +3086 4197 +3089 3090 +3092 3600 +3093 3094 +3093 3095 +3093 3096 +3093 3548 +3094 4171 +3094 4244 +3094 4245 +3094 4246 +3094 4247 +3094 4248 +3094 4249 +3094 4250 +3095 5865 +3096 5480 +3096 5865 +3097 3134 +3098 3099 +3098 3100 +3098 3101 +3098 3102 +3098 3103 +3098 3104 +3098 3105 +3098 3106 +3098 3107 +3098 3108 +3102 3106 +3102 6840 +3103 3467 +3105 3106 +3106 4169 +3106 4170 +3108 6840 +3109 3110 +3112 3113 +3117 3118 +3117 3119 +3120 3121 +3120 3122 +3123 3472 +3124 3472 +3125 3126 +3128 6918 +3129 3130 +3129 3131 +3129 3132 +3133 5198 +3134 3135 +3134 3136 +3134 3137 +3134 6673 +3135 3136 +3135 3137 +3138 3602 +3140 3141 +3143 4760 +3144 3145 +3144 3146 +3144 3147 +3146 5503 +3146 6511 +3149 3150 +3149 3151 +3153 3904 +3153 4461 +3156 3157 +3156 3158 +3156 3159 +3156 3160 +3156 3161 +3156 3162 +3158 6467 +3161 4500 +3163 3164 +3163 3165 +3163 3166 +3164 3165 +3164 3166 +3164 3579 +3164 4692 +3165 3859 +3166 4323 +3167 3168 +3169 3170 +3169 3171 +3169 3172 +3169 3173 +3175 3448 +3176 3177 +3176 4569 +3177 3178 +3179 5909 +3180 3181 +3180 3182 +3183 3184 +3183 3185 +3186 3447 +3187 3188 +3187 3189 +3187 3190 +3187 4855 +3188 5479 +3195 3540 +3196 3197 +3196 3219 +3198 3199 +3198 3733 +3199 4993 +3199 4994 +3200 3201 +3202 6145 +3203 3416 +3207 3208 +3212 7233 +3215 3216 +3215 3605 +3216 3605 +3217 4804 +3218 4804 +3219 3220 +3221 3222 +3223 3292 +3226 3408 +3227 3733 +3228 3229 +3228 3230 +3228 3231 +3232 3233 +3234 3235 +3234 3236 +3234 3237 +3234 3238 +3234 3239 +3235 6165 +3235 6428 +3236 4565 +3236 6428 +3239 4015 +3239 7222 +3240 3241 +3240 3242 +3240 3243 +3241 4460 +3246 3255 +3247 5061 +3248 3252 +3248 3993 +3249 3252 +3249 5480 +3254 3255 +3255 3256 +3255 3426 +3259 3260 +3259 3261 +3259 3262 +3259 3263 +3259 3264 +3265 4970 +3266 4711 +3266 4878 +3269 3270 +3270 3271 +3270 4204 +3272 4489 +3273 4893 +3274 4804 +3275 3276 +3276 5633 +3277 3278 +3279 3280 +3281 5916 +3282 3960 +3283 3284 +3283 3285 +3283 5198 +3284 3285 +3284 5020 +3285 5020 +3286 5477 +3287 3288 +3287 3289 +3287 3290 +3287 3317 +3291 3317 +3291 4284 +3292 3293 +3292 3294 +3295 3296 +3297 4876 +3299 7047 +3305 3306 +3305 3307 +3305 3308 +3305 3309 +3307 5230 +3311 3312 +3311 3313 +3311 3314 +3311 3315 +3311 7235 +3312 3315 +3312 3857 +3312 6468 +3313 6393 +3313 7235 +3315 4130 +3315 5285 +3320 5317 +3321 3322 +3321 3323 +3325 3326 +3325 3327 +3325 3328 +3325 3329 +3325 3330 +3325 3331 +3325 3332 +3325 3333 +3329 3332 +3330 3332 +3331 3332 +3332 3333 +3332 4067 +3332 4068 +3332 4069 +3332 4070 +3332 4071 +3332 4072 +3333 5074 +3336 3337 +3338 5483 +3340 5444 +3342 3343 +3344 3345 +3344 4766 +3346 5884 +3349 3350 +3351 6995 +3353 3354 +3355 3356 +3355 3357 +3355 3358 +3355 3359 +3355 3360 +3355 3361 +3355 3362 +3355 3363 +3358 3363 +3362 3363 +3363 3364 +3363 3365 +3363 3366 +3363 3367 +3363 3368 +3363 3369 +3363 3370 +3371 3372 +3371 3373 +3371 3374 +3371 3375 +3371 3376 +3372 5165 +3373 4155 +3374 3428 +3377 4135 +3380 4149 +3380 4150 +3380 4151 +3381 3382 +3381 3383 +3384 3385 +3384 3386 +3384 3387 +3384 3388 +3384 3389 +3386 3389 +3386 4076 +3386 4077 +3386 4277 +3386 4278 +3390 3391 +3390 3392 +3390 3393 +3390 3394 +3390 3488 +3393 3503 +3395 4741 +3398 3569 +3400 3401 +3400 4262 +3402 5449 +3402 6846 +3405 3406 +3406 7039 +3407 4212 +3409 3410 +3411 3922 +3411 4599 +3412 3413 +3414 3415 +3416 3417 +3416 3418 +3417 4054 +3417 4551 +3420 6250 +3421 6567 +3425 3426 +3427 3701 +3427 4741 +3427 6845 +3428 3429 +3428 3430 +3428 3431 +3428 3432 +3434 3435 +3437 3438 +3439 3440 +3439 4690 +3440 3441 +3440 4562 +3440 5306 +3440 5591 +3443 3444 +3443 3445 +3443 3446 +3449 3450 +3449 3451 +3449 3452 +3454 3455 +3454 3456 +3454 3457 +3458 3482 +3458 4470 +3461 3462 +3461 3463 +3464 3465 +3464 3466 +3468 4655 +3469 3540 +3473 3930 +3474 3475 +3474 3577 +3475 3577 +3475 4592 +3477 3478 +3479 3560 +3480 3761 +3483 3581 +3483 3673 +3488 3489 +3490 3491 +3490 3492 +3490 3493 +3490 3494 +3490 3495 +3493 3578 +3494 3578 +3494 3591 +3494 3592 +3494 3621 +3496 3497 +3498 3499 +3498 3500 +3501 3502 +3502 3906 +3502 3944 +3502 4075 +3502 5265 +3502 5266 +3502 5267 +3502 5268 +3503 3504 +3503 3505 +3503 3997 +3503 6580 +3506 7112 +3507 4061 +3508 3509 +3510 3511 +3510 3512 +3510 3513 +3510 3514 +3510 3515 +3510 4401 +3510 7296 +3514 3576 +3516 3517 +3516 3518 +3516 3519 +3518 3519 +3520 3521 +3520 3522 +3520 3523 +3520 3524 +3525 3526 +3527 4344 +3528 3529 +3528 3530 +3528 3531 +3529 4497 +3532 6952 +3534 4065 +3535 3536 +3535 3537 +3537 3600 +3538 3929 +3539 4108 +3540 3541 +3540 3542 +3540 3543 +3540 3544 +3540 3545 +3540 3546 +3540 3547 +3546 3941 +3546 4294 +3546 4295 +3548 3549 +3548 3550 +3548 6918 +3549 4192 +3551 3552 +3554 4018 +3556 3557 +3556 3558 +3559 3560 +3560 3638 +3560 4975 +3561 3562 +3563 3564 +3563 3565 +3566 4704 +3567 3568 +3568 3850 +3568 6447 +3569 3893 +3570 3621 +3572 3573 +3573 4061 +3580 4840 +3581 3582 +3582 3673 +3582 4690 +3582 5154 +3582 5306 +3583 4740 +3583 5248 +3584 5494 +3586 5116 +3586 5812 +3588 4995 +3590 3848 +3593 3594 +3593 3595 +3593 3596 +3593 3597 +3593 6925 +3597 7178 +3598 3901 +3600 3601 +3602 3603 +3602 3604 +3605 3666 +3606 5249 +3608 3609 +3608 3610 +3608 3611 +3609 3611 +3613 3614 +3615 3935 +3616 4158 +3616 4862 +3616 7330 +3618 3619 +3618 3620 +3618 3621 +3619 3620 +3621 3847 +3621 3860 +3621 4003 +3621 4004 +3622 4158 +3624 3625 +3624 3626 +3624 3627 +3624 3628 +3624 3629 +3624 5404 +3624 6566 +3625 3629 +3625 3682 +3625 4036 +3628 3629 +3629 3682 +3629 4120 +3629 6667 +3632 4366 +3633 3634 +3635 4121 +3636 5197 +3637 4160 +3639 4716 +3640 3641 +3640 3642 +3641 3642 +3641 4824 +3642 4824 +3642 5403 +3644 5911 +3645 3646 +3645 3647 +3646 3647 +3649 3731 +3649 4476 +3650 6053 +3651 3673 +3652 4993 +3652 4994 +3656 3657 +3656 3658 +3656 3659 +3657 3658 +3660 4489 +3661 3662 +3663 5835 +3664 3665 +3667 3764 +3668 4373 +3670 3671 +3670 4426 +3671 4426 +3673 3674 +3673 3675 +3673 3676 +3673 3677 +3676 5154 +3676 6147 +3676 7154 +3678 4336 +3680 3681 +3682 4232 +3683 3684 +3683 3685 +3686 5040 +3687 3688 +3687 4590 +3690 3691 +3690 3692 +3694 3829 +3696 4460 +3697 3991 +3698 6795 +3699 3700 +3701 6845 +3702 3703 +3703 6765 +3704 3705 +3708 4755 +3709 5374 +3710 6916 +3711 5252 +3712 3713 +3714 6270 +3715 5284 +3716 3717 +3718 7002 +3719 3720 +3722 4476 +3723 3747 +3723 4232 +3724 4778 +3725 4207 +3725 4783 +3726 3727 +3728 4447 +3729 4035 +3730 5260 +3733 3734 +3735 5016 +3737 4065 +3738 5101 +3739 4580 +3740 3741 +3740 3742 +3742 5477 +3743 5252 +3743 6379 +3743 7114 +3744 3745 +3744 3746 +3748 3913 +3748 3914 +3749 4489 +3750 3751 +3750 3752 +3750 3753 +3754 4491 +3756 3757 +3757 3758 +3762 4226 +3764 3765 +3764 3766 +3764 3767 +3764 3768 +3765 5732 +3766 4455 +3769 7248 +3769 7249 +3771 3871 +3771 3872 +3771 3873 +3771 3874 +3771 3875 +3771 3876 +3771 3877 +3771 3878 +3772 3773 +3774 4553 +3775 4791 +3777 3778 +3777 3779 +3780 3781 +3780 3782 +3781 3782 +3781 3885 +3782 3885 +3783 3784 +3783 3785 +3783 3786 +3783 3787 +3784 3786 +3786 3787 +3786 5346 +3788 6212 +3790 3791 +3790 3792 +3790 3793 +3790 3794 +3790 3795 +3790 3796 +3790 3797 +3791 3795 +3793 3795 +3794 3795 +3795 4299 +3796 5649 +3798 3799 +3798 7173 +3799 4113 +3800 3801 +3800 3802 +3800 3803 +3802 4730 +3804 3805 +3805 4426 +3805 5018 +3808 3809 +3810 3811 +3811 3812 +3814 4217 +3815 4927 +3817 3818 +3817 3819 +3817 3820 +3817 3821 +3817 3822 +3818 5215 +3818 5216 +3818 5217 +3820 5215 +3820 5217 +3823 3824 +3825 3826 +3825 3827 +3825 3828 +3829 3830 +3829 3831 +3829 3832 +3829 3833 +3829 3834 +3829 3835 +3829 3836 +3829 3837 +3829 3838 +3829 3839 +3830 4167 +3834 6820 +3836 3839 +3837 4716 +3839 4528 +3839 4529 +3839 4530 +3840 3841 +3842 4785 +3843 3844 +3843 3845 +3844 3845 +3844 4081 +3844 4471 +3845 4081 +3845 4790 +3847 4163 +3850 3851 +3850 3852 +3850 5198 +3851 5855 +3853 3854 +3854 3927 +3854 4045 +3854 4175 +3854 4176 +3854 4177 +3854 4803 +3860 4716 +3861 4723 +3861 4724 +3862 6135 +3864 3865 +3864 3866 +3865 3866 +3866 4770 +3867 3868 +3867 3869 +3867 3870 +3872 4021 +3873 4021 +3873 6353 +3873 6655 +3879 3880 +3879 3881 +3879 3883 +3879 5210 +3879 6371 +3880 3882 +3880 3883 +3884 3905 +3884 3906 +3884 6130 +3884 6463 +3886 6353 +3886 6655 +3887 4903 +3889 6818 +3890 3891 +3894 4727 +3895 3896 +3897 4059 +3898 3899 +3900 3901 +3902 4580 +3903 3904 +3905 3906 +3905 6463 +3908 3909 +3910 4479 +3911 3912 +3913 4506 +3914 4504 +3914 4505 +3915 3916 +3918 3919 +3918 3920 +3921 6092 +3921 6342 +3922 3923 +3924 3925 +3926 5434 +3928 5371 +3929 3930 +3929 3931 +3929 3932 +3929 3933 +3929 4431 +3930 3931 +3930 3976 +3930 4117 +3930 4432 +3931 6662 +3934 4197 +3937 3938 +3937 3939 +3942 6757 +3943 4195 +3943 4197 +3945 3946 +3945 3947 +3947 4074 +3947 5253 +3949 3950 +3949 3951 +3953 3954 +3953 4228 +3953 4229 +3953 4230 +3953 4599 +3954 4228 +3954 4229 +3954 4230 +3955 4058 +3956 4330 +3957 3958 +3957 3959 +3960 3961 +3961 5219 +3962 4421 +3964 3965 +3964 3966 +3964 3967 +3964 3968 +3964 3969 +3964 3970 +3964 3971 +3965 3967 +3965 3969 +3966 3967 +3966 3969 +3967 3968 +3967 3969 +3967 3970 +3967 3971 +3968 3969 +3969 3970 +3969 3971 +3973 6932 +3977 7235 +3978 4015 +3981 6384 +3982 3983 +3983 5308 +3984 3985 +3986 5231 +3987 7288 +3989 3990 +3994 3995 +3994 3996 +3997 3998 +3997 3999 +4001 4035 +4005 5404 +4005 6413 +4005 6414 +4005 6647 +4011 5319 +4011 6034 +4013 4791 +4014 4059 +4015 4032 +4016 4017 +4016 4018 +4017 5249 +4018 4791 +4018 4792 +4018 5076 +4018 5077 +4019 4020 +4022 7142 +4033 4034 +4036 4656 +4037 4038 +4037 4039 +4040 4041 +4042 4043 +4044 5550 +4046 4047 +4048 5524 +4049 4050 +4052 4741 +4052 6554 +4053 4804 +4055 4056 +4057 4058 +4062 4518 +4064 4967 +4065 4066 +4070 4071 +4071 4072 +4071 6886 +4073 5085 +4074 4076 +4076 4077 +4076 4078 +4080 6884 +4082 4083 +4085 4086 +4085 4087 +4088 4089 +4088 4090 +4089 4090 +4092 4093 +4092 4094 +4092 4095 +4092 4096 +4092 4097 +4092 4098 +4094 6457 +4098 4230 +4099 4100 +4099 4101 +4099 4102 +4099 4103 +4104 5957 +4105 4106 +4105 4107 +4109 4110 +4109 4111 +4109 4112 +4112 4163 +4113 4114 +4115 4116 +4118 5249 +4120 6566 +4121 4122 +4123 4446 +4124 4217 +4125 6277 +4126 4127 +4126 4128 +4126 4129 +4126 4130 +4126 4131 +4126 4132 +4126 4133 +4128 6282 +4134 4135 +4134 4136 +4134 4137 +4138 4139 +4138 4140 +4141 5300 +4143 5565 +4157 4364 +4158 4159 +4160 4161 +4160 4162 +4163 4164 +4163 4165 +4166 6659 +4167 5100 +4168 7038 +4172 5776 +4173 5513 +4182 5252 +4183 4184 +4185 4186 +4187 4458 +4188 4189 +4190 4191 +4192 4193 +4192 4194 +4195 4196 +4195 4197 +4195 4198 +4195 4199 +4197 4199 +4197 5264 +4201 5442 +4202 4232 +4202 6704 +4204 4205 +4204 4206 +4205 4206 +4207 4783 +4209 4210 +4209 4211 +4213 4214 +4213 4215 +4215 4690 +4215 4830 +4216 7133 +4218 4219 +4218 6087 +4218 6088 +4218 6089 +4219 6087 +4219 6088 +4219 6089 +4220 4221 +4222 4223 +4224 4225 +4228 4229 +4228 4230 +4228 4231 +4229 4230 +4230 4231 +4230 4359 +4232 4233 +4234 4235 +4234 4236 +4234 4237 +4234 4238 +4234 4239 +4237 4238 +4240 4241 +4242 5156 +4244 6782 +4251 4252 +4257 5352 +4258 5319 +4262 4263 +4262 4741 +4264 4265 +4264 4266 +4264 4267 +4264 4268 +4266 4267 +4266 4943 +4266 4944 +4267 4943 +4267 4944 +4267 5852 +4269 4270 +4269 6289 +4270 6289 +4271 4767 +4272 4366 +4273 4274 +4276 4425 +4278 4973 +4278 6281 +4279 4280 +4279 4281 +4280 4281 +4282 4283 +4284 4373 +4285 4286 +4288 4487 +4290 6127 +4290 6129 +4290 6130 +4292 4293 +4296 4297 +4298 4791 +4300 4301 +4302 4303 +4303 4304 +4303 6130 +4304 4305 +4304 6130 +4306 4307 +4308 6508 +4309 4310 +4309 4311 +4309 4312 +4309 4313 +4310 4808 +4310 5731 +4314 4315 +4314 4316 +4314 4317 +4318 4319 +4318 4320 +4319 4320 +4321 6135 +4322 4545 +4324 7149 +4325 4326 +4325 4327 +4325 4328 +4325 4329 +4326 4327 +4326 4328 +4326 4329 +4327 4328 +4328 4329 +4330 4331 +4330 4332 +4330 4333 +4330 4334 +4334 5787 +4335 5194 +4337 4338 +4339 4340 +4339 4341 +4340 4341 +4342 6681 +4342 6751 +4342 6953 +4343 4590 +4344 4345 +4344 4346 +4348 4433 +4348 6200 +4349 6518 +4350 5478 +4351 4352 +4351 4353 +4351 4354 +4351 4355 +4356 4487 +4357 6342 +4358 5207 +4358 5929 +4360 4373 +4360 6405 +4361 4362 +4363 4708 +4364 4365 +4364 5380 +4364 5835 +4366 4367 +4368 5251 +4369 4370 +4371 4372 +4372 4519 +4372 4521 +4373 4374 +4373 4375 +4374 6014 +4376 4377 +4378 4893 +4380 5189 +4380 5198 +4381 4382 +4381 4383 +4381 4384 +4385 4386 +4387 4388 +4387 4389 +4387 4390 +4388 6601 +4391 6653 +4392 5885 +4393 4741 +4394 5056 +4394 6353 +4394 6655 +4395 5357 +4397 5098 +4397 5827 +4400 7011 +4402 5118 +4403 6419 +4404 4405 +4404 4406 +4405 4406 +4405 6015 +4406 6015 +4407 7133 +4408 4409 +4410 4411 +4410 4412 +4411 4412 +4413 4755 +4415 4416 +4415 4417 +4415 4418 +4415 4419 +4415 4420 +4416 7179 +4418 6178 +4421 4422 +4421 4423 +4424 7245 +4427 4428 +4429 4468 +4429 4469 +4433 4434 +4433 4435 +4433 4436 +4433 4437 +4433 4438 +4433 4439 +4433 4440 +4433 4441 +4442 6899 +4444 4445 +4445 6551 +4447 4448 +4449 4450 +4449 4451 +4449 4452 +4449 4453 +4449 5568 +4449 5569 +4450 4451 +4450 5568 +4451 5462 +4451 5568 +4451 5569 +4454 6129 +4455 4456 +4456 6124 +4457 6476 +4458 4459 +4460 6014 +4461 4462 +4461 4463 +4466 6114 +4467 6353 +4467 6655 +4468 4469 +4469 5624 +4472 4473 +4474 4475 +4476 4477 +4476 4478 +4476 4479 +4477 4479 +4477 5390 +4477 5391 +4478 4479 +4480 4481 +4480 6859 +4483 5173 +4484 4485 +4484 4486 +4484 4487 +4487 4958 +4487 4959 +4487 4960 +4488 4655 +4490 5011 +4490 6034 +4491 4492 +4498 4499 +4499 5248 +4501 4502 +4507 6092 +4508 4509 +4510 6123 +4511 4512 +4511 7154 +4515 5326 +4516 4517 +4519 4520 +4519 4521 +4522 7034 +4523 4524 +4525 4526 +4530 6484 +4530 6488 +4531 4532 +4533 4534 +4535 4536 +4537 4550 +4538 7154 +4541 5909 +4542 4543 +4544 4654 +4544 4655 +4546 4547 +4546 4548 +4548 5522 +4548 6296 +4549 4640 +4549 6090 +4552 6853 +4557 6634 +4558 4559 +4560 4626 +4563 6387 +4564 4678 +4565 4566 +4565 4567 +4568 6527 +4571 4572 +4571 4573 +4574 4575 +4574 4576 +4575 4576 +4577 4578 +4578 4655 +4579 4631 +4580 4581 +4580 4582 +4580 4583 +4580 4584 +4580 4585 +4580 4586 +4580 4587 +4580 4588 +4580 4589 +4580 4590 +4583 4893 +4588 5502 +4590 4591 +4592 4593 +4594 4595 +4596 5284 +4597 4598 +4599 4600 +4599 4601 +4599 4602 +4599 4603 +4599 4604 +4599 4605 +4599 4606 +4599 4607 +4599 4608 +4599 4609 +4599 4610 +4599 4611 +4599 4612 +4602 4603 +4613 4614 +4613 4615 +4613 4616 +4613 4617 +4613 4618 +4613 4619 +4613 4620 +4613 4693 +4614 4693 +4615 4673 +4615 4674 +4615 4693 +4622 6427 +4624 4625 +4627 4628 +4628 7329 +4629 4630 +4629 4631 +4629 4632 +4629 4633 +4629 6125 +4631 4632 +4631 4633 +4631 5415 +4632 6208 +4634 5502 +4641 4642 +4644 4791 +4644 4880 +4645 4646 +4646 5657 +4647 4893 +4648 4649 +4650 6211 +4652 4653 +4654 4655 +4657 6127 +4657 6129 +4657 6130 +4658 6232 +4659 4660 +4661 7321 +4662 4663 +4664 4665 +4664 4666 +4664 5578 +4664 6045 +4665 4667 +4665 5578 +4666 6786 +4668 6963 +4669 4670 +4671 5260 +4673 4674 +4673 4675 +4673 4676 +4673 4693 +4674 4676 +4674 5815 +4674 5817 +4677 6838 +4678 4679 +4678 4680 +4678 4681 +4678 4682 +4684 4685 +4684 4686 +4684 4687 +4684 7265 +4685 4686 +4685 4687 +4685 4719 +4685 7265 +4686 4687 +4687 7265 +4688 5118 +4690 4691 +4691 5414 +4694 4695 +4696 4697 +4696 4865 +4698 4699 +4701 4702 +4702 6792 +4705 5561 +4705 5827 +4705 6692 +4707 4708 +4707 4709 +4707 4710 +4708 4710 +4712 4713 +4714 4715 +4716 4717 +4718 5992 +4720 4721 +4720 4722 +4723 4724 +4725 4726 +4725 5716 +4728 4729 +4729 4961 +4729 5768 +4731 4732 +4731 4733 +4731 4734 +4735 4804 +4736 4737 +4736 4738 +4736 4739 +4736 4917 +4736 5875 +4738 5255 +4738 5256 +4738 5257 +4741 4742 +4741 4743 +4744 6906 +4745 4746 +4745 4747 +4750 4751 +4752 7017 +4753 4754 +4756 6075 +4757 4758 +4759 4828 +4761 4762 +4763 4933 +4763 6927 +4768 5443 +4768 5527 +4769 5954 +4772 4773 +4772 4774 +4772 5577 +4772 6505 +4773 4774 +4773 5577 +4773 6505 +4774 5577 +4775 4776 +4775 5961 +4776 5960 +4776 5961 +4778 4779 +4780 7256 +4781 4782 +4784 4785 +4784 4786 +4785 4787 +4785 4788 +4785 4789 +4785 5300 +4789 5589 +4791 4792 +4793 5396 +4794 5004 +4795 5238 +4796 5775 +4798 5305 +4799 4800 +4800 5741 +4804 4805 +4804 4806 +4807 5793 +4810 4811 +4810 4812 +4810 5061 +4813 7291 +4814 5805 +4815 4816 +4815 4817 +4815 4818 +4815 4819 +4817 5896 +4821 6289 +4822 4823 +4826 7116 +4827 7154 +4829 6499 +4831 4832 +4833 4834 +4833 4835 +4833 4836 +4833 4837 +4838 4839 +4839 5582 +4841 4842 +4841 4843 +4841 4844 +4841 4845 +4841 4846 +4841 4847 +4841 4848 +4844 4956 +4847 4848 +4857 4858 +4858 4877 +4858 5279 +4858 5316 +4859 4860 +4861 6801 +4862 4863 +4867 4868 +4873 6289 +4874 6751 +4875 6662 +4878 4879 +4880 4881 +4880 4882 +4880 6186 +4883 4884 +4883 4885 +4883 4886 +4883 4887 +4883 4888 +4889 4890 +4891 5445 +4892 5107 +4893 4894 +4893 4895 +4893 4896 +4893 4897 +4893 4898 +4895 7099 +4896 7099 +4899 5524 +4901 4902 +4904 4905 +4906 4907 +4909 4910 +4911 4912 +4913 4914 +4915 4916 +4915 4922 +4918 4919 +4925 6531 +4926 5397 +4927 4928 +4927 4929 +4927 4930 +4931 4932 +4934 5699 +4935 5914 +4935 6232 +4936 6151 +4937 5691 +4937 6782 +4938 7183 +4939 5300 +4940 5437 +4941 7095 +4946 4947 +4946 4948 +4949 4950 +4951 4952 +4953 7233 +4954 4955 +4956 4957 +4965 4966 +4968 4969 +4970 4971 +4972 4973 +4973 6280 +4974 5736 +4978 4979 +4980 5914 +4981 5012 +4982 4983 +4985 4986 +4988 4993 +4988 4994 +4988 6888 +4989 5887 +4990 5875 +4991 4992 +4991 5372 +4992 5087 +4992 5372 +4996 4997 +4998 4999 +5000 5001 +5000 5002 +5005 5006 +5005 5007 +5005 5008 +5009 5487 +5009 5488 +5011 5668 +5012 5013 +5012 5014 +5016 5017 +5019 5033 +5021 5022 +5021 5023 +5024 5025 +5024 5026 +5024 5027 +5024 5028 +5024 5029 +5024 5030 +5024 5031 +5029 6364 +5033 5034 +5033 5035 +5033 5036 +5033 5037 +5038 5923 +5038 6124 +5039 5040 +5042 6232 +5043 5804 +5044 5045 +5044 6296 +5045 5522 +5045 5648 +5045 5989 +5045 6276 +5045 7260 +5046 5891 +5047 6933 +5047 6934 +5048 5049 +5050 6974 +5051 5513 +5053 5755 +5054 6204 +5055 7299 +5057 6676 +5058 5623 +5059 5228 +5059 5300 +5059 5360 +5059 5943 +5059 6124 +5060 5930 +5061 5062 +5061 5063 +5064 5216 +5065 5066 +5067 5471 +5068 5752 +5072 6010 +5073 5930 +5073 6208 +5074 5075 +5075 5867 +5077 5864 +5078 5079 +5081 5082 +5083 5084 +5085 5086 +5089 5215 +5089 5217 +5090 6145 +5094 5095 +5095 6150 +5096 5280 +5097 5098 +5097 5099 +5098 5561 +5098 5827 +5101 5102 +5101 5103 +5101 5104 +5105 5550 +5108 5109 +5110 5111 +5112 5483 +5113 6554 +5114 6775 +5117 5445 +5118 5119 +5118 5120 +5118 6645 +5120 6645 +5121 5122 +5123 5124 +5126 5127 +5126 7272 +5128 5129 +5128 5130 +5128 5131 +5128 5132 +5128 5133 +5128 5134 +5128 5135 +5128 5136 +5128 5137 +5128 5138 +5129 5136 +5129 5137 +5130 5134 +5131 5136 +5131 5137 +5134 5136 +5134 5137 +5135 5136 +5135 5137 +5136 5137 +5136 5429 +5136 5430 +5136 5431 +5136 7283 +5136 7284 +5137 5429 +5137 5430 +5137 5431 +5140 6062 +5142 5943 +5143 6271 +5144 6791 +5145 5280 +5147 5529 +5148 5149 +5150 5151 +5152 5823 +5153 5235 +5155 6492 +5157 5158 +5159 6387 +5160 6606 +5161 6579 +5162 6669 +5164 6227 +5165 5455 +5166 5595 +5167 5691 +5167 5926 +5169 6379 +5171 5479 +5172 6973 +5175 5176 +5177 5178 +5177 5179 +5177 5180 +5178 5179 +5178 5180 +5179 5180 +5182 5183 +5184 5185 +5184 5186 +5187 7033 +5188 6340 +5189 5190 +5189 5191 +5189 5198 +5189 5855 +5192 6790 +5193 6557 +5194 5195 +5196 6112 +5197 6695 +5198 5199 +5198 5200 +5198 5201 +5198 5202 +5200 6602 +5200 6603 +5200 7248 +5200 7249 +5204 5205 +5204 5206 +5207 5208 +5207 5929 +5208 5929 +5208 6325 +5209 6371 +5211 6039 +5212 5213 +5212 5214 +5215 5216 +5215 5217 +5215 5218 +5216 5217 +5217 5218 +5220 5677 +5221 5222 +5223 5224 +5223 5225 +5223 5226 +5223 5227 +5228 5229 +5230 5231 +5231 5814 +5232 5233 +5232 5234 +5235 5236 +5235 5237 +5239 5240 +5241 5248 +5242 5243 +5242 5244 +5245 5246 +5245 5247 +5249 5250 +5258 5259 +5260 5261 +5260 5262 +5260 5263 +5269 5270 +5269 5271 +5274 5275 +5276 5277 +5278 6829 +5280 5281 +5280 5282 +5283 5579 +5285 5286 +5285 6962 +5285 7203 +5288 5289 +5290 6703 +5291 6208 +5292 6208 +5295 5845 +5295 6562 +5296 5775 +5297 5298 +5299 6000 +5301 6058 +5303 7201 +5304 7273 +5307 6621 +5307 7139 +5309 5310 +5311 5312 +5311 5313 +5315 6383 +5316 6896 +5319 6034 +5320 5321 +5322 5787 +5322 5796 +5322 6929 +5322 6952 +5323 5778 +5324 7098 +5325 6958 +5327 5328 +5339 5340 +5340 5873 +5342 7055 +5343 6769 +5344 5345 +5347 5348 +5349 5350 +5354 5355 +5354 5356 +5357 5358 +5357 6210 +5360 5361 +5360 5362 +5360 6248 +5361 6248 +5363 5471 +5364 5365 +5365 6021 +5366 6578 +5367 6448 +5368 6197 +5369 5370 +5373 7309 +5375 5376 +5378 5662 +5379 7098 +5380 5381 +5380 5382 +5380 5383 +5380 5384 +5385 5386 +5385 5387 +5385 5388 +5385 5389 +5392 5393 +5392 5394 +5392 5399 +5394 5736 +5395 5699 +5401 5402 +5404 7198 +5407 5408 +5409 5987 +5410 5411 +5410 5412 +5413 6208 +5415 5437 +5416 5417 +5416 5418 +5416 5419 +5416 5420 +5421 5802 +5421 5862 +5421 6385 +5423 6340 +5424 5425 +5424 5697 +5424 5698 +5425 5697 +5425 5698 +5426 5427 +5426 5428 +5432 5433 +5435 5988 +5436 6733 +5439 5835 +5440 5441 +5443 5897 +5446 5583 +5450 5451 +5452 5453 +5452 5454 +5456 5457 +5456 5458 +5456 5459 +5456 5460 +5456 5461 +5462 5568 +5462 5569 +5463 6670 +5464 6670 +5465 5466 +5466 6845 +5467 5533 +5468 5469 +5468 5470 +5469 5470 +5471 5472 +5471 5473 +5474 5475 +5476 6768 +5481 5482 +5483 5484 +5483 5485 +5483 5486 +5487 5488 +5487 5489 +5491 5563 +5492 5493 +5495 5751 +5495 6508 +5496 5497 +5496 5498 +5496 5499 +5503 5504 +5505 5506 +5508 5509 +5508 6812 +5509 6812 +5509 6813 +5510 6250 +5511 5512 +5514 5515 +5514 5516 +5517 6324 +5518 6124 +5519 5520 +5521 6296 +5523 6670 +5525 5526 +5527 5528 +5527 5897 +5529 5530 +5529 5531 +5529 5532 +5533 5534 +5535 5536 +5535 5537 +5535 5538 +5535 5539 +5535 5540 +5535 5541 +5536 5541 +5537 5541 +5538 5541 +5539 5541 +5540 5541 +5541 5542 +5543 6087 +5543 6088 +5543 6089 +5543 6447 +5544 5545 +5546 5868 +5547 5548 +5550 5551 +5550 5552 +5550 5553 +5553 5622 +5554 5555 +5554 6907 +5556 5823 +5557 6296 +5558 6301 +5558 6960 +5559 5560 +5562 6910 +5564 6279 +5566 5567 +5568 5569 +5569 5574 +5570 5571 +5570 5572 +5570 5573 +5575 5576 +5580 5581 +5584 5892 +5586 5587 +5586 5588 +5586 6012 +5586 6274 +5593 5752 +5594 6296 +5596 5597 +5599 6303 +5600 5601 +5602 6067 +5603 6535 +5604 5849 +5605 5606 +5605 5607 +5606 5607 +5608 5609 +5608 5610 +5608 5611 +5608 5612 +5611 6502 +5613 5626 +5615 5616 +5615 5617 +5615 5618 +5615 5619 +5616 5618 +5617 5618 +5618 5619 +5620 5621 +5622 5623 +5625 6223 +5627 6645 +5628 7248 +5628 7249 +5631 5632 +5633 5634 +5633 5635 +5633 5636 +5637 5638 +5637 5639 +5640 5641 +5642 5643 +5644 5645 +5644 5646 +5644 5647 +5648 6296 +5649 5650 +5649 5651 +5652 6204 +5653 5654 +5655 5656 +5657 5658 +5659 5660 +5659 6184 +5660 6184 +5661 6770 +5663 5664 +5663 5665 +5663 5666 +5663 5667 +5668 5669 +5670 5699 +5671 6199 +5672 6958 +5673 6127 +5673 6129 +5673 6130 +5674 6771 +5675 5676 +5678 5679 +5678 5680 +5681 5682 +5683 5684 +5683 5685 +5683 5686 +5683 5687 +5683 5688 +5689 5871 +5691 5692 +5691 5693 +5691 5926 +5694 5695 +5697 5698 +5699 5700 +5699 5701 +5699 5702 +5699 5703 +5699 5704 +5705 5706 +5707 6415 +5708 6652 +5709 5710 +5711 5712 +5713 5714 +5713 5715 +5714 5715 +5717 5718 +5719 5720 +5721 5722 +5723 7090 +5724 5725 +5726 5727 +5726 5728 +5726 5729 +5726 5730 +5727 5728 +5727 5729 +5730 7335 +5733 6877 +5734 5735 +5736 5737 +5736 5738 +5739 5740 +5742 5743 +5744 6561 +5745 5987 +5746 5787 +5747 7040 +5747 7041 +5748 6963 +5749 7000 +5750 7301 +5752 5753 +5752 5754 +5752 5755 +5754 5755 +5755 5913 +5756 6206 +5757 6651 +5758 5759 +5760 5936 +5761 5762 +5762 5763 +5764 5765 +5764 5766 +5764 5767 +5770 5771 +5773 5774 +5777 6248 +5779 5780 +5779 5781 +5780 5781 +5782 5783 +5782 5784 +5783 5784 +5787 5788 +5787 5789 +5787 5790 +5787 5791 +5787 5792 +5794 5795 +5796 5797 +5796 5798 +5796 5799 +5798 5904 +5798 6929 +5800 6009 +5801 6091 +5802 5862 +5806 5807 +5808 5809 +5810 5811 +5813 7097 +5815 5816 +5815 5817 +5818 5819 +5818 5820 +5818 5821 +5819 5820 +5819 5821 +5820 5821 +5822 6405 +5823 5824 +5823 5825 +5823 5826 +5828 5829 +5828 5933 +5829 5933 +5830 6232 +5832 5833 +5836 5837 +5838 5880 +5839 5840 +5842 5843 +5842 5844 +5845 7193 +5846 5858 +5847 5848 +5850 5851 +5853 5854 +5855 5856 +5859 5860 +5862 5863 +5865 5866 +5867 5868 +5868 7250 +5869 5870 +5872 6829 +5875 5876 +5876 6426 +5876 6519 +5876 6740 +5876 6741 +5876 6742 +5876 7096 +5880 5881 +5882 5883 +5886 5887 +5886 5888 +5887 6402 +5889 5890 +5889 6066 +5891 5892 +5893 6227 +5894 5895 +5898 6216 +5899 6944 +5900 5901 +5900 5902 +5900 6554 +5903 7186 +5904 5905 +5904 5906 +5907 5908 +5910 7148 +5914 5915 +5917 5918 +5919 6938 +5920 6199 +5921 6427 +5923 5924 +5923 5925 +5927 5928 +5930 5931 +5930 5932 +5934 6420 +5935 6711 +5936 5937 +5936 5938 +5937 7301 +5940 5941 +5942 6991 +5943 5944 +5945 5946 +5947 5948 +5951 7117 +5952 6493 +5953 5954 +5955 6553 +5956 6383 +5957 5958 +5957 5959 +5963 5964 +5965 5966 +5967 5968 +5969 7037 +5970 6910 +5971 5972 +5973 5974 +5975 5976 +5977 5978 +5979 6897 +5980 6068 +5981 5982 +5983 5984 +5985 5986 +5989 6296 +5990 6212 +5991 6296 +5993 6087 +5993 6088 +5993 6089 +5994 5995 +5994 5996 +5997 5998 +5998 6032 +5998 6033 +5999 6897 +6000 6001 +6002 6003 +6004 6839 +6005 6006 +6005 6007 +6008 6031 +6009 6010 +6011 6928 +6012 6013 +6017 6018 +6017 6019 +6022 6153 +6023 6751 +6024 6025 +6026 6027 +6028 6079 +6029 6030 +6034 6035 +6036 6037 +6038 7251 +6040 6041 +6040 6042 +6043 6044 +6043 6045 +6045 6786 +6046 6047 +6048 6049 +6050 6051 +6052 6087 +6052 6088 +6052 6089 +6055 6056 +6058 6059 +6060 6061 +6063 6064 +6065 6621 +6069 6070 +6071 6116 +6072 6073 +6072 6074 +6075 6076 +6075 6077 +6078 7154 +6079 6080 +6081 6082 +6083 6203 +6084 7151 +6085 7273 +6086 6802 +6087 6088 +6087 6089 +6088 6089 +6089 7193 +6092 6093 +6094 6095 +6096 6097 +6098 7093 +6099 6964 +6100 6101 +6100 6102 +6100 6103 +6104 6105 +6104 6106 +6104 6107 +6104 6108 +6105 6106 +6105 6107 +6106 6107 +6106 6108 +6107 6108 +6109 6207 +6110 7201 +6111 6375 +6113 6296 +6115 7150 +6117 6118 +6119 7105 +6120 6121 +6122 6341 +6125 6126 +6127 6128 +6127 6129 +6127 6130 +6127 6131 +6128 6436 +6129 6130 +6129 6131 +6129 6132 +6130 6131 +6133 6134 +6135 6136 +6135 6137 +6135 6138 +6135 6139 +6140 6141 +6140 6142 +6140 6143 +6140 6144 +6145 6146 +6152 6458 +6153 6154 +6155 6156 +6157 7085 +6158 7164 +6159 6669 +6160 6161 +6163 7185 +6164 6705 +6164 6914 +6165 6166 +6167 6168 +6169 6170 +6171 6172 +6173 6174 +6174 6444 +6174 7287 +6175 6176 +6177 6941 +6179 6180 +6179 6181 +6179 6182 +6179 6183 +6185 6224 +6187 6188 +6188 6189 +6190 6191 +6192 6977 +6193 6296 +6194 6396 +6195 6918 +6196 6227 +6198 6940 +6201 6202 +6205 6206 +6208 6209 +6213 6214 +6215 7149 +6216 6217 +6218 6219 +6220 7003 +6221 6959 +6222 6312 +6224 6225 +6226 6428 +6227 7022 +6228 6229 +6230 6231 +6233 6234 +6233 6235 +6234 6781 +6236 6237 +6237 6885 +6238 6948 +6238 6949 +6238 6950 +6239 6985 +6240 6947 +6241 6242 +6241 6243 +6244 6245 +6246 6247 +6249 7040 +6249 7041 +6251 6252 +6251 6253 +6251 6254 +6255 6256 +6255 6257 +6255 6258 +6259 6260 +6259 6261 +6259 6262 +6264 6383 +6265 6266 +6267 6995 +6268 6269 +6272 6273 +6273 7320 +6275 6822 +6278 7301 +6283 6284 +6283 6285 +6287 6289 +6290 6789 +6291 7163 +6292 7037 +6293 6960 +6294 6789 +6295 6942 +6296 6297 +6296 6298 +6299 6300 +6302 6346 +6304 6305 +6304 6306 +6308 7175 +6309 6310 +6309 6311 +6309 6933 +6309 6934 +6310 6311 +6310 7048 +6313 6962 +6314 6315 +6316 6902 +6317 6318 +6319 6320 +6319 6321 +6319 6322 +6319 6323 +6319 6395 +6319 6396 +6320 6321 +6320 6322 +6320 6323 +6320 6395 +6320 6396 +6321 6322 +6321 6323 +6321 6395 +6321 6396 +6321 6441 +6321 6442 +6321 6443 +6323 6396 +6326 7155 +6327 7068 +6328 6329 +6328 6330 +6331 6332 +6332 6903 +6334 7094 +6335 6959 +6336 6946 +6337 6735 +6338 6339 +6343 6344 +6345 6901 +6347 6348 +6349 6350 +6351 6352 +6353 6354 +6353 6355 +6354 6655 +6355 6655 +6356 6357 +6357 7065 +6358 6359 +6360 6361 +6362 6363 +6365 6901 +6367 6368 +6369 6370 +6371 6372 +6374 7038 +6376 6978 +6377 6944 +6379 6380 +6381 6789 +6382 6937 +6385 6386 +6387 6388 +6387 6389 +6387 6390 +6387 6391 +6387 6392 +6393 6394 +6395 6396 +6395 6397 +6396 6441 +6396 7134 +6396 7135 +6396 7136 +6396 7137 +6396 7138 +6398 7233 +6399 6400 +6403 6404 +6406 6407 +6406 6408 +6406 7018 +6409 6410 +6411 6412 +6416 6417 +6418 6852 +6420 6421 +6422 6423 +6424 6425 +6429 6430 +6429 6431 +6432 6433 +6434 6435 +6437 6438 +6439 6772 +6444 6602 +6444 6603 +6444 7248 +6444 7249 +6445 6446 +6449 6450 +6449 6451 +6450 6451 +6453 6454 +6455 6646 +6458 6459 +6458 6460 +6461 6462 +6463 6464 +6465 6466 +6468 6469 +6470 6471 +6472 6473 +6472 6474 +6472 6475 +6473 6474 +6474 6475 +6476 6477 +6476 6478 +6476 6479 +6476 6480 +6476 6481 +6476 6990 +6477 6478 +6478 6990 +6482 6483 +6484 6485 +6484 6486 +6484 6487 +6484 6488 +6484 6489 +6484 6490 +6485 6488 +6486 6488 +6487 6488 +6491 6896 +6494 6495 +6494 6496 +6497 6498 +6501 7267 +6501 7268 +6503 6504 +6506 6507 +6509 6510 +6512 6513 +6514 6519 +6515 6516 +6516 6754 +6517 6518 +6518 6723 +6519 6520 +6519 6521 +6519 6522 +6523 6524 +6525 6526 +6525 6527 +6527 6528 +6529 6530 +6533 6534 +6536 6537 +6536 6538 +6536 6539 +6540 6541 +6542 6543 +6544 6545 +6544 6546 +6544 6547 +6545 6547 +6546 6547 +6548 6787 +6548 6788 +6548 7036 +6549 6550 +6554 6555 +6554 6556 +6558 6942 +6559 6560 +6563 6564 +6565 7193 +6567 6568 +6570 7095 +6573 6574 +6573 6575 +6574 6575 +6582 6583 +6584 6585 +6584 6586 +6587 6588 +6589 6590 +6591 6592 +6593 6594 +6593 6595 +6596 6597 +6598 6599 +6598 6600 +6602 6603 +6603 6604 +6607 6608 +6609 6954 +6610 6611 +6610 6612 +6610 6613 +6610 6614 +6610 6615 +6610 6616 +6612 6613 +6612 6614 +6613 6614 +6613 6734 +6617 6618 +6617 6619 +6620 6812 +6620 6813 +6621 6622 +6621 6623 +6624 7255 +6625 6626 +6625 6627 +6625 6628 +6625 6629 +6625 6630 +6625 6631 +6632 6633 +6636 6637 +6640 6641 +6640 6642 +6643 6644 +6647 6648 +6649 6650 +6656 6657 +6656 6658 +6659 6660 +6659 6661 +6662 6663 +6664 6665 +6664 6666 +6664 6667 +6664 6668 +6665 6666 +6665 6667 +6665 6668 +6666 6667 +6666 6668 +6667 6668 +6671 6951 +6672 6951 +6675 6676 +6676 6677 +6679 6680 +6682 6683 +6684 7114 +6685 6686 +6687 6688 +6687 6689 +6690 6691 +6693 6930 +6694 6821 +6695 6696 +6695 6697 +6698 7244 +6699 7019 +6699 7020 +6699 7021 +6701 6702 +6705 6914 +6707 6708 +6709 6710 +6712 6713 +6712 6714 +6714 6752 +6715 6716 +6715 6717 +6715 6718 +6715 6719 +6715 6720 +6715 6721 +6724 6725 +6724 6726 +6727 6728 +6729 6730 +6731 6732 +6735 6736 +6735 6737 +6738 6739 +6741 7096 +6743 6744 +6745 6746 +6747 6748 +6749 6750 +6752 6753 +6755 6756 +6758 6759 +6758 6760 +6761 6762 +6763 7000 +6763 7001 +6764 7099 +6766 6767 +6778 6779 +6780 6781 +6782 6783 +6784 6785 +6787 6788 +6787 7036 +6793 6794 +6803 6804 +6805 6806 +6806 6807 +6808 6809 +6810 6811 +6812 6813 +6815 6816 +6819 7180 +6823 6824 +6825 6826 +6827 6828 +6830 6831 +6832 6834 +6833 6835 +6836 6837 +6841 6842 +6843 6844 +6847 6848 +6850 7028 +6851 7090 +6854 6855 +6854 6856 +6857 6858 +6859 6860 +6862 6863 +6862 6864 +6862 6865 +6862 6866 +6862 6867 +6862 6868 +6862 6869 +6862 6870 +6862 6871 +6862 6872 +6862 6873 +6862 6874 +6862 6875 +6862 6876 +6878 6879 +6878 6880 +6878 6881 +6878 6882 +6878 6883 +6879 6883 +6880 6882 +6880 6883 +6881 6882 +6889 6890 +6889 6891 +6892 6893 +6892 6894 +6897 6898 +6903 6904 +6903 6905 +6908 6909 +6911 6912 +6915 7033 +6917 6918 +6918 7199 +6919 7033 +6920 6921 +6922 6923 +6924 7192 +6926 6979 +6930 6931 +6933 6934 +6935 6936 +6939 6968 +6942 6943 +6945 7192 +6948 6949 +6948 6950 +6948 6976 +6949 6950 +6949 6976 +6955 6956 +6960 6961 +6965 6966 +6965 6967 +6969 6970 +6971 7201 +6972 7030 +6975 7078 +6981 6982 +6983 6984 +6983 6985 +6986 6987 +6986 6988 +6986 6989 +6991 6992 +6993 6994 +6996 6997 +6998 6999 +7000 7001 +7004 7005 +7006 7007 +7008 7009 +7008 7010 +7012 7013 +7014 7015 +7016 7081 +7019 7020 +7024 7025 +7026 7027 +7028 7029 +7031 7032 +7034 7035 +7040 7041 +7042 7043 +7044 7045 +7046 7111 +7049 7050 +7051 7052 +7051 7053 +7052 7053 +7057 7058 +7059 7060 +7059 7061 +7060 7061 +7062 7063 +7062 7064 +7066 7067 +7069 7070 +7069 7071 +7072 7073 +7074 7144 +7075 7093 +7076 7082 +7079 7080 +7083 7084 +7086 7087 +7088 7089 +7091 7092 +7101 7102 +7103 7104 +7107 7108 +7112 7113 +7114 7115 +7118 7119 +7118 7120 +7121 7257 +7122 7123 +7124 7125 +7126 7127 +7128 7129 +7130 7131 +7130 7132 +7131 7132 +7140 7141 +7145 7273 +7146 7147 +7152 7153 +7157 7158 +7159 7160 +7161 7162 +7165 7166 +7167 7168 +7169 7170 +7171 7172 +7176 7177 +7181 7318 +7182 7203 +7183 7184 +7187 7188 +7187 7189 +7188 7190 +7188 7191 +7189 7190 +7195 7196 +7201 7202 +7204 7205 +7206 7207 +7208 7209 +7210 7211 +7212 7213 +7214 7215 +7216 7217 +7218 7219 +7220 7221 +7223 7224 +7224 7269 +7224 7293 +7224 7319 +7226 7227 +7228 7229 +7230 7231 +7230 7232 +7233 7234 +7235 7236 +7237 7238 +7239 7240 +7240 7328 +7241 7242 +7246 7247 +7248 7249 +7253 7254 +7258 7259 +7261 7262 +7263 7264 +7266 7267 +7266 7268 +7267 7268 +7270 7271 +7273 7274 +7275 7276 +7275 7277 +7276 7277 +7278 7279 +7279 7280 +7281 7282 +7283 7284 +7285 7286 +7289 7290 +7294 7295 +7297 7298 +7302 7303 +7304 7305 +7304 7306 +7307 7308 +7310 7311 +7313 7314 +7316 7317 +7321 7322 +7323 7324 +7326 7327 +7332 7333 +7332 7334 +7335 7336 +7335 7337 +7335 7338 diff --git a/tests/unit/zapsmall.c b/tests/unit/zapsmall.c new file mode 100644 index 0000000..d55e873 --- /dev/null +++ b/tests/unit/zapsmall.c @@ -0,0 +1,49 @@ +/* + 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, see . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + igraph_matrix_t mat; + + igraph_matrix_init(&mat, 3, 3); + MATRIX(mat, 0, 0) = 1e-11; + MATRIX(mat, 0, 1) = -1e-11; + MATRIX(mat, 0, 2) = 1e-10; + MATRIX(mat, 1, 0) = -1e-10; + MATRIX(mat, 1, 1) = IGRAPH_INFINITY; + MATRIX(mat, 1, 2) = -IGRAPH_INFINITY; + MATRIX(mat, 2, 0) = IGRAPH_NAN; + MATRIX(mat, 2, 1) = 0.0; + MATRIX(mat, 2, 2) = 1.0; + + print_matrix(&mat); + igraph_matrix_zapsmall(&mat, 0); + print_matrix(&mat); + + CHECK_ERROR(igraph_matrix_zapsmall(&mat, -1), IGRAPH_EINVAL); + + igraph_matrix_destroy(&mat); + + /* TODO complex case */ + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/zapsmall.out b/tests/unit/zapsmall.out new file mode 100644 index 0000000..ced5d12 --- /dev/null +++ b/tests/unit/zapsmall.out @@ -0,0 +1,6 @@ +[ 1e-11 -1e-11 1e-10 + -1e-10 Inf -Inf + NaN 0 1 ] +[ 0 0 1e-10 + -1e-10 Inf -Inf + NaN 0 1 ] diff --git a/tests/unit/zero_allocs.c b/tests/unit/zero_allocs.c new file mode 100644 index 0000000..83588b1 --- /dev/null +++ b/tests/unit/zero_allocs.c @@ -0,0 +1,37 @@ +/* + 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 . +*/ + +#include +#include "test_utilities.h" + +int main(void) { + int *a = IGRAPH_CALLOC(0, int); + + IGRAPH_ASSERT(a); + + a = IGRAPH_REALLOC(a, 0, int); + + IGRAPH_ASSERT(a); + + IGRAPH_FREE(a); + + IGRAPH_ASSERT(!a); + + VERIFY_FINALLY_STACK(); + return 0; +} diff --git a/tools/removeexamples.py b/tools/removeexamples.py new file mode 100644 index 0000000..aae2b33 --- /dev/null +++ b/tools/removeexamples.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Helper script used to remove the bundled examples from the DocBook files +that are used to generate the PDF documentation. + +This file is part of the documentation build process. You do not need to call +it manually. +""" + +import sys +from xml.etree.ElementTree import ElementTree + + +def usage(): + print(sys.argv[0], " ") + + +def main(): + if len(sys.argv) != 3: + usage() + sys.exit(2) + + # Read in + tree = ElementTree() + tree.parse(sys.argv[1]) + + # Remove examples + examples = tree.findall(".//example") + for ex in examples: + prog = ex.find("programlisting") + ex.remove(prog) + + # Write result + tree.write(sys.argv[2]) + + +if __name__ == "__main__": + main() diff --git a/tools/strip_licenses_from_examples.py b/tools/strip_licenses_from_examples.py new file mode 100644 index 0000000..b516d56 --- /dev/null +++ b/tools/strip_licenses_from_examples.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Strips the license notices from the bundled igraph examples so they are +not visible in the documentation. +""" + +import argparse +import sys + +from pathlib import Path +from typing import IO + + +def strip_license_notice(infp: IO[str], outfp: IO[str]) -> None: + seen_code = False + + for line in infp: + if not seen_code: + # This is an approximation; we consider the first line that starts + # with a non-whitespace character, * or / as "real code", and we strip + # everything before that + if line and line[0] not in ' \n\t\r/*': + seen_code = True + outfp.write(line) + else: + outfp.write(line) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "-o", "--out-dir", help="output directory to put the stripped files in", + ) + parser.add_argument('files', help="input files to process", nargs="*") + options = parser.parse_args() + + if not options.out_dir: + # We must have one or two args; one arg means that we are printing to + # stdout + if len(options.files) == 0: + return 0 + elif len(options.files) > 2: + parser.error( + "At most two files (one input, one output) must be given " + "when -o is not used" + ) + return 1 + + with Path(options.files[0]).open("r") as infp: + if len(options.files) > 1: + with Path(options.files[1]).open("w") as outfp: + strip_license_notice(infp, outfp) + else: + strip_license_notice(infp, sys.stdout) + + else: + # We have an output dir so we can handle an arbitrary number of input + # files + for filename in options.files: + path = Path(filename) + with path.open("r") as infp: + with (Path(options.out_dir) / path.name).open("w") as outfp: + strip_license_notice(infp, outfp) + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt new file mode 100644 index 0000000..9befa6f --- /dev/null +++ b/vendor/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(cs) +add_subdirectory(f2c) +add_subdirectory(glpk) +add_subdirectory(lapack) +add_subdirectory(mini-gmp) +add_subdirectory(pcg) +add_subdirectory(plfit) diff --git a/vendor/cs/CMakeLists.txt b/vendor/cs/CMakeLists.txt new file mode 100644 index 0000000..bef000d --- /dev/null +++ b/vendor/cs/CMakeLists.txt @@ -0,0 +1,93 @@ +# Declare the files needed to compile our vendored CXSparse copy +add_library( + cxsparse_vendored + OBJECT + EXCLUDE_FROM_ALL + cs_add.c + cs_amd.c + cs_chol.c + cs_cholsol.c + cs_compress.c + cs_counts.c + cs_cumsum.c + cs_dfs.c + cs_dmperm.c + cs_droptol.c + cs_dropzeros.c + cs_dupl.c + cs_entry.c + cs_ereach.c + cs_etree.c + cs_fkeep.c + cs_gaxpy.c + cs_happly.c + cs_house.c + cs_ipvec.c + cs_leaf.c + cs_load.c + cs_lsolve.c + cs_ltsolve.c + cs_lu.c + cs_lusol.c + cs_malloc.c + cs_maxtrans.c + cs_multiply.c + cs_norm.c + cs_permute.c + cs_pinv.c + cs_post.c + cs_pvec.c + cs_qr.c + cs_qrsol.c + cs_randperm.c + cs_reach.c + cs_scatter.c + cs_scc.c + cs_schol.c + cs_spsolve.c + cs_sqr.c + cs_symperm.c + cs_tdfs.c + cs_transpose.c + cs_updown.c + cs_usolve.c + cs_util.c + cs_utsolve.c + # the following files are not needed - they contain no symbols + # cs_print.c +) + +target_include_directories( + cxsparse_vendored + PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_BINARY_DIR}/include +) + +if (BUILD_SHARED_LIBS) + set_property(TARGET cxsparse_vendored PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +# Disable complex number support for CXSparse because: +# - It is necessary to compile with MSVC +# - igraph does not need complex number support from CXSparse on any platform +target_compile_definitions(cxsparse_vendored PUBLIC NCOMPLEX) + +# Since these are included as object files, they should call the +# function as is (without a visibility specification) +target_compile_definitions(cxsparse_vendored PRIVATE IGRAPH_STATIC) + +use_all_warnings(cxsparse_vendored) + +if (MSVC) + target_compile_options( + cxsparse_vendored PRIVATE + /wd4100 + ) # disable unreferenced parameter warning +else() + target_compile_options( + cxsparse_vendored PRIVATE + $<$:-Wno-unused-variable> + ) +endif() + diff --git a/vendor/cs/License.txt b/vendor/cs/License.txt new file mode 100644 index 0000000..df6f425 --- /dev/null +++ b/vendor/cs/License.txt @@ -0,0 +1,19 @@ +CXSparse: a Concise Sparse matrix package - Extended. +Copyright (c) 2006, Timothy A. Davis. +http://www.suitesparse.com + +-------------------------------------------------------------------------------- + +CXSparse is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +CXSparse is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this Module; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/vendor/cs/cs.h b/vendor/cs/cs.h new file mode 100644 index 0000000..a505ca5 --- /dev/null +++ b/vendor/cs/cs.h @@ -0,0 +1,315 @@ +/* This is a MODIFIED version of the original CXSparse/Include/cs.h file from + * SuiteSparse 5.12.0 (CXSparse version 3.2.0). The modifications are outlined + * here: + * + * - Dependency on SuiteSparse_long was removed + * - CXSparse is configured to use igraph_integer_t as cs_long_t + * - CXSparse function prefix is set to cs_igraph instead of cs_igraph + * - Unneeded CXSparse function variants are removed + * + * The remaining comments below are from the original cs.h header */ + +/* ========================================================================== */ +/* 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 + +#include "igraph_types.h" + +#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 + +#define cs_long_t igraph_integer_t +#define cs_long_t_id "%" IGRAPH_PRId +#define cs_long_t_max IGRAPH_INTEGER_MAX + +/* -------------------------------------------------------------------------- */ +/* double/cs_long_t version of CXSparse */ +/* -------------------------------------------------------------------------- */ + +/* --- primary CSparse routines and data structures ------------------------- */ + +typedef struct cs_igraph_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_igraph ; + +cs_igraph *cs_igraph_add (const cs_igraph *A, const cs_igraph *B, double alpha, double beta) ; +cs_long_t cs_igraph_cholsol (cs_long_t order, const cs_igraph *A, double *b) ; +cs_long_t cs_igraph_dupl (cs_igraph *A) ; +cs_long_t cs_igraph_entry (cs_igraph *T, cs_long_t i, cs_long_t j, double x) ; +cs_long_t cs_igraph_lusol (cs_long_t order, const cs_igraph *A, double *b, double tol) ; +cs_long_t cs_igraph_gaxpy (const cs_igraph *A, const double *x, double *y) ; +cs_igraph *cs_igraph_multiply (const cs_igraph *A, const cs_igraph *B) ; +cs_long_t cs_igraph_qrsol (cs_long_t order, const cs_igraph *A, double *b) ; +cs_igraph *cs_igraph_transpose (const cs_igraph *A, cs_long_t values) ; +cs_igraph *cs_igraph_compress (const cs_igraph *T) ; +double cs_igraph_norm (const cs_igraph *A) ; +/*cs_long_t cs_igraph_print (const cs_igraph *A, cs_long_t brief) ;*/ +cs_igraph *cs_igraph_load (FILE *f) ; + +/* utilities */ +void *cs_igraph_calloc (cs_long_t n, size_t size) ; +void *cs_igraph_free (void *p) ; +void *cs_igraph_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; +cs_igraph *cs_igraph_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, + cs_long_t t) ; +cs_igraph *cs_igraph_spfree (cs_igraph *A) ; +cs_long_t cs_igraph_sprealloc (cs_igraph *A, cs_long_t nzmax) ; +void *cs_igraph_malloc (cs_long_t n, size_t size) ; + +/* --- secondary CSparse routines and data structures ----------------------- */ + +typedef struct cs_igraph_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_igraphs ; + +typedef struct cs_igraph_numeric /* numeric Cholesky, LU, or QR factorization */ +{ + cs_igraph *L ; /* L for LU and Cholesky, V for QR */ + cs_igraph *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_igraphn ; + +typedef struct cs_igraph_dmperm_results /* cs_igraph_dmperm or cs_igraph_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_igraphd ; + +cs_long_t *cs_igraph_amd (cs_long_t order, const cs_igraph *A) ; +cs_igraphn *cs_igraph_chol (const cs_igraph *A, const cs_igraphs *S) ; +cs_igraphd *cs_igraph_dmperm (const cs_igraph *A, cs_long_t seed) ; +cs_long_t cs_igraph_droptol (cs_igraph *A, double tol) ; +cs_long_t cs_igraph_dropzeros (cs_igraph *A) ; +cs_long_t cs_igraph_happly (const cs_igraph *V, cs_long_t i, double beta, double *x) ; +cs_long_t cs_igraph_ipvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_long_t cs_igraph_lsolve (const cs_igraph *L, double *x) ; +cs_long_t cs_igraph_ltsolve (const cs_igraph *L, double *x) ; +cs_igraphn *cs_igraph_lu (const cs_igraph *A, const cs_igraphs *S, double tol) ; +cs_igraph *cs_igraph_permute (const cs_igraph *A, const cs_long_t *pinv, const cs_long_t *q, + cs_long_t values) ; +cs_long_t *cs_igraph_pinv (const cs_long_t *p, cs_long_t n) ; +cs_long_t cs_igraph_pvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_igraphn *cs_igraph_qr (const cs_igraph *A, const cs_igraphs *S) ; +cs_igraphs *cs_igraph_schol (cs_long_t order, const cs_igraph *A) ; +cs_igraphs *cs_igraph_sqr (cs_long_t order, const cs_igraph *A, cs_long_t qr) ; +cs_igraph *cs_igraph_symperm (const cs_igraph *A, const cs_long_t *pinv, cs_long_t values) ; +cs_long_t cs_igraph_usolve (const cs_igraph *U, double *x) ; +cs_long_t cs_igraph_utsolve (const cs_igraph *U, double *x) ; +cs_long_t cs_igraph_updown (cs_igraph *L, cs_long_t sigma, const cs_igraph *C, + const cs_long_t *parent) ; + +/* utilities */ +cs_igraphs *cs_igraph_sfree (cs_igraphs *S) ; +cs_igraphn *cs_igraph_nfree (cs_igraphn *N) ; +cs_igraphd *cs_igraph_dfree (cs_igraphd *D) ; + +/* --- tertiary CSparse routines -------------------------------------------- */ + +cs_long_t *cs_igraph_counts (const cs_igraph *A, const cs_long_t *parent, + const cs_long_t *post, cs_long_t ata) ; +double cs_igraph_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; +cs_long_t cs_igraph_dfs (cs_long_t j, cs_igraph *G, cs_long_t top, cs_long_t *xi, + cs_long_t *pstack, const cs_long_t *pinv) ; +cs_long_t *cs_igraph_etree (const cs_igraph *A, cs_long_t ata) ; +cs_long_t cs_igraph_fkeep (cs_igraph *A, + cs_long_t (*fkeep) (cs_long_t, cs_long_t, double, void *), void *other) ; +double cs_igraph_house (double *x, double *beta, cs_long_t n) ; +cs_long_t *cs_igraph_maxtrans (const cs_igraph *A, cs_long_t seed) ; +cs_long_t *cs_igraph_post (const cs_long_t *parent, cs_long_t n) ; +cs_igraphd *cs_igraph_scc (cs_igraph *A) ; +cs_long_t cs_igraph_scatter (const cs_igraph *A, cs_long_t j, double beta, cs_long_t *w, + double *x, cs_long_t mark,cs_igraph *C, cs_long_t nz) ; +cs_long_t cs_igraph_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_igraph_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_igraph_reach (cs_igraph *G, const cs_igraph *B, cs_long_t k, cs_long_t *xi, + const cs_long_t *pinv) ; +cs_long_t cs_igraph_spsolve (cs_igraph *L, const cs_igraph *B, cs_long_t k, cs_long_t *xi, + double *x, const cs_long_t *pinv, cs_long_t lo) ; +cs_long_t cs_igraph_ereach (const cs_igraph *A, cs_long_t k, const cs_long_t *parent, + cs_long_t *s, cs_long_t *w) ; +cs_long_t *cs_igraph_randperm (cs_long_t n, cs_long_t seed) ; + +/* utilities */ +cs_igraphd *cs_igraph_dalloc (cs_long_t m, cs_long_t n) ; +cs_igraph *cs_igraph_done (cs_igraph *C, void *w, void *x, cs_long_t ok) ; +cs_long_t *cs_igraph_idone (cs_long_t *p, cs_igraph *C, void *w, cs_long_t ok) ; +cs_igraphn *cs_igraph_ndone (cs_igraphn *N, cs_igraph *C, void *w, void *x, cs_long_t ok) ; +cs_igraphd *cs_igraph_ddone (cs_igraphd *D, cs_igraph *C, void *w, cs_long_t ok) ; + +/* -------------------------------------------------------------------------- */ +/* Macros for constructing each version of CSparse */ +/* -------------------------------------------------------------------------- */ + +#define CS_INT cs_long_t +#define CS_INT_MAX cs_long_t_max +#define CS_ID cs_long_t_id +#define CS_ENTRY double +#define CS_NAME(nm) cs_igraph ## nm +#define cs cs_igraph + +#define CS_REAL(x) (x) +#define CS_IMAG(x) (0.) +#define CS_CONJ(x) (x) +#define CS_ABS(x) fabs(x) + +#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) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/vendor/cs/cs_add.c b/vendor/cs/cs_add.c new file mode 100644 index 0000000..44b0d3f --- /dev/null +++ b/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/vendor/cs/cs_amd.c b/vendor/cs/cs_amd.c new file mode 100644 index 0000000..3f5c702 --- /dev/null +++ b/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/vendor/cs/cs_chol.c b/vendor/cs/cs_chol.c new file mode 100644 index 0000000..535809a --- /dev/null +++ b/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/vendor/cs/cs_cholsol.c b/vendor/cs/cs_cholsol.c new file mode 100644 index 0000000..6e7dc4f --- /dev/null +++ b/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/vendor/cs/cs_compress.c b/vendor/cs/cs_compress.c new file mode 100644 index 0000000..dc62eba --- /dev/null +++ b/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/vendor/cs/cs_counts.c b/vendor/cs/cs_counts.c new file mode 100644 index 0000000..a1b3de0 --- /dev/null +++ b/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/vendor/cs/cs_cumsum.c b/vendor/cs/cs_cumsum.c new file mode 100644 index 0000000..e839497 --- /dev/null +++ b/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/vendor/cs/cs_dfs.c b/vendor/cs/cs_dfs.c new file mode 100644 index 0000000..6c7115d --- /dev/null +++ b/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/vendor/cs/cs_dmperm.c b/vendor/cs/cs_dmperm.c new file mode 100644 index 0000000..e213845 --- /dev/null +++ b/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/vendor/cs/cs_droptol.c b/vendor/cs/cs_droptol.c new file mode 100644 index 0000000..59b8df2 --- /dev/null +++ b/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/vendor/cs/cs_dropzeros.c b/vendor/cs/cs_dropzeros.c new file mode 100644 index 0000000..d93f605 --- /dev/null +++ b/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/vendor/cs/cs_dupl.c b/vendor/cs/cs_dupl.c new file mode 100644 index 0000000..fdf2e1e --- /dev/null +++ b/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/vendor/cs/cs_entry.c b/vendor/cs/cs_entry.c new file mode 100644 index 0000000..f712ba7 --- /dev/null +++ b/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/vendor/cs/cs_ereach.c b/vendor/cs/cs_ereach.c new file mode 100644 index 0000000..9edad52 --- /dev/null +++ b/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/vendor/cs/cs_etree.c b/vendor/cs/cs_etree.c new file mode 100644 index 0000000..5620928 --- /dev/null +++ b/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/vendor/cs/cs_fkeep.c b/vendor/cs/cs_fkeep.c new file mode 100644 index 0000000..09219e8 --- /dev/null +++ b/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/vendor/cs/cs_gaxpy.c b/vendor/cs/cs_gaxpy.c new file mode 100644 index 0000000..db93cbc --- /dev/null +++ b/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/vendor/cs/cs_happly.c b/vendor/cs/cs_happly.c new file mode 100644 index 0000000..98a306c --- /dev/null +++ b/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/vendor/cs/cs_house.c b/vendor/cs/cs_house.c new file mode 100644 index 0000000..e825c89 --- /dev/null +++ b/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/vendor/cs/cs_ipvec.c b/vendor/cs/cs_ipvec.c new file mode 100644 index 0000000..4935ace --- /dev/null +++ b/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/vendor/cs/cs_leaf.c b/vendor/cs/cs_leaf.c new file mode 100644 index 0000000..bd93bda --- /dev/null +++ b/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/vendor/cs/cs_load.c b/vendor/cs/cs_load.c new file mode 100644 index 0000000..91e1f37 --- /dev/null +++ b/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/vendor/cs/cs_lsolve.c b/vendor/cs/cs_lsolve.c new file mode 100644 index 0000000..099b0c5 --- /dev/null +++ b/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/vendor/cs/cs_ltsolve.c b/vendor/cs/cs_ltsolve.c new file mode 100644 index 0000000..29b1ca2 --- /dev/null +++ b/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/vendor/cs/cs_lu.c b/vendor/cs/cs_lu.c new file mode 100644 index 0000000..270172c --- /dev/null +++ b/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/vendor/cs/cs_lusol.c b/vendor/cs/cs_lusol.c new file mode 100644 index 0000000..e0727e2 --- /dev/null +++ b/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/vendor/cs/cs_malloc.c b/vendor/cs/cs_malloc.c new file mode 100644 index 0000000..2a3f6da --- /dev/null +++ b/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/vendor/cs/cs_maxtrans.c b/vendor/cs/cs_maxtrans.c new file mode 100644 index 0000000..4947cee --- /dev/null +++ b/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/vendor/cs/cs_multiply.c b/vendor/cs/cs_multiply.c new file mode 100644 index 0000000..34e3a36 --- /dev/null +++ b/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/vendor/cs/cs_norm.c b/vendor/cs/cs_norm.c new file mode 100644 index 0000000..0e7b3c6 --- /dev/null +++ b/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/vendor/cs/cs_permute.c b/vendor/cs/cs_permute.c new file mode 100644 index 0000000..9adae45 --- /dev/null +++ b/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/vendor/cs/cs_pinv.c b/vendor/cs/cs_pinv.c new file mode 100644 index 0000000..de0660e --- /dev/null +++ b/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/vendor/cs/cs_post.c b/vendor/cs/cs_post.c new file mode 100644 index 0000000..0f61203 --- /dev/null +++ b/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/vendor/cs/cs_print.c b/vendor/cs/cs_print.c new file mode 100644 index 0000000..7e7b16d --- /dev/null +++ b/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/vendor/cs/cs_pvec.c b/vendor/cs/cs_pvec.c new file mode 100644 index 0000000..1254c2a --- /dev/null +++ b/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/vendor/cs/cs_qr.c b/vendor/cs/cs_qr.c new file mode 100644 index 0000000..8bce32e --- /dev/null +++ b/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/vendor/cs/cs_qrsol.c b/vendor/cs/cs_qrsol.c new file mode 100644 index 0000000..d817ef2 --- /dev/null +++ b/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/vendor/cs/cs_randperm.c b/vendor/cs/cs_randperm.c new file mode 100644 index 0000000..4570da0 --- /dev/null +++ b/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/vendor/cs/cs_reach.c b/vendor/cs/cs_reach.c new file mode 100644 index 0000000..0efb342 --- /dev/null +++ b/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/vendor/cs/cs_scatter.c b/vendor/cs/cs_scatter.c new file mode 100644 index 0000000..734fdb2 --- /dev/null +++ b/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/vendor/cs/cs_scc.c b/vendor/cs/cs_scc.c new file mode 100644 index 0000000..cc6d805 --- /dev/null +++ b/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/vendor/cs/cs_schol.c b/vendor/cs/cs_schol.c new file mode 100644 index 0000000..7da2a57 --- /dev/null +++ b/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/vendor/cs/cs_spsolve.c b/vendor/cs/cs_spsolve.c new file mode 100644 index 0000000..8c6ecce --- /dev/null +++ b/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/vendor/cs/cs_sqr.c b/vendor/cs/cs_sqr.c new file mode 100644 index 0000000..1b14ca4 --- /dev/null +++ b/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/vendor/cs/cs_symperm.c b/vendor/cs/cs_symperm.c new file mode 100644 index 0000000..7bbd6fe --- /dev/null +++ b/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/vendor/cs/cs_tdfs.c b/vendor/cs/cs_tdfs.c new file mode 100644 index 0000000..f32077b --- /dev/null +++ b/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/vendor/cs/cs_transpose.c b/vendor/cs/cs_transpose.c new file mode 100644 index 0000000..15a6c00 --- /dev/null +++ b/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/vendor/cs/cs_updown.c b/vendor/cs/cs_updown.c new file mode 100644 index 0000000..e49af83 --- /dev/null +++ b/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/vendor/cs/cs_usolve.c b/vendor/cs/cs_usolve.c new file mode 100644 index 0000000..6e89c83 --- /dev/null +++ b/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/vendor/cs/cs_util.c b/vendor/cs/cs_util.c new file mode 100644 index 0000000..bb887ea --- /dev/null +++ b/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/vendor/cs/cs_utsolve.c b/vendor/cs/cs_utsolve.c new file mode 100644 index 0000000..d879fae --- /dev/null +++ b/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/vendor/pcg/CMakeLists.txt b/vendor/pcg/CMakeLists.txt new file mode 100644 index 0000000..c106f5e --- /dev/null +++ b/vendor/pcg/CMakeLists.txt @@ -0,0 +1,21 @@ +# Declare the files needed to compile our vendored copy of the PCG random +# number generator +add_library( + pcg + OBJECT + EXCLUDE_FROM_ALL + pcg-advance-64.c + pcg-advance-128.c + pcg-output-32.c + pcg-output-64.c + pcg-output-128.c + pcg-rngs-64.c + pcg-rngs-128.c +) + +if (BUILD_SHARED_LIBS) + set_property(TARGET pcg PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +use_all_warnings(pcg) + diff --git a/vendor/pcg/LICENSE.txt b/vendor/pcg/LICENSE.txt new file mode 100644 index 0000000..2315960 --- /dev/null +++ b/vendor/pcg/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/pcg/pcg-advance-128.c b/vendor/pcg/pcg-advance-128.c new file mode 100644 index 0000000..eceb90e --- /dev/null +++ b/vendor/pcg/pcg-advance-128.c @@ -0,0 +1,61 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * Repetative C code is derived using C preprocessor metaprogramming + * techniques. + */ + +#include "pcg_variants.h" + +/* Multi-step advance functions (jump-ahead, jump-back) + * + * The method used here is based on Brown, "Random Number Generation + * with Arbitrary Stride,", Transactions of the American Nuclear + * Society (Nov. 1994). The algorithm is very similar to fast + * exponentiation. + * + * Even though delta is an unsigned integer, we can pass a + * signed integer to go backwards, it just goes "the long way round". + */ + +#if PCG_HAS_128BIT_OPS +pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, pcg128_t cur_mult, + pcg128_t cur_plus) +{ + pcg128_t acc_mult = 1u; + pcg128_t acc_plus = 0u; + while (delta > 0) { + if (delta & 1) { + acc_mult *= cur_mult; + acc_plus = acc_plus * cur_mult + cur_plus; + } + cur_plus = (cur_mult + 1) * cur_plus; + cur_mult *= cur_mult; + delta /= 2; + } + return acc_mult * state + acc_plus; +} +#endif diff --git a/vendor/pcg/pcg-advance-64.c b/vendor/pcg/pcg-advance-64.c new file mode 100644 index 0000000..ecd05de --- /dev/null +++ b/vendor/pcg/pcg-advance-64.c @@ -0,0 +1,59 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * Repetative C code is derived using C preprocessor metaprogramming + * techniques. + */ + +#include "pcg_variants.h" + +/* Multi-step advance functions (jump-ahead, jump-back) + * + * The method used here is based on Brown, "Random Number Generation + * with Arbitrary Stride,", Transactions of the American Nuclear + * Society (Nov. 1994). The algorithm is very similar to fast + * exponentiation. + * + * Even though delta is an unsigned integer, we can pass a + * signed integer to go backwards, it just goes "the long way round". + */ + +uint64_t pcg_advance_lcg_64(uint64_t state, uint64_t delta, uint64_t cur_mult, + uint64_t cur_plus) +{ + uint64_t acc_mult = 1u; + uint64_t acc_plus = 0u; + while (delta > 0) { + if (delta & 1) { + acc_mult *= cur_mult; + acc_plus = acc_plus * cur_mult + cur_plus; + } + cur_plus = (cur_mult + 1) * cur_plus; + cur_mult *= cur_mult; + delta /= 2; + } + return acc_mult * state + acc_plus; +} diff --git a/vendor/pcg/pcg-output-128.c b/vendor/pcg/pcg-output-128.c new file mode 100644 index 0000000..3955499 --- /dev/null +++ b/vendor/pcg/pcg-output-128.c @@ -0,0 +1,63 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* + * Rotate helper functions. + */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t pcg_rotr_128(pcg128_t value, unsigned int rot); +#endif + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +/* XSH RS */ + +/* XSH RR */ + +/* RXS M XS */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t pcg_output_rxs_m_xs_128_128(pcg128_t state); +#endif + +/* RXS M */ + +/* XSL RR (only defined for >= 64 bits) */ + +/* XSL RR RR (only defined for >= 64 bits) */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t pcg_output_xsl_rr_rr_128_128(pcg128_t state); +#endif diff --git a/vendor/pcg/pcg-output-32.c b/vendor/pcg/pcg-output-32.c new file mode 100644 index 0000000..dfe0cc5 --- /dev/null +++ b/vendor/pcg/pcg-output-32.c @@ -0,0 +1,63 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* + * Rotate helper functions. + */ + +extern inline uint32_t pcg_rotr_32(uint32_t value, unsigned int rot); + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +/* XSH RS */ + +extern inline uint32_t pcg_output_xsh_rs_64_32(uint64_t state); + +/* XSH RR */ + +extern inline uint32_t pcg_output_xsh_rr_64_32(uint64_t state); + +/* RXS M XS */ + +extern inline uint32_t pcg_output_rxs_m_xs_32_32(uint32_t state); + +/* RXS M */ + +extern inline uint32_t pcg_output_rxs_m_64_32(uint64_t state); + +/* XSL RR (only defined for >= 64 bits) */ + +extern inline uint32_t pcg_output_xsl_rr_64_32(uint64_t state); + +/* XSL RR RR (only defined for >= 64 bits) */ diff --git a/vendor/pcg/pcg-output-64.c b/vendor/pcg/pcg-output-64.c new file mode 100644 index 0000000..ba3598d --- /dev/null +++ b/vendor/pcg/pcg-output-64.c @@ -0,0 +1,73 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* + * Rotate helper functions. + */ + +extern inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot); + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +/* XSH RS */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_output_xsh_rs_128_64(pcg128_t state); +#endif + +/* XSH RR */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_output_xsh_rr_128_64(pcg128_t state); +#endif + +/* RXS M XS */ + +extern inline uint64_t pcg_output_rxs_m_xs_64_64(uint64_t state); + +/* RXS M */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_output_rxs_m_128_64(pcg128_t state); +#endif + +/* XSL RR (only defined for >= 64 bits) */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state); +#endif + +/* XSL RR RR (only defined for >= 64 bits) */ + +extern inline uint64_t pcg_output_xsl_rr_rr_64_64(uint64_t state); diff --git a/vendor/pcg/pcg-rngs-128.c b/vendor/pcg/pcg-rngs-128.c new file mode 100644 index 0000000..6f8797f --- /dev/null +++ b/vendor/pcg/pcg-rngs-128.c @@ -0,0 +1,378 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* Functions to advance the underlying LCG, one version for each size and + * each style. These functions are considered semi-private. There is rarely + * a good reason to call them directly. + */ + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_oneseq_128_step_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_oneseq_128_advance_r(struct pcg_state_128* rng, + pcg128_t delta); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_mcg_128_step_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_mcg_128_advance_r(struct pcg_state_128* rng, + pcg128_t delta); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_unique_128_step_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_unique_128_advance_r(struct pcg_state_128* rng, + pcg128_t delta); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_setseq_128_step_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_setseq_128_advance_r(struct pcg_state_setseq_128* rng, + pcg128_t delta); +#endif + +/* Functions to seed the RNG state, one version for each size and each + * style. Unlike the step functions, regular users can and should call + * these functions. + */ + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_oneseq_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_mcg_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_unique_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_setseq_128_srandom_r(struct pcg_state_setseq_128* rng, + pcg128_t initstate, + pcg128_t initseq); +#endif + +/* Now, finally we create each of the individual generators. We provide + * a random_r function that provides a random number of the appropriate + * type (using the full range of the type) and a boundedrand_r version + * that provides + * + * Implementation notes for boundedrand_r: + * + * To avoid bias, we need to make the range of the RNG a multiple of + * bound, which we do by dropping output less than a threshold. + * Let's consider a 32-bit case... A naive scheme to calculate the + * threshold would be to do + * + * uint32_t threshold = 0x100000000ull % bound; + * + * but 64-bit div/mod is slower than 32-bit div/mod (especially on + * 32-bit platforms). In essence, we do + * + * uint32_t threshold = (0x100000000ull-bound) % bound; + * + * because this version will calculate the same modulus, but the LHS + * value is less than 2^32. + * + * (Note that using modulo is only wise for good RNGs, poorer RNGs + * such as raw LCGs do better using a technique based on division.) + * Empirical tests show that division is preferable to modulus for + * reducing the range of an RNG. It's faster, and sometimes it can + * even be statistically prefereable. + */ + +/* Generation functions for XSH RS */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsh_rs_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsh_rs_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsh_rs_64_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsh_rs_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); +#endif + +/* Generation functions for XSH RR */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsh_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsh_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsh_rr_64_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsh_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); +#endif + +/* Generation functions for RXS M XS (no MCG versions because they + * don't make sense when you want to use the entire state) + */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_oneseq_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_oneseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_unique_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_unique_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_setseq_128* rng, + pcg128_t bound); +#endif + +/* Generation functions for RXS M */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_rxs_m_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_rxs_m_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_rxs_m_64_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_rxs_m_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_mcg_128_rxs_m_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); +#endif + +/* Generation functions for XSL RR (only defined for "large" types) */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsl_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsl_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsl_rr_64_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsl_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); +#endif + +/* Generation functions for XSL RR RR (only defined for "large" types) */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_oneseq_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_oneseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_unique_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_unique_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_setseq_128* rng, + pcg128_t bound); +#endif diff --git a/vendor/pcg/pcg-rngs-64.c b/vendor/pcg/pcg-rngs-64.c new file mode 100644 index 0000000..a22df31 --- /dev/null +++ b/vendor/pcg/pcg-rngs-64.c @@ -0,0 +1,255 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* Functions to advance the underlying LCG, one version for each size and + * each style. These functions are considered semi-private. There is rarely + * a good reason to call them directly. + */ + +extern inline void pcg_oneseq_64_step_r(struct pcg_state_64* rng); + +extern inline void pcg_oneseq_64_advance_r(struct pcg_state_64* rng, + uint64_t delta); + +extern inline void pcg_mcg_64_step_r(struct pcg_state_64* rng); + +extern inline void pcg_mcg_64_advance_r(struct pcg_state_64* rng, + uint64_t delta); + +extern inline void pcg_unique_64_step_r(struct pcg_state_64* rng); + +extern inline void pcg_unique_64_advance_r(struct pcg_state_64* rng, + uint64_t delta); + +extern inline void pcg_setseq_64_step_r(struct pcg_state_setseq_64* rng); + +extern inline void pcg_setseq_64_advance_r(struct pcg_state_setseq_64* rng, + uint64_t delta); + +/* Functions to seed the RNG state, one version for each size and each + * style. Unlike the step functions, regular users can and should call + * these functions. + */ + +extern inline void pcg_oneseq_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate); + +extern inline void pcg_mcg_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate); + +extern inline void pcg_unique_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate); + +extern inline void pcg_setseq_64_srandom_r(struct pcg_state_setseq_64* rng, + uint64_t initstate, + uint64_t initseq); + +/* Now, finally we create each of the individual generators. We provide + * a random_r function that provides a random number of the appropriate + * type (using the full range of the type) and a boundedrand_r version + * that provides + * + * Implementation notes for boundedrand_r: + * + * To avoid bias, we need to make the range of the RNG a multiple of + * bound, which we do by dropping output less than a threshold. + * Let's consider a 32-bit case... A naive scheme to calculate the + * threshold would be to do + * + * uint32_t threshold = 0x100000000ull % bound; + * + * but 64-bit div/mod is slower than 32-bit div/mod (especially on + * 32-bit platforms). In essence, we do + * + * uint32_t threshold = (0x100000000ull-bound) % bound; + * + * because this version will calculate the same modulus, but the LHS + * value is less than 2^32. + * + * (Note that using modulo is only wise for good RNGs, poorer RNGs + * such as raw LCGs do better using a technique based on division.) + * Empirical tests show that division is preferable to modulus for + * reducing the range of an RNG. It's faster, and sometimes it can + * even be statistically prefereable. + */ + +/* Generation functions for XSH RS */ + +extern inline uint32_t +pcg_oneseq_64_xsh_rs_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_oneseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_unique_64_xsh_rs_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_unique_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_setseq_64_xsh_rs_32_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint32_t +pcg_setseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound); + +extern inline uint32_t pcg_mcg_64_xsh_rs_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_mcg_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +/* Generation functions for XSH RR */ + +extern inline uint32_t +pcg_oneseq_64_xsh_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_oneseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_unique_64_xsh_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_unique_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_setseq_64_xsh_rr_32_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint32_t +pcg_setseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound); + +extern inline uint32_t pcg_mcg_64_xsh_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_mcg_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +/* Generation functions for RXS M XS (no MCG versions because they + * don't make sense when you want to use the entire state) + */ + +extern inline uint64_t +pcg_oneseq_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng); + +extern inline uint64_t +pcg_oneseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound); + +extern inline uint64_t +pcg_unique_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng); + +extern inline uint64_t +pcg_unique_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound); + +extern inline uint64_t +pcg_setseq_64_rxs_m_xs_64_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint64_t +pcg_setseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_setseq_64* rng, + uint64_t bound); + +/* Generation functions for RXS M */ + +extern inline uint32_t +pcg_oneseq_64_rxs_m_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_oneseq_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_unique_64_rxs_m_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_unique_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_setseq_64_rxs_m_32_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint32_t +pcg_setseq_64_rxs_m_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound); + +extern inline uint32_t pcg_mcg_64_rxs_m_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_mcg_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +/* Generation functions for XSL RR (only defined for "large" types) */ + +extern inline uint32_t +pcg_oneseq_64_xsl_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_oneseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_unique_64_xsl_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_unique_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_setseq_64_xsl_rr_32_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint32_t +pcg_setseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound); + +extern inline uint32_t pcg_mcg_64_xsl_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_mcg_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +/* Generation functions for XSL RR RR (only defined for "large" types) */ + +extern inline uint64_t +pcg_oneseq_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng); + +extern inline uint64_t +pcg_oneseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound); + +extern inline uint64_t +pcg_unique_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng); + +extern inline uint64_t +pcg_unique_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound); + +extern inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_setseq_64* rng, + uint64_t bound); diff --git a/vendor/pcg/pcg_variants.h b/vendor/pcg/pcg_variants.h new file mode 100644 index 0000000..25ca2e7 --- /dev/null +++ b/vendor/pcg/pcg_variants.h @@ -0,0 +1,2557 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * Much of the derivation was performed mechanically. In particular, the + * output functions were generated by compiling the C++ output functions + * into LLVM bitcode and then transforming that using the LLVM C backend + * (from https://github.com/draperlaboratory/llvm-cbe), and then + * postprocessing and hand editing the output. + * + * Much of the remaining code was generated by C-preprocessor metaprogramming. + */ + +#ifndef PCG_VARIANTS_H_INCLUDED +#define PCG_VARIANTS_H_INCLUDED 1 + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4146) /* "unary minus operator applied to unsigned type, result still unsigned" */ +#endif + +#if __SIZEOF_INT128__ + typedef __uint128_t pcg128_t; + #define PCG_128BIT_CONSTANT(high,low) \ + ((((pcg128_t)high) << 64) + low) + #define PCG_HAS_128BIT_OPS 1 +#endif + +/* Checking for !__GNUC_STDC_INLINE__ is a hack to work around a bug in the + * Intel compiler where it defined both __GNUC_GNU_INLINE__ and __GNUC_STDC_INLINE__ + * to 1 when using -std=gnu99. igraph is always compiled with -std=gnu99. + * + * Tested with icc (ICC) 2021.3.0 20210609 on Linux */ +#if __GNUC_GNU_INLINE__ && !__GNUC_STDC_INLINE__ && !defined(__cplusplus) + #error Nonstandard GNU inlining semantics. Compile with -std=c99 or better. + /* We could instead use macros PCG_INLINE and PCG_EXTERN_INLINE + but better to just reject ancient C code. */ +#endif + +#if __cplusplus +extern "C" { +#endif + +/* + * Rotate helper functions. + */ + +inline uint8_t pcg_rotr_8(uint8_t value, unsigned int rot) +{ +/* Unfortunately, clang is kinda pathetic when it comes to properly + * recognizing idiomatic rotate code, so for clang we actually provide + * assembler directives (enabled with PCG_USE_INLINE_ASM). Boo, hiss. + */ +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm ("rorb %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((- rot) & 7)); +#endif +} + +inline uint16_t pcg_rotr_16(uint16_t value, unsigned int rot) +{ +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm ("rorw %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((- rot) & 15)); +#endif +} + +inline uint32_t pcg_rotr_32(uint32_t value, unsigned int rot) +{ +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm ("rorl %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((- rot) & 31)); +#endif +} + +inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot) +{ +#if 0 && PCG_USE_INLINE_ASM && __clang__ && __x86_64__ + /* For whatever reason, clang actually *does* generate rotq by + itself, so we don't need this code. */ + asm ("rorq %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((- rot) & 63)); +#endif +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_rotr_128(pcg128_t value, unsigned int rot) +{ + return (value >> rot) | (value << ((- rot) & 127)); +} +#endif + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +/* XSH RS */ + +inline uint8_t pcg_output_xsh_rs_16_8(uint16_t state) +{ + return (uint8_t)(((state >> 7u) ^ state) >> ((state >> 14u) + 3u)); +} + +inline uint16_t pcg_output_xsh_rs_32_16(uint32_t state) +{ + return (uint16_t)(((state >> 11u) ^ state) >> ((state >> 30u) + 11u)); +} + +inline uint32_t pcg_output_xsh_rs_64_32(uint64_t state) +{ + + return (uint32_t)(((state >> 22u) ^ state) >> ((state >> 61u) + 22u)); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsh_rs_128_64(pcg128_t state) +{ + return (uint64_t)(((state >> 43u) ^ state) >> ((state >> 124u) + 45u)); +} +#endif + +/* XSH RR */ + +inline uint8_t pcg_output_xsh_rr_16_8(uint16_t state) +{ + return pcg_rotr_8(((state >> 5u) ^ state) >> 5u, state >> 13u); +} + +inline uint16_t pcg_output_xsh_rr_32_16(uint32_t state) +{ + return pcg_rotr_16(((state >> 10u) ^ state) >> 12u, state >> 28u); +} + +inline uint32_t pcg_output_xsh_rr_64_32(uint64_t state) +{ + return pcg_rotr_32(((state >> 18u) ^ state) >> 27u, state >> 59u); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsh_rr_128_64(pcg128_t state) +{ + return pcg_rotr_64(((state >> 35u) ^ state) >> 58u, state >> 122u); +} +#endif + +/* RXS M XS */ + +inline uint8_t pcg_output_rxs_m_xs_8_8(uint8_t state) +{ + uint8_t word = ((state >> ((state >> 6u) + 2u)) ^ state) * 217u; + return (word >> 6u) ^ word; +} + +inline uint16_t pcg_output_rxs_m_xs_16_16(uint16_t state) +{ + uint16_t word = ((state >> ((state >> 13u) + 3u)) ^ state) * 62169u; + return (word >> 11u) ^ word; +} + +inline uint32_t pcg_output_rxs_m_xs_32_32(uint32_t state) +{ + uint32_t word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; +} + +inline uint64_t pcg_output_rxs_m_xs_64_64(uint64_t state) +{ + uint64_t word = ((state >> ((state >> 59u) + 5u)) ^ state) + * 12605985483714917081ull; + return (word >> 43u) ^ word; +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_output_rxs_m_xs_128_128(pcg128_t state) +{ + pcg128_t word = ((state >> ((state >> 122u) + 6u)) ^ state) + * (PCG_128BIT_CONSTANT(17766728186571221404ULL, + 12605985483714917081ULL)); + /* 327738287884841127335028083622016905945 */ + return (word >> 86u) ^ word; +} +#endif + +/* RXS M */ + +inline uint8_t pcg_output_rxs_m_16_8(uint16_t state) +{ + return (((state >> ((state >> 13u) + 3u)) ^ state) * 62169u) >> 8u; +} + +inline uint16_t pcg_output_rxs_m_32_16(uint32_t state) +{ + return (((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u) >> 16u; +} + +inline uint32_t pcg_output_rxs_m_64_32(uint64_t state) +{ + return (((state >> ((state >> 59u) + 5u)) ^ state) + * 12605985483714917081ull) >> 32u; +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_rxs_m_128_64(pcg128_t state) +{ + return (((state >> ((state >> 122u) + 6u)) ^ state) + * (PCG_128BIT_CONSTANT(17766728186571221404ULL, + 12605985483714917081ULL))) >> 64u; + /* 327738287884841127335028083622016905945 */ +} +#endif + +/* XSL RR (only defined for >= 64 bits) */ + +inline uint32_t pcg_output_xsl_rr_64_32(uint64_t state) +{ + return pcg_rotr_32(((uint32_t)(state >> 32u)) ^ (uint32_t)state, + state >> 59u); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state) +{ + return pcg_rotr_64(((uint64_t)(state >> 64u)) ^ (uint64_t)state, + state >> 122u); +} +#endif + +/* XSL RR RR (only defined for >= 64 bits) */ + +inline uint64_t pcg_output_xsl_rr_rr_64_64(uint64_t state) +{ + uint32_t rot1 = (uint32_t)(state >> 59u); + uint32_t high = (uint32_t)(state >> 32u); + uint32_t low = (uint32_t)state; + uint32_t xored = high ^ low; + uint32_t newlow = pcg_rotr_32(xored, rot1); + uint32_t newhigh = pcg_rotr_32(high, newlow & 31u); + return (((uint64_t)newhigh) << 32u) | newlow; +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_output_xsl_rr_rr_128_128(pcg128_t state) +{ + uint32_t rot1 = (uint32_t)(state >> 122u); + uint64_t high = (uint64_t)(state >> 64u); + uint64_t low = (uint64_t)state; + uint64_t xored = high ^ low; + uint64_t newlow = pcg_rotr_64(xored, rot1); + uint64_t newhigh = pcg_rotr_64(high, newlow & 63u); + return (((pcg128_t)newhigh) << 64u) | newlow; +} +#endif + +#define PCG_DEFAULT_MULTIPLIER_8 141U +#define PCG_DEFAULT_MULTIPLIER_16 12829U +#define PCG_DEFAULT_MULTIPLIER_32 747796405U +#define PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL + +#define PCG_DEFAULT_INCREMENT_8 77U +#define PCG_DEFAULT_INCREMENT_16 47989U +#define PCG_DEFAULT_INCREMENT_32 2891336453U +#define PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL + +#if PCG_HAS_128BIT_OPS +#define PCG_DEFAULT_MULTIPLIER_128 \ + PCG_128BIT_CONSTANT(2549297995355413924ULL,4865540595714422341ULL) +#define PCG_DEFAULT_INCREMENT_128 \ + PCG_128BIT_CONSTANT(6364136223846793005ULL,1442695040888963407ULL) +#endif + +/* + * Static initialization constants (if you can't call srandom for some + * bizarre reason). + */ + +#define PCG_STATE_ONESEQ_8_INITIALIZER { 0xd7U } +#define PCG_STATE_ONESEQ_16_INITIALIZER { 0x20dfU } +#define PCG_STATE_ONESEQ_32_INITIALIZER { 0x46b56677U } +#define PCG_STATE_ONESEQ_64_INITIALIZER { 0x4d595df4d0f33173ULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_ONESEQ_128_INITIALIZER \ + { PCG_128BIT_CONSTANT(0xb8dc10e158a92392ULL, 0x98046df007ec0a53ULL) } +#endif + +#define PCG_STATE_UNIQUE_8_INITIALIZER PCG_STATE_ONESEQ_8_INITIALIZER +#define PCG_STATE_UNIQUE_16_INITIALIZER PCG_STATE_ONESEQ_16_INITIALIZER +#define PCG_STATE_UNIQUE_32_INITIALIZER PCG_STATE_ONESEQ_32_INITIALIZER +#define PCG_STATE_UNIQUE_64_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_UNIQUE_128_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#endif + +#define PCG_STATE_MCG_8_INITIALIZER { 0xe5U } +#define PCG_STATE_MCG_16_INITIALIZER { 0xa5e5U } +#define PCG_STATE_MCG_32_INITIALIZER { 0xd15ea5e5U } +#define PCG_STATE_MCG_64_INITIALIZER { 0xcafef00dd15ea5e5ULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_MCG_128_INITIALIZER \ + { PCG_128BIT_CONSTANT(0x0000000000000000ULL, 0xcafef00dd15ea5e5ULL) } +#endif + +#define PCG_STATE_SETSEQ_8_INITIALIZER { 0x9bU, 0xdbU } +#define PCG_STATE_SETSEQ_16_INITIALIZER { 0xe39bU, 0x5bdbU } +#define PCG_STATE_SETSEQ_32_INITIALIZER { 0xec02d89bU, 0x94b95bdbU } +#define PCG_STATE_SETSEQ_64_INITIALIZER \ + { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_SETSEQ_128_INITIALIZER \ + { PCG_128BIT_CONSTANT(0x979c9a98d8462005ULL, 0x7d3e9cb6cfe0549bULL), \ + PCG_128BIT_CONSTANT(0x0000000000000001ULL, 0xda3e39cb94b95bdbULL) } +#endif + +/* Representations for the oneseq, mcg, and unique variants */ + +struct pcg_state_8 { + uint8_t state; +}; + +struct pcg_state_16 { + uint16_t state; +}; + +struct pcg_state_32 { + uint32_t state; +}; + +struct pcg_state_64 { + uint64_t state; +}; + +#if PCG_HAS_128BIT_OPS +struct pcg_state_128 { + pcg128_t state; +}; +#endif + +/* Representations setseq variants */ + +struct pcg_state_setseq_8 { + uint8_t state; + uint8_t inc; +}; + +struct pcg_state_setseq_16 { + uint16_t state; + uint16_t inc; +}; + +struct pcg_state_setseq_32 { + uint32_t state; + uint32_t inc; +}; + +struct pcg_state_setseq_64 { + uint64_t state; + uint64_t inc; +}; + +#if PCG_HAS_128BIT_OPS +struct pcg_state_setseq_128 { + pcg128_t state; + pcg128_t inc; +}; +#endif + +/* Multi-step advance functions (jump-ahead, jump-back) */ + +extern uint8_t pcg_advance_lcg_8(uint8_t state, uint8_t delta, uint8_t cur_mult, + uint8_t cur_plus); +extern uint16_t pcg_advance_lcg_16(uint16_t state, uint16_t delta, + uint16_t cur_mult, uint16_t cur_plus); +extern uint32_t pcg_advance_lcg_32(uint32_t state, uint32_t delta, + uint32_t cur_mult, uint32_t cur_plus); +extern uint64_t pcg_advance_lcg_64(uint64_t state, uint64_t delta, + uint64_t cur_mult, uint64_t cur_plus); + +#if PCG_HAS_128BIT_OPS +extern pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, + pcg128_t cur_mult, pcg128_t cur_plus); +#endif + +/* Functions to advance the underlying LCG, one version for each size and + * each style. These functions are considered semi-private. There is rarely + * a good reason to call them directly. + */ + +inline void pcg_oneseq_8_step_r(struct pcg_state_8* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + + PCG_DEFAULT_INCREMENT_8; +} + +inline void pcg_oneseq_8_advance_r(struct pcg_state_8* rng, uint8_t delta) +{ + rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, + PCG_DEFAULT_INCREMENT_8); +} + +inline void pcg_mcg_8_step_r(struct pcg_state_8* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8; +} + +inline void pcg_mcg_8_advance_r(struct pcg_state_8* rng, uint8_t delta) +{ + rng->state + = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, 0u); +} + +inline void pcg_unique_8_step_r(struct pcg_state_8* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + + (uint8_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_8_advance_r(struct pcg_state_8* rng, uint8_t delta) +{ + rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, + (uint8_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_8_step_r(struct pcg_state_setseq_8* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + rng->inc; +} + +inline void pcg_setseq_8_advance_r(struct pcg_state_setseq_8* rng, + uint8_t delta) +{ + rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, + rng->inc); +} + +inline void pcg_oneseq_16_step_r(struct pcg_state_16* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 + + PCG_DEFAULT_INCREMENT_16; +} + +inline void pcg_oneseq_16_advance_r(struct pcg_state_16* rng, uint16_t delta) +{ + rng->state = pcg_advance_lcg_16( + rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, PCG_DEFAULT_INCREMENT_16); +} + +inline void pcg_mcg_16_step_r(struct pcg_state_16* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16; +} + +inline void pcg_mcg_16_advance_r(struct pcg_state_16* rng, uint16_t delta) +{ + rng->state + = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, 0u); +} + +inline void pcg_unique_16_step_r(struct pcg_state_16* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 + + (uint16_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_16_advance_r(struct pcg_state_16* rng, uint16_t delta) +{ + rng->state + = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, + (uint16_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_16_step_r(struct pcg_state_setseq_16* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 + rng->inc; +} + +inline void pcg_setseq_16_advance_r(struct pcg_state_setseq_16* rng, + uint16_t delta) +{ + rng->state = pcg_advance_lcg_16(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_16, rng->inc); +} + +inline void pcg_oneseq_32_step_r(struct pcg_state_32* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 + + PCG_DEFAULT_INCREMENT_32; +} + +inline void pcg_oneseq_32_advance_r(struct pcg_state_32* rng, uint32_t delta) +{ + rng->state = pcg_advance_lcg_32( + rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, PCG_DEFAULT_INCREMENT_32); +} + +inline void pcg_mcg_32_step_r(struct pcg_state_32* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32; +} + +inline void pcg_mcg_32_advance_r(struct pcg_state_32* rng, uint32_t delta) +{ + rng->state + = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, 0u); +} + +inline void pcg_unique_32_step_r(struct pcg_state_32* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 + + (uint32_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_32_advance_r(struct pcg_state_32* rng, uint32_t delta) +{ + rng->state + = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, + (uint32_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_32_step_r(struct pcg_state_setseq_32* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 + rng->inc; +} + +inline void pcg_setseq_32_advance_r(struct pcg_state_setseq_32* rng, + uint32_t delta) +{ + rng->state = pcg_advance_lcg_32(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_32, rng->inc); +} + +inline void pcg_oneseq_64_step_r(struct pcg_state_64* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + + PCG_DEFAULT_INCREMENT_64; +} + +inline void pcg_oneseq_64_advance_r(struct pcg_state_64* rng, uint64_t delta) +{ + rng->state = pcg_advance_lcg_64( + rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, PCG_DEFAULT_INCREMENT_64); +} + +inline void pcg_mcg_64_step_r(struct pcg_state_64* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64; +} + +inline void pcg_mcg_64_advance_r(struct pcg_state_64* rng, uint64_t delta) +{ + rng->state + = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, 0u); +} + +inline void pcg_unique_64_step_r(struct pcg_state_64* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + + (uint64_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_64_advance_r(struct pcg_state_64* rng, uint64_t delta) +{ + rng->state + = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, + (uint64_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_64_step_r(struct pcg_state_setseq_64* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + rng->inc; +} + +inline void pcg_setseq_64_advance_r(struct pcg_state_setseq_64* rng, + uint64_t delta) +{ + rng->state = pcg_advance_lcg_64(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_64, rng->inc); +} + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_step_r(struct pcg_state_128* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + + PCG_DEFAULT_INCREMENT_128; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) +{ + rng->state + = pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, + PCG_DEFAULT_INCREMENT_128); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_step_r(struct pcg_state_128* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) +{ + rng->state = pcg_advance_lcg_128(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_128, 0u); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_step_r(struct pcg_state_128* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + + (pcg128_t)(((intptr_t)rng) | 1u); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) +{ + rng->state + = pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, + (pcg128_t)(((intptr_t)rng) | 1u)); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_step_r(struct pcg_state_setseq_128* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + rng->inc; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_advance_r(struct pcg_state_setseq_128* rng, + pcg128_t delta) +{ + rng->state = pcg_advance_lcg_128(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_128, rng->inc); +} +#endif + +/* Functions to seed the RNG state, one version for each size and each + * style. Unlike the step functions, regular users can and should call + * these functions. + */ + +inline void pcg_oneseq_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) +{ + rng->state = 0U; + pcg_oneseq_8_step_r(rng); + rng->state += initstate; + pcg_oneseq_8_step_r(rng); +} + +inline void pcg_mcg_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) +{ + rng->state = initstate | 1u; +} + +inline void pcg_unique_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) +{ + rng->state = 0U; + pcg_unique_8_step_r(rng); + rng->state += initstate; + pcg_unique_8_step_r(rng); +} + +inline void pcg_setseq_8_srandom_r(struct pcg_state_setseq_8* rng, + uint8_t initstate, uint8_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_8_step_r(rng); + rng->state += initstate; + pcg_setseq_8_step_r(rng); +} + +inline void pcg_oneseq_16_srandom_r(struct pcg_state_16* rng, + uint16_t initstate) +{ + rng->state = 0U; + pcg_oneseq_16_step_r(rng); + rng->state += initstate; + pcg_oneseq_16_step_r(rng); +} + +inline void pcg_mcg_16_srandom_r(struct pcg_state_16* rng, uint16_t initstate) +{ + rng->state = initstate | 1u; +} + +inline void pcg_unique_16_srandom_r(struct pcg_state_16* rng, + uint16_t initstate) +{ + rng->state = 0U; + pcg_unique_16_step_r(rng); + rng->state += initstate; + pcg_unique_16_step_r(rng); +} + +inline void pcg_setseq_16_srandom_r(struct pcg_state_setseq_16* rng, + uint16_t initstate, uint16_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_16_step_r(rng); + rng->state += initstate; + pcg_setseq_16_step_r(rng); +} + +inline void pcg_oneseq_32_srandom_r(struct pcg_state_32* rng, + uint32_t initstate) +{ + rng->state = 0U; + pcg_oneseq_32_step_r(rng); + rng->state += initstate; + pcg_oneseq_32_step_r(rng); +} + +inline void pcg_mcg_32_srandom_r(struct pcg_state_32* rng, uint32_t initstate) +{ + rng->state = initstate | 1u; +} + +inline void pcg_unique_32_srandom_r(struct pcg_state_32* rng, + uint32_t initstate) +{ + rng->state = 0U; + pcg_unique_32_step_r(rng); + rng->state += initstate; + pcg_unique_32_step_r(rng); +} + +inline void pcg_setseq_32_srandom_r(struct pcg_state_setseq_32* rng, + uint32_t initstate, uint32_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_32_step_r(rng); + rng->state += initstate; + pcg_setseq_32_step_r(rng); +} + +inline void pcg_oneseq_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate) +{ + rng->state = 0U; + pcg_oneseq_64_step_r(rng); + rng->state += initstate; + pcg_oneseq_64_step_r(rng); +} + +inline void pcg_mcg_64_srandom_r(struct pcg_state_64* rng, uint64_t initstate) +{ + rng->state = initstate | 1u; +} + +inline void pcg_unique_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate) +{ + rng->state = 0U; + pcg_unique_64_step_r(rng); + rng->state += initstate; + pcg_unique_64_step_r(rng); +} + +inline void pcg_setseq_64_srandom_r(struct pcg_state_setseq_64* rng, + uint64_t initstate, uint64_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_64_step_r(rng); + rng->state += initstate; + pcg_setseq_64_step_r(rng); +} + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate) +{ + rng->state = 0U; + pcg_oneseq_128_step_r(rng); + rng->state += initstate; + pcg_oneseq_128_step_r(rng); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_srandom_r(struct pcg_state_128* rng, pcg128_t initstate) +{ + rng->state = initstate | 1u; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate) +{ + rng->state = 0U; + pcg_unique_128_step_r(rng); + rng->state += initstate; + pcg_unique_128_step_r(rng); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_srandom_r(struct pcg_state_setseq_128* rng, + pcg128_t initstate, pcg128_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_128_step_r(rng); + rng->state += initstate; + pcg_setseq_128_step_r(rng); +} +#endif + +/* Now, finally we create each of the individual generators. We provide + * a random_r function that provides a random number of the appropriate + * type (using the full range of the type) and a boundedrand_r version + * that provides + * + * Implementation notes for boundedrand_r: + * + * To avoid bias, we need to make the range of the RNG a multiple of + * bound, which we do by dropping output less than a threshold. + * Let's consider a 32-bit case... A naive scheme to calculate the + * threshold would be to do + * + * uint32_t threshold = 0x100000000ull % bound; + * + * but 64-bit div/mod is slower than 32-bit div/mod (especially on + * 32-bit platforms). In essence, we do + * + * uint32_t threshold = (0x100000000ull-bound) % bound; + * + * because this version will calculate the same modulus, but the LHS + * value is less than 2^32. + * + * (Note that using modulo is only wise for good RNGs, poorer RNGs + * such as raw LCGs do better using a technique based on division.) + * Empricical tests show that division is preferable to modulus for + * reducting the range of an RNG. It's faster, and sometimes it can + * even be statistically prefereable. + */ + +/* Generation functions for XSH RS */ + +inline uint8_t pcg_oneseq_16_xsh_rs_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_oneseq_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_32_xsh_rs_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_oneseq_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_64_xsh_rs_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsh_rs_64_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_unique_16_xsh_rs_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_unique_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_unique_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_unique_32_xsh_rs_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_unique_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_64_xsh_rs_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsh_rs_64_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_setseq_16_xsh_rs_8_random_r(struct pcg_state_setseq_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t +pcg_setseq_16_xsh_rs_8_boundedrand_r(struct pcg_state_setseq_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_32_xsh_rs_16_random_r(struct pcg_state_setseq_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t +pcg_setseq_32_xsh_rs_16_boundedrand_r(struct pcg_state_setseq_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_64_xsh_rs_32_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rs_64_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_mcg_16_xsh_rs_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_mcg_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_mcg_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_mcg_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_mcg_32_xsh_rs_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_mcg_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_mcg_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_mcg_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_mcg_64_xsh_rs_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rs_64_random_r(struct pcg_state_128* rng) +{ + pcg_mcg_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSH RR */ + +inline uint8_t pcg_oneseq_16_xsh_rr_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_oneseq_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_32_xsh_rr_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_oneseq_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_64_xsh_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsh_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_unique_16_xsh_rr_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_unique_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_unique_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_unique_32_xsh_rr_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_unique_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_64_xsh_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsh_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_setseq_16_xsh_rr_8_random_r(struct pcg_state_setseq_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t +pcg_setseq_16_xsh_rr_8_boundedrand_r(struct pcg_state_setseq_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_32_xsh_rr_16_random_r(struct pcg_state_setseq_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t +pcg_setseq_32_xsh_rr_16_boundedrand_r(struct pcg_state_setseq_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_64_xsh_rr_32_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rr_64_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_mcg_16_xsh_rr_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_mcg_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_mcg_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_mcg_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_mcg_32_xsh_rr_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_mcg_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_mcg_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_mcg_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_mcg_64_xsh_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_mcg_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for RXS M XS (no MCG versions because they + * don't make sense when you want to use the entire state) + */ + +inline uint8_t pcg_oneseq_8_rxs_m_xs_8_random_r(struct pcg_state_8* rng) +{ + uint8_t oldstate = rng->state; + pcg_oneseq_8_step_r(rng); + return pcg_output_rxs_m_xs_8_8(oldstate); +} + +inline uint8_t pcg_oneseq_8_rxs_m_xs_8_boundedrand_r(struct pcg_state_8* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_8_rxs_m_xs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_16_rxs_m_xs_16_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_oneseq_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_16* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_32_rxs_m_xs_32_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_oneseq_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_32* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t pcg_oneseq_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_oneseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_oneseq_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_oneseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_oneseq_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint16_t pcg_unique_16_rxs_m_xs_16_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_unique_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_16* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_32_rxs_m_xs_32_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_unique_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_32* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t pcg_unique_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_unique_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_unique_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_unique_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_unique_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_setseq_8_rxs_m_xs_8_random_r(struct pcg_state_setseq_8* rng) +{ + uint8_t oldstate = rng->state; + pcg_setseq_8_step_r(rng); + return pcg_output_rxs_m_xs_8_8(oldstate); +} + +inline uint8_t +pcg_setseq_8_rxs_m_xs_8_boundedrand_r(struct pcg_state_setseq_8* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_8_rxs_m_xs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_16_rxs_m_xs_16_random_r(struct pcg_state_setseq_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_setseq_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_setseq_16* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_32_rxs_m_xs_32_random_r(struct pcg_state_setseq_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_setseq_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_setseq_32* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t +pcg_setseq_64_rxs_m_xs_64_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_setseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_setseq_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_setseq_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_setseq_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for RXS M */ + +inline uint8_t pcg_oneseq_16_rxs_m_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_rxs_m_16_8(oldstate); +} + +inline uint8_t pcg_oneseq_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_16_rxs_m_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_32_rxs_m_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_rxs_m_32_16(oldstate); +} + +inline uint16_t pcg_oneseq_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_32_rxs_m_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_64_rxs_m_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_rxs_m_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_rxs_m_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_rxs_m_64_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_rxs_m_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_rxs_m_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_unique_16_rxs_m_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_rxs_m_16_8(oldstate); +} + +inline uint8_t pcg_unique_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_unique_16_rxs_m_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_unique_32_rxs_m_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_rxs_m_32_16(oldstate); +} + +inline uint16_t pcg_unique_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_32_rxs_m_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_64_rxs_m_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_rxs_m_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_rxs_m_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_rxs_m_64_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_rxs_m_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_rxs_m_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_setseq_16_rxs_m_8_random_r(struct pcg_state_setseq_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_rxs_m_16_8(oldstate); +} + +inline uint8_t +pcg_setseq_16_rxs_m_8_boundedrand_r(struct pcg_state_setseq_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_16_rxs_m_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_setseq_32_rxs_m_16_random_r(struct pcg_state_setseq_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_rxs_m_32_16(oldstate); +} + +inline uint16_t +pcg_setseq_32_rxs_m_16_boundedrand_r(struct pcg_state_setseq_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_32_rxs_m_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_setseq_64_rxs_m_32_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_rxs_m_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_rxs_m_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_rxs_m_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_rxs_m_64_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_rxs_m_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_rxs_m_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_rxs_m_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_mcg_16_rxs_m_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_mcg_16_step_r(rng); + return pcg_output_rxs_m_16_8(oldstate); +} + +inline uint8_t pcg_mcg_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_mcg_16_rxs_m_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_mcg_32_rxs_m_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_mcg_32_step_r(rng); + return pcg_output_rxs_m_32_16(oldstate); +} + +inline uint16_t pcg_mcg_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_mcg_32_rxs_m_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_mcg_64_rxs_m_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_rxs_m_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_rxs_m_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_rxs_m_64_random_r(struct pcg_state_128* rng) +{ + pcg_mcg_128_step_r(rng); + return pcg_output_rxs_m_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_rxs_m_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSL RR (only defined for "large" types) */ + +inline uint32_t pcg_oneseq_64_xsl_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsl_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t pcg_unique_64_xsl_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsl_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t +pcg_setseq_64_xsl_rr_32_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsl_rr_64_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t pcg_mcg_64_xsl_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsl_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_mcg_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSL RR RR (only defined for "large" types) */ + +inline uint64_t pcg_oneseq_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_oneseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_oneseq_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_oneseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_oneseq_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint64_t pcg_unique_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_unique_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_unique_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_unique_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_unique_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_setseq_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_setseq_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_setseq_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/*** Typedefs */ +typedef struct pcg_state_setseq_64 pcg32_random_t; +typedef struct pcg_state_64 pcg32s_random_t; +typedef struct pcg_state_64 pcg32u_random_t; +typedef struct pcg_state_64 pcg32f_random_t; +/*** random_r */ +#define pcg32_random_r pcg_setseq_64_xsh_rr_32_random_r +#define pcg32s_random_r pcg_oneseq_64_xsh_rr_32_random_r +#define pcg32u_random_r pcg_unique_64_xsh_rr_32_random_r +#define pcg32f_random_r pcg_mcg_64_xsh_rs_32_random_r +/*** boundedrand_r */ +#define pcg32_boundedrand_r pcg_setseq_64_xsh_rr_32_boundedrand_r +#define pcg32s_boundedrand_r pcg_oneseq_64_xsh_rr_32_boundedrand_r +#define pcg32u_boundedrand_r pcg_unique_64_xsh_rr_32_boundedrand_r +#define pcg32f_boundedrand_r pcg_mcg_64_xsh_rs_32_boundedrand_r +/*** srandom_r */ +#define pcg32_srandom_r pcg_setseq_64_srandom_r +#define pcg32s_srandom_r pcg_oneseq_64_srandom_r +#define pcg32u_srandom_r pcg_unique_64_srandom_r +#define pcg32f_srandom_r pcg_mcg_64_srandom_r +/*** advance_r */ +#define pcg32_advance_r pcg_setseq_64_advance_r +#define pcg32s_advance_r pcg_oneseq_64_advance_r +#define pcg32u_advance_r pcg_unique_64_advance_r +#define pcg32f_advance_r pcg_mcg_64_advance_r + +#if PCG_HAS_128BIT_OPS +/*** Typedefs */ +typedef struct pcg_state_setseq_128 pcg64_random_t; +typedef struct pcg_state_128 pcg64s_random_t; +typedef struct pcg_state_128 pcg64u_random_t; +typedef struct pcg_state_128 pcg64f_random_t; +/*** random_r */ +#define pcg64_random_r pcg_setseq_128_xsl_rr_64_random_r +#define pcg64s_random_r pcg_oneseq_128_xsl_rr_64_random_r +#define pcg64u_random_r pcg_unique_128_xsl_rr_64_random_r +#define pcg64f_random_r pcg_mcg_128_xsl_rr_64_random_r +/*** boundedrand_r */ +#define pcg64_boundedrand_r pcg_setseq_128_xsl_rr_64_boundedrand_r +#define pcg64s_boundedrand_r pcg_oneseq_128_xsl_rr_64_boundedrand_r +#define pcg64u_boundedrand_r pcg_unique_128_xsl_rr_64_boundedrand_r +#define pcg64f_boundedrand_r pcg_mcg_128_xsl_rr_64_boundedrand_r +/*** srandom_r */ +#define pcg64_srandom_r pcg_setseq_128_srandom_r +#define pcg64s_srandom_r pcg_oneseq_128_srandom_r +#define pcg64u_srandom_r pcg_unique_128_srandom_r +#define pcg64f_srandom_r pcg_mcg_128_srandom_r +/*** advance_r */ +#define pcg64_advance_r pcg_setseq_128_advance_r +#define pcg64s_advance_r pcg_oneseq_128_advance_r +#define pcg64u_advance_r pcg_unique_128_advance_r +#define pcg64f_advance_r pcg_mcg_128_advance_r +#endif + +/*** Typedefs */ +typedef struct pcg_state_8 pcg8si_random_t; +typedef struct pcg_state_16 pcg16si_random_t; +typedef struct pcg_state_32 pcg32si_random_t; +typedef struct pcg_state_64 pcg64si_random_t; +/*** random_r */ +#define pcg8si_random_r pcg_oneseq_8_rxs_m_xs_8_random_r +#define pcg16si_random_r pcg_oneseq_16_rxs_m_xs_16_random_r +#define pcg32si_random_r pcg_oneseq_32_rxs_m_xs_32_random_r +#define pcg64si_random_r pcg_oneseq_64_rxs_m_xs_64_random_r +/*** boundedrand_r */ +#define pcg8si_boundedrand_r pcg_oneseq_8_rxs_m_xs_8_boundedrand_r +#define pcg16si_boundedrand_r pcg_oneseq_16_rxs_m_xs_16_boundedrand_r +#define pcg32si_boundedrand_r pcg_oneseq_32_rxs_m_xs_32_boundedrand_r +#define pcg64si_boundedrand_r pcg_oneseq_64_rxs_m_xs_64_boundedrand_r +/*** srandom_r */ +#define pcg8si_srandom_r pcg_oneseq_8_srandom_r +#define pcg16si_srandom_r pcg_oneseq_16_srandom_r +#define pcg32si_srandom_r pcg_oneseq_32_srandom_r +#define pcg64si_srandom_r pcg_oneseq_64_srandom_r +/*** advance_r */ +#define pcg8si_advance_r pcg_oneseq_8_advance_r +#define pcg16si_advance_r pcg_oneseq_16_advance_r +#define pcg32si_advance_r pcg_oneseq_32_advance_r +#define pcg64si_advance_r pcg_oneseq_64_advance_r + +#if PCG_HAS_128BIT_OPS +typedef struct pcg_state_128 pcg128si_random_t; +#define pcg128si_random_r pcg_oneseq_128_rxs_m_xs_128_random_r +#define pcg128si_boundedrand_r pcg_oneseq_128_rxs_m_xs_128_boundedrand_r +#define pcg128si_srandom_r pcg_oneseq_128_srandom_r +#define pcg128si_advance_r pcg_oneseq_128_advance_r +#endif + +/*** Typedefs */ +typedef struct pcg_state_setseq_8 pcg8i_random_t; +typedef struct pcg_state_setseq_16 pcg16i_random_t; +typedef struct pcg_state_setseq_32 pcg32i_random_t; +typedef struct pcg_state_setseq_64 pcg64i_random_t; +/*** random_r */ +#define pcg8i_random_r pcg_setseq_8_rxs_m_xs_8_random_r +#define pcg16i_random_r pcg_setseq_16_rxs_m_xs_16_random_r +#define pcg32i_random_r pcg_setseq_32_rxs_m_xs_32_random_r +#define pcg64i_random_r pcg_setseq_64_rxs_m_xs_64_random_r +/*** boundedrand_r */ +#define pcg8i_boundedrand_r pcg_setseq_8_rxs_m_xs_8_boundedrand_r +#define pcg16i_boundedrand_r pcg_setseq_16_rxs_m_xs_16_boundedrand_r +#define pcg32i_boundedrand_r pcg_setseq_32_rxs_m_xs_32_boundedrand_r +#define pcg64i_boundedrand_r pcg_setseq_64_rxs_m_xs_64_boundedrand_r +/*** srandom_r */ +#define pcg8i_srandom_r pcg_setseq_8_srandom_r +#define pcg16i_srandom_r pcg_setseq_16_srandom_r +#define pcg32i_srandom_r pcg_setseq_32_srandom_r +#define pcg64i_srandom_r pcg_setseq_64_srandom_r +/*** advance_r */ +#define pcg8i_advance_r pcg_setseq_8_advance_r +#define pcg16i_advance_r pcg_setseq_16_advance_r +#define pcg32i_advance_r pcg_setseq_32_advance_r +#define pcg64i_advance_r pcg_setseq_64_advance_r + +#if PCG_HAS_128BIT_OPS +typedef struct pcg_state_setseq_128 pcg128i_random_t; +#define pcg128i_random_r pcg_setseq_128_rxs_m_xs_128_random_r +#define pcg128i_boundedrand_r pcg_setseq_128_rxs_m_xs_128_boundedrand_r +#define pcg128i_srandom_r pcg_setseq_128_srandom_r +#define pcg128i_advance_r pcg_setseq_128_advance_r +#endif + +extern uint32_t pcg32_random(void); +extern uint32_t pcg32_boundedrand(uint32_t bound); +extern void pcg32_srandom(uint64_t seed, uint64_t seq); +extern void pcg32_advance(uint64_t delta); + +#if PCG_HAS_128BIT_OPS +extern uint64_t pcg64_random(void); +extern uint64_t pcg64_boundedrand(uint64_t bound); +extern void pcg64_srandom(pcg128_t seed, pcg128_t seq); +extern void pcg64_advance(pcg128_t delta); +#endif + +/* + * Static initialization constants (if you can't call srandom for some + * bizarre reason). + */ + +#define PCG32_INITIALIZER PCG_STATE_SETSEQ_64_INITIALIZER +#define PCG32U_INITIALIZER PCG_STATE_UNIQUE_64_INITIALIZER +#define PCG32S_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#define PCG32F_INITIALIZER PCG_STATE_MCG_64_INITIALIZER + +#if PCG_HAS_128BIT_OPS +#define PCG64_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER +#define PCG64U_INITIALIZER PCG_STATE_UNIQUE_128_INITIALIZER +#define PCG64S_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#define PCG64F_INITIALIZER PCG_STATE_MCG_128_INITIALIZER +#endif + +#define PCG8SI_INITIALIZER PCG_STATE_ONESEQ_8_INITIALIZER +#define PCG16SI_INITIALIZER PCG_STATE_ONESEQ_16_INITIALIZER +#define PCG32SI_INITIALIZER PCG_STATE_ONESEQ_32_INITIALIZER +#define PCG64SI_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG128SI_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#endif + +#define PCG8I_INITIALIZER PCG_STATE_SETSEQ_8_INITIALIZER +#define PCG16I_INITIALIZER PCG_STATE_SETSEQ_16_INITIALIZER +#define PCG32I_INITIALIZER PCG_STATE_SETSEQ_32_INITIALIZER +#define PCG64I_INITIALIZER PCG_STATE_SETSEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG128I_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER +#endif + +#if __cplusplus +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* PCG_VARIANTS_H_INCLUDED */ -- 2.30.2